mixed-mode rework, allign flags with javabuilder, don't use temp dir - declare class dir, reorganize source.
diff --git a/kotlin/rules/compile.bzl b/kotlin/rules/compile.bzl
index 80c591c..460de38 100644
--- a/kotlin/rules/compile.bzl
+++ b/kotlin/rules/compile.bzl
@@ -40,9 +40,12 @@
by the caller -- kotlin-reflect could be optional.
opts: struct containing Kotlin compilation options.
"""
+ compiler_output_base=ctx.actions.declare_directory(ctx.label.name + "." + "kotlinc")
+
args = [
- "--label", ctx.label,
- "--output_classjar", output_jar.path,
+ "--target_label", ctx.label,
+ "--compiler_output_base", compiler_output_base.path,
+ "--output", output_jar.path,
"--output_jdeps", ctx.outputs.jdeps.path,
"--classpath", ":".join([f.path for f in compile_jars.to_list()]),
"--sources", ":".join([f.path for f in ctx.files.srcs]),
@@ -79,7 +82,7 @@
ctx.action(
mnemonic = "KotlinCompile",
inputs = compile_inputs,
- outputs = [output_jar, ctx.outputs.jdeps],
+ outputs = [output_jar, ctx.outputs.jdeps, compiler_output_base],
executable = ctx.executable._kotlinw,
execution_requirements = {"supports-workers": "1"},
arguments = ["@" + args_file.path],
diff --git a/kotlin/workers/src/io/bazel/ruleskotlin/workers/BazelWorker.java b/kotlin/workers/src/io/bazel/ruleskotlin/workers/BazelWorker.java
index 14b9db3..6ee7f1b 100644
--- a/kotlin/workers/src/io/bazel/ruleskotlin/workers/BazelWorker.java
+++ b/kotlin/workers/src/io/bazel/ruleskotlin/workers/BazelWorker.java
@@ -17,7 +17,7 @@
import com.google.devtools.build.lib.worker.WorkerProtocol;
-import io.bazel.ruleskotlin.workers.compilers.jvm.utils.Utils;
+import io.bazel.ruleskotlin.workers.utils.IOUtils;
import java.io.*;
import java.nio.file.Files;
@@ -135,7 +135,7 @@
}
private boolean wasInterrupted(Throwable e) {
- Throwable cause = Utils.getRootCause(e);
+ Throwable cause = IOUtils.getRootCause(e);
if (cause instanceof InterruptedException
|| cause instanceof InterruptedIOException) {
output.println("Terminating worker due to interrupt signal");
diff --git a/kotlin/workers/src/io/bazel/ruleskotlin/workers/compilers/jvm/actions/BuildAction.java b/kotlin/workers/src/io/bazel/ruleskotlin/workers/BuildAction.java
similarity index 85%
rename from kotlin/workers/src/io/bazel/ruleskotlin/workers/compilers/jvm/actions/BuildAction.java
rename to kotlin/workers/src/io/bazel/ruleskotlin/workers/BuildAction.java
index 7039fb5..79dcd52 100644
--- a/kotlin/workers/src/io/bazel/ruleskotlin/workers/compilers/jvm/actions/BuildAction.java
+++ b/kotlin/workers/src/io/bazel/ruleskotlin/workers/BuildAction.java
@@ -13,9 +13,8 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package io.bazel.ruleskotlin.workers.compilers.jvm.actions;
+package io.bazel.ruleskotlin.workers;
-import io.bazel.ruleskotlin.workers.compilers.jvm.Context;
import java.util.function.Function;
diff --git a/kotlin/workers/src/io/bazel/ruleskotlin/workers/CompileResult.java b/kotlin/workers/src/io/bazel/ruleskotlin/workers/CompileResult.java
new file mode 100644
index 0000000..49ea365
--- /dev/null
+++ b/kotlin/workers/src/io/bazel/ruleskotlin/workers/CompileResult.java
@@ -0,0 +1,120 @@
+/*
+ * Copyright 2018 The Bazel Authors. All rights reserved.
+ *
+ * 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 io.bazel.ruleskotlin.workers;
+
+import java.util.Optional;
+import java.util.function.Function;
+import java.util.function.Supplier;
+
+@FunctionalInterface
+public interface CompileResult {
+ /**
+ * The status of this operation.
+ */
+ default int status() {
+ return 0;
+ }
+
+ default Optional<Exception> error() {
+ return Optional.empty();
+ }
+
+ default void propogateError(String message) throws RuntimeException {
+ error().ifPresent(e -> {
+ throw new RuntimeException(message, e);
+ });
+ }
+
+ static CompileResult just(final int status) {
+ return new CompileResult() {
+ @Override
+ public int status() {
+ return status;
+ }
+
+ @Override
+ public Integer render(Context ctx) {
+ return status;
+ }
+ };
+ }
+
+ static CompileResult error(final Exception error) {
+ return new CompileResult() {
+ @Override
+ public int status() {
+ return -1;
+ }
+
+ @Override
+ public Optional<Exception> error() {
+ return Optional.of(error);
+ }
+
+ @Override
+ public Integer render(Context ctx) {
+ throw new RuntimeException(error);
+ }
+ };
+ }
+
+ static CompileResult deferred(final int status, Function<Context, Integer> renderer) {
+ return new CompileResult() {
+ @Override
+ public int status() {
+ return status;
+ }
+
+ @Override
+ public Integer render(Context ctx) {
+ return renderer.apply(ctx);
+ }
+ };
+ }
+
+ final class Meta extends io.bazel.ruleskotlin.workers.Meta<CompileResult> {
+ public Meta(String id) {
+ super(id);
+ }
+
+ public CompileResult run(final Context ctx, Function<Context, Integer> op) {
+ CompileResult result;
+ try {
+ result = CompileResult.just(op.apply(ctx));
+ } catch (Exception e) {
+ result = CompileResult.error(e);
+ }
+ return result;
+ }
+
+ public CompileResult runAndBind(final Context ctx, Function<Context, Integer> op) {
+ CompileResult res = run(ctx, op);
+ bind(ctx, res);
+ return res;
+ }
+
+ public CompileResult runAndBind(final Context ctx, Supplier<Integer> op) {
+ return runAndBind(ctx, (c) -> op.get());
+ }
+ }
+
+ /**
+ * Materialise the output of the compile result.
+ *
+ * @return the new status of the compile operation, this shouldn't make a failing status pass, but it could fail a compile operation.
+ */
+ Integer render(Context ctx);
+}
diff --git a/kotlin/workers/src/io/bazel/ruleskotlin/workers/compilers/jvm/Context.java b/kotlin/workers/src/io/bazel/ruleskotlin/workers/Context.java
similarity index 66%
rename from kotlin/workers/src/io/bazel/ruleskotlin/workers/compilers/jvm/Context.java
rename to kotlin/workers/src/io/bazel/ruleskotlin/workers/Context.java
index 6ceb27d..5bb4fad 100644
--- a/kotlin/workers/src/io/bazel/ruleskotlin/workers/compilers/jvm/Context.java
+++ b/kotlin/workers/src/io/bazel/ruleskotlin/workers/Context.java
@@ -13,17 +13,19 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package io.bazel.ruleskotlin.workers.compilers.jvm;
+package io.bazel.ruleskotlin.workers;
import java.util.*;
+import java.util.function.Consumer;
import java.util.stream.Collectors;
+import java.util.stream.Stream;
public class Context {
- private final EnumMap<Flag, String> args = new EnumMap<>(Flag.class);
+ private final EnumMap<Flags, String> args = new EnumMap<>(Flags.class);
private final Map<Meta<?>, Object> meta = new HashMap<>();
- private static final Map<String, Flag> ALL_FIELDS_MAP = Arrays.stream(Flag.values()).collect(Collectors.toMap(x -> x.name, x -> x));
- private static final Flag[] MANDATORY_FIELDS = Arrays.stream(Flag.values()).filter(x -> x.mandatory).toArray(Flag[]::new);
+ private static final Map<String, Flags> ALL_FIELDS_MAP = Arrays.stream(Flags.values()).collect(Collectors.toMap(x -> x.name, x -> x));
+ private static final Flags[] MANDATORY_FIELDS = Arrays.stream(Flags.values()).filter(x -> x.mandatory).toArray(Flags[]::new);
private Context(List<String> args) {
if (args.size() % 2 != 0) {
@@ -33,27 +35,27 @@
for (int i = 0; i < args.size() / 2; i++) {
String flag = args.get(i * 2);
String value = args.get((i * 2) + 1);
- Flag field = ALL_FIELDS_MAP.get(flag);
+ Flags field = ALL_FIELDS_MAP.get(flag);
if (field == null) {
throw new RuntimeException("unrecognised arg: " + flag);
}
this.args.put(field, value);
}
- for (Flag mandatoryField : MANDATORY_FIELDS) {
+ for (Flags mandatoryField : MANDATORY_FIELDS) {
if (!this.args.containsKey(mandatoryField)) {
throw new RuntimeException("mandatory arg missing: " + mandatoryField.name);
}
}
}
- static Context from(List<String> args) {
+ public static Context from(List<String> args) {
return new Context(args);
}
- public EnumMap<Flag, String> copyOfArgsContaining(Flag... fields) {
- EnumMap<Flag, String> result = new EnumMap<>(Flag.class);
- for (Flag field : fields) {
+ public EnumMap<Flags, String> of(Flags... fields) {
+ EnumMap<Flags, String> result = new EnumMap<>(Flags.class);
+ for (Flags field : fields) {
String value = args.get(field);
if (value != null) {
result.put(field, value);
@@ -62,7 +64,15 @@
return result;
}
- String get(Flag field) {
+ public interface Action extends Consumer<Context> {
+ }
+
+ public void apply(Action... consumers) {
+ Stream.of(consumers).forEach(c -> c.accept(this));
+ }
+
+
+ String get(Flags field) {
return args.get(field);
}
@@ -70,6 +80,7 @@
<T> T get(Meta<T> key) {
return (T) meta.get(key);
}
+
@SuppressWarnings("unchecked")
<T> T putIfAbsent(Meta<T> key, T value) {
return (T) meta.putIfAbsent(key, value);
diff --git a/kotlin/workers/src/io/bazel/ruleskotlin/workers/compilers/jvm/Flag.java b/kotlin/workers/src/io/bazel/ruleskotlin/workers/Flags.java
similarity index 67%
rename from kotlin/workers/src/io/bazel/ruleskotlin/workers/compilers/jvm/Flag.java
rename to kotlin/workers/src/io/bazel/ruleskotlin/workers/Flags.java
index f827d1f..7aaa65a 100644
--- a/kotlin/workers/src/io/bazel/ruleskotlin/workers/compilers/jvm/Flag.java
+++ b/kotlin/workers/src/io/bazel/ruleskotlin/workers/Flags.java
@@ -13,14 +13,21 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package io.bazel.ruleskotlin.workers.compilers.jvm;
+package io.bazel.ruleskotlin.workers;
-public enum Flag {
- LABEL("--label", null, true),
- OUTPUT_CLASSJAR("--output_classjar", "-d", true),
+
+public enum Flags {
+ // flags that line up with the java builder.
+ LABEL(JavaBuilderFlags.TARGET_LABEL.flag, null, true),
+ OUTPUT_CLASSJAR(JavaBuilderFlags.OUTPUT.flag, null, true),
+ SOURCES(JavaBuilderFlags.SOURCES.flag, null, true),
+ CLASSPATH(JavaBuilderFlags.CLASSPATH.flag, "-cp", true),
+
+ // flags that could be aligned with the java builder.
OUTPUT_JDEPS("--output_jdeps", null, true),
- CLASSPATH("--classpath", "-cp", true),
- SOURCES("--sources", null, true),
+ COMPILER_OUTPUT_BASE("--compiler_output_base", null, true),
+
+ // flags for kotlin.
KOTLIN_API_VERSION("--kotlin_api_version", "-api-version", false),
KOTLIN_LANGUAGE_VERSION("--kotlin_language_version", "-language-version", false),
KOTLIN_JVM_TARGET("--kotlin_jvm_target", "-jvm-target", false);
@@ -29,7 +36,7 @@
public final String kotlinFlag;
final boolean mandatory;
- Flag(String name, String kotlinName, boolean mandatory) {
+ Flags(String name, String kotlinName, boolean mandatory) {
this.name = name;
this.kotlinFlag = kotlinName;
this.mandatory = mandatory;
diff --git a/kotlin/workers/src/io/bazel/ruleskotlin/workers/JavaBuilderFlags.java b/kotlin/workers/src/io/bazel/ruleskotlin/workers/JavaBuilderFlags.java
new file mode 100644
index 0000000..dcecc40
--- /dev/null
+++ b/kotlin/workers/src/io/bazel/ruleskotlin/workers/JavaBuilderFlags.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2018 The Bazel Authors. All rights reserved.
+ *
+ * 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 io.bazel.ruleskotlin.workers;
+
+/**
+ * Flags used by the java builder.
+ */
+@SuppressWarnings("unused")
+public enum JavaBuilderFlags {
+ TARGET_LABEL("--target_label"),
+ CLASSPATH("--classpath"),
+ JAVAC_OPTS("--javacopts"),
+ DEPENDENCIES("--dependencies"),
+ DIRECT_DEPENDENCIES("--direct_dependencies"),
+ DIRECT_DEPENDENCY("--direct_dependency"),
+ INDIRECT_DEPENDENCY("--indirect_dependency"),
+ STRICT_JAVA_DEPS("--strict_java_deps"),
+ OUTPUT_DEPS_PROTO("--output_deps_proto"),
+ DEPS_ARTIFACTS("--deps_artifacts"),
+ REDUCE_CLASSPATH("--reduce_classpath"),
+ SOURCEGEN_DIR("--sourcegendir"),
+ GENERATED_SOURCES_OUTPUT("--generated_sources_output"),
+ OUTPUT_MANIFEST_PROTO("--output_manifest_proto"),
+ SOURCES("--sources"),
+ SOURCE_ROOTS("--source_roots"),
+ SOURCE_JARS("--source_jars"),
+ SOURCE_PATH("--sourcepath"),
+ BOOT_CLASSPATH("--bootclasspath"),
+ PROCESS_PATH("--processorpath"),
+ PROCESSORS("--processors"),
+ EXT_CLASSPATH("--extclasspath"),
+ EXT_DIR("--extdir"),
+ OUTPUT("--output"),
+ NATIVE_HEADER_OUTPUT("--native_header_output"),
+ CLASSDIR("--classdir"),
+ TEMPDIR("--tempdir"),
+ GENDIR("--gendir"),
+ POST_PROCESSOR("--post_processor"),
+ COMPRESS_JAR("--compress_jar"),
+ RULE_KIND("--rule_kind"),
+ TEST_ONLY("--testonly");
+
+ public final String flag;
+
+ JavaBuilderFlags(String flag) {
+ this.flag = flag;
+ }
+}
diff --git a/kotlin/workers/src/io/bazel/ruleskotlin/workers/compilers/jvm/utils/KotlinPreloadedCompilerBuilder.java b/kotlin/workers/src/io/bazel/ruleskotlin/workers/KotlinToolchain.java
similarity index 68%
rename from kotlin/workers/src/io/bazel/ruleskotlin/workers/compilers/jvm/utils/KotlinPreloadedCompilerBuilder.java
rename to kotlin/workers/src/io/bazel/ruleskotlin/workers/KotlinToolchain.java
index f08cf98..4f8e5c1 100644
--- a/kotlin/workers/src/io/bazel/ruleskotlin/workers/compilers/jvm/utils/KotlinPreloadedCompilerBuilder.java
+++ b/kotlin/workers/src/io/bazel/ruleskotlin/workers/KotlinToolchain.java
@@ -13,42 +13,42 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package io.bazel.ruleskotlin.workers.compilers.jvm.utils;
+package io.bazel.ruleskotlin.workers;
import io.bazel.ruleskotlin.workers.compilers.jvm.Locations;
import org.jetbrains.kotlin.preloading.ClassPreloadingUtils;
import org.jetbrains.kotlin.preloading.Preloader;
-import java.io.File;
+import java.io.IOException;
import java.io.PrintStream;
import java.lang.reflect.Method;
import java.nio.file.Paths;
-import java.util.List;
import java.util.function.BiFunction;
-import java.util.stream.Collectors;
-import java.util.stream.Stream;
-public final class KotlinPreloadedCompilerBuilder {
+public final class KotlinToolchain {
private static final Object[] NO_ARGS = new Object[]{};
+ private final ClassLoader classLoader;
- private static final List<File> PRELOAD_JARS = Stream.concat(
- Locations.KOTLIN_REPO.verifiedRelativeFiles(Paths.get("lib", "kotlin-compiler.jar")),
- Locations.JAVA_HOME.verifiedRelativeFiles(Paths.get("lib", "tools.jar"))
- ).collect(Collectors.toList());
+ public KotlinToolchain() throws IOException {
+ this.classLoader = ClassPreloadingUtils.preloadClasses(
+ Locations.KOTLIN_REPO.verifiedRelativeFiles(
+ Paths.get("lib", "kotlin-compiler.jar")
+ ),
+ Preloader.DEFAULT_CLASS_NUMBER_ESTIMATE,
+ Thread.currentThread().getContextClassLoader(),
+ null
+ );
+ }
+
+ public interface KotlinCompiler extends BiFunction<String[], PrintStream, Integer> {
+ }
/**
- * Load the Kotlin compiler and the javac tools.jar into a Preloading classloader. The Kotlin compiler is invoked reflectively to eventually allow
+ * Load the Kotlin compiler and the javac tools.jar into a Preloading classLoader. The Kotlin compiler is invoked reflectively to eventually allow
* toolchain replacement.
*/
- public static BiFunction<String[], PrintStream,Integer> build() {
+ public KotlinCompiler kotlinCompiler() {
try {
- ClassLoader classLoader = ClassPreloadingUtils.preloadClasses(
- PRELOAD_JARS,
- Preloader.DEFAULT_CLASS_NUMBER_ESTIMATE,
- Thread.currentThread().getContextClassLoader(),
- null
- );
-
Class<?> compilerClass = classLoader.loadClass("org.jetbrains.kotlin.cli.jvm.K2JVMCompiler");
Class<?> exitCodeClass = classLoader.loadClass("org.jetbrains.kotlin.cli.common.ExitCode");
diff --git a/kotlin/workers/src/io/bazel/ruleskotlin/workers/Meta.java b/kotlin/workers/src/io/bazel/ruleskotlin/workers/Meta.java
new file mode 100644
index 0000000..94a0712
--- /dev/null
+++ b/kotlin/workers/src/io/bazel/ruleskotlin/workers/Meta.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright 2018 The Bazel Authors. All rights reserved.
+ *
+ * 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 io.bazel.ruleskotlin.workers;
+
+import java.util.Optional;
+
+public class Meta<T> {
+ private final String id;
+
+ private final T defaultValue;
+
+ public Meta(String id) {
+ this.id = id;
+ this.defaultValue = null;
+ }
+
+ @SuppressWarnings("unused")
+ private Meta(String id, T defaultValue) {
+ this.id = id;
+ this.defaultValue = defaultValue;
+ }
+
+ /**
+ * Gets a mandatory value.
+ */
+ public T mustGet(Context ctx) {
+ T res = ctx.get(this);
+ if(res == null) {
+ assert defaultValue != null : "mandatory meta parameter missing in context and does not have a default value";
+ return defaultValue;
+ }
+ return res;
+ }
+
+ /**
+ * Gets an optional value, if it has not been bound the default value is used.
+ */
+ public Optional<T> get(Context ctx) {
+ T res = ctx.get(this);
+ if( res != null) {
+ return Optional.of(res);
+ } else if(defaultValue != null) {
+ return Optional.of(defaultValue);
+ } else {
+ return Optional.empty();
+ }
+ }
+
+ public void bind(Context ctx, T value) {
+ if (ctx.putIfAbsent(this, value) != null) {
+ throw new RuntimeException("attempting to change bound meta variable " + id);
+ }
+ }
+}
diff --git a/kotlin/workers/src/io/bazel/ruleskotlin/workers/compilers/jvm/KotlinJvmBuilder.java b/kotlin/workers/src/io/bazel/ruleskotlin/workers/compilers/jvm/KotlinJvmBuilder.java
index 71b881e..d6bdf85 100644
--- a/kotlin/workers/src/io/bazel/ruleskotlin/workers/compilers/jvm/KotlinJvmBuilder.java
+++ b/kotlin/workers/src/io/bazel/ruleskotlin/workers/compilers/jvm/KotlinJvmBuilder.java
@@ -16,24 +16,35 @@
package io.bazel.ruleskotlin.workers.compilers.jvm;
-import io.bazel.ruleskotlin.workers.BazelWorker;
-import io.bazel.ruleskotlin.workers.CommandLineProgram;
-import io.bazel.ruleskotlin.workers.compilers.jvm.actions.BuildAction;
-import io.bazel.ruleskotlin.workers.compilers.jvm.actions.GenerateJdepsFile;
-import io.bazel.ruleskotlin.workers.compilers.jvm.actions.KotlinCreateClassJar;
-import io.bazel.ruleskotlin.workers.compilers.jvm.actions.KotlinMainCompile;
+import io.bazel.ruleskotlin.workers.*;
+import io.bazel.ruleskotlin.workers.compilers.jvm.actions.*;
+import java.io.IOException;
import java.util.List;
/**
* Bazel Kotlin Compiler worker.
*/
public final class KotlinJvmBuilder implements CommandLineProgram {
- private static final BuildAction[] compileActions = new BuildAction[] {
- KotlinMainCompile.INSTANCE,
- KotlinCreateClassJar.INSTANCE,
- GenerateJdepsFile.INSTANCE,
- };
+ private final BuildAction[] compileActions;
+
+ private KotlinJvmBuilder() {
+ KotlinToolchain kotlinToolchain;
+ try {
+ kotlinToolchain = new KotlinToolchain();
+ } catch (IOException e) {
+ throw new RuntimeException("could not initialize toolchain", e);
+ }
+
+ compileActions = new BuildAction[]{
+ Initialize.INSTANCE,
+ new KotlinMainCompile(kotlinToolchain),
+ new JavaMainCompile(),
+ KotlinRenderClassCompileResult.INSTANCE,
+ CreateOutputJar.INSTANCE,
+ GenerateJdepsFile.INSTANCE,
+ };
+ }
@Override
public Integer apply(List<String> args) {
@@ -41,7 +52,7 @@
Integer exitCode = 0;
for (BuildAction action : compileActions) {
exitCode = action.apply(context);
- if(exitCode != 0)
+ if (exitCode != 0)
break;
}
return exitCode;
diff --git a/kotlin/workers/src/io/bazel/ruleskotlin/workers/compilers/jvm/Locations.java b/kotlin/workers/src/io/bazel/ruleskotlin/workers/compilers/jvm/Locations.java
index f90e031..c877564 100644
--- a/kotlin/workers/src/io/bazel/ruleskotlin/workers/compilers/jvm/Locations.java
+++ b/kotlin/workers/src/io/bazel/ruleskotlin/workers/compilers/jvm/Locations.java
@@ -19,6 +19,8 @@
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Arrays;
+import java.util.List;
+import java.util.stream.Collectors;
import java.util.stream.Stream;
public enum Locations {
@@ -38,8 +40,8 @@
/**
* Return a stream of paths that are known to exists relative to this location.
*/
- public final Stream<File> verifiedRelativeFiles(Path... paths) {
- return Stream.of(paths).map(relative -> verified(path.resolve(relative)));
+ public final List<File> verifiedRelativeFiles(Path... paths) {
+ return Stream.of(paths).map(relative -> verified(path.resolve(relative))).collect(Collectors.toList());
}
private File verified(Path target) {
diff --git a/kotlin/workers/src/io/bazel/ruleskotlin/workers/compilers/jvm/Meta.java b/kotlin/workers/src/io/bazel/ruleskotlin/workers/compilers/jvm/Meta.java
deleted file mode 100644
index f6f0c9c..0000000
--- a/kotlin/workers/src/io/bazel/ruleskotlin/workers/compilers/jvm/Meta.java
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- * Copyright 2018 The Bazel Authors. All rights reserved.
- *
- * 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 io.bazel.ruleskotlin.workers.compilers.jvm;
-
-import java.io.File;
-import java.util.Optional;
-
-/**
- * Meta is a key to some compilation state, it is stored in a {@link Context}. A meta is meant for setting up state for other actions.
- */
-public final class Meta<T> {
- // if present contains the directory that classes were compiled to.
- public static final Meta<File> COMPILE_TO_DIRECTORY = new Meta<>("compile_to_jar");
-
- private final String id;
-
- private Meta(String id) {
- this.id = id;
- }
-
- public Optional<T> get(Context ctx) {
- return Optional.ofNullable(ctx.get(this));
- }
-
- public void bind(Context ctx, T value) {
- if (ctx.putIfAbsent(this, value) != null) {
- throw new RuntimeException("attempting to change bound meta variable " + id);
- }
- }
-}
\ No newline at end of file
diff --git a/kotlin/workers/src/io/bazel/ruleskotlin/workers/compilers/jvm/Metas.java b/kotlin/workers/src/io/bazel/ruleskotlin/workers/compilers/jvm/Metas.java
new file mode 100644
index 0000000..eb8ff54
--- /dev/null
+++ b/kotlin/workers/src/io/bazel/ruleskotlin/workers/compilers/jvm/Metas.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2018 The Bazel Authors. All rights reserved.
+ *
+ * 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 io.bazel.ruleskotlin.workers.compilers.jvm;
+
+import io.bazel.ruleskotlin.workers.CompileResult;
+import io.bazel.ruleskotlin.workers.Meta;
+
+import java.nio.file.Path;
+import java.util.List;
+
+/**
+ * Meta is a key to some compilation state,.
+ */
+public class Metas {
+ // mandatory: the package part of the label.
+ public static final Meta<String> PKG = new Meta<>("package");
+ // mandatory: The target part of the label.
+ public static final Meta<String> TARGET = new Meta<>("target");
+ // mandatory: the class staging directory.
+ public static final Meta<Path> CLASSES_DIRECTORY = new Meta<>("class_directory");
+ // mandatory: If this is non empty then it is a mixed mode operation.
+ public static final Meta<List<String>> JAVA_SOURCES = new Meta<>("java_sources");
+ // mandatory:
+ public static final Meta<List<String>> ALL_SOURCES = new Meta<>("all_sources");
+ // mandatory:
+ public static final CompileResult.Meta KOTLINC_RESULT = new CompileResult.Meta("kotlin_compile_result");
+ // optional: when not a mixed mode operation.
+ public static final CompileResult.Meta JAVAC_RESULT = new CompileResult.Meta("javac_compile_result");
+}
\ No newline at end of file
diff --git a/kotlin/workers/src/io/bazel/ruleskotlin/workers/compilers/jvm/actions/CreateOutputJar.java b/kotlin/workers/src/io/bazel/ruleskotlin/workers/compilers/jvm/actions/CreateOutputJar.java
new file mode 100644
index 0000000..aba021e
--- /dev/null
+++ b/kotlin/workers/src/io/bazel/ruleskotlin/workers/compilers/jvm/actions/CreateOutputJar.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2018 The Bazel Authors. All rights reserved.
+ *
+ * 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 io.bazel.ruleskotlin.workers.compilers.jvm.actions;
+
+
+import io.bazel.ruleskotlin.workers.BuildAction;
+import io.bazel.ruleskotlin.workers.Context;
+import io.bazel.ruleskotlin.workers.Flags;
+import io.bazel.ruleskotlin.workers.compilers.jvm.Locations;
+import io.bazel.ruleskotlin.workers.compilers.jvm.Metas;
+import io.bazel.ruleskotlin.workers.utils.IOUtils;
+
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * Create a jar from the classes.
+ */
+public final class CreateOutputJar implements BuildAction {
+ public static final CreateOutputJar INSTANCE = new CreateOutputJar();
+ private static final String JAR_TOOL_PATH = Locations.JAVA_HOME.resolveVerified("bin", "jar").toString();
+
+ private CreateOutputJar() {
+ }
+
+ @Override
+ public Integer apply(Context ctx) {
+ try {
+ List<String> command = Arrays.asList(JAR_TOOL_PATH,
+ "cf", Flags.OUTPUT_CLASSJAR.get(ctx),
+ "-C", Metas.CLASSES_DIRECTORY.mustGet(ctx).toString(),
+ ".");
+ IOUtils.executeAndAwaitSuccess(10, command);
+ } catch (Exception e) {
+ throw new RuntimeException("unable to create class jar", e);
+ }
+ return 0;
+ }
+}
diff --git a/kotlin/workers/src/io/bazel/ruleskotlin/workers/compilers/jvm/actions/GenerateJdepsFile.java b/kotlin/workers/src/io/bazel/ruleskotlin/workers/compilers/jvm/actions/GenerateJdepsFile.java
index dfcc204..683cce5 100644
--- a/kotlin/workers/src/io/bazel/ruleskotlin/workers/compilers/jvm/actions/GenerateJdepsFile.java
+++ b/kotlin/workers/src/io/bazel/ruleskotlin/workers/compilers/jvm/actions/GenerateJdepsFile.java
@@ -16,10 +16,11 @@
package io.bazel.ruleskotlin.workers.compilers.jvm.actions;
import com.google.devtools.build.lib.view.proto.Deps;
-import io.bazel.ruleskotlin.workers.compilers.jvm.Context;
+import io.bazel.ruleskotlin.workers.BuildAction;
+import io.bazel.ruleskotlin.workers.Context;
import io.bazel.ruleskotlin.workers.compilers.jvm.Locations;
import io.bazel.ruleskotlin.workers.compilers.jvm.utils.JdepsParser;
-import io.bazel.ruleskotlin.workers.compilers.jvm.utils.Utils;
+import io.bazel.ruleskotlin.workers.utils.IOUtils;
import java.io.FileOutputStream;
import java.nio.file.Files;
@@ -28,7 +29,8 @@
import java.util.List;
import java.util.function.Predicate;
-import static io.bazel.ruleskotlin.workers.compilers.jvm.Flag.*;
+import static io.bazel.ruleskotlin.workers.Flags.*;
+
public final class GenerateJdepsFile implements BuildAction {
private static final String JDEPS_PATH = Locations.JAVA_HOME.resolveVerified("bin", "jdeps").toString();
@@ -52,7 +54,7 @@
output = OUTPUT_JDEPS.get(ctx);
Deps.Dependencies jdepsContent;
try {
- List<String> jdepLines = Utils.waitForOutput(new String[]{JDEPS_PATH, "-cp", classPath, classJar}, System.err);
+ List<String> jdepLines = IOUtils.executeAndWaitOutput(10, JDEPS_PATH, "-cp", classPath, classJar);
jdepsContent = JdepsParser.parse(
LABEL.get(ctx),
classJar,
@@ -61,7 +63,7 @@
IS_KOTLIN_IMPLICIT
);
} catch (Exception e) {
- throw new RuntimeException("error reading or parsing jdeps file", Utils.getRootCause(e));
+ throw new RuntimeException("error reading or parsing jdeps file", IOUtils.getRootCause(e));
}
try {
@@ -71,7 +73,7 @@
jdepsContent.writeTo(fileOutputStream);
}
} catch (Exception e) {
- throw new RuntimeException("error writing out jdeps file", Utils.getRootCause(e));
+ throw new RuntimeException("error writing out jdeps file", IOUtils.getRootCause(e));
}
return 0;
diff --git a/kotlin/workers/src/io/bazel/ruleskotlin/workers/compilers/jvm/actions/Initialize.java b/kotlin/workers/src/io/bazel/ruleskotlin/workers/compilers/jvm/actions/Initialize.java
new file mode 100644
index 0000000..2e175bc
--- /dev/null
+++ b/kotlin/workers/src/io/bazel/ruleskotlin/workers/compilers/jvm/actions/Initialize.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright 2018 The Bazel Authors. All rights reserved.
+ *
+ * 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 io.bazel.ruleskotlin.workers.compilers.jvm.actions;
+
+
+import io.bazel.ruleskotlin.workers.BuildAction;
+import io.bazel.ruleskotlin.workers.Context;
+import io.bazel.ruleskotlin.workers.Flags;
+import io.bazel.ruleskotlin.workers.Meta;
+import io.bazel.ruleskotlin.workers.compilers.jvm.Metas;
+import io.bazel.ruleskotlin.workers.utils.IOUtils;
+
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * Should be the first step, does mandatory pre-processing.
+ */
+public final class Initialize implements BuildAction {
+ public static final Initialize INSTANCE = new Initialize();
+
+ private Initialize() {
+ }
+
+ @Override
+ public Integer apply(Context ctx) {
+ ctx.apply(
+ Initialize::initializeAndBindBindDirectories,
+ Initialize::bindLabelComponents,
+ Initialize::bindSources
+ );
+ return 0;
+ }
+
+ private static void bindSources(Context ctx) {
+ List<String> javaSources = new ArrayList<>();
+ List<String> allSources = new ArrayList<>();
+ for (String src : Flags.SOURCES.get(ctx).split(":")) {
+ if (src.endsWith(".java")) {
+ javaSources.add(src);
+ allSources.add(src);
+ } else if (src.endsWith(".kt")) {
+ allSources.add(src);
+ } else {
+ throw new RuntimeException("unrecognised file type: " + src);
+ }
+ }
+ Metas.JAVA_SOURCES.bind(ctx, Collections.unmodifiableList(javaSources));
+ Metas.ALL_SOURCES.bind(ctx, Collections.unmodifiableList(allSources));
+ }
+
+ private static void initializeAndBindBindDirectories(Context ctx) {
+ Path outputBase;
+
+ try {
+ outputBase = Files.createDirectories(Paths.get(Flags.COMPILER_OUTPUT_BASE.get(ctx)));
+ } catch (IOException e) {
+ throw new RuntimeException("could not create compiler output base", e);
+ }
+
+ try {
+ IOUtils.purgeDirectory(outputBase);
+ } catch (IOException e) {
+ throw new RuntimeException("could not purge output directory", e);
+ }
+
+ createAndBindComponentDirectory(ctx, outputBase, Metas.CLASSES_DIRECTORY, "_classes");
+ }
+
+ private static void createAndBindComponentDirectory(Context ctx, Path outputBase, Meta<Path> key, String component) {
+ try {
+ key.bind(ctx, Files.createDirectories(outputBase.resolve(component)));
+ } catch (IOException e) {
+ throw new RuntimeException("could not create subdirectory for component " + component, e);
+ }
+ }
+
+ /**
+ * parses the label, sets up the meta elements and returns the target part.
+ */
+ private static void bindLabelComponents(Context ctx) {
+ String label = Flags.LABEL.get(ctx);
+ String[] parts = label.split(":");
+ if (parts.length != 2) {
+ throw new RuntimeException("the label " + label + " is invalid");
+ }
+ Metas.PKG.bind(ctx, parts[0]);
+ Metas.TARGET.bind(ctx, parts[1]);
+ }
+}
diff --git a/kotlin/workers/src/io/bazel/ruleskotlin/workers/compilers/jvm/actions/JavaMainCompile.java b/kotlin/workers/src/io/bazel/ruleskotlin/workers/compilers/jvm/actions/JavaMainCompile.java
new file mode 100644
index 0000000..da6da78
--- /dev/null
+++ b/kotlin/workers/src/io/bazel/ruleskotlin/workers/compilers/jvm/actions/JavaMainCompile.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2018 The Bazel Authors. All rights reserved.
+ *
+ * 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 io.bazel.ruleskotlin.workers.compilers.jvm.actions;
+
+import io.bazel.ruleskotlin.workers.BuildAction;
+import io.bazel.ruleskotlin.workers.Context;
+import io.bazel.ruleskotlin.workers.Flags;
+import io.bazel.ruleskotlin.workers.compilers.jvm.*;
+import io.bazel.ruleskotlin.workers.utils.IOUtils;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * Simple java compile action that invokes javac directly and simply.
+ */
+public final class JavaMainCompile implements BuildAction {
+ private static final String JAVAC_PATH = Locations.JAVA_HOME.resolveVerified("bin", "javac").toString();
+
+ public JavaMainCompile() {}
+
+ @Override
+ public Integer apply(Context ctx) {
+ List<String> javaSources = Metas.JAVA_SOURCES.mustGet(ctx);
+ if (!javaSources.isEmpty()) {
+ List<String> args = new ArrayList<>();
+ String classesDirectory = Metas.CLASSES_DIRECTORY.mustGet(ctx).toString();
+ Collections.addAll(args,
+ JAVAC_PATH, "-cp", classesDirectory + "/:" + Flags.CLASSPATH.get(ctx),
+ "-d", classesDirectory
+ );
+ args.addAll(javaSources);
+ Metas.JAVAC_RESULT.runAndBind(ctx, () -> IOUtils.executeAndAwait(30, args));
+ }
+ return 0;
+ }
+}
+
diff --git a/kotlin/workers/src/io/bazel/ruleskotlin/workers/compilers/jvm/actions/KotlinCreateClassJar.java b/kotlin/workers/src/io/bazel/ruleskotlin/workers/compilers/jvm/actions/KotlinCreateClassJar.java
deleted file mode 100644
index 4edc9e9..0000000
--- a/kotlin/workers/src/io/bazel/ruleskotlin/workers/compilers/jvm/actions/KotlinCreateClassJar.java
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * Copyright 2018 The Bazel Authors. All rights reserved.
- *
- * 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 io.bazel.ruleskotlin.workers.compilers.jvm.actions;
-
-import io.bazel.ruleskotlin.workers.compilers.jvm.Context;
-import io.bazel.ruleskotlin.workers.compilers.jvm.Flag;
-import io.bazel.ruleskotlin.workers.compilers.jvm.Locations;
-import io.bazel.ruleskotlin.workers.compilers.jvm.Meta;
-import io.bazel.ruleskotlin.workers.compilers.jvm.utils.Utils;
-
-/**
- * If classes for the main artifact were compiled to an intermediate temp directory turn them into a jar and clean up.
- */
-public final class KotlinCreateClassJar implements BuildAction {
- public static final KotlinCreateClassJar INSTANCE = new KotlinCreateClassJar();
- private static final String JAR_TOOL_PATH = Locations.JAVA_HOME.resolveVerified("bin", "jar").toString();
-
- private KotlinCreateClassJar() {}
-
- @Override
- public Integer apply(Context ctx) {
- Meta.COMPILE_TO_DIRECTORY.get(ctx).ifPresent((classDirectory) -> {
- try {
- String classJarPath = Flag.OUTPUT_CLASSJAR.get(ctx);
- Utils.waitForSuccess(new String[]{JAR_TOOL_PATH, "cf", classJarPath, "-C", classDirectory.toString(), "."}, System.err);
- Utils.deleteDirectory(classDirectory.toPath());
- } catch (Exception e) {
- throw new RuntimeException("unable to create class jar", e);
- }
- });
- return 0;
- }
-}
diff --git a/kotlin/workers/src/io/bazel/ruleskotlin/workers/compilers/jvm/actions/KotlinMainCompile.java b/kotlin/workers/src/io/bazel/ruleskotlin/workers/compilers/jvm/actions/KotlinMainCompile.java
index 6e69f9e..ad467fe 100644
--- a/kotlin/workers/src/io/bazel/ruleskotlin/workers/compilers/jvm/actions/KotlinMainCompile.java
+++ b/kotlin/workers/src/io/bazel/ruleskotlin/workers/compilers/jvm/actions/KotlinMainCompile.java
@@ -15,54 +15,40 @@
*/
package io.bazel.ruleskotlin.workers.compilers.jvm.actions;
-import io.bazel.ruleskotlin.workers.compilers.jvm.Context;
-import io.bazel.ruleskotlin.workers.compilers.jvm.Flag;
-import io.bazel.ruleskotlin.workers.compilers.jvm.Locations;
-import io.bazel.ruleskotlin.workers.compilers.jvm.Meta;
+import io.bazel.ruleskotlin.workers.*;
+import io.bazel.ruleskotlin.workers.compilers.jvm.Metas;
import io.bazel.ruleskotlin.workers.compilers.jvm.utils.KotlinCompilerOutputProcessor;
-import io.bazel.ruleskotlin.workers.compilers.jvm.utils.KotlinPreloadedCompilerBuilder;
-import java.io.File;
-import java.io.IOException;
-import java.io.PrintStream;
-import java.nio.file.Files;
+
import java.util.ArrayList;
import java.util.Collections;
-import java.util.EnumMap;
import java.util.List;
-import java.util.function.BiFunction;
/**
* Either compiles to a jar directly or when performing mixed-mode-compilation compiles to a temp directory first.
+ * <p>
+ * Mixed-Mode:
+ * <p>
+ * The Kotlin compiler is not suited for javac compilation as of 1.2.21. The errors are not conveyed directly and would need to be preprocessed, also javac
+ * invocations Configured via Kotlin use eager analysis in some corner cases this can result in classpath exceptions from the Java Compiler..
*/
public final class KotlinMainCompile implements BuildAction {
- public static final KotlinMainCompile INSTANCE = new KotlinMainCompile(KotlinPreloadedCompilerBuilder.build());
+ private final KotlinToolchain.KotlinCompiler kotlinCompiler;
- private static final String
- JAVAC_PATH = Locations.JAVA_HOME.resolveVerified("bin", "javac").toString();
-
- private static final String
- X_COMPILE_JAVA_FLAG = "-Xcompile-java",
- X_JAVAC_ARGUMENTS_FLAG = "-Xjavac-arguments",
- X_USE_JAVAC_FLAG = "-Xuse-javac";
+ public KotlinMainCompile(KotlinToolchain toolchains) {
+ this.kotlinCompiler = toolchains.kotlinCompiler();
+ }
/**
* Default fields that are directly mappable to kotlin compiler args.
*/
- private static final Flag[] COMPILE_MAPPED_FLAGS = new Flag[]{
- Flag.OUTPUT_CLASSJAR,
- Flag.CLASSPATH,
- Flag.KOTLIN_API_VERSION,
- Flag.KOTLIN_LANGUAGE_VERSION,
- Flag.KOTLIN_JVM_TARGET
+ private static final Flags[] COMPILE_MAPPED_FLAGS = new Flags[]{
+ Flags.CLASSPATH,
+ Flags.KOTLIN_API_VERSION,
+ Flags.KOTLIN_LANGUAGE_VERSION,
+ Flags.KOTLIN_JVM_TARGET
};
- private final BiFunction<String[], PrintStream, Integer> compiler;
-
- private KotlinMainCompile(BiFunction<String[], PrintStream, Integer> compiler) {
- this.compiler = compiler;
- }
-
/**
* Evaluate the compilation context and add Metadata to the ctx if needed.
*
@@ -70,46 +56,32 @@
*/
private static String[] setupCompileContext(Context ctx) {
List<String> args = new ArrayList<>();
- EnumMap<Flag, String> compileMappedFields = ctx.copyOfArgsContaining(COMPILE_MAPPED_FLAGS);
- String[] sources = Flag.SOURCES.get(ctx).split(":");
-
- for (String source : sources) {
- if (source.endsWith(".java")) {
- try {
- // Redirect the kotlin and java compilers to a temp directory.
- File temporaryClassOutputDirectory = Files.createTempDirectory("kotlinCompile").toFile();
- Meta.COMPILE_TO_DIRECTORY.bind(ctx, temporaryClassOutputDirectory);
- compileMappedFields.put(Flag.OUTPUT_CLASSJAR, temporaryClassOutputDirectory.toString());
- Collections.addAll(args,
- X_COMPILE_JAVA_FLAG,
- X_USE_JAVAC_FLAG + "=" + JAVAC_PATH,
- X_JAVAC_ARGUMENTS_FLAG + "=-d=" + temporaryClassOutputDirectory.toString());
- break;
- } catch (IOException e) {
- throw new RuntimeException("could not create temp directory for kotlin compile operation", e);
- }
- }
- }
- compileMappedFields.forEach((field, arg) -> Collections.addAll(args, field.kotlinFlag, arg));
- Collections.addAll(args, sources);
+ Collections.addAll(args, "-d", Metas.CLASSES_DIRECTORY.mustGet(ctx).toString());
+ ctx.of(COMPILE_MAPPED_FLAGS).forEach((field, arg) -> Collections.addAll(args, field.kotlinFlag, arg));
+ args.addAll(Metas.ALL_SOURCES.mustGet(ctx));
return args.toArray(new String[args.size()]);
}
@Override
public Integer apply(Context ctx) {
- KotlinCompilerOutputProcessor outputProcessor = KotlinCompilerOutputProcessor.delegatingTo(System.out);
- try {
- Integer exitCode = compiler.apply(setupCompileContext(ctx), outputProcessor.getCollector());
- if (exitCode < 2) {
- // 1 is a standard compilation error
- // 2 is an internal error
- // 3 is the script execution error
+ KotlinCompilerOutputProcessor outputProcessor;
+ outputProcessor = new KotlinCompilerOutputProcessor.ForKotlinC(System.out);
+
+ final Integer exitCode = kotlinCompiler.apply(setupCompileContext(ctx), outputProcessor.getCollector());
+ if (exitCode < 2) {
+ // 1 is a standard compilation error
+ // 2 is an internal error
+ // 3 is the script execution error
+
+ // give javac a chance to process the java sources.
+ Metas.KOTLINC_RESULT.bind(ctx, CompileResult.deferred(exitCode, (c) -> {
+ outputProcessor.process();
return exitCode;
- } else {
- throw new RuntimeException("KotlinMainCompile returned terminal error code: " + exitCode);
- }
- } finally {
+ }));
+ return 0;
+ } else {
outputProcessor.process();
+ throw new RuntimeException("KotlinMainCompile returned terminal error code: " + exitCode);
}
}
}
diff --git a/kotlin/workers/src/io/bazel/ruleskotlin/workers/compilers/jvm/actions/KotlinRenderClassCompileResult.java b/kotlin/workers/src/io/bazel/ruleskotlin/workers/compilers/jvm/actions/KotlinRenderClassCompileResult.java
new file mode 100644
index 0000000..76c8674
--- /dev/null
+++ b/kotlin/workers/src/io/bazel/ruleskotlin/workers/compilers/jvm/actions/KotlinRenderClassCompileResult.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright 2018 The Bazel Authors. All rights reserved.
+ *
+ * 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 io.bazel.ruleskotlin.workers.compilers.jvm.actions;
+
+
+import io.bazel.ruleskotlin.workers.BuildAction;
+import io.bazel.ruleskotlin.workers.CompileResult;
+import io.bazel.ruleskotlin.workers.Context;
+import io.bazel.ruleskotlin.workers.compilers.jvm.Metas;
+
+import java.util.Optional;
+
+
+/**
+ * Render the result of class compilation. This is a separate step at the moment for mixed mode compilation scenarios. If there is an error in Java sources in
+ * a large mixed mode package the Kotlin errors don't make any sense and overwhelm the console and intellij. The {@link KotlinMainCompile} step binds a deferred
+ * renderer and proceeds to lets javac compile the java sources. The step below merges the result of the two actions.
+ */
+public final class KotlinRenderClassCompileResult implements BuildAction {
+ public static final KotlinRenderClassCompileResult INSTANCE = new KotlinRenderClassCompileResult();
+
+ private KotlinRenderClassCompileResult() {
+ }
+
+ @Override
+ public Integer apply(Context ctx) {
+ CompileResult kotlincResult = Metas.KOTLINC_RESULT.mustGet(ctx);
+ Optional<CompileResult> javacResult = Metas.JAVAC_RESULT.get(ctx);
+ if (!javacResult.isPresent()) {
+ return kotlincResult.render(ctx);
+ } else {
+ try {
+ javacResult.get().propogateError("javac failed");
+ if (kotlincResult.status() != 0) {
+ return kotlincResult.status();
+ } else if (javacResult.get().status() != 0) {
+ // treat all javac statuses as non terminal compile errors.
+ return 1;
+ }
+ return 0;
+ } finally {
+ kotlincResult.render(ctx);
+ }
+ }
+ }
+}
diff --git a/kotlin/workers/src/io/bazel/ruleskotlin/workers/compilers/jvm/utils/KotlinCompilerOutputProcessor.java b/kotlin/workers/src/io/bazel/ruleskotlin/workers/compilers/jvm/utils/KotlinCompilerOutputProcessor.java
index a8c18ba..f40d071 100644
--- a/kotlin/workers/src/io/bazel/ruleskotlin/workers/compilers/jvm/utils/KotlinCompilerOutputProcessor.java
+++ b/kotlin/workers/src/io/bazel/ruleskotlin/workers/compilers/jvm/utils/KotlinCompilerOutputProcessor.java
@@ -17,6 +17,7 @@
import java.io.*;
import java.nio.file.Paths;
+import java.util.stream.Collectors;
/**
@@ -25,29 +26,51 @@
*/
// The kotlin compiler produces absolute file paths but the intellij plugin expects workspace root relative paths to
// render errors.
-public class KotlinCompilerOutputProcessor {
- private ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
+public abstract class KotlinCompilerOutputProcessor {
+ private final ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
// Get the absolute path to ensure the sandbox root is resolved.
private final String executionRoot = Paths.get("").toAbsolutePath().toString() + File.separator;
- private final PrintStream delegate;
-
+ final PrintStream delegate;
private KotlinCompilerOutputProcessor(PrintStream delegate) {
this.delegate = delegate;
}
- public static KotlinCompilerOutputProcessor delegatingTo(PrintStream delegate) {
- return new KotlinCompilerOutputProcessor(delegate);
- }
-
public PrintStream getCollector() {
return new PrintStream(byteArrayOutputStream);
}
+ public static class ForKotlinC extends KotlinCompilerOutputProcessor {
+ public ForKotlinC(PrintStream delegate) {
+ super(delegate);
+ }
+
+ @Override
+ protected boolean processLine(String line) {
+ delegate.println(trimExecutionRootPrefix(line));
+ return true;
+ }
+ }
+
+
+ final String trimExecutionRootPrefix(String toPrint) {
+ // trim off the workspace component
+ if (toPrint.startsWith(executionRoot)) {
+ return toPrint.replaceFirst(executionRoot, "");
+ }
+ return toPrint;
+ }
+
+ protected abstract boolean processLine(String line);
+
public void process() {
- new BufferedReader(new InputStreamReader(new ByteArrayInputStream(byteArrayOutputStream.toByteArray())))
- .lines()
- .forEach(line -> delegate.println(line.replace(executionRoot, "")));
+ for (String s : new BufferedReader(new InputStreamReader(new ByteArrayInputStream(byteArrayOutputStream.toByteArray())))
+ .lines().collect(Collectors.toList())) {
+ boolean shouldContinue = processLine(s);
+ if(!shouldContinue) {
+ break;
+ }
+ }
delegate.flush();
}
}
diff --git a/kotlin/workers/src/io/bazel/ruleskotlin/workers/compilers/jvm/utils/Utils.java b/kotlin/workers/src/io/bazel/ruleskotlin/workers/compilers/jvm/utils/Utils.java
deleted file mode 100644
index 3303d26..0000000
--- a/kotlin/workers/src/io/bazel/ruleskotlin/workers/compilers/jvm/utils/Utils.java
+++ /dev/null
@@ -1,85 +0,0 @@
-/*
- * Copyright 2018 The Bazel Authors. All rights reserved.
- *
- * 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 io.bazel.ruleskotlin.workers.compilers.jvm.utils;
-
-import java.io.*;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.util.List;
-import java.util.stream.Collectors;
-
-public final class Utils {
- public static List<String> waitForOutput(String[] command, PrintStream err) {
- try {
- ProcessBuilder builder = new ProcessBuilder(command);
- Process process = builder.start();
- try (BufferedReader processError = new BufferedReader(new InputStreamReader(process.getErrorStream()));
- BufferedReader output = new BufferedReader(new InputStreamReader(process.getInputStream()))) {
- while (true) {
- String line = processError.readLine();
- if (line == null)
- break;
- err.println(line);
- }
- if (process.waitFor() != 0) {
- throw new RuntimeException("non-zero return: " + process.exitValue());
- }
- return output.lines().collect(Collectors.toList());
- }
- } catch (Exception e) {
- throw new RuntimeException(e);
- }
- }
-
- public static void waitForSuccess(String[] command, PrintStream err) {
- try {
- ProcessBuilder builder = new ProcessBuilder(command);
- Process process = builder.start();
- try (BufferedReader in = new BufferedReader(new InputStreamReader(process.getErrorStream()))) {
- while (true) {
- String line = in.readLine();
- if (line == null)
- break;
- err.println(line);
- }
- if (process.waitFor() != 0) {
- throw new RuntimeException("non-zero return: " + process.exitValue());
- }
- }
- } catch (Exception e) {
- throw new RuntimeException(e);
- }
- }
-
-
- public static void deleteDirectory(Path directory) throws IOException {
- Files.walk(directory)
- .map(Path::toFile)
- .sorted((o1, o2) -> -o1.compareTo(o2))
- .forEach(File::delete);
-
- }
-
- public static Throwable getRootCause(Throwable e) {
- Throwable cause;
- Throwable result = e;
-
- while (null != (cause = result.getCause()) && (result != cause)) {
- result = cause;
- }
- return result;
- }
-}
diff --git a/kotlin/workers/src/io/bazel/ruleskotlin/workers/utils/IOUtils.java b/kotlin/workers/src/io/bazel/ruleskotlin/workers/utils/IOUtils.java
new file mode 100644
index 0000000..d4e83eb
--- /dev/null
+++ b/kotlin/workers/src/io/bazel/ruleskotlin/workers/utils/IOUtils.java
@@ -0,0 +1,133 @@
+/*
+ * Copyright 2018 The Bazel Authors. All rights reserved.
+ *
+ * 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 io.bazel.ruleskotlin.workers.utils;
+
+import java.io.*;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+
+public final class IOUtils {
+ // sort this one out
+ public static List<String> executeAndWaitOutput(int timeoutSeconds, String... command) {
+ try {
+ ProcessBuilder builder = new ProcessBuilder(command).redirectError(ProcessBuilder.Redirect.INHERIT);
+ Process process = builder.start();
+ ArrayList<String> al = new ArrayList<>();
+ CompletableFuture<Void> streamReader = null;
+ try (BufferedReader output = new BufferedReader(new InputStreamReader(process.getInputStream()))) {
+ streamReader = CompletableFuture.runAsync(() -> {
+ while (true) {
+ try {
+ String line = output.readLine();
+ if (line == null)
+ break;
+ al.add(line);
+ } catch (IOException e) {
+ throw new UncheckedIOException(e);
+ }
+ }
+ });
+ executeAwait(timeoutSeconds, process);
+ return al;
+ } finally {
+ if (streamReader != null && !streamReader.isDone()) {
+ streamReader.cancel(true);
+ }
+ }
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ private static int executeAwait(int timeoutSeconds, Process process) throws TimeoutException {
+ try {
+ if (!process.waitFor(timeoutSeconds, TimeUnit.SECONDS)) {
+ throw new TimeoutException();
+ }
+ return process.exitValue();
+ } catch (InterruptedException e) {
+ throw new RuntimeException(e);
+ } finally {
+ if (process.isAlive()) {
+ process.destroy();
+ }
+ }
+ }
+
+ public static int executeAndAwait(int timeoutSeconds, List<String> args) {
+ BufferedReader is = null;
+ BufferedReader es = null;
+ try {
+ ProcessBuilder builder = new ProcessBuilder(args.toArray(new String[args.size()]));
+ builder.redirectInput(ProcessBuilder.Redirect.PIPE);
+ builder.redirectError(ProcessBuilder.Redirect.PIPE);
+ Process process = builder.start();
+ is = new BufferedReader(new InputStreamReader(process.getInputStream()));
+ es = new BufferedReader(new InputStreamReader(process.getErrorStream()));
+ return executeAwait(timeoutSeconds, process);
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ } finally {
+ drainStreamTo(System.out, is);
+ drainStreamTo(System.err, es);
+ }
+ }
+
+ private static void drainStreamTo(PrintStream writer, BufferedReader reader) {
+ if (reader != null) {
+ reader.lines().forEach(writer::println);
+ try {
+ reader.close();
+ } catch (IOException e) {
+ throw new UncheckedIOException(e);
+ }
+ }
+ }
+
+ public static void executeAndAwaitSuccess(int timeoutSeconds, List<String> command) {
+ int status = executeAndAwait(timeoutSeconds, command);
+ if (status != 0) {
+ throw new RuntimeException("process failed with status: " + status);
+ }
+ }
+
+
+ public static void purgeDirectory(Path directory) throws IOException {
+ File directoryAsFile = directory.toFile();
+ Files.walk(directory)
+ .map(Path::toFile)
+ .sorted((o1, o2) -> -o1.compareTo(o2))
+ .filter(file -> !directoryAsFile.equals(file)) // nasty
+ .forEach(file -> {
+ assert !file.delete();
+ });
+ }
+
+ public static Throwable getRootCause(Throwable e) {
+ Throwable cause;
+ Throwable result = e;
+
+ while (null != (cause = result.getCause()) && (result != cause)) {
+ result = cause;
+ }
+ return result;
+ }
+}
diff --git a/tests/smoke/BUILD b/tests/smoke/BUILD
index 794b455..99a28d7 100644
--- a/tests/smoke/BUILD
+++ b/tests/smoke/BUILD
@@ -102,8 +102,13 @@
data=glob(["data/*"]),
)
-kotlin_binary(
+kotlin_library(
name = "hellojava",
- srcs = ["hellojava/HelloWorld.kt", "hellojava/MessageHolder.java"],
- main_class = "hellojava.HelloWorldKt"
+ srcs = glob(["hellojava/*.kt", "hellojava/*.java"]),
+)
+
+kotlin_library(
+ name = "hellojava_withmerge",
+ resources = glob(["resourcejar/**"]),
+ srcs = glob(["hellojava/*.kt", "hellojava/*.java"]),
)
\ No newline at end of file
diff --git a/tests/smoke/basic_tests.py b/tests/smoke/basic_tests.py
index 610a96c..3f97712 100644
--- a/tests/smoke/basic_tests.py
+++ b/tests/smoke/basic_tests.py
@@ -58,8 +58,25 @@
self.buildLaunchExpectingSuccess("propagation_rt_via_runtime_deps_consumer")
def test_mixed_mode_compilation(self):
- self.buildLaunchExpectingSuccess("hellojava")
+ jar = self.buildJarGetZipFile("hellojava", "jar")
+ self.assertJarContains(
+ jar,
+ "hellojava/HelloWorldJava.class",
+ "hellojava/MessageHolderKotlin.class",
+ "hellojava/MessageHolder.class",
+ "hellojava/HelloWorldKt.class"
+ )
+ def test_mixed_mode_compilation_with_merge(self):
+ jar = self.buildJarGetZipFile("hellojava_withmerge", "jar")
+ self.assertJarContains(
+ jar,
+ "hellojava/HelloWorldJava.class",
+ "hellojava/MessageHolderKotlin.class",
+ "hellojava/MessageHolder.class",
+ "hellojava/HelloWorldKt.class",
+ "tests/smoke/resourcejar/pkg/file.txt"
+ )
# re-enable this test, and ensure the srcjar includes java sources when mixed mode.
# def test_srcjar(self):
# jar = self.buildJarGetZipFile("testresources", "srcjar")
diff --git a/tests/smoke/hellojava/Another.java b/tests/smoke/hellojava/HelloWorldJava.java
similarity index 81%
copy from tests/smoke/hellojava/Another.java
copy to tests/smoke/hellojava/HelloWorldJava.java
index a8c2eda..4aae5ae 100644
--- a/tests/smoke/hellojava/Another.java
+++ b/tests/smoke/hellojava/HelloWorldJava.java
@@ -16,5 +16,8 @@
package hellojava;
-public class Another {
+public class HelloWorldJava {
+ public static void main(String[] args) {
+ System.out.println(MessageHolderKotlin.INSTANCE.hello());
+ }
}
diff --git a/tests/smoke/hellojava/Another.java b/tests/smoke/hellojava/MessageHolderKotlin.kt
similarity index 87%
rename from tests/smoke/hellojava/Another.java
rename to tests/smoke/hellojava/MessageHolderKotlin.kt
index a8c2eda..d7e5112 100644
--- a/tests/smoke/hellojava/Another.java
+++ b/tests/smoke/hellojava/MessageHolderKotlin.kt
@@ -13,8 +13,8 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+package hellojava
-package hellojava;
-
-public class Another {
-}
+object MessageHolderKotlin {
+ fun hello() = "hello from kotlin"
+}
\ No newline at end of file
diff --git a/tests/smoke/propagation/CompileTimeDependent.java b/tests/smoke/propagation/CompileTimeDependent.java
index e505025..6d47706 100644
--- a/tests/smoke/propagation/CompileTimeDependent.java
+++ b/tests/smoke/propagation/CompileTimeDependent.java
@@ -17,7 +17,6 @@
import org.junit.Test;
-
public class CompileTimeDependent {
@Test
public void justSoIcanUseTheTestAnnotation() {