port the worker files to kotlin and add bootstrap macros.
diff --git a/.bazelproject b/.bazelproject index 2035d82..7828fba 100644 --- a/.bazelproject +++ b/.bazelproject
@@ -17,8 +17,8 @@ . targets: - //kotlin/workers/... - -//tests/smoke/... + //kotlin/workers:worker_for_ide + //kotlin/workers:unittests test_sources: */unittests/*
diff --git a/kotlin/workers/BUILD b/kotlin/workers/BUILD index 342ddaf..622afeb 100644 --- a/kotlin/workers/BUILD +++ b/kotlin/workers/BUILD
@@ -11,28 +11,37 @@ # 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. -java_library( - name = "lib", - srcs = glob(["src/**/*.java"]), +load("//kotlin/workers:bootstrap.bzl", "kotlin_worker_lib") + +kotlin_worker_lib( + srcs = glob(["src/**/*.kt"]), + args = ["-jvm-target", "1.8"], deps = [ "//kotlin/workers/proto", - "@com_github_jetbrains_kotlin//:preloader" + "@com_github_jetbrains_kotlin//:preloader", + "@io_bazel_rules_kotlin_protobuf_protobuf_java//jar", ], - exports = ["//kotlin/workers/proto"], - visibility = ["//visibility:private"] + exports = [ + "//kotlin/workers/proto" + ], + runtime_deps = [ + "@com_github_jetbrains_kotlin//:kotlin-stdlib", + "@com_github_jetbrains_kotlin//:kotlin-stdlib-jdk7", + "@com_github_jetbrains_kotlin//:kotlin-stdlib-jdk8" + ] ) java_binary( name = "compiler_jvm", main_class = "io.bazel.ruleskotlin.workers.compilers.jvm.KotlinJvmBuilder", visibility = ["//visibility:public"], - runtime_deps = ["lib"] + runtime_deps = [":worker_lib"] ) java_test( name = "unittests", test_class = "io.bazel.ruleskotlin.workers.compilers.jvm.utils.JdepsParserTest", srcs = glob(["unittests/**/*.java"]), - deps = ["lib"], + deps = [":worker_lib"], size = "small" ) \ No newline at end of file
diff --git a/kotlin/workers/bootstrap.bzl b/kotlin/workers/bootstrap.bzl new file mode 100644 index 0000000..dbdc866 --- /dev/null +++ b/kotlin/workers/bootstrap.bzl
@@ -0,0 +1,87 @@ +# 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. +load("//kotlin:kotlin.bzl", _for_ide = "kotlin_library") + +_HEADER = """ +function join_by { local IFS="$$1"; shift; echo "$$*"; } + +CP=$$(join_by : $(locations :%s)) +""" + +# This is kept arround incase it's needed -- there shouldn't be a need for mixed compilation, but just in case. +def _gen_mixed_cmd(name, args): + return _HEADER + (""" +ARGS="-Xcompile-java -Xuse-javac %s" + +mkdir -p compile_classes + +JAVA_HOME=external/local_jdk ./$(location @com_github_jetbrains_kotlin//:kotlinc) -cp $${CP} -d compile_classes $${ARGS} $(SRCS) + +jar cf $(OUTS) -C compile_classes . + +rm -rf compile_classes +""") % (name, args) + +def _gen_cmd(name, args): + return (_HEADER + """ +ARGS="%s" + +JAVA_HOME=external/local_jdk ./$(location @com_github_jetbrains_kotlin//:kotlinc) -cp $${CP} -d $(OUTS) $${ARGS} $(SRCS) +""") % (name,args) + + + + +def kotlin_worker_lib(srcs =[], args = [], deps=[], runtime_deps=[], exports=[]): + name = "worker" + dep_label = name + "_deps" + jar_file_label = name + "_file" + jar_name = name+".jar" + libary_label = name + "_lib" + + # Dummy target loaded by `kotlin_library` so intellij can understand the sources. + _for_ide( + name = "for_ide", + srcs = srcs, + deps = deps, + runtime_deps = runtime_deps, + exports = exports, + visibility=["//visibility:private"], + ) + + native.filegroup( + name = dep_label, + srcs = deps, + visibility = ["//visibility:private"] + ) + native.genrule( + name = jar_file_label, + tools = [ + "@com_github_jetbrains_kotlin//:home", + "@com_github_jetbrains_kotlin//:kotlinc", + "@local_jdk//:jdk", + dep_label + ] + deps, + srcs = srcs, + outs = [jar_name], + cmd = _gen_cmd(dep_label, " ".join(args)), + visibility = ["//visibility:private"] + ) + native.java_import( + name = libary_label, + jars = [jar_name], + exports = exports, + runtime_deps = (depset(runtime_deps) + exports + deps).to_list(), + visibility = ["//visibility:private"] + ) \ No newline at end of file
diff --git a/kotlin/workers/proto/BUILD b/kotlin/workers/proto/BUILD index ebf55a8..e5770b9 100644 --- a/kotlin/workers/proto/BUILD +++ b/kotlin/workers/proto/BUILD
@@ -19,4 +19,13 @@ ], exports = ["@io_bazel_rules_kotlin_protobuf_protobuf_java//jar"], visibility = ["//kotlin/workers:__subpackages__"] +) + +filegroup( + name = "files", + srcs = [ + "jars/libdeps_proto-speed.jar", + "jars/libworker_protocol_proto-speed.jar" + ], + visibility = ["//kotlin/workers:__subpackages__"] ) \ No newline at end of file
diff --git a/kotlin/workers/src/io/bazel/ruleskotlin/workers/BazelWorker.java b/kotlin/workers/src/io/bazel/ruleskotlin/workers/BazelWorker.java deleted file mode 100644 index 6ee7f1b..0000000 --- a/kotlin/workers/src/io/bazel/ruleskotlin/workers/BazelWorker.java +++ /dev/null
@@ -1,146 +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; - - -import com.google.devtools.build.lib.worker.WorkerProtocol; -import io.bazel.ruleskotlin.workers.utils.IOUtils; - -import java.io.*; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.util.List; -import java.util.stream.Collectors; - -import static java.nio.charset.StandardCharsets.UTF_8; - -/** - * Bazel worker runner. - * <p> - * <p>This class adapts a traditional command line program so it can be spawned by Bazel as a - * persistent worker process that handles multiple invocations per JVM. It will also be backwards - * compatible with being run as a normal single-invocation command. - * - * @param <T> delegate program type - */ -public final class BazelWorker<T extends CommandLineProgram> implements CommandLineProgram { - private final CommandLineProgram delegate; - private final String mnemonic; - private final PrintStream output; - - public BazelWorker(T delegate, PrintStream output, String mnemonic) { - this.delegate = delegate; - this.output = output; - this.mnemonic = mnemonic; - } - - @Override - public Integer apply(List<String> args) { - for (String arg : args) { - if (arg.equals("--persistent_worker")) { - return runAsPersistentWorker(args); - } - } - return delegate.apply(loadArguments(args, false)); - } - - @SuppressWarnings("unused") - private int runAsPersistentWorker(List<String> ignored) { - InputStream realStdIn = System.in; - PrintStream realStdOut = System.out; - PrintStream realStdErr = System.err; - try (InputStream emptyIn = new ByteArrayInputStream(new byte[0]); - ByteArrayOutputStream buffer = new ByteArrayOutputStream(); - PrintStream ps = new PrintStream(buffer)) { - System.setIn(emptyIn); - System.setOut(ps); - System.setErr(ps); - while (true) { - WorkerProtocol.WorkRequest request = WorkerProtocol.WorkRequest.parseDelimitedFrom(realStdIn); - if (request == null) { - return 0; - } - int exitCode; - - try { - exitCode = delegate.apply(loadArguments(request.getArgumentsList(), true)); - } catch (RuntimeException e) { - if (wasInterrupted(e)) { - return 0; - } - System.err.println( - "ERROR: Worker threw uncaught exception with args: " + - request.getArgumentsList().stream().collect(Collectors.joining(" "))); - e.printStackTrace(System.err); - exitCode = 1; - } - WorkerProtocol.WorkResponse.newBuilder() - .setOutput(buffer.toString()) - .setExitCode(exitCode) - .build() - .writeDelimitedTo(realStdOut); - realStdOut.flush(); - buffer.reset(); - System.gc(); // be a good little worker process and consume less memory when idle - } - } catch (IOException | RuntimeException e) { - if (wasInterrupted(e)) { - return 0; - } - throw new RuntimeException(e); - } finally { - System.setIn(realStdIn); - System.setOut(realStdOut); - System.setErr(realStdErr); - } - } - - private List<String> loadArguments(List<String> args, boolean isWorker) { - if (args.size() > 0) { - String lastArg = args.get(args.size() - 1); - - if (lastArg.startsWith("@")) { - String pathElement = lastArg.substring(1); - Path flagFile = Paths.get(pathElement); - if ((isWorker && lastArg.startsWith("@@")) || Files.exists(flagFile)) { - if (!isWorker && !mnemonic.isEmpty()) { - output.printf( - "HINT: %s will compile faster if you run: " - + "echo \"build --strategy=%s=worker\" >>~/.bazelrc\n", - mnemonic, mnemonic); - } - try { - return Files.readAllLines(flagFile, UTF_8); - } catch (IOException e) { - throw new RuntimeException(e); - } - } - } - } - return args; - } - - private boolean wasInterrupted(Throwable e) { - Throwable cause = IOUtils.getRootCause(e); - if (cause instanceof InterruptedException - || cause instanceof InterruptedIOException) { - output.println("Terminating worker due to interrupt signal"); - return true; - } - return false; - } -}
diff --git a/kotlin/workers/src/io/bazel/ruleskotlin/workers/BazelWorker.kt b/kotlin/workers/src/io/bazel/ruleskotlin/workers/BazelWorker.kt new file mode 100644 index 0000000..0aa3f75 --- /dev/null +++ b/kotlin/workers/src/io/bazel/ruleskotlin/workers/BazelWorker.kt
@@ -0,0 +1,141 @@ +/* + * 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 com.google.devtools.build.lib.worker.WorkerProtocol +import io.bazel.ruleskotlin.workers.utils.rootCause +import java.io.* +import java.nio.charset.StandardCharsets.UTF_8 +import java.nio.file.Files +import java.nio.file.Paths +import java.util.stream.Collectors + +/** + * Bazel worker runner. + * + * + * + * This class adapts a traditional command line program so it can be spawned by Bazel as a + * persistent worker process that handles multiple invocations per JVM. It will also be backwards + * compatible with being run as a normal single-invocation command. + * + * @param <T> delegate program type +</T> */ +class BazelWorker<T : CommandLineProgram>(private val delegate: T, private val output: PrintStream, private val mnemonic: String) : CommandLineProgram { + companion object { + private const val INTERUPTED_STATUS = 0 + private const val ERROR_STATUS = 1 + } + + override fun apply(args: List<String>): Int { + return if (args.contains("--persistent_worker")) + runAsPersistentWorker(args) + else delegate.apply(loadArguments(args, false)) + } + + @Suppress("UNUSED_PARAMETER") + private fun runAsPersistentWorker(ignored: List<String>): Int { + val realStdIn = System.`in` + val realStdOut = System.out + val realStdErr = System.err + try { + ByteArrayInputStream(ByteArray(0)).use { emptyIn -> + ByteArrayOutputStream().use { buffer -> + PrintStream(buffer).use { ps -> + System.setIn(emptyIn) + System.setOut(ps) + System.setErr(ps) + while (true) { + val request = WorkerProtocol.WorkRequest.parseDelimitedFrom(realStdIn) ?: return 0 + var exitCode: Int + + exitCode = try { + delegate.apply(loadArguments(request.argumentsList, true)) + } catch (e: RuntimeException) { + if (wasInterrupted(e)) { + return INTERUPTED_STATUS + } + System.err.println( + "ERROR: Worker threw uncaught exception with args: " + request.argumentsList.stream().collect(Collectors.joining(" "))) + e.printStackTrace(System.err) + ERROR_STATUS + } + + WorkerProtocol.WorkResponse.newBuilder() + .setOutput(buffer.toString()) + .setExitCode(exitCode) + .build() + .writeDelimitedTo(realStdOut) + realStdOut.flush() + buffer.reset() + System.gc() // be a good little worker process and consume less memory when idle + } + } + } + } + } catch (e: IOException) { + if (wasInterrupted(e)) { + return INTERUPTED_STATUS + } + throw e + } catch (e: RuntimeException) { + if (wasInterrupted(e)) { + return INTERUPTED_STATUS + } + throw e + } finally { + System.setIn(realStdIn) + System.setOut(realStdOut) + System.setErr(realStdErr) + } + throw RuntimeException("drop through") + } + + private fun loadArguments(args: List<String>, isWorker: Boolean): List<String> { + if (args.isNotEmpty()) { + val lastArg = args[args.size - 1] + + if (lastArg.startsWith("@")) { + val pathElement = lastArg.substring(1) + val flagFile = Paths.get(pathElement) + if (isWorker && lastArg.startsWith("@@") || Files.exists(flagFile)) { + if (!isWorker && !mnemonic.isEmpty()) { + output.printf( + "HINT: %s will compile faster if you run: " + "echo \"build --strategy=%s=worker\" >>~/.bazelrc\n", + mnemonic, mnemonic) + } + try { + return Files.readAllLines(flagFile, UTF_8) + } catch (e: IOException) { + throw RuntimeException(e) + } + + } + } + } + return args + } + + private fun wasInterrupted(e: Throwable): Boolean { + val cause = e.rootCause + if (cause is InterruptedException || cause is InterruptedIOException) { + output.println("Terminating worker due to interrupt signal") + return true + } + return false + } +}
diff --git a/kotlin/workers/src/io/bazel/ruleskotlin/workers/BuildAction.java b/kotlin/workers/src/io/bazel/ruleskotlin/workers/BuildAction.kt similarity index 75% rename from kotlin/workers/src/io/bazel/ruleskotlin/workers/BuildAction.java rename to kotlin/workers/src/io/bazel/ruleskotlin/workers/BuildAction.kt index 79dcd52..7805229 100644 --- a/kotlin/workers/src/io/bazel/ruleskotlin/workers/BuildAction.java +++ b/kotlin/workers/src/io/bazel/ruleskotlin/workers/BuildAction.kt
@@ -13,11 +13,9 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package io.bazel.ruleskotlin.workers; +package io.bazel.ruleskotlin.workers -import java.util.function.Function; - -public interface BuildAction extends Function<Context, Integer> { - +abstract class BuildAction(@Suppress("unused") val id : String, protected val toolchain: KotlinToolchain) { + abstract operator fun invoke(ctx: Context): Int }
diff --git a/kotlin/workers/src/io/bazel/ruleskotlin/workers/CommandLineProgram.java b/kotlin/workers/src/io/bazel/ruleskotlin/workers/CommandLineProgram.java deleted file mode 100644 index 751241f..0000000 --- a/kotlin/workers/src/io/bazel/ruleskotlin/workers/CommandLineProgram.java +++ /dev/null
@@ -1,45 +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; - - -import java.util.Arrays; -import java.util.List; -import java.util.function.Function; - -/** - * Interface for command line programs. - * <p> - * <p>This is the same thing as a main function, except not static. - */ -public interface CommandLineProgram extends Function<List<String>, Integer> { - - /** - * Runs blocking program start to finish. - * <p> - * <p>This function might be called multiple times throughout the life of this object. Output - * must be sent to {@link System#out} and {@link System#err}. - * - * @param args command line arguments - * @return program exit code, i.e. 0 for success, non-zero for failure - */ - @Override - Integer apply(List<String> args); - - default Integer apply(String[] args) { - return apply(Arrays.asList(args)); - } -}
diff --git a/kotlin/workers/src/io/bazel/ruleskotlin/workers/CommandLineProgram.kt b/kotlin/workers/src/io/bazel/ruleskotlin/workers/CommandLineProgram.kt new file mode 100644 index 0000000..5fb40fe --- /dev/null +++ b/kotlin/workers/src/io/bazel/ruleskotlin/workers/CommandLineProgram.kt
@@ -0,0 +1,38 @@ +/* + * 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 + + +/** + * Interface for command line programs. + * + * This is the same thing as a main function, except not static. + */ +interface CommandLineProgram { + + /** + * Runs blocking program start to finish. + * + * + * + * This function might be called multiple times throughout the life of this object. Output + * must be sent to [System.out] and [System.err]. + * + * @param args command line arguments + * @return program exit code, i.e. 0 for success, non-zero for failure + */ + fun apply(args: List<String>): Int +}
diff --git a/kotlin/workers/src/io/bazel/ruleskotlin/workers/CompileResult.java b/kotlin/workers/src/io/bazel/ruleskotlin/workers/CompileResult.java deleted file mode 100644 index 49ea365..0000000 --- a/kotlin/workers/src/io/bazel/ruleskotlin/workers/CompileResult.java +++ /dev/null
@@ -1,120 +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; - -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/CompileResult.kt b/kotlin/workers/src/io/bazel/ruleskotlin/workers/CompileResult.kt new file mode 100644 index 0000000..6ac5a57 --- /dev/null +++ b/kotlin/workers/src/io/bazel/ruleskotlin/workers/CompileResult.kt
@@ -0,0 +1,110 @@ +/* + * 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.* + +interface CompileResult { + /** + * The status of this operation. + */ + fun status(): Int { + return 0 + } + + fun error(): Optional<Exception> { + return Optional.empty() + } + + @Throws(RuntimeException::class) + fun propogateError(message: String) { + error().ifPresent { e -> throw RuntimeException(message, e) } + } + + class Meta(id: String) : io.bazel.ruleskotlin.workers.Meta<CompileResult>(id) { + + fun run(ctx: Context, op: (Context) -> Int): CompileResult { + var result: CompileResult + try { + result = CompileResult.just(op(ctx)) + } catch (e: Exception) { + result = CompileResult.error(e) + } + + return result + } + + fun runAndBind(ctx: Context, op: (Context) -> Int): CompileResult { + val 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. + */ + fun render(ctx: Context): Int + + companion object { + + fun just(status: Int): CompileResult { + return object : CompileResult { + override fun status(): Int { + return status + } + + override fun render(ctx: Context): Int { + return status + } + } + } + + fun error(error: Exception): CompileResult { + return object : CompileResult { + override fun status(): Int { + return -1 + } + + override fun error(): Optional<Exception> { + return Optional.of(error) + } + + override fun render(ctx: Context): Int { + throw RuntimeException(error) + } + } + } + + fun deferred(status: Int, renderer: (Context) -> Int): CompileResult { + return object : CompileResult { + override fun status(): Int { + return status + } + + override fun render(ctx: Context): Int { + return renderer(ctx) + } + } + } + } +}
diff --git a/kotlin/workers/src/io/bazel/ruleskotlin/workers/Context.java b/kotlin/workers/src/io/bazel/ruleskotlin/workers/Context.java deleted file mode 100644 index 5bb4fad..0000000 --- a/kotlin/workers/src/io/bazel/ruleskotlin/workers/Context.java +++ /dev/null
@@ -1,88 +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; - -import java.util.*; -import java.util.function.Consumer; -import java.util.stream.Collectors; -import java.util.stream.Stream; - -public class Context { - private final EnumMap<Flags, String> args = new EnumMap<>(Flags.class); - private final Map<Meta<?>, Object> meta = new HashMap<>(); - - 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) { - throw new RuntimeException("args should be k,v pairs"); - } - - for (int i = 0; i < args.size() / 2; i++) { - String flag = args.get(i * 2); - String value = args.get((i * 2) + 1); - Flags field = ALL_FIELDS_MAP.get(flag); - if (field == null) { - throw new RuntimeException("unrecognised arg: " + flag); - } - this.args.put(field, value); - } - - for (Flags mandatoryField : MANDATORY_FIELDS) { - if (!this.args.containsKey(mandatoryField)) { - throw new RuntimeException("mandatory arg missing: " + mandatoryField.name); - } - } - } - - public static Context from(List<String> args) { - return new Context(args); - } - - 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); - } - } - return result; - } - - 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); - } - - @SuppressWarnings("unchecked") - <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/Context.kt b/kotlin/workers/src/io/bazel/ruleskotlin/workers/Context.kt new file mode 100644 index 0000000..1a42331 --- /dev/null +++ b/kotlin/workers/src/io/bazel/ruleskotlin/workers/Context.kt
@@ -0,0 +1,71 @@ +/* + * 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. + */ +@file:Suppress("UNCHECKED_CAST") + +package io.bazel.ruleskotlin.workers + +import java.util.* +import java.util.stream.Stream + +class Context private constructor(args: List<String>) { + private val args = EnumMap<Flags, String>(Flags::class.java) + private val meta = HashMap<Meta<*>, Any>() + + init { + if (args.size % 2 != 0) { + throw RuntimeException("args should be k,v pairs") + } + + for (i in 0 until args.size / 2) { + val flag = args[i * 2] + val value = args[i * 2 + 1] + val field = ALL_FIELDS_MAP[flag] ?: throw RuntimeException("unrecognised arg: " + flag) + this.args[field] = value + } + + MANDATORY_FIELDS.asSequence() + .filterNot { this.args.containsKey(it) } + .forEach { throw RuntimeException("mandatory arg missing: " + it.globalFlag) } + } + + fun of(vararg fields: Flags): EnumMap<Flags, String> { + val result = EnumMap<Flags, String>(Flags::class.java) + for (field in fields) { + val value = args[field] + if (value != null) { + result[field] = value + } + } + return result + } + + fun apply(vararg consumers: (Context) -> Unit) { + Stream.of(*consumers).forEach { it(this) } + } + + internal operator fun get(field: Flags): String? = args[field] + internal operator fun <T : Any> get(key: Meta<T>): T? = meta[key] as T? + internal fun <T : Any> putIfAbsent(key: Meta<T>, value: T): T? = meta.putIfAbsent(key, value as Any) as T? + + companion object { + private val ALL_FIELDS_MAP = Flags.values().map { it.globalFlag to Flags.valueOf(it.name) }.toMap() + private val MANDATORY_FIELDS = Flags.values().filter { x -> x.mandatory } + + fun from(args: List<String>): Context { + return Context(args) + } + } +}
diff --git a/kotlin/workers/src/io/bazel/ruleskotlin/workers/Flags.java b/kotlin/workers/src/io/bazel/ruleskotlin/workers/Flags.kt similarity index 76% rename from kotlin/workers/src/io/bazel/ruleskotlin/workers/Flags.java rename to kotlin/workers/src/io/bazel/ruleskotlin/workers/Flags.kt index 7aaa65a..fe20049 100644 --- a/kotlin/workers/src/io/bazel/ruleskotlin/workers/Flags.java +++ b/kotlin/workers/src/io/bazel/ruleskotlin/workers/Flags.kt
@@ -13,10 +13,10 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package io.bazel.ruleskotlin.workers; +package io.bazel.ruleskotlin.workers -public enum Flags { +enum class Flags(val globalFlag: String, val kotlinFlag: String?, internal val mandatory: Boolean) { // flags that line up with the java builder. LABEL(JavaBuilderFlags.TARGET_LABEL.flag, null, true), OUTPUT_CLASSJAR(JavaBuilderFlags.OUTPUT.flag, null, true), @@ -32,17 +32,5 @@ KOTLIN_LANGUAGE_VERSION("--kotlin_language_version", "-language-version", false), KOTLIN_JVM_TARGET("--kotlin_jvm_target", "-jvm-target", false); - public final String name; - public final String kotlinFlag; - final boolean mandatory; - - Flags(String name, String kotlinName, boolean mandatory) { - this.name = name; - this.kotlinFlag = kotlinName; - this.mandatory = mandatory; - } - - public String get(Context context) { - return context.get(this); - } + operator fun get(context: Context): String? = context[this] } \ No newline at end of file
diff --git a/kotlin/workers/src/io/bazel/ruleskotlin/workers/JavaBuilderFlags.java b/kotlin/workers/src/io/bazel/ruleskotlin/workers/JavaBuilderFlags.kt similarity index 88% rename from kotlin/workers/src/io/bazel/ruleskotlin/workers/JavaBuilderFlags.java rename to kotlin/workers/src/io/bazel/ruleskotlin/workers/JavaBuilderFlags.kt index dcecc40..3287a27 100644 --- a/kotlin/workers/src/io/bazel/ruleskotlin/workers/JavaBuilderFlags.java +++ b/kotlin/workers/src/io/bazel/ruleskotlin/workers/JavaBuilderFlags.kt
@@ -13,13 +13,12 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package io.bazel.ruleskotlin.workers; +package io.bazel.ruleskotlin.workers /** * Flags used by the java builder. */ -@SuppressWarnings("unused") -public enum JavaBuilderFlags { +enum class JavaBuilderFlags(val flag: String) { TARGET_LABEL("--target_label"), CLASSPATH("--classpath"), JAVAC_OPTS("--javacopts"), @@ -51,11 +50,5 @@ 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; - } + TEST_ONLY("--testonly") }
diff --git a/kotlin/workers/src/io/bazel/ruleskotlin/workers/KotlinToolchain.java b/kotlin/workers/src/io/bazel/ruleskotlin/workers/KotlinToolchain.java deleted file mode 100644 index 4f8e5c1..0000000 --- a/kotlin/workers/src/io/bazel/ruleskotlin/workers/KotlinToolchain.java +++ /dev/null
@@ -1,72 +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; - -import io.bazel.ruleskotlin.workers.compilers.jvm.Locations; -import org.jetbrains.kotlin.preloading.ClassPreloadingUtils; -import org.jetbrains.kotlin.preloading.Preloader; - -import java.io.IOException; -import java.io.PrintStream; -import java.lang.reflect.Method; -import java.nio.file.Paths; -import java.util.function.BiFunction; - -public final class KotlinToolchain { - private static final Object[] NO_ARGS = new Object[]{}; - private final ClassLoader classLoader; - - 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 - * toolchain replacement. - */ - public KotlinCompiler kotlinCompiler() { - try { - Class<?> compilerClass = classLoader.loadClass("org.jetbrains.kotlin.cli.jvm.K2JVMCompiler"); - Class<?> exitCodeClass = classLoader.loadClass("org.jetbrains.kotlin.cli.common.ExitCode"); - - Object compiler = compilerClass.newInstance(); - Method execMethod = compilerClass.getMethod("exec", PrintStream.class, String[].class); - Method getCodeMethod = exitCodeClass.getMethod("getCode"); - - return (args, stream) -> { - final Object exitCodeInstance; - try { - exitCodeInstance = execMethod.invoke(compiler, stream, args); - return (Integer) getCodeMethod.invoke(exitCodeInstance, NO_ARGS); - } catch (Exception e) { - throw new RuntimeException(e); - } - }; - } catch (Exception e) { - throw new RuntimeException(e); - } - } -}
diff --git a/kotlin/workers/src/io/bazel/ruleskotlin/workers/KotlinToolchain.kt b/kotlin/workers/src/io/bazel/ruleskotlin/workers/KotlinToolchain.kt new file mode 100644 index 0000000..6e7a2c8 --- /dev/null +++ b/kotlin/workers/src/io/bazel/ruleskotlin/workers/KotlinToolchain.kt
@@ -0,0 +1,88 @@ +/* + * 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 io.bazel.ruleskotlin.workers.utils.resolveVerified +import io.bazel.ruleskotlin.workers.utils.verifiedRelativeFiles +import org.jetbrains.kotlin.preloading.ClassPreloadingUtils +import org.jetbrains.kotlin.preloading.Preloader +import java.io.PrintStream +import java.nio.file.Path +import java.nio.file.Paths + +@Suppress("PropertyName") +class KotlinToolchain { + companion object { + internal val JAVA_HOME = Paths.get("external", "local_jdk") + internal val KOTLIN_HOME = Paths.get("external", "com_github_jetbrains_kotlin") + + internal val NO_ARGS = arrayOf<Any>() + } + + val JAVAC_PATH = JAVA_HOME.resolveVerified("bin", "javac").toString() + val JAR_TOOL_PATH = JAVA_HOME.resolveVerified("bin", "jar").toString() + val JDEPS_PATH = JAVA_HOME.resolveVerified("bin", "jdeps").toString() + val KOTLIN_LIB_DIR: Path = KOTLIN_HOME.resolveVerified("lib").toPath() + + private val kotlinPreloadJars = KOTLIN_LIB_DIR.verifiedRelativeFiles( + Paths.get("kotlin-compiler.jar") + ) + + val KOTLIN_STD_LIBS = arrayOf( + "kotlin-stdlib.jar", + "kotlin-stdlib-jdk7.jar", + "kotlin-stdlib-jdk8.jar" + ) + + private val classLoader: ClassLoader by lazy { + ClassPreloadingUtils.preloadClasses( + kotlinPreloadJars, + Preloader.DEFAULT_CLASS_NUMBER_ESTIMATE, + Thread.currentThread().contextClassLoader, + null + ) + } + + interface KotlinCompiler { + fun compile(args: Array<String>, out: PrintStream): Int + } + + /** + * Load the Kotlin compiler into a Preloading classLoader. + */ + val kotlinCompiler: KotlinCompiler by lazy { + val compilerClass = classLoader.loadClass("org.jetbrains.kotlin.cli.jvm.K2JVMCompiler") + val exitCodeClass = classLoader.loadClass("org.jetbrains.kotlin.cli.common.ExitCode") + + val compiler = compilerClass.newInstance() + val execMethod = compilerClass.getMethod("exec", PrintStream::class.java, Array<String>::class.java) + val getCodeMethod = exitCodeClass.getMethod("getCode") + + object : KotlinCompiler { + override fun compile(args: Array<String>, out: PrintStream): Int { + val exitCodeInstance: Any + try { + exitCodeInstance = execMethod.invoke(compiler, out, args) + return getCodeMethod.invoke(exitCodeInstance, *NO_ARGS) as Int + } catch (e: Exception) { + throw RuntimeException(e) + } + } + } + } +} + +
diff --git a/kotlin/workers/src/io/bazel/ruleskotlin/workers/Meta.java b/kotlin/workers/src/io/bazel/ruleskotlin/workers/Meta.java deleted file mode 100644 index 94a0712..0000000 --- a/kotlin/workers/src/io/bazel/ruleskotlin/workers/Meta.java +++ /dev/null
@@ -1,67 +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; - -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/Meta.kt b/kotlin/workers/src/io/bazel/ruleskotlin/workers/Meta.kt new file mode 100644 index 0000000..a0aca11 --- /dev/null +++ b/kotlin/workers/src/io/bazel/ruleskotlin/workers/Meta.kt
@@ -0,0 +1,43 @@ +/* + * 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 + +open class Meta<T: Any>( + private val id: String, + private val defaultValue: T? = null +) { + constructor(id: String) : this(id, null) + + /** + * Gets a mandatory value. + */ + fun mustGet(ctx: Context): T = + ctx[this] ?: checkNotNull(defaultValue) { "mandatory meta parameter missing in context and does not have a default value" } + + /** + * Gets an optional value, if it has not been bound the default value is used. + */ + operator fun get(ctx: Context): T? { + val res = ctx[this] + return when { + res != null -> res + defaultValue != null -> defaultValue + else -> null + } + } + + fun bind(ctx: Context, value: T) { check(ctx.putIfAbsent(this, value) == null) { "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 deleted file mode 100644 index d6bdf85..0000000 --- a/kotlin/workers/src/io/bazel/ruleskotlin/workers/compilers/jvm/KotlinJvmBuilder.java +++ /dev/null
@@ -1,70 +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 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 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) { - Context context = Context.from(args); - Integer exitCode = 0; - for (BuildAction action : compileActions) { - exitCode = action.apply(context); - if (exitCode != 0) - break; - } - return exitCode; - } - - public static void main(String[] args) { - KotlinJvmBuilder kotlinBuilder = new KotlinJvmBuilder(); - BazelWorker<KotlinJvmBuilder> kotlinCompilerBazelWorker = new BazelWorker<>( - kotlinBuilder, - System.err, - "KotlinCompile" - ); - System.exit(kotlinCompilerBazelWorker.apply(args)); - } -}
diff --git a/kotlin/workers/src/io/bazel/ruleskotlin/workers/compilers/jvm/KotlinJvmBuilder.kt b/kotlin/workers/src/io/bazel/ruleskotlin/workers/compilers/jvm/KotlinJvmBuilder.kt new file mode 100644 index 0000000..571bdd1 --- /dev/null +++ b/kotlin/workers/src/io/bazel/ruleskotlin/workers/compilers/jvm/KotlinJvmBuilder.kt
@@ -0,0 +1,71 @@ +/* + * 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.* +import io.bazel.ruleskotlin.workers.compilers.jvm.actions.* + +import java.io.IOException + +/** + * Bazel Kotlin Compiler worker. + */ +class KotlinJvmBuilder private constructor() : CommandLineProgram { + private val compileActions: Array<BuildAction> + + init { + val toolchain: KotlinToolchain + try { + toolchain = KotlinToolchain() + } catch (e: IOException) { + throw RuntimeException("could not initialize toolchain", e) + } + + compileActions = arrayOf( + Initialize(toolchain), + KotlinMainCompile(toolchain), + JavaMainCompile(toolchain), + ProcessCompileResult(toolchain), + CreateOutputJar(toolchain), + GenerateJdepsFile(toolchain) + ) + } + + override fun apply(args: List<String>): Int { + val ctx = Context.from(args) + var exitCode = 0 + for (action in compileActions) { + exitCode = action(ctx) + if (exitCode != 0) + break + } + return exitCode + } + + companion object { + @JvmStatic + fun main(args: Array<String>) { + val kotlinBuilder = KotlinJvmBuilder() + val kotlinCompilerBazelWorker = BazelWorker( + kotlinBuilder, + System.err, + "KotlinCompile" + ) + System.exit(kotlinCompilerBazelWorker.apply(args.toList())) + } + } +}
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 deleted file mode 100644 index c877564..0000000 --- a/kotlin/workers/src/io/bazel/ruleskotlin/workers/compilers/jvm/Locations.java +++ /dev/null
@@ -1,54 +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.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 { - KOTLIN_REPO(Paths.get("external", "com_github_jetbrains_kotlin")), - JAVA_HOME(Paths.get("external", "local_jdk")); - - private final Path path; - - Locations(Path path) { - this.path = path; - } - - public final File resolveVerified(String... parts) { - return verified(path.resolve(Paths.get(parts[0], Arrays.copyOfRange(parts, 1, parts.length)))); - } - - /** - * Return a stream of paths that are known to exists relative to this location. - */ - 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) { - File asFile = target.toFile(); - if (!asFile.exists()) { - throw new RuntimeException("location " + this.name() + " did not have relative path file " + target); - } - return asFile; - } - } \ 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 deleted file mode 100644 index eb8ff54..0000000 --- a/kotlin/workers/src/io/bazel/ruleskotlin/workers/compilers/jvm/Metas.java +++ /dev/null
@@ -1,42 +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 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/Metas.kt b/kotlin/workers/src/io/bazel/ruleskotlin/workers/compilers/jvm/Metas.kt new file mode 100644 index 0000000..6b56242 --- /dev/null +++ b/kotlin/workers/src/io/bazel/ruleskotlin/workers/compilers/jvm/Metas.kt
@@ -0,0 +1,41 @@ +/* + * 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 + +/** + * Meta is a key to some compilation state,. + */ +object Metas { + // mandatory: the package part of the label. + val PKG = Meta<String>("package") + // mandatory: The target part of the label. + val TARGET = Meta<String>("target") + // mandatory: the class staging directory. + val CLASSES_DIRECTORY = Meta<Path>("class_directory") + // mandatory: If this is non empty then it is a mixed mode operation. + val JAVA_SOURCES = Meta<List<String>>("java_sources") + // mandatory: + val ALL_SOURCES = Meta<List<String>>("all_sources") + // mandatory: + val KOTLINC_RESULT = CompileResult.Meta("kotlin_compile_result") + // optional: when not a mixed mode operation. + val JAVAC_RESULT = 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 deleted file mode 100644 index aba021e..0000000 --- a/kotlin/workers/src/io/bazel/ruleskotlin/workers/compilers/jvm/actions/CreateOutputJar.java +++ /dev/null
@@ -1,52 +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.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/CreateOutputJar.kt b/kotlin/workers/src/io/bazel/ruleskotlin/workers/compilers/jvm/actions/CreateOutputJar.kt new file mode 100644 index 0000000..38545fa --- /dev/null +++ b/kotlin/workers/src/io/bazel/ruleskotlin/workers/compilers/jvm/actions/CreateOutputJar.kt
@@ -0,0 +1,43 @@ +/* + * 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.KotlinToolchain +import io.bazel.ruleskotlin.workers.compilers.jvm.Metas +import io.bazel.ruleskotlin.workers.utils.executeAndAwaitSuccess + +/** + * Create a jar from the classes. + */ +class CreateOutputJar(toolchain: KotlinToolchain) : BuildAction("create output jar", toolchain) { + override fun invoke(ctx: Context): Int { + try { + executeAndAwaitSuccess(10, + toolchain.JAR_TOOL_PATH, + "cf", checkNotNull(Flags.OUTPUT_CLASSJAR[ctx]), + "-C", Metas.CLASSES_DIRECTORY.mustGet(ctx).toString(), + "." + ) + } catch (e: Exception) { + throw 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 deleted file mode 100644 index 683cce5..0000000 --- a/kotlin/workers/src/io/bazel/ruleskotlin/workers/compilers/jvm/actions/GenerateJdepsFile.java +++ /dev/null
@@ -1,81 +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 com.google.devtools.build.lib.view.proto.Deps; -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.utils.IOUtils; - -import java.io.FileOutputStream; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.util.List; -import java.util.function.Predicate; - -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(); - - private static final Predicate<String> IS_KOTLIN_IMPLICIT = JdepsParser.pathSuffixMatchingPredicate( - Paths.get("external", "com_github_jetbrains_kotlin", "lib"), - "kotlin-stdlib.jar", - "kotlin-stdlib-jdk7.jar", - "kotlin-stdlib-jdk8.jar"); - - public static final GenerateJdepsFile INSTANCE = new GenerateJdepsFile(); - - private GenerateJdepsFile() { - } - - @Override - public Integer apply(Context ctx) { - final String - classJar = OUTPUT_CLASSJAR.get(ctx), - classPath = CLASSPATH.get(ctx), - output = OUTPUT_JDEPS.get(ctx); - Deps.Dependencies jdepsContent; - try { - List<String> jdepLines = IOUtils.executeAndWaitOutput(10, JDEPS_PATH, "-cp", classPath, classJar); - jdepsContent = JdepsParser.parse( - LABEL.get(ctx), - classJar, - classPath, - jdepLines.stream(), - IS_KOTLIN_IMPLICIT - ); - } catch (Exception e) { - throw new RuntimeException("error reading or parsing jdeps file", IOUtils.getRootCause(e)); - } - - try { - Path outputPath = Paths.get(output); - Files.deleteIfExists(outputPath); - try (FileOutputStream fileOutputStream = new FileOutputStream(Files.createFile(outputPath).toFile())) { - jdepsContent.writeTo(fileOutputStream); - } - } catch (Exception 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/GenerateJdepsFile.kt b/kotlin/workers/src/io/bazel/ruleskotlin/workers/compilers/jvm/actions/GenerateJdepsFile.kt new file mode 100644 index 0000000..120e38d --- /dev/null +++ b/kotlin/workers/src/io/bazel/ruleskotlin/workers/compilers/jvm/actions/GenerateJdepsFile.kt
@@ -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.compilers.jvm.actions + +import com.google.devtools.build.lib.view.proto.Deps +import io.bazel.ruleskotlin.workers.BuildAction +import io.bazel.ruleskotlin.workers.Context +import io.bazel.ruleskotlin.workers.Flags.* +import io.bazel.ruleskotlin.workers.KotlinToolchain +import io.bazel.ruleskotlin.workers.compilers.jvm.utils.JdepsParser +import io.bazel.ruleskotlin.workers.utils.executeAndWaitOutput +import io.bazel.ruleskotlin.workers.utils.rootCause +import java.io.FileOutputStream +import java.nio.file.Files +import java.nio.file.Paths + + +class GenerateJdepsFile(toolchain: KotlinToolchain) : BuildAction("generate jdeps", toolchain) { + private val isKotlinImplicit = JdepsParser.pathSuffixMatchingPredicate(toolchain.KOTLIN_LIB_DIR, *toolchain.KOTLIN_STD_LIBS) + + override fun invoke(ctx: Context): Int { + val classJar = checkNotNull(OUTPUT_CLASSJAR[ctx]) + val classPath = checkNotNull(CLASSPATH[ctx]) + val output = checkNotNull(OUTPUT_JDEPS[ctx]) + val label = checkNotNull(LABEL[ctx]) + val jdepsContent: Deps.Dependencies + + try { + jdepsContent = executeAndWaitOutput(10, toolchain.JDEPS_PATH, "-cp", classPath, classJar).let { + JdepsParser.parse(label, classJar, classPath, it.stream(), isKotlinImplicit) + } + } catch (e: Exception) { + throw RuntimeException("error reading or parsing jdeps file", e.rootCause) + } + + try { + Paths.get(output).also { + Files.deleteIfExists(it) + FileOutputStream(Files.createFile(it).toFile()).use { + jdepsContent.writeTo(it) + } + } + } catch (e: Exception) { + throw RuntimeException("error writing out jdeps file", e.rootCause) + } + 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 deleted file mode 100644 index 2e175bc..0000000 --- a/kotlin/workers/src/io/bazel/ruleskotlin/workers/compilers/jvm/actions/Initialize.java +++ /dev/null
@@ -1,108 +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.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/Initialize.kt b/kotlin/workers/src/io/bazel/ruleskotlin/workers/compilers/jvm/actions/Initialize.kt new file mode 100644 index 0000000..6a3f3b9 --- /dev/null +++ b/kotlin/workers/src/io/bazel/ruleskotlin/workers/compilers/jvm/actions/Initialize.kt
@@ -0,0 +1,98 @@ +/* + * 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.* +import io.bazel.ruleskotlin.workers.compilers.jvm.Metas + +import io.bazel.ruleskotlin.workers.utils.purgeDirectory + +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 + +/** + * Should be the first step, does mandatory pre-processing. + */ +class Initialize(toolchain: KotlinToolchain) : BuildAction("initialize KotlinBuilder", toolchain) { + override fun invoke(ctx: Context): Int { + ctx.apply( + ::initializeAndBindBindDirectories, + ::bindLabelComponents, + ::bindSources + ) + return 0 + } + + private fun bindSources(ctx: Context) { + val javaSources = ArrayList<String>() + val allSources = ArrayList<String>() + for (src in requireNotNull(Flags.SOURCES[ctx]).split(":")) { + when { + src.endsWith(".java") -> { + javaSources.add(src) + allSources.add(src) + } + src.endsWith(".kt") -> allSources.add(src) + else -> throw RuntimeException("unrecognised file type: $src") + } + } + Metas.JAVA_SOURCES.bind(ctx, Collections.unmodifiableList(javaSources)) + Metas.ALL_SOURCES.bind(ctx, Collections.unmodifiableList(allSources)) + } + + private fun initializeAndBindBindDirectories(ctx: Context) { + val outputBase: Path + + try { + outputBase = Files.createDirectories(Paths.get(checkNotNull(Flags.COMPILER_OUTPUT_BASE[ctx]))) + } catch (e: IOException) { + throw RuntimeException("could not create compiler output base", e) + } + + try { + outputBase.purgeDirectory() + } catch (e: IOException) { + throw RuntimeException("could not purge output directory", e) + } + + createAndBindComponentDirectory(ctx, outputBase, Metas.CLASSES_DIRECTORY, "_classes") + } + + private fun createAndBindComponentDirectory(ctx: Context, outputBase: Path, key: Meta<Path>, component: String) { + try { + key.bind(ctx, Files.createDirectories(outputBase.resolve(component))) + } catch (e: IOException) { + throw RuntimeException("could not create subdirectory for component " + component, e) + } + + } + + /** + * parses the label, sets up the meta elements and returns the target part. + */ + private fun bindLabelComponents(ctx: Context) { + val label = requireNotNull(Flags.LABEL[ctx]) + val parts = label.split(":") + require(parts.size == 2) { "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 deleted file mode 100644 index da6da78..0000000 --- a/kotlin/workers/src/io/bazel/ruleskotlin/workers/compilers/jvm/actions/JavaMainCompile.java +++ /dev/null
@@ -1,52 +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.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/JavaMainCompile.kt b/kotlin/workers/src/io/bazel/ruleskotlin/workers/compilers/jvm/actions/JavaMainCompile.kt new file mode 100644 index 0000000..3ae306b --- /dev/null +++ b/kotlin/workers/src/io/bazel/ruleskotlin/workers/compilers/jvm/actions/JavaMainCompile.kt
@@ -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.actions + +import io.bazel.ruleskotlin.workers.BuildAction +import io.bazel.ruleskotlin.workers.Context +import io.bazel.ruleskotlin.workers.Flags +import io.bazel.ruleskotlin.workers.KotlinToolchain +import io.bazel.ruleskotlin.workers.compilers.jvm.Metas +import io.bazel.ruleskotlin.workers.utils.executeAndAwait + +/** + * Simple java compile action that invokes javac directly and simply. + */ +class JavaMainCompile(toolchain: KotlinToolchain) : BuildAction("compile java classes", toolchain) { + override fun invoke(ctx: Context): Int { + val javaSources = Metas.JAVA_SOURCES.mustGet(ctx) + val classpath = checkNotNull(Flags.CLASSPATH[ctx]) + + if (!javaSources.isEmpty()) { + val classesDirectory = Metas.CLASSES_DIRECTORY.mustGet(ctx).toString() + + val args = mutableListOf(toolchain.JAVAC_PATH, "-cp", "$classesDirectory/:$classpath", "-d", classesDirectory).also { it.addAll(javaSources) } + Metas.JAVAC_RESULT.runAndBind(ctx) { executeAndAwait(30, args) } + } + 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 deleted file mode 100644 index ad467fe..0000000 --- a/kotlin/workers/src/io/bazel/ruleskotlin/workers/compilers/jvm/actions/KotlinMainCompile.java +++ /dev/null
@@ -1,87 +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.*; -import io.bazel.ruleskotlin.workers.compilers.jvm.Metas; -import io.bazel.ruleskotlin.workers.compilers.jvm.utils.KotlinCompilerOutputProcessor; - - -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; - -/** - * 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 { - private final KotlinToolchain.KotlinCompiler kotlinCompiler; - - public KotlinMainCompile(KotlinToolchain toolchains) { - this.kotlinCompiler = toolchains.kotlinCompiler(); - } - - /** - * Default fields that are directly mappable to kotlin compiler args. - */ - private static final Flags[] COMPILE_MAPPED_FLAGS = new Flags[]{ - Flags.CLASSPATH, - Flags.KOTLIN_API_VERSION, - Flags.KOTLIN_LANGUAGE_VERSION, - Flags.KOTLIN_JVM_TARGET - }; - - /** - * Evaluate the compilation context and add Metadata to the ctx if needed. - * - * @return The args to pass to the kotlin compile class. - */ - private static String[] setupCompileContext(Context ctx) { - List<String> args = new ArrayList<>(); - 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; - 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; - })); - 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/KotlinMainCompile.kt b/kotlin/workers/src/io/bazel/ruleskotlin/workers/compilers/jvm/actions/KotlinMainCompile.kt new file mode 100644 index 0000000..bf5b1ab --- /dev/null +++ b/kotlin/workers/src/io/bazel/ruleskotlin/workers/compilers/jvm/actions/KotlinMainCompile.kt
@@ -0,0 +1,90 @@ +/* + * 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.* +import io.bazel.ruleskotlin.workers.compilers.jvm.Metas +import io.bazel.ruleskotlin.workers.compilers.jvm.utils.KotlinCompilerOutputProcessor + + +import java.util.ArrayList +import java.util.Collections + +/** + * Either compiles to a jar directly or when performing mixed-mode-compilation compiles to a temp directory first. + * + * + * Mixed-Mode: + * + * + * 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.. + */ +class KotlinMainCompile(toolchain: KotlinToolchain) : BuildAction("compile kotlin classes", toolchain) { + companion object { + /** + * Default fields that are directly mappable to kotlin compiler args. + */ + private val COMPILE_MAPPED_FLAGS = arrayOf( + Flags.CLASSPATH, + Flags.KOTLIN_API_VERSION, + Flags.KOTLIN_LANGUAGE_VERSION, + Flags.KOTLIN_JVM_TARGET) + + /** + * Evaluate the compilation context and add Metadata to the ctx if needed. + * + * @return The args to pass to the kotlin compile class. + */ + private fun setupCompileContext(ctx: Context): Array<String> { + val args = ArrayList<String>() + Collections.addAll(args, "-d", Metas.CLASSES_DIRECTORY.mustGet(ctx).toString()) + ctx.of(*COMPILE_MAPPED_FLAGS).forEach { field, arg -> + args.add(field.kotlinFlag!!); args.add(arg) + + } + args.addAll(Metas.ALL_SOURCES.mustGet(ctx)) + return args.toTypedArray() + } + } + + override fun invoke(ctx: Context): Int { + val outputProcessor = KotlinCompilerOutputProcessor.ForKotlinC(System.out) + + val exitCode = try { + toolchain.kotlinCompiler.compile(setupCompileContext(ctx), outputProcessor.collector) + } catch (ex: Exception) { + outputProcessor.process() + throw ex + } + + 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) { _ -> + outputProcessor.process() + exitCode + }) + return 0 + } else { + outputProcessor.process() + throw 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 deleted file mode 100644 index 76c8674..0000000 --- a/kotlin/workers/src/io/bazel/ruleskotlin/workers/compilers/jvm/actions/KotlinRenderClassCompileResult.java +++ /dev/null
@@ -1,59 +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.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/actions/ProcessCompileResult.kt b/kotlin/workers/src/io/bazel/ruleskotlin/workers/compilers/jvm/actions/ProcessCompileResult.kt new file mode 100644 index 0000000..01fbf3b --- /dev/null +++ b/kotlin/workers/src/io/bazel/ruleskotlin/workers/compilers/jvm/actions/ProcessCompileResult.kt
@@ -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.KotlinToolchain +import io.bazel.ruleskotlin.workers.compilers.jvm.Metas + + +/** + * 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 [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. + */ +class ProcessCompileResult(toolchain: KotlinToolchain) : BuildAction("render class compile output", toolchain) { + override fun invoke(ctx: Context): Int { + val kotlincResult = Metas.KOTLINC_RESULT.mustGet(ctx) + val javacResult = Metas.JAVAC_RESULT[ctx] + + return if (javacResult == null) { + kotlincResult.render(ctx) + } else { + try { + javacResult.propogateError("javac failed") + if (kotlincResult.status() != 0) { + return kotlincResult.status() + } else if (javacResult.status() != 0) { + // treat all javac statuses as non terminal compile errors. + return 1 + } + 0 + } finally { + kotlincResult.render(ctx) + } + } + } +}
diff --git a/kotlin/workers/src/io/bazel/ruleskotlin/workers/compilers/jvm/utils/JdepsParser.java b/kotlin/workers/src/io/bazel/ruleskotlin/workers/compilers/jvm/utils/JdepsParser.java deleted file mode 100644 index 9d705b8..0000000 --- a/kotlin/workers/src/io/bazel/ruleskotlin/workers/compilers/jvm/utils/JdepsParser.java +++ /dev/null
@@ -1,144 +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 com.google.devtools.build.lib.view.proto.Deps; - -import java.nio.file.Path; -import java.nio.file.Paths; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Map; -import java.util.Set; -import java.util.function.Predicate; -import java.util.stream.Stream; - -public class JdepsParser { - private final String filename; - private final String packageSuffix; - private final Predicate<String> isImplicit; - - private final Map<String, Deps.Dependency.Builder> depMap = new HashMap<>(); - private final Set<String> packages = new HashSet<>(); - - private JdepsParser(String filename, Predicate<String> isImplicit) { - this.filename = filename; - this.packageSuffix = " (" + filename + ")"; - this.isImplicit = isImplicit; - } - - private void consumeJarLine(String classJarPath, Deps.Dependency.Kind kind) { - Path path = Paths.get(classJarPath); - - // ignore absolute files, -- jdk jar paths etc. - // only process jar files - if (!(path.isAbsolute() || !classJarPath.endsWith(".jar"))) { - Deps.Dependency.Builder entry = depMap.computeIfAbsent(classJarPath, (key) -> { - Deps.Dependency.Builder depBuilder = Deps.Dependency.newBuilder(); - depBuilder.setPath(classJarPath); - depBuilder.setKind(kind); - - if (isImplicit.test(classJarPath)) { - depBuilder.setKind(Deps.Dependency.Kind.IMPLICIT); - } - return depBuilder; - }); - - // don't flip an implicit dep. - if (entry.getKind() != Deps.Dependency.Kind.IMPLICIT) { - entry.setKind(kind); - } - } - } - - private enum Mode { - COLLECT_DEPS, - DETERMINE_JDK, - COLLECT_PACKAGES_JDK8, - COLLECT_PACKAGES_JDK9 - } - - private Mode mode = Mode.COLLECT_DEPS; - - // maybe simplify this by tokenizing on whitespace and arrows. - private void processLine(String line) { - String trimmedLine = line.trim(); - switch (mode) { - case COLLECT_DEPS: - if (!line.startsWith(" ")) { - String[] parts = line.split(" -> "); - if (parts.length == 2) { - if (!parts[0].equals(filename)) { - throw new RuntimeException("should only get dependencies for dep: " + filename); - } - consumeJarLine(parts[1], Deps.Dependency.Kind.EXPLICIT); - } - } else { - mode = Mode.DETERMINE_JDK; - processLine(line); - } - break; - case DETERMINE_JDK: - mode = Mode.COLLECT_PACKAGES_JDK8; - if (!line.endsWith(packageSuffix)) { - mode = Mode.COLLECT_PACKAGES_JDK9; - } - processLine(line); - break; - case COLLECT_PACKAGES_JDK8: - if (trimmedLine.endsWith(packageSuffix)) { - packages.add(trimmedLine.substring(0, trimmedLine.length() - packageSuffix.length())); - } else if (trimmedLine.startsWith("-> ")) { - // ignore package detail lines, in the jdk8 format these start with arrows. - } else throw new RuntimeException("unexpected line while collecting packages: " + line); - break; - case COLLECT_PACKAGES_JDK9: - String[] pkg = trimmedLine.split("\\s+"); - packages.add(pkg[0]); - break; - } - } - - - public static Predicate<String> pathSuffixMatchingPredicate(Path directory, String... jars) { - String[] suffixes = Stream.of(jars).map(lib -> directory.resolve(lib).toString()).toArray(String[]::new); - return (jar) -> { - for (String implicitJarsEnding : suffixes) { - if (jar.endsWith(implicitJarsEnding)) { - return true; - } - } - return false; - }; - } - - public static Deps.Dependencies parse(String label, String classJar, String classPath, Stream<String> jdepLines, Predicate<String> isImplicit) { - String filename = Paths.get(classJar).getFileName().toString(); - JdepsParser jdepsParser = new JdepsParser(filename, isImplicit); - Stream.of(classPath.split(":")).forEach(x -> jdepsParser.consumeJarLine(x, Deps.Dependency.Kind.UNUSED)); - jdepLines.forEach(jdepsParser::processLine); - - Deps.Dependencies.Builder rootBuilder = Deps.Dependencies.newBuilder(); - rootBuilder.setSuccess(false); - rootBuilder.setRuleLabel(label); - - rootBuilder.addAllContainedPackage(jdepsParser.packages); - jdepsParser.depMap.values().forEach(b -> rootBuilder.addDependency(b.build())); - - rootBuilder.setSuccess(true); - return rootBuilder.build(); - } -} \ No newline at end of file
diff --git a/kotlin/workers/src/io/bazel/ruleskotlin/workers/compilers/jvm/utils/JdepsParser.kt b/kotlin/workers/src/io/bazel/ruleskotlin/workers/compilers/jvm/utils/JdepsParser.kt new file mode 100644 index 0000000..dc835dd --- /dev/null +++ b/kotlin/workers/src/io/bazel/ruleskotlin/workers/compilers/jvm/utils/JdepsParser.kt
@@ -0,0 +1,131 @@ +/* + * 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 com.google.devtools.build.lib.view.proto.Deps +import java.nio.file.Path +import java.nio.file.Paths +import java.util.* +import java.util.function.Predicate +import java.util.stream.Stream + +class JdepsParser private constructor(private val filename: String, private val isImplicit: Predicate<String>) { + private val packageSuffix: String = " ($filename)" + + private val depMap = HashMap<String, Deps.Dependency.Builder>() + private val packages = HashSet<String>() + + private var mode = Mode.COLLECT_DEPS + + private fun consumeJarLine(classJarPath: String, kind: Deps.Dependency.Kind) { + val path = Paths.get(classJarPath) + + // ignore absolute files, -- jdk jar paths etc. + // only process jar files + if (!(path.isAbsolute || !classJarPath.endsWith(".jar"))) { + val entry = depMap.computeIfAbsent(classJarPath) { + val depBuilder = Deps.Dependency.newBuilder() + depBuilder.path = classJarPath + depBuilder.kind = kind + + if (isImplicit.test(classJarPath)) { + depBuilder.kind = Deps.Dependency.Kind.IMPLICIT + } + depBuilder + } + + // don't flip an implicit dep. + if (entry.kind != Deps.Dependency.Kind.IMPLICIT) { + entry.kind = kind + } + } + } + + private enum class Mode { + COLLECT_DEPS, + DETERMINE_JDK, + COLLECT_PACKAGES_JDK8, + COLLECT_PACKAGES_JDK9 + } + + // maybe simplify this by tokenizing on whitespace and arrows. + private fun processLine(line: String) { + val trimmedLine = line.trim { it <= ' ' } + when (mode) { + JdepsParser.Mode.COLLECT_DEPS -> if (!line.startsWith(" ")) { + val parts = line.split(" -> ".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray() + if (parts.size == 2) { + if (parts[0] != filename) { + throw RuntimeException("should only get dependencies for dep: " + filename) + } + consumeJarLine(parts[1], Deps.Dependency.Kind.EXPLICIT) + } + } else { + mode = Mode.DETERMINE_JDK + processLine(line) + } + JdepsParser.Mode.DETERMINE_JDK -> { + mode = Mode.COLLECT_PACKAGES_JDK8 + if (!line.endsWith(packageSuffix)) { + mode = Mode.COLLECT_PACKAGES_JDK9 + } + processLine(line) + } + JdepsParser.Mode.COLLECT_PACKAGES_JDK8 -> when { + trimmedLine.endsWith(packageSuffix) -> packages.add(trimmedLine.substring(0, trimmedLine.length - packageSuffix.length)) + trimmedLine.startsWith("-> ") -> { + // ignore package detail lines, in the jdk8 format these start with arrows. + } + else -> throw RuntimeException("unexpected line while collecting packages: " + line) + } + JdepsParser.Mode.COLLECT_PACKAGES_JDK9 -> { + val pkg = trimmedLine.split("\\s+".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray() + packages.add(pkg[0]) + } + } + } + + companion object { + fun pathSuffixMatchingPredicate(directory: Path, vararg jars: String): Predicate<String> { + val suffixes = jars.map { directory.resolve(it).toString() } + return Predicate { jar -> + for (implicitJarsEnding in suffixes) { + if (jar.endsWith(implicitJarsEnding)) { + return@Predicate true + } + } + false + } + } + + fun parse(label: String, classJar: String, classPath: String, jdepLines: Stream<String>, isImplicit: Predicate<String>): Deps.Dependencies { + val filename = Paths.get(classJar).fileName.toString() + val jdepsParser = JdepsParser(filename, isImplicit) + Stream.of(*classPath.split(":".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()).forEach { x -> jdepsParser.consumeJarLine(x, Deps.Dependency.Kind.UNUSED) } + jdepLines.forEach { jdepsParser.processLine(it) } + + val rootBuilder = Deps.Dependencies.newBuilder() + rootBuilder.success = false + rootBuilder.ruleLabel = label + + rootBuilder.addAllContainedPackage(jdepsParser.packages) + jdepsParser.depMap.values.forEach { b -> rootBuilder.addDependency(b.build()) } + + rootBuilder.success = true + return rootBuilder.build() + } + } +} \ No newline at end of file
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 deleted file mode 100644 index f40d071..0000000 --- a/kotlin/workers/src/io/bazel/ruleskotlin/workers/compilers/jvm/utils/KotlinCompilerOutputProcessor.java +++ /dev/null
@@ -1,76 +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.Paths; -import java.util.stream.Collectors; - - -/** - * Utility class to perform common pre-processing on the compiler output before it is passed onto a delegate - * PrintStream. - */ -// The kotlin compiler produces absolute file paths but the intellij plugin expects workspace root relative paths to -// render errors. -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; - final PrintStream delegate; - - private KotlinCompilerOutputProcessor(PrintStream delegate) { - this.delegate = 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() { - 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/KotlinCompilerOutputProcessor.kt b/kotlin/workers/src/io/bazel/ruleskotlin/workers/compilers/jvm/utils/KotlinCompilerOutputProcessor.kt new file mode 100644 index 0000000..899fbc8 --- /dev/null +++ b/kotlin/workers/src/io/bazel/ruleskotlin/workers/compilers/jvm/utils/KotlinCompilerOutputProcessor.kt
@@ -0,0 +1,66 @@ +/* + * 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.ByteArrayInputStream +import java.io.ByteArrayOutputStream +import java.io.File +import java.io.PrintStream +import java.nio.file.Paths + + +/** + * Utility class to perform common pre-processing on the compiler output before it is passed onto a delegate + * PrintStream. + */ +// The kotlin compiler produces absolute file paths but the intellij plugin expects workspace root relative paths to +// render errors. +abstract class KotlinCompilerOutputProcessor private constructor(internal val delegate: PrintStream) { + private val byteArrayOutputStream = ByteArrayOutputStream() + // Get the absolute path to ensure the sandbox root is resolved. + private val executionRoot = Paths.get("").toAbsolutePath().toString() + File.separator + + val collector: PrintStream = PrintStream(byteArrayOutputStream) + + class ForKotlinC(delegate: PrintStream) : KotlinCompilerOutputProcessor(delegate) { + override fun processLine(line: String): Boolean { + delegate.println(trimExecutionRootPrefix(line)) + return true + } + } + + internal fun trimExecutionRootPrefix(toPrint: String): String { + // trim off the workspace component + return if (toPrint.startsWith(executionRoot)) { + toPrint.replaceFirst(executionRoot.toRegex(), "") + } else toPrint + } + + protected abstract fun processLine(line: String): Boolean + + fun process() { + try { + for (s in ByteArrayInputStream(byteArrayOutputStream.toByteArray()).bufferedReader().lineSequence()) { + val shouldContinue = processLine(s) + if (!shouldContinue) { + break + } + } + } finally { + delegate.flush() + } + } +}
diff --git a/kotlin/workers/src/io/bazel/ruleskotlin/workers/utils/IOUtils.java b/kotlin/workers/src/io/bazel/ruleskotlin/workers/utils/IOUtils.java deleted file mode 100644 index d4e83eb..0000000 --- a/kotlin/workers/src/io/bazel/ruleskotlin/workers/utils/IOUtils.java +++ /dev/null
@@ -1,133 +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.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/kotlin/workers/src/io/bazel/ruleskotlin/workers/utils/IOUtils.kt b/kotlin/workers/src/io/bazel/ruleskotlin/workers/utils/IOUtils.kt new file mode 100644 index 0000000..f743403 --- /dev/null +++ b/kotlin/workers/src/io/bazel/ruleskotlin/workers/utils/IOUtils.kt
@@ -0,0 +1,126 @@ +/* + * 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. + */ +@file:JvmName("IOUtils") + +package io.bazel.ruleskotlin.workers.utils + +import java.io.* +import java.nio.file.Path +import java.nio.file.Paths +import java.util.* +import java.util.concurrent.CompletableFuture +import java.util.concurrent.TimeUnit +import java.util.concurrent.TimeoutException + +fun executeAndWaitOutput(timeoutSeconds: Int, vararg command: String): List<String> { + try { + val builder = ProcessBuilder(*command).redirectError(ProcessBuilder.Redirect.INHERIT) + val process = builder.start() + val al = ArrayList<String>() + var streamReader: CompletableFuture<Void>? = null + try { + BufferedReader(InputStreamReader(process.inputStream)).use { output -> + streamReader = CompletableFuture.runAsync { + while (true) { + try { + val line = output.readLine() ?: break + al.add(line) + } catch (e: IOException) { + throw UncheckedIOException(e) + } + + } + } + executeAwait(timeoutSeconds, process) + return al + } + } finally { + if (streamReader != null && !streamReader!!.isDone) { + streamReader!!.cancel(true) + } + } + } catch (e: Exception) { + throw RuntimeException(e) + } +} + +private fun executeAwait(timeoutSeconds: Int, process: Process): Int { + try { + if (!process.waitFor(timeoutSeconds.toLong(), TimeUnit.SECONDS)) { + throw TimeoutException() + } + return process.exitValue() + } finally { + if (process.isAlive) { + process.destroy() + } + } +} + +fun executeAndAwait(timeoutSeconds: Int, args: List<String>): Int { + val process = ProcessBuilder(*args.toTypedArray()).let { + it.redirectError(ProcessBuilder.Redirect.PIPE) + it.redirectOutput(ProcessBuilder.Redirect.PIPE) + it.start() + } + + var isr: BufferedReader? = null + var esr: BufferedReader? = null + + try { + isr = process.inputStream.bufferedReader() + esr = process.errorStream.bufferedReader() + return executeAwait(timeoutSeconds, process) + } finally { + isr?.drainTo(System.out) + esr?.drainTo(System.err) + } +} + +fun executeAndAwaitSuccess(timeoutSeconds: Int, vararg command: String) { + val status = executeAndAwait(timeoutSeconds, command.toList()) + check(status == 0) { + "process failed with status: $status" + } +} + +private fun BufferedReader.drainTo(pw: PrintStream) { + lines().forEach(pw::println); close() +} + +fun Path.purgeDirectory() { + toFile().listFiles().forEach { check(it.deleteRecursively()) { "$it could not be deleted" } } +} + +fun Path.resolveVerified(vararg parts: String): File = resolve(Paths.get(parts[0], *Arrays.copyOfRange(parts, 1, parts.size))).verified() + +/** + * Return a stream of paths that are known to exists relative to this location. + */ +fun Path.verifiedRelativeFiles(vararg paths: Path): List<File> = paths.map { relative -> resolve(relative).verified() } + +private fun Path.verified(): File = this.toFile().also { check(it.exists()) { "file did not exist: $this" } } + + +val Throwable.rootCause: Throwable + get() { + var result = this + do { + val cause = result.cause + if (cause != null) result = cause + } while (cause != null && result != cause) + return result + }
diff --git a/kotlin/workers/unittests/io/bazel/ruleskotlin/workers/compilers/jvm/utils/JdepsParserTest.java b/kotlin/workers/unittests/io/bazel/ruleskotlin/workers/compilers/jvm/utils/JdepsParserTest.java index b61377c..ec49d86 100644 --- a/kotlin/workers/unittests/io/bazel/ruleskotlin/workers/compilers/jvm/utils/JdepsParserTest.java +++ b/kotlin/workers/unittests/io/bazel/ruleskotlin/workers/compilers/jvm/utils/JdepsParserTest.java
@@ -89,7 +89,7 @@ private static final String LABEL = "//cloud/qa/integrationtests/pkg/alt"; private static final String CLASS_JAR = "bazel-bin/something/alt.jar"; - private static final Predicate<String> IS_KOTLIN_IMPLICIT = JdepsParser.pathSuffixMatchingPredicate( + private static final Predicate<String> IS_KOTLIN_IMPLICIT = JdepsParser.Companion.pathSuffixMatchingPredicate( Paths.get("external", "com_github_jetbrains_kotlin", "lib"), "kotlin-stdlib.jar", "kotlin-stdlib-jdk7.jar", @@ -106,7 +106,7 @@ } private void testWithFixture(String fixture) throws IOException { - Deps.Dependencies result = JdepsParser.parse(LABEL, CLASS_JAR, CLASSPATH.stream().collect(Collectors.joining(":")), Stream.of(fixture.split("\n")), IS_KOTLIN_IMPLICIT); + Deps.Dependencies result = JdepsParser.Companion.parse(LABEL, CLASS_JAR, CLASSPATH.stream().collect(Collectors.joining(":")), Stream.of(fixture.split("\n")), IS_KOTLIN_IMPLICIT); Assert.assertEquals(LABEL, result.getRuleLabel()); Assert.assertEquals(7, result.getDependencyCount());