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() }