huge refactor, performance improvments and bazel_deps
diff --git a/BUILD b/BUILD
index b8b3116..b80e4d9 100644
--- a/BUILD
+++ b/BUILD
@@ -15,6 +15,7 @@
     name = "all_tests",
     tests = [
         "//kotlin/builder:unittests",
+        "//kotlin/builder:integrationtests",
         "//tests/integrationtests"
     ]
 )
\ No newline at end of file
diff --git a/WORKSPACE b/WORKSPACE
index 91debff..78c3887 100644
--- a/WORKSPACE
+++ b/WORKSPACE
@@ -12,7 +12,13 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 workspace(name="io_bazel_rules_kotlin")
-load("//kotlin/internal:bootstrap.bzl", "github_archive")
+load("//kotlin/internal:bootstrap.bzl",github_archive="github_archive")
+
+github_archive(
+    name = "com_google_protobuf",
+    repo = "google/protobuf",
+    commit = "106ffc04be1abf3ff3399f54ccf149815b287dd9",
+)
 
 git_repository(
     name = "io_bazel_rules_sass",
@@ -30,19 +36,12 @@
 load("@io_bazel_skydoc//skylark:skylark.bzl", "skydoc_repositories")
 skydoc_repositories()
 
+http_jar(
+    name = "bazel_deps",
+    url = "https://github.com/axsy-dev/bazel-deps/releases/download/2/bazel-deps.jar",
+    sha256 = "bbd51188141f2bb09222a35675af25edbbb5b6507d779acb6c70c19e51cd67bd",
+)
+
 load("//kotlin:kotlin.bzl", "kotlin_repositories", "kt_register_toolchains")
 kotlin_repositories()
 kt_register_toolchains()
-
-# test and example dependencies.
-maven_jar(name = "junit_junit",artifact = "junit:junit:jar:4.12")
-maven_jar(name = "autovalue", artifact="com.google.auto.value:auto-value:1.5")
-maven_jar(name = "auto_common", artifact="com.google.auto:auto-common:0.10")
-maven_jar(name = "autoservice", artifact="com.google.auto.service:auto-service:1.0-rc4")
-maven_jar(name = "javax_inject", artifact = "javax.inject:javax.inject:1")
-# After 2.9 dagger requires much more dependencies.
-maven_jar(name = "dagger", artifact="com.google.dagger:dagger:2.9")
-maven_jar(name = "dagger_compiler", artifact="com.google.dagger:dagger-compiler:2.9")
-maven_jar(name = "dagger_producers", artifact="com.google.dagger:dagger-producers:2.9")
-
-maven_jar(name="com_google_truth_truth", artifact="com.google.truth:truth:0.40", sha1="0d74e716afec045cc4a178dbbfde2a8314ae5574")
diff --git a/examples/dagger/BUILD b/examples/dagger/BUILD
index 17c4145..50a0ef0 100644
--- a/examples/dagger/BUILD
+++ b/examples/dagger/BUILD
@@ -18,11 +18,11 @@
 java_plugin(
     name = "dagger_plugin",
     deps = [
-        "@dagger_compiler//jar",
-        "@com_google_guava_guava//jar",
-        "@dagger_producers//jar",
-        "@dagger//jar",
-        "@javax_inject//jar"
+        "//third_party/jvm/com/google/dagger:dagger_compiler",
+        "@//third_party/jvm/com/google/guava",
+        "//third_party/jvm/com/google/dagger:dagger_producers",
+        "//third_party/jvm/com/google/dagger",
+        "//third_party/jvm/javax/inject:javax_inject"
     ],
     processor_class = "dagger.internal.codegen.ComponentProcessor"
 )
@@ -30,8 +30,8 @@
 java_library(
     name = "dagger_lib",
     exports = [
-        "@javax_inject//jar",
-        "@dagger//jar",
+        "//third_party/jvm/javax/inject:javax_inject",
+        "//third_party/jvm/com/google/dagger"
     ],
     exported_plugins = ["dagger_plugin"]
 )
diff --git a/kotlin/builder/BUILD b/kotlin/builder/BUILD
index 0fe8f78..a8f2e6d 100644
--- a/kotlin/builder/BUILD
+++ b/kotlin/builder/BUILD
@@ -19,14 +19,15 @@
     deps = [
         "//kotlin/builder/proto",
         "@com_github_jetbrains_kotlin//:preloader",
-        "@com_google_protobuf//:protobuf_java",
-        "@com_google_protobuf//:protobuf_java_util",
-        "@com_google_code_gson_gson//jar",
-        "@com_google_guava_guava//jar",
+        "@io_bazel_rules_kotlin_com_google_protobuf_protobuf_java//jar",
+        "@io_bazel_rules_kotlin_com_google_protobuf_protobuf_java_util//jar",
+        "@io_bazel_rules_kotlin_com_google_inject_guice//jar",
+        "@io_bazel_rules_kotlin_aopalliance_aopalliance//jar",
+        "@io_bazel_rules_kotlin_javax_inject_javax_inject//jar",
+        "@io_bazel_rules_kotlin_com_google_guava_guava//jar",
+        "@io_bazel_rules_kotlin_com_google_code_gson_gson//jar",
     ],
-    exports = [
-        "//kotlin/builder/proto",
-    ],
+    exports = ["//kotlin/builder/proto"],
     runtime_deps = [
         "@com_github_jetbrains_kotlin//:kotlin-stdlib",
         "@com_github_jetbrains_kotlin//:kotlin-stdlib-jdk7",
@@ -42,13 +43,41 @@
     runtime_deps = [":worker_lib"]
 )
 
+filegroup(
+    name = "builder_runfiles",
+    srcs = [
+        "@com_github_jetbrains_kotlin//:kotlin-annotation-processing",
+        "@com_github_jetbrains_kotlin//:kotlin-script-runtime",
+        "@com_github_jetbrains_kotlin//:compiler",
+    ],
+    visibility = ["//visibility:private"]
+)
+
+java_test(
+    name = "integrationtests",
+    test_class = "io.bazel.kotlin.builder.KotlinBuilderTestSuite",
+    srcs = glob(["integrationtests/*.java"]),
+    deps = [
+        "//kotlin/builder:worker_lib",
+        "//kotlin/builder/proto:proto",
+        "//third_party/jvm/junit",
+        "//third_party/jvm/com/google/protobuf:protobuf_java",
+        "//third_party/jvm/com/google/guava",
+        "//third_party/jvm/com/google/inject:guice",
+        "//third_party/jvm/com/google/truth"
+    ],
+    data = [
+        ":builder_runfiles"
+    ]
+)
+
 java_test(
     name = "unittests",
     test_class = "io.bazel.kotlin.builder.mode.jvm.utils.JdepsParserTest",
     srcs = glob(["unittests/**/*.java"]),
     deps = [
         ":worker_lib",
-        "@com_google_protobuf//:protobuf_java"
+        "//third_party/jvm/com/google/protobuf:protobuf_java",
     ],
     size = "small"
 )
\ No newline at end of file
diff --git a/kotlin/builder/integrationtests/KotlinBuilderActionTests.java b/kotlin/builder/integrationtests/KotlinBuilderActionTests.java
new file mode 100644
index 0000000..59b1fdb
--- /dev/null
+++ b/kotlin/builder/integrationtests/KotlinBuilderActionTests.java
@@ -0,0 +1,14 @@
+package io.bazel.kotlin.builder;
+
+import io.bazel.kotlin.builder.mode.jvm.actions.KotlinCompiler;
+import org.junit.Test;
+
+public class KotlinBuilderActionTests extends KotlinBuilderTestCase {
+  @Test
+  public void testCompileSimple() {
+    addSource("AClass.kt", "package something;" + "class AClass{}");
+    instance(KotlinCompiler.class).compile(builderCommand());
+    assertFileExists(DirectoryType.CLASSES, "something/AClass.class");
+    assertFileDoesNotExist(outputs().getOutput());
+  }
+}
diff --git a/kotlin/builder/integrationtests/KotlinBuilderTestCase.java b/kotlin/builder/integrationtests/KotlinBuilderTestCase.java
new file mode 100644
index 0000000..8e4b25d
--- /dev/null
+++ b/kotlin/builder/integrationtests/KotlinBuilderTestCase.java
@@ -0,0 +1,160 @@
+package io.bazel.kotlin.builder;
+
+import com.google.common.base.Joiner;
+import com.google.common.base.Preconditions;
+import com.google.inject.Injector;
+import io.bazel.kotlin.model.KotlinModel;
+import org.junit.Before;
+
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.IOException;
+import java.io.UncheckedIOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import static com.google.common.base.Charsets.UTF_8;
+import static com.google.common.truth.Truth.assertWithMessage;
+
+abstract class KotlinBuilderTestCase {
+  private static final Path BAZEL_TEST_DIR =
+      Paths.get(Preconditions.checkNotNull(System.getenv("TEST_TMPDIR")));
+  private static final AtomicInteger counter = new AtomicInteger(0);
+
+  private final KotlinModel.BuilderCommand.Builder builder =
+      KotlinModel.BuilderCommand.newBuilder();
+  private final Injector injector = KotlinToolchain.createInjector(System.out, null);
+  private String label = null;
+  private Path inputSourceDir = null;
+
+  @Before
+  public void setupNext() {
+    resetTestContext("a_test_" + counter.incrementAndGet());
+  }
+
+  protected KotlinModel.BuilderCommand.Outputs outputs() {
+    return builder.getOutputs();
+  }
+
+  protected String label() {
+    return Preconditions.checkNotNull(label);
+  }
+
+  private Path classDir() {
+    return Paths.get(outputs().getClassDirectory());
+  }
+
+  protected KotlinModel.BuilderCommand builderCommand() {
+    return builder.build();
+  }
+
+  protected <T> T instance(Class<T> clazz) {
+    return injector.getInstance(clazz);
+  }
+
+  protected void addSource(String filename, String... lines) {
+    Path file = Preconditions.checkNotNull(inputSourceDir, "initialize test context").resolve(filename);
+    try (BufferedWriter writer = com.google.common.io.Files.newWriter(file.toFile(), UTF_8)) {
+      writer.write(Joiner.on("\n").join(lines));
+      String f = file.toString();
+      if (f.endsWith(".kt")) {
+        builder.getInputsBuilder().addKotlinSources(f);
+      } else if (f.endsWith(".java")) {
+        builder.getInputsBuilder().addJavaSources(f);
+      } else {
+        throw new RuntimeException("unhandled file type: " + f);
+      }
+
+    } catch (IOException e) {
+      throw new UncheckedIOException(e);
+    }
+  }
+
+  protected void resetTestContext(String label) {
+    this.label = label;
+    builder.clear();
+    KotlinModel.BuilderCommand.Outputs.Builder testOuputs = setupTestOutputs(label);
+    builder.setInfo(
+        KotlinModel.BuilderCommand.Info.newBuilder()
+            .setLabel("//some/bogus:" + label)
+            .setKotlinModuleName("some_bogus_module")
+            .setRuleKind("kt_jvm_library")
+            .setToolchainInfo(
+                KotlinModel.KotlinToolchainInfo.newBuilder()
+                    .setCommon(
+                        KotlinModel.KotlinToolchainInfo.Common.newBuilder()
+                            .setApiVersion("1.2")
+                            .setCoroutines("enabled")
+                            .setLanguageVersion("1.2"))
+                    .setJvm(KotlinModel.KotlinToolchainInfo.Jvm.newBuilder().setJvmTarget("1.8"))));
+    builder.setOutputs(testOuputs);
+  }
+
+  private static String createTestOuputDirectory(Path path) {
+    try {
+      return Files.createDirectory(BAZEL_TEST_DIR.resolve(path)).toAbsolutePath().toString();
+    } catch (IOException e) {
+      throw new UncheckedIOException(e);
+    }
+  }
+
+  private static String createTestOutputFile(Path path) {
+    try {
+      return Files.createFile(BAZEL_TEST_DIR.resolve(path)).toAbsolutePath().toString();
+    } catch (IOException e) {
+      throw new UncheckedIOException(e);
+    }
+  }
+
+  private KotlinModel.BuilderCommand.Outputs.Builder setupTestOutputs(String label) {
+    Path prefixPath = Paths.get(label);
+    createTestOuputDirectory(prefixPath);
+    inputSourceDir = Paths.get(createTestOuputDirectory(prefixPath.resolve("input_sources")));
+    return KotlinModel.BuilderCommand.Outputs.newBuilder()
+        .setClassDirectory(prefixPath.resolve("classes").toAbsolutePath().toString())
+        .setSourceGenDir(prefixPath.resolve("sources").toAbsolutePath().toString())
+        .setTempDirectory(prefixPath.resolve("temp").toAbsolutePath().toString())
+        .setOutputJdeps(prefixPath.resolve("jdeps_files.jdeps").toAbsolutePath().toString())
+        .setOutput(prefixPath.resolve("jar_file.jar").toAbsolutePath().toString());
+  }
+
+  protected enum DirectoryType {
+    ROOT,
+    CLASSES,
+    TEMP,
+    SOURCE_GEN;
+
+    protected static Path select(DirectoryType type, KotlinModel.BuilderCommand command) {
+      Path ret;
+      switch (type) {
+        case CLASSES:
+          ret = Paths.get(command.getOutputs().getClassDirectory());
+          break;
+        case TEMP:
+          ret = Paths.get(command.getOutputs().getTempDirectory());
+          break;
+        case SOURCE_GEN:
+          ret = Paths.get(command.getOutputs().getSourceGenDir());
+          break;
+        default:
+          throw new RuntimeException("unhandled type: " + type);
+      }
+      return ret;
+    }
+  }
+
+  void assertFileExists(DirectoryType dir, String filePath) {
+    Path file = DirectoryType.select(dir, builderCommand()).resolve(filePath);
+    assertFileExists(file.toString());
+  }
+
+  void assertFileDoesNotExist(String filePath) {
+    assertWithMessage("file exisst: " + filePath).that(new File(filePath).exists()).isFalse();
+  }
+
+  void assertFileExists(String filePath) {
+    assertWithMessage("file did not exist: " + filePath).that(new File(filePath).exists()).isTrue();
+  }
+}
diff --git a/kotlin/builder/integrationtests/KotlinBuilderTestSuite.java b/kotlin/builder/integrationtests/KotlinBuilderTestSuite.java
new file mode 100644
index 0000000..649c0f2
--- /dev/null
+++ b/kotlin/builder/integrationtests/KotlinBuilderTestSuite.java
@@ -0,0 +1,8 @@
+package io.bazel.kotlin.builder;
+
+import org.junit.runner.RunWith;
+import org.junit.runners.Suite;
+
+@RunWith(Suite.class)
+@Suite.SuiteClasses({KotlinBuilderActionTests.class, KotlinBuilderTests.class})
+public class KotlinBuilderTestSuite {}
diff --git a/kotlin/builder/integrationtests/KotlinBuilderTests.java b/kotlin/builder/integrationtests/KotlinBuilderTests.java
new file mode 100644
index 0000000..f3d0b23
--- /dev/null
+++ b/kotlin/builder/integrationtests/KotlinBuilderTests.java
@@ -0,0 +1,54 @@
+package io.bazel.kotlin.builder;
+
+import com.google.common.truth.Truth;
+import com.google.devtools.build.lib.view.proto.Deps;
+import io.bazel.kotlin.builder.mode.jvm.KotlinJvmCompilationExecutor;
+import org.junit.Test;
+
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.UncheckedIOException;
+import java.nio.file.Paths;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+
+public class KotlinBuilderTests extends KotlinBuilderTestCase {
+  @Test
+  public void testSimpleCompile() {
+    addSource("AClass.kt", "package something;" + "class AClass{}");
+    runCompileTask();
+    assertFileExists(DirectoryType.CLASSES, "something/AClass.class");
+  }
+
+  @Test
+  public void testMixedModeCompile() {
+    addSource("AClass.kt", "package something;" + "class AClass{}");
+    addSource("AnotherClass.java", "package something;", "", "class AnotherClass{}");
+    runCompileTask();
+    assertFileExists(DirectoryType.CLASSES, "something/AClass.class");
+    assertFileExists(DirectoryType.CLASSES, "something/AnotherClass.class");
+    assertFileExists(outputs().getOutput());
+  }
+
+  private void runCompileTask() {
+    int timeoutSeconds = 10;
+    KotlinJvmCompilationExecutor executor= instance(KotlinJvmCompilationExecutor.class);
+    try {
+      CompletableFuture.runAsync(() -> executor.compile(builderCommand()))
+          .get(timeoutSeconds, TimeUnit.SECONDS);
+    } catch (TimeoutException e) {
+      throw new AssertionError("did not complete in: " + timeoutSeconds);
+    } catch (Exception e) {
+      throw new RuntimeException(e);
+    }
+    assertFileExists(outputs().getOutput());
+    assertFileExists(outputs().getOutputJdeps());
+    try(FileInputStream fs = new FileInputStream(Paths.get(outputs().getOutputJdeps()).toFile())) {
+      Deps.Dependencies dependencies = Deps.Dependencies.parseFrom(fs);
+      Truth.assertThat(dependencies.getRuleLabel()).endsWith(label());
+    } catch (IOException e) {
+      throw new UncheckedIOException(e);
+    }
+  }
+}
diff --git a/kotlin/builder/proto/jars/libkotlin_model_proto-speed.jar b/kotlin/builder/proto/jars/libkotlin_model_proto-speed.jar
index 9bfc978..97f045d 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 efebc18..7fdfe16 100644
--- a/kotlin/builder/proto/kotlin_model.proto
+++ b/kotlin/builder/proto/kotlin_model.proto
@@ -1,3 +1,16 @@
+// 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.
 syntax = "proto3";
 
 package bazel.kotlin;
@@ -33,4 +46,62 @@
         // oneof "1.6", or "1.8"
         string jvm_target = 4;
     }
+
+    Common common = 1;
+    Jvm jvm = 2;
+}
+
+message BuilderCommand {
+    message Info {
+        string label = 1;
+        // derived from label
+        string package = 2;
+        // derived from label
+        string target = 3;
+
+        string rule_kind = 4;
+        string kotlin_module_name = 5;
+
+        string passthrough_flags = 6;
+        KotlinToolchainInfo toolchain_info = 7;
+
+        CompilerPlugins plugins = 8;
+
+        // derived from plugins
+        repeated string encoded_plugin_descriptors=9;
+    }
+
+    message Outputs {
+        string class_directory = 1;
+        string temp_directory = 2;
+        string source_gen_dir = 3;
+
+        string output = 4;
+        string output_jdeps = 5;
+    }
+
+    message Inputs {
+        repeated string classpath = 1;
+        // derived from command line classpath
+        string joined_classpath = 2;
+
+        map<string, string> direct_dependencies = 3;
+        map<string, string> indirect_dependencies = 4;
+
+        // partitioned from builder flags
+        repeated string kotlin_sources = 5;
+        // partitioned from builder flags
+        repeated string java_sources = 6;
+
+        // created during compilation
+        repeated string generated_kotlin_sources = 7;
+        // created during compilation
+        repeated string generated_java_sources = 8;
+
+        repeated string source_jars = 9;
+    }
+
+    Info info = 1;
+    Outputs outputs = 2;
+    Inputs inputs = 3;
 }
\ No newline at end of file
diff --git a/kotlin/builder/src/io/bazel/kotlin/builder/BazelWorker.kt b/kotlin/builder/src/io/bazel/kotlin/builder/BazelWorker.kt
index 7834b4b..d839009 100644
--- a/kotlin/builder/src/io/bazel/kotlin/builder/BazelWorker.kt
+++ b/kotlin/builder/src/io/bazel/kotlin/builder/BazelWorker.kt
@@ -24,6 +24,48 @@
 import java.nio.file.Paths
 import java.util.stream.Collectors
 
+sealed class ToolException(
+    msg: String,
+    val category: Category,
+    ex: Throwable? = null
+) : RuntimeException(msg, ex) {
+    enum class Category(val code: Int) {
+        COMPILER_ERROR(1), // 1 is a standard compilation error
+        INTERNAL(2),// 2 is an internal error
+        SCRIPT(3)// 3 is the script execution error
+    }
+}
+
+class CompilationException(msg: String, cause: Throwable? = null, category: Category = Category.INTERNAL) :
+    ToolException(msg, category, cause)
+
+class CompilationStatusException(
+    msg: String,
+    val status: Int,
+    val lines: List<String> = emptyList(),
+    category: Category = Category.COMPILER_ERROR
+) : ToolException("$msg:${lines.joinToString("\n", "\n")}", category)
+
+
+/**
+ * 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
+}
+
+
 /**
  * Bazel worker runner.
  *
@@ -35,10 +77,17 @@
  *
  * @param <T> delegate program type
 </T> */
-class BazelWorker<T : CommandLineProgram>(private val delegate: T, private val output: PrintStream, private val mnemonic: String) : CommandLineProgram {
+class BazelWorker(
+    private val delegate: CommandLineProgram,
+    private val output: PrintStream,
+    private val mnemonic: String
+) : CommandLineProgram {
     companion object {
         private const val INTERUPTED_STATUS = 0
         private const val ERROR_STATUS = 1
+
+        @JvmStatic
+        val IS_DEBUG: Boolean = System.getProperty("bazel.kotlin.worker.debug") != null
     }
 
     override fun apply(args: List<String>): Int {
@@ -70,16 +119,17 @@
                                     return INTERUPTED_STATUS
                                 }
                                 System.err.println(
-                                        "ERROR: Worker threw uncaught exception with args: " + request.argumentsList.stream().collect(Collectors.joining(" ")))
+                                    "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)
+                                .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
@@ -115,8 +165,9 @@
                 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)
+                            "HINT: %s will compile faster if you run: " + "echo \"build --strategy=%s=worker\" >>~/.bazelrc\n",
+                            mnemonic, mnemonic
+                        )
                     }
                     try {
                         return Files.readAllLines(flagFile, UTF_8)
diff --git a/kotlin/builder/src/io/bazel/kotlin/builder/BuildAction.kt b/kotlin/builder/src/io/bazel/kotlin/builder/BuildAction.kt
deleted file mode 100644
index 87e6b09..0000000
--- a/kotlin/builder/src/io/bazel/kotlin/builder/BuildAction.kt
+++ /dev/null
@@ -1,21 +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
-
-
-abstract class BuildAction(@Suppress("unused") val id : String, protected val toolchain: KotlinToolchain) {
-    abstract operator fun invoke(ctx: Context): Int
-}
diff --git a/kotlin/builder/src/io/bazel/kotlin/builder/BuildCommandBuilder.kt b/kotlin/builder/src/io/bazel/kotlin/builder/BuildCommandBuilder.kt
new file mode 100644
index 0000000..b31b31d
--- /dev/null
+++ b/kotlin/builder/src/io/bazel/kotlin/builder/BuildCommandBuilder.kt
@@ -0,0 +1,181 @@
+/*
+ * 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
+
+import com.google.inject.ImplementedBy
+import com.google.inject.Inject
+import com.google.inject.Singleton
+import com.google.protobuf.util.JsonFormat
+import io.bazel.kotlin.builder.utils.ArgMap
+import io.bazel.kotlin.builder.utils.DefaultKotlinCompilerPluginArgsEncoder
+import io.bazel.kotlin.model.KotlinModel
+import io.bazel.kotlin.model.KotlinModel.BuilderCommand
+
+
+@ImplementedBy(DefaultBuildCommandBuilder::class)
+interface BuildCommandBuilder {
+    fun fromInput(argMap: ArgMap): BuilderCommand
+    fun withSources(command: BuilderCommand, sources: Iterator<String>): BuilderCommand
+    fun withGeneratedSources(command: BuilderCommand, sources: Iterator<String>): BuilderCommand
+}
+
+@Singleton
+private class DefaultBuildCommandBuilder @Inject constructor(
+    private val pluginEncoder: DefaultKotlinCompilerPluginArgsEncoder
+) : BuildCommandBuilder {
+    companion object {
+        @JvmStatic
+        private val jsonTypeRegistry = JsonFormat.TypeRegistry.newBuilder()
+            .add(KotlinModel.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(val flag: String) {
+        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")
+    }
+
+    override fun fromInput(argMap: ArgMap): BuilderCommand =
+        BuilderCommand.newBuilder().let { root ->
+            with(root.outputsBuilder) {
+                classDirectory = argMap.mandatorySingle(JavaBuilderFlags.CLASSDIR.flag)
+                tempDirectory = argMap.mandatorySingle(JavaBuilderFlags.TEMPDIR.flag)
+                sourceGenDir = argMap.mandatorySingle(JavaBuilderFlags.SOURCEGEN_DIR.flag)
+                output = argMap.mandatorySingle(JavaBuilderFlags.OUTPUT.flag)
+                outputJdeps = argMap.mandatorySingle("--output_jdeps")
+            }
+
+            with(root.inputsBuilder) {
+                addAllClasspath(argMap.mandatory(JavaBuilderFlags.CLASSPATH.flag))
+                putAllIndirectDependencies(argMap.labelDepMap(JavaBuilderFlags.DIRECT_DEPENDENCY.flag))
+                putAllIndirectDependencies(argMap.labelDepMap(JavaBuilderFlags.INDIRECT_DEPENDENCY.flag))
+
+                argMap.optional(JavaBuilderFlags.SOURCES.flag)?.iterator()?.partitionSources(
+                    { addKotlinSources(it) },
+                    { addJavaSources(it) }
+                )
+                argMap.optional(JavaBuilderFlags.SOURCE_JARS.flag)?.also {
+                    addAllSourceJars(it)
+                }
+
+
+                joinedClasspath = classpathList.joinToString(":")
+            }
+
+            with(root.infoBuilder) {
+                label = argMap.mandatorySingle(JavaBuilderFlags.TARGET_LABEL.flag)
+                ruleKind = argMap.mandatorySingle(JavaBuilderFlags.RULE_KIND.flag)
+                kotlinModuleName = argMap.optionalSingle("--kotlin_module_name")
+                passthroughFlags = argMap.optionalSingle("--kotlin_passthrough_flags")
+                toolchainInfoBuilder.commonBuilder.apiVersion = argMap.mandatorySingle("--kotlin_api_version")
+                toolchainInfoBuilder.commonBuilder.languageVersion = argMap.mandatorySingle("--kotlin_language_version")
+                toolchainInfoBuilder.jvmBuilder.jvmTarget = argMap.mandatorySingle("--kotlin_jvm_target")
+
+                argMap.optionalSingle("--kt-plugins")?.let { input ->
+                    plugins = KotlinModel.CompilerPlugins.newBuilder().let {
+                        jsonFormat.merge(input, it)
+                        it.build()
+                    }
+                }
+
+                if (plugins.annotationProcessorsList.isNotEmpty()) {
+                    addAllEncodedPluginDescriptors(pluginEncoder.encode(root.outputs, root.info.plugins))
+                }
+
+                label.split(":").also {
+                    check(it.size == 2) { "the label ${root.info.label} is invalid" }
+                    `package` = it[0]
+                    target = it[1]
+                }
+
+                kotlinModuleName = kotlinModuleName.supplyIfNullOrBlank {
+                    "${`package`.trimStart { it == '/' }.replace('/', '_')}-$target"
+                }
+            }
+            root.build()
+        }
+
+    override fun withSources(command: BuilderCommand, sources: Iterator<String>): BuilderCommand =
+        command.updateBuilder { builder ->
+            sources.partitionSources(
+                { builder.inputsBuilder.addGeneratedKotlinSources(it) },
+                { builder.inputsBuilder.addGeneratedJavaSources(it) })
+        }
+
+
+    override fun withGeneratedSources(command: BuilderCommand, sources: Iterator<String>): BuilderCommand =
+        command.updateBuilder { builder ->
+            sources.partitionSources(
+                { builder.inputsBuilder.addGeneratedKotlinSources(it) },
+                { builder.inputsBuilder.addGeneratedJavaSources(it) })
+        }
+
+    private fun BuilderCommand.updateBuilder(init: (BuilderCommand.Builder) -> Unit): BuilderCommand =
+        toBuilder().let {
+            init(it)
+            it.build()
+        }
+
+
+    private fun Iterator<String>.partitionSources(kt: (String) -> Unit, java: (String) -> Unit) {
+        forEach {
+            when {
+                it.endsWith(".kt") -> kt(it)
+                it.endsWith(".java") -> java(it)
+                else -> throw IllegalStateException("invalid source file type $it")
+            }
+        }
+    }
+
+    private fun String?.supplyIfNullOrBlank(s: () -> String): String = this?.takeIf { it.isNotBlank() } ?: s()
+}
+
diff --git a/kotlin/builder/src/io/bazel/kotlin/builder/CommandLineProgram.kt b/kotlin/builder/src/io/bazel/kotlin/builder/CommandLineProgram.kt
deleted file mode 100644
index bfe532a..0000000
--- a/kotlin/builder/src/io/bazel/kotlin/builder/CommandLineProgram.kt
+++ /dev/null
@@ -1,57 +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
-
-import io.bazel.kotlin.builder.model.Flags
-
-/**
- * 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
-
-    abstract class Base(
-    ) : CommandLineProgram {
-        private fun createContext(toolchain: KotlinToolchain, args: List<String>): Context {
-            return Context(toolchain, Flags(ArgMaps.from(args)))
-        }
-
-        abstract val toolchain: KotlinToolchain
-        abstract fun actions(toolchain: KotlinToolchain, ctx: Context): List<BuildAction>
-
-        override fun apply(args: List<String>): Int {
-            val ctx = createContext(toolchain, args)
-            var exitCode = 0
-            for (action in actions(toolchain, ctx)) {
-                exitCode = action(ctx)
-                if (exitCode != 0)
-                    break
-            }
-            return exitCode
-        }
-    }
-}
\ No newline at end of file
diff --git a/kotlin/builder/src/io/bazel/kotlin/builder/CompileResult.kt b/kotlin/builder/src/io/bazel/kotlin/builder/CompileResult.kt
deleted file mode 100644
index 5cf91cf..0000000
--- a/kotlin/builder/src/io/bazel/kotlin/builder/CompileResult.kt
+++ /dev/null
@@ -1,111 +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
-
-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.kotlin.builder.Meta<CompileResult> {
-        override val id: String = 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)
-            set(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/builder/src/io/bazel/kotlin/builder/Context.kt b/kotlin/builder/src/io/bazel/kotlin/builder/Context.kt
deleted file mode 100644
index 1e04e69..0000000
--- a/kotlin/builder/src/io/bazel/kotlin/builder/Context.kt
+++ /dev/null
@@ -1,36 +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.
- */
-@file:Suppress("UNCHECKED_CAST")
-
-package io.bazel.kotlin.builder
-
-import io.bazel.kotlin.builder.model.Flags
-import java.util.stream.Stream
-
-class Context internal constructor(
-        val toolchain: KotlinToolchain,
-        val flags: Flags
-
-) {
-    private val meta = mutableMapOf<Meta<*>, Any>()
-
-    fun apply(vararg consumers: (Context) -> Unit) {
-        Stream.of(*consumers).forEach { it(this) }
-    }
-
-    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?
-}
diff --git a/kotlin/builder/src/io/bazel/kotlin/builder/KotlinBuilder.kt b/kotlin/builder/src/io/bazel/kotlin/builder/KotlinBuilder.kt
index 1bcece6..7830d46 100644
--- a/kotlin/builder/src/io/bazel/kotlin/builder/KotlinBuilder.kt
+++ b/kotlin/builder/src/io/bazel/kotlin/builder/KotlinBuilder.kt
@@ -15,39 +15,94 @@
  */
 package io.bazel.kotlin.builder
 
+import com.google.common.collect.ImmutableList
+import com.google.inject.Inject
+import com.google.inject.Singleton
+import io.bazel.kotlin.builder.mode.jvm.KotlinJvmCompilationExecutor
+import io.bazel.kotlin.builder.utils.ArgMap
+import io.bazel.kotlin.builder.utils.ArgMaps
+import io.bazel.kotlin.model.KotlinModel
+import java.nio.file.Files
+import java.nio.file.Paths
 
-import io.bazel.kotlin.builder.mode.jvm.actions.*
-import java.io.IOException
+@Singleton
+@Suppress("MemberVisibilityCanBePrivate")
+class KotlinBuilder @Inject internal constructor(
+    private val commandBuilder: BuildCommandBuilder,
+    private val jarToolInvoker: KotlinToolchain.JarToolInvoker,
+    private val compilationExector: KotlinJvmCompilationExecutor
+) : CommandLineProgram {
+    fun execute(command: KotlinModel.BuilderCommand): Int =
+        prepareForExecution(command).let { doExecute(it) }
 
-/**
- * Bazel Kotlin Compiler worker.
- */
-object KotlinBuilder : CommandLineProgram.Base() {
-    override val toolchain: KotlinToolchain = try {
-        KotlinToolchain()
-    } catch (e: IOException) {
-        throw RuntimeException("could not initialize toolchain", e)
+    fun execute(args: List<String>): Int =
+        execute(ArgMaps.from(args))
+
+    fun execute(args: ArgMap): Int =
+        prepareForExecution(commandBuilder.fromInput(args)).let {
+            execute(it)
+        }
+
+
+    private fun doExecute(command: KotlinModel.BuilderCommand): Int {
+        return try {
+            compilationExector.compile(command)
+            // gets basic timings on the tasks - create a better interface for this. and remove the line.
+            // println(res.command.info.label + "\n" + res.timings.joinToString("\n"))
+            0
+        } catch (ex: CompilationStatusException) {
+            ex.lines.forEach { println(it) }
+            return ex.status
+        }
     }
 
-    private val compileActions: List<BuildAction> = listOf(
-            UnpackSourceJars(toolchain),
-            Initialize(toolchain),
-            KotlinMainCompile(toolchain),
-            JavaMainCompile(toolchain),
-            ProcessCompileResult(toolchain),
-            CreateOutputJar(toolchain),
-            GenerateJdepsFile(toolchain)
-    )
+    private fun prepareForExecution(originalCommand: KotlinModel.BuilderCommand): KotlinModel.BuilderCommand {
+        ensureOutputDirectories(originalCommand)
+        return commandBuilder.withSources(originalCommand, unpackSources(originalCommand))
+    }
 
-    override fun actions(toolchain: KotlinToolchain, ctx: Context): List<BuildAction> = compileActions
+    private fun ensureOutputDirectories(command: KotlinModel.BuilderCommand) {
+        Files.createDirectories(Paths.get(command.outputs.classDirectory))
+        Files.createDirectories(Paths.get(command.outputs.tempDirectory))
+        Files.createDirectories(Paths.get(command.outputs.sourceGenDir))
+    }
 
-    @JvmStatic
-    fun main(args: Array<String>) {
-        val kotlinCompilerBazelWorker = BazelWorker(
-                this,
-                System.err,
-                "KotlinCompile"
-        )
-        System.exit(kotlinCompilerBazelWorker.apply(args.toList()))
+    private fun unpackSources(command: KotlinModel.BuilderCommand): Iterator<String> {
+        if (command.inputs.sourceJarsList.isEmpty()) {
+            ImmutableList.of<String>().iterator()
+        }
+
+        val sourceUnpackDirectory =
+            Paths.get(command.outputs.tempDirectory).let {
+                it.resolve("_srcjars").toFile().let {
+                    try {
+                        it.mkdirs(); it
+                    } catch (ex: Exception) {
+                        throw RuntimeException("could not create unpack directory at $it", ex)
+                    }
+                }
+            }
+
+        for (sourceJar in command.inputs.sourceJarsList) {
+            jarToolInvoker.invoke(listOf("xf", Paths.get(sourceJar).toAbsolutePath().toString()), sourceUnpackDirectory)
+        }
+
+        return sourceUnpackDirectory
+            .walk()
+            .filter { it.name.endsWith(".kt") || it.name.endsWith(".java") }
+            .map { it.toString() }
+            .iterator()
+    }
+
+    override fun apply(args: List<String>): Int {
+        return execute(args)
+    }
+
+    companion object {
+        @JvmStatic
+        fun main(args: Array<String>) {
+            val worker = KotlinToolchain.createInjector(System.err).getInstance(BazelWorker::class.java)
+            System.exit(worker.apply(args.toList()))
+        }
     }
 }
diff --git a/kotlin/builder/src/io/bazel/kotlin/builder/KotlinToolchain.kt b/kotlin/builder/src/io/bazel/kotlin/builder/KotlinToolchain.kt
index cf68e6f..9220c38 100644
--- a/kotlin/builder/src/io/bazel/kotlin/builder/KotlinToolchain.kt
+++ b/kotlin/builder/src/io/bazel/kotlin/builder/KotlinToolchain.kt
@@ -15,72 +15,124 @@
  */
 package io.bazel.kotlin.builder
 
+import com.google.inject.*
+import com.google.inject.util.Modules
+import io.bazel.kotlin.builder.utils.executeAndAwait
 import io.bazel.kotlin.builder.utils.resolveVerified
 import io.bazel.kotlin.builder.utils.verifiedRelativeFiles
 import org.jetbrains.kotlin.preloading.ClassPreloadingUtils
 import org.jetbrains.kotlin.preloading.Preloader
 import java.io.File
 import java.io.PrintStream
+import java.io.PrintWriter
 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()
-
-    val KAPT_PLUGIN = CompilerPlugin(KOTLIN_LIB_DIR.resolveVerified("kotlin-annotation-processing.jar").toString(), "org.jetbrains.kotlin.kapt3")
-
-    private val kotlinPreloadJars = mutableListOf<File>().let {
-        it.addAll(KOTLIN_LIB_DIR.verifiedRelativeFiles(Paths.get("kotlin-compiler.jar")))
-
-        // tools.jar is need for annotation processing
-        it.addAll(JAVA_HOME.verifiedRelativeFiles(Paths.get("lib", "tools.jar")))
-        it.toList()
-    }
-
-    val KOTLIN_STD_LIBS = arrayOf(
-            "kotlin-stdlib.jar",
-            "kotlin-stdlib-jdk7.jar",
-            "kotlin-stdlib-jdk8.jar"
+class KotlinToolchain constructor(
+    val kotlinLibraryDirectory: Path,
+    val kotlinStandardLibraries: Array<String> = arrayOf(
+        "kotlin-stdlib.jar",
+        "kotlin-stdlib-jdk7.jar",
+        "kotlin-stdlib-jdk8.jar"
     )
+) : AbstractModule() {
+    companion object {
+        internal val NO_ARGS = arrayOf<Any>()
 
-    private val classLoader: ClassLoader by lazy {
-        ClassPreloadingUtils.preloadClasses(
-                kotlinPreloadJars,
-                Preloader.DEFAULT_CLASS_NUMBER_ESTIMATE,
-                Thread.currentThread().contextClassLoader,
-                null
-        )
+        @JvmStatic
+        fun createInjector(output: PrintStream, overrides: Module? = null): Injector =
+            Guice.createInjector(
+                object : AbstractModule() {
+                    override fun configure() {
+                        bind(PrintStream::class.java).toInstance(output)
+                        install(
+                            KotlinToolchain.TCModule(
+                                javaHome = Paths.get("external", "local_jdk"),
+                                kotlinHome = Paths.get("external", "com_github_jetbrains_kotlin")
+                            )
+                        )
+                    }
+                }.let { module -> overrides?.let { Modules.override(module).with(it) }?: module }
+            )
     }
 
-    interface KotlinCompiler {
+    data class CompilerPlugin(val jarPath: String, val id: String) {
+        @BindingAnnotation
+        annotation class Kapt3
+    }
+
+    interface JavacInvoker {
+        fun compile(args: Array<String>): Int
+        fun compile(args: Array<String>, out: PrintWriter): Int
+    }
+
+
+    interface JDepsInvoker {
+        fun run(args: Array<String>, out: PrintWriter): Int
+    }
+
+
+    interface KotlincInvoker {
         fun compile(args: Array<String>, out: PrintStream): Int
     }
 
-    data class CompilerPlugin(val jarPath: String, val id: String)
+    interface JarToolInvoker {
+        fun invoke(args: List<String>, directory: File? = null)
+    }
 
-    /**
-     * 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")
+    private class TCModule constructor(
+        javaHome: Path,
+        kotlinHome: Path,
+        kotlinLibraryDirectory: Path = kotlinHome.resolveVerified("lib").toPath(),
+        kapt3Jar: File= kotlinLibraryDirectory.resolveVerified("kotlin-annotation-processing.jar"),
+        classloader: ClassLoader = ClassPreloadingUtils.preloadClasses(
+            mutableListOf<File>().let {
+                it.addAll(kotlinLibraryDirectory.verifiedRelativeFiles(Paths.get("kotlin-compiler.jar")))
+                it.addAll(javaHome.verifiedRelativeFiles(Paths.get("lib", "tools.jar")))
+                it.toList()
+            },
+            Preloader.DEFAULT_CLASS_NUMBER_ESTIMATE,
+            Thread.currentThread().contextClassLoader,
+            null
+        )
+    ): AbstractModule() {
+        private val toolchain = KotlinToolchain(kotlinHome)
 
-        val compiler = compilerClass.newInstance()
-        val execMethod = compilerClass.getMethod("exec", PrintStream::class.java, Array<String>::class.java)
-        val getCodeMethod = exitCodeClass.getMethod("getCode")
+        private val kapt3 = CompilerPlugin(kapt3Jar.toString(), "org.jetbrains.kotlin.kapt3")
 
-        object : KotlinCompiler {
+        private val jarToolInvoker = object : JarToolInvoker {
+            val jarToolPath = javaHome.resolveVerified("bin", "jar").absolutePath.toString()
+            override fun invoke(args: List<String>, directory: File?) {
+                val command = mutableListOf(jarToolPath).also { it.addAll(args) }
+                executeAndAwait(10, directory, command).takeIf { it != 0 }?.also {
+                    throw CompilationStatusException("error running jar command ${command.joinToString(" ")}", it)
+                }
+            }
+        }
+
+        private val javacInvoker = object : JavacInvoker {
+            val c = classloader.loadClass("com.sun.tools.javac.Main")
+            val m = c.getMethod("compile", Array<String>::class.java)
+            val mPw = c.getMethod("compile", Array<String>::class.java, PrintWriter::class.java)
+            override fun compile(args: Array<String>) = m.invoke(c, args) as Int
+            override fun compile(args: Array<String>, out: PrintWriter) = mPw.invoke(c, args, out) as Int
+        }
+
+        private val jdepsInvoker = object : JDepsInvoker {
+            val clazz = classloader.loadClass("com.sun.tools.jdeps.Main")
+            val method = clazz.getMethod("run", Array<String>::class.java, PrintWriter::class.java)
+            override fun run(args: Array<String>, out: PrintWriter): Int = method.invoke(clazz, args, out) as Int
+        }
+
+        private val kotlincInvoker = object : KotlincInvoker {
+            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")
+
+
             override fun compile(args: Array<String>, out: PrintStream): Int {
                 val exitCodeInstance: Any
                 try {
@@ -91,6 +143,24 @@
                 }
             }
         }
+
+        override fun configure() {
+            bind(KotlinToolchain::class.java).toInstance(toolchain)
+            bind(JarToolInvoker::class.java).toInstance(jarToolInvoker)
+            bind(JavacInvoker::class.java).toInstance(javacInvoker)
+            bind(JDepsInvoker::class.java).toInstance(jdepsInvoker)
+            bind(KotlincInvoker::class.java).toInstance(kotlincInvoker)
+        }
+
+        @Provides
+        @CompilerPlugin.Kapt3
+        fun provideKapt3(): CompilerPlugin = kapt3
+
+        @Provides
+        fun provideBazelWorker(
+            kotlinBuilder: KotlinBuilder,
+            output: PrintStream
+        ): BazelWorker = BazelWorker(kotlinBuilder, output, "KotlinCompile")
     }
 }
 
diff --git a/kotlin/builder/src/io/bazel/kotlin/builder/Meta.kt b/kotlin/builder/src/io/bazel/kotlin/builder/Meta.kt
deleted file mode 100644
index dd089bb..0000000
--- a/kotlin/builder/src/io/bazel/kotlin/builder/Meta.kt
+++ /dev/null
@@ -1,62 +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
-
-interface Meta<T : Any> {
-    val id: String
-        get() = this.javaClass.simpleName
-
-    val defaultValue: T?
-        get() = 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
-        }
-    }
-
-    operator fun set(ctx: Context, value: T?) {
-        checkNotNull(value ?: defaultValue) { "attampting to initialize ctx to a null value: (hasDefault: ${defaultValue != null}" }.also {
-            check(ctx.putIfAbsent(this, it) == null) { "attempting to change bound meta variable: $id " }
-        }
-    }
-
-    companion object {
-        operator fun <T : Any> invoke(id: String): Meta<T> = object : Meta<T> {
-            override val id: String = id
-            override val defaultValue: T? = null
-        }
-    }
-}
-
-open class MandatoryMeta<T: Any>(
-        override val id: String,
-        override val defaultValue: T? = null
-): Meta<T> {
-    override fun get(ctx: Context): T = checkNotNull(super.get(ctx)) { "ctx missing mandatory meta ${this.id}" }
-}
diff --git a/kotlin/builder/src/io/bazel/kotlin/builder/mode/jvm/KotlinJvmCompilationExecutor.kt b/kotlin/builder/src/io/bazel/kotlin/builder/mode/jvm/KotlinJvmCompilationExecutor.kt
new file mode 100644
index 0000000..7887a2a
--- /dev/null
+++ b/kotlin/builder/src/io/bazel/kotlin/builder/mode/jvm/KotlinJvmCompilationExecutor.kt
@@ -0,0 +1,103 @@
+/*
+ * 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.mode.jvm
+
+import com.google.common.base.Stopwatch
+import com.google.inject.ImplementedBy
+import com.google.inject.Inject
+import com.google.inject.Singleton
+import io.bazel.kotlin.builder.BuildCommandBuilder
+import io.bazel.kotlin.builder.mode.jvm.KotlinJvmCompilationExecutor.Result
+import io.bazel.kotlin.builder.mode.jvm.actions.JDepsGenerator
+import io.bazel.kotlin.builder.mode.jvm.actions.JavaCompiler
+import io.bazel.kotlin.builder.mode.jvm.actions.KotlinCompiler
+import io.bazel.kotlin.builder.mode.jvm.actions.OutputJarCreator
+import io.bazel.kotlin.builder.mode.jvm.utils.KotlinCompilerOutputSink
+import io.bazel.kotlin.model.KotlinModel.BuilderCommand
+import java.io.File
+import java.util.concurrent.TimeUnit
+
+@ImplementedBy(DefaultKotlinJvmCompilationExecutor::class)
+interface KotlinJvmCompilationExecutor {
+    class Result(val timings: List<String>, val command: BuilderCommand)
+
+    fun compile(command: BuilderCommand): Result
+}
+
+@Singleton
+private class DefaultKotlinJvmCompilationExecutor @Inject constructor(
+    private val commandBuilder: BuildCommandBuilder,
+    private val kotlinCompiler: KotlinCompiler,
+    private val outputSink: KotlinCompilerOutputSink,
+    private val javaCompiler: JavaCompiler,
+    private val jDepsGenerator: JDepsGenerator,
+    private val outputJarCreator: OutputJarCreator
+) : KotlinJvmCompilationExecutor {
+    override fun compile(command: BuilderCommand): Result {
+        val context = Context()
+        val commandWithApSources = context.execute("kapt") {
+            runAnnotationProcessors(command)
+        }
+        compileClasses(context, commandWithApSources)
+        context.execute("create jar") {
+            outputJarCreator.createOutputJar(commandWithApSources)
+        }
+        context.execute("generate jdeps") {
+            jDepsGenerator.generateJDeps(commandWithApSources)
+        }
+        return Result(context.timings, commandWithApSources)
+    }
+
+    private fun runAnnotationProcessors(command: BuilderCommand): BuilderCommand =
+        if (command.info.plugins.annotationProcessorsList.isNotEmpty()) {
+            kotlinCompiler.runAnnotationProcessor(command)
+            File(command.outputs.sourceGenDir).walkTopDown()
+                .filter { it.isFile }
+                .map { it.path }
+                .iterator()
+                .let { commandBuilder.withGeneratedSources(command, it) }
+        } else {
+            command
+        }
+
+    private fun compileClasses(context: Context, command: BuilderCommand) {
+        val result = context.execute("kotlinc") {
+            kotlinCompiler.compile(command)
+        }
+        try {
+            context.execute("javac") {
+                javaCompiler.compile(command)
+            }
+        } finally {
+            result.forEach(outputSink::deliver)
+        }
+    }
+
+    internal class Context {
+        val timings = mutableListOf<String>()
+        val sw: Stopwatch = Stopwatch.createUnstarted()
+        inline fun <T> execute(name: String, task: () -> T): T {
+            sw.start()
+            return try {
+                task()
+            } finally {
+                sw.stop()
+                timings += "$name: ${sw.elapsed(TimeUnit.MILLISECONDS)} ms"
+                sw.reset()
+            }
+        }
+    }
+}
diff --git a/kotlin/builder/src/io/bazel/kotlin/builder/mode/jvm/actions/CreateOutputJar.kt b/kotlin/builder/src/io/bazel/kotlin/builder/mode/jvm/actions/CreateOutputJar.kt
deleted file mode 100644
index ca94178..0000000
--- a/kotlin/builder/src/io/bazel/kotlin/builder/mode/jvm/actions/CreateOutputJar.kt
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- * Copyright 2018 The Bazel Authors. All rights reserved.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *    http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package io.bazel.kotlin.builder.mode.jvm.actions
-
-import io.bazel.kotlin.builder.BuildAction
-import io.bazel.kotlin.builder.Context
-import io.bazel.kotlin.builder.KotlinToolchain
-import io.bazel.kotlin.builder.utils.executeAndAwaitSuccess
-import java.nio.file.Path
-
-/**
- * Create a jar from all the input.
- */
-class CreateOutputJar(toolchain: KotlinToolchain) : BuildAction("create output jar", toolchain) {
-    private fun MutableList<String>.addAllFrom(dir: Path) = addAll(arrayOf("-C", dir.toString(), "."))
-
-    override fun invoke(ctx: Context): Int {
-        try {
-            mutableListOf(
-                    toolchain.JAR_TOOL_PATH,
-                    "cf", ctx.flags.outputClassJar
-            ).also { args ->
-                args.addAllFrom(ctx.flags.classDir.value)
-            }.also { executeAndAwaitSuccess(10, *it.toTypedArray()) }
-        } catch (e: Exception) {
-            throw RuntimeException("unable to create class jar", e)
-        }
-        return 0
-    }
-}
diff --git a/kotlin/builder/src/io/bazel/kotlin/builder/mode/jvm/actions/GenerateJdepsFile.kt b/kotlin/builder/src/io/bazel/kotlin/builder/mode/jvm/actions/GenerateJdepsFile.kt
deleted file mode 100644
index f4971f2..0000000
--- a/kotlin/builder/src/io/bazel/kotlin/builder/mode/jvm/actions/GenerateJdepsFile.kt
+++ /dev/null
@@ -1,61 +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.mode.jvm.actions
-
-import com.google.devtools.build.lib.view.proto.Deps
-import io.bazel.kotlin.builder.BuildAction
-import io.bazel.kotlin.builder.Context
-import io.bazel.kotlin.builder.KotlinToolchain
-import io.bazel.kotlin.builder.mode.jvm.utils.JdepsParser
-import io.bazel.kotlin.builder.model.Metas
-import io.bazel.kotlin.builder.utils.executeAndWaitOutput
-import io.bazel.kotlin.builder.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 jdepsContent: Deps.Dependencies
-        val classpath = Metas.CLASSPATH_STRING[ctx]
-        try {
-            jdepsContent = executeAndWaitOutput(10, toolchain.JDEPS_PATH, "-cp", classpath, ctx.flags.outputClassJar).let {
-                JdepsParser.parse(
-                        ctx.flags.label,
-                        ctx.flags.outputClassJar,
-                        classpath,
-                        it.stream(), isKotlinImplicit)
-            }
-        } catch (e: Exception) {
-            throw RuntimeException("error reading or parsing jdeps file", e.rootCause)
-        }
-
-        try {
-            Paths.get(ctx.flags.outputJdeps).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/builder/src/io/bazel/kotlin/builder/mode/jvm/actions/Initialize.kt b/kotlin/builder/src/io/bazel/kotlin/builder/mode/jvm/actions/Initialize.kt
deleted file mode 100644
index 383a521..0000000
--- a/kotlin/builder/src/io/bazel/kotlin/builder/mode/jvm/actions/Initialize.kt
+++ /dev/null
@@ -1,92 +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.mode.jvm.actions
-
-
-import io.bazel.kotlin.builder.BuildAction
-import io.bazel.kotlin.builder.Context
-import io.bazel.kotlin.builder.KotlinToolchain
-import io.bazel.kotlin.builder.model.CompilePluginConfig
-import io.bazel.kotlin.builder.model.Metas
-import io.bazel.kotlin.builder.utils.PluginArgs
-
-/**
- * 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(
-                ::bindLabelComponents,
-                ::bindPluginStatus,
-                ::bindSources,
-                ::memoize
-        )
-        return 0
-    }
-
-    private fun memoize(ctx: Context) {
-        Metas.CLASSPATH_STRING[ctx] = ctx.flags.classpath.joinToString(":")
-    }
-
-    private fun bindPluginStatus(ctx: Context) {
-        CompilePluginConfig[ctx] = ctx.flags.plugins?.let {
-            PluginArgs.from(ctx)?.let {
-                CompilePluginConfig(hasAnnotationProcessors = true, args = it.toTypedArray())
-            }
-        }
-    }
-
-    private fun bindSources(ctx: Context) {
-        val javaSources = mutableListOf<String>()
-        val allSources = mutableListOf<String>()
-
-        val sourcePool = with(mutableListOf<String>()) {
-            ctx.flags.source?.also {
-                check(it.isNotEmpty())
-                addAll(it)
-            }
-            addAll(Metas.UNPACKED_SOURCES[ctx] ?: emptyList())
-            toList()
-        }
-
-        if(sourcePool.isEmpty()) {
-            throw RuntimeException("no compilable sources found")
-        }
-
-        for (src in sourcePool) {
-            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[ctx] = javaSources.toList()
-        Metas.ALL_SOURCES[ctx] = allSources.toList()
-    }
-
-    /**
-     * parses the label, sets up the meta elements and returns the target part.
-     */
-    private fun bindLabelComponents(ctx: Context) {
-        val parts = ctx.flags.label.split(":")
-        require(parts.size == 2) { "the label ${ctx.flags.label} is invalid" }
-        Metas.PKG[ctx] = parts[0]
-        Metas.TARGET[ctx] = parts[1]
-    }
-}
diff --git a/kotlin/builder/src/io/bazel/kotlin/builder/mode/jvm/actions/JDepsGenerator.kt b/kotlin/builder/src/io/bazel/kotlin/builder/mode/jvm/actions/JDepsGenerator.kt
new file mode 100644
index 0000000..2c0db72
--- /dev/null
+++ b/kotlin/builder/src/io/bazel/kotlin/builder/mode/jvm/actions/JDepsGenerator.kt
@@ -0,0 +1,80 @@
+/*
+ * 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.mode.jvm.actions
+
+import com.google.devtools.build.lib.view.proto.Deps
+import com.google.inject.ImplementedBy
+import com.google.inject.Inject
+import io.bazel.kotlin.builder.CompilationException
+import io.bazel.kotlin.builder.CompilationStatusException
+import io.bazel.kotlin.builder.KotlinToolchain
+import io.bazel.kotlin.builder.mode.jvm.utils.JdepsParser
+import io.bazel.kotlin.builder.utils.rootCause
+import io.bazel.kotlin.model.KotlinModel
+import java.io.ByteArrayOutputStream
+import java.io.FileOutputStream
+import java.io.PrintWriter
+import java.nio.file.Files
+import java.nio.file.Paths
+
+@ImplementedBy(DefaultJDepsGenerator::class)
+interface JDepsGenerator {
+    fun generateJDeps(command: KotlinModel.BuilderCommand)
+}
+
+private class DefaultJDepsGenerator @Inject constructor(
+    toolchain: KotlinToolchain,
+    val invoker: KotlinToolchain.JDepsInvoker
+) : JDepsGenerator {
+    private val isKotlinImplicit = JdepsParser.pathSuffixMatchingPredicate(toolchain.kotlinLibraryDirectory, *toolchain.kotlinStandardLibraries)
+    override fun generateJDeps(command: KotlinModel.BuilderCommand) {
+        val jdepsContent =
+            if (command.inputs.classpathList.isEmpty()) {
+                Deps.Dependencies.newBuilder().let {
+                    it.ruleLabel = command.info.label
+                    it.build()
+                }
+            } else {
+                ByteArrayOutputStream().use { out ->
+                    PrintWriter(out).use { writer ->
+                        val res = invoker.run(arrayOf("-cp", command.inputs.joinedClasspath, command.outputs.output), writer)
+                        out.toByteArray().inputStream().bufferedReader().readLines().let {
+                            if (res != 0) {
+                                throw CompilationStatusException("could not run jdeps tool", res, it)
+                            }
+                            try {
+                                JdepsParser.parse(
+                                    command.info.label,
+                                    command.outputs.output,
+                                    command.inputs.joinedClasspath,
+                                    it,
+                                    isKotlinImplicit
+                                )
+                            } catch (e: Exception) {
+                                throw CompilationException("error reading or parsing jdeps file", e.rootCause)
+                            }
+                        }
+                    }
+                }
+            }
+        Paths.get(command.outputs.outputJdeps).also {
+            Files.deleteIfExists(it)
+            FileOutputStream(Files.createFile(it).toFile()).use {
+                jdepsContent.writeTo(it)
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/kotlin/builder/src/io/bazel/kotlin/builder/mode/jvm/actions/JavaCompiler.kt b/kotlin/builder/src/io/bazel/kotlin/builder/mode/jvm/actions/JavaCompiler.kt
new file mode 100644
index 0000000..acab09a
--- /dev/null
+++ b/kotlin/builder/src/io/bazel/kotlin/builder/mode/jvm/actions/JavaCompiler.kt
@@ -0,0 +1,49 @@
+/*
+ * 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.mode.jvm.actions
+
+import com.google.inject.ImplementedBy
+import com.google.inject.Inject
+import io.bazel.kotlin.builder.CompilationStatusException
+import io.bazel.kotlin.builder.KotlinToolchain
+import io.bazel.kotlin.model.KotlinModel.BuilderCommand
+
+@ImplementedBy(DefaultJavaCompiler::class)
+interface JavaCompiler {
+    fun compile(command: BuilderCommand)
+}
+
+private class DefaultJavaCompiler @Inject constructor(
+    val javacInvoker: KotlinToolchain.JavacInvoker
+) : JavaCompiler {
+    override fun compile(command: BuilderCommand) {
+        val inputs = command.inputs
+        val outputs = command.outputs
+        if (inputs.javaSourcesList.isNotEmpty() || inputs.generatedJavaSourcesList.isNotEmpty()) {
+            val args = mutableListOf(
+                "-cp", "${outputs.classDirectory}/:${outputs.tempDirectory}/:${inputs.joinedClasspath}",
+                "-d", outputs.classDirectory
+            ).let {
+                // Kotlin takes care of annotation processing.
+                it.add("-proc:none")
+                it.addAll(inputs.javaSourcesList)
+                it.addAll(inputs.generatedJavaSourcesList)
+                it.toTypedArray()
+            }
+            javacInvoker.compile(args).takeIf { it != 0 }?.also { throw CompilationStatusException("javac failed",it) }
+        }
+    }
+}
\ No newline at end of file
diff --git a/kotlin/builder/src/io/bazel/kotlin/builder/mode/jvm/actions/JavaMainCompile.kt b/kotlin/builder/src/io/bazel/kotlin/builder/mode/jvm/actions/JavaMainCompile.kt
deleted file mode 100644
index 2b0f5bc..0000000
--- a/kotlin/builder/src/io/bazel/kotlin/builder/mode/jvm/actions/JavaMainCompile.kt
+++ /dev/null
@@ -1,53 +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.mode.jvm.actions
-
-import io.bazel.kotlin.builder.BuildAction
-import io.bazel.kotlin.builder.CompileResult
-import io.bazel.kotlin.builder.Context
-import io.bazel.kotlin.builder.KotlinToolchain
-import io.bazel.kotlin.builder.model.Metas
-import io.bazel.kotlin.builder.utils.annotationProcessingGeneratedJavaSources
-import io.bazel.kotlin.builder.utils.executeAndAwait
-
-/**
- * Simple java compile action that invokes javac directly and simply.
- */
-class JavaMainCompile(toolchain: KotlinToolchain) : BuildAction("compile java classes", toolchain) {
-    companion object {
-        val Result = CompileResult.Meta("javac_compile_result")
-    }
-
-    override fun invoke(ctx: Context): Int {
-        val javaSources = Metas.JAVA_SOURCES.mustGet(ctx)
-
-        val additionalJavaSources = ctx.annotationProcessingGeneratedJavaSources()?.toList() ?: emptyList()
-
-        if (javaSources.isNotEmpty() || additionalJavaSources.isNotEmpty()) {
-            val classesDirectory = ctx.flags.classDir.value.toString()
-            val incrementalData = ctx.flags.tempDirPath.value.toString()
-
-            val args = mutableListOf(toolchain.JAVAC_PATH, "-cp", "$classesDirectory/:$incrementalData/:${ctx.flags.classpath.joinToString(":")}", "-d", classesDirectory).also {
-                // Kotlin takes care of annotation processing.
-                it.add("-proc:none")
-                it.addAll(javaSources)
-                it.addAll(additionalJavaSources)
-            }
-            Result.runAndBind(ctx) { executeAndAwait(30, null, args) }
-        }
-        return 0
-    }
-}
\ No newline at end of file
diff --git a/kotlin/builder/src/io/bazel/kotlin/builder/mode/jvm/actions/KotlinCompiler.kt b/kotlin/builder/src/io/bazel/kotlin/builder/mode/jvm/actions/KotlinCompiler.kt
new file mode 100644
index 0000000..d8b9042
--- /dev/null
+++ b/kotlin/builder/src/io/bazel/kotlin/builder/mode/jvm/actions/KotlinCompiler.kt
@@ -0,0 +1,97 @@
+/*
+ * 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.mode.jvm.actions
+
+import com.google.inject.ImplementedBy
+import com.google.inject.Inject
+import io.bazel.kotlin.builder.CompilationStatusException
+import io.bazel.kotlin.builder.KotlinToolchain
+import io.bazel.kotlin.builder.utils.addAll
+import io.bazel.kotlin.model.KotlinModel
+import java.io.ByteArrayInputStream
+import java.io.ByteArrayOutputStream
+import java.io.PrintStream
+
+@ImplementedBy(DefaultKotlinCompiler::class)
+interface KotlinCompiler {
+    fun runAnnotationProcessor(command: KotlinModel.BuilderCommand): List<String>
+    fun compile(command: KotlinModel.BuilderCommand): List<String>
+}
+
+// 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..
+//
+// 1 is a standard compilation error
+// 2 is an internal error
+// 3 is the script execution error
+private class DefaultKotlinCompiler @Inject constructor(
+    val compiler: KotlinToolchain.KotlincInvoker
+) : KotlinCompiler {
+    override fun runAnnotationProcessor(command: KotlinModel.BuilderCommand): List<String> {
+        check(command.info.plugins.annotationProcessorsList.isNotEmpty()) {
+            "method called without annotation processors"
+        }
+        return setupCompileContext(command).also {
+            it.addAll(command.info.encodedPluginDescriptorsList)
+            it.addAll(command.inputs.kotlinSourcesList)
+            it.addAll(command.inputs.javaSourcesList)
+        }.let { invokeCompilePhase(it) }
+    }
+
+    /**
+     * 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(command: KotlinModel.BuilderCommand): MutableList<String> {
+        val args = mutableListOf<String>()
+
+        args.addAll(
+            "-cp", command.inputs.joinedClasspath,
+            "-api-version", command.info.toolchainInfo.common.apiVersion,
+            "-language-version", command.info.toolchainInfo.common.languageVersion,
+            "-jvm-target", command.info.toolchainInfo.jvm.jvmTarget
+        )
+
+        args
+            .addAll("-module-name", command.info.kotlinModuleName)
+            .addAll("-d", command.outputs.classDirectory)
+
+        command.info.passthroughFlags?.takeIf { it.isNotBlank() }?.also { args.addAll(it.split(" ")) }
+        return args
+    }
+
+    override fun compile(command: KotlinModel.BuilderCommand): List<String> =
+        with(setupCompileContext(command)) {
+            addAll(command.inputs.javaSourcesList)
+            addAll(command.inputs.generatedJavaSourcesList)
+            addAll(command.inputs.kotlinSourcesList)
+            addAll(command.inputs.generatedKotlinSourcesList)
+            invokeCompilePhase(this)
+        }
+
+    private fun invokeCompilePhase(args: List<String>): List<String> {
+        val outputStream = ByteArrayOutputStream()
+        val ps = PrintStream(outputStream)
+        val result = compiler.compile(args.toTypedArray(), ps)
+        val output = ByteArrayInputStream(outputStream.toByteArray()).bufferedReader().readLines()
+        if (result != 0) {
+            throw CompilationStatusException("compile phase failed", result, output)
+        } else {
+            return output
+        }
+    }
+}
\ No newline at end of file
diff --git a/kotlin/builder/src/io/bazel/kotlin/builder/mode/jvm/actions/KotlinMainCompile.kt b/kotlin/builder/src/io/bazel/kotlin/builder/mode/jvm/actions/KotlinMainCompile.kt
deleted file mode 100644
index 0388516..0000000
--- a/kotlin/builder/src/io/bazel/kotlin/builder/mode/jvm/actions/KotlinMainCompile.kt
+++ /dev/null
@@ -1,117 +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.mode.jvm.actions
-
-
-import io.bazel.kotlin.builder.BuildAction
-import io.bazel.kotlin.builder.CompileResult
-import io.bazel.kotlin.builder.Context
-import io.bazel.kotlin.builder.KotlinToolchain
-import io.bazel.kotlin.builder.mode.jvm.utils.KotlinCompilerOutputProcessor
-import io.bazel.kotlin.builder.model.CompilePluginConfig
-import io.bazel.kotlin.builder.model.Metas
-import io.bazel.kotlin.builder.utils.addAll
-import io.bazel.kotlin.builder.utils.annotationProcessingGeneratedJavaSources
-import io.bazel.kotlin.builder.utils.moduleName
-
-// 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 {
-        val Result = CompileResult.Meta("kotlin_compile_result")
-    }
-
-    /**
-     * 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): MutableList<String> {
-        val args = mutableListOf<String>()
-
-        args.addAll(
-                "-cp", Metas.CLASSPATH_STRING[ctx],
-                "-api-version", ctx.flags.kotlinApiVersion,
-                "-language-version", ctx.flags.kotlinLanguageVersion,
-                "-jvm-target", ctx.flags.kotlinJvmTarget
-        )
-
-        args
-                .addAll("-module-name", ctx.moduleName)
-                .addAll("-d", ctx.flags.classDir.value.toString())
-
-        ctx.flags.kotlinPassthroughFlags?.takeIf { it.isNotBlank() }?.also { args.addAll(it.split(" ")) }
-
-        return args
-    }
-
-    override fun invoke(ctx: Context): Int {
-        val commonArgs = setupCompileContext(ctx)
-        val sources = Metas.ALL_SOURCES.mustGet(ctx)
-        val pluginStatus = CompilePluginConfig[ctx]
-
-        // run a kapt generation phase if needed.
-        if (pluginStatus.hasAnnotationProcessors) {
-            invokeCompilePhase(
-                    args = mutableListOf(*commonArgs.toTypedArray()).let {
-                        it.addAll(pluginStatus.args)
-                        it.addAll(sources)
-                        it.toTypedArray()
-                    },
-                    onNonTeminalExitCode = { outputProcessors, exitCode ->
-                        outputProcessors.process()
-                        exitCode
-                    }
-            ).takeIf { it != 0 }?.also { return it }
-        }
-        return invokeCompilePhase(
-                args = commonArgs.let { args ->
-                    args.addAll(sources)
-                    ctx.annotationProcessingGeneratedJavaSources()?.also { args.addAll(it) }
-                    args.toTypedArray()
-                },
-                onNonTeminalExitCode = { outputProcessor, exitCode ->
-                    // give javac a chance to process the java sources.
-                    Result[ctx] = CompileResult.deferred(exitCode) { _ ->
-                        outputProcessor.process()
-                        exitCode
-                    }
-                    0
-                }
-        )
-    }
-
-    private fun invokeCompilePhase(args: Array<String>, onNonTeminalExitCode: (KotlinCompilerOutputProcessor, Int) -> Int): Int {
-        val outputProcessor = KotlinCompilerOutputProcessor.ForKotlinC(System.out)
-
-        val exitCode = try {
-            toolchain.kotlinCompiler.compile(args, 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
-            return onNonTeminalExitCode(outputProcessor, exitCode)
-        } else {
-            outputProcessor.process()
-            throw RuntimeException("KotlinMainCompile returned terminal error code: $exitCode")
-        }
-    }
-}
diff --git a/kotlin/builder/src/io/bazel/kotlin/builder/mode/jvm/actions/OutputJarCreator.kt b/kotlin/builder/src/io/bazel/kotlin/builder/mode/jvm/actions/OutputJarCreator.kt
new file mode 100644
index 0000000..19aab9f
--- /dev/null
+++ b/kotlin/builder/src/io/bazel/kotlin/builder/mode/jvm/actions/OutputJarCreator.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.kotlin.builder.mode.jvm.actions
+
+import com.google.inject.ImplementedBy
+import com.google.inject.Inject
+import io.bazel.kotlin.builder.KotlinToolchain
+import io.bazel.kotlin.model.KotlinModel
+import java.nio.file.Path
+import java.nio.file.Paths
+
+@ImplementedBy(DefaultOutputJarCreator::class)
+interface OutputJarCreator {
+    fun createOutputJar(command: KotlinModel.BuilderCommand)
+}
+
+private class DefaultOutputJarCreator @Inject constructor(
+    val toolInvoker: KotlinToolchain.JarToolInvoker
+) : OutputJarCreator {
+    private fun MutableList<String>.addAllFrom(dir: Path) = addAll(arrayOf("-C", dir.toString(), "."))
+    override fun createOutputJar(command: KotlinModel.BuilderCommand) {
+        mutableListOf(
+            "cf", command.outputs.output
+        ).also { args ->
+            args.addAllFrom(Paths.get(command.outputs.classDirectory))
+        }.let { toolInvoker.invoke(it) }
+    }
+}
diff --git a/kotlin/builder/src/io/bazel/kotlin/builder/mode/jvm/actions/ProcessCompileResult.kt b/kotlin/builder/src/io/bazel/kotlin/builder/mode/jvm/actions/ProcessCompileResult.kt
deleted file mode 100644
index ae183ac..0000000
--- a/kotlin/builder/src/io/bazel/kotlin/builder/mode/jvm/actions/ProcessCompileResult.kt
+++ /dev/null
@@ -1,51 +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.mode.jvm.actions
-
-
-import io.bazel.kotlin.builder.BuildAction
-import io.bazel.kotlin.builder.Context
-import io.bazel.kotlin.builder.KotlinToolchain
-
-
-/**
- * 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 = KotlinMainCompile.Result.mustGet(ctx)
-        val javacResult = JavaMainCompile.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/builder/src/io/bazel/kotlin/builder/mode/jvm/actions/UnpackSourceJars.kt b/kotlin/builder/src/io/bazel/kotlin/builder/mode/jvm/actions/UnpackSourceJars.kt
deleted file mode 100644
index a63f33d..0000000
--- a/kotlin/builder/src/io/bazel/kotlin/builder/mode/jvm/actions/UnpackSourceJars.kt
+++ /dev/null
@@ -1,64 +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.mode.jvm.actions
-
-import io.bazel.kotlin.builder.BuildAction
-import io.bazel.kotlin.builder.Context
-import io.bazel.kotlin.builder.KotlinToolchain
-import io.bazel.kotlin.builder.model.Metas
-import io.bazel.kotlin.builder.utils.executeAndAwaitSuccess
-import java.nio.file.Paths
-
-/**
- * Unpack files with the srcjar extension into a temp directory.
- */
-class UnpackSourceJars(toolchain: KotlinToolchain) : BuildAction("unpack srcjars", toolchain) {
-    override fun invoke(ctx: Context): Int {
-        if (ctx.flags.sourceJars != null) {
-            check(ctx.flags.sourceJars.isNotEmpty())
-
-            val unpackDir = ctx.flags.tempDirPath.value.resolve("_srcjars").toFile()
-                    .also {
-                        try {
-                            it.mkdirs()
-                        } catch (ex: Exception) {
-                            throw RuntimeException("could not create unpack directory at $it", ex)
-                        }
-                    }
-            ctx.flags.sourceJars.map { Paths.get(it) }.forEach { srcjar ->
-                try {
-                    mutableListOf(
-                            Paths.get(toolchain.JAR_TOOL_PATH).toAbsolutePath().toString(),
-                            "xf", srcjar.toAbsolutePath().toString()
-                    ).also {
-                        executeAndAwaitSuccess(10, unpackDir, it)
-                    }
-                } catch (e: Exception) {
-                    throw RuntimeException("unable to unpack source jar: $srcjar", e)
-                }
-            }
-            unpackDir.walk()
-                    .filter { it.name.endsWith(".kt") || it.name.endsWith(".java") }
-                    .map { it.toString() }
-                    .toList()
-                    // bind the sources even if the list is empty. throw an appropriate error if needed in Initialize.
-                    .also { Metas.UNPACKED_SOURCES[ctx] = it }
-            return 0
-        } else {
-            return 0
-        }
-    }
-}
diff --git a/kotlin/builder/src/io/bazel/kotlin/builder/mode/jvm/utils/JdepsParser.kt b/kotlin/builder/src/io/bazel/kotlin/builder/mode/jvm/utils/JdepsParser.kt
index 8854fc3..f3419dc 100644
--- a/kotlin/builder/src/io/bazel/kotlin/builder/mode/jvm/utils/JdepsParser.kt
+++ b/kotlin/builder/src/io/bazel/kotlin/builder/mode/jvm/utils/JdepsParser.kt
@@ -69,7 +69,7 @@
                 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)
+                        throw RuntimeException("should only get dependencies for dep: $filename")
                     }
                     consumeJarLine(parts[1], Deps.Dependency.Kind.EXPLICIT)
                 }
@@ -89,7 +89,7 @@
                 trimmedLine.startsWith("-> ") -> {
                     // ignore package detail lines, in the jdk8 format these start with arrows.
                 }
-                else -> throw RuntimeException("unexpected line while collecting packages: " + line)
+                else -> throw RuntimeException("unexpected line while collecting packages: $line")
             }
             Mode.COLLECT_PACKAGES_JDK9 -> {
                 val pkg = trimmedLine.split("\\s+".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()
@@ -111,10 +111,17 @@
             }
         }
 
-        fun parse(label: String, classJar: String, classPath: String, jdepLines: Stream<String>, isImplicit: Predicate<String>): Deps.Dependencies {
+        fun parse(
+            label: String,
+            classJar: String,
+            classPath: String,
+            jdepLines: List<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) }
+            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()
diff --git a/kotlin/builder/src/io/bazel/kotlin/builder/mode/jvm/utils/KotlinCompilerOutputProcessor.kt b/kotlin/builder/src/io/bazel/kotlin/builder/mode/jvm/utils/KotlinCompilerOutputProcessor.kt
deleted file mode 100644
index 6f0cc25..0000000
--- a/kotlin/builder/src/io/bazel/kotlin/builder/mode/jvm/utils/KotlinCompilerOutputProcessor.kt
+++ /dev/null
@@ -1,66 +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.mode.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/builder/src/io/bazel/kotlin/builder/mode/jvm/utils/KotlinCompilerOutputSink.kt b/kotlin/builder/src/io/bazel/kotlin/builder/mode/jvm/utils/KotlinCompilerOutputSink.kt
new file mode 100644
index 0000000..0958724
--- /dev/null
+++ b/kotlin/builder/src/io/bazel/kotlin/builder/mode/jvm/utils/KotlinCompilerOutputSink.kt
@@ -0,0 +1,44 @@
+/*
+ * 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.mode.jvm.utils
+
+import com.google.inject.ImplementedBy
+import com.google.inject.Inject
+import java.io.File
+import java.io.PrintStream
+import java.nio.file.Paths
+
+@ImplementedBy(DefaultOutputProcessorFactory::class)
+interface KotlinCompilerOutputSink {
+    fun deliver(line: String)
+}
+
+private class DefaultOutputProcessorFactory @Inject constructor(
+    val stream: PrintStream
+) : KotlinCompilerOutputSink {
+    private val executionRoot: String = Paths.get("").toAbsolutePath().toString() + File.separator
+
+    override fun deliver(line: String) {
+        stream.println(trimExecutionRootPrefix(line))
+    }
+
+    private fun trimExecutionRootPrefix(toPrint: String): String {
+        // trim off the workspace component
+        return if (toPrint.startsWith(executionRoot)) {
+            toPrint.replaceFirst(executionRoot.toRegex(), "")
+        } else toPrint
+    }
+}
diff --git a/kotlin/builder/src/io/bazel/kotlin/builder/model/CompileState.kt b/kotlin/builder/src/io/bazel/kotlin/builder/model/CompileState.kt
deleted file mode 100644
index 0b29abe..0000000
--- a/kotlin/builder/src/io/bazel/kotlin/builder/model/CompileState.kt
+++ /dev/null
@@ -1,27 +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.model
-
-import io.bazel.kotlin.builder.MandatoryMeta
-
-sealed class CompileState
-
-class CompilePluginConfig(
-        val hasAnnotationProcessors: Boolean = false,
-        val args: Array<String> = emptyArray()
-): CompileState() {
-    companion object: MandatoryMeta<CompilePluginConfig>("plugin_config", defaultValue = CompilePluginConfig())
-}
\ No newline at end of file
diff --git a/kotlin/builder/src/io/bazel/kotlin/builder/model/Flags.kt b/kotlin/builder/src/io/bazel/kotlin/builder/model/Flags.kt
deleted file mode 100644
index 0af3e68..0000000
--- a/kotlin/builder/src/io/bazel/kotlin/builder/model/Flags.kt
+++ /dev/null
@@ -1,69 +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.model
-
-import com.google.protobuf.util.JsonFormat
-import io.bazel.kotlin.builder.*
-import io.bazel.kotlin.model.KotlinModel
-
-/**
- * The flags supported by the worker.
- */
-class Flags(argMap: ArgMap) {
-    val label = argMap.mandatorySingle(JavaBuilderFlags.TARGET_LABEL.flag)
-    val ruleKind = argMap.mandatorySingle(JavaBuilderFlags.RULE_KIND.flag)
-
-    val classDir = argMap.lazilyCreatedDirectory(JavaBuilderFlags.CLASSDIR.flag)
-    val tempDirPath = argMap.lazilyCreatedDirectory(JavaBuilderFlags.TEMPDIR.flag)
-    val sourceGenDir = argMap.lazilyCreatedDirectory(JavaBuilderFlags.SOURCEGEN_DIR.flag)
-
-    // val strictJavaDeps = argMap.mandatorySingle(JavaBuilderFlags.STRICT_JAVA_DEPS.flag)
-    val outputClassJar = argMap.mandatorySingle(JavaBuilderFlags.OUTPUT.flag)
-
-    val source = argMap.optional(JavaBuilderFlags.SOURCES.flag)
-    val sourceJars = argMap.optional(JavaBuilderFlags.SOURCE_JARS.flag)
-
-    val classpath = argMap.mandatory(JavaBuilderFlags.CLASSPATH.flag)
-    val plugins: KotlinModel.CompilerPlugins? = argMap.optionalSingle("--kt-plugins")?.let { input ->
-        KotlinModel.CompilerPlugins.newBuilder().let {
-            jsonFormat.merge(input, it)
-            it.build()
-        }
-    }
-
-    val outputJdeps = argMap.mandatorySingle("--output_jdeps")
-
-    val kotlinApiVersion = argMap.mandatorySingle("--kotlin_api_version")
-    val kotlinLanguageVersion = argMap.mandatorySingle("--kotlin_language_version")
-    val kotlinJvmTarget = argMap.mandatorySingle("--kotlin_jvm_target")
-
-    /**
-     * These flags are passed through to the compiler verbatim, the rules ensure they are safe. These flags are to toggle features or they carry a single value
-     * so the string is tokenized by space.
-     */
-    val kotlinPassthroughFlags = argMap.optionalSingle("--kotlin_passthrough_flags")
-
-    val kotlinModuleName = argMap.optionalSingle("--kotlin_module_name")
-
-    companion object {
-        @JvmStatic
-        private val jsonTypeRegistry = JsonFormat.TypeRegistry.newBuilder()
-            .add(KotlinModel.getDescriptor().messageTypes).build()
-
-        @JvmStatic
-        private val jsonFormat = JsonFormat.parser().usingTypeRegistry(jsonTypeRegistry)
-    }
-}
\ No newline at end of file
diff --git a/kotlin/builder/src/io/bazel/kotlin/builder/model/JavaBuilderFlags.kt b/kotlin/builder/src/io/bazel/kotlin/builder/model/JavaBuilderFlags.kt
deleted file mode 100644
index c44a85e..0000000
--- a/kotlin/builder/src/io/bazel/kotlin/builder/model/JavaBuilderFlags.kt
+++ /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.kotlin.builder.model
-
-/**
- * Declares the flags used by the java builder.
- */
-internal enum class JavaBuilderFlags(val flag: String) {
-    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")
-}
diff --git a/kotlin/builder/src/io/bazel/kotlin/builder/model/Metas.kt b/kotlin/builder/src/io/bazel/kotlin/builder/model/Metas.kt
deleted file mode 100644
index 32e845e..0000000
--- a/kotlin/builder/src/io/bazel/kotlin/builder/model/Metas.kt
+++ /dev/null
@@ -1,40 +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.model
-
-import io.bazel.kotlin.builder.MandatoryMeta
-import io.bazel.kotlin.builder.Meta
-
-/**
- * Listin of Meta keys that don't make sense as companion objects.
- */
-object Metas {
-    // the package part of the label.
-    val PKG = MandatoryMeta<String>("package")
-    // The target part of the label.
-    val TARGET = MandatoryMeta<String>("target")
-
-    // If this is non empty then it is a mixed mode operation.
-    val JAVA_SOURCES = MandatoryMeta<List<String>>("java_sources")
-
-    // .kt and .java source files unpacked from .srcjar files
-    val UNPACKED_SOURCES = Meta<List<String>>("unpacked_sources")
-
-    val ALL_SOURCES = MandatoryMeta<List<String>>("all_sources")
-
-    // memoized classpath string
-    val CLASSPATH_STRING = MandatoryMeta<String>("joined_classpath")
-}
\ No newline at end of file
diff --git a/kotlin/builder/src/io/bazel/kotlin/builder/Args.kt b/kotlin/builder/src/io/bazel/kotlin/builder/utils/ArgMap.kt
similarity index 60%
rename from kotlin/builder/src/io/bazel/kotlin/builder/Args.kt
rename to kotlin/builder/src/io/bazel/kotlin/builder/utils/ArgMap.kt
index 909a51b..43839bd 100644
--- a/kotlin/builder/src/io/bazel/kotlin/builder/Args.kt
+++ b/kotlin/builder/src/io/bazel/kotlin/builder/utils/ArgMap.kt
@@ -14,27 +14,20 @@
  * limitations under the License.
  */
 
-package io.bazel.kotlin.builder
+package io.bazel.kotlin.builder.utils
 
-import java.nio.file.Files
-import java.nio.file.Paths
+import java.io.File
 
-typealias ArgMap = Map<String, List<String>>
-
-/**
- * Get the mandatory single value from a key
- */
-fun ArgMap.mandatorySingle(key: String): String =
+class ArgMap(private val map: Map<String, List<String>>) {
+    /**
+     * Get the mandatory single value from a key
+     */
+    fun mandatorySingle(key: String): String =
         optionalSingle(key) ?: throw IllegalArgumentException("$key is not optional")
 
-/**
- * A flag bound to a directory that is lazily created.
- */
-fun ArgMap.lazilyCreatedDirectory(key: String) = lazy {
-    mandatorySingle(key).let { Files.createDirectories(Paths.get(it)) }
-}
+    fun labelDepMap(key: String) = optional(key)?.asSequence()?.windowed(2, 2)?.map { it[0] to it[1] }?.toMap() ?: emptyMap()
 
-fun ArgMap.optionalSingle(key: String): String? =
+    fun optionalSingle(key: String): String? =
         optional(key)?.let {
             when (it.size) {
                 0 -> throw IllegalArgumentException("$key did not have a value")
@@ -43,19 +36,22 @@
             }
         }
 
-fun ArgMap.mandatory(key: String): Array<String> = optional(key) ?: throw IllegalArgumentException("$key is not optional")
-fun ArgMap.optional(key: String): Array<String>? = this[key]?.toTypedArray()
+    fun mandatory(key: String): List<String> = optional(key) ?: throw IllegalArgumentException("$key is not optional")
+    fun optional(key: String): List<String>? = map[key]
+}
 
 
-/**
- * Test if a flag is set
- */
-fun ArgMap.flag(key: String): Boolean = this[key]?.let { true } ?: false
-
 object ArgMaps {
-    fun from(args: List<String>): ArgMap = mutableMapOf<String, MutableList<String>>().also { argsToMap(args, it) }
+    @JvmStatic
+    fun from(args: List<String>): ArgMap =
+        mutableMapOf<String, MutableList<String>>()
+            .also { argsToMap(args, it) }
+            .let { ArgMap(it) }
 
-    fun argsToMap(args: List<String>, argMap: MutableMap<String, MutableList<String>>,  isFlag: (String) -> Boolean = { it.startsWith("--") }) {
+    @JvmStatic
+    fun from(file: File): ArgMap = from(file.reader().readLines())
+
+    private fun argsToMap(args: List<String>, argMap: MutableMap<String, MutableList<String>>, isFlag: (String) -> Boolean = { it.startsWith("--") }) {
         var currentKey: String = args.first().also { require(isFlag(it)) { "first arg must be a flag" } }
         val currentValue = mutableListOf<String>()
         val mergeCurrent = {
diff --git a/kotlin/builder/src/io/bazel/kotlin/builder/utils/IOUtils.kt b/kotlin/builder/src/io/bazel/kotlin/builder/utils/IOUtils.kt
index 4da723c..1ef89b4 100644
--- a/kotlin/builder/src/io/bazel/kotlin/builder/utils/IOUtils.kt
+++ b/kotlin/builder/src/io/bazel/kotlin/builder/utils/IOUtils.kt
@@ -91,17 +91,6 @@
     }
 }
 
-fun executeAndAwaitSuccess(timeoutSeconds: Int, vararg command: String) {
-    executeAndAwaitSuccess(timeoutSeconds, null, command = command.toList())
-}
-
-fun executeAndAwaitSuccess(timeoutSeconds: Int, directory: File?, command: List<String>) {
-    val status = executeAndAwait(timeoutSeconds, directory, command)
-    check(status == 0) {
-        "process failed with status: $status"
-    }
-}
-
 private fun BufferedReader.drainTo(pw: PrintStream) {
     lines().forEach(pw::println); close()
 }
diff --git a/kotlin/builder/src/io/bazel/kotlin/builder/utils/KotlinCompilerPluginArgsEncoder.kt b/kotlin/builder/src/io/bazel/kotlin/builder/utils/KotlinCompilerPluginArgsEncoder.kt
new file mode 100644
index 0000000..3d7c68b
--- /dev/null
+++ b/kotlin/builder/src/io/bazel/kotlin/builder/utils/KotlinCompilerPluginArgsEncoder.kt
@@ -0,0 +1,104 @@
+/*
+ * 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.common.collect.ImmutableList
+import com.google.inject.ImplementedBy
+import com.google.inject.Inject
+import io.bazel.kotlin.builder.KotlinToolchain.CompilerPlugin
+import io.bazel.kotlin.model.KotlinModel
+import java.io.ByteArrayOutputStream
+import java.io.ObjectOutputStream
+import java.util.*
+
+@ImplementedBy(DefaultKotlinCompilerPluginArgsEncoder::class)
+interface KotlinCompilerPluginArgsEncoder {
+    fun encode(
+        outputs: KotlinModel.BuilderCommand.Outputs,
+        plugins: KotlinModel.CompilerPlugins
+    ): List<String>
+}
+
+class DefaultKotlinCompilerPluginArgsEncoder @Inject internal constructor(
+    @CompilerPlugin.Kapt3
+    private val kapt3: CompilerPlugin
+) : KotlinCompilerPluginArgsEncoder {
+
+    /**
+     * Plugin using the undocumented encoding format for kapt3
+     */
+    inner class PluginArgs internal constructor() {
+        private val tally = mutableMapOf<String, MutableList<String>>()
+
+        operator fun set(key: String, value: String) {
+            check(tally[key] == null) { "value allready set" }
+            tally[key] = mutableListOf(value)
+        }
+
+        fun bindMulti(key: String, value: String) {
+            tally[key].also { if (it != null) it.add(value) else this[key] = value }
+        }
+
+        // "configuration" is an undocumented kapt3 argument. preparing the arguments this way is the only way to get more than one annotation processor class
+        // passed to kotlinc.
+        fun encode(): ImmutableList<String> =
+            ImmutableList.of(
+                "-Xplugin=${kapt3.jarPath}",
+                "-P", "plugin:${kapt3.id}:configuration=${encodePluginOptions(tally)}"
+            )
+
+        private fun encodePluginOptions(options: Map<String, List<String>>): String {
+            val os = ByteArrayOutputStream()
+            val oos = ObjectOutputStream(os)
+
+            oos.writeInt(options.size)
+            for ((key, values) in options.entries) {
+                oos.writeUTF(key)
+
+                oos.writeInt(values.size)
+                for (value in values) {
+                    oos.writeUTF(value)
+                }
+            }
+
+            oos.flush()
+            return Base64.getEncoder().encodeToString(os.toByteArray())
+        }
+    }
+
+    override fun encode(
+        outputs: KotlinModel.BuilderCommand.Outputs,
+        plugins: KotlinModel.CompilerPlugins
+    ): List<String> =
+        plugins.takeIf { it.annotationProcessorsList.isNotEmpty() }?.let { plugin ->
+            PluginArgs().let { arg ->
+                arg["sources"] = outputs.sourceGenDir.toString()
+                arg["classes"] = outputs.classDirectory.toString()
+                arg["stubs"] = outputs.tempDirectory.toString()
+                arg["incrementalData"] = outputs.tempDirectory.toString()
+
+                arg["aptMode"] = "stubsAndApt"
+                arg["correctErrorTypes"] = "true"
+//                  arg["verbose"] = "true"
+
+                arg["processors"] = plugin.annotationProcessorsList
+                    .filter { it.processorClass.isNotEmpty() }
+                    .onEach { it.classpathList.forEach { arg.bindMulti("apclasspath", it) } }
+                    .joinToString(",") { it.processorClass }
+                arg.encode()
+            }
+        }?.let { ImmutableList.copyOf(it) } ?: ImmutableList.of()
+}
\ No newline at end of file
diff --git a/kotlin/builder/src/io/bazel/kotlin/builder/utils/MiscUtils.kt b/kotlin/builder/src/io/bazel/kotlin/builder/utils/MiscUtils.kt
index 1aab958..bbbf19f 100644
--- a/kotlin/builder/src/io/bazel/kotlin/builder/utils/MiscUtils.kt
+++ b/kotlin/builder/src/io/bazel/kotlin/builder/utils/MiscUtils.kt
@@ -16,12 +16,5 @@
 
 package io.bazel.kotlin.builder.utils
 
-import io.bazel.kotlin.builder.Context
-import io.bazel.kotlin.builder.model.Metas
-
 fun <T, C : MutableCollection<T>> C.addAll(vararg entries: T): C = this.also { addAll(entries) }
 
-fun String?.supplyIfNullOrBlank(s: () -> String): String = this?.takeIf { it.isNotBlank() } ?: s()
-
-val Context.moduleName: String
-    get() = flags.kotlinModuleName.supplyIfNullOrBlank { "${Metas.PKG[this].trimStart { it == '/' }.replace('/', '_')}-${Metas.TARGET[this]}" }
diff --git a/kotlin/builder/src/io/bazel/kotlin/builder/utils/PluginUtils.kt b/kotlin/builder/src/io/bazel/kotlin/builder/utils/PluginUtils.kt
deleted file mode 100644
index 616e299..0000000
--- a/kotlin/builder/src/io/bazel/kotlin/builder/utils/PluginUtils.kt
+++ /dev/null
@@ -1,89 +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.utils
-
-import io.bazel.kotlin.builder.Context
-import io.bazel.kotlin.builder.KotlinToolchain.CompilerPlugin
-import java.io.ByteArrayOutputStream
-import java.io.ObjectOutputStream
-import java.util.*
-
-/**
- * Plugin using the undocumented encoding format for kapt3
- */
-class PluginArgs private constructor(private val kapt: CompilerPlugin) {
-    private val tally = mutableMapOf<String, MutableList<String>>()
-
-    operator fun set(key: String, value: String) {
-        check(tally[key] == null) { "value allready set" }
-        tally[key] = mutableListOf(value)
-    }
-
-    fun bindMulti(key: String, value: String) {
-        tally[key].also { if (it != null) it.add(value) else this[key] = value }
-    }
-
-    // "configuration" is an undocumented kapt3 argument. preparing the arguments this way is the only way to get more than one annotation processor class
-    // passed to kotlinc.
-    fun encode(): List<String> = listOf("-Xplugin=${kapt.jarPath}", "-P", "plugin:${kapt.id}:configuration=${encodePluginOptions(tally)}")
-
-    private fun encodePluginOptions(options: Map<String, List<String>>): String {
-        val os = ByteArrayOutputStream()
-        val oos = ObjectOutputStream(os)
-
-        oos.writeInt(options.size)
-        for ((key, values) in options.entries) {
-            oos.writeUTF(key)
-
-            oos.writeInt(values.size)
-            for (value in values) {
-                oos.writeUTF(value)
-            }
-        }
-
-        oos.flush()
-        return Base64.getEncoder().encodeToString(os.toByteArray())
-    }
-
-    companion object {
-        fun from(ctx: Context): List<String>? =
-            ctx.flags.plugins?.takeIf { it.annotationProcessorsList.isNotEmpty() }?.let { plugin ->
-                PluginArgs(ctx.toolchain.KAPT_PLUGIN).let { arg ->
-                    arg["sources"] = ctx.flags.sourceGenDir.value.toString()
-                    arg["classes"] = ctx.flags.classDir.value.toString()
-                    arg["stubs"] = ctx.flags.tempDirPath.value.toString()
-                    arg["incrementalData"] = ctx.flags.tempDirPath.value.toString()
-
-                    arg["aptMode"] = "stubsAndApt"
-                    arg["correctErrorTypes"] = "true"
-//                  arg["verbose"] = "true"
-
-                    arg["processors"] = plugin.annotationProcessorsList
-                        .filter { it.processorClass.isNotEmpty() }
-                        .onEach { it.classpathList.forEach { arg.bindMulti("apclasspath", it) } }
-                        .joinToString(",") { it.processorClass }
-                    arg.encode()
-                }
-            }
-    }
-}
-
-fun Context.annotationProcessingGeneratedSources(): Sequence<String>? =
-    if (flags.sourceGenDir.isInitialized()) {
-        flags.sourceGenDir.value.toFile().walkTopDown().filter { it.isFile }.map { it.path }
-    } else null
-
-fun Context.annotationProcessingGeneratedJavaSources(): Sequence<String>? = annotationProcessingGeneratedSources()?.filter { it.endsWith(".java") }
\ No newline at end of file
diff --git a/kotlin/builder/unittests/io/bazel/kotlin/workers/mode/jvm/utils/JdepsParserTest.java b/kotlin/builder/unittests/io/bazel/kotlin/workers/mode/jvm/utils/JdepsParserTest.java
index 6f43e6e..7013b35 100644
--- a/kotlin/builder/unittests/io/bazel/kotlin/workers/mode/jvm/utils/JdepsParserTest.java
+++ b/kotlin/builder/unittests/io/bazel/kotlin/workers/mode/jvm/utils/JdepsParserTest.java
@@ -27,101 +27,108 @@
 import java.util.List;
 import java.util.function.Predicate;
 import java.util.stream.Collectors;
-import java.util.stream.Stream;
 
 @RunWith(JUnit4.class)
 public class JdepsParserTest {
-    private static final String JDK8_FIXTURE =
-            "alt.jar -> bazel-server-cloud/external/com_github_jetbrains_kotlin/lib/kotlin-stdlib-jdk7.jar\n" +
-                    "alt.jar -> bazel-server-cloud/external/com_github_jetbrains_kotlin/lib/kotlin-stdlib.jar\n" +
-                    "alt.jar -> bazel-bin/cloud/qa/integrationtests/pkg/extensions/postgres/postgres.jar\n" +
-                    "alt.jar -> bazel-server-cloud/bazel-out/darwin-fastbuild/bin/cloud/qa/integrationtests/pkg/alt/alt.runfiles/__main__/external/org_postgresql_postgresql/jar/postgresql-42.1.1.jar\n" +
-                    "alt.jar -> /Library/Java/JavaVirtualMachines/jdk1.8.0_144.jdk/Contents/Home/jre/lib/rt.jar\n" +
-                    "alt.jar -> bazel-bin/cloud/qa/integrationtests/pkg/utils/utils.jar\n" +
-                    "   com.axsy.testing.alt (alt.jar)\n" +
-                    "      -> com.axsy.testing.extensions.postgres               postgres.jar\n" +
-                    "      -> com.axsy.testing.pkg.utils                         utils.jar\n" +
-                    "      -> java.io                                            \n" +
-                    "      -> java.lang                                          \n" +
-                    "      -> java.sql                                           \n" +
-                    "      -> javax.sql                                          \n" +
-                    "      -> kotlin                                             kotlin-stdlib.jar\n" +
-                    "      -> kotlin.jdk7                                        kotlin-stdlib-jdk7.jar\n" +
-                    "      -> kotlin.jvm.internal                                kotlin-stdlib.jar\n" +
-                    "      -> org.postgresql.ds                                  postgresql-42.1.1.jar\n" +
-                    "   com.axsy.testing.alt.sub (alt.jar)\n" +
-                    "      -> java.lang                                          \n" +
-                    "      -> kotlin                                             kotlin-stdlib.jar\n";
+  private static final String JDK8_FIXTURE =
+      "alt.jar -> bazel-server-cloud/external/com_github_jetbrains_kotlin/lib/kotlin-stdlib-jdk7.jar\n"
+          + "alt.jar -> bazel-server-cloud/external/com_github_jetbrains_kotlin/lib/kotlin-stdlib.jar\n"
+          + "alt.jar -> bazel-bin/cloud/qa/integrationtests/pkg/extensions/postgres/postgres.jar\n"
+          + "alt.jar -> bazel-server-cloud/bazel-out/darwin-fastbuild/bin/cloud/qa/integrationtests/pkg/alt/alt.runfiles/__main__/external/org_postgresql_postgresql/jar/postgresql-42.1.1.jar\n"
+          + "alt.jar -> /Library/Java/JavaVirtualMachines/jdk1.8.0_144.jdk/Contents/Home/jre/lib/rt.jar\n"
+          + "alt.jar -> bazel-bin/cloud/qa/integrationtests/pkg/utils/utils.jar\n"
+          + "   com.axsy.testing.alt (alt.jar)\n"
+          + "      -> com.axsy.testing.extensions.postgres               postgres.jar\n"
+          + "      -> com.axsy.testing.pkg.utils                         utils.jar\n"
+          + "      -> java.io                                            \n"
+          + "      -> java.lang                                          \n"
+          + "      -> java.sql                                           \n"
+          + "      -> javax.sql                                          \n"
+          + "      -> kotlin                                             kotlin-stdlib.jar\n"
+          + "      -> kotlin.jdk7                                        kotlin-stdlib-jdk7.jar\n"
+          + "      -> kotlin.jvm.internal                                kotlin-stdlib.jar\n"
+          + "      -> org.postgresql.ds                                  postgresql-42.1.1.jar\n"
+          + "   com.axsy.testing.alt.sub (alt.jar)\n"
+          + "      -> java.lang                                          \n"
+          + "      -> kotlin                                             kotlin-stdlib.jar\n";
 
-    private static final String JDK9_FIXTURE =
-            "alt.jar -> java.base\n" +
-                    "alt.jar -> java.sql\n" +
-                    "alt.jar -> bazel-server-cloud/external/com_github_jetbrains_kotlin/lib/kotlin-stdlib-jdk7.jar\n" +
-                    "alt.jar -> bazel-server-cloud/external/com_github_jetbrains_kotlin/lib/kotlin-stdlib.jar\n" +
-                    "alt.jar -> bazel-bin/cloud/qa/integrationtests/pkg/extensions/postgres/postgres.jar\n" +
-                    "alt.jar -> bazel-server-cloud/bazel-out/darwin-fastbuild/bin/cloud/qa/integrationtests/pkg/alt/alt.runfiles/__main__/external/org_postgresql_postgresql/jar/postgresql-42.1.1.jar\n" +
-                    "alt.jar -> bazel-bin/cloud/qa/integrationtests/pkg/utils/utils.jar\n" +
-                    "   com.axsy.testing.alt                               -> com.axsy.testing.extensions.postgres               postgres.jar\n" +
-                    "   com.axsy.testing.alt                               -> com.axsy.testing.pkg.utils                         utils.jar\n" +
-                    "   com.axsy.testing.alt                               -> java.io                                            java.base\n" +
-                    "   com.axsy.testing.alt                               -> java.lang                                          java.base\n" +
-                    "   com.axsy.testing.alt                               -> java.sql                                           java.sql\n" +
-                    "   com.axsy.testing.alt                               -> javax.sql                                          java.sql\n" +
-                    "   com.axsy.testing.alt                               -> kotlin                                             kotlin-stdlib.jar\n" +
-                    "   com.axsy.testing.alt                               -> kotlin.jdk7                                        kotlin-stdlib-jdk7.jar\n" +
-                    "   com.axsy.testing.alt                               -> kotlin.jvm.internal                                kotlin-stdlib.jar\n" +
-                    "   com.axsy.testing.alt                               -> org.postgresql.ds                                  postgresql-42.1.1.jar\n" +
-                    "   com.axsy.testing.alt.sub                           -> java.lang                                          java.base\n" +
-                    "   com.axsy.testing.alt.sub                           -> kotlin                                             kotlin-stdlib.jar\n";
+  private static final String JDK9_FIXTURE =
+      "alt.jar -> java.base\n"
+          + "alt.jar -> java.sql\n"
+          + "alt.jar -> bazel-server-cloud/external/com_github_jetbrains_kotlin/lib/kotlin-stdlib-jdk7.jar\n"
+          + "alt.jar -> bazel-server-cloud/external/com_github_jetbrains_kotlin/lib/kotlin-stdlib.jar\n"
+          + "alt.jar -> bazel-bin/cloud/qa/integrationtests/pkg/extensions/postgres/postgres.jar\n"
+          + "alt.jar -> bazel-server-cloud/bazel-out/darwin-fastbuild/bin/cloud/qa/integrationtests/pkg/alt/alt.runfiles/__main__/external/org_postgresql_postgresql/jar/postgresql-42.1.1.jar\n"
+          + "alt.jar -> bazel-bin/cloud/qa/integrationtests/pkg/utils/utils.jar\n"
+          + "   com.axsy.testing.alt                               -> com.axsy.testing.extensions.postgres               postgres.jar\n"
+          + "   com.axsy.testing.alt                               -> com.axsy.testing.pkg.utils                         utils.jar\n"
+          + "   com.axsy.testing.alt                               -> java.io                                            java.base\n"
+          + "   com.axsy.testing.alt                               -> java.lang                                          java.base\n"
+          + "   com.axsy.testing.alt                               -> java.sql                                           java.sql\n"
+          + "   com.axsy.testing.alt                               -> javax.sql                                          java.sql\n"
+          + "   com.axsy.testing.alt                               -> kotlin                                             kotlin-stdlib.jar\n"
+          + "   com.axsy.testing.alt                               -> kotlin.jdk7                                        kotlin-stdlib-jdk7.jar\n"
+          + "   com.axsy.testing.alt                               -> kotlin.jvm.internal                                kotlin-stdlib.jar\n"
+          + "   com.axsy.testing.alt                               -> org.postgresql.ds                                  postgresql-42.1.1.jar\n"
+          + "   com.axsy.testing.alt.sub                           -> java.lang                                          java.base\n"
+          + "   com.axsy.testing.alt.sub                           -> kotlin                                             kotlin-stdlib.jar\n";
 
+  private static final List<String> CLASSPATH =
+      Arrays.asList(
+          "bazel-bin/cloud/qa/integrationtests/pkg/extensions/postgres/unused.jar",
+          "bazel-server-cloud/external/com_github_jetbrains_kotlin/lib/kotlin-stdlib-jdk8.jar",
+          "bazel-server-cloud/external/com_github_jetbrains_kotlin/lib/kotlin-stdlib-jdk7.jar",
+          "bazel-server-cloud/external/com_github_jetbrains_kotlin/lib/kotlin-stdlib.jar",
+          "bazel-bin/cloud/qa/integrationtests/pkg/extensions/postgres/postgres.jar",
+          "bazel-server-cloud/bazel-out/darwin-fastbuild/bin/cloud/qa/integrationtests/pkg/alt/alt.runfiles/__main__/external/org_postgresql_postgresql/jar/postgresql-42.1.1.jar",
+          "bazel-bin/cloud/qa/integrationtests/pkg/utils/utils.jar");
 
-    private static final List<String> CLASSPATH = Arrays.asList(
-            "bazel-bin/cloud/qa/integrationtests/pkg/extensions/postgres/unused.jar",
-            "bazel-server-cloud/external/com_github_jetbrains_kotlin/lib/kotlin-stdlib-jdk8.jar",
-            "bazel-server-cloud/external/com_github_jetbrains_kotlin/lib/kotlin-stdlib-jdk7.jar",
-            "bazel-server-cloud/external/com_github_jetbrains_kotlin/lib/kotlin-stdlib.jar",
-            "bazel-bin/cloud/qa/integrationtests/pkg/extensions/postgres/postgres.jar",
-            "bazel-server-cloud/bazel-out/darwin-fastbuild/bin/cloud/qa/integrationtests/pkg/alt/alt.runfiles/__main__/external/org_postgresql_postgresql/jar/postgresql-42.1.1.jar",
-            "bazel-bin/cloud/qa/integrationtests/pkg/utils/utils.jar"
-    );
+  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.Companion.pathSuffixMatchingPredicate(
+          Paths.get("external", "com_github_jetbrains_kotlin", "lib"),
+          "kotlin-stdlib.jar",
+          "kotlin-stdlib-jdk7.jar",
+          "kotlin-stdlib-jdk8.jar");
 
-    private static final String LABEL = "//cloud/qa/integrationtests/pkg/alt";
-    private static final String CLASS_JAR = "bazel-bin/something/alt.jar";
+  @Test
+  public void parseJDK8Format() throws IOException {
+    testWithFixture(JDK8_FIXTURE);
+  }
 
-    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",
-            "kotlin-stdlib-jdk8.jar");
+  @Test
+  public void parseJDK9Format() throws IOException {
+    testWithFixture(JDK9_FIXTURE);
+  }
 
-    @Test
-    public void parseJDK8Format() throws IOException {
-        testWithFixture(JDK8_FIXTURE);
-    }
+  private void testWithFixture(String fixture) throws IOException {
+    Deps.Dependencies result =
+        JdepsParser.Companion.parse(
+            LABEL,
+            CLASS_JAR,
+            CLASSPATH.stream().collect(Collectors.joining(":")),
+            Arrays.asList(fixture.split("\n")),
+            IS_KOTLIN_IMPLICIT);
+    Assert.assertEquals(LABEL, result.getRuleLabel());
 
-    @Test
-    public void parseJDK9Format() throws IOException {
-        testWithFixture(JDK9_FIXTURE);
-    }
+    Assert.assertEquals(7, result.getDependencyCount());
+    Assert.assertEquals(1, depKinds(result, Deps.Dependency.Kind.UNUSED).size());
+    Assert.assertEquals(3, depKinds(result, Deps.Dependency.Kind.IMPLICIT).size());
+    Assert.assertEquals(3, depKinds(result, Deps.Dependency.Kind.EXPLICIT).size());
 
-    private void testWithFixture(String fixture) throws IOException {
-        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(2, result.getContainedPackageCount());
 
-        Assert.assertEquals(7, result.getDependencyCount());
-        Assert.assertEquals(1, depKinds(result, Deps.Dependency.Kind.UNUSED).size());
-        Assert.assertEquals(3, depKinds(result, Deps.Dependency.Kind.IMPLICIT).size());
-        Assert.assertEquals(3, depKinds(result, Deps.Dependency.Kind.EXPLICIT).size());
+    result.writeTo(System.out);
+    System.out.flush();
+  }
 
-        Assert.assertEquals(2, result.getContainedPackageCount());
-
-        result.writeTo(System.out);
-        System.out.flush();
-    }
-
-    private List<Deps.Dependency> depKinds(Deps.Dependencies result, Deps.Dependency.Kind kind) {
-        return result.getDependencyList().stream().filter(x -> x.getKind() == kind).collect(Collectors.toList());
-    }
+  private List<Deps.Dependency> depKinds(Deps.Dependencies result, Deps.Dependency.Kind kind) {
+    return result
+        .getDependencyList()
+        .stream()
+        .filter(x -> x.getKind() == kind)
+        .collect(Collectors.toList());
+  }
 }
-
diff --git a/kotlin/kotlin.bzl b/kotlin/kotlin.bzl
index ab62206..0935acf 100644
--- a/kotlin/kotlin.bzl
+++ b/kotlin/kotlin.bzl
@@ -115,8 +115,9 @@
 load(
     "//kotlin:kotlin_compiler_repositories.bzl",
     "KOTLIN_CURRENT_RELEASE",
-    _kotlin_compiler_repository = "kotlin_compiler_repository",
+    _kotlin_compiler_repository = "kotlin_compiler_repositories",
 )
+load("//third_party/jvm:workspace.bzl", _maven_dependencies="maven_dependencies")
 
 _kt_compile_filetypes = FileType([
     # source jars these will be unpacked by the compiler.
@@ -258,6 +259,7 @@
       kotlin_release_version: The kotlin compiler release version. If this is not set the latest release version is
       chosen by default.
     """
+    _maven_dependencies()
     _kotlin_compiler_repository(kotlin_release_version)
 
 def kt_register_toolchains():
diff --git a/kotlin/kotlin_compiler_repositories.bzl b/kotlin/kotlin_compiler_repositories.bzl
index 03cb06c..a886ceb 100644
--- a/kotlin/kotlin_compiler_repositories.bzl
+++ b/kotlin/kotlin_compiler_repositories.bzl
@@ -11,15 +11,16 @@
 # 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.
-"""This file contains the Kotlin compiler repository definitions.
+"""This file contains the Kotlin compiler repository definitions. It should not be loaded directly by client workspaces.
 """
 
 load(
     "//kotlin/internal:kt.bzl",
     _kt = "kt",
 )
-load("//kotlin/internal:bootstrap.bzl",
-  _github_archive="github_archive"
+load(
+  "//third_party/jvm:workspace.bzl",
+  _maven_dependencies="maven_dependencies"
 )
 
 KOTLIN_RELEASES = {
@@ -170,12 +171,15 @@
 
 _BAZEL_JAVA_LAUNCHER_VERSION = "0.8.1"
 
-def kotlin_compiler_repository(
+def kotlin_compiler_repositories(
     kotlin_release_version=KOTLIN_CURRENT_RELEASE,
-    omit_com_google_protobuf=False,
-    omit_com_google_code_gson_gson=False,
-    omit_com_google_guava_guava=False,
 ):
+    """
+    Prime the compiler repository.
+
+    This function should not be called directly instead `kotlin_repositories` from `//kotlin:kotlin.bzl` should be
+    called to ensure common deps are loaded.
+    """
     release=KOTLIN_RELEASES[kotlin_release_version]
     if not release:
         fail('"%s" not a valid kotlin release, current release is "%s"' % (kotlin_release_version, KOTLIN_CURRENT_RELEASE))
@@ -196,36 +200,3 @@
            "java_stub_template.txt"),
         sha256 = "86660ee7d5b498ccf611a1e000564f45268dbf301e0b2b08c984dcecc6513f6e",
     )
-
-    if not omit_com_google_guava_guava:
-        com_google_guava_guava()
-    if not omit_com_google_code_gson_gson:
-        com_google_code_gson_gson()
-    if not omit_com_google_protobuf:
-        com_google_protobuf()
-
-
-def com_google_protobuf():
-    _github_archive(
-        name = "com_google_protobuf",
-        repo = "google/protobuf",
-        commit = "106ffc04be1abf3ff3399f54ccf149815b287dd9",
-    )
-
-def com_google_code_gson_gson():
-    native.maven_jar(
-        name = "com_google_code_gson_gson",
-        artifact = "com.google.code.gson:gson:2.8.4",
-        sha1 = "d0de1ca9b69e69d1d497ee3c6009d015f64dad57"
-    )
-    native.bind(name="gson", actual="@com_google_code_gson_gson//jar")
-
-def com_google_guava_guava():
-    native.maven_jar(
-        name = "com_google_guava_guava",
-        artifact = "com.google.guava:guava:25.0-jre",
-        sha1 = "7319c34fa5866a85b6bad445adad69d402323129"
-    )
-    native.bind(name="guava", actual="@com_google_guava_guava//jar")
-
-
diff --git a/tests/integrationtests/jvm/basic/BUILD b/tests/integrationtests/jvm/basic/BUILD
index 7a534ab..6b200b9 100644
--- a/tests/integrationtests/jvm/basic/BUILD
+++ b/tests/integrationtests/jvm/basic/BUILD
@@ -68,13 +68,13 @@
 kt_jvm_library(
     name = "propagation_test_exporter_lib",
     srcs = ["propagation/Stub.kt"],
-    exports = ["@junit_junit//jar"]
+    exports = ["//third_party/jvm/junit"]
 )
 
 kt_jvm_library(
     name = "propagation_test_runtime_lib",
     srcs = ["propagation/Stub.kt"],
-    runtime_deps = ["@junit_junit//jar"]
+    runtime_deps = ["//third_party/jvm/junit"]
 )
 
 java_binary(
diff --git a/tests/integrationtests/jvm/kapt/BUILD b/tests/integrationtests/jvm/kapt/BUILD
index f1670dc..a29f835 100644
--- a/tests/integrationtests/jvm/kapt/BUILD
+++ b/tests/integrationtests/jvm/kapt/BUILD
@@ -16,7 +16,7 @@
 
 java_plugin(
     name = "autovalue",
-    deps = ["@autovalue//jar"],
+    deps = ["//third_party/jvm/com/google/auto/value:auto_value"],
     processor_class = "com.google.auto.value.processor.AutoValueProcessor",
     generates_api = 1,
 )
@@ -24,9 +24,9 @@
 java_plugin(
     name = "autoservice",
     deps = [
-        "@autoservice//jar",
-        "@com_google_guava_guava//jar",
-        "@auto_common//jar"
+        "//third_party/jvm/com/google/auto/service:auto_service",
+        "//third_party/jvm/com/google/guava",
+        "//third_party/jvm/com/google/auto:auto_common"
     ],
     processor_class = "com.google.auto.service.processor.AutoServiceProcessor",
     generates_api = 0
@@ -34,55 +34,55 @@
 
 java_plugin(
     name = "autovalue_no_processor_class",
-    deps = ["@autovalue//jar"],
+    deps = ["//third_party/jvm/com/google/auto/value:auto_value"],
     generates_api = 1,
 )
 
 kt_jvm_library(
     name = "ap_kotlin",
     srcs = ["kotlin/TestKtValue.kt"],
-    deps = ["@autovalue//jar"],
+    deps = ["//third_party/jvm/com/google/auto/value:auto_value"],
     plugins = [":autovalue"]
 )
 
 kt_jvm_library(
     name = "ap_kotlin_mixed_no_plugin",
     srcs = ["kotlin/TestKtAPNoGenReference.kt", "java/TestAPNoGenReferences.java"],
-    deps = ["@autovalue//jar"]
+    deps = ["//third_party/jvm/com/google/auto/value:auto_value"]
 )
 
 kt_jvm_library(
     name = "ap_kotlin_mixed",
     srcs = ["kotlin/TestKtValue.kt", "java/TestAutoValue.java"],
-    deps = ["@autovalue//jar"],
+    deps = ["//third_party/jvm/com/google/auto/value:auto_value"],
     plugins = [":autovalue"]
 )
 
 kt_jvm_library(
     name = "ap_kotlin_resources",
     srcs = ["kotlin/TestKtService.kt"],
-    deps = ["@autoservice//jar"],
+    deps = ["//third_party/jvm/com/google/auto/service:auto_service"],
     plugins = [":autoservice"]
 )
 
 kt_jvm_library(
     name = "ap_kotlin_resources_mixed",
     srcs = ["kotlin/TestKtService.kt", "java/TestJavaService.java"],
-    deps = ["@autoservice//jar"],
+    deps = ["//third_party/jvm/com/google/auto/service:auto_service"],
     plugins = [":autoservice"]
 )
 
 kt_jvm_library(
     name = "ap_kotlin_mixed_multiple_plugins",
     srcs = ["kotlin/TestKtService.kt", "java/TestJavaService.java", "java/TestAutoValue.java", "kotlin/TestKtValue.kt"],
-    deps = ["@autoservice//jar", "@autovalue//jar"],
+    deps = ["//third_party/jvm/com/google/auto/service:auto_service", "//third_party/jvm/com/google/auto/value:auto_value"],
     plugins = [":autoservice", ":autovalue"]
 )
 
 kt_jvm_library(
     name = "ap_kotlin_mixed_multiple_plugins_one_without_processor_class",
     srcs = ["kotlin/TestKtService.kt", "java/TestJavaService.java", "java/TestAPNoGenReferences.java", "kotlin/TestKtAPNoGenReference.kt"],
-    deps = ["@autoservice//jar", "@autovalue//jar"],
+    deps = ["//third_party/jvm/com/google/auto/service:auto_service", "//third_party/jvm/com/google/auto/value:auto_value"],
     plugins = [":autoservice", ":autovalue_no_processor_class"]
 )
 
@@ -94,7 +94,7 @@
 kt_jvm_library(
     name = "ap_kotlin_mixed_inherit_plugin_via_exported_deps",
     srcs = ["kotlin/TestKtService.kt", "java/TestJavaService.java", "java/TestAutoValue.java", "kotlin/TestKtValue.kt"],
-    deps = ["@autoservice//jar", "@autovalue//jar", "library_exporting_autovalue_and_junit"],
+    deps = ["//third_party/jvm/com/google/auto/service:auto_service", "//third_party/jvm/com/google/auto/value:auto_value", "library_exporting_autovalue_and_junit"],
     plugins = [":autovalue"]
 )
 
diff --git a/tests/rules/defs.bzl b/tests/rules/defs.bzl
index f30a2d2..9a92d32 100644
--- a/tests/rules/defs.bzl
+++ b/tests/rules/defs.bzl
@@ -15,8 +15,8 @@
 
 _TEST_COMMON_DEPS=[
     "//tests/rules:assertion_test_case",
-    "@junit_junit//jar",
-    "@com_google_truth_truth//jar"
+    "//third_party/jvm/com/google/truth",
+    "//third_party/jvm/junit:junit"
 ]
 
 def kt_it_assertion_test(name, cases, test_class, data = [], deps=[]):
diff --git a/third_party/BUILD b/third_party/BUILD
new file mode 100644
index 0000000..2150165
--- /dev/null
+++ b/third_party/BUILD
@@ -0,0 +1,20 @@
+# 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(default_visibility = ["//visibility:public"])
+
+java_binary(
+    name = "bazel_deps",
+    main_class = "com.github.johnynek.bazel_deps.ParseProject",
+    runtime_deps = ["@bazel_deps//jar"],
+)
\ No newline at end of file
diff --git a/third_party/dependencies.yaml b/third_party/dependencies.yaml
new file mode 100644
index 0000000..ca64699
--- /dev/null
+++ b/third_party/dependencies.yaml
@@ -0,0 +1,58 @@
+options:
+  thirdPartyDirectory: "third_party/jvm"
+  buildHeader: [ 'licenses(["notice"])' ]
+  resolvers:
+    - id: "mavencentral"
+      type: "default"
+      url: https://repo.maven.apache.org/maven2/
+  transitivity: runtime_deps
+  versionConflictPolicy: highest
+  namePrefix: "io_bazel_rules_kotlin_"
+
+dependencies:
+  com.google.inject:
+    guice:
+      lang: "java"
+      version: "4.2.0"
+  com.google.protobuf:
+    protobuf:
+      modules: ["java", "java-util"]
+      lang: "java"
+      version: "3.5.1"
+  com.google.code.gson:
+    gson:
+      lang: "java"
+      version: "2.8.4"
+  com.google.guava:
+    guava:
+      lang: "java"
+      version: "25.0-jre"
+  com.google.truth:
+    truth:
+      lang: "java"
+      version: "0.40"
+  com.google.auto.service:
+    auto-service:
+      lang: "java"
+      version: "1.0-rc4"
+  com.google.auto.value:
+    auto-value:
+      lang: "java"
+      version: "1.5.3"
+  com.google.dagger:
+    dagger:
+      modules: ["", "compiler", "producers"]
+      lang: "java"
+      version: "2.9"
+
+replacements:
+  org.jetbrains.kotlin:
+    kotlin-stdlib:
+      lang: java
+      target: "@com_github_jetbrains_kotlin//:kotlin-stdlib"
+    kotlin-reflect:
+      lang: java
+      target: "@com_github_jetbrains_kotlin//:kotlin-reflect"
+    kotlin-script-runtime:
+      lang: java
+      target: "@com_github_jetbrains_kotlin//:kotlin-script-runtime"
\ No newline at end of file
diff --git a/third_party/jvm/BUILD b/third_party/jvm/BUILD
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/third_party/jvm/BUILD
diff --git a/third_party/jvm/aopalliance/BUILD b/third_party/jvm/aopalliance/BUILD
new file mode 100644
index 0000000..2badc05
--- /dev/null
+++ b/third_party/jvm/aopalliance/BUILD
@@ -0,0 +1,12 @@
+licenses(["notice"])
+java_library(
+    name = "aopalliance",
+    exports = [
+        "//external:jar/io_bazel_rules_kotlin_aopalliance/aopalliance"
+    ],
+    visibility = [
+        "//visibility:public"
+    ]
+)
+
+
diff --git a/third_party/jvm/com/google/auto/BUILD b/third_party/jvm/com/google/auto/BUILD
new file mode 100644
index 0000000..dd7196e
--- /dev/null
+++ b/third_party/jvm/com/google/auto/BUILD
@@ -0,0 +1,12 @@
+licenses(["notice"])
+java_library(
+    name = "auto_common",
+    exports = [
+        "//external:jar/io_bazel_rules_kotlin_com/google/auto/auto_common"
+    ],
+    visibility = [
+        "//visibility:public"
+    ]
+)
+
+
diff --git a/third_party/jvm/com/google/auto/service/BUILD b/third_party/jvm/com/google/auto/service/BUILD
new file mode 100644
index 0000000..5d19c82
--- /dev/null
+++ b/third_party/jvm/com/google/auto/service/BUILD
@@ -0,0 +1,16 @@
+licenses(["notice"])
+java_library(
+    name = "auto_service",
+    exports = [
+        "//external:jar/io_bazel_rules_kotlin_com/google/auto/service/auto_service"
+    ],
+    runtime_deps = [
+        "//third_party/jvm/com/google/auto:auto_common",
+        "//third_party/jvm/com/google/guava:guava"
+    ],
+    visibility = [
+        "//visibility:public"
+    ]
+)
+
+
diff --git a/third_party/jvm/com/google/auto/value/BUILD b/third_party/jvm/com/google/auto/value/BUILD
new file mode 100644
index 0000000..18b4e6f
--- /dev/null
+++ b/third_party/jvm/com/google/auto/value/BUILD
@@ -0,0 +1,12 @@
+licenses(["notice"])
+java_library(
+    name = "auto_value",
+    exports = [
+        "//external:jar/io_bazel_rules_kotlin_com/google/auto/value/auto_value"
+    ],
+    visibility = [
+        "//visibility:public"
+    ]
+)
+
+
diff --git a/third_party/jvm/com/google/code/findbugs/BUILD b/third_party/jvm/com/google/code/findbugs/BUILD
new file mode 100644
index 0000000..ab817e9
--- /dev/null
+++ b/third_party/jvm/com/google/code/findbugs/BUILD
@@ -0,0 +1,12 @@
+licenses(["notice"])
+java_library(
+    name = "jsr305",
+    exports = [
+        "//external:jar/io_bazel_rules_kotlin_com/google/code/findbugs/jsr305"
+    ],
+    visibility = [
+        "//visibility:public"
+    ]
+)
+
+
diff --git a/third_party/jvm/com/google/code/gson/BUILD b/third_party/jvm/com/google/code/gson/BUILD
new file mode 100644
index 0000000..0494dae
--- /dev/null
+++ b/third_party/jvm/com/google/code/gson/BUILD
@@ -0,0 +1,12 @@
+licenses(["notice"])
+java_library(
+    name = "gson",
+    exports = [
+        "//external:jar/io_bazel_rules_kotlin_com/google/code/gson/gson"
+    ],
+    visibility = [
+        "//visibility:public"
+    ]
+)
+
+
diff --git a/third_party/jvm/com/google/dagger/BUILD b/third_party/jvm/com/google/dagger/BUILD
new file mode 100644
index 0000000..b33e3b9
--- /dev/null
+++ b/third_party/jvm/com/google/dagger/BUILD
@@ -0,0 +1,48 @@
+licenses(["notice"])
+java_library(
+    name = "dagger",
+    exports = [
+        "//external:jar/io_bazel_rules_kotlin_com/google/dagger/dagger"
+    ],
+    runtime_deps = [
+        "//third_party/jvm/javax/inject:javax_inject"
+    ],
+    visibility = [
+        "//visibility:public"
+    ]
+)
+
+
+
+java_library(
+    name = "dagger_compiler",
+    exports = [
+        "//external:jar/io_bazel_rules_kotlin_com/google/dagger/dagger_compiler"
+    ],
+    runtime_deps = [
+        "//third_party/jvm/com/google/guava:guava",
+        ":dagger",
+        ":dagger_producers"
+    ],
+    visibility = [
+        "//visibility:public"
+    ]
+)
+
+
+
+java_library(
+    name = "dagger_producers",
+    exports = [
+        "//external:jar/io_bazel_rules_kotlin_com/google/dagger/dagger_producers"
+    ],
+    runtime_deps = [
+        "//third_party/jvm/com/google/guava:guava",
+        ":dagger"
+    ],
+    visibility = [
+        "//visibility:public"
+    ]
+)
+
+
diff --git a/third_party/jvm/com/google/errorprone/BUILD b/third_party/jvm/com/google/errorprone/BUILD
new file mode 100644
index 0000000..1d979a3
--- /dev/null
+++ b/third_party/jvm/com/google/errorprone/BUILD
@@ -0,0 +1,12 @@
+licenses(["notice"])
+java_library(
+    name = "error_prone_annotations",
+    exports = [
+        "//external:jar/io_bazel_rules_kotlin_com/google/errorprone/error_prone_annotations"
+    ],
+    visibility = [
+        "//visibility:public"
+    ]
+)
+
+
diff --git a/third_party/jvm/com/google/guava/BUILD b/third_party/jvm/com/google/guava/BUILD
new file mode 100644
index 0000000..1cd774b
--- /dev/null
+++ b/third_party/jvm/com/google/guava/BUILD
@@ -0,0 +1,19 @@
+licenses(["notice"])
+java_library(
+    name = "guava",
+    exports = [
+        "//external:jar/io_bazel_rules_kotlin_com/google/guava/guava"
+    ],
+    runtime_deps = [
+        "//third_party/jvm/com/google/code/findbugs:jsr305",
+        "//third_party/jvm/com/google/errorprone:error_prone_annotations",
+        "//third_party/jvm/com/google/j2objc:j2objc_annotations",
+        "//third_party/jvm/org/checkerframework:checker_compat_qual",
+        "//third_party/jvm/org/codehaus/mojo:animal_sniffer_annotations"
+    ],
+    visibility = [
+        "//visibility:public"
+    ]
+)
+
+
diff --git a/third_party/jvm/com/google/inject/BUILD b/third_party/jvm/com/google/inject/BUILD
new file mode 100644
index 0000000..d2857fc
--- /dev/null
+++ b/third_party/jvm/com/google/inject/BUILD
@@ -0,0 +1,17 @@
+licenses(["notice"])
+java_library(
+    name = "guice",
+    exports = [
+        "//external:jar/io_bazel_rules_kotlin_com/google/inject/guice"
+    ],
+    runtime_deps = [
+        "//third_party/jvm/aopalliance:aopalliance",
+        "//third_party/jvm/com/google/guava:guava",
+        "//third_party/jvm/javax/inject:javax_inject"
+    ],
+    visibility = [
+        "//visibility:public"
+    ]
+)
+
+
diff --git a/third_party/jvm/com/google/j2objc/BUILD b/third_party/jvm/com/google/j2objc/BUILD
new file mode 100644
index 0000000..0fac48d
--- /dev/null
+++ b/third_party/jvm/com/google/j2objc/BUILD
@@ -0,0 +1,12 @@
+licenses(["notice"])
+java_library(
+    name = "j2objc_annotations",
+    exports = [
+        "//external:jar/io_bazel_rules_kotlin_com/google/j2objc/j2objc_annotations"
+    ],
+    visibility = [
+        "//visibility:public"
+    ]
+)
+
+
diff --git a/third_party/jvm/com/google/protobuf/BUILD b/third_party/jvm/com/google/protobuf/BUILD
new file mode 100644
index 0000000..3992e33
--- /dev/null
+++ b/third_party/jvm/com/google/protobuf/BUILD
@@ -0,0 +1,29 @@
+licenses(["notice"])
+java_library(
+    name = "protobuf_java",
+    exports = [
+        "//external:jar/io_bazel_rules_kotlin_com/google/protobuf/protobuf_java"
+    ],
+    visibility = [
+        "//visibility:public"
+    ]
+)
+
+
+
+java_library(
+    name = "protobuf_java_util",
+    exports = [
+        "//external:jar/io_bazel_rules_kotlin_com/google/protobuf/protobuf_java_util"
+    ],
+    runtime_deps = [
+        "//third_party/jvm/com/google/code/gson:gson",
+        "//third_party/jvm/com/google/guava:guava",
+        ":protobuf_java"
+    ],
+    visibility = [
+        "//visibility:public"
+    ]
+)
+
+
diff --git a/third_party/jvm/com/google/truth/BUILD b/third_party/jvm/com/google/truth/BUILD
new file mode 100644
index 0000000..5e318f1
--- /dev/null
+++ b/third_party/jvm/com/google/truth/BUILD
@@ -0,0 +1,18 @@
+licenses(["notice"])
+java_library(
+    name = "truth",
+    exports = [
+        "//external:jar/io_bazel_rules_kotlin_com/google/truth/truth"
+    ],
+    runtime_deps = [
+        "//third_party/jvm/com/google/errorprone:error_prone_annotations",
+        "//third_party/jvm/com/google/guava:guava",
+        "//third_party/jvm/com/googlecode/java_diff_utils:diffutils",
+        "//third_party/jvm/junit:junit"
+    ],
+    visibility = [
+        "//visibility:public"
+    ]
+)
+
+
diff --git a/third_party/jvm/com/googlecode/java_diff_utils/BUILD b/third_party/jvm/com/googlecode/java_diff_utils/BUILD
new file mode 100644
index 0000000..2eba942
--- /dev/null
+++ b/third_party/jvm/com/googlecode/java_diff_utils/BUILD
@@ -0,0 +1,12 @@
+licenses(["notice"])
+java_library(
+    name = "diffutils",
+    exports = [
+        "//external:jar/io_bazel_rules_kotlin_com/googlecode/java_diff_utils/diffutils"
+    ],
+    visibility = [
+        "//visibility:public"
+    ]
+)
+
+
diff --git a/third_party/jvm/javax/inject/BUILD b/third_party/jvm/javax/inject/BUILD
new file mode 100644
index 0000000..eb77060
--- /dev/null
+++ b/third_party/jvm/javax/inject/BUILD
@@ -0,0 +1,12 @@
+licenses(["notice"])
+java_library(
+    name = "javax_inject",
+    exports = [
+        "//external:jar/io_bazel_rules_kotlin_javax/inject/javax_inject"
+    ],
+    visibility = [
+        "//visibility:public"
+    ]
+)
+
+
diff --git a/third_party/jvm/junit/BUILD b/third_party/jvm/junit/BUILD
new file mode 100644
index 0000000..00e3bf0
--- /dev/null
+++ b/third_party/jvm/junit/BUILD
@@ -0,0 +1,15 @@
+licenses(["notice"])
+java_library(
+    name = "junit",
+    exports = [
+        "//external:jar/io_bazel_rules_kotlin_junit/junit"
+    ],
+    runtime_deps = [
+        "//third_party/jvm/org/hamcrest:hamcrest_core"
+    ],
+    visibility = [
+        "//visibility:public"
+    ]
+)
+
+
diff --git a/third_party/jvm/org/checkerframework/BUILD b/third_party/jvm/org/checkerframework/BUILD
new file mode 100644
index 0000000..8b9967c
--- /dev/null
+++ b/third_party/jvm/org/checkerframework/BUILD
@@ -0,0 +1,12 @@
+licenses(["notice"])
+java_library(
+    name = "checker_compat_qual",
+    exports = [
+        "//external:jar/io_bazel_rules_kotlin_org/checkerframework/checker_compat_qual"
+    ],
+    visibility = [
+        "//visibility:public"
+    ]
+)
+
+
diff --git a/third_party/jvm/org/codehaus/mojo/BUILD b/third_party/jvm/org/codehaus/mojo/BUILD
new file mode 100644
index 0000000..42ea30c
--- /dev/null
+++ b/third_party/jvm/org/codehaus/mojo/BUILD
@@ -0,0 +1,12 @@
+licenses(["notice"])
+java_library(
+    name = "animal_sniffer_annotations",
+    exports = [
+        "//external:jar/io_bazel_rules_kotlin_org/codehaus/mojo/animal_sniffer_annotations"
+    ],
+    visibility = [
+        "//visibility:public"
+    ]
+)
+
+
diff --git a/third_party/jvm/org/hamcrest/BUILD b/third_party/jvm/org/hamcrest/BUILD
new file mode 100644
index 0000000..5b9dd79
--- /dev/null
+++ b/third_party/jvm/org/hamcrest/BUILD
@@ -0,0 +1,12 @@
+licenses(["notice"])
+java_library(
+    name = "hamcrest_core",
+    exports = [
+        "//external:jar/io_bazel_rules_kotlin_org/hamcrest/hamcrest_core"
+    ],
+    visibility = [
+        "//visibility:public"
+    ]
+)
+
+
diff --git a/third_party/jvm/org/jetbrains/kotlin/BUILD b/third_party/jvm/org/jetbrains/kotlin/BUILD
new file mode 100644
index 0000000..ec836d2
--- /dev/null
+++ b/third_party/jvm/org/jetbrains/kotlin/BUILD
@@ -0,0 +1,36 @@
+licenses(["notice"])
+java_library(
+    name = "kotlin_reflect",
+    exports = [
+        "@com_github_jetbrains_kotlin//:kotlin-reflect"
+    ],
+    visibility = [
+        "//visibility:public"
+    ]
+)
+
+
+
+java_library(
+    name = "kotlin_script_runtime",
+    exports = [
+        "@com_github_jetbrains_kotlin//:kotlin-script-runtime"
+    ],
+    visibility = [
+        "//visibility:public"
+    ]
+)
+
+
+
+java_library(
+    name = "kotlin_stdlib",
+    exports = [
+        "@com_github_jetbrains_kotlin//:kotlin-stdlib"
+    ],
+    visibility = [
+        "//visibility:public"
+    ]
+)
+
+
diff --git a/third_party/jvm/workspace.bzl b/third_party/jvm/workspace.bzl
new file mode 100644
index 0000000..bf5d21a
--- /dev/null
+++ b/third_party/jvm/workspace.bzl
@@ -0,0 +1,50 @@
+# Do not edit. bazel-deps autogenerates this file from third_party/dependencies.yaml.
+
+def declare_maven(hash):
+    native.maven_jar(
+        name = hash["name"],
+        artifact = hash["artifact"],
+        sha1 = hash["sha1"],
+        repository = hash["repository"]
+    )
+    native.bind(
+        name = hash["bind"],
+        actual = hash["actual"]
+    )
+
+def list_dependencies():
+    return [
+    {"artifact": "aopalliance:aopalliance:1.0", "lang": "java", "sha1": "0235ba8b489512805ac13a8f9ea77a1ca5ebe3e8", "repository": "https://repo.maven.apache.org/maven2/", "name": "io_bazel_rules_kotlin_aopalliance_aopalliance", "actual": "@io_bazel_rules_kotlin_aopalliance_aopalliance//jar", "bind": "jar/io_bazel_rules_kotlin_aopalliance/aopalliance"},
+    {"artifact": "com.google.auto.service:auto-service:1.0-rc4", "lang": "java", "sha1": "44954d465f3b9065388bbd2fc08a3eb8fd07917c", "repository": "https://repo.maven.apache.org/maven2/", "name": "io_bazel_rules_kotlin_com_google_auto_service_auto_service", "actual": "@io_bazel_rules_kotlin_com_google_auto_service_auto_service//jar", "bind": "jar/io_bazel_rules_kotlin_com/google/auto/service/auto_service"},
+    {"artifact": "com.google.auto.value:auto-value:1.5.3", "lang": "java", "sha1": "514df6a7c7938de35c7f68dc8b8f22df86037f38", "repository": "https://repo.maven.apache.org/maven2/", "name": "io_bazel_rules_kotlin_com_google_auto_value_auto_value", "actual": "@io_bazel_rules_kotlin_com_google_auto_value_auto_value//jar", "bind": "jar/io_bazel_rules_kotlin_com/google/auto/value/auto_value"},
+    {"artifact": "com.google.auto:auto-common:0.8", "lang": "java", "sha1": "c6f7af0e57b9d69d81b05434ef9f3c5610d498c4", "repository": "https://repo.maven.apache.org/maven2/", "name": "io_bazel_rules_kotlin_com_google_auto_auto_common", "actual": "@io_bazel_rules_kotlin_com_google_auto_auto_common//jar", "bind": "jar/io_bazel_rules_kotlin_com/google/auto/auto_common"},
+    {"artifact": "com.google.code.findbugs:jsr305:3.0.2", "lang": "java", "sha1": "25ea2e8b0c338a877313bd4672d3fe056ea78f0d", "repository": "https://repo.maven.apache.org/maven2/", "name": "io_bazel_rules_kotlin_com_google_code_findbugs_jsr305", "actual": "@io_bazel_rules_kotlin_com_google_code_findbugs_jsr305//jar", "bind": "jar/io_bazel_rules_kotlin_com/google/code/findbugs/jsr305"},
+    {"artifact": "com.google.code.gson:gson:2.8.4", "lang": "java", "sha1": "d0de1ca9b69e69d1d497ee3c6009d015f64dad57", "repository": "https://repo.maven.apache.org/maven2/", "name": "io_bazel_rules_kotlin_com_google_code_gson_gson", "actual": "@io_bazel_rules_kotlin_com_google_code_gson_gson//jar", "bind": "jar/io_bazel_rules_kotlin_com/google/code/gson/gson"},
+    {"artifact": "com.google.dagger:dagger-compiler:2.9", "lang": "java", "sha1": "276b9c7acc73acb449e1837a47623a7b94afd90b", "repository": "https://repo.maven.apache.org/maven2/", "name": "io_bazel_rules_kotlin_com_google_dagger_dagger_compiler", "actual": "@io_bazel_rules_kotlin_com_google_dagger_dagger_compiler//jar", "bind": "jar/io_bazel_rules_kotlin_com/google/dagger/dagger_compiler"},
+    {"artifact": "com.google.dagger:dagger-producers:2.9", "lang": "java", "sha1": "159090ee31e1752408dedda6fa92ede36a312834", "repository": "https://repo.maven.apache.org/maven2/", "name": "io_bazel_rules_kotlin_com_google_dagger_dagger_producers", "actual": "@io_bazel_rules_kotlin_com_google_dagger_dagger_producers//jar", "bind": "jar/io_bazel_rules_kotlin_com/google/dagger/dagger_producers"},
+    {"artifact": "com.google.dagger:dagger:2.9", "lang": "java", "sha1": "75a739e59d7ede2f7f425f369955bdd1c2ac122b", "repository": "https://repo.maven.apache.org/maven2/", "name": "io_bazel_rules_kotlin_com_google_dagger_dagger", "actual": "@io_bazel_rules_kotlin_com_google_dagger_dagger//jar", "bind": "jar/io_bazel_rules_kotlin_com/google/dagger/dagger"},
+    {"artifact": "com.google.errorprone:error_prone_annotations:2.1.3", "lang": "java", "sha1": "39b109f2cd352b2d71b52a3b5a1a9850e1dc304b", "repository": "https://repo.maven.apache.org/maven2/", "name": "io_bazel_rules_kotlin_com_google_errorprone_error_prone_annotations", "actual": "@io_bazel_rules_kotlin_com_google_errorprone_error_prone_annotations//jar", "bind": "jar/io_bazel_rules_kotlin_com/google/errorprone/error_prone_annotations"},
+# duplicates in com.google.guava:guava fixed to 25.0-jre
+# - com.google.auto.service:auto-service:1.0-rc4 wanted version 23.5-jre
+# - com.google.dagger:dagger-compiler:2.9 wanted version 20.0-rc1
+# - com.google.dagger:dagger-producers:2.9 wanted version 20.0-rc1
+# - com.google.inject:guice:4.2.0 wanted version 23.6-android
+# - com.google.protobuf:protobuf-java-util:3.5.1 wanted version 19.0
+# - com.google.truth:truth:0.40 wanted version 23.4-android
+    {"artifact": "com.google.guava:guava:25.0-jre", "lang": "java", "sha1": "7319c34fa5866a85b6bad445adad69d402323129", "repository": "https://repo.maven.apache.org/maven2/", "name": "io_bazel_rules_kotlin_com_google_guava_guava", "actual": "@io_bazel_rules_kotlin_com_google_guava_guava//jar", "bind": "jar/io_bazel_rules_kotlin_com/google/guava/guava"},
+    {"artifact": "com.google.inject:guice:4.2.0", "lang": "java", "sha1": "25e1f4c1d528a1cffabcca0d432f634f3132f6c8", "repository": "https://repo.maven.apache.org/maven2/", "name": "io_bazel_rules_kotlin_com_google_inject_guice", "actual": "@io_bazel_rules_kotlin_com_google_inject_guice//jar", "bind": "jar/io_bazel_rules_kotlin_com/google/inject/guice"},
+    {"artifact": "com.google.j2objc:j2objc-annotations:1.1", "lang": "java", "sha1": "ed28ded51a8b1c6b112568def5f4b455e6809019", "repository": "https://repo.maven.apache.org/maven2/", "name": "io_bazel_rules_kotlin_com_google_j2objc_j2objc_annotations", "actual": "@io_bazel_rules_kotlin_com_google_j2objc_j2objc_annotations//jar", "bind": "jar/io_bazel_rules_kotlin_com/google/j2objc/j2objc_annotations"},
+    {"artifact": "com.google.protobuf:protobuf-java-util:3.5.1", "lang": "java", "sha1": "6e40a6a3f52455bd633aa2a0dba1a416e62b4575", "repository": "https://repo.maven.apache.org/maven2/", "name": "io_bazel_rules_kotlin_com_google_protobuf_protobuf_java_util", "actual": "@io_bazel_rules_kotlin_com_google_protobuf_protobuf_java_util//jar", "bind": "jar/io_bazel_rules_kotlin_com/google/protobuf/protobuf_java_util"},
+    {"artifact": "com.google.protobuf:protobuf-java:3.5.1", "lang": "java", "sha1": "8c3492f7662fa1cbf8ca76a0f5eb1146f7725acd", "repository": "https://repo.maven.apache.org/maven2/", "name": "io_bazel_rules_kotlin_com_google_protobuf_protobuf_java", "actual": "@io_bazel_rules_kotlin_com_google_protobuf_protobuf_java//jar", "bind": "jar/io_bazel_rules_kotlin_com/google/protobuf/protobuf_java"},
+    {"artifact": "com.google.truth:truth:0.40", "lang": "java", "sha1": "0d74e716afec045cc4a178dbbfde2a8314ae5574", "repository": "https://repo.maven.apache.org/maven2/", "name": "io_bazel_rules_kotlin_com_google_truth_truth", "actual": "@io_bazel_rules_kotlin_com_google_truth_truth//jar", "bind": "jar/io_bazel_rules_kotlin_com/google/truth/truth"},
+    {"artifact": "com.googlecode.java-diff-utils:diffutils:1.3.0", "lang": "java", "sha1": "7e060dd5b19431e6d198e91ff670644372f60fbd", "repository": "https://repo.maven.apache.org/maven2/", "name": "io_bazel_rules_kotlin_com_googlecode_java_diff_utils_diffutils", "actual": "@io_bazel_rules_kotlin_com_googlecode_java_diff_utils_diffutils//jar", "bind": "jar/io_bazel_rules_kotlin_com/googlecode/java_diff_utils/diffutils"},
+    {"artifact": "javax.inject:javax.inject:1", "lang": "java", "sha1": "6975da39a7040257bd51d21a231b76c915872d38", "repository": "https://repo.maven.apache.org/maven2/", "name": "io_bazel_rules_kotlin_javax_inject_javax_inject", "actual": "@io_bazel_rules_kotlin_javax_inject_javax_inject//jar", "bind": "jar/io_bazel_rules_kotlin_javax/inject/javax_inject"},
+    {"artifact": "junit:junit:4.12", "lang": "java", "sha1": "2973d150c0dc1fefe998f834810d68f278ea58ec", "repository": "https://repo.maven.apache.org/maven2/", "name": "io_bazel_rules_kotlin_junit_junit", "actual": "@io_bazel_rules_kotlin_junit_junit//jar", "bind": "jar/io_bazel_rules_kotlin_junit/junit"},
+    {"artifact": "org.checkerframework:checker-compat-qual:2.0.0", "lang": "java", "sha1": "fc89b03860d11d6213d0154a62bcd1c2f69b9efa", "repository": "https://repo.maven.apache.org/maven2/", "name": "io_bazel_rules_kotlin_org_checkerframework_checker_compat_qual", "actual": "@io_bazel_rules_kotlin_org_checkerframework_checker_compat_qual//jar", "bind": "jar/io_bazel_rules_kotlin_org/checkerframework/checker_compat_qual"},
+    {"artifact": "org.codehaus.mojo:animal-sniffer-annotations:1.14", "lang": "java", "sha1": "775b7e22fb10026eed3f86e8dc556dfafe35f2d5", "repository": "https://repo.maven.apache.org/maven2/", "name": "io_bazel_rules_kotlin_org_codehaus_mojo_animal_sniffer_annotations", "actual": "@io_bazel_rules_kotlin_org_codehaus_mojo_animal_sniffer_annotations//jar", "bind": "jar/io_bazel_rules_kotlin_org/codehaus/mojo/animal_sniffer_annotations"},
+    {"artifact": "org.hamcrest:hamcrest-core:1.3", "lang": "java", "sha1": "42a25dc3219429f0e5d060061f71acb49bf010a0", "repository": "https://repo.maven.apache.org/maven2/", "name": "io_bazel_rules_kotlin_org_hamcrest_hamcrest_core", "actual": "@io_bazel_rules_kotlin_org_hamcrest_hamcrest_core//jar", "bind": "jar/io_bazel_rules_kotlin_org/hamcrest/hamcrest_core"},
+    ]
+
+def maven_dependencies(callback = declare_maven):
+    for hash in list_dependencies():
+        callback(hash)
diff --git a/third_party/regen_deps.sh b/third_party/regen_deps.sh
new file mode 100755
index 0000000..1072e02
--- /dev/null
+++ b/third_party/regen_deps.sh
@@ -0,0 +1,13 @@
+#!/usr/bin/env bash
+
+set -e
+
+WS_ROOT=$(bazel info workspace)
+THIRD_PARTY_DIR=${WS_ROOT}/third_party
+
+bazel run //third_party:bazel_deps generate -- \
+    -r ${WS_ROOT} \
+    -d third_party/dependencies.yaml \
+    -s third_party/jvm/workspace.bzl
+
+