cherry pick changes and fixes from annotation-processing-branch
diff --git a/.bazelproject b/.bazelproject
index 7828fba..529a61b 100644
--- a/.bazelproject
+++ b/.bazelproject
@@ -17,7 +17,7 @@
   .
 
 targets:
-  //kotlin/workers:worker_for_ide
+  //kotlin/workers:for_ide
   //kotlin/workers:unittests
 
 test_sources:
diff --git a/kotlin/workers/BUILD b/kotlin/workers/BUILD
index 622afeb..b57623f 100644
--- a/kotlin/workers/BUILD
+++ b/kotlin/workers/BUILD
@@ -27,13 +27,14 @@
     runtime_deps = [
         "@com_github_jetbrains_kotlin//:kotlin-stdlib",
         "@com_github_jetbrains_kotlin//:kotlin-stdlib-jdk7",
-        "@com_github_jetbrains_kotlin//:kotlin-stdlib-jdk8"
+        "@com_github_jetbrains_kotlin//:kotlin-stdlib-jdk8",
+        "@com_github_jetbrains_kotlin//:kotlin-reflect"
     ]
 )
 
 java_binary(
     name = "compiler_jvm",
-    main_class = "io.bazel.ruleskotlin.workers.compilers.jvm.KotlinJvmBuilder",
+    main_class = "io.bazel.ruleskotlin.workers.KotlinJvmBuilder",
     visibility = ["//visibility:public"],
     runtime_deps = [":worker_lib"]
 )
diff --git a/kotlin/workers/bootstrap.bzl b/kotlin/workers/bootstrap.bzl
index dbdc866..70fccd5 100644
--- a/kotlin/workers/bootstrap.bzl
+++ b/kotlin/workers/bootstrap.bzl
@@ -72,7 +72,7 @@
             "@com_github_jetbrains_kotlin//:kotlinc",
             "@local_jdk//:jdk",
             dep_label
-        ] + deps,
+        ],
         srcs = srcs,
         outs = [jar_name],
         cmd = _gen_cmd(dep_label, " ".join(args)),
@@ -83,5 +83,6 @@
         jars = [jar_name],
         exports = exports,
         runtime_deps = (depset(runtime_deps) + exports + deps).to_list(),
-        visibility = ["//visibility:private"]
+        visibility = ["//visibility:private"],
+        tags = ["no-ide"]
     )
\ No newline at end of file
diff --git a/kotlin/workers/src/io/bazel/ruleskotlin/workers/CommandLineProgram.kt b/kotlin/workers/src/io/bazel/ruleskotlin/workers/CommandLineProgram.kt
index 5fb40fe..60638bd 100644
--- a/kotlin/workers/src/io/bazel/ruleskotlin/workers/CommandLineProgram.kt
+++ b/kotlin/workers/src/io/bazel/ruleskotlin/workers/CommandLineProgram.kt
@@ -15,14 +15,12 @@
  */
 package io.bazel.ruleskotlin.workers
 
-
 /**
  * Interface for command line programs.
  *
  * This is the same thing as a main function, except not static.
  */
 interface CommandLineProgram {
-
     /**
      * Runs blocking program start to finish.
      *
@@ -35,4 +33,43 @@
      * @return program exit code, i.e. 0 for success, non-zero for failure
      */
     fun apply(args: List<String>): Int
-}
+
+    abstract class Base(
+            protected val flags: FlagNameMap = emptyMap()
+    ): CommandLineProgram {
+        private val mandatoryFlags: List<Flag> = flags.values.filter { it is Flag.Mandatory }
+
+        private fun createContext(args: List<String>): Context {
+            val tally = mutableMapOf<Flag, String>()
+
+            if (args.size % 2 != 0) {
+                throw RuntimeException("args should be k,v pairs")
+            }
+
+            for (i in 0 until args.size / 2) {
+                val flag = args[i * 2]
+                val value = args[i * 2 + 1]
+                val field = flags[flag] ?: throw RuntimeException("unrecognised arg: " + flag)
+                tally[field] = value
+            }
+
+            mandatoryFlags.forEach { require(tally.containsKey(it)) { "missing mandatory flag ${it.globalFlag}" } }
+            return Context(tally)
+        }
+
+        abstract fun toolchain(ctx: Context): KotlinToolchain
+        abstract fun actions(toolchain: KotlinToolchain, ctx: Context): List<BuildAction>
+
+        override fun apply(args: List<String>): Int {
+            val ctx = createContext(args)
+            val toolchain = toolchain(ctx)
+            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/workers/src/io/bazel/ruleskotlin/workers/CompileResult.kt b/kotlin/workers/src/io/bazel/ruleskotlin/workers/CompileResult.kt
index 6ac5a57..52c30b4 100644
--- a/kotlin/workers/src/io/bazel/ruleskotlin/workers/CompileResult.kt
+++ b/kotlin/workers/src/io/bazel/ruleskotlin/workers/CompileResult.kt
@@ -34,7 +34,8 @@
         error().ifPresent { e -> throw RuntimeException(message, e) }
     }
 
-    class Meta(id: String) : io.bazel.ruleskotlin.workers.Meta<CompileResult>(id) {
+    class Meta(id: String) : io.bazel.ruleskotlin.workers.Meta<CompileResult> {
+        override val id: String = id
 
         fun run(ctx: Context, op: (Context) -> Int): CompileResult {
             var result: CompileResult
@@ -49,7 +50,7 @@
 
         fun runAndBind(ctx: Context, op: (Context) -> Int): CompileResult {
             val res = run(ctx, op)
-            bind(ctx, res)
+            set(ctx,res)
             return res
         }
 
diff --git a/kotlin/workers/src/io/bazel/ruleskotlin/workers/Context.kt b/kotlin/workers/src/io/bazel/ruleskotlin/workers/Context.kt
index 1a42331..18b1d93 100644
--- a/kotlin/workers/src/io/bazel/ruleskotlin/workers/Context.kt
+++ b/kotlin/workers/src/io/bazel/ruleskotlin/workers/Context.kt
@@ -17,55 +17,20 @@
 
 package io.bazel.ruleskotlin.workers
 
-import java.util.*
 import java.util.stream.Stream
 
-class Context private constructor(args: List<String>) {
-    private val args = EnumMap<Flags, String>(Flags::class.java)
-    private val meta = HashMap<Meta<*>, Any>()
+class Context internal constructor(
+        private val flags: Map<Flag, String>
+) {
+    private val meta = mutableMapOf<Meta<*>, Any>()
 
-    init {
-        if (args.size % 2 != 0) {
-            throw RuntimeException("args should be k,v pairs")
-        }
-
-        for (i in 0 until args.size / 2) {
-            val flag = args[i * 2]
-            val value = args[i * 2 + 1]
-            val field = ALL_FIELDS_MAP[flag] ?: throw RuntimeException("unrecognised arg: " + flag)
-            this.args[field] = value
-        }
-
-        MANDATORY_FIELDS.asSequence()
-                .filterNot { this.args.containsKey(it) }
-                .forEach { throw RuntimeException("mandatory arg missing: " + it.globalFlag) }
-    }
-
-    fun of(vararg fields: Flags): EnumMap<Flags, String> {
-        val result = EnumMap<Flags, String>(Flags::class.java)
-        for (field in fields) {
-            val value = args[field]
-            if (value != null) {
-                result[field] = value
-            }
-        }
-        return result
-    }
+    fun copyOfFlags(vararg fields: Flag): Map<Flag, String> = fields.mapNotNull { f -> flags[f]?.let { f to it } }.toMap()
 
     fun apply(vararg consumers: (Context) -> Unit) {
         Stream.of(*consumers).forEach { it(this) }
     }
 
-    internal operator fun get(field: Flags): String? = args[field]
-    internal operator fun <T : Any> get(key: Meta<T>): T? = meta[key] as T?
+    internal operator fun get(flag: Flag): String? = flags[flag]
+    operator fun <T : Any> get(key: Meta<T>): T? = meta[key] as T?
     internal fun <T : Any> putIfAbsent(key: Meta<T>, value: T): T? = meta.putIfAbsent(key, value as Any) as T?
-
-    companion object {
-        private val ALL_FIELDS_MAP = Flags.values().map { it.globalFlag to Flags.valueOf(it.name) }.toMap()
-        private val MANDATORY_FIELDS = Flags.values().filter { x -> x.mandatory }
-
-        fun from(args: List<String>): Context {
-            return Context(args)
-        }
-    }
 }
diff --git a/kotlin/workers/src/io/bazel/ruleskotlin/workers/Flag.kt b/kotlin/workers/src/io/bazel/ruleskotlin/workers/Flag.kt
new file mode 100644
index 0000000..e00301e
--- /dev/null
+++ b/kotlin/workers/src/io/bazel/ruleskotlin/workers/Flag.kt
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2018 The Bazel Authors. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package io.bazel.ruleskotlin.workers
+
+import kotlin.reflect.KClass
+import kotlin.reflect.full.declaredMemberProperties
+
+sealed class Flag(val globalFlag: String, val kotlinFlag: String? = null) {
+    open operator fun get(context: Context): String? = context[this]
+
+    class Optional(globalFlag: String, kotlinFlag: String? = null) : Flag(globalFlag, kotlinFlag)
+
+    class Mandatory(globalFlag: String, kotlinFlag: String? = null) : Flag(globalFlag, kotlinFlag) {
+        override fun get(context: Context): String = requireNotNull(super.get(context)) { "mandatory flag $globalFlag not present" }
+    }
+}
+
+/**
+ * all of the static flag properties declared in a class.
+ */
+// works for objects only.
+private fun <T : Any> KClass<T>.allFlags(): Sequence<Flag> {
+    val obj = requireNotNull(this.objectInstance) { "only collects flag instances from classes with an object instance" }
+    return declaredMemberProperties.asSequence().mapNotNull {
+        it.get(obj).takeIf(Flag::class::isInstance).let { it as Flag }
+    }
+}
+
+typealias FlagNameMap = Map<String, Flag>
+
+/**
+ * Map from flag name to flag collected from the static properteis declared in a class.
+ */
+fun <T : Any> KClass<T>.flagsByName(): FlagNameMap = allFlags().associateBy { it.globalFlag }
\ No newline at end of file
diff --git a/kotlin/workers/src/io/bazel/ruleskotlin/workers/Flags.kt b/kotlin/workers/src/io/bazel/ruleskotlin/workers/Flags.kt
deleted file mode 100644
index fe20049..0000000
--- a/kotlin/workers/src/io/bazel/ruleskotlin/workers/Flags.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.
- */
-package io.bazel.ruleskotlin.workers
-
-
-enum class Flags(val globalFlag: String, val kotlinFlag: String?, internal val mandatory: Boolean) {
-    // flags that line up with the java builder.
-    LABEL(JavaBuilderFlags.TARGET_LABEL.flag, null, true),
-    OUTPUT_CLASSJAR(JavaBuilderFlags.OUTPUT.flag, null, true),
-    SOURCES(JavaBuilderFlags.SOURCES.flag, null, true),
-    CLASSPATH(JavaBuilderFlags.CLASSPATH.flag, "-cp", true),
-
-    // flags that could be aligned with the java builder.
-    OUTPUT_JDEPS("--output_jdeps", null, true),
-    COMPILER_OUTPUT_BASE("--compiler_output_base", null, true),
-
-    // flags for kotlin.
-    KOTLIN_API_VERSION("--kotlin_api_version", "-api-version", false),
-    KOTLIN_LANGUAGE_VERSION("--kotlin_language_version", "-language-version", false),
-    KOTLIN_JVM_TARGET("--kotlin_jvm_target", "-jvm-target", false);
-
-    operator fun get(context: Context): String? = context[this]
-}
\ No newline at end of file
diff --git a/kotlin/workers/src/io/bazel/ruleskotlin/workers/KotlinJvmBuilder.kt b/kotlin/workers/src/io/bazel/ruleskotlin/workers/KotlinJvmBuilder.kt
new file mode 100644
index 0000000..a2a4b6a
--- /dev/null
+++ b/kotlin/workers/src/io/bazel/ruleskotlin/workers/KotlinJvmBuilder.kt
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2018 The Bazel Authors. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package io.bazel.ruleskotlin.workers
+
+
+import io.bazel.ruleskotlin.workers.compilers.jvm.actions.*
+import io.bazel.ruleskotlin.workers.model.Flags
+import java.io.IOException
+
+/**
+ * Bazel Kotlin Compiler worker.
+ */
+object KotlinJvmBuilder : CommandLineProgram.Base(
+        flags = Flags::class.flagsByName()
+) {
+    private val toolchain: KotlinToolchain = try {
+        KotlinToolchain()
+    } catch (e: IOException) {
+        throw RuntimeException("could not initialize toolchain", e)
+    }
+
+    private val compileActions: List<BuildAction> = listOf(
+            Initialize(toolchain),
+            KotlinMainCompile(toolchain),
+            JavaMainCompile(toolchain),
+            ProcessCompileResult(toolchain),
+            CreateOutputJar(toolchain),
+            GenerateJdepsFile(toolchain)
+    )
+
+    override fun toolchain(ctx: Context): KotlinToolchain = toolchain
+    override fun actions(toolchain: KotlinToolchain, ctx: Context): List<BuildAction> = compileActions
+
+    @JvmStatic
+    fun main(args: Array<String>) {
+        val kotlinCompilerBazelWorker = BazelWorker(
+                this,
+                System.err,
+                "KotlinCompile"
+        )
+        System.exit(kotlinCompilerBazelWorker.apply(args.toList()))
+    }
+}
diff --git a/kotlin/workers/src/io/bazel/ruleskotlin/workers/KotlinToolchain.kt b/kotlin/workers/src/io/bazel/ruleskotlin/workers/KotlinToolchain.kt
index 6e7a2c8..622c46d 100644
--- a/kotlin/workers/src/io/bazel/ruleskotlin/workers/KotlinToolchain.kt
+++ b/kotlin/workers/src/io/bazel/ruleskotlin/workers/KotlinToolchain.kt
@@ -19,6 +19,7 @@
 import io.bazel.ruleskotlin.workers.utils.verifiedRelativeFiles
 import org.jetbrains.kotlin.preloading.ClassPreloadingUtils
 import org.jetbrains.kotlin.preloading.Preloader
+import java.io.File
 import java.io.PrintStream
 import java.nio.file.Path
 import java.nio.file.Paths
@@ -37,9 +38,10 @@
     val JDEPS_PATH = JAVA_HOME.resolveVerified("bin", "jdeps").toString()
     val KOTLIN_LIB_DIR: Path = KOTLIN_HOME.resolveVerified("lib").toPath()
 
-    private val kotlinPreloadJars = KOTLIN_LIB_DIR.verifiedRelativeFiles(
-            Paths.get("kotlin-compiler.jar")
-    )
+    private val kotlinPreloadJars = mutableListOf<File>().let {
+        it.addAll(KOTLIN_LIB_DIR.verifiedRelativeFiles(Paths.get("kotlin-compiler.jar")))
+        it.toList()
+    }
 
     val KOTLIN_STD_LIBS = arrayOf(
             "kotlin-stdlib.jar",
diff --git a/kotlin/workers/src/io/bazel/ruleskotlin/workers/Meta.kt b/kotlin/workers/src/io/bazel/ruleskotlin/workers/Meta.kt
index a0aca11..1b48198 100644
--- a/kotlin/workers/src/io/bazel/ruleskotlin/workers/Meta.kt
+++ b/kotlin/workers/src/io/bazel/ruleskotlin/workers/Meta.kt
@@ -15,17 +15,18 @@
  */
 package io.bazel.ruleskotlin.workers
 
-open class Meta<T: Any>(
-        private val id: String,
-        private val defaultValue: T? = null
-) {
-    constructor(id: String) : this(id, null)
+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" }
+            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.
@@ -39,5 +40,25 @@
         }
     }
 
-    fun bind(ctx: Context, value: T) { check(ctx.putIfAbsent(this, value) == null) { "attempting to change bound meta variable: $id " } }
+    operator fun set(ctx: Context, value: T) {
+        check(ctx.putIfAbsent(this, value) == 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
+        }
+    }
+}
+
+interface MandatoryMeta<T: Any>: Meta<T> {
+    override fun get(ctx: Context): T = checkNotNull(super.get(ctx)) { "ctx missing mandatory meta ${this.id}" }
+
+    companion object {
+        operator fun <T : Any> invoke(id: String): Meta<T> = object : MandatoryMeta<T> {
+            override val id: String = id
+            override val defaultValue: T? = null
+        }
+    }
 }
diff --git a/kotlin/workers/src/io/bazel/ruleskotlin/workers/compilers/jvm/KotlinJvmBuilder.kt b/kotlin/workers/src/io/bazel/ruleskotlin/workers/compilers/jvm/KotlinJvmBuilder.kt
deleted file mode 100644
index 571bdd1..0000000
--- a/kotlin/workers/src/io/bazel/ruleskotlin/workers/compilers/jvm/KotlinJvmBuilder.kt
+++ /dev/null
@@ -1,71 +0,0 @@
-/*
- * Copyright 2018 The Bazel Authors. All rights reserved.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *    http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package io.bazel.ruleskotlin.workers.compilers.jvm
-
-
-import io.bazel.ruleskotlin.workers.*
-import io.bazel.ruleskotlin.workers.compilers.jvm.actions.*
-
-import java.io.IOException
-
-/**
- * Bazel Kotlin Compiler worker.
- */
-class KotlinJvmBuilder private constructor() : CommandLineProgram {
-    private val compileActions: Array<BuildAction>
-
-    init {
-        val toolchain: KotlinToolchain
-        try {
-            toolchain = KotlinToolchain()
-        } catch (e: IOException) {
-            throw RuntimeException("could not initialize toolchain", e)
-        }
-
-        compileActions = arrayOf(
-                Initialize(toolchain),
-                KotlinMainCompile(toolchain),
-                JavaMainCompile(toolchain),
-                ProcessCompileResult(toolchain),
-                CreateOutputJar(toolchain),
-                GenerateJdepsFile(toolchain)
-        )
-    }
-
-    override fun apply(args: List<String>): Int {
-        val ctx = Context.from(args)
-        var exitCode = 0
-        for (action in compileActions) {
-            exitCode = action(ctx)
-            if (exitCode != 0)
-                break
-        }
-        return exitCode
-    }
-
-    companion object {
-        @JvmStatic
-        fun main(args: Array<String>) {
-            val kotlinBuilder = KotlinJvmBuilder()
-            val kotlinCompilerBazelWorker = BazelWorker(
-                    kotlinBuilder,
-                    System.err,
-                    "KotlinCompile"
-            )
-            System.exit(kotlinCompilerBazelWorker.apply(args.toList()))
-        }
-    }
-}
diff --git a/kotlin/workers/src/io/bazel/ruleskotlin/workers/compilers/jvm/Metas.kt b/kotlin/workers/src/io/bazel/ruleskotlin/workers/compilers/jvm/Metas.kt
deleted file mode 100644
index 6b56242..0000000
--- a/kotlin/workers/src/io/bazel/ruleskotlin/workers/compilers/jvm/Metas.kt
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * Copyright 2018 The Bazel Authors. All rights reserved.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *    http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package io.bazel.ruleskotlin.workers.compilers.jvm
-
-import io.bazel.ruleskotlin.workers.CompileResult
-import io.bazel.ruleskotlin.workers.Meta
-
-import java.nio.file.Path
-
-/**
- * Meta is a key to some compilation state,.
- */
-object Metas {
-    // mandatory: the package part of the label.
-    val PKG = Meta<String>("package")
-    // mandatory: The target part of the label.
-    val TARGET = Meta<String>("target")
-    // mandatory: the class staging directory.
-    val CLASSES_DIRECTORY = Meta<Path>("class_directory")
-    // mandatory: If this is non empty then it is a mixed mode operation.
-    val JAVA_SOURCES = Meta<List<String>>("java_sources")
-    // mandatory:
-    val ALL_SOURCES = Meta<List<String>>("all_sources")
-    // mandatory:
-    val KOTLINC_RESULT = CompileResult.Meta("kotlin_compile_result")
-    // optional: when not a mixed mode operation.
-    val JAVAC_RESULT = CompileResult.Meta("javac_compile_result")
-}
\ No newline at end of file
diff --git a/kotlin/workers/src/io/bazel/ruleskotlin/workers/compilers/jvm/actions/CreateOutputJar.kt b/kotlin/workers/src/io/bazel/ruleskotlin/workers/compilers/jvm/actions/CreateOutputJar.kt
index 38545fa..be2732d 100644
--- a/kotlin/workers/src/io/bazel/ruleskotlin/workers/compilers/jvm/actions/CreateOutputJar.kt
+++ b/kotlin/workers/src/io/bazel/ruleskotlin/workers/compilers/jvm/actions/CreateOutputJar.kt
@@ -18,9 +18,9 @@
 
 import io.bazel.ruleskotlin.workers.BuildAction
 import io.bazel.ruleskotlin.workers.Context
-import io.bazel.ruleskotlin.workers.Flags
 import io.bazel.ruleskotlin.workers.KotlinToolchain
-import io.bazel.ruleskotlin.workers.compilers.jvm.Metas
+import io.bazel.ruleskotlin.workers.model.CompileDirectories
+import io.bazel.ruleskotlin.workers.model.Flags
 import io.bazel.ruleskotlin.workers.utils.executeAndAwaitSuccess
 
 /**
@@ -32,7 +32,7 @@
             executeAndAwaitSuccess(10,
                     toolchain.JAR_TOOL_PATH,
                     "cf", checkNotNull(Flags.OUTPUT_CLASSJAR[ctx]),
-                    "-C", Metas.CLASSES_DIRECTORY.mustGet(ctx).toString(),
+                    "-C", CompileDirectories[ctx].classes,
                     "."
             )
         } catch (e: Exception) {
diff --git a/kotlin/workers/src/io/bazel/ruleskotlin/workers/compilers/jvm/actions/GenerateJdepsFile.kt b/kotlin/workers/src/io/bazel/ruleskotlin/workers/compilers/jvm/actions/GenerateJdepsFile.kt
index 120e38d..ca13558 100644
--- a/kotlin/workers/src/io/bazel/ruleskotlin/workers/compilers/jvm/actions/GenerateJdepsFile.kt
+++ b/kotlin/workers/src/io/bazel/ruleskotlin/workers/compilers/jvm/actions/GenerateJdepsFile.kt
@@ -18,9 +18,12 @@
 import com.google.devtools.build.lib.view.proto.Deps
 import io.bazel.ruleskotlin.workers.BuildAction
 import io.bazel.ruleskotlin.workers.Context
-import io.bazel.ruleskotlin.workers.Flags.*
 import io.bazel.ruleskotlin.workers.KotlinToolchain
 import io.bazel.ruleskotlin.workers.compilers.jvm.utils.JdepsParser
+import io.bazel.ruleskotlin.workers.model.Flags.CLASSPATH
+import io.bazel.ruleskotlin.workers.model.Flags.LABEL
+import io.bazel.ruleskotlin.workers.model.Flags.OUTPUT_CLASSJAR
+import io.bazel.ruleskotlin.workers.model.Flags.OUTPUT_JDEPS
 import io.bazel.ruleskotlin.workers.utils.executeAndWaitOutput
 import io.bazel.ruleskotlin.workers.utils.rootCause
 import java.io.FileOutputStream
@@ -32,10 +35,11 @@
     private val isKotlinImplicit = JdepsParser.pathSuffixMatchingPredicate(toolchain.KOTLIN_LIB_DIR, *toolchain.KOTLIN_STD_LIBS)
 
     override fun invoke(ctx: Context): Int {
-        val classJar = checkNotNull(OUTPUT_CLASSJAR[ctx])
-        val classPath = checkNotNull(CLASSPATH[ctx])
-        val output = checkNotNull(OUTPUT_JDEPS[ctx])
-        val label = checkNotNull(LABEL[ctx])
+        val classJar = OUTPUT_CLASSJAR[ctx]
+        val classPath = CLASSPATH[ctx]
+        val output = OUTPUT_JDEPS[ctx]
+        val label = LABEL[ctx]
+
         val jdepsContent: Deps.Dependencies
 
         try {
diff --git a/kotlin/workers/src/io/bazel/ruleskotlin/workers/compilers/jvm/actions/Initialize.kt b/kotlin/workers/src/io/bazel/ruleskotlin/workers/compilers/jvm/actions/Initialize.kt
index 6a3f3b9..31a8a9d 100644
--- a/kotlin/workers/src/io/bazel/ruleskotlin/workers/compilers/jvm/actions/Initialize.kt
+++ b/kotlin/workers/src/io/bazel/ruleskotlin/workers/compilers/jvm/actions/Initialize.kt
@@ -16,17 +16,14 @@
 package io.bazel.ruleskotlin.workers.compilers.jvm.actions
 
 
-import io.bazel.ruleskotlin.workers.*
-import io.bazel.ruleskotlin.workers.compilers.jvm.Metas
-
-import io.bazel.ruleskotlin.workers.utils.purgeDirectory
-
-import java.io.IOException
+import io.bazel.ruleskotlin.workers.BuildAction
+import io.bazel.ruleskotlin.workers.Context
+import io.bazel.ruleskotlin.workers.KotlinToolchain
+import io.bazel.ruleskotlin.workers.model.CompileDirectories
+import io.bazel.ruleskotlin.workers.model.Flags
+import io.bazel.ruleskotlin.workers.model.Metas
 import java.nio.file.Files
-import java.nio.file.Path
 import java.nio.file.Paths
-import java.util.ArrayList
-import java.util.Collections
 
 /**
  * Should be the first step, does mandatory pre-processing.
@@ -42,8 +39,8 @@
     }
 
     private fun bindSources(ctx: Context) {
-        val javaSources = ArrayList<String>()
-        val allSources = ArrayList<String>()
+        val javaSources = mutableListOf<String>()
+        val allSources = mutableListOf<String>()
         for (src in requireNotNull(Flags.SOURCES[ctx]).split(":")) {
             when {
                 src.endsWith(".java") -> {
@@ -54,35 +51,14 @@
                 else -> throw RuntimeException("unrecognised file type: $src")
             }
         }
-        Metas.JAVA_SOURCES.bind(ctx, Collections.unmodifiableList(javaSources))
-        Metas.ALL_SOURCES.bind(ctx, Collections.unmodifiableList(allSources))
+        Metas.JAVA_SOURCES[ctx] = javaSources.toList()
+        Metas.ALL_SOURCES[ctx] = allSources.toList()
     }
 
     private fun initializeAndBindBindDirectories(ctx: Context) {
-        val outputBase: Path
-
-        try {
-            outputBase = Files.createDirectories(Paths.get(checkNotNull(Flags.COMPILER_OUTPUT_BASE[ctx])))
-        } catch (e: IOException) {
-            throw RuntimeException("could not create compiler output base", e)
+        Files.createDirectories(Paths.get(Flags.COMPILER_OUTPUT_BASE[ctx])).let {
+            CompileDirectories[ctx] = CompileDirectories(it)
         }
-
-        try {
-            outputBase.purgeDirectory()
-        } catch (e: IOException) {
-            throw RuntimeException("could not purge output directory", e)
-        }
-
-        createAndBindComponentDirectory(ctx, outputBase, Metas.CLASSES_DIRECTORY, "_classes")
-    }
-
-    private fun createAndBindComponentDirectory(ctx: Context, outputBase: Path, key: Meta<Path>, component: String) {
-        try {
-            key.bind(ctx, Files.createDirectories(outputBase.resolve(component)))
-        } catch (e: IOException) {
-            throw RuntimeException("could not create subdirectory for component " + component, e)
-        }
-
     }
 
     /**
@@ -92,7 +68,7 @@
         val label = requireNotNull(Flags.LABEL[ctx])
         val parts = label.split(":")
         require(parts.size == 2) { "the label $label is invalid" }
-        Metas.PKG.bind(ctx, parts[0])
-        Metas.TARGET.bind(ctx, parts[1])
+        Metas.PKG[ctx] = parts[0]
+        Metas.TARGET[ctx] = parts[1]
     }
 }
diff --git a/kotlin/workers/src/io/bazel/ruleskotlin/workers/compilers/jvm/actions/JavaMainCompile.kt b/kotlin/workers/src/io/bazel/ruleskotlin/workers/compilers/jvm/actions/JavaMainCompile.kt
index 3ae306b..3295c57 100644
--- a/kotlin/workers/src/io/bazel/ruleskotlin/workers/compilers/jvm/actions/JavaMainCompile.kt
+++ b/kotlin/workers/src/io/bazel/ruleskotlin/workers/compilers/jvm/actions/JavaMainCompile.kt
@@ -15,26 +15,28 @@
  */
 package io.bazel.ruleskotlin.workers.compilers.jvm.actions
 
-import io.bazel.ruleskotlin.workers.BuildAction
-import io.bazel.ruleskotlin.workers.Context
-import io.bazel.ruleskotlin.workers.Flags
-import io.bazel.ruleskotlin.workers.KotlinToolchain
-import io.bazel.ruleskotlin.workers.compilers.jvm.Metas
+import io.bazel.ruleskotlin.workers.*
+import io.bazel.ruleskotlin.workers.model.Metas
+import io.bazel.ruleskotlin.workers.model.CompileDirectories
+import io.bazel.ruleskotlin.workers.model.Flags
 import io.bazel.ruleskotlin.workers.utils.executeAndAwait
 
 /**
  * Simple java compile action that invokes javac directly and simply.
  */
 class JavaMainCompile(toolchain: KotlinToolchain) : BuildAction("compile java classes", toolchain) {
+    companion object {
+        val Result = CompileResult.Meta("javac_compile_result")
+    }
     override fun invoke(ctx: Context): Int {
         val javaSources = Metas.JAVA_SOURCES.mustGet(ctx)
-        val classpath = checkNotNull(Flags.CLASSPATH[ctx])
+        val classpath = Flags.CLASSPATH[ctx]
 
         if (!javaSources.isEmpty()) {
-            val classesDirectory = Metas.CLASSES_DIRECTORY.mustGet(ctx).toString()
+            val classesDirectory = CompileDirectories[ctx].classes
 
             val args = mutableListOf(toolchain.JAVAC_PATH, "-cp", "$classesDirectory/:$classpath", "-d", classesDirectory).also { it.addAll(javaSources) }
-            Metas.JAVAC_RESULT.runAndBind(ctx) { executeAndAwait(30, args) }
+            Result.runAndBind(ctx) { executeAndAwait(30, args) }
         }
         return 0
     }
diff --git a/kotlin/workers/src/io/bazel/ruleskotlin/workers/compilers/jvm/actions/KotlinMainCompile.kt b/kotlin/workers/src/io/bazel/ruleskotlin/workers/compilers/jvm/actions/KotlinMainCompile.kt
index bf5b1ab..0be74ba 100644
--- a/kotlin/workers/src/io/bazel/ruleskotlin/workers/compilers/jvm/actions/KotlinMainCompile.kt
+++ b/kotlin/workers/src/io/bazel/ruleskotlin/workers/compilers/jvm/actions/KotlinMainCompile.kt
@@ -15,13 +15,13 @@
  */
 package io.bazel.ruleskotlin.workers.compilers.jvm.actions
 
+
 import io.bazel.ruleskotlin.workers.*
-import io.bazel.ruleskotlin.workers.compilers.jvm.Metas
+import io.bazel.ruleskotlin.workers.model.Metas
 import io.bazel.ruleskotlin.workers.compilers.jvm.utils.KotlinCompilerOutputProcessor
-
-
-import java.util.ArrayList
-import java.util.Collections
+import io.bazel.ruleskotlin.workers.model.CompileDirectories
+import io.bazel.ruleskotlin.workers.model.Flags
+import java.util.*
 
 /**
  * Either compiles to a jar directly or when performing mixed-mode-compilation compiles to a temp directory first.
@@ -44,21 +44,29 @@
                 Flags.KOTLIN_LANGUAGE_VERSION,
                 Flags.KOTLIN_JVM_TARGET)
 
-        /**
-         * Evaluate the compilation context and add Metadata to the ctx if needed.
-         *
-         * @return The args to pass to the kotlin compile class.
-         */
-        private fun setupCompileContext(ctx: Context): Array<String> {
-            val args = ArrayList<String>()
-            Collections.addAll(args, "-d", Metas.CLASSES_DIRECTORY.mustGet(ctx).toString())
-            ctx.of(*COMPILE_MAPPED_FLAGS).forEach { field, arg ->
-                args.add(field.kotlinFlag!!); args.add(arg)
+        val Result = CompileResult.Meta("kotlin_compile_result")
+    }
 
-            }
-            args.addAll(Metas.ALL_SOURCES.mustGet(ctx))
-            return args.toTypedArray()
+    /**
+     * Evaluate the compilation context and add Metadata to the ctx if needed.
+     *
+     * @return The args to pass to the kotlin compile class.
+     */
+    private fun setupCompileContext(ctx: Context): Array<String> {
+        val args = mutableListOf<String>()
+        val compileDirectories = CompileDirectories[ctx]
+
+        ctx.copyOfFlags(*COMPILE_MAPPED_FLAGS).forEach { field, arg ->
+            args.add(field.kotlinFlag!!); args.add(arg)
         }
+
+        Collections.addAll(args, "-kotlin-home", KotlinToolchain.KOTLIN_HOME.toString())
+        Collections.addAll(args, "-d", compileDirectories.classes)
+
+
+        args.addAll(Metas.ALL_SOURCES.mustGet(ctx))
+        println(args.joinToString(" "))
+        return args.toTypedArray()
     }
 
     override fun invoke(ctx: Context): Int {
@@ -77,10 +85,10 @@
             // 3 is the script execution error
 
             // give javac a chance to process the java sources.
-            Metas.KOTLINC_RESULT.bind(ctx, CompileResult.deferred(exitCode) { _ ->
+            Result[ctx] = CompileResult.deferred(exitCode) { _ ->
                 outputProcessor.process()
                 exitCode
-            })
+            }
             return 0
         } else {
             outputProcessor.process()
diff --git a/kotlin/workers/src/io/bazel/ruleskotlin/workers/compilers/jvm/actions/ProcessCompileResult.kt b/kotlin/workers/src/io/bazel/ruleskotlin/workers/compilers/jvm/actions/ProcessCompileResult.kt
index 01fbf3b..5165880 100644
--- a/kotlin/workers/src/io/bazel/ruleskotlin/workers/compilers/jvm/actions/ProcessCompileResult.kt
+++ b/kotlin/workers/src/io/bazel/ruleskotlin/workers/compilers/jvm/actions/ProcessCompileResult.kt
@@ -19,7 +19,6 @@
 import io.bazel.ruleskotlin.workers.BuildAction
 import io.bazel.ruleskotlin.workers.Context
 import io.bazel.ruleskotlin.workers.KotlinToolchain
-import io.bazel.ruleskotlin.workers.compilers.jvm.Metas
 
 
 /**
@@ -29,8 +28,8 @@
  */
 class ProcessCompileResult(toolchain: KotlinToolchain) : BuildAction("render class compile output", toolchain) {
     override fun invoke(ctx: Context): Int {
-        val kotlincResult = Metas.KOTLINC_RESULT.mustGet(ctx)
-        val javacResult = Metas.JAVAC_RESULT[ctx]
+        val kotlincResult = KotlinMainCompile.Result.mustGet(ctx)
+        val javacResult = JavaMainCompile.Result[ctx]
 
         return if (javacResult == null) {
             kotlincResult.render(ctx)
diff --git a/kotlin/workers/src/io/bazel/ruleskotlin/workers/model/CompileDirectories.kt b/kotlin/workers/src/io/bazel/ruleskotlin/workers/model/CompileDirectories.kt
new file mode 100644
index 0000000..c547f87
--- /dev/null
+++ b/kotlin/workers/src/io/bazel/ruleskotlin/workers/model/CompileDirectories.kt
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2018 The Bazel Authors. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package io.bazel.ruleskotlin.workers.model
+
+import io.bazel.ruleskotlin.workers.MandatoryMeta
+import java.nio.file.Files
+import java.nio.file.Path
+
+/**
+ * Temporary output directories used durng compilation.
+ */
+class CompileDirectories(private val outputBase: Path) {
+    val classes by lazy { dir("_classes") }
+
+    private fun dir(component: String) = Files.createDirectories(outputBase.resolve(component)).toString()
+
+    companion object: MandatoryMeta<CompileDirectories>
+}
\ No newline at end of file
diff --git a/kotlin/workers/src/io/bazel/ruleskotlin/workers/model/Flags.kt b/kotlin/workers/src/io/bazel/ruleskotlin/workers/model/Flags.kt
new file mode 100644
index 0000000..de2a9f1
--- /dev/null
+++ b/kotlin/workers/src/io/bazel/ruleskotlin/workers/model/Flags.kt
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2018 The Bazel Authors. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package io.bazel.ruleskotlin.workers.model
+
+import io.bazel.ruleskotlin.workers.Flag
+
+/**
+ * The flags supported by the worker.
+ */
+object Flags {
+    val LABEL = Flag.Mandatory(JavaBuilderFlags.TARGET_LABEL.flag)
+    val OUTPUT_CLASSJAR = Flag.Mandatory(JavaBuilderFlags.OUTPUT.flag)
+    val SOURCES = Flag.Mandatory(JavaBuilderFlags.SOURCES.flag)
+    val CLASSPATH = Flag.Mandatory(JavaBuilderFlags.CLASSPATH.flag, "-cp")
+
+    val OUTPUT_JDEPS = Flag.Mandatory("--output_jdeps")
+    val COMPILER_OUTPUT_BASE = Flag.Mandatory("--compiler_output_base")
+
+    val KOTLIN_API_VERSION = Flag.Optional("--kotlin_api_version", "-api-version")
+    val KOTLIN_LANGUAGE_VERSION = Flag.Optional("--kotlin_language_version", "-language-version")
+    val KOTLIN_JVM_TARGET = Flag.Optional("--kotlin_jvm_target", "-jvm-target")
+}
\ No newline at end of file
diff --git a/kotlin/workers/src/io/bazel/ruleskotlin/workers/JavaBuilderFlags.kt b/kotlin/workers/src/io/bazel/ruleskotlin/workers/model/JavaBuilderFlags.kt
similarity index 92%
rename from kotlin/workers/src/io/bazel/ruleskotlin/workers/JavaBuilderFlags.kt
rename to kotlin/workers/src/io/bazel/ruleskotlin/workers/model/JavaBuilderFlags.kt
index 3287a27..54cfebf 100644
--- a/kotlin/workers/src/io/bazel/ruleskotlin/workers/JavaBuilderFlags.kt
+++ b/kotlin/workers/src/io/bazel/ruleskotlin/workers/model/JavaBuilderFlags.kt
@@ -13,12 +13,12 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package io.bazel.ruleskotlin.workers
+package io.bazel.ruleskotlin.workers.model
 
 /**
- * Flags used by the java builder.
+ * Declares the flags used by the java builder.
  */
-enum class JavaBuilderFlags(val flag: String) {
+internal enum class JavaBuilderFlags(val flag: String) {
     TARGET_LABEL("--target_label"),
     CLASSPATH("--classpath"),
     JAVAC_OPTS("--javacopts"),
diff --git a/kotlin/workers/src/io/bazel/ruleskotlin/workers/model/Metas.kt b/kotlin/workers/src/io/bazel/ruleskotlin/workers/model/Metas.kt
new file mode 100644
index 0000000..0951c39
--- /dev/null
+++ b/kotlin/workers/src/io/bazel/ruleskotlin/workers/model/Metas.kt
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2018 The Bazel Authors. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package io.bazel.ruleskotlin.workers.model
+
+import io.bazel.ruleskotlin.workers.MandatoryMeta
+import io.bazel.ruleskotlin.workers.Meta
+
+/**
+ * Listin of Meta keys that don't make sense as companion objects.
+ */
+object Metas {
+    // mandatory: the package part of the label.
+    val PKG = Meta<String>("package")
+    // mandatory: The target part of the label.
+    val TARGET = Meta<String>("target")
+
+    // mandatory:  If this is non empty then it is a mixed mode operation.
+    val JAVA_SOURCES = MandatoryMeta<List<String>>("java_sources")
+
+    // mandatory:
+    val ALL_SOURCES = MandatoryMeta<List<String>>("all_sources")
+}
\ No newline at end of file
diff --git a/tests/smoke/BUILD b/tests/smoke/BUILD
index 99a28d7..5233b03 100644
--- a/tests/smoke/BUILD
+++ b/tests/smoke/BUILD
@@ -78,7 +78,8 @@
     name = "propagation_ct_consumer_fail_on_runtime",
     main_class = "testing.CompileTimeDependent",
     srcs = ["propagation/CompileTimeDependent.java"],
-    deps = [":propagation_test_runtime_lib"]
+    deps = [":propagation_test_runtime_lib"],
+    tags = ["manual"]
 )
 
 java_binary(