centralise output sink class and rework into a task context
diff --git a/WORKSPACE b/WORKSPACE index e68b3c4..af7d9de 100644 --- a/WORKSPACE +++ b/WORKSPACE
@@ -39,7 +39,7 @@ github_archive( name = "bazel_skylib", repo = "bazelbuild/bazel-skylib", - commit = "6301f974f02350fe973d8631cf1bb87ab8d2a2bd" + commit = "3fea8cb680f4a53a129f7ebace1a5a4d1e035914" ) github_archive(
diff --git a/kotlin/builder/integrationtests/KotlinBuilderTestCase.java b/kotlin/builder/integrationtests/KotlinBuilderTestCase.java index 71b69b5..3bfa7db 100644 --- a/kotlin/builder/integrationtests/KotlinBuilderTestCase.java +++ b/kotlin/builder/integrationtests/KotlinBuilderTestCase.java
@@ -29,7 +29,6 @@ private final JvmCompilationTask.Builder builder = JvmCompilationTask.newBuilder(); private final KotlinBuilderComponent component = DaggerKotlinBuilderComponent.builder() - .out(System.err) .toolchain(KotlinToolchain.createToolchain()) .build();
diff --git a/kotlin/builder/integrationtests/KotlinBuilderTests.java b/kotlin/builder/integrationtests/KotlinBuilderTests.java index 56534f7..df4ebea 100644 --- a/kotlin/builder/integrationtests/KotlinBuilderTests.java +++ b/kotlin/builder/integrationtests/KotlinBuilderTests.java
@@ -2,6 +2,7 @@ import com.google.common.truth.Truth; import com.google.devtools.build.lib.view.proto.Deps; +import io.bazel.kotlin.builder.utils.CompilationTaskContext; import io.bazel.kotlin.model.JvmCompilationTask; import org.junit.Test; @@ -44,9 +45,13 @@ } } int timeoutSeconds = 10; -// KotlinJvmTaskExecutor executor = instance(KotlinJvmTaskExecutor.class); try { - CompletableFuture.runAsync(() -> component().jvmTaskExecutor().execute(command)) + + CompletableFuture.runAsync( + () -> + component() + .jvmTaskExecutor() + .execute(new CompilationTaskContext(command.getInfo(), System.err), command)) .get(timeoutSeconds, TimeUnit.SECONDS); } catch (TimeoutException e) { throw new AssertionError("did not complete in: " + timeoutSeconds);
diff --git a/kotlin/builder/src/io/bazel/kotlin/builder/BUILD b/kotlin/builder/src/io/bazel/kotlin/builder/BUILD index f76b592..f270e45 100644 --- a/kotlin/builder/src/io/bazel/kotlin/builder/BUILD +++ b/kotlin/builder/src/io/bazel/kotlin/builder/BUILD
@@ -40,10 +40,10 @@ ":builder_kt", "//kotlin/builder/proto:kotlin_model", "//third_party:dagger", - "@com_github_jetbrains_kotlin//:annotations", + "@com_github_jetbrains_kotlin//:kotlin-stdlib", + "@io_bazel_rules_kotlin_com_google_protobuf_protobuf_java//jar", ], runtime_deps = [ - "@com_github_jetbrains_kotlin//:kotlin-stdlib", "@com_github_jetbrains_kotlin//:kotlin-stdlib-jdk7", "@com_github_jetbrains_kotlin//:kotlin-stdlib-jdk8", "@io_bazel_rules_kotlin_com_google_code_gson_gson//jar",
diff --git a/kotlin/builder/src/io/bazel/kotlin/builder/KotlinBuilderComponent.java b/kotlin/builder/src/io/bazel/kotlin/builder/KotlinBuilderComponent.java index 73e187a..db95f4d 100644 --- a/kotlin/builder/src/io/bazel/kotlin/builder/KotlinBuilderComponent.java +++ b/kotlin/builder/src/io/bazel/kotlin/builder/KotlinBuilderComponent.java
@@ -18,32 +18,32 @@ import dagger.BindsInstance; import dagger.Component; import dagger.Provides; +import io.bazel.kotlin.builder.tasks.BazelWorker; import io.bazel.kotlin.builder.tasks.KotlinBuilder; import io.bazel.kotlin.builder.tasks.jvm.KotlinJvmCompiler; import io.bazel.kotlin.builder.tasks.jvm.KotlinJvmTaskExecutor; import io.bazel.kotlin.builder.toolchain.KotlinToolchain; -import io.bazel.kotlin.builder.tasks.BazelWorker; import io.bazel.kotlin.builder.utils.KotlinCompilerPluginArgsEncoder; import javax.inject.Singleton; import java.io.PrintStream; +import java.util.function.Supplier; @Singleton @dagger.Component(modules = {KotlinBuilderComponent.Module.class}) public interface KotlinBuilderComponent { KotlinToolchain toolchain(); + KotlinJvmTaskExecutor jvmTaskExecutor(); + KotlinJvmCompiler jvmCompiler(); + BazelWorker worker(); @Component.Builder interface Builder { @BindsInstance KotlinBuilderComponent.Builder toolchain(KotlinToolchain toolchain); - - @BindsInstance - KotlinBuilderComponent.Builder out(PrintStream out); - KotlinBuilderComponent build(); } @@ -56,6 +56,11 @@ } @Provides + public PrintStream provideDebugPrintStream() { + return System.err; + } + + @Provides public BazelWorker provideWorker(KotlinBuilder builder) { return new BazelWorker(builder, System.err, "KotlinCompile"); }
diff --git a/kotlin/builder/src/io/bazel/kotlin/builder/KotlinBuilderMain.java b/kotlin/builder/src/io/bazel/kotlin/builder/KotlinBuilderMain.java index c7182da..c98ad2d 100644 --- a/kotlin/builder/src/io/bazel/kotlin/builder/KotlinBuilderMain.java +++ b/kotlin/builder/src/io/bazel/kotlin/builder/KotlinBuilderMain.java
@@ -17,6 +17,8 @@ import io.bazel.kotlin.builder.toolchain.KotlinToolchain; +import javax.inject.Provider; +import java.io.PrintStream; import java.util.Arrays; public class KotlinBuilderMain { @@ -24,7 +26,6 @@ KotlinBuilderComponent component = DaggerKotlinBuilderComponent.builder() .toolchain(KotlinToolchain.createToolchain()) - .out(System.err) .build(); System.exit(component.worker().apply(Arrays.asList(args))); }
diff --git a/kotlin/builder/src/io/bazel/kotlin/builder/tasks/KotlinBuilder.kt b/kotlin/builder/src/io/bazel/kotlin/builder/tasks/KotlinBuilder.kt index 8fd3bc0..8c6c973 100644 --- a/kotlin/builder/src/io/bazel/kotlin/builder/tasks/KotlinBuilder.kt +++ b/kotlin/builder/src/io/bazel/kotlin/builder/tasks/KotlinBuilder.kt
@@ -20,16 +20,19 @@ import io.bazel.kotlin.builder.toolchain.CompilationStatusException import io.bazel.kotlin.builder.utils.* import io.bazel.kotlin.model.* +import java.io.PrintStream import java.nio.charset.StandardCharsets import java.nio.file.Files import java.nio.file.Paths import java.util.regex.Pattern import javax.inject.Inject +import javax.inject.Provider import javax.inject.Singleton @Singleton @Suppress("MemberVisibilityCanBePrivate") class KotlinBuilder @Inject internal constructor( + private val outputProvider: Provider<PrintStream>, private val jvmTaskExecutor: KotlinJvmTaskExecutor ) : CommandLineProgram { companion object { @@ -45,19 +48,12 @@ } override fun apply(args: List<String>): Int { - check(args.isNotEmpty()) { "expected at least a single arg got: ${args.joinToString(" ")}" } - val (flagFileName, primaryOutputPath, idx) = - checkNotNull(FLAGFILE_RE.matchEntire(args[0])) { "invalid flagfile ${args[0]}" }.destructured - val argMap = Files.readAllLines(Paths.get(flagFileName), StandardCharsets.UTF_8).let(ArgMaps::from) - val commonInfo = buildTaskInfo(argMap).also { - it.primaryOutputPath = primaryOutputPath - }.build() - + val (argMap, context) = buildContext(args) try { @Suppress("WHEN_ENUM_CAN_BE_NULL_IN_JAVA") - when (commonInfo.platform) { - Platform.JVM -> executeJvmTask(commonInfo, argMap) - Platform.UNRECOGNIZED, Platform.JS -> throw IllegalStateException("unrecognized platform: $commonInfo") + when (context.info.platform) { + Platform.JVM -> executeJvmTask(context, argMap) + Platform.UNRECOGNIZED, Platform.JS -> throw IllegalStateException("unrecognized platform: ${context.info}") } } catch (ex: CompilationStatusException) { return ex.status @@ -65,6 +61,18 @@ return 0 } + private fun buildContext(args: List<String>): Pair<ArgMap, CompilationTaskContext> { + check(args.isNotEmpty()) { "expected at least a single arg got: ${args.joinToString(" ")}" } + val (flagFileName, primaryOutputPath, idx) = + checkNotNull(FLAGFILE_RE.matchEntire(args[0])) { "invalid flagfile ${args[0]}" }.destructured + val argMap = Files.readAllLines(Paths.get(flagFileName), StandardCharsets.UTF_8).let(ArgMaps::from) + val info = buildTaskInfo(argMap).also { + it.primaryOutputPath = primaryOutputPath + }.build() + val context = CompilationTaskContext(info, outputProvider.get()) + return Pair(argMap, context) + } + /** * Declares the flags used by the java builder. @@ -142,8 +150,11 @@ this } - private fun executeJvmTask(info: CompilationTaskInfo, argMap: ArgMap) = - jvmTaskExecutor.execute(buildJvmTask(info,argMap)) + private fun executeJvmTask(context: CompilationTaskContext, argMap: ArgMap) { + val task = buildJvmTask(context.info, argMap) + context.debugPrintProto("jvm task message", task) + jvmTaskExecutor.execute(context, task) + } private fun buildJvmTask(info: CompilationTaskInfo, argMap: ArgMap): JvmCompilationTask = JvmCompilationTask.newBuilder().let { root ->
diff --git a/kotlin/builder/src/io/bazel/kotlin/builder/tasks/jvm/KotlinCompilerOutputSink.kt b/kotlin/builder/src/io/bazel/kotlin/builder/tasks/jvm/KotlinCompilerOutputSink.kt deleted file mode 100644 index 9b7a594..0000000 --- a/kotlin/builder/src/io/bazel/kotlin/builder/tasks/jvm/KotlinCompilerOutputSink.kt +++ /dev/null
@@ -1,46 +0,0 @@ -/* - * Copyright 2018 The Bazel Authors. All rights reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.bazel.kotlin.builder.tasks.jvm - - -import java.io.File -import java.io.PrintStream -import java.nio.file.Paths -import javax.inject.Inject -import javax.inject.Provider -import javax.inject.Singleton - -@Singleton -internal class KotlinCompilerOutputSink @Inject constructor( - private val streamProvider: Provider<PrintStream> -) { - private val executionRoot: String = Paths.get("").toAbsolutePath().toString() + File.separator - - fun deliver(line: String) { - streamProvider.get().println(trimExecutionRootPrefix(line)) - } - - fun deliver(lines: List<String>) { - streamProvider.get().also { stream -> lines.map(::trimExecutionRootPrefix).forEach(stream::println) } - } - - private fun trimExecutionRootPrefix(toPrint: String): String { - // trim off the workspace component - return if (toPrint.startsWith(executionRoot)) { - toPrint.replaceFirst(executionRoot, "") - } else toPrint - } -}
diff --git a/kotlin/builder/src/io/bazel/kotlin/builder/tasks/jvm/KotlinJvmCompiler.kt b/kotlin/builder/src/io/bazel/kotlin/builder/tasks/jvm/KotlinJvmCompiler.kt index a004df3..b4b5bb5 100644 --- a/kotlin/builder/src/io/bazel/kotlin/builder/tasks/jvm/KotlinJvmCompiler.kt +++ b/kotlin/builder/src/io/bazel/kotlin/builder/tasks/jvm/KotlinJvmCompiler.kt
@@ -17,6 +17,7 @@ import io.bazel.kotlin.builder.toolchain.CompilationStatusException import io.bazel.kotlin.builder.toolchain.KotlinToolchain +import io.bazel.kotlin.builder.utils.CompilationTaskContext import io.bazel.kotlin.builder.utils.KotlinCompilerPluginArgsEncoder import io.bazel.kotlin.builder.utils.addAll import io.bazel.kotlin.builder.utils.joinedClasspath @@ -40,12 +41,12 @@ private val compiler: KotlinToolchain.KotlincInvoker, private val pluginArgsEncoder: KotlinCompilerPluginArgsEncoder ) { - fun runAnnotationProcessor(command: JvmCompilationTask): List<String> { + fun runAnnotationProcessor(context: CompilationTaskContext, command: JvmCompilationTask): List<String> { check(command.info.plugins.annotationProcessorsList.isNotEmpty()) { "method called without annotation processors" } return getCommonArgs(command).also { - it.addAll(pluginArgsEncoder.encode(command)) + it.addAll(pluginArgsEncoder.encode(context, command)) it.addAll(command.inputs.kotlinSourcesList) it.addAll(command.inputs.javaSourcesList) }.let(::invokeCompilePhase)
diff --git a/kotlin/builder/src/io/bazel/kotlin/builder/tasks/jvm/KotlinJvmTaskExecutor.kt b/kotlin/builder/src/io/bazel/kotlin/builder/tasks/jvm/KotlinJvmTaskExecutor.kt index 7727861..caa3372 100644 --- a/kotlin/builder/src/io/bazel/kotlin/builder/tasks/jvm/KotlinJvmTaskExecutor.kt +++ b/kotlin/builder/src/io/bazel/kotlin/builder/tasks/jvm/KotlinJvmTaskExecutor.kt
@@ -17,7 +17,7 @@ import io.bazel.kotlin.builder.toolchain.CompilationStatusException -import io.bazel.kotlin.builder.toolchain.CompilationTaskExecutor +import io.bazel.kotlin.builder.utils.CompilationTaskContext import io.bazel.kotlin.builder.utils.IS_JVM_SOURCE_FILE import io.bazel.kotlin.builder.utils.ensureDirectories import io.bazel.kotlin.builder.utils.expandWithSources @@ -33,18 +33,16 @@ @Singleton class KotlinJvmTaskExecutor @Inject internal constructor( private val kotlinCompiler: KotlinJvmCompiler, - private val outputSink: KotlinCompilerOutputSink, private val javaCompiler: JavaCompiler, private val jDepsGenerator: JDepsGenerator, private val outputJarCreator: OutputJarCreator -) : CompilationTaskExecutor<JvmCompilationTask>() { - override fun execute(task: JvmCompilationTask): Result { +) { + fun execute(context: CompilationTaskContext, task: JvmCompilationTask) { val preprocessedTask = preprocessingSteps(task) // fix error handling try { - val context = Context() val commandWithApSources = context.execute("kapt") { - runAnnotationProcessors(preprocessedTask) + runAnnotationProcessors(context, preprocessedTask) } compileClasses(context, commandWithApSources) context.execute("create jar") { @@ -54,8 +52,6 @@ context.execute("generate jdeps") { jDepsGenerator.generateJDeps(commandWithApSources) } - return Result(context.timings, commandWithApSources) - } catch (ex: Throwable) { throw RuntimeException(ex) } @@ -114,10 +110,13 @@ } } - private fun runAnnotationProcessors(command: JvmCompilationTask): JvmCompilationTask = + private fun runAnnotationProcessors( + context: CompilationTaskContext, + command: JvmCompilationTask + ): JvmCompilationTask = try { if (command.info.plugins.annotationProcessorsList.isNotEmpty()) { - kotlinCompiler.runAnnotationProcessor(command) + kotlinCompiler.runAnnotationProcessor(context, command) File(command.directories.generatedSources).walkTopDown() .filter { it.isFile } .map { it.path } @@ -127,11 +126,14 @@ command } } catch (ex: CompilationStatusException) { - ex.lines.also(outputSink::deliver) + ex.lines.also(context::printCompilerOutput) throw ex } - private fun compileClasses(context: Context, command: JvmCompilationTask) { + private fun compileClasses( + context: CompilationTaskContext, + command: JvmCompilationTask + ) { var kotlinError: CompilationStatusException? = null var result: List<String>? = null context.execute("kotlinc") { @@ -147,21 +149,8 @@ javaCompiler.compile(command) } } finally { - checkNotNull(result).also(outputSink::deliver) + checkNotNull(result).also(context::printCompilerOutput) kotlinError?.also { throw it } } } - - internal class Context { - val timings = mutableListOf<String>() - inline fun <T> execute(name: String, task: () -> T): T { - val start = System.currentTimeMillis() - return try { - task() - } finally { - val stop = System.currentTimeMillis() - timings += "$name: ${stop - start} ms" - } - } - } }
diff --git a/kotlin/builder/src/io/bazel/kotlin/builder/toolchain/CompilationTaskExecutor.kt b/kotlin/builder/src/io/bazel/kotlin/builder/toolchain/CompilationTaskExecutor.kt deleted file mode 100644 index bda3300..0000000 --- a/kotlin/builder/src/io/bazel/kotlin/builder/toolchain/CompilationTaskExecutor.kt +++ /dev/null
@@ -1,28 +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.kotlin.builder.toolchain - -import com.google.protobuf.MessageOrBuilder - -/** - * Base Compilation Task Executor class - */ -abstract class CompilationTaskExecutor<T: MessageOrBuilder> { - @Suppress("unused") - inner class Result(val timings: List<String>, val command: T) - - abstract fun execute(task: T): Result -} \ No newline at end of file
diff --git a/kotlin/builder/src/io/bazel/kotlin/builder/utils/CompilationTaskContext.kt b/kotlin/builder/src/io/bazel/kotlin/builder/utils/CompilationTaskContext.kt new file mode 100644 index 0000000..17946ae --- /dev/null +++ b/kotlin/builder/src/io/bazel/kotlin/builder/utils/CompilationTaskContext.kt
@@ -0,0 +1,81 @@ +/* + * 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.kotlin.builder.utils + + +import com.google.protobuf.MessageOrBuilder +import com.google.protobuf.TextFormat +import io.bazel.kotlin.model.CompilationTaskInfo +import java.io.File +import java.io.PrintStream +import java.nio.file.Paths + +class CompilationTaskContext(val info: CompilationTaskInfo, private val out: PrintStream) { + private val executionRoot: String = Paths.get("").toAbsolutePath().toString() + File.separator + private val timings = mutableListOf<String>() + val isDebug: Boolean = info.debug > 0 + + /** + * Print debugging messages if it is enabled for the task. + */ + fun debugPrint(msg: String) { + if (info.debug > 0) { + out.println(msg) + } + } + + /** + * Print a debugging message if it debugging is enabled for the task. The lines are tab seperated. + */ + private inline fun debugPrintHeadedLines(header: String, lines: () -> String) { + if (info.debug > 0) { + out.println(if (header.endsWith(":")) header else "$header:") + out.print("| ${lines().replace("\n", "\n| ")}") + } + } + + /** + * Print a proto message if debugging is enabled for the task. + */ + fun debugPrintProto(header: String, msg: MessageOrBuilder) { + debugPrintHeadedLines(header) { TextFormat.printToString(msg) } + } + + fun printCompilerOutput(line: String) { + out.println(trimExecutionRootPrefix(line)) + } + + fun printCompilerOutput(lines: List<String>) { + lines.map(::trimExecutionRootPrefix).forEach(out::println) + } + + private fun trimExecutionRootPrefix(toPrint: String): String { + // trim off the workspace component + return if (toPrint.startsWith(executionRoot)) { + toPrint.replaceFirst(executionRoot, "") + } else toPrint + } + + fun <T> execute(name: String, task: () -> T): T { + val start = System.currentTimeMillis() + return try { + task() + } finally { + val stop = System.currentTimeMillis() + timings += "$name: ${stop - start} ms" + } + } +}
diff --git a/kotlin/builder/src/io/bazel/kotlin/builder/utils/KotlinCompilerPluginArgsEncoder.kt b/kotlin/builder/src/io/bazel/kotlin/builder/utils/KotlinCompilerPluginArgsEncoder.kt index 3f3f98c..6974f0c 100644 --- a/kotlin/builder/src/io/bazel/kotlin/builder/utils/KotlinCompilerPluginArgsEncoder.kt +++ b/kotlin/builder/src/io/bazel/kotlin/builder/utils/KotlinCompilerPluginArgsEncoder.kt
@@ -85,9 +85,7 @@ ) } - fun encode( - command: JvmCompilationTask - ): List<String> { + fun encode(context: CompilationTaskContext, command: JvmCompilationTask): List<String> { val javacArgs = mutableMapOf<String, String>( "-target" to command.info.toolchainInfo.jvm.jvmTarget ) @@ -101,7 +99,7 @@ arg["javacArguments"] = javacArgs.let(Companion::encodeMap) arg["aptMode"] = "stubsAndApt" arg["correctErrorTypes"] = "true" -// arg["verbose"] = "true" + arg["verbose"] = context.isDebug.toString() arg["processors"] = plugin.annotationProcessorsList .filter { it.processorClass.isNotEmpty() }