merge task builder into kotlin builder and move task specific logic to
executor class
diff --git a/kotlin/builder/proto/jars/libkotlin_model_proto-speed.jar b/kotlin/builder/proto/jars/libkotlin_model_proto-speed.jar
index 2e614f0..6b75802 100755
--- a/kotlin/builder/proto/jars/libkotlin_model_proto-speed.jar
+++ b/kotlin/builder/proto/jars/libkotlin_model_proto-speed.jar
Binary files differ
diff --git a/kotlin/builder/proto/kotlin_model.proto b/kotlin/builder/proto/kotlin_model.proto
index 2af674f..ead4726 100644
--- a/kotlin/builder/proto/kotlin_model.proto
+++ b/kotlin/builder/proto/kotlin_model.proto
@@ -94,6 +94,8 @@
     CompilerPlugins plugins = 7;
     // Paths to Jars that the kotlin compiler will allow package private access to.
     repeated string friend_paths = 8;
+    // The path of the primary output for the task, this is derived from the flagfile.
+    string primary_output_path = 9;
 }
 
 // Mested messages not marked with stable could be refactored.
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 901bfb5..70ce4ae 100644
--- a/kotlin/builder/src/io/bazel/kotlin/builder/tasks/KotlinBuilder.kt
+++ b/kotlin/builder/src/io/bazel/kotlin/builder/tasks/KotlinBuilder.kt
@@ -15,15 +15,12 @@
  */
 package io.bazel.kotlin.builder.tasks
 
+import com.google.protobuf.util.JsonFormat
 import io.bazel.kotlin.builder.tasks.jvm.KotlinJvmTaskExecutor
 import io.bazel.kotlin.builder.toolchain.CompilationStatusException
-import io.bazel.kotlin.builder.utils.ArgMaps
-import io.bazel.kotlin.builder.utils.IS_JVM_SOURCE_FILE
-import io.bazel.kotlin.builder.utils.ensureDirectories
-import io.bazel.kotlin.builder.utils.expandWithSources
-import io.bazel.kotlin.builder.utils.jars.SourceJarExtractor
-import io.bazel.kotlin.model.JvmCompilationTask
-import java.nio.charset.StandardCharsets.UTF_8
+import io.bazel.kotlin.builder.utils.*
+import io.bazel.kotlin.model.*
+import java.nio.charset.StandardCharsets
 import java.nio.file.Files
 import java.nio.file.Paths
 import java.util.regex.Pattern
@@ -33,70 +30,160 @@
 @Singleton
 @Suppress("MemberVisibilityCanBePrivate")
 class KotlinBuilder @Inject internal constructor(
-    private val taskBuilder: TaskBuilder,
     private val jvmTaskExecutor: KotlinJvmTaskExecutor
 ) : CommandLineProgram {
     companion object {
-        // regex that matches the regular bazel param file naming convention.
-        private val STANDARD_FLAGFILE_RE = Pattern.compile(""".*.jar-\d+.params$""").toRegex()
+        @JvmStatic
+        private val jsonTypeRegistry = JsonFormat.TypeRegistry.newBuilder()
+            .add(KotlinModel.getDescriptor().messageTypes).build()
+
+        @JvmStatic
+        private val jsonFormat: JsonFormat.Parser = JsonFormat.parser().usingTypeRegistry(jsonTypeRegistry)
+
+        @JvmStatic
+        private val FLAGFILE_RE = Pattern.compile("""^--flagfile=((.*)-(\d+).params)$""").toRegex()
     }
 
-    fun execute(args: List<String>): Int {
-        check(args.isNotEmpty() && args[0].startsWith("--flagfile=")) { "no flag file supplied" }
-        val flagFile = args[0].replace("--flagfile=", "")
-        val flagFilePath = Paths.get(flagFile)
-        check(flagFilePath.toFile().exists()) { "flagfile $flagFile does not exist" }
-        val task = when {
-            STANDARD_FLAGFILE_RE.matches(flagFile) -> {
-                Files.readAllLines(flagFilePath, UTF_8).let { loadedFlags ->
-                    ArgMaps.from(loadedFlags).let {
-                        val taskInfo = taskBuilder.buildTaskInfo(it)
-                        taskBuilder.buildJvm(taskInfo, it)
+    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()
+
+        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")
+            }
+        } catch (ex: CompilationStatusException) {
+            return ex.status
+        }
+        return 0
+    }
+
+
+    /**
+     * Declares the flags used by the java builder.
+     */
+    private enum class JavaBuilderFlags(override val flag: String) : Flag {
+        TARGET_LABEL("--target_label"),
+        CLASSPATH("--classpath"),
+        JAVAC_OPTS("--javacopts"),
+        DEPENDENCIES("--dependencies"),
+        DIRECT_DEPENDENCIES("--direct_dependencies"),
+        DIRECT_DEPENDENCY("--direct_dependency"),
+        INDIRECT_DEPENDENCY("--indirect_dependency"),
+        STRICT_JAVA_DEPS("--strict_java_deps"),
+        OUTPUT_DEPS_PROTO("--output_deps_proto"),
+        DEPS_ARTIFACTS("--deps_artifacts"),
+        REDUCE_CLASSPATH("--reduce_classpath"),
+        SOURCEGEN_DIR("--sourcegendir"),
+        GENERATED_SOURCES_OUTPUT("--generated_sources_output"),
+        OUTPUT_MANIFEST_PROTO("--output_manifest_proto"),
+        SOURCES("--sources"),
+        SOURCE_ROOTS("--source_roots"),
+        SOURCE_JARS("--source_jars"),
+        SOURCE_PATH("--sourcepath"),
+        BOOT_CLASSPATH("--bootclasspath"),
+        PROCESS_PATH("--processorpath"),
+        PROCESSORS("--processors"),
+        EXT_CLASSPATH("--extclasspath"),
+        EXT_DIR("--extdir"),
+        OUTPUT("--output"),
+        NATIVE_HEADER_OUTPUT("--native_header_output"),
+        CLASSDIR("--classdir"),
+        TEMPDIR("--tempdir"),
+        GENDIR("--gendir"),
+        POST_PROCESSOR("--post_processor"),
+        COMPRESS_JAR("--compress_jar"),
+        RULE_KIND("--rule_kind"),
+        TEST_ONLY("--testonly");
+    }
+
+    private enum class KotlinBuilderFlags(override val flag: String) : Flag {
+        MODULE_NAME("--kotlin_module_name"),
+        PASSTHROUGH_FLAGS("--kotlin_passthrough_flags"),
+        API_VERSION("--kotlin_api_version"),
+        LANGUAGE_VERSION("--kotlin_language_version"),
+        JVM_TARGET("--kotlin_jvm_target"),
+        OUTPUT_SRCJAR("--kotlin_output_srcjar"),
+        GENERATED_CLASSDIR("--kotlin_generated_classdir"),
+        PLUGINS("--kotlin_plugins"),
+        FRIEND_PATHS("--kotlin_friend_paths"),
+        OUTPUT_JDEPS("--kotlin_output_jdeps"),
+        TASK_ID("--kotlin_task_id");
+    }
+
+    private fun buildTaskInfo(argMap: ArgMap): CompilationTaskInfo.Builder =
+        with(CompilationTaskInfo.newBuilder()) {
+            label = argMap.mandatorySingle(JavaBuilderFlags.TARGET_LABEL)
+            argMap.mandatorySingle(JavaBuilderFlags.RULE_KIND).split("_").also {
+                check(it.size == 3 && it[0] == "kt") { "invalid rule kind $it" }
+                platform = checkNotNull(Platform.valueOf(it[1].toUpperCase())) {
+                    "unrecognized platform ${it[1]}"
+                }
+                ruleKind = checkNotNull(RuleKind.valueOf(it[2].toUpperCase())) {
+                    "unrecognized rule kind ${it[2]}"
+                }
+            }
+            moduleName = argMap.mandatorySingle(KotlinBuilderFlags.MODULE_NAME).also {
+                check(it.isNotBlank()) { "--kotlin_module_name should not be blank" }
+            }
+            passthroughFlags = argMap.optionalSingle(KotlinBuilderFlags.PASSTHROUGH_FLAGS)
+            argMap.optional(KotlinBuilderFlags.FRIEND_PATHS)?.let(::addAllFriendPaths)
+            toolchainInfoBuilder.commonBuilder.apiVersion = argMap.mandatorySingle(KotlinBuilderFlags.API_VERSION)
+            toolchainInfoBuilder.commonBuilder.languageVersion = argMap.mandatorySingle(KotlinBuilderFlags.LANGUAGE_VERSION)
+            this
+        }
+
+    private fun executeJvmTask(info: CompilationTaskInfo, argMap: ArgMap) =
+        jvmTaskExecutor.execute(buildJvmTask(info,argMap))
+
+    private fun buildJvmTask(info: CompilationTaskInfo, argMap: ArgMap): JvmCompilationTask =
+        JvmCompilationTask.newBuilder().let { root ->
+            root.info = info
+
+            with(root.outputsBuilder) {
+                jar = argMap.mandatorySingle(JavaBuilderFlags.OUTPUT)
+                jdeps = argMap.mandatorySingle(KotlinBuilderFlags.OUTPUT_JDEPS)
+                srcjar = argMap.mandatorySingle(KotlinBuilderFlags.OUTPUT_SRCJAR)
+            }
+
+            with(root.directoriesBuilder) {
+                classes = argMap.mandatorySingle(JavaBuilderFlags.CLASSDIR)
+                generatedClasses = argMap.mandatorySingle(KotlinBuilderFlags.GENERATED_CLASSDIR)
+                temp = argMap.mandatorySingle(JavaBuilderFlags.TEMPDIR)
+                generatedSources = argMap.mandatorySingle(JavaBuilderFlags.SOURCEGEN_DIR)
+            }
+
+            with(root.inputsBuilder) {
+                addAllClasspath(argMap.mandatory(JavaBuilderFlags.CLASSPATH))
+                putAllIndirectDependencies(argMap.labelDepMap(JavaBuilderFlags.DIRECT_DEPENDENCY))
+                putAllIndirectDependencies(argMap.labelDepMap(JavaBuilderFlags.INDIRECT_DEPENDENCY))
+
+                argMap.optional(JavaBuilderFlags.SOURCES)?.iterator()?.partitionSources(
+                    { addKotlinSources(it) },
+                    { addJavaSources(it) }
+                )
+                argMap.optional(JavaBuilderFlags.SOURCE_JARS)?.also {
+                    addAllSourceJars(it)
+                }
+            }
+
+            with(root.infoBuilder) {
+                toolchainInfoBuilder.jvmBuilder.jvmTarget = argMap.mandatorySingle(KotlinBuilderFlags.JVM_TARGET)
+
+                argMap.optionalSingle(KotlinBuilderFlags.PLUGINS)?.let { input ->
+                    plugins = CompilerPlugins.newBuilder().let {
+                        jsonFormat.merge(input, it)
+                        it.build()
                     }
                 }
             }
-            else -> throw IllegalStateException("unknown flag file format for $flagFile")
+            root.build()
         }
-
-        return execute(task)
-    }
-
-    fun execute(command: JvmCompilationTask): Int {
-        ensureDirectories(
-            command.directories.classes,
-            command.directories.temp,
-            command.directories.generatedSources,
-            command.directories.generatedClasses
-        )
-        val updatedCommand = expandWithSourceJarSources(command)
-        return try {
-            jvmTaskExecutor.execute(updatedCommand)
-            0
-        } catch (ex: CompilationStatusException) {
-            ex.status
-        }
-    }
-
-    /**
-     * If any srcjars were provided expand the jars sources and create a new [JvmCompilationTask] with the
-     * Java and Kotlin sources merged in.
-     */
-    private fun expandWithSourceJarSources(command: JvmCompilationTask): JvmCompilationTask =
-        if (command.inputs.sourceJarsList.isEmpty()) {
-            command
-        } else {
-            SourceJarExtractor(
-                destDir = Paths.get(command.directories.temp).resolve("_srcjars"),
-                fileMatcher = IS_JVM_SOURCE_FILE
-            ).also {
-                it.jarFiles.addAll(command.inputs.sourceJarsList.map { p -> Paths.get(p) })
-                it.execute()
-            }.let {
-                command.expandWithSources(it.sourcesList.iterator())
-            }
-        }
-
-    override fun apply(args: List<String>): Int {
-        return execute(args)
-    }
 }
diff --git a/kotlin/builder/src/io/bazel/kotlin/builder/tasks/TaskBuilder.kt b/kotlin/builder/src/io/bazel/kotlin/builder/tasks/TaskBuilder.kt
deleted file mode 100644
index 63e984c..0000000
--- a/kotlin/builder/src/io/bazel/kotlin/builder/tasks/TaskBuilder.kt
+++ /dev/null
@@ -1,158 +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
-
-import com.google.protobuf.util.JsonFormat
-import io.bazel.kotlin.builder.tasks.TaskBuilder.JavaBuilderFlags.*
-import io.bazel.kotlin.builder.tasks.TaskBuilder.KotlinBuilderFlags.*
-import io.bazel.kotlin.builder.utils.*
-import io.bazel.kotlin.model.*
-import io.bazel.kotlin.model.KotlinModel.getDescriptor
-import javax.inject.Inject
-import javax.inject.Singleton
-
-@Singleton
-class TaskBuilder @Inject internal constructor() {
-    companion object {
-        @JvmStatic
-        private val jsonTypeRegistry = JsonFormat.TypeRegistry.newBuilder()
-            .add(getDescriptor().messageTypes).build()
-
-        @JvmStatic
-        private val jsonFormat: JsonFormat.Parser = JsonFormat.parser().usingTypeRegistry(jsonTypeRegistry)
-    }
-
-    /**
-     * Declares the flags used by the java builder.
-     */
-    private enum class JavaBuilderFlags(override val flag: String) : Flag {
-        TARGET_LABEL("--target_label"),
-        CLASSPATH("--classpath"),
-        JAVAC_OPTS("--javacopts"),
-        DEPENDENCIES("--dependencies"),
-        DIRECT_DEPENDENCIES("--direct_dependencies"),
-        DIRECT_DEPENDENCY("--direct_dependency"),
-        INDIRECT_DEPENDENCY("--indirect_dependency"),
-        STRICT_JAVA_DEPS("--strict_java_deps"),
-        OUTPUT_DEPS_PROTO("--output_deps_proto"),
-        DEPS_ARTIFACTS("--deps_artifacts"),
-        REDUCE_CLASSPATH("--reduce_classpath"),
-        SOURCEGEN_DIR("--sourcegendir"),
-        GENERATED_SOURCES_OUTPUT("--generated_sources_output"),
-        OUTPUT_MANIFEST_PROTO("--output_manifest_proto"),
-        SOURCES("--sources"),
-        SOURCE_ROOTS("--source_roots"),
-        SOURCE_JARS("--source_jars"),
-        SOURCE_PATH("--sourcepath"),
-        BOOT_CLASSPATH("--bootclasspath"),
-        PROCESS_PATH("--processorpath"),
-        PROCESSORS("--processors"),
-        EXT_CLASSPATH("--extclasspath"),
-        EXT_DIR("--extdir"),
-        OUTPUT("--output"),
-        NATIVE_HEADER_OUTPUT("--native_header_output"),
-        CLASSDIR("--classdir"),
-        TEMPDIR("--tempdir"),
-        GENDIR("--gendir"),
-        POST_PROCESSOR("--post_processor"),
-        COMPRESS_JAR("--compress_jar"),
-        RULE_KIND("--rule_kind"),
-        TEST_ONLY("--testonly");
-    }
-
-    enum class KotlinBuilderFlags(override val flag: String) : Flag {
-        MODULE_NAME("--kotlin_module_name"),
-        PASSTHROUGH_FLAGS("--kotlin_passthrough_flags"),
-        API_VERSION("--kotlin_api_version"),
-        LANGUAGE_VERSION("--kotlin_language_version"),
-        JVM_TARGET("--kotlin_jvm_target"),
-        OUTPUT_SRCJAR("--kotlin_output_srcjar"),
-        GENERATED_CLASSDIR("--kotlin_generated_classdir"),
-        PLUGINS("--kotlin_plugins"),
-        FRIEND_PATHS("--kotlin_friend_paths"),
-        OUTPUT_JDEPS("--kotlin_output_jdeps"),
-        TASK_ID("--kotlin_task_id");
-    }
-
-    fun buildTaskInfo(argMap: ArgMap): CompilationTaskInfo =
-        with(CompilationTaskInfo.newBuilder()) {
-            label = argMap.mandatorySingle(TARGET_LABEL)
-            argMap.mandatorySingle(RULE_KIND).split("_").also {
-                check(it.size == 3 && it[0] == "kt") { "invalid rule kind $it" }
-                platform = checkNotNull(Platform.valueOf(it[1].toUpperCase())) {
-                    "unrecognized platform ${it[1]}"
-                }
-                ruleKind = checkNotNull(RuleKind.valueOf(it[2].toUpperCase())) {
-                    "unrecognized rule kind ${it[2]}"
-                }
-            }
-            moduleName = argMap.mandatorySingle(MODULE_NAME).also {
-                check(it.isNotBlank()) { "--kotlin_module_name should not be blank" }
-            }
-            passthroughFlags = argMap.optionalSingle(PASSTHROUGH_FLAGS)
-            argMap.optional(FRIEND_PATHS)?.let(::addAllFriendPaths)
-            toolchainInfoBuilder.commonBuilder.apiVersion = argMap.mandatorySingle(API_VERSION)
-            toolchainInfoBuilder.commonBuilder.languageVersion = argMap.mandatorySingle(LANGUAGE_VERSION)
-            build()
-        }
-
-
-    fun buildJvm(info: CompilationTaskInfo, argMap: ArgMap): JvmCompilationTask =
-        JvmCompilationTask.newBuilder().let { root ->
-            root.info = info
-
-            with(root.outputsBuilder) {
-                jar = argMap.mandatorySingle(OUTPUT)
-                jdeps = argMap.mandatorySingle(OUTPUT_JDEPS)
-                srcjar = argMap.mandatorySingle(OUTPUT_SRCJAR)
-            }
-
-            with(root.directoriesBuilder) {
-                classes = argMap.mandatorySingle(CLASSDIR)
-                generatedClasses = argMap.mandatorySingle(GENERATED_CLASSDIR)
-                temp = argMap.mandatorySingle(TEMPDIR)
-                generatedSources = argMap.mandatorySingle(SOURCEGEN_DIR)
-            }
-
-            with(root.inputsBuilder) {
-                addAllClasspath(argMap.mandatory(CLASSPATH))
-                putAllIndirectDependencies(argMap.labelDepMap(DIRECT_DEPENDENCY))
-                putAllIndirectDependencies(argMap.labelDepMap(INDIRECT_DEPENDENCY))
-
-                argMap.optional(SOURCES)?.iterator()?.partitionSources(
-                    { addKotlinSources(it) },
-                    { addJavaSources(it) }
-                )
-                argMap.optional(SOURCE_JARS)?.also {
-                    addAllSourceJars(it)
-                }
-            }
-
-            with(root.infoBuilder) {
-                toolchainInfoBuilder.jvmBuilder.jvmTarget = argMap.mandatorySingle(JVM_TARGET)
-
-                argMap.optionalSingle(PLUGINS)?.let { input ->
-                    plugins = CompilerPlugins.newBuilder().let {
-                        jsonFormat.merge(input, it)
-                        it.build()
-                    }
-                }
-            }
-
-            root.build()
-        }
-}
-
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 738fd88..7727861 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
@@ -18,8 +18,11 @@
 
 import io.bazel.kotlin.builder.toolchain.CompilationStatusException
 import io.bazel.kotlin.builder.toolchain.CompilationTaskExecutor
+import io.bazel.kotlin.builder.utils.IS_JVM_SOURCE_FILE
+import io.bazel.kotlin.builder.utils.ensureDirectories
 import io.bazel.kotlin.builder.utils.expandWithSources
 import io.bazel.kotlin.builder.utils.jars.SourceJarCreator
+import io.bazel.kotlin.builder.utils.jars.SourceJarExtractor
 import io.bazel.kotlin.model.JvmCompilationTask
 import java.io.File
 import java.nio.file.Files
@@ -36,11 +39,12 @@
     private val outputJarCreator: OutputJarCreator
 ) : CompilationTaskExecutor<JvmCompilationTask>() {
     override fun execute(task: JvmCompilationTask): Result {
+        val preprocessedTask = preprocessingSteps(task)
         // fix error handling
         try {
             val context = Context()
             val commandWithApSources = context.execute("kapt") {
-                runAnnotationProcessors(task)
+                runAnnotationProcessors(preprocessedTask)
             }
             compileClasses(context, commandWithApSources)
             context.execute("create jar") {
@@ -58,6 +62,35 @@
     }
 
 
+    private fun preprocessingSteps(command: JvmCompilationTask): JvmCompilationTask {
+        ensureDirectories(
+            command.directories.classes,
+            command.directories.temp,
+            command.directories.generatedSources,
+            command.directories.generatedClasses
+        )
+        return expandWithSourceJarSources(command)
+    }
+
+    /**
+     * If any srcjars were provided expand the jars sources and create a new [JvmCompilationTask] with the
+     * Java and Kotlin sources merged in.
+     */
+    private fun expandWithSourceJarSources(command: JvmCompilationTask): JvmCompilationTask =
+        if (command.inputs.sourceJarsList.isEmpty()) {
+            command
+        } else {
+            SourceJarExtractor(
+                destDir = Paths.get(command.directories.temp).resolve("_srcjars"),
+                fileMatcher = IS_JVM_SOURCE_FILE
+            ).also {
+                it.jarFiles.addAll(command.inputs.sourceJarsList.map { p -> Paths.get(p) })
+                it.execute()
+            }.let {
+                command.expandWithSources(it.sourcesList.iterator())
+            }
+        }
+
     private fun produceSourceJar(command: JvmCompilationTask) {
         Paths.get(command.outputs.srcjar).also { sourceJarPath ->
             Files.createFile(sourceJarPath)