Fix broken compilation with BTAPI and add tests (#1663)

- Add missing dependency with `org.jetbrains.kotlin.daemon.common` package required for BTAPI infrastructure
- Pass the sources and the destination directory to the compiler as dedicated parameters.
- Introduce the `KotlinCompiler` interface defining the exec() function contract.
- Added `KotlinBuilderJvmBtaTest` and `useBuildToolsApi()` flag to test framework to enable compilation with BTAPI in tests.
  In `KotlinAbstractTestBuilder` ensure that CompilationTaskInfo instance returned by CompilationTaskContext is the same as the JvmCompilationTask uses.
diff --git a/src/main/kotlin/BUILD.release.bazel b/src/main/kotlin/BUILD.release.bazel
index 4fadd45..84d9732 100644
--- a/src/main/kotlin/BUILD.release.bazel
+++ b/src/main/kotlin/BUILD.release.bazel
@@ -54,6 +54,7 @@
         "//kotlin/compiler:jvm-abi-gen",
         "//kotlin/compiler:kotlin-annotation-processing",
         "//kotlin/compiler:kotlin-compiler",
+        "//kotlin/compiler:kotlin-daemon-client",
         "//kotlin/compiler:kotlin-reflect",
         "//src/main/kotlin/io/bazel/kotlin/compiler",
         "@com_github_jetbrains_kotlin//:home",
@@ -71,6 +72,7 @@
         "-D@com_github_jetbrains_kotlin...build-tools-api=$(rlocationpath @kotlin_build_tools_api//file)",
         "-D@com_github_jetbrains_kotlin...jvm-abi-gen=$(rlocationpath //kotlin/compiler:jvm-abi-gen)",
         "-D@com_github_jetbrains_kotlin...kotlin-compiler=$(rlocationpath //kotlin/compiler:kotlin-compiler)",
+        "-D@com_github_jetbrains_kotlin...kotlin-daemon-client=$(rlocationpath //kotlin/compiler:kotlin-daemon-client)",
         "-D@com_github_jetbrains_kotlin...kapt=$(rlocationpath //kotlin/compiler:kotlin-annotation-processing)",
         "-D@rules_kotlin...jdeps-gen=$(rlocationpath //src/main/kotlin:jdeps-gen)",
         "-D@rules_kotlin...skip-code-gen=$(rlocationpath //src/main/kotlin:skip-code-gen)",
diff --git a/src/main/kotlin/io/bazel/kotlin/builder/cmd/BUILD.bazel b/src/main/kotlin/io/bazel/kotlin/builder/cmd/BUILD.bazel
index 1b46e99..93e2ed2 100644
--- a/src/main/kotlin/io/bazel/kotlin/builder/cmd/BUILD.bazel
+++ b/src/main/kotlin/io/bazel/kotlin/builder/cmd/BUILD.bazel
@@ -18,6 +18,7 @@
         "//kotlin/compiler:jvm-abi-gen",
         "//kotlin/compiler:kotlin-annotation-processing",
         "//kotlin/compiler:kotlin-compiler",
+        "//kotlin/compiler:kotlin-daemon-client",
         "//src/main/kotlin:jdeps-gen",
         "//src/main/kotlin:skip-code-gen",
         "//src/main/kotlin/io/bazel/kotlin/compiler:compiler.jar",
@@ -37,6 +38,7 @@
         "-D@com_github_jetbrains_kotlin...build-tools-api=$(rlocationpath @kotlin_build_tools_api//file)",
         "-D@com_github_jetbrains_kotlin...jvm-abi-gen=$(rlocationpath //kotlin/compiler:jvm-abi-gen)",
         "-D@com_github_jetbrains_kotlin...kotlin-compiler=$(rlocationpath //kotlin/compiler:kotlin-compiler)",
+        "-D@com_github_jetbrains_kotlin...kotlin-daemon-client=$(rlocationpath //kotlin/compiler:kotlin-daemon-client)",
         "-D@com_github_jetbrains_kotlin...kapt=$(rlocationpath //kotlin/compiler:kotlin-annotation-processing)",
         "-D@rules_kotlin...jdeps-gen=$(rlocationpath //src/main/kotlin:jdeps-gen)",
         "-D@rules_kotlin...skip-code-gen=$(rlocationpath //src/main/kotlin:skip-code-gen)",
diff --git a/src/main/kotlin/io/bazel/kotlin/builder/tasks/jvm/CompilationTask.kt b/src/main/kotlin/io/bazel/kotlin/builder/tasks/jvm/CompilationTask.kt
index 03d7fea..0a5fb77 100644
--- a/src/main/kotlin/io/bazel/kotlin/builder/tasks/jvm/CompilationTask.kt
+++ b/src/main/kotlin/io/bazel/kotlin/builder/tasks/jvm/CompilationTask.kt
@@ -54,8 +54,7 @@
   CompilationArgs()
     .absolutePaths(info.friendPathsList) {
       "-Xfriend-paths=${it.joinToString(X_FRIENDS_PATH_SEPARATOR)}"
-    }.flag("-d", directories.classes)
-    .values(info.passthroughFlagsList)
+    }.values(info.passthroughFlagsList)
 
 fun JvmCompilationTask.baseArgs(overrides: Map<String, String> = emptyMap()): CompilationArgs {
   val classpath =
@@ -241,6 +240,7 @@
   compiler: KotlinToolchain.KotlincInvoker,
 ): JvmCompilationTask {
   return context.execute("kapt (${inputs.processorsList.joinToString(", ")})") {
+    val sources = (inputs.kotlinSourcesList + inputs.javaSourcesList).toTypedArray()
     baseArgs()
       .plus(
         plugins(
@@ -249,14 +249,12 @@
         ),
       ).plus(
         kaptArgs(context, plugins, "stubsAndApt"),
-      ).flag("-d", directories.generatedClasses)
-      .values(inputs.kotlinSourcesList)
-      .values(inputs.javaSourcesList)
-      .list()
+      ).list()
       .let { args ->
         context.executeCompilerTask(
-          args,
-          compiler::compile,
+          { out ->
+            compiler.compile(args.toTypedArray(), sources, directories.generatedClasses, out)
+          },
           printOnSuccess = context.whenTracing { true } == true,
         )
       }.let { outputLines ->
@@ -387,23 +385,25 @@
     writeJdeps(outputs.jdeps, emptyJdeps(info.label))
     return emptyList()
   } else {
+    val sources = (inputs.javaSourcesList + inputs.kotlinSourcesList).toTypedArray()
     return (
       args +
         plugins(
           options = inputs.compilerPluginOptionsList,
           classpath = inputs.compilerPluginClasspathList,
         )
-    ).values(inputs.javaSourcesList)
-      .values(inputs.kotlinSourcesList)
-      .flag("-d", directories.classes)
-      .list()
+    ).list()
       .let {
         context.whenTracing {
           context.printLines("compileKotlin arguments:\n", it)
         }
         return@let context
-          .executeCompilerTask(it, compiler::compile, printOnFail = printOnFail)
-          .also {
+          .executeCompilerTask(
+            { out ->
+              compiler.compile(it.toTypedArray(), sources, directories.classes, out)
+            },
+            printOnFail = printOnFail,
+          ).also {
             context.whenTracing {
               printLines(
                 "kotlinc Files Created:",
diff --git a/src/main/kotlin/io/bazel/kotlin/builder/toolchain/CompilationTaskContext.kt b/src/main/kotlin/io/bazel/kotlin/builder/toolchain/CompilationTaskContext.kt
index 3b0fab2..7e02cdc 100644
--- a/src/main/kotlin/io/bazel/kotlin/builder/toolchain/CompilationTaskContext.kt
+++ b/src/main/kotlin/io/bazel/kotlin/builder/toolchain/CompilationTaskContext.kt
@@ -112,20 +112,18 @@
    * Execute a compilation task.
    *
    * @throws CompilationStatusException if the compiler returns a status of anything but zero.
-   * @param args the compiler command line switches
    * @param printOnFail if this is true the output will be printed if the task fails else the caller is responsible
    *  for logging it by catching the [CompilationStatusException] exception.
-   * @param compile the compilation method.
+   * @param compile the compilation method reporting its output to the given stream.
    */
   fun executeCompilerTask(
-    args: List<String>,
-    compile: (Array<String>, PrintStream) -> Int,
+    compile: (PrintStream) -> Int,
     printOnFail: Boolean = true,
     printOnSuccess: Boolean = true,
   ): List<String> {
     val outputStream = ByteArrayOutputStream()
     val ps = PrintStream(outputStream)
-    val result = compile(args.toTypedArray(), ps)
+    val result = compile(ps)
     val output =
       ByteArrayInputStream(outputStream.toByteArray())
         .bufferedReader()
diff --git a/src/main/kotlin/io/bazel/kotlin/builder/toolchain/KotlinToolchain.kt b/src/main/kotlin/io/bazel/kotlin/builder/toolchain/KotlinToolchain.kt
index 2704d16..c04ffdc 100644
--- a/src/main/kotlin/io/bazel/kotlin/builder/toolchain/KotlinToolchain.kt
+++ b/src/main/kotlin/io/bazel/kotlin/builder/toolchain/KotlinToolchain.kt
@@ -20,7 +20,8 @@
 import io.bazel.kotlin.builder.utils.verified
 import java.io.File
 import java.io.PrintStream
-import java.lang.reflect.Method
+import java.lang.invoke.MethodHandle
+import java.lang.invoke.MethodHandles
 import java.net.URLClassLoader
 
 class KotlinToolchain private constructor(
@@ -73,6 +74,13 @@
         ).toPath()
     }
 
+    private val KOTLIN_DAEMON_CLIENT by lazy {
+      BazelRunFiles
+        .resolveVerifiedFromProperty(
+          "@com_github_jetbrains_kotlin...kotlin-daemon-client",
+        ).toPath()
+    }
+
     private val KOTLINX_SERIALIZATION_CORE_JVM by lazy {
       BazelRunFiles
         .resolveVerifiedFromProperty(
@@ -108,12 +116,11 @@
         ).toPath()
     }
 
-    internal val NO_ARGS = arrayOf<Any>()
-
     @JvmStatic
     fun createToolchain(): KotlinToolchain =
       createToolchain(
         KOTLINC.verified().absoluteFile,
+        KOTLIN_DAEMON_CLIENT.verified().absoluteFile,
         BUILD_TOOLS_IMPL.verified().absoluteFile,
         BUILD_TOOLS_API.verified().absoluteFile,
         COMPILER.verified().absoluteFile,
@@ -129,6 +136,7 @@
     @JvmStatic
     fun createToolchain(
       kotlinc: File,
+      kotlinDaemonClient: File,
       buildTools: File,
       buildToolsApi: File,
       compiler: File,
@@ -143,6 +151,7 @@
       KotlinToolchain(
         listOf(
           kotlinc,
+          kotlinDaemonClient,
           compiler,
           buildTools,
           buildToolsApi,
@@ -193,18 +202,23 @@
     clazz: String,
   ) {
     private val compiler: Any
-    private val execMethod: Method
-    private val getCodeMethod: Method
+    private val execHandle: MethodHandle
+    private val getCodeHandle: MethodHandle
 
     init {
       val compilerClass = toolchain.classLoader.loadClass(clazz)
+      val compilerInterface =
+        toolchain.classLoader.loadClass("io.bazel.kotlin.compiler.KotlinCompiler")
       val exitCodeClass =
         toolchain.classLoader.loadClass("org.jetbrains.kotlin.cli.common.ExitCode")
 
-      compiler = compilerClass.getConstructor().newInstance()
-      execMethod =
-        compilerClass.getMethod("exec", PrintStream::class.java, Array<String>::class.java)
-      getCodeMethod = exitCodeClass.getMethod("getCode")
+      compiler = compilerInterface.cast(compilerClass.getConstructor().newInstance())
+
+      // The interface is the source of truth for the exec method signature.
+      val execMethod = compilerInterface.declaredMethods.single { it.name == "exec" }
+      val lookup = MethodHandles.lookup()
+      execHandle = lookup.unreflect(execMethod)
+      getCodeHandle = lookup.unreflect(exitCodeClass.getMethod("getCode"))
     }
 
     // Kotlin error codes:
@@ -213,10 +227,12 @@
     // 3 is the script execution error
     fun compile(
       args: Array<String>,
+      sources: Array<String>,
+      destination: String,
       out: PrintStream,
     ): Int {
-      val exitCodeInstance = execMethod.invoke(compiler, out, args)
-      return getCodeMethod.invoke(exitCodeInstance, *NO_ARGS) as Int
+      val exitCode = execHandle.invoke(compiler, out, args, sources, destination)
+      return getCodeHandle.invoke(exitCode) as Int
     }
   }
 
diff --git a/src/main/kotlin/io/bazel/kotlin/compiler/BazelK2JVMCompiler.kt b/src/main/kotlin/io/bazel/kotlin/compiler/BazelK2JVMCompiler.kt
index f42c2da..47844c1 100644
--- a/src/main/kotlin/io/bazel/kotlin/compiler/BazelK2JVMCompiler.kt
+++ b/src/main/kotlin/io/bazel/kotlin/compiler/BazelK2JVMCompiler.kt
@@ -20,16 +20,21 @@
 import org.jetbrains.kotlin.cli.common.messages.PrintingMessageCollector
 import org.jetbrains.kotlin.cli.jvm.K2JVMCompiler
 import org.jetbrains.kotlin.config.Services
+import java.io.PrintStream
 
 @Suppress("unused")
-class BazelK2JVMCompiler {
-  fun exec(
-    errStream: java.io.PrintStream,
-    vararg args: String,
+class BazelK2JVMCompiler : KotlinCompiler {
+  override fun exec(
+    errStream: PrintStream,
+    args: Array<String>,
+    sources: Array<String>,
+    destination: String,
   ): ExitCode {
     System.setProperty("zip.handler.uses.crc.instead.of.timestamp", "true")
     val delegate: K2JVMCompiler = K2JVMCompiler()
-    val arguments = delegate.createArguments().also { delegate.parseArguments(args, it) }
+
+    val cliArgs = args + arrayOf("-d", destination) + sources
+    val arguments = delegate.createArguments().also { delegate.parseArguments(cliArgs, it) }
     val collector =
       PrintingMessageCollector(errStream, MessageRenderer.PLAIN_RELATIVE_PATHS, arguments.verbose)
     return delegate.exec(collector, Services.EMPTY, arguments)
diff --git a/src/main/kotlin/io/bazel/kotlin/compiler/BuildToolsAPICompiler.kt b/src/main/kotlin/io/bazel/kotlin/compiler/BuildToolsAPICompiler.kt
index ced1579..aa14ce7 100644
--- a/src/main/kotlin/io/bazel/kotlin/compiler/BuildToolsAPICompiler.kt
+++ b/src/main/kotlin/io/bazel/kotlin/compiler/BuildToolsAPICompiler.kt
@@ -21,27 +21,28 @@
 import org.jetbrains.kotlin.buildtools.api.getToolchain
 import org.jetbrains.kotlin.buildtools.api.jvm.JvmPlatformToolchain
 import org.jetbrains.kotlin.cli.common.ExitCode
-import java.nio.file.Path
+import java.io.PrintStream
+import java.nio.file.Paths
 
 @Suppress("unused")
-class BuildToolsAPICompiler {
+class BuildToolsAPICompiler : KotlinCompiler {
   @OptIn(ExperimentalBuildToolsApi::class)
-  fun exec(
-    errStream: java.io.PrintStream,
-    vararg args: String,
+  override fun exec(
+    errStream: PrintStream,
+    args: Array<String>,
+    sources: Array<String>,
+    destination: String,
   ): ExitCode {
     System.setProperty("zip.handler.uses.crc.instead.of.timestamp", "true")
 
     val kotlinToolchains = KotlinToolchains.loadImplementation(this.javaClass.classLoader!!)
 
-    // Create compilation operation with empty sources and dummy destination
-    // (the actual sources/destination will be set via applyArgumentStrings)
     val operationBuilder =
       kotlinToolchains
         .getToolchain<JvmPlatformToolchain>()
         .jvmCompilationOperationBuilder(
-          emptyList(),
-          Path.of("."),
+          sources.map { Paths.get(it) },
+          Paths.get(destination),
         )
 
     // Apply raw CLI arguments - this parses the args and sets all compiler options
diff --git a/src/main/kotlin/io/bazel/kotlin/compiler/KotlinCompiler.kt b/src/main/kotlin/io/bazel/kotlin/compiler/KotlinCompiler.kt
new file mode 100644
index 0000000..704fbdf
--- /dev/null
+++ b/src/main/kotlin/io/bazel/kotlin/compiler/KotlinCompiler.kt
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2026 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.compiler
+
+import org.jetbrains.kotlin.cli.common.ExitCode
+import java.io.PrintStream
+
+/**
+ * The compilation contract between the worker and the compiler implementations loaded into the
+ * compiler classloader and invoked reflectively.
+ */
+interface KotlinCompiler {
+  fun exec(
+    errStream: PrintStream,
+    args: Array<String>,
+    sources: Array<String>,
+    destination: String,
+  ): ExitCode
+}
diff --git a/src/test/kotlin/io/bazel/kotlin/builder/KotlinAbstractTestBuilder.java b/src/test/kotlin/io/bazel/kotlin/builder/KotlinAbstractTestBuilder.java
index 6300f77..a5e3272 100644
--- a/src/test/kotlin/io/bazel/kotlin/builder/KotlinAbstractTestBuilder.java
+++ b/src/test/kotlin/io/bazel/kotlin/builder/KotlinAbstractTestBuilder.java
@@ -20,6 +20,7 @@
 import io.bazel.kotlin.builder.toolchain.CompilationTaskContext;
 import io.bazel.kotlin.builder.toolchain.KotlinToolchain;
 import io.bazel.kotlin.model.CompilationTaskInfo;
+import io.bazel.kotlin.model.JvmCompilationTaskOrBuilder;
 import io.bazel.kotlin.model.KotlinToolchainInfo;
 import io.bazel.kotlin.model.Platform;
 import io.bazel.kotlin.model.RuleKind;
@@ -44,7 +45,7 @@
 import static java.util.Collections.unmodifiableList;
 import static java.util.stream.Collectors.toList;
 
-abstract class KotlinAbstractTestBuilder<T> {
+abstract class KotlinAbstractTestBuilder<T extends JvmCompilationTaskOrBuilder> {
     private static final Path BAZEL_TEST_DIR =
             FileSystems.getDefault().getPath(System.getenv("TEST_TMPDIR"));
 
@@ -152,15 +153,13 @@
     }
 
     final <R> R runCompileTask(BiFunction<CompilationTaskContext, T, R> operation) {
-        T task = buildTask();
-        return runCompileTask(infoBuilder.build(), task, (ctx, t) -> operation.apply(ctx, task));
+        return runCompileTask(buildTask(), operation);
     }
 
-    private <R> R runCompileTask(
-            CompilationTaskInfo info, T task, BiFunction<CompilationTaskContext, T, R> operation) {
+    private <R> R runCompileTask(T task, BiFunction<CompilationTaskContext, T, R> operation) {
         ByteArrayOutputStream out = new ByteArrayOutputStream();
         try (PrintStream outputStream = new PrintStream(out)) {
-            return operation.apply(new CompilationTaskContext(info, outputStream,
+            return operation.apply(new CompilationTaskContext(task.getInfo(), outputStream,
                     instanceRoot().toAbsolutePath() + File.separator), task);
         } finally {
             outLines = unmodifiableList(
@@ -229,6 +228,7 @@
     static KotlinToolchain toolchainForTest() {
         return KotlinToolchain.createToolchain(
                 new File(Deps.Dep.fromLabel("//kotlin/compiler:kotlin-compiler").singleCompileJar()),
+                new File(Deps.Dep.fromLabel("//kotlin/compiler:kotlin-daemon-client").singleCompileJar()),
                 new File(Deps.Dep.fromLabel("@kotlin_build_tools_impl//file").singleCompileJar()),
                 new File(Deps.Dep.fromLabel("@kotlin_build_tools_api//file").singleCompileJar()),
                 new File(Deps.Dep.fromLabel("//src/main/kotlin/io/bazel/kotlin/compiler:compiler.jar").singleCompileJar()),
diff --git a/src/test/kotlin/io/bazel/kotlin/builder/KotlinJvmTestBuilder.java b/src/test/kotlin/io/bazel/kotlin/builder/KotlinJvmTestBuilder.java
index 08f91bf..f17e91b 100644
--- a/src/test/kotlin/io/bazel/kotlin/builder/KotlinJvmTestBuilder.java
+++ b/src/test/kotlin/io/bazel/kotlin/builder/KotlinJvmTestBuilder.java
@@ -132,13 +132,13 @@
                     );
 
                     return Dep.builder()
-                            .label(taskBuilder.getInfo().getLabel())
+                            .label(task.getInfo().getLabel())
                             .compileJars(ImmutableList.of(
                                     outputs.getAbijar().isEmpty() ? outputs.getJar() : outputs.getAbijar()
                             ))
                             .jdeps(outputs.getJdeps())
-                            .runtimeDeps(ImmutableList.copyOf(taskBuilder.getInputs().getClasspathList()))
-                            .sourceJar(taskBuilder.getOutputs().getSrcjar())
+                            .runtimeDeps(ImmutableList.copyOf(task.getInputs().getClasspathList()))
+                            .sourceJar(outputs.getSrcjar())
                             .build();
                 });
     }
@@ -268,5 +268,10 @@
                     .setLanguageVersion("2.0");
             return this;
         }
+
+        public TaskBuilder useBuildToolsApi() {
+            taskBuilder.getInfoBuilder().setBuildToolsApi(true);
+            return this;
+        }
     }
 }
diff --git a/src/test/kotlin/io/bazel/kotlin/builder/tasks/BUILD.bazel b/src/test/kotlin/io/bazel/kotlin/builder/tasks/BUILD.bazel
index c1af861..c5bd529 100644
--- a/src/test/kotlin/io/bazel/kotlin/builder/tasks/BUILD.bazel
+++ b/src/test/kotlin/io/bazel/kotlin/builder/tasks/BUILD.bazel
@@ -61,6 +61,11 @@
 )
 
 kt_rules_test(
+    name = "KotlinBuilderJvmBtaTest",
+    srcs = ["jvm/KotlinBuilderJvmBtaTest.java"],
+)
+
+kt_rules_test(
     name = "KotlinBuilderJvmJdepsTest",
     size = "large",
     srcs = ["jvm/KotlinBuilderJvmJdepsTest.kt"],
@@ -139,6 +144,7 @@
         ":KotlinBuilderBuildTest",
         ":KotlinBuilderJvmAbiTest",
         ":KotlinBuilderJvmBasicTest",
+        ":KotlinBuilderJvmBtaTest",
         ":KotlinBuilderJvmCoverageTest",
         ":KotlinBuilderJvmJdepsTest",
         ":KotlinBuilderJvmKaptTest",
diff --git a/src/test/kotlin/io/bazel/kotlin/builder/tasks/jvm/KotlinBuilderBuildTest.kt b/src/test/kotlin/io/bazel/kotlin/builder/tasks/jvm/KotlinBuilderBuildTest.kt
index 550be58..95b738a 100644
--- a/src/test/kotlin/io/bazel/kotlin/builder/tasks/jvm/KotlinBuilderBuildTest.kt
+++ b/src/test/kotlin/io/bazel/kotlin/builder/tasks/jvm/KotlinBuilderBuildTest.kt
@@ -86,6 +86,7 @@
     val toolchain =
       KotlinToolchain.createToolchain(
         File(Deps.Dep.fromLabel("//kotlin/compiler:kotlin-compiler").singleCompileJar()),
+        File(Deps.Dep.fromLabel("//kotlin/compiler:kotlin-daemon-client").singleCompileJar()),
         File(Deps.Dep.fromLabel("@kotlin_build_tools_impl//file").singleCompileJar()),
         File(Deps.Dep.fromLabel("@kotlin_build_tools_api//file").singleCompileJar()),
         File(
diff --git a/src/test/kotlin/io/bazel/kotlin/builder/tasks/jvm/KotlinBuilderJvmBtaTest.java b/src/test/kotlin/io/bazel/kotlin/builder/tasks/jvm/KotlinBuilderJvmBtaTest.java
new file mode 100644
index 0000000..0953e31
--- /dev/null
+++ b/src/test/kotlin/io/bazel/kotlin/builder/tasks/jvm/KotlinBuilderJvmBtaTest.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright 2026 The Bazel Authors. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package io.bazel.kotlin.builder.tasks.jvm;
+
+import io.bazel.kotlin.builder.DirectoryType;
+import io.bazel.kotlin.builder.KotlinJvmTestBuilder;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/** Compiles through the Build Tools API path ({@code --build_tools_api=true}). */
+@RunWith(JUnit4.class)
+public class KotlinBuilderJvmBtaTest {
+    private static final KotlinJvmTestBuilder ctx = new KotlinJvmTestBuilder();
+
+    @Test
+    public void testSimpleKotlinCompile() {
+        ctx.runCompileTask(
+                c -> {
+                    c.useBuildToolsApi();
+                    c.compileKotlin();
+                    c.addSource("AClass.kt", "package something;" + "class AClass{}");
+                    c.outputJar();
+                    c.outputJdeps();
+                });
+        ctx.assertFilesExist(DirectoryType.CLASSES, "something/AClass.class");
+    }
+
+    @Test
+    public void testMixedModeCompile() {
+        // The Kotlin class references the Java class, so the compilation is only successful, if the .java source reaches the compiler's source list.
+        ctx.runCompileTask(
+                c -> {
+                    c.useBuildToolsApi();
+                    c.compileKotlin();
+                    c.addSource(
+                            "AClass.kt",
+                            "package something;" + "class AClass{ val other = AnotherClass() }");
+                    c.addSource(
+                            "AnotherClass.java",
+                            "package something;",
+                            "",
+                            "public class AnotherClass{}");
+                    c.outputJar();
+                    c.outputJdeps();
+                });
+        ctx.assertFilesExist(DirectoryType.CLASSES, "something/AClass.class");
+    }
+}
diff --git a/src/test/kotlin/io/bazel/kotlin/defs.bzl b/src/test/kotlin/io/bazel/kotlin/defs.bzl
index 2d55c0d..31b0aba 100644
--- a/src/test/kotlin/io/bazel/kotlin/defs.bzl
+++ b/src/test/kotlin/io/bazel/kotlin/defs.bzl
@@ -37,6 +37,7 @@
         "//kotlin/compiler:annotations",
         "//kotlin/compiler:jvm-abi-gen",
         "//kotlin/compiler:kotlin-compiler",
+        "//kotlin/compiler:kotlin-daemon-client",
         "//kotlin/compiler:kotlin-stdlib",
         "//kotlin/compiler:kotlin-stdlib-jdk7",
         "//kotlin/compiler:kotlin-stdlib-jdk8",