Compiler.kt
/*
* Copyright 2021 The Android Open Source Project
*
* 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 androidx.compose.compiler.daemon
import java.io.File
import java.nio.file.Files
import org.jetbrains.kotlin.build.DEFAULT_KOTLIN_SOURCE_FILES_EXTENSIONS
import org.jetbrains.kotlin.build.report.BuildReporter
import org.jetbrains.kotlin.build.report.DoNothingICReporter
import org.jetbrains.kotlin.build.report.metrics.DoNothingBuildMetricsReporter
import org.jetbrains.kotlin.cli.common.ExitCode
import org.jetbrains.kotlin.cli.common.arguments.K2JVMCompilerArguments
import org.jetbrains.kotlin.cli.common.messages.MessageRenderer
import org.jetbrains.kotlin.cli.common.messages.PrintingMessageCollector
import org.jetbrains.kotlin.cli.jvm.K2JVMCompiler
import org.jetbrains.kotlin.config.Services
import org.jetbrains.kotlin.incremental.ClasspathChanges.ClasspathSnapshotEnabled
import org.jetbrains.kotlin.incremental.ClasspathSnapshotFiles
import org.jetbrains.kotlin.incremental.IncrementalJvmCompilerRunner
import org.jetbrains.kotlin.incremental.multiproject.EmptyModulesApiHistory
import org.jetbrains.kotlin.incremental.withIncrementalCompilation
internal fun parseArgs(
args: Array<String>,
composePluginPath: String? = null,
compiler: K2JVMCompiler = K2JVMCompiler()
): K2JVMCompilerArguments {
val compilerArgs = compiler.createArguments()
compiler.parseArguments(args, compilerArgs)
composePluginPath?.let {
compilerArgs.pluginClasspaths = (compilerArgs.pluginClasspaths ?: emptyArray()) + it
compilerArgs.pluginOptions =
(compilerArgs.pluginOptions ?: emptyArray()) +
"plugin:androidx.compose.plugins.idea:enabled=true"
compilerArgs.useIR = true // Force IR since it's required for Compose
}
return compilerArgs
}
data class DaemonCompilerSettings(val composePluginPath: String? = null) {
companion object {
val DefaultSettings = DaemonCompilerSettings()
}
}
@JvmDefaultWithCompatibility
interface DaemonCompiler {
fun compile(
args: Array<String>,
daemonCompilerSettings: DaemonCompilerSettings = DaemonCompilerSettings.DefaultSettings
): ExitCode
}
/**
* A [DaemonCompiler] that uses regular `kotlinc` invocations.
*/
object BasicDaemonCompiler : DaemonCompiler {
private val compiler = K2JVMCompiler()
override fun compile(
args: Array<String>,
daemonCompilerSettings: DaemonCompilerSettings
): ExitCode {
return try {
val compilerArgs = parseArgs(args, daemonCompilerSettings.composePluginPath)
compiler.exec(
PrintingMessageCollector(System.err, MessageRenderer.PLAIN_RELATIVE_PATHS, true),
Services.EMPTY,
compilerArgs
)
} catch (t: Throwable) {
t.printStackTrace(System.err)
ExitCode.INTERNAL_ERROR
}
}
}
/**
* A [DaemonCompiler] that calls `kotlinc` in incremental mode.
*/
object IncrementalDaemonCompiler : DaemonCompiler {
private val compiler = IncrementalJvmCompilerRunner(
workingDir = Files.createTempDirectory("workingDir").toFile(),
reporter = BuildReporter(DoNothingICReporter, DoNothingBuildMetricsReporter),
usePreciseJavaTracking = true,
outputDirs = null,
buildHistoryFile = Files.createTempFile("build-history", ".bin").toFile(),
modulesApiHistory = EmptyModulesApiHistory,
kotlinSourceFilesExtensions = DEFAULT_KOTLIN_SOURCE_FILES_EXTENSIONS,
classpathChanges = ClasspathSnapshotEnabled.NotAvailableDueToMissingClasspathSnapshot(
ClasspathSnapshotFiles(emptyList(), Files.createTempDirectory("snapshots").toFile())
)
)
override fun compile(
args: Array<String>,
daemonCompilerSettings: DaemonCompilerSettings
): ExitCode {
println("Incremental compiler invoke")
return try {
val compilerArgs = parseArgs(args, daemonCompilerSettings.composePluginPath)
compilerArgs.moduleName = "test"
withIncrementalCompilation(compilerArgs) {
compiler.compile(
compilerArgs.freeArgs.map { File(it) },
compilerArgs,
PrintingMessageCollector(
System.err,
MessageRenderer.PLAIN_RELATIVE_PATHS,
true
),
null,
null
)
}
} catch (t: Throwable) {
t.printStackTrace(System.err)
ExitCode.INTERNAL_ERROR
}
}
}