diff --git a/.idea/libraries/test_lib.xml b/.idea/libraries/test_lib.xml
index 732f2d2d418..5b92e91060a 100644
--- a/.idea/libraries/test_lib.xml
+++ b/.idea/libraries/test_lib.xml
@@ -5,13 +5,13 @@
-
+
@@ -21,4 +21,4 @@
-
+
\ No newline at end of file
diff --git a/src/com/facebook/buck/android/KotlinAndroidLibraryCompiler.java b/src/com/facebook/buck/android/KotlinAndroidLibraryCompiler.java
index eaf565294ef..4b65b597bf8 100644
--- a/src/com/facebook/buck/android/KotlinAndroidLibraryCompiler.java
+++ b/src/com/facebook/buck/android/KotlinAndroidLibraryCompiler.java
@@ -43,8 +43,6 @@ public CompileToJarStepFactory compileToJar(
JavacOptions javacOptions,
BuildRuleResolver resolver) {
return new KotlincToJarStepFactory(
- kotlinBuckConfig.getKotlinCompiler().get(),
- ImmutableList.of(),
- ANDROID_CLASSPATH_FROM_CONTEXT);
+ kotlinBuckConfig.getKotlinc(), ImmutableList.of(), ANDROID_CLASSPATH_FROM_CONTEXT);
}
}
diff --git a/src/com/facebook/buck/jvm/kotlin/AbstractKotlincVersion.java b/src/com/facebook/buck/jvm/kotlin/AbstractKotlincVersion.java
new file mode 100644
index 00000000000..cbb0dc44c09
--- /dev/null
+++ b/src/com/facebook/buck/jvm/kotlin/AbstractKotlincVersion.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2016-present Facebook, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may
+ * not use this file except in compliance with the License. You may obtain
+ * a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+package com.facebook.buck.jvm.kotlin;
+
+import com.facebook.buck.util.immutables.BuckStyleImmutable;
+import org.immutables.value.Value;
+
+@Value.Immutable
+@BuckStyleImmutable
+abstract class AbstractKotlincVersion {
+ @Value.Parameter
+ public abstract String getVersionString();
+
+ @Override
+ public String toString() {
+ return getVersionString();
+ }
+}
diff --git a/src/com/facebook/buck/jvm/kotlin/BUCK b/src/com/facebook/buck/jvm/kotlin/BUCK
index d3acd356868..d7357fad833 100644
--- a/src/com/facebook/buck/jvm/kotlin/BUCK
+++ b/src/com/facebook/buck/jvm/kotlin/BUCK
@@ -6,6 +6,8 @@ java_immutables_library(
],
visibility = ["PUBLIC"],
deps = [
+ "//src/com/facebook/buck/android:apkmodule",
+ "//src/com/facebook/buck/android:packageable",
"//src/com/facebook/buck/cli:config",
"//src/com/facebook/buck/io:executable-finder",
"//src/com/facebook/buck/io:io",
@@ -23,13 +25,20 @@ java_immutables_library(
"//src/com/facebook/buck/rules:rules",
"//src/com/facebook/buck/rules:source_path",
"//src/com/facebook/buck/rules/args:args",
+ "//src/com/facebook/buck/rules/keys:keys",
"//src/com/facebook/buck/rules/macros:macros",
"//src/com/facebook/buck/shell:steps",
"//src/com/facebook/buck/step:step",
"//src/com/facebook/buck/util:exceptions",
+ "//src/com/facebook/buck/util:io",
+ "//src/com/facebook/buck/util:process_executor",
+ "//src/com/facebook/buck/util:util",
+ "//src/com/facebook/buck/util/environment:platform",
+ "//src/com/facebook/buck/util/immutables:immutables",
"//src/com/facebook/buck/versions:versions",
"//third-party/java/guava:guava",
"//third-party/java/infer-annotations:infer-annotations",
+ "//third-party/java/jackson:jackson-annotations",
"//third-party/java/jsr:jsr305",
],
)
diff --git a/src/com/facebook/buck/jvm/kotlin/ExternalKotlinc.java b/src/com/facebook/buck/jvm/kotlin/ExternalKotlinc.java
new file mode 100644
index 00000000000..40fcef4c9fb
--- /dev/null
+++ b/src/com/facebook/buck/jvm/kotlin/ExternalKotlinc.java
@@ -0,0 +1,175 @@
+/*
+ * Copyright 2016-present Facebook, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may
+ * not use this file except in compliance with the License. You may obtain
+ * a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+package com.facebook.buck.jvm.kotlin;
+
+import static com.google.common.collect.Iterables.transform;
+
+import com.facebook.buck.io.ProjectFilesystem;
+import com.facebook.buck.model.BuildTarget;
+import com.facebook.buck.rules.BuildRule;
+import com.facebook.buck.rules.RuleKeyObjectSink;
+import com.facebook.buck.rules.SourcePath;
+import com.facebook.buck.rules.SourcePathResolver;
+import com.facebook.buck.rules.SourcePathRuleFinder;
+import com.facebook.buck.step.ExecutionContext;
+import com.facebook.buck.util.Console;
+import com.facebook.buck.util.DefaultProcessExecutor;
+import com.facebook.buck.util.ProcessExecutor;
+import com.facebook.buck.util.ProcessExecutorParams;
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.base.Joiner;
+import com.google.common.base.Strings;
+import com.google.common.base.Supplier;
+import com.google.common.base.Suppliers;
+import com.google.common.collect.ImmutableCollection;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSortedSet;
+import java.io.IOException;
+import java.nio.file.Path;
+import java.util.Optional;
+
+public class ExternalKotlinc implements Kotlinc {
+
+ private static final KotlincVersion DEFAULT_VERSION = KotlincVersion.of("unknown version");
+
+ private final Path pathToKotlinc;
+ private final Supplier version;
+
+ public ExternalKotlinc(final Path pathToKotlinc) {
+ this.pathToKotlinc = pathToKotlinc;
+
+ this.version =
+ Suppliers.memoize(
+ () -> {
+ ProcessExecutorParams params =
+ ProcessExecutorParams.builder()
+ .setCommand(ImmutableList.of(pathToKotlinc.toString(), "-version"))
+ .build();
+ ProcessExecutor.Result result;
+ try {
+ result = createProcessExecutor().launchAndExecute(params);
+ } catch (InterruptedException | IOException e) {
+ throw new RuntimeException(e);
+ }
+ Optional stderr = result.getStderr();
+ String output = stderr.orElse("").trim();
+ if (Strings.isNullOrEmpty(output)) {
+ return DEFAULT_VERSION;
+ } else {
+ return KotlincVersion.of(output);
+ }
+ });
+ }
+
+ @Override
+ public void appendToRuleKey(RuleKeyObjectSink sink) {
+ if (DEFAULT_VERSION.equals(getVersion())) {
+ // What we really want to do here is use a VersionedTool, however, this will suffice for now.
+ sink.setReflectively("kotlinc", getShortName());
+ } else {
+ sink.setReflectively("kotlinc.version", getVersion().toString());
+ }
+ }
+
+ @Override
+ public ImmutableCollection getDeps(SourcePathRuleFinder ruleFinder) {
+ return ruleFinder.filterBuildRuleInputs(getInputs());
+ }
+
+ @Override
+ public ImmutableCollection getInputs() {
+ return ImmutableSortedSet.of();
+ }
+
+ @Override
+ public ImmutableList getCommandPrefix(SourcePathResolver resolver) {
+ return ImmutableList.of(pathToKotlinc.toString());
+ }
+
+ @Override
+ public KotlincVersion getVersion() {
+ return version.get();
+ }
+
+ @Override
+ public int buildWithClasspath(
+ ExecutionContext context,
+ BuildTarget invokingRule,
+ ImmutableList options,
+ ImmutableSortedSet kotlinSourceFilePaths,
+ Path pathToSrcsList,
+ Optional workingDirectory,
+ ProjectFilesystem projectFilesystem)
+ throws InterruptedException {
+
+ ImmutableList command =
+ ImmutableList.builder()
+ .add(pathToKotlinc.toString())
+ .addAll(
+ transform(
+ kotlinSourceFilePaths,
+ path -> projectFilesystem.resolve(path).toAbsolutePath().toString()))
+ .build();
+
+ // Run the command
+ int exitCode = -1;
+ try {
+ ProcessExecutorParams params =
+ ProcessExecutorParams.builder()
+ .setCommand(command)
+ .setEnvironment(context.getEnvironment())
+ .setDirectory(projectFilesystem.getRootPath().toAbsolutePath())
+ .build();
+ ProcessExecutor.Result result = context.getProcessExecutor().launchAndExecute(params);
+ exitCode = result.getExitCode();
+ } catch (IOException e) {
+ e.printStackTrace(context.getStdErr());
+ return exitCode;
+ }
+
+ return exitCode;
+ }
+
+ @VisibleForTesting
+ ProcessExecutor createProcessExecutor() {
+ return new DefaultProcessExecutor(Console.createNullConsole());
+ }
+
+ @Override
+ public String getDescription(
+ ImmutableList options,
+ ImmutableSortedSet kotlinSourceFilePaths,
+ Path pathToSrcsList) {
+ StringBuilder builder = new StringBuilder(getShortName());
+ builder.append(" ");
+ Joiner.on(" ").appendTo(builder, options);
+ builder.append(" ");
+ builder.append("@").append(pathToSrcsList);
+
+ return builder.toString();
+ }
+
+ @Override
+ public String getShortName() {
+ return pathToKotlinc.toString();
+ }
+
+ @Override
+ public ImmutableMap getEnvironment(SourcePathResolver resolver) {
+ return ImmutableMap.of();
+ }
+}
diff --git a/src/com/facebook/buck/jvm/kotlin/JarBackedReflectedKotlinc.java b/src/com/facebook/buck/jvm/kotlin/JarBackedReflectedKotlinc.java
new file mode 100644
index 00000000000..a51e64fb4f8
--- /dev/null
+++ b/src/com/facebook/buck/jvm/kotlin/JarBackedReflectedKotlinc.java
@@ -0,0 +1,199 @@
+/*
+ * Copyright 2017-present Facebook, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may
+ * not use this file except in compliance with the License. You may obtain
+ * a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.facebook.buck.jvm.kotlin;
+
+import static com.google.common.collect.Iterables.transform;
+
+import com.facebook.buck.io.ProjectFilesystem;
+import com.facebook.buck.model.BuildTarget;
+import com.facebook.buck.rules.BuildRule;
+import com.facebook.buck.rules.RuleKeyObjectSink;
+import com.facebook.buck.rules.SourcePath;
+import com.facebook.buck.rules.SourcePathResolver;
+import com.facebook.buck.rules.SourcePathRuleFinder;
+import com.facebook.buck.step.ExecutionContext;
+import com.facebook.buck.util.ClassLoaderCache;
+import com.google.common.base.Joiner;
+import com.google.common.collect.ImmutableCollection;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.ImmutableSortedSet;
+import java.io.File;
+import java.io.PrintStream;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.nio.file.Path;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+
+public class JarBackedReflectedKotlinc implements Kotlinc {
+
+ private static final String COMPILER_CLASS = "org.jetbrains.kotlin.cli.jvm.K2JVMCompiler";
+ private static final String EXIT_CODE_CLASS = "org.jetbrains.kotlin.cli.common.ExitCode";
+ private static final KotlincVersion VERSION = KotlincVersion.of("in memory");
+
+ private static final Function PATH_TO_URL =
+ p -> {
+ try {
+ return p.toUri().toURL();
+ } catch (MalformedURLException e) {
+ throw new RuntimeException(e);
+ }
+ };
+
+ // Used to hang onto the KotlinDaemonShim for the life of the buckd process
+ private static final Map, Object> kotlinShims = new ConcurrentHashMap<>();
+
+ private final ImmutableSet compilerClassPath;
+
+ JarBackedReflectedKotlinc(ImmutableSet compilerClassPath) {
+ this.compilerClassPath = compilerClassPath;
+ }
+
+ @Override
+ public KotlincVersion getVersion() {
+ return VERSION;
+ }
+
+ @Override
+ public String getDescription(
+ ImmutableList options,
+ ImmutableSortedSet javaSourceFilePaths,
+ Path pathToSrcsList) {
+ StringBuilder builder = new StringBuilder("kotlinc ");
+ Joiner.on(" ").appendTo(builder, options);
+ builder.append(" ");
+ builder.append("@").append(pathToSrcsList);
+
+ return builder.toString();
+ }
+
+ @Override
+ public String getShortName() {
+ return "kotlinc";
+ }
+
+ @Override
+ public ImmutableList getCommandPrefix(SourcePathResolver resolver) {
+ throw new UnsupportedOperationException("In memory kotlinc may not be used externally");
+ }
+
+ @Override
+ public int buildWithClasspath(
+ ExecutionContext context,
+ BuildTarget invokingRule,
+ ImmutableList options,
+ ImmutableSortedSet kotlinSourceFilePaths,
+ Path pathToSrcsList,
+ Optional workingDirectory,
+ ProjectFilesystem projectFilesystem)
+ throws InterruptedException {
+
+ ImmutableList args =
+ ImmutableList.builder()
+ .addAll(options)
+ .addAll(
+ transform(
+ kotlinSourceFilePaths,
+ path -> projectFilesystem.resolve(path).toAbsolutePath().toString()))
+ .build();
+
+ Set compilerIdPaths =
+ compilerClassPath.stream().map(Path::toFile).collect(Collectors.toSet());
+
+ try {
+ Object compilerShim =
+ kotlinShims.computeIfAbsent(
+ compilerIdPaths.stream().map(File::getAbsolutePath).collect(Collectors.toSet()),
+ k -> loadCompilerShim(context));
+
+ Method compile = compilerShim.getClass().getMethod("exec", PrintStream.class, String[].class);
+
+ Class> exitCodeClass = compilerShim.getClass().getClassLoader().loadClass(EXIT_CODE_CLASS);
+
+ Method getCode = exitCodeClass.getMethod("getCode");
+
+ try (UncloseablePrintStream stdErr = new UncloseablePrintStream(context.getStdErr())) {
+ Object exitCode = compile.invoke(compilerShim, stdErr, args.toArray(new String[0]));
+
+ return (Integer) getCode.invoke(exitCode);
+ }
+
+ } catch (IllegalAccessException
+ | InvocationTargetException
+ | NoSuchMethodException
+ | ClassNotFoundException ex) {
+ throw new RuntimeException(ex);
+ }
+ }
+
+ @Override
+ public ImmutableCollection getDeps(SourcePathRuleFinder ruleFinder) {
+ return ruleFinder.filterBuildRuleInputs(getInputs());
+ }
+
+ @Override
+ public ImmutableCollection getInputs() {
+ return ImmutableSet.of();
+ }
+
+ @Override
+ public void appendToRuleKey(RuleKeyObjectSink sink) {
+ sink.setReflectively("kotlinc", "jar-backed")
+ .setReflectively("kotlinc.version", "in-memory")
+ .setReflectively("kotlinc.classpath", compilerClassPath.toString());
+ }
+
+ private Object loadCompilerShim(ExecutionContext context) {
+ try {
+ ClassLoaderCache classLoaderCache = context.getClassLoaderCache();
+ classLoaderCache.addRef();
+
+ ClassLoader classLoader =
+ classLoaderCache.getClassLoaderForClassPath(
+ null /* parent classloader */,
+ ImmutableList.copyOf(compilerClassPath.stream().map(PATH_TO_URL).iterator()));
+
+ return classLoader.loadClass(COMPILER_CLASS).newInstance();
+ } catch (Exception ex) {
+ throw new RuntimeException(ex);
+ }
+ }
+
+ @Override
+ public ImmutableMap getEnvironment(SourcePathResolver resolver) {
+ throw new UnsupportedOperationException("In memory kotlinc may not be used externally");
+ }
+
+ private static class UncloseablePrintStream extends PrintStream {
+ UncloseablePrintStream(PrintStream delegate) {
+ super(delegate);
+ }
+
+ @Override
+ public void close() {
+ // ignore
+ }
+ }
+}
diff --git a/src/com/facebook/buck/jvm/kotlin/KotlinBuckConfig.java b/src/com/facebook/buck/jvm/kotlin/KotlinBuckConfig.java
index c0bfc8d66ea..0c1ee979255 100644
--- a/src/com/facebook/buck/jvm/kotlin/KotlinBuckConfig.java
+++ b/src/com/facebook/buck/jvm/kotlin/KotlinBuckConfig.java
@@ -18,13 +18,8 @@
import com.facebook.buck.cli.BuckConfig;
import com.facebook.buck.io.ExecutableFinder;
-import com.facebook.buck.model.Either;
-import com.facebook.buck.rules.HashedFileTool;
-import com.facebook.buck.rules.SourcePath;
-import com.facebook.buck.rules.Tool;
import com.facebook.buck.util.HumanReadableException;
-import com.google.common.base.Supplier;
-import com.google.common.base.Suppliers;
+import com.google.common.collect.ImmutableSet;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
@@ -44,12 +39,18 @@ public KotlinBuckConfig(BuckConfig delegate) {
this.delegate = delegate;
}
- /**
- * Get the Tool instance for the Kotlin compiler.
- *
- * @return the Kotlin compiler Tool
- */
- public Supplier getKotlinCompiler() {
+ public Kotlinc getKotlinc() {
+ if (isExternalCompilation()) {
+ return new ExternalKotlinc(getPathToCompilerBinary());
+ } else {
+ ImmutableSet classpathEntries =
+ ImmutableSet.of(getPathToStdlibJar(), getPathToCompilerJar());
+
+ return new JarBackedReflectedKotlinc(classpathEntries);
+ }
+ }
+
+ Path getPathToCompilerBinary() {
Path compilerPath = getKotlinHome().resolve("kotlinc");
if (!Files.isExecutable(compilerPath)) {
compilerPath = getKotlinHome().resolve(Paths.get("bin", "kotlinc"));
@@ -58,8 +59,7 @@ public Supplier getKotlinCompiler() {
}
}
- Path compiler = new ExecutableFinder().getExecutable(compilerPath, delegate.getEnvironment());
- return Suppliers.ofInstance(new HashedFileTool(compiler));
+ return new ExecutableFinder().getExecutable(compilerPath, delegate.getEnvironment());
}
/**
@@ -67,58 +67,114 @@ public Supplier getKotlinCompiler() {
*
* @return the Kotlin runtime jar path
*/
- public Either getPathToRuntimeJar() {
- Optional value = delegate.getValue(SECTION, "runtime_jar");
+ Path getPathToStdlibJar() {
+ Path stdlib = getKotlinHome().resolve("kotlin-stdlib.jar");
+ if (Files.isRegularFile(stdlib)) {
+ return stdlib.normalize();
+ }
- if (value.isPresent()) {
- boolean isAbsolute = Paths.get(value.get()).isAbsolute();
- if (isAbsolute) {
- return Either.ofRight(delegate.getPath(SECTION, "runtime_jar", false).get().normalize());
- } else {
- return Either.ofLeft(delegate.getSourcePath(SECTION, "runtime_jar").get());
- }
+ stdlib = getKotlinHome().resolve(Paths.get("lib", "kotlin-stdlib.jar"));
+ if (Files.isRegularFile(stdlib)) {
+ return stdlib.normalize();
+ }
+
+ stdlib = getKotlinHome().resolve(Paths.get("libexec", "lib", "kotlin-stdlib.jar"));
+ if (Files.isRegularFile(stdlib)) {
+ return stdlib.normalize();
+ }
+
+ // Support for Kotlin < 1.1 ... kotlin-stdlib used to be kotlin-runtime.
+ stdlib = getKotlinHome().resolve("kotlin-runtime.jar");
+ if (Files.isRegularFile(stdlib)) {
+ return stdlib.normalize();
+ }
+
+ stdlib = getKotlinHome().resolve(Paths.get("lib", "kotlin-runtime.jar"));
+ if (Files.isRegularFile(stdlib)) {
+ return stdlib.normalize();
+ }
+
+ stdlib = getKotlinHome().resolve(Paths.get("libexec", "lib", "kotlin-runtime.jar"));
+ if (Files.isRegularFile(stdlib)) {
+ return stdlib.normalize();
}
- Path runtime = getKotlinHome().resolve("kotlin-runtime.jar");
- if (Files.isRegularFile(runtime)) {
- return Either.ofRight(runtime.toAbsolutePath().normalize());
+ throw new HumanReadableException(
+ "Could not resolve kotlin stdlib JAR location (kotlin home:" + getKotlinHome() + ").");
+ }
+
+ /**
+ * Get the path to the Kotlin compiler jar.
+ *
+ * @return the Kotlin compiler jar path
+ */
+ Path getPathToCompilerJar() {
+ Path compiler = getKotlinHome().resolve("kotlin-compiler.jar");
+ if (Files.isRegularFile(compiler)) {
+ return compiler.normalize();
}
- runtime = getKotlinHome().resolve(Paths.get("lib", "kotlin-runtime.jar"));
- if (Files.isRegularFile(runtime)) {
- return Either.ofRight(runtime.toAbsolutePath().normalize());
+ compiler = getKotlinHome().resolve(Paths.get("lib", "kotlin-compiler.jar"));
+ if (Files.isRegularFile(compiler)) {
+ return compiler.normalize();
}
- throw new HumanReadableException("Could not resolve kotlin runtime JAR location.");
+ compiler = getKotlinHome().resolve(Paths.get("libexec", "lib", "kotlin-compiler.jar"));
+ if (Files.isRegularFile(compiler)) {
+ return compiler.normalize();
+ }
+
+ throw new HumanReadableException(
+ "Could not resolve kotlin compiler JAR location (kotlin home:" + getKotlinHome() + ").");
+ }
+
+ /**
+ * Determine whether external Kotlin compilation is being forced. The default is internal
+ * (in-process) execution, but this can be overridden in .buckconfig by setting the "external"
+ * property to "true".
+ *
+ * @return true is external compilation is requested, false otherwise
+ */
+ private boolean isExternalCompilation() {
+ Optional value = delegate.getBoolean(SECTION, "external");
+ return value.orElse(false);
}
+ /**
+ * Find the Kotlin home (installation) directory by searching in this order:
+ *
+ *
+ * - If the "kotlin_home" directory is specified in .buckconfig then use it.
+ *
- Check the environment for a KOTLIN_HOME variable, if defined then use it.
+ *
- Resolve "kotlinc" with an ExecutableFinder, and if found then deduce the kotlin home
+ * directory from it.
+ *
+ *
+ * @return the Kotlin home path
+ */
private Path getKotlinHome() {
if (kotlinHome != null) {
return kotlinHome;
}
try {
- // Check the buck configuration for a specified kotlin compliler
- Optional value = delegate.getValue(SECTION, "compiler");
- boolean isAbsolute = (value.isPresent() && Paths.get(value.get()).isAbsolute());
- Optional compilerPath = delegate.getPath(SECTION, "compiler", !isAbsolute);
- if (compilerPath.isPresent()) {
- if (Files.isExecutable(compilerPath.get())) {
- kotlinHome = compilerPath.get().toRealPath().getParent().normalize();
- if (kotlinHome != null && kotlinHome.endsWith(Paths.get("bin"))) {
- kotlinHome = kotlinHome.getParent().normalize();
- }
- return kotlinHome;
+ // Check the buck configuration for a specified kotlin home
+ Optional value = delegate.getValue(SECTION, "kotlin_home");
+
+ if (value.isPresent()) {
+ boolean isAbsolute = Paths.get(value.get()).isAbsolute();
+ Optional homePath = delegate.getPath(SECTION, "kotlin_home", !isAbsolute);
+ if (homePath.isPresent() && Files.isDirectory(homePath.get())) {
+ return homePath.get().toRealPath().normalize();
} else {
throw new HumanReadableException(
- "Could not deduce kotlin home directory from path " + compilerPath.toString());
+ "Kotlin home directory (" + homePath + ") specified in .buckconfig was not found.");
}
} else {
// If the KOTLIN_HOME environment variable is specified we trust it
String home = delegate.getEnvironment().get("KOTLIN_HOME");
if (home != null) {
- kotlinHome = Paths.get(home).normalize();
- return kotlinHome;
+ return Paths.get(home).normalize();
} else {
// Lastly, we try to resolve from the system PATH
Optional compiler =
diff --git a/src/com/facebook/buck/jvm/kotlin/DefaultKotlinLibraryBuilder.java b/src/com/facebook/buck/jvm/kotlin/KotlinLibraryBuilder.java
similarity index 86%
rename from src/com/facebook/buck/jvm/kotlin/DefaultKotlinLibraryBuilder.java
rename to src/com/facebook/buck/jvm/kotlin/KotlinLibraryBuilder.java
index d7a65d5e66a..4e0e46d5a9c 100644
--- a/src/com/facebook/buck/jvm/kotlin/DefaultKotlinLibraryBuilder.java
+++ b/src/com/facebook/buck/jvm/kotlin/KotlinLibraryBuilder.java
@@ -16,6 +16,8 @@
package com.facebook.buck.jvm.kotlin;
+import static com.facebook.buck.jvm.java.BaseCompileToJarStepFactory.EMPTY_EXTRA_CLASSPATH;
+
import com.facebook.buck.jvm.java.CompileToJarStepFactory;
import com.facebook.buck.jvm.java.DefaultJavaLibraryBuilder;
import com.facebook.buck.jvm.java.JavaLibraryDescription;
@@ -26,11 +28,11 @@
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableList;
-public class DefaultKotlinLibraryBuilder extends DefaultJavaLibraryBuilder {
+public class KotlinLibraryBuilder extends DefaultJavaLibraryBuilder {
private final KotlinBuckConfig kotlinBuckConfig;
private ImmutableList extraKotlincArguments = ImmutableList.of();
- public DefaultKotlinLibraryBuilder(
+ KotlinLibraryBuilder(
TargetGraph targetGraph,
BuildRuleParams params,
BuildRuleResolver buildRuleResolver,
@@ -41,7 +43,7 @@ public DefaultKotlinLibraryBuilder(
}
@Override
- public DefaultKotlinLibraryBuilder setArgs(JavaLibraryDescription.CoreArg args) {
+ public KotlinLibraryBuilder setArgs(JavaLibraryDescription.CoreArg args) {
super.setArgs(args);
KotlinLibraryDescription.CoreArg kotlinArgs = (KotlinLibraryDescription.CoreArg) args;
@@ -58,8 +60,9 @@ protected class BuilderHelper extends DefaultJavaLibraryBuilder.BuilderHelper {
@Override
protected CompileToJarStepFactory buildCompileStepFactory() {
return new KotlincToJarStepFactory(
- Preconditions.checkNotNull(kotlinBuckConfig).getKotlinCompiler().get(),
- extraKotlincArguments);
+ Preconditions.checkNotNull(kotlinBuckConfig).getKotlinc(),
+ extraKotlincArguments,
+ EMPTY_EXTRA_CLASSPATH);
}
}
}
diff --git a/src/com/facebook/buck/jvm/kotlin/KotlinLibraryDescription.java b/src/com/facebook/buck/jvm/kotlin/KotlinLibraryDescription.java
index 2f3627fb276..dc04eca1b24 100644
--- a/src/com/facebook/buck/jvm/kotlin/KotlinLibraryDescription.java
+++ b/src/com/facebook/buck/jvm/kotlin/KotlinLibraryDescription.java
@@ -101,8 +101,8 @@ public BuildRule createBuildRule(
}
}
- DefaultKotlinLibraryBuilder defaultKotlinLibraryBuilder =
- new DefaultKotlinLibraryBuilder(targetGraph, params, resolver, cellRoots, kotlinBuckConfig)
+ KotlinLibraryBuilder defaultKotlinLibraryBuilder =
+ new KotlinLibraryBuilder(targetGraph, params, resolver, cellRoots, kotlinBuckConfig)
.setArgs(args);
// We know that the flavour we're being asked to create is valid, since the check is done when
@@ -116,6 +116,7 @@ public BuildRule createBuildRule(
if (!flavors.contains(JavaLibrary.MAVEN_JAR)) {
return defaultKotlinLibrary;
} else {
+ resolver.addToIndex(defaultKotlinLibrary);
return MavenUberJar.create(
defaultKotlinLibrary,
Preconditions.checkNotNull(paramsWithMavenFlavor),
diff --git a/src/com/facebook/buck/jvm/kotlin/KotlinTest.java b/src/com/facebook/buck/jvm/kotlin/KotlinTest.java
deleted file mode 100644
index d247edd721d..00000000000
--- a/src/com/facebook/buck/jvm/kotlin/KotlinTest.java
+++ /dev/null
@@ -1,79 +0,0 @@
-/*
- * Copyright 2016-present Facebook, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License"); you may
- * not use this file except in compliance with the License. You may obtain
- * a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- */
-
-package com.facebook.buck.jvm.kotlin;
-
-import com.facebook.buck.jvm.java.ForkMode;
-import com.facebook.buck.jvm.java.JavaLibrary;
-import com.facebook.buck.jvm.java.JavaRuntimeLauncher;
-import com.facebook.buck.jvm.java.JavaTest;
-import com.facebook.buck.jvm.java.TestType;
-import com.facebook.buck.model.Either;
-import com.facebook.buck.rules.BuildRuleParams;
-import com.facebook.buck.rules.SourcePath;
-import com.facebook.buck.rules.SourcePathResolver;
-import com.facebook.buck.rules.args.Arg;
-import com.google.common.collect.ImmutableMap;
-import com.google.common.collect.ImmutableSet;
-import java.nio.file.Path;
-import java.util.List;
-import java.util.Map;
-import java.util.Optional;
-import java.util.Set;
-import java.util.logging.Level;
-
-@SuppressWarnings("PMD.TestClassWithoutTestCases")
-public class KotlinTest extends JavaTest {
-
- public KotlinTest(
- BuildRuleParams params,
- SourcePathResolver pathResolver,
- JavaLibrary compiledTestsLibrary,
- ImmutableSet> additionalClasspathEntries,
- Set labels,
- Set contacts,
- TestType testType,
- JavaRuntimeLauncher javaRuntimeLauncher,
- List vmArgs,
- Map nativeLibsEnvironment,
- Optional testRuleTimeoutMs,
- Optional testCaseTimeoutMs,
- ImmutableMap env,
- boolean runTestSeparately,
- ForkMode forkMode,
- Optional stdOutLogLevel,
- Optional stdErrLogLevel) {
-
- super(
- params,
- pathResolver,
- compiledTestsLibrary,
- additionalClasspathEntries,
- labels,
- contacts,
- testType,
- javaRuntimeLauncher,
- vmArgs,
- nativeLibsEnvironment,
- testRuleTimeoutMs,
- testCaseTimeoutMs,
- env,
- runTestSeparately,
- forkMode,
- stdOutLogLevel,
- stdErrLogLevel);
- }
-}
diff --git a/src/com/facebook/buck/jvm/kotlin/KotlinTestDescription.java b/src/com/facebook/buck/jvm/kotlin/KotlinTestDescription.java
index 6d1f3eb9134..bb2285b6ca9 100644
--- a/src/com/facebook/buck/jvm/kotlin/KotlinTestDescription.java
+++ b/src/com/facebook/buck/jvm/kotlin/KotlinTestDescription.java
@@ -16,10 +16,10 @@
package com.facebook.buck.jvm.kotlin;
+import com.facebook.buck.jvm.java.DefaultJavaLibrary;
import com.facebook.buck.jvm.java.DefaultJavaLibraryBuilder;
import com.facebook.buck.jvm.java.ForkMode;
import com.facebook.buck.jvm.java.HasJavaAbi;
-import com.facebook.buck.jvm.java.JavaLibrary;
import com.facebook.buck.jvm.java.JavaOptions;
import com.facebook.buck.jvm.java.JavaTest;
import com.facebook.buck.jvm.java.JavacOptions;
@@ -34,7 +34,6 @@
import com.facebook.buck.rules.CellPathResolver;
import com.facebook.buck.rules.Description;
import com.facebook.buck.rules.ImplicitDepsInferringDescription;
-import com.facebook.buck.rules.SourcePath;
import com.facebook.buck.rules.SourcePathResolver;
import com.facebook.buck.rules.SourcePathRuleFinder;
import com.facebook.buck.rules.TargetGraph;
@@ -52,7 +51,6 @@
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ImmutableSortedSet;
import com.google.common.collect.Maps;
-import java.nio.file.Path;
import java.util.Optional;
import java.util.logging.Level;
import org.immutables.value.Value;
@@ -97,7 +95,7 @@ public BuildRule createBuildRule(
params.withAppendedFlavor(JavaTest.COMPILED_TESTS_LIBRARY_FLAVOR);
DefaultJavaLibraryBuilder defaultJavaLibraryBuilder =
- new DefaultKotlinLibraryBuilder(
+ new KotlinLibraryBuilder(
targetGraph, testsLibraryParams, resolver, cellRoots, kotlinBuckConfig)
.setArgs(args)
.setGeneratedSourceFolder(templateJavacOptions.getGeneratedSourceFolderName());
@@ -106,20 +104,20 @@ public BuildRule createBuildRule(
return defaultJavaLibraryBuilder.buildAbi();
}
- JavaLibrary testsLibrary = resolver.addToIndex(defaultJavaLibraryBuilder.build());
+ DefaultJavaLibrary testsLibrary = resolver.addToIndex(defaultJavaLibraryBuilder.build());
SourcePathRuleFinder ruleFinder = new SourcePathRuleFinder(resolver);
SourcePathResolver pathResolver = new SourcePathResolver(ruleFinder);
Function toMacroArgFunction =
MacroArg.toMacroArgFunction(MACRO_HANDLER, params.getBuildTarget(), cellRoots, resolver);
- return new KotlinTest(
+ return new JavaTest(
params.copyReplacingDeclaredAndExtraDeps(
Suppliers.ofInstance(ImmutableSortedSet.of(testsLibrary)),
Suppliers.ofInstance(ImmutableSortedSet.of())),
pathResolver,
testsLibrary,
- ImmutableSet.>of(kotlinBuckConfig.getPathToRuntimeJar()),
+ ImmutableSet.of(Either.ofRight(kotlinBuckConfig.getPathToStdlibJar())),
args.getLabels(),
args.getContacts(),
args.getTestType().orElse(TestType.JUNIT),
diff --git a/src/com/facebook/buck/jvm/kotlin/Kotlinc.java b/src/com/facebook/buck/jvm/kotlin/Kotlinc.java
new file mode 100644
index 00000000000..ef764133e11
--- /dev/null
+++ b/src/com/facebook/buck/jvm/kotlin/Kotlinc.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2014-present Facebook, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may
+ * not use this file except in compliance with the License. You may obtain
+ * a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.facebook.buck.jvm.kotlin;
+
+import com.facebook.buck.io.ProjectFilesystem;
+import com.facebook.buck.model.BuildTarget;
+import com.facebook.buck.rules.Tool;
+import com.facebook.buck.step.ExecutionContext;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSortedSet;
+import java.nio.file.Path;
+import java.util.Optional;
+
+public interface Kotlinc extends Tool {
+
+ KotlincVersion getVersion();
+
+ int buildWithClasspath(
+ ExecutionContext context,
+ BuildTarget invokingRule,
+ ImmutableList options,
+ ImmutableSortedSet kotlinSourceFilePaths,
+ Path pathToSrcsList,
+ Optional workingDirectory,
+ ProjectFilesystem fileSystem)
+ throws InterruptedException;
+
+ String getDescription(
+ ImmutableList options,
+ ImmutableSortedSet kotlinSourceFilePaths,
+ Path pathToSrcsList);
+
+ String getShortName();
+}
diff --git a/src/com/facebook/buck/jvm/kotlin/KotlincStep.java b/src/com/facebook/buck/jvm/kotlin/KotlincStep.java
index 4df302b1569..d7783548ab5 100644
--- a/src/com/facebook/buck/jvm/kotlin/KotlincStep.java
+++ b/src/com/facebook/buck/jvm/kotlin/KotlincStep.java
@@ -13,73 +13,158 @@
* License for the specific language governing permissions and limitations
* under the License.
*/
-// Copyright 2004-present Facebook. All Rights Reserved.
-
package com.facebook.buck.jvm.kotlin;
import static com.google.common.collect.Iterables.transform;
import com.facebook.buck.io.ProjectFilesystem;
-import com.facebook.buck.rules.SourcePathResolver;
-import com.facebook.buck.rules.Tool;
-import com.facebook.buck.shell.ShellStep;
+import com.facebook.buck.model.BuildTarget;
import com.facebook.buck.step.ExecutionContext;
+import com.facebook.buck.step.Step;
+import com.facebook.buck.step.StepExecutionResult;
+import com.facebook.buck.util.CapturingPrintStream;
+import com.facebook.buck.util.Verbosity;
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.base.Charsets;
import com.google.common.base.Joiner;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSortedSet;
import java.io.File;
+import java.io.IOException;
import java.nio.file.Path;
+import java.util.Optional;
-public class KotlincStep extends ShellStep {
-
+public class KotlincStep implements Step {
private static final String CLASSPATH_FLAG = "-cp";
private static final String DESTINATION_FLAG = "-d";
private static final String INCLUDE_RUNTIME_FLAG = "-include-runtime";
- private final Tool kotlinc;
- private final SourcePathResolver resolver;
- private final ImmutableSortedSet declaredClassPathEntries;
+ private final Kotlinc kotlinc;
+ private final ImmutableSortedSet combinedClassPathEntries;
private final Path outputDirectory;
private final ImmutableList extraArguments;
private final ImmutableSortedSet sourceFilePaths;
+ private final ProjectFilesystem filesystem;
+ private final Path pathToSrcsList;
+ private final BuildTarget invokingRule;
KotlincStep(
- Tool kotlinc,
- ImmutableList extraArguments,
- SourcePathResolver resolver,
+ BuildTarget invokingRule,
Path outputDirectory,
ImmutableSortedSet sourceFilePaths,
- ImmutableSortedSet declaredClassPathEntries,
+ Path pathToSrcsList,
+ ImmutableSortedSet combinedClassPathEntries,
+ Kotlinc kotlinc,
+ ImmutableList extraArguments,
ProjectFilesystem filesystem) {
- super(filesystem.getRootPath());
- this.kotlinc = kotlinc;
- this.resolver = resolver;
- this.declaredClassPathEntries = declaredClassPathEntries;
+ this.invokingRule = invokingRule;
this.outputDirectory = outputDirectory;
- this.extraArguments = extraArguments;
this.sourceFilePaths = sourceFilePaths;
+ this.pathToSrcsList = pathToSrcsList;
+ this.kotlinc = kotlinc;
+ this.combinedClassPathEntries = combinedClassPathEntries;
+ this.extraArguments = extraArguments;
+ this.filesystem = filesystem;
}
@Override
public String getShortName() {
- return Joiner.on(" ").join(kotlinc.getCommandPrefix(resolver));
+ return getKotlinc().getShortName();
+ }
+
+ @Override
+ public StepExecutionResult execute(ExecutionContext context)
+ throws IOException, InterruptedException {
+ Verbosity verbosity =
+ context.getVerbosity().isSilent() ? Verbosity.STANDARD_INFORMATION : context.getVerbosity();
+
+ try (CapturingPrintStream stdout = new CapturingPrintStream();
+ CapturingPrintStream stderr = new CapturingPrintStream();
+ ExecutionContext firstOrderContext =
+ context.createSubContext(stdout, stderr, Optional.of(verbosity))) {
+
+ int declaredDepsBuildResult =
+ kotlinc.buildWithClasspath(
+ firstOrderContext,
+ invokingRule,
+ getOptions(context, combinedClassPathEntries),
+ sourceFilePaths,
+ pathToSrcsList,
+ Optional.empty(),
+ filesystem);
+
+ String firstOrderStderr = stderr.getContentsAsString(Charsets.UTF_8);
+ Optional returnedStderr;
+ if (declaredDepsBuildResult != 0) {
+ returnedStderr = Optional.of(firstOrderStderr);
+ } else {
+ returnedStderr = Optional.empty();
+ }
+ return StepExecutionResult.of(declaredDepsBuildResult, returnedStderr);
+ }
+ }
+
+ @VisibleForTesting
+ Kotlinc getKotlinc() {
+ return kotlinc;
}
@Override
- protected ImmutableList getShellCommandInternal(ExecutionContext context) {
- final ImmutableList.Builder command =
- ImmutableList.builder().addAll(kotlinc.getCommandPrefix(resolver));
-
- String classpath =
- Joiner.on(File.pathSeparator).join(transform(declaredClassPathEntries, Object::toString));
- command
- .add(INCLUDE_RUNTIME_FLAG)
- .add(CLASSPATH_FLAG)
- .add(classpath.isEmpty() ? "''" : classpath)
- .add(DESTINATION_FLAG)
- .add(outputDirectory.toString());
-
- command.addAll(extraArguments).addAll(transform(sourceFilePaths, Object::toString));
- return command.build();
+ public String getDescription(ExecutionContext context) {
+ return getKotlinc()
+ .getDescription(
+ getOptions(context, getClasspathEntries()), sourceFilePaths, pathToSrcsList);
+ }
+
+ /**
+ * Returns a list of command-line options to pass to javac. These options reflect the
+ * configuration of this javac command.
+ *
+ * @param context the ExecutionContext with in which javac will run
+ * @return list of String command-line options.
+ */
+ @VisibleForTesting
+ ImmutableList getOptions(
+ ExecutionContext context, ImmutableSortedSet buildClasspathEntries) {
+ return getOptions(filesystem, outputDirectory, buildClasspathEntries);
+ }
+
+ private ImmutableList getOptions(
+ ProjectFilesystem filesystem,
+ Path outputDirectory,
+ ImmutableSortedSet buildClasspathEntries) {
+
+ final ImmutableList.Builder builder = ImmutableList.builder();
+
+ builder.add(INCLUDE_RUNTIME_FLAG);
+
+ if (!buildClasspathEntries.isEmpty()) {
+ builder.add(
+ CLASSPATH_FLAG,
+ Joiner.on(File.pathSeparator)
+ .join(
+ transform(
+ buildClasspathEntries,
+ path -> filesystem.resolve(path).toAbsolutePath().toString())));
+ }
+
+ builder.add(DESTINATION_FLAG, filesystem.resolve(outputDirectory).toString());
+
+ if (!extraArguments.isEmpty()) {
+ builder.addAll(extraArguments);
+ }
+
+ return builder.build();
+ }
+
+ /** @return The classpath entries used to invoke javac. */
+ @VisibleForTesting
+ ImmutableSortedSet getClasspathEntries() {
+ return combinedClassPathEntries;
+ }
+
+ @VisibleForTesting
+ ImmutableSortedSet getSrcs() {
+ return sourceFilePaths;
}
}
diff --git a/src/com/facebook/buck/jvm/kotlin/KotlincToJarStepFactory.java b/src/com/facebook/buck/jvm/kotlin/KotlincToJarStepFactory.java
index cd42d70e79f..98c0cd06899 100644
--- a/src/com/facebook/buck/jvm/kotlin/KotlincToJarStepFactory.java
+++ b/src/com/facebook/buck/jvm/kotlin/KotlincToJarStepFactory.java
@@ -35,16 +35,12 @@
public class KotlincToJarStepFactory extends BaseCompileToJarStepFactory {
- private final Tool kotlinc;
+ private final Kotlinc kotlinc;
private final ImmutableList extraArguments;
private final Function> extraClassPath;
- public KotlincToJarStepFactory(Tool kotlinc, ImmutableList extraArguments) {
- this(kotlinc, extraArguments, EMPTY_EXTRA_CLASSPATH);
- }
-
public KotlincToJarStepFactory(
- Tool kotlinc,
+ Kotlinc kotlinc,
ImmutableList extraArguments,
Function> extraClassPath) {
this.kotlinc = kotlinc;
@@ -54,7 +50,7 @@ public KotlincToJarStepFactory(
@Override
public void createCompileStep(
- BuildContext context,
+ BuildContext buildContext,
ImmutableSortedSet sourceFilePaths,
BuildTarget invokingRule,
SourcePathResolver resolver,
@@ -68,18 +64,21 @@ public void createCompileStep(
/* out params */
ImmutableList.Builder steps,
BuildableContext buildableContext) {
+
steps.add(
new KotlincStep(
- kotlinc,
- extraArguments,
- resolver,
+ invokingRule,
outputDirectory,
sourceFilePaths,
+ pathToSrcsList,
ImmutableSortedSet.naturalOrder()
.addAll(
- Optional.ofNullable(extraClassPath.apply(context)).orElse(ImmutableList.of()))
+ Optional.ofNullable(extraClassPath.apply(buildContext))
+ .orElse(ImmutableList.of()))
.addAll(declaredClasspathEntries)
.build(),
+ kotlinc,
+ extraArguments,
filesystem));
}
diff --git a/test/com/facebook/buck/ide/intellij/DefaultIjModuleFactoryTest.java b/test/com/facebook/buck/ide/intellij/DefaultIjModuleFactoryTest.java
index aa3825dd513..30c5f22bf7a 100644
--- a/test/com/facebook/buck/ide/intellij/DefaultIjModuleFactoryTest.java
+++ b/test/com/facebook/buck/ide/intellij/DefaultIjModuleFactoryTest.java
@@ -48,7 +48,7 @@
import com.facebook.buck.jvm.java.JavaLibraryBuilder;
import com.facebook.buck.jvm.java.JavaTestBuilder;
import com.facebook.buck.jvm.java.JvmLibraryArg;
-import com.facebook.buck.jvm.kotlin.KotlinLibraryBuilder;
+import com.facebook.buck.jvm.kotlin.FauxKotlinLibraryBuilder;
import com.facebook.buck.model.BuildTarget;
import com.facebook.buck.model.BuildTargetFactory;
import com.facebook.buck.rules.BuildRuleResolver;
@@ -376,7 +376,7 @@ public void testKotlinLibrary() {
IjModuleFactory factory = createIjModuleFactory();
TargetNode, ?> kotlinLib =
- KotlinLibraryBuilder.createBuilder(
+ FauxKotlinLibraryBuilder.createBuilder(
BuildTargetFactory.newInstance("//kotlin/com/example/base:base"))
.addSrc(Paths.get("kotlin/com/example/base/File.kt"))
.build();
diff --git a/test/com/facebook/buck/jvm/kotlin/KotlinLibraryBuilder.java b/test/com/facebook/buck/jvm/kotlin/FauxKotlinLibraryBuilder.java
similarity index 82%
rename from test/com/facebook/buck/jvm/kotlin/KotlinLibraryBuilder.java
rename to test/com/facebook/buck/jvm/kotlin/FauxKotlinLibraryBuilder.java
index dc1d53fec5a..47120182b48 100644
--- a/test/com/facebook/buck/jvm/kotlin/KotlinLibraryBuilder.java
+++ b/test/com/facebook/buck/jvm/kotlin/FauxKotlinLibraryBuilder.java
@@ -26,29 +26,29 @@
import com.google.common.hash.HashCode;
import java.nio.file.Path;
-public class KotlinLibraryBuilder
+public class FauxKotlinLibraryBuilder
extends AbstractNodeBuilder<
KotlinLibraryDescriptionArg.Builder, KotlinLibraryDescriptionArg, KotlinLibraryDescription,
BuildRule> {
private final ProjectFilesystem projectFilesystem;
- protected KotlinLibraryBuilder(
+ protected FauxKotlinLibraryBuilder(
BuildTarget target, ProjectFilesystem projectFilesystem, HashCode hashCode) {
super(new KotlinLibraryDescription(null), target, projectFilesystem, hashCode);
this.projectFilesystem = projectFilesystem;
}
- public static KotlinLibraryBuilder createBuilder(BuildTarget target) {
- return new KotlinLibraryBuilder(target, new FakeProjectFilesystem(), null);
+ public static FauxKotlinLibraryBuilder createBuilder(BuildTarget target) {
+ return new FauxKotlinLibraryBuilder(target, new FakeProjectFilesystem(), null);
}
- public KotlinLibraryBuilder addSrc(SourcePath path) {
+ public FauxKotlinLibraryBuilder addSrc(SourcePath path) {
getArgForPopulating().addSrcs(path);
return this;
}
- public KotlinLibraryBuilder addSrc(Path path) {
+ public FauxKotlinLibraryBuilder addSrc(Path path) {
return addSrc(new PathSourcePath(projectFilesystem, path));
}
}
diff --git a/test/com/facebook/buck/jvm/kotlin/KotlinBuckConfigTest.java b/test/com/facebook/buck/jvm/kotlin/KotlinBuckConfigTest.java
index c46c0c5cf60..d3d17e3baad 100644
--- a/test/com/facebook/buck/jvm/kotlin/KotlinBuckConfigTest.java
+++ b/test/com/facebook/buck/jvm/kotlin/KotlinBuckConfigTest.java
@@ -17,15 +17,14 @@
import static java.io.File.pathSeparator;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
import com.facebook.buck.cli.BuckConfig;
import com.facebook.buck.cli.FakeBuckConfig;
import com.facebook.buck.io.MoreFiles;
import com.facebook.buck.io.ProjectFilesystem;
-import com.facebook.buck.rules.PathSourcePath;
-import com.facebook.buck.testutil.integration.ProjectWorkspace;
import com.facebook.buck.testutil.integration.TemporaryPaths;
-import com.facebook.buck.testutil.integration.TestDataHelper;
import com.facebook.buck.util.HumanReadableException;
import com.google.common.collect.ImmutableMap;
import java.io.IOException;
@@ -40,67 +39,107 @@ public class KotlinBuckConfigTest {
@Rule public TemporaryPaths tmp = new TemporaryPaths();
- private ProjectWorkspace workspace;
+ private Path testDataDirectory;
@Before
public void setUp() throws InterruptedException, IOException {
KotlinTestAssumptions.assumeUnixLike();
- workspace = TestDataHelper.createProjectWorkspaceForScenario(this, "kotlin_compiler_test", tmp);
- workspace.setUp();
+ tmp.newFolder("faux_kotlin_home", "bin");
+ tmp.newFolder("faux_kotlin_home", "libexec", "bin");
+ tmp.newFolder("faux_kotlin_home", "libexec", "lib");
+ tmp.newExecutableFile("faux_kotlin_home/bin/kotlinc");
+ tmp.newExecutableFile("faux_kotlin_home/libexec/bin/kotlinc");
+ tmp.newExecutableFile("faux_kotlin_home/libexec/lib/kotlin-compiler.jar");
+ tmp.newExecutableFile("faux_kotlin_home/libexec/lib/kotlin-stdlib.jar");
+
+ testDataDirectory = tmp.getRoot();
}
@Test
- public void testFindsKotlinCompilerInPath() throws HumanReadableException, IOException {
+ public void testFindsKotlinCompilerInPathLibexec() throws HumanReadableException, IOException {
// Get faux kotlinc binary location in project
- Path kotlinPath = workspace.resolve("bin");
- Path kotlinCompiler = kotlinPath.resolve("kotlinc");
+ Path kotlinHome = testDataDirectory.resolve("faux_kotlin_home/libexec/bin").normalize();
+ Path kotlinCompiler = kotlinHome.resolve("kotlinc");
MoreFiles.makeExecutable(kotlinCompiler);
BuckConfig buckConfig =
FakeBuckConfig.builder()
+ .setSections(ImmutableMap.of("kotlin", ImmutableMap.of("external", "true")))
.setEnvironment(
ImmutableMap.of(
- "PATH", kotlinPath.toString() + pathSeparator + System.getenv("PATH")))
+ "PATH", kotlinHome.toString() + pathSeparator + System.getenv("PATH")))
.build();
KotlinBuckConfig kotlinBuckConfig = new KotlinBuckConfig(buckConfig);
- String command = kotlinBuckConfig.getKotlinCompiler().get().getCommandPrefix(null).get(0);
+ String command = kotlinBuckConfig.getPathToCompilerBinary().toString();
assertEquals(command, kotlinCompiler.toString());
}
@Test
- public void testFindsKotlinCompilerInHome() throws HumanReadableException, IOException {
+ public void testFindsKotlinCompilerInPathBin() throws HumanReadableException, IOException {
// Get faux kotlinc binary location in project
- Path kotlinCompiler = workspace.resolve("bin").resolve("kotlinc");
+ Path kotlinHome = testDataDirectory.resolve("faux_kotlin_home/bin").normalize();
+ Path kotlinCompiler = kotlinHome.resolve("kotlinc");
MoreFiles.makeExecutable(kotlinCompiler);
BuckConfig buckConfig =
FakeBuckConfig.builder()
+ .setSections(ImmutableMap.of("kotlin", ImmutableMap.of("external", "true")))
.setEnvironment(
- ImmutableMap.of("KOTLIN_HOME", workspace.getPath(".").normalize().toString()))
+ ImmutableMap.of(
+ "PATH", kotlinHome.toString() + pathSeparator + System.getenv("PATH")))
.build();
KotlinBuckConfig kotlinBuckConfig = new KotlinBuckConfig(buckConfig);
- String command = kotlinBuckConfig.getKotlinCompiler().get().getCommandPrefix(null).get(0);
+ String command = kotlinBuckConfig.getPathToCompilerBinary().toString();
assertEquals(command, kotlinCompiler.toString());
}
@Test
- public void testFindsKotlinCompilerInConfigWithAbsolutePath()
+ public void testFindsKotlinCompilerInHomeEnvironment()
throws HumanReadableException, IOException {
// Get faux kotlinc binary location in project
- Path kotlinCompiler = workspace.resolve("bin").resolve("kotlinc");
+ Path kotlinHome = testDataDirectory.resolve("faux_kotlin_home").normalize();
+ Path kotlinCompiler = kotlinHome.resolve("bin").resolve("kotlinc");
MoreFiles.makeExecutable(kotlinCompiler);
BuckConfig buckConfig =
FakeBuckConfig.builder()
- .setSections(
- ImmutableMap.of("kotlin", ImmutableMap.of("compiler", kotlinCompiler.toString())))
+ .setSections(ImmutableMap.of("kotlin", ImmutableMap.of("external", "true")))
+ .setEnvironment(
+ ImmutableMap.of(
+ "KOTLIN_HOME",
+ testDataDirectory.resolve("faux_kotlin_home").toAbsolutePath().toString()))
+ .build();
+
+ KotlinBuckConfig kotlinBuckConfig = new KotlinBuckConfig(buckConfig);
+ String command = kotlinBuckConfig.getPathToCompilerBinary().toString();
+ assertEquals(command, kotlinCompiler.toString());
+ }
+
+ @Test
+ public void testFindsKotlinCompilerInHomeEnvironment2()
+ throws HumanReadableException, IOException {
+ // Get faux kotlinc binary location in project
+ Path kotlinHome = testDataDirectory.resolve("faux_kotlin_home/libexec").normalize();
+ Path kotlinCompiler = kotlinHome.resolve("bin").resolve("kotlinc");
+ MoreFiles.makeExecutable(kotlinCompiler);
+
+ BuckConfig buckConfig =
+ FakeBuckConfig.builder()
+ .setSections(ImmutableMap.of("kotlin", ImmutableMap.of("external", "true")))
+ .setEnvironment(
+ ImmutableMap.of(
+ "KOTLIN_HOME",
+ testDataDirectory
+ .resolve("faux_kotlin_home/libexec/bin")
+ .toAbsolutePath()
+ .toString()))
.build();
KotlinBuckConfig kotlinBuckConfig = new KotlinBuckConfig(buckConfig);
- String command = kotlinBuckConfig.getKotlinCompiler().get().getCommandPrefix(null).get(0);
+ String command = kotlinBuckConfig.getPathToCompilerBinary().toString();
assertEquals(command, kotlinCompiler.toString());
}
@@ -108,77 +147,186 @@ public void testFindsKotlinCompilerInConfigWithAbsolutePath()
public void testFindsKotlinCompilerInConfigWithRelativePath()
throws HumanReadableException, InterruptedException, IOException {
// Get faux kotlinc binary location in project
- Path kotlinCompiler = workspace.resolve("bin").resolve("kotlinc");
+ Path kotlinHome = testDataDirectory.resolve("faux_kotlin_home").normalize();
+ Path kotlinCompiler = kotlinHome.resolve("bin").resolve("kotlinc");
MoreFiles.makeExecutable(kotlinCompiler);
- ProjectFilesystem filesystem = new ProjectFilesystem(workspace.resolve("."));
+ ProjectFilesystem filesystem = new ProjectFilesystem(testDataDirectory.resolve("."));
BuckConfig buckConfig =
FakeBuckConfig.builder()
.setFilesystem(filesystem)
- .setSections(ImmutableMap.of("kotlin", ImmutableMap.of("compiler", "bin/kotlinc")))
+ .setSections(
+ ImmutableMap.of(
+ "kotlin",
+ ImmutableMap.of("kotlin_home", "./faux_kotlin_home", "external", "true")))
.build();
KotlinBuckConfig kotlinBuckConfig = new KotlinBuckConfig(buckConfig);
- String command = kotlinBuckConfig.getKotlinCompiler().get().getCommandPrefix(null).get(0);
+ String command = kotlinBuckConfig.getPathToCompilerBinary().toString();
assertEquals(command, kotlinCompiler.toString());
}
@Test
- public void testFindsKotlinRuntimeLibraryInPath() throws IOException {
+ public void testFindsKotlinCompilerJarInConfigWithAbsolutePath()
+ throws HumanReadableException, InterruptedException, IOException {
+
+ Path kotlinRuntime =
+ testDataDirectory
+ .resolve("faux_kotlin_home")
+ .resolve("libexec")
+ .resolve("lib")
+ .resolve("kotlin-compiler.jar");
+
+ BuckConfig buckConfig =
+ FakeBuckConfig.builder()
+ .setSections(
+ ImmutableMap.of(
+ "kotlin",
+ ImmutableMap.of(
+ "kotlin_home",
+ testDataDirectory.resolve("faux_kotlin_home").toAbsolutePath().toString(),
+ "external",
+ "false")))
+ .build();
+
+ KotlinBuckConfig kotlinBuckConfig = new KotlinBuckConfig(buckConfig);
+ Path compilerJar = kotlinBuckConfig.getPathToCompilerJar();
+ assertEquals(kotlinRuntime, compilerJar);
+ }
+
+ @Test
+ public void testFindsKotlinCompilerJarInConfigWithAbsolutePath2()
+ throws HumanReadableException, InterruptedException, IOException {
+
+ Path kotlinRuntime =
+ testDataDirectory
+ .resolve("faux_kotlin_home")
+ .resolve("libexec")
+ .resolve("lib")
+ .resolve("kotlin-compiler.jar");
+
+ BuckConfig buckConfig =
+ FakeBuckConfig.builder()
+ .setSections(
+ ImmutableMap.of(
+ "kotlin",
+ ImmutableMap.of(
+ "kotlin_home",
+ testDataDirectory
+ .resolve("faux_kotlin_home")
+ .resolve("libexec")
+ .toAbsolutePath()
+ .toString(),
+ "external",
+ "false")))
+ .build();
+
+ KotlinBuckConfig kotlinBuckConfig = new KotlinBuckConfig(buckConfig);
+ Path compilerJar = kotlinBuckConfig.getPathToCompilerJar();
+ assertEquals(kotlinRuntime, compilerJar);
+ }
+
+ @Test
+ public void testFindsKotlinCompilerLibraryInPath() throws IOException {
// Get faux kotlinc binary location in project
- Path kotlinPath = workspace.resolve("bin");
- Path kotlinCompiler = kotlinPath.resolve("kotlinc");
+ Path kotlinHome = testDataDirectory.resolve("faux_kotlin_home").normalize();
+ Path kotlinCompiler = kotlinHome.resolve("libexec").resolve("bin").resolve("kotlinc");
MoreFiles.makeExecutable(kotlinCompiler);
BuckConfig buckConfig =
FakeBuckConfig.builder()
+ .setSections(ImmutableMap.of("kotlin", ImmutableMap.of("external", "true")))
.setEnvironment(
ImmutableMap.of(
- "PATH", kotlinPath.toString() + pathSeparator + System.getenv("PATH")))
+ "PATH",
+ kotlinCompiler.getParent().toString() + pathSeparator + System.getenv("PATH")))
.build();
KotlinBuckConfig kotlinBuckConfig = new KotlinBuckConfig(buckConfig);
- Path runtimeJar = kotlinBuckConfig.getPathToRuntimeJar().getRight();
+ Path compilerJar = kotlinBuckConfig.getPathToCompilerJar();
Assert.assertThat(
- runtimeJar.toString(),
- Matchers.containsString(workspace.getPath(".").normalize().toString()));
+ compilerJar.toString(), Matchers.containsString(testDataDirectory.toString()));
}
@Test
- public void testFindsKotlinRuntimeInConfigWithAbsolutePath()
- throws HumanReadableException, IOException {
+ public void testFindsKotlinStdlibJarInConfigWithAbsolutePath()
+ throws HumanReadableException, InterruptedException, IOException {
- Path kotlinRuntime = workspace.resolve("lib").resolve("kotlin-runtime.jar");
+ Path kotlinRuntime =
+ testDataDirectory
+ .resolve("faux_kotlin_home")
+ .resolve("libexec")
+ .resolve("lib")
+ .resolve("kotlin-stdlib.jar");
BuckConfig buckConfig =
FakeBuckConfig.builder()
.setSections(
- ImmutableMap.of("kotlin", ImmutableMap.of("runtime_jar", kotlinRuntime.toString())))
- .setEnvironment(
- ImmutableMap.of("KOTLIN_HOME", workspace.getPath(".").normalize().toString()))
+ ImmutableMap.of(
+ "kotlin",
+ ImmutableMap.of(
+ "kotlin_home",
+ testDataDirectory.resolve("faux_kotlin_home").toAbsolutePath().toString(),
+ "external",
+ "false")))
.build();
KotlinBuckConfig kotlinBuckConfig = new KotlinBuckConfig(buckConfig);
- Path runtimeJar = kotlinBuckConfig.getPathToRuntimeJar().getRight();
- assertEquals(runtimeJar.toString(), kotlinRuntime.toString());
+ Path runtimeJar = kotlinBuckConfig.getPathToStdlibJar();
+ assertEquals(kotlinRuntime, runtimeJar);
}
@Test
- public void testFindsKotlinRuntimeInConfigWithRelativePath()
+ public void testFindsKotlinStdlibJarInConfigWithAbsolutePath2()
throws HumanReadableException, InterruptedException, IOException {
- ProjectFilesystem filesystem = new ProjectFilesystem(workspace.resolve("."));
+ Path kotlinRuntime =
+ testDataDirectory
+ .resolve("faux_kotlin_home")
+ .resolve("libexec")
+ .resolve("lib")
+ .resolve("kotlin-stdlib.jar");
+
BuckConfig buckConfig =
FakeBuckConfig.builder()
- .setFilesystem(filesystem)
.setSections(
- ImmutableMap.of("kotlin", ImmutableMap.of("runtime_jar", "lib/kotlin-runtime.jar")))
- .setEnvironment(
- ImmutableMap.of("KOTLIN_HOME", workspace.getPath(".").normalize().toString()))
+ ImmutableMap.of(
+ "kotlin",
+ ImmutableMap.of(
+ "kotlin_home",
+ testDataDirectory
+ .resolve("faux_kotlin_home")
+ .resolve("libexec")
+ .resolve("lib")
+ .toAbsolutePath()
+ .toString(),
+ "external",
+ "false")))
+ .build();
+
+ KotlinBuckConfig kotlinBuckConfig = new KotlinBuckConfig(buckConfig);
+ Path runtimeJar = kotlinBuckConfig.getPathToStdlibJar();
+ assertEquals(kotlinRuntime, runtimeJar);
+ }
+
+ @Test
+ public void testFindsKotlinStdlibJarInConfigWithRelativePath()
+ throws HumanReadableException, InterruptedException, IOException {
+
+ BuckConfig buckConfig =
+ FakeBuckConfig.builder()
+ .setFilesystem(new ProjectFilesystem(testDataDirectory))
+ .setSections(
+ ImmutableMap.of(
+ "kotlin",
+ ImmutableMap.of(
+ "kotlin_home", "faux_kotlin_home",
+ "external", "false")))
.build();
KotlinBuckConfig kotlinBuckConfig = new KotlinBuckConfig(buckConfig);
- PathSourcePath runtimeJar = (PathSourcePath) kotlinBuckConfig.getPathToRuntimeJar().getLeft();
- assertEquals(runtimeJar.getRelativePath().toString(), "lib/kotlin-runtime.jar");
+ Path runtimeJar = kotlinBuckConfig.getPathToStdlibJar();
+ assertNotNull(runtimeJar);
+ assertTrue(runtimeJar.endsWith("faux_kotlin_home/libexec/lib/kotlin-stdlib.jar"));
}
}
diff --git a/test/com/facebook/buck/jvm/kotlin/KotlinLibraryIntegrationTest.java b/test/com/facebook/buck/jvm/kotlin/KotlinLibraryIntegrationTest.java
index 9df12b31599..9a9cb0f3ad7 100644
--- a/test/com/facebook/buck/jvm/kotlin/KotlinLibraryIntegrationTest.java
+++ b/test/com/facebook/buck/jvm/kotlin/KotlinLibraryIntegrationTest.java
@@ -39,6 +39,7 @@ public void setUp() throws IOException, InterruptedException {
@Test
public void shouldCompileKotlinClass() throws Exception {
+ KotlinTestAssumptions.assumeCompilerAvailable();
ProjectWorkspace.ProcessResult buildResult =
workspace.runBuckCommand("build", "//com/example/good:example");
buildResult.assertSuccess("Build should have succeeded.");
@@ -46,6 +47,7 @@ public void shouldCompileKotlinClass() throws Exception {
@Test
public void shouldCompileLibraryWithDependencyOnAnother() throws Exception {
+ KotlinTestAssumptions.assumeCompilerAvailable();
ProjectWorkspace.ProcessResult buildResult =
workspace.runBuckCommand("build", "//com/example/child:child");
buildResult.assertSuccess("Build should have succeeded.");
@@ -53,6 +55,7 @@ public void shouldCompileLibraryWithDependencyOnAnother() throws Exception {
@Test
public void shouldFailToCompileInvalidKotlinCode() throws Exception {
+ KotlinTestAssumptions.assumeCompilerAvailable();
ProjectWorkspace.ProcessResult buildResult =
workspace.runBuckCommand("build", "//com/example/bad:fail");
buildResult.assertFailure();
diff --git a/test/com/facebook/buck/jvm/kotlin/KotlinTestAssumptions.java b/test/com/facebook/buck/jvm/kotlin/KotlinTestAssumptions.java
index 2068bb1f3d9..4b4a4194d34 100644
--- a/test/com/facebook/buck/jvm/kotlin/KotlinTestAssumptions.java
+++ b/test/com/facebook/buck/jvm/kotlin/KotlinTestAssumptions.java
@@ -32,7 +32,7 @@ public static void assumeUnixLike() {
public static void assumeCompilerAvailable() throws IOException {
Throwable exception = null;
try {
- new KotlinBuckConfig(FakeBuckConfig.builder().build()).getKotlinCompiler();
+ new KotlinBuckConfig(FakeBuckConfig.builder().build()).getPathToCompilerJar();
} catch (HumanReadableException e) {
exception = e;
}
diff --git a/test/com/facebook/buck/jvm/kotlin/testdata/kotlin_compiler_test/lib/kotlin-compiler.jar b/test/com/facebook/buck/jvm/kotlin/testdata/kotlin_compiler_test/lib/kotlin-compiler.jar
new file mode 100644
index 00000000000..1b3cfcec1a6
--- /dev/null
+++ b/test/com/facebook/buck/jvm/kotlin/testdata/kotlin_compiler_test/lib/kotlin-compiler.jar
@@ -0,0 +1 @@
+This is not really a jar.
\ No newline at end of file
diff --git a/test/com/facebook/buck/jvm/kotlin/testdata/kotlin_test_description/BUCK.fixture b/test/com/facebook/buck/jvm/kotlin/testdata/kotlin_test_description/BUCK.fixture
index b910bba347e..d2d6b8eb1dd 100644
--- a/test/com/facebook/buck/jvm/kotlin/testdata/kotlin_test_description/BUCK.fixture
+++ b/test/com/facebook/buck/jvm/kotlin/testdata/kotlin_test_description/BUCK.fixture
@@ -17,9 +17,3 @@ prebuilt_jar(
name = 'hamcrest-library',
binary_jar = 'hamcrest-library-1.3.jar',
)
-
-prebuilt_jar(
- name = 'kotlin-runtime',
- binary_jar = 'kotlin-runtime-1.0.4.jar',
- visibility = [ 'PUBLIC' ],
-)
diff --git a/test/com/facebook/buck/jvm/kotlin/testdata/kotlin_test_description/com/example/basic/BUCK.fixture b/test/com/facebook/buck/jvm/kotlin/testdata/kotlin_test_description/com/example/basic/BUCK.fixture
index a4a49f2587f..6e59b589437 100644
--- a/test/com/facebook/buck/jvm/kotlin/testdata/kotlin_test_description/com/example/basic/BUCK.fixture
+++ b/test/com/facebook/buck/jvm/kotlin/testdata/kotlin_test_description/com/example/basic/BUCK.fixture
@@ -5,7 +5,6 @@ kotlin_test(
],
deps = [
'//:junit',
- '//:kotlin-runtime',
],
)
@@ -16,6 +15,5 @@ kotlin_test(
],
deps = [
'//:junit',
- '//:kotlin-runtime',
],
)
diff --git a/test/com/facebook/buck/jvm/kotlin/testdata/kotlin_test_description/kotlin-runtime-1.0.4.jar b/test/com/facebook/buck/jvm/kotlin/testdata/kotlin_test_description/kotlin-runtime-1.0.4.jar
deleted file mode 100644
index a7624530802..00000000000
Binary files a/test/com/facebook/buck/jvm/kotlin/testdata/kotlin_test_description/kotlin-runtime-1.0.4.jar and /dev/null differ