add builder tests that validate errors are rendered relative to workspace.
diff --git a/src/main/kotlin/io/bazel/kotlin/builder/tasks/jvm/JavaCompiler.kt b/src/main/kotlin/io/bazel/kotlin/builder/tasks/jvm/JavaCompiler.kt index b52a222..c7385bb 100644 --- a/src/main/kotlin/io/bazel/kotlin/builder/tasks/jvm/JavaCompiler.kt +++ b/src/main/kotlin/io/bazel/kotlin/builder/tasks/jvm/JavaCompiler.kt
@@ -15,11 +15,12 @@ */ package io.bazel.kotlin.builder.tasks.jvm -import io.bazel.kotlin.builder.toolchain.CompilationStatusException import io.bazel.kotlin.builder.toolchain.KotlinToolchain +import io.bazel.kotlin.builder.utils.CompilationTaskContext import io.bazel.kotlin.builder.utils.addAll import io.bazel.kotlin.builder.utils.joinedClasspath import io.bazel.kotlin.model.JvmCompilationTask +import java.io.PrintWriter import javax.inject.Inject import javax.inject.Singleton @@ -27,14 +28,14 @@ internal class JavaCompiler @Inject constructor( private val javacInvoker: KotlinToolchain.JavacInvoker ) { - fun compile(command: JvmCompilationTask) { + fun compile(context: CompilationTaskContext, command: JvmCompilationTask) { val i = command.inputs val d = command.directories if (i.javaSourcesList.isNotEmpty()) { val args = mutableListOf( "-cp", "${d.classes}/:${d.temp}/:${i.joinedClasspath}", "-d", d.classes - ).let { + ).also { it.addAll( // Kotlin takes care of annotation processing. "-proc:none", @@ -44,9 +45,10 @@ "-target", command.info.toolchainInfo.jvm.jvmTarget ) it.addAll(i.javaSourcesList) - it.toTypedArray() } - javacInvoker.compile(args).takeIf { it != 0 }?.also { throw CompilationStatusException("javac failed", it) } + context.executeCompilerTask(args, { a, pw -> + javacInvoker.compile(a, PrintWriter(pw)) + }) } } } \ No newline at end of file
diff --git a/src/main/kotlin/io/bazel/kotlin/builder/tasks/jvm/KotlinJvmTaskExecutor.kt b/src/main/kotlin/io/bazel/kotlin/builder/tasks/jvm/KotlinJvmTaskExecutor.kt index 10bf26b..7debb8d 100644 --- a/src/main/kotlin/io/bazel/kotlin/builder/tasks/jvm/KotlinJvmTaskExecutor.kt +++ b/src/main/kotlin/io/bazel/kotlin/builder/tasks/jvm/KotlinJvmTaskExecutor.kt
@@ -36,16 +36,11 @@ private val jDepsGenerator: JDepsGenerator ) { fun execute(context: CompilationTaskContext, task: JvmCompilationTask) { - // TODO fix error handling - try { - val preprocessedTask = task.preProcessingSteps(context) - context.execute("compile classes") { preprocessedTask.compileAll(context) } - context.execute("create jar") { preprocessedTask.createOutputJar() } - context.execute("produce src jar") { preprocessedTask.produceSourceJar() } - context.execute("generate jdeps") { jDepsGenerator.generateJDeps(preprocessedTask) } - } catch (ex: Throwable) { - throw RuntimeException(ex) - } + val preprocessedTask = task.preProcessingSteps(context) + context.execute("compile classes") { preprocessedTask.compileAll(context) } + context.execute("create jar") { preprocessedTask.createOutputJar() } + context.execute("produce src jar") { preprocessedTask.produceSourceJar() } + context.execute("generate jdeps") { jDepsGenerator.generateJDeps(preprocessedTask) } } private fun JvmCompilationTask.preProcessingSteps(context: CompilationTaskContext): JvmCompilationTask { @@ -166,7 +161,7 @@ } } try { - context.execute("javac") { javaCompiler.compile(this) } + context.execute("javac") { javaCompiler.compile(context, this) } } finally { checkNotNull(result).also(context::printCompilerOutput) kotlinError?.also { throw it } @@ -176,7 +171,7 @@ /** * Compiles Kotlin sources to classes. Does not compile Java sources. */ - fun JvmCompilationTask.compileKotlin(context: CompilationTaskContext, printOnFail: Boolean = true) = + private fun JvmCompilationTask.compileKotlin(context: CompilationTaskContext, printOnFail: Boolean = true) = getCommonArgs().let { args -> args.addAll(inputs.javaSourcesList) args.addAll(inputs.kotlinSourcesList)
diff --git a/src/main/kotlin/io/bazel/kotlin/builder/utils/CompilationTaskContext.kt b/src/main/kotlin/io/bazel/kotlin/builder/utils/CompilationTaskContext.kt index 9351233..9d02c20 100644 --- a/src/main/kotlin/io/bazel/kotlin/builder/utils/CompilationTaskContext.kt +++ b/src/main/kotlin/io/bazel/kotlin/builder/utils/CompilationTaskContext.kt
@@ -27,7 +27,9 @@ class CompilationTaskContext(val info: CompilationTaskInfo, private val out: PrintStream) { private val start = System.currentTimeMillis() - private val executionRoot: String = Paths.get("").toAbsolutePath().toString() + File.separator + private val executionRoot: String = Paths.get( + System.getProperty("user.dir") + ).toAbsolutePath().toString() + File.separator private var timings: MutableList<String>? private var level = -1 @PublishedApi @@ -100,7 +102,7 @@ * @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] excepotion. + * for logging it by catching the [CompilationStatusException] exception. * @param compile the compilation method. */ inline fun executeCompilerTask(
diff --git a/src/test/kotlin/io/bazel/kotlin/builder/KotlinBuilderJvmTestTask.java b/src/test/kotlin/io/bazel/kotlin/builder/KotlinBuilderJvmTestTask.java index e3c1f97..d04604e 100644 --- a/src/test/kotlin/io/bazel/kotlin/builder/KotlinBuilderJvmTestTask.java +++ b/src/test/kotlin/io/bazel/kotlin/builder/KotlinBuilderJvmTestTask.java
@@ -94,7 +94,7 @@ public void runCompileTask(BiConsumer<CompilationTaskContext, JvmCompilationTask> operation) { JvmCompilationTask task = taskBuilder.build(); super.runCompileTask( - new CompilationTaskContext(task.getInfo(), System.err), + task.getInfo(), task, (ctx, t) -> { operation.accept(ctx, t); @@ -105,7 +105,6 @@ @SuppressWarnings("unused") public <R> R runCompileTask(BiFunction<CompilationTaskContext, JvmCompilationTask, R> operation) { JvmCompilationTask task = taskBuilder.build(); - return super.runCompileTask( - new CompilationTaskContext(task.getInfo(), System.err), task, operation); + return super.runCompileTask(task.getInfo(), task, operation); } }
diff --git a/src/test/kotlin/io/bazel/kotlin/builder/KotlinBuilderResource.java b/src/test/kotlin/io/bazel/kotlin/builder/KotlinBuilderResource.java index 02db955..29c47ad 100644 --- a/src/test/kotlin/io/bazel/kotlin/builder/KotlinBuilderResource.java +++ b/src/test/kotlin/io/bazel/kotlin/builder/KotlinBuilderResource.java
@@ -1,16 +1,18 @@ package io.bazel.kotlin.builder; +import io.bazel.kotlin.builder.toolchain.CompilationException; +import io.bazel.kotlin.builder.toolchain.CompilationStatusException; import io.bazel.kotlin.builder.utils.CompilationTaskContext; import io.bazel.kotlin.model.CompilationTaskInfo; import org.junit.rules.ExternalResource; -import java.io.IOException; -import java.io.UncheckedIOException; +import java.io.*; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.util.*; import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import java.util.concurrent.atomic.AtomicInteger; @@ -109,8 +111,13 @@ return Objects.requireNonNull(instanceRoot); } + public List<String> outLines() { + return outLines; + } + @Override protected void before() throws Throwable { + outLines = null; label = "a_test_" + counter.incrementAndGet(); setTimeout(DEFAULT_TIMEOUT); try { @@ -152,6 +159,7 @@ private int DEFAULT_TIMEOUT = 10; private int timeoutSeconds = DEFAULT_TIMEOUT; + private List<String> outLines = null; /** * sets the timeout for the builder tasks. @@ -165,20 +173,37 @@ } <T, R> R runCompileTask( - CompilationTaskContext context, T task, BiFunction<CompilationTaskContext, T, R> operation) { + CompilationTaskInfo info, T task, BiFunction<CompilationTaskContext, T, R> operation) { String curDir = System.getProperty("user.dir"); - System.setProperty("user.dir", instanceRoot().toAbsolutePath().toString()); - try { + ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); + try (PrintStream outputStream = new PrintStream(byteArrayOutputStream)) { + System.setProperty("user.dir", instanceRoot().toAbsolutePath().toString()); CompletableFuture<R> future = - CompletableFuture.supplyAsync(() -> operation.apply(context, task)); + CompletableFuture.supplyAsync( + () -> operation.apply(new CompilationTaskContext(info, outputStream), task)); return timeoutSeconds > 0 ? future.get(timeoutSeconds, TimeUnit.SECONDS) : future.get(); - + } catch (ExecutionException e) { + if (e.getCause() instanceof CompilationStatusException) { + throw (CompilationStatusException) e.getCause(); + } else if (e.getCause() instanceof CompilationException) { + throw (CompilationException) e.getCause(); + } else { + throw new RuntimeException(e.getCause()); + } } catch (TimeoutException e) { throw new AssertionError("did not complete in: " + timeoutSeconds); } catch (Exception e) { throw new RuntimeException(e); } finally { System.setProperty("user.dir", curDir); + outLines = + Collections.unmodifiableList( + new BufferedReader( + new InputStreamReader( + new ByteArrayInputStream(byteArrayOutputStream.toByteArray()))) + .lines() + .peek(System.err::println) + .collect(Collectors.toList())); } } @@ -195,10 +220,10 @@ assertFileExistence(resolved(dir, filePath), false); } - private static void assertFileExistence(Stream<Path> pathStream, boolean shouldexist) { + private static void assertFileExistence(Stream<Path> pathStream, boolean shouldExist) { pathStream.forEach( path -> { - if (shouldexist) + if (shouldExist) assertWithMessage("file did not exist: " + path).that(path.toFile().exists()).isTrue(); else assertWithMessage("file existed: " + path).that(path.toFile().exists()).isFalse(); }); @@ -212,7 +237,7 @@ /** * Normalize a path string. * - * @param path a path using '/' as the seperator. + * @param path a path using '/' as the separator. * @return a path string suitable for the target platform. */ private static Path toPlatformPath(String path) {
diff --git a/src/test/kotlin/io/bazel/kotlin/builder/tasks/jvm/KotlinBuilderJvmTest.java b/src/test/kotlin/io/bazel/kotlin/builder/tasks/jvm/KotlinBuilderJvmTest.java index 386bf8c..46eb6af 100644 --- a/src/test/kotlin/io/bazel/kotlin/builder/tasks/jvm/KotlinBuilderJvmTest.java +++ b/src/test/kotlin/io/bazel/kotlin/builder/tasks/jvm/KotlinBuilderJvmTest.java
@@ -5,6 +5,7 @@ import io.bazel.kotlin.builder.KotlinBuilderJvmTestTask; import io.bazel.kotlin.builder.KotlinBuilderResource.Dep; import io.bazel.kotlin.builder.KotlinBuilderResource.DirectoryType; +import io.bazel.kotlin.builder.toolchain.CompilationStatusException; import io.bazel.kotlin.builder.toolchain.KotlinToolchain; import io.bazel.kotlin.builder.utils.CompilationTaskContext; import io.bazel.kotlin.model.AnnotationProcessor; @@ -13,6 +14,10 @@ import org.junit.Rule; import org.junit.Test; +import java.util.List; +import java.util.function.Consumer; + +import static com.google.common.truth.Truth.assertThat; import static io.bazel.kotlin.builder.KotlinBuilderResource.KOTLIN_ANNOTATIONS; import static io.bazel.kotlin.builder.KotlinBuilderResource.KOTLIN_STDLIB; @@ -154,24 +159,37 @@ } @Test + public void testKotlinErrorRendering() { + ctx.addSource("AClass.kt", "package something;" + "class AClass{"); + testExpectingCompileError(lines -> assertThat(lines.get(0)).startsWith("sources/AClass")); + } + + @Test + public void testJavaErrorRendering() { + ctx.addSource("AClass.kt", "package something;" + "class AClass{}"); + ctx.addSource("AnotherClass.java", "package something;", "", "class AnotherClass{"); + testExpectingCompileError(lines -> assertThat(lines.get(0)).startsWith("sources/AnotherClass")); + } + + @Test @Ignore("The Kotlin compiler expects a single kotlin file at least.") public void testCompileSingleJavaFile() { ctx.addSource("AnotherClass.java", "package something;", "", "class AnotherClass{}"); ctx.runCompileTask(this::jvmCompilationTask); } - @Test - @Ignore( - "This test needs to test the output of error handling by the compiler, when the kotlin compiler compiles java " - + "this test needs to validate it.") - public void testWithJavaError() { - ctx.addSource("AClass.kt", "package something;" + "class AClass{}"); - ctx.addSource("AnotherClass.java", "package something;", "", "class AnotherClass{"); - ctx.runCompileTask(this::jvmCompilationTask); - } - private void jvmCompilationTask(CompilationTaskContext taskContext, JvmCompilationTask task) { component.jvmTaskExecutor().execute(taskContext, task); ctx.assertFilesExist(task.getOutputs().getJar(), task.getOutputs().getJdeps()); } + + private void testExpectingCompileError(Consumer<List<String>> validator) { + try { + ctx.runCompileTask(this::jvmCompilationTask); + } catch (CompilationStatusException ex) { + validator.accept(ctx.outLines()); + return; + } + throw new RuntimeException("expected an exception"); + } }