Kapt: Implement 'kapt' command-line tool (KT-24998, KT-24997)
This commit is contained in:
@@ -0,0 +1,22 @@
|
||||
plugins {
|
||||
kotlin("jvm")
|
||||
id("jps-compatible")
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compile(project(":compiler:cli"))
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
"main" { projectDefault() }
|
||||
"test" { }
|
||||
}
|
||||
|
||||
testsJar {}
|
||||
|
||||
dist()
|
||||
|
||||
projectTest {
|
||||
workingDir = rootDir
|
||||
dependsOn(":dist")
|
||||
}
|
||||
@@ -0,0 +1,178 @@
|
||||
/*
|
||||
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
|
||||
* that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
@file:JvmName("KaptCli")
|
||||
package org.jetbrains.kotlin.kapt.cli
|
||||
|
||||
import com.intellij.util.PathUtil
|
||||
import org.jetbrains.annotations.TestOnly
|
||||
import org.jetbrains.kotlin.cli.common.ExitCode
|
||||
import org.jetbrains.kotlin.cli.common.arguments.ArgumentParseErrors
|
||||
import org.jetbrains.kotlin.cli.common.arguments.preprocessCommandLineArguments
|
||||
import org.jetbrains.kotlin.cli.common.arguments.validateArguments
|
||||
import org.jetbrains.kotlin.cli.common.messages.*
|
||||
import org.jetbrains.kotlin.cli.jvm.K2JVMCompiler
|
||||
import org.jetbrains.kotlin.cli.jvm.modules.isAtLeastJava9
|
||||
import org.jetbrains.kotlin.kapt.cli.CliToolOption.Format.*
|
||||
import java.io.File
|
||||
|
||||
fun main(args: Array<String>) {
|
||||
val messageCollector = PrintingMessageCollector(System.err, MessageRenderer.PLAIN_RELATIVE_PATHS, false)
|
||||
|
||||
if (args.isEmpty() || args.contains("-help")) {
|
||||
printHelp()
|
||||
return
|
||||
}
|
||||
|
||||
val kaptTransformed = transformArgs(args.asList(), messageCollector, false)
|
||||
|
||||
if (messageCollector.hasErrors()) {
|
||||
System.exit(ExitCode.COMPILATION_ERROR.code)
|
||||
return
|
||||
}
|
||||
|
||||
K2JVMCompiler.main(kaptTransformed.toTypedArray())
|
||||
}
|
||||
|
||||
@TestOnly
|
||||
internal fun transformArgs(args: List<String>, messageCollector: MessageCollector, isTest: Boolean): List<String> {
|
||||
val parseErrors = ArgumentParseErrors()
|
||||
val kotlincTransformed = preprocessCommandLineArguments(args, parseErrors)
|
||||
|
||||
val errorMessage = validateArguments(parseErrors)
|
||||
if (errorMessage != null) {
|
||||
messageCollector.report(CompilerMessageSeverity.ERROR, errorMessage)
|
||||
return emptyList()
|
||||
}
|
||||
|
||||
return try {
|
||||
transformKaptToolArgs(kotlincTransformed, messageCollector, isTest)
|
||||
} catch (e: IllegalArgumentException) {
|
||||
messageCollector.report(CompilerMessageSeverity.ERROR, e.localizedMessage)
|
||||
emptyList()
|
||||
}
|
||||
}
|
||||
|
||||
private const val KAPT_COMPILER_PLUGIN_JAR_NAME = "kotlin-annotation-processing.jar"
|
||||
|
||||
private fun transformKaptToolArgs(args: List<String>, messageCollector: MessageCollector, isTest: Boolean): List<String> {
|
||||
val transformed = mutableListOf<String>()
|
||||
|
||||
if (!isTest) {
|
||||
val kaptCompilerPluginFile = findKaptCompilerPlugin()
|
||||
?: throw IllegalStateException("Can't find $KAPT_COMPILER_PLUGIN_JAR_NAME")
|
||||
|
||||
transformed += "-Xplugin=${kaptCompilerPluginFile.absolutePath}"
|
||||
}
|
||||
|
||||
var toolsJarPassed = false
|
||||
var aptModePassed = false
|
||||
var kaptVerboseModePassed = false
|
||||
|
||||
data class Option(val cliToolOption: CliToolOption, val pluginOption: KaptCliOption)
|
||||
|
||||
val cliOptions = KaptCliOption.values().mapNotNull { Option(it.cliToolOption ?: return@mapNotNull null, it) }
|
||||
|
||||
val iterator = args.asIterable().iterator()
|
||||
loop@ while (iterator.hasNext()) {
|
||||
val arg = iterator.next()
|
||||
if (arg == "--") {
|
||||
transformed += arg
|
||||
iterator.forEach { transformed += it }
|
||||
break
|
||||
}
|
||||
|
||||
if (arg == "-help") {
|
||||
throw IllegalStateException("-help option should be already processed")
|
||||
}
|
||||
|
||||
val option = cliOptions.firstOrNull { it.cliToolOption.matches(arg) }
|
||||
if (option == null) {
|
||||
transformed += arg
|
||||
continue
|
||||
}
|
||||
|
||||
val transformedOption = option.cliToolOption.transform(arg)
|
||||
|
||||
when (option.pluginOption) {
|
||||
KaptCliOption.TOOLS_JAR_OPTION -> {
|
||||
// TOOLS_JAR option is not passed as other compiler plugin options.
|
||||
// It is only used in kapt-cli, and we add a -Xplugin compiler option instead.
|
||||
|
||||
toolsJarPassed = true
|
||||
transformed.add(0, "-Xplugin=$transformedOption")
|
||||
continue@loop
|
||||
}
|
||||
KaptCliOption.APT_MODE_OPTION -> aptModePassed = true
|
||||
KaptCliOption.VERBOSE_MODE_OPTION -> kaptVerboseModePassed = true
|
||||
else -> {}
|
||||
}
|
||||
|
||||
transformed += kaptArg(option.pluginOption, transformedOption)
|
||||
}
|
||||
|
||||
if (!aptModePassed) {
|
||||
transformed.addAll(0, kaptArg(KaptCliOption.APT_MODE_OPTION, "compile"))
|
||||
}
|
||||
|
||||
if (!isTest && !isAtLeastJava9() && !areJavacComponentsAvailable() && !toolsJarPassed) {
|
||||
val toolsJarFile = findToolsJar()
|
||||
?: argError("'tools.jar' location should be specified (${KaptCliOption.TOOLS_JAR_OPTION.cliToolOption!!.name}=<path>)")
|
||||
transformed.add(0, "-Xplugin=" + toolsJarFile.absolutePath)
|
||||
}
|
||||
|
||||
if (kaptVerboseModePassed) {
|
||||
messageCollector.report(CompilerMessageSeverity.INFO, "Options passed to kotlinc: " + transformed.joinToString(" "))
|
||||
}
|
||||
|
||||
return transformed
|
||||
}
|
||||
|
||||
private fun CliToolOption.matches(arg: String) = when (format) {
|
||||
FLAG, VALUE -> arg.startsWith(name + "=")
|
||||
KEY_VALUE -> arg.startsWith(name + ":")
|
||||
}
|
||||
|
||||
private fun CliToolOption.transform(arg: String): String {
|
||||
val optionName = name
|
||||
|
||||
return when (format) {
|
||||
FLAG -> {
|
||||
fun err(): Nothing = argError("Invalid option format, should be $optionName=true/false")
|
||||
|
||||
if (arg.length < (optionName.length + 2)) err()
|
||||
arg.drop(optionName.length + 1).takeIf { it == "true" || it == "false" } ?: err()
|
||||
}
|
||||
VALUE -> {
|
||||
fun err(): Nothing = argError("Invalid option format, should be $optionName=<value>")
|
||||
|
||||
if (arg.length < (optionName.length + 2)) err()
|
||||
arg.drop(optionName.length + 1)
|
||||
}
|
||||
KEY_VALUE -> {
|
||||
fun err(): Nothing = argError("Invalid option format, should be $optionName:<key>=<value>")
|
||||
|
||||
if (arg.length < (optionName.length + 3) || arg[optionName.length] != ':') err()
|
||||
arg.drop(optionName.length + 1).takeIf { it.contains('=') } ?: err()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun kaptArg(option: KaptCliOption, value: String): List<String> {
|
||||
return listOf("-P", "plugin:" + KaptCliOption.ANNOTATION_PROCESSING_COMPILER_PLUGIN_ID + ":" + option.optionName + "=" + value)
|
||||
}
|
||||
|
||||
private fun argError(text: String): Nothing {
|
||||
throw IllegalArgumentException(text)
|
||||
}
|
||||
|
||||
private fun findKaptCompilerPlugin(): File? {
|
||||
val pathToThisJar = File(PathUtil.getJarPathForClass(CliToolOption::class.java))
|
||||
if (pathToThisJar.extension.toLowerCase() != "jar") {
|
||||
return null
|
||||
}
|
||||
|
||||
return File(pathToThisJar.parentFile, KAPT_COMPILER_PLUGIN_JAR_NAME).takeIf { it.exists() }
|
||||
}
|
||||
@@ -0,0 +1,160 @@
|
||||
/*
|
||||
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
|
||||
* that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.kapt.cli
|
||||
|
||||
import org.jetbrains.kotlin.compiler.plugin.AbstractCliOption
|
||||
import org.jetbrains.kotlin.kapt.cli.CliToolOption.Format.*
|
||||
|
||||
class CliToolOption(val name: String, val format: Format) {
|
||||
enum class Format {
|
||||
/**
|
||||
* A boolean flag.
|
||||
* Example: '-option=true'
|
||||
* */
|
||||
FLAG,
|
||||
|
||||
/**
|
||||
* An option with value.
|
||||
* Example: '-option=some/path'
|
||||
*/
|
||||
VALUE,
|
||||
|
||||
/**
|
||||
* A key-value pair option.
|
||||
* Example: '-option:key=value'
|
||||
*/
|
||||
KEY_VALUE
|
||||
}
|
||||
}
|
||||
|
||||
enum class KaptCliOption(
|
||||
override val optionName: String,
|
||||
override val valueDescription: String,
|
||||
override val description: String,
|
||||
override val allowMultipleOccurrences: Boolean = false,
|
||||
val cliToolOption: CliToolOption? = null
|
||||
) : AbstractCliOption {
|
||||
APT_MODE_OPTION(
|
||||
"aptMode", "<apt|stubs|stubsAndApt|compile>",
|
||||
"Annotation processing mode: only apt, only stub generation, both, or with the subsequent compilation",
|
||||
cliToolOption = CliToolOption("-Kapt-mode", VALUE)
|
||||
),
|
||||
|
||||
@Deprecated("Do not use in CLI")
|
||||
CONFIGURATION("configuration", "<encoded>", "Encoded configuration"),
|
||||
|
||||
SOURCE_OUTPUT_DIR_OPTION(
|
||||
"sources",
|
||||
"<path>",
|
||||
"Output path for generated sources",
|
||||
cliToolOption = CliToolOption("-Kapt-sources", VALUE)
|
||||
),
|
||||
|
||||
CLASS_OUTPUT_DIR_OPTION(
|
||||
"classes",
|
||||
"<path>",
|
||||
"Output path for generated classes",
|
||||
cliToolOption = CliToolOption("-Kapt-classes", VALUE)
|
||||
),
|
||||
|
||||
STUBS_OUTPUT_DIR_OPTION(
|
||||
"stubs",
|
||||
"<path>",
|
||||
"Output path for Java stubs",
|
||||
cliToolOption = CliToolOption("-Kapt-stubs", VALUE)
|
||||
),
|
||||
|
||||
INCREMENTAL_DATA_OUTPUT_DIR_OPTION("incrementalData", "<path>", "Output path for incremental data"),
|
||||
|
||||
ANNOTATION_PROCESSOR_CLASSPATH_OPTION(
|
||||
"apclasspath",
|
||||
"<classpath>",
|
||||
"Annotation processor classpath",
|
||||
true,
|
||||
cliToolOption = CliToolOption("-Kapt-classpath", VALUE)
|
||||
),
|
||||
|
||||
ANNOTATION_PROCESSORS_OPTION(
|
||||
"processors",
|
||||
"<fqname,[fqname2,...]>",
|
||||
"Annotation processor qualified names",
|
||||
true,
|
||||
cliToolOption = CliToolOption("-Kapt-processors", VALUE)
|
||||
),
|
||||
|
||||
APT_OPTION_OPTION(
|
||||
"apOption",
|
||||
":<key>=<value>",
|
||||
"Annotation processor options",
|
||||
cliToolOption = CliToolOption("-Kapt-option", KEY_VALUE)
|
||||
),
|
||||
|
||||
JAVAC_OPTION_OPTION(
|
||||
"javacOption",
|
||||
":<key>=<value>",
|
||||
"Javac options",
|
||||
cliToolOption = CliToolOption("-Kapt-javac-option", KEY_VALUE)
|
||||
),
|
||||
|
||||
TOOLS_JAR_OPTION(
|
||||
"toolsJarLocation",
|
||||
"<path>",
|
||||
"tools.jar file location (for JDK versions up to 1.8)",
|
||||
cliToolOption = CliToolOption("-Kapt-tools-jar-location", VALUE)
|
||||
),
|
||||
|
||||
USE_LIGHT_ANALYSIS_OPTION(
|
||||
"useLightAnalysis",
|
||||
"true | false",
|
||||
"Skip body analysis if possible",
|
||||
cliToolOption = CliToolOption("-Kapt-use-light-analysis", FLAG)
|
||||
),
|
||||
|
||||
CORRECT_ERROR_TYPES_OPTION(
|
||||
"correctErrorTypes",
|
||||
"true | false",
|
||||
"Replace generated or error types with ones from the generated sources",
|
||||
cliToolOption = CliToolOption("-Kapt-correct-error-types", FLAG)
|
||||
),
|
||||
|
||||
MAP_DIAGNOSTIC_LOCATIONS_OPTION(
|
||||
"mapDiagnosticLocations",
|
||||
"true | false",
|
||||
"Map diagnostic reported on kapt stubs to original locations in Kotlin sources",
|
||||
cliToolOption = CliToolOption("-Kapt-map-diagnostic-locations", FLAG)
|
||||
),
|
||||
|
||||
VERBOSE_MODE_OPTION(
|
||||
"verbose",
|
||||
"true | false",
|
||||
"Enable verbose output",
|
||||
cliToolOption = CliToolOption("-Kapt-verbose", FLAG)
|
||||
),
|
||||
|
||||
STRICT_MODE_OPTION(
|
||||
"strict",
|
||||
"true | false",
|
||||
"Show errors on incompatibilities during stub generation",
|
||||
cliToolOption = CliToolOption("-Kapt-strict", FLAG)
|
||||
),
|
||||
|
||||
INFO_AS_WARNINGS_OPTION("infoAsWarnings", "true | false", "Show information messages as warnings"),
|
||||
|
||||
@Deprecated("Do not use in CLI")
|
||||
APT_OPTIONS_OPTION("apoptions", "options map", "Encoded annotation processor options", false),
|
||||
|
||||
@Deprecated("Do not use in CLI")
|
||||
JAVAC_CLI_OPTIONS_OPTION("javacArguments", "javac CLI options map", "Encoded javac CLI options", false),
|
||||
|
||||
@Deprecated("Use APT_MODE_OPTION instead.")
|
||||
APT_ONLY_OPTION("aptOnly", "true | false", "Run only annotation processing, do not compile Kotlin files");
|
||||
|
||||
override val required: Boolean = false
|
||||
|
||||
companion object {
|
||||
const val ANNOTATION_PROCESSING_COMPILER_PLUGIN_ID: String = "org.jetbrains.kotlin.kapt3"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
|
||||
* that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.kapt.cli
|
||||
|
||||
import org.jetbrains.kotlin.kapt.cli.CliToolOption.Format.*
|
||||
|
||||
internal fun printHelp() {
|
||||
class OptionToRender(nameArgs: String, val description: String) {
|
||||
val nameArgs = nameArgs.trim()
|
||||
fun render(width: Int) = " " + nameArgs + " ".repeat(width - nameArgs.length) + description
|
||||
}
|
||||
|
||||
val options = KaptCliOption.values()
|
||||
.filter { it.cliToolOption != null }
|
||||
.map { OptionToRender(it.nameArgs(), it.description) }
|
||||
|
||||
val optionNameColumnWidth = options.asSequence().map { it.nameArgs.length }.max()!! + 2
|
||||
val renderedOptions = options.joinToString("\n|") { it.render(optionNameColumnWidth) }
|
||||
|
||||
val message = """
|
||||
|kapt: Run annotation processing over the specified Kotlin source files.
|
||||
|Usage: kapt <options> <source files>
|
||||
|
||||
|Options related to annotation processing:
|
||||
|$renderedOptions
|
||||
|
||||
|You can also pass all valid Kotlin compiler options.
|
||||
|Run 'kotlinc -help' to show them.
|
||||
""".trimMargin()
|
||||
|
||||
println(message)
|
||||
}
|
||||
|
||||
private fun KaptCliOption.nameArgs(): String {
|
||||
val cliToolOption = this.cliToolOption!!
|
||||
return when (cliToolOption.format) {
|
||||
FLAG -> cliToolOption.name + "=<true|false>"
|
||||
VALUE -> cliToolOption.name + "=" + valueDescription
|
||||
KEY_VALUE -> cliToolOption.name + valueDescription
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
|
||||
* that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.kapt.cli
|
||||
|
||||
import java.io.File
|
||||
|
||||
internal const val JAVAC_CONTEXT_CLASS = "com.sun.tools.javac.util.Context"
|
||||
|
||||
internal fun areJavacComponentsAvailable(): Boolean {
|
||||
return try {
|
||||
Class.forName(JAVAC_CONTEXT_CLASS)
|
||||
true
|
||||
} catch (e: ClassNotFoundException) {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
internal fun findToolsJar(): File? {
|
||||
val currentJavaHome = System.getProperty("java.home")
|
||||
?.takeIf { it.isNotEmpty() }
|
||||
?.let(::File)
|
||||
?.takeIf { it.exists() }
|
||||
?: return null
|
||||
|
||||
fun getToolsJar(javaHome: File) = File(javaHome, "lib/tools.jar").takeIf { it.exists() }
|
||||
|
||||
getToolsJar(currentJavaHome)?.let { return it}
|
||||
|
||||
if (currentJavaHome.name == "jre") {
|
||||
getToolsJar(currentJavaHome.parentFile)?.let { return it}
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
Reference in New Issue
Block a user