blob: caa33720adcd31092f318575c5cc3acab09813f0 [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.tasks.jvm
import io.bazel.kotlin.builder.toolchain.CompilationStatusException
import io.bazel.kotlin.builder.utils.CompilationTaskContext
import io.bazel.kotlin.builder.utils.IS_JVM_SOURCE_FILE
import io.bazel.kotlin.builder.utils.ensureDirectories
import io.bazel.kotlin.builder.utils.expandWithSources
import io.bazel.kotlin.builder.utils.jars.SourceJarCreator
import io.bazel.kotlin.builder.utils.jars.SourceJarExtractor
import io.bazel.kotlin.model.JvmCompilationTask
import java.io.File
import java.nio.file.Files
import java.nio.file.Paths
import javax.inject.Inject
import javax.inject.Singleton
@Singleton
class KotlinJvmTaskExecutor @Inject internal constructor(
private val kotlinCompiler: KotlinJvmCompiler,
private val javaCompiler: JavaCompiler,
private val jDepsGenerator: JDepsGenerator,
private val outputJarCreator: OutputJarCreator
) {
fun execute(context: CompilationTaskContext, task: JvmCompilationTask) {
val preprocessedTask = preprocessingSteps(task)
// fix error handling
try {
val commandWithApSources = context.execute("kapt") {
runAnnotationProcessors(context, preprocessedTask)
}
compileClasses(context, commandWithApSources)
context.execute("create jar") {
outputJarCreator.createOutputJar(commandWithApSources)
}
produceSourceJar(commandWithApSources)
context.execute("generate jdeps") {
jDepsGenerator.generateJDeps(commandWithApSources)
}
} catch (ex: Throwable) {
throw RuntimeException(ex)
}
}
private fun preprocessingSteps(command: JvmCompilationTask): JvmCompilationTask {
ensureDirectories(
command.directories.classes,
command.directories.temp,
command.directories.generatedSources,
command.directories.generatedClasses
)
return expandWithSourceJarSources(command)
}
/**
* If any srcjars were provided expand the jars sources and create a new [JvmCompilationTask] with the
* Java and Kotlin sources merged in.
*/
private fun expandWithSourceJarSources(command: JvmCompilationTask): JvmCompilationTask =
if (command.inputs.sourceJarsList.isEmpty()) {
command
} else {
SourceJarExtractor(
destDir = Paths.get(command.directories.temp).resolve("_srcjars"),
fileMatcher = IS_JVM_SOURCE_FILE
).also {
it.jarFiles.addAll(command.inputs.sourceJarsList.map { p -> Paths.get(p) })
it.execute()
}.let {
command.expandWithSources(it.sourcesList.iterator())
}
}
private fun produceSourceJar(command: JvmCompilationTask) {
Paths.get(command.outputs.srcjar).also { sourceJarPath ->
Files.createFile(sourceJarPath)
SourceJarCreator(
sourceJarPath
).also { creator ->
// This check asserts that source jars were unpacked if present.
check(
command.inputs.sourceJarsList.isEmpty() ||
Files.exists(Paths.get(command.directories.temp).resolve("_srcjars"))
)
listOf(
// Any (input) source jars should already have been expanded so do not add them here.
command.inputs.javaSourcesList.stream(),
command.inputs.kotlinSourcesList.stream()
).stream()
.flatMap { it.map { p -> Paths.get(p) } }
.also { creator.addSources(it) }
creator.execute()
}
}
}
private fun runAnnotationProcessors(
context: CompilationTaskContext,
command: JvmCompilationTask
): JvmCompilationTask =
try {
if (command.info.plugins.annotationProcessorsList.isNotEmpty()) {
kotlinCompiler.runAnnotationProcessor(context, command)
File(command.directories.generatedSources).walkTopDown()
.filter { it.isFile }
.map { it.path }
.iterator()
.let { command.expandWithSources(it) }
} else {
command
}
} catch (ex: CompilationStatusException) {
ex.lines.also(context::printCompilerOutput)
throw ex
}
private fun compileClasses(
context: CompilationTaskContext,
command: JvmCompilationTask
) {
var kotlinError: CompilationStatusException? = null
var result: List<String>? = null
context.execute("kotlinc") {
result = try {
kotlinCompiler.compile(command)
} catch (ex: CompilationStatusException) {
kotlinError = ex
ex.lines
}
}
try {
context.execute("javac") {
javaCompiler.compile(command)
}
} finally {
checkNotNull(result).also(context::printCompilerOutput)
kotlinError?.also { throw it }
}
}
}