blob: 76e2b2235da26dbb9f5ea12ba98e33c942b4f013 [file] [log] [blame]
/*
* 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.CompileDirectories
import io.bazel.kotlin.builder.model.CompilePluginConfig
import io.bazel.kotlin.builder.model.Flags
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 {
/**
* Default fields that are directly mappable to kotlin compiler args.
*/
private val COMPILE_MAPPED_FLAGS = arrayOf(
Flags.CLASSPATH,
Flags.KOTLIN_API_VERSION,
Flags.KOTLIN_LANGUAGE_VERSION,
Flags.KOTLIN_JVM_TARGET)
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>()
val compileDirectories = CompileDirectories[ctx]
ctx.copyOfFlags(*COMPILE_MAPPED_FLAGS).forEach { field, arg ->
args.add(field.kotlinFlag!!); args.add(arg)
}
args
.addAll("-module-name", ctx.moduleName)
.addAll("-d", compileDirectories.classes.toString())
Flags.KOTLIN_PASSTHROUGH_FLAGS[ctx]?.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")
}
}
}