blob: 5b4baae76f5cc54e778f0f3fd546f07a5d80e585 [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
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.mandatorySingle("--kotlin_module_name").also {
check(it.isNotBlank()) { "--kotlin_module_name should not be blank" }
}
passthroughFlags = argMap.optionalSingle("--kotlin_passthrough_flags")
addAllFriendPaths(argMap.mandatory("--kotlin_friend_paths"))
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]
}
}
root.build()
}
override fun withSources(command: BuilderCommand, sources: Iterator<String>): BuilderCommand =
command.updateBuilder { builder ->
sources.partitionSources(
{ builder.inputsBuilder.addKotlinSources(it) },
{ builder.inputsBuilder.addJavaSources(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()
}