From 6e5eb0fdd44596c2bdc3141c7f88120b527ff129 Mon Sep 17 00:00:00 2001 From: Yan Zhulanow Date: Mon, 22 Oct 2018 21:25:59 +0300 Subject: [PATCH] Kapt: Implement 'kapt' command-line tool (KT-24998, KT-24997) --- compiler/cli/bin/kapt | 24 +++ compiler/cli/bin/kapt.bat | 21 +++ compiler/cli/bin/kotlinc | 8 +- compiler/cli/bin/kotlinc.bat | 8 +- .../preprocessCommandLineArguments.kt | 2 +- .../kotlin/compiler/plugin/CliOptions.kt | 3 + .../pom.xml | 2 + plugins/kapt3/kapt3-cli/build.gradle.kts | 22 +++ plugins/kapt3/kapt3-cli/src/KaptCli.kt | 178 ++++++++++++++++++ plugins/kapt3/kapt3-cli/src/KaptCliOption.kt | 160 ++++++++++++++++ plugins/kapt3/kapt3-cli/src/help.kt | 44 +++++ plugins/kapt3/kapt3-cli/src/javacUtils.kt | 37 ++++ plugins/kapt3/kapt3-compiler/build.gradle.kts | 2 + .../org/jetbrains/kotlin/kapt3/Kapt3Plugin.kt | 108 +++++------ settings.gradle | 2 + 15 files changed, 560 insertions(+), 61 deletions(-) create mode 100755 compiler/cli/bin/kapt create mode 100644 compiler/cli/bin/kapt.bat create mode 100644 plugins/kapt3/kapt3-cli/build.gradle.kts create mode 100644 plugins/kapt3/kapt3-cli/src/KaptCli.kt create mode 100644 plugins/kapt3/kapt3-cli/src/KaptCliOption.kt create mode 100644 plugins/kapt3/kapt3-cli/src/help.kt create mode 100644 plugins/kapt3/kapt3-cli/src/javacUtils.kt diff --git a/compiler/cli/bin/kapt b/compiler/cli/bin/kapt new file mode 100755 index 00000000000..29be2add330 --- /dev/null +++ b/compiler/cli/bin/kapt @@ -0,0 +1,24 @@ +#!/usr/bin/env bash + +# Copyright 2010-2015 JetBrains s.r.o. +# +# 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. + +export KOTLIN_TOOL=kotlin-annotation-processing-cli.jar + +export KOTLIN_COMPILER=org.jetbrains.kotlin.kapt.cli.KaptCli + +DIR="${BASH_SOURCE[0]%/*}" +: ${DIR:="."} + +"${DIR}"/kotlinc "$@" diff --git a/compiler/cli/bin/kapt.bat b/compiler/cli/bin/kapt.bat new file mode 100644 index 00000000000..7cd246de65b --- /dev/null +++ b/compiler/cli/bin/kapt.bat @@ -0,0 +1,21 @@ +@echo off + +rem Copyright 2010-2015 JetBrains s.r.o. +rem +rem Licensed under the Apache License, Version 2.0 (the "License"); +rem you may not use this file except in compliance with the License. +rem You may obtain a copy of the License at +rem +rem http://www.apache.org/licenses/LICENSE-2.0 +rem +rem Unless required by applicable law or agreed to in writing, software +rem distributed under the License is distributed on an "AS IS" BASIS, +rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +rem See the License for the specific language governing permissions and +rem limitations under the License. + +setlocal +set _KOTLIN_COMPILER=org.jetbrains.kotlin.kapt.cli.KaptCli +set _KOTLIN_TOOL=kotlin-annotation-processing-cli.jar + +call %~dps0kotlinc.bat %* diff --git a/compiler/cli/bin/kotlinc b/compiler/cli/bin/kotlinc index 72e90717c02..7f45ac9146e 100755 --- a/compiler/cli/bin/kotlinc +++ b/compiler/cli/bin/kotlinc @@ -68,7 +68,13 @@ else [ -n "$KOTLIN_COMPILER" ] || KOTLIN_COMPILER=org.jetbrains.kotlin.cli.jvm.K2JVMCompiler java_args=("${java_args[@]}" "-noverify") - kotlin_app=("${KOTLIN_HOME}/lib/kotlin-preloader.jar" "org.jetbrains.kotlin.preloading.Preloader" "-cp" "${KOTLIN_HOME}/lib/kotlin-compiler.jar" $KOTLIN_COMPILER) + declare additional_classpath="" + if [ -n "$KOTLIN_TOOL" ]; + then + additional_classpath=":${KOTLIN_HOME}/lib/${KOTLIN_TOOL}" + fi + + kotlin_app=("${KOTLIN_HOME}/lib/kotlin-preloader.jar" "org.jetbrains.kotlin.preloading.Preloader" "-cp" "${KOTLIN_HOME}/lib/kotlin-compiler.jar${additional_classpath}" $KOTLIN_COMPILER) fi "${JAVACMD:=java}" $JAVA_OPTS "${java_args[@]}" -cp "${kotlin_app[@]}" "${kotlin_args[@]}" diff --git a/compiler/cli/bin/kotlinc.bat b/compiler/cli/bin/kotlinc.bat index b10e23aade9..f12d7ec9c05 100644 --- a/compiler/cli/bin/kotlinc.bat +++ b/compiler/cli/bin/kotlinc.bat @@ -31,8 +31,14 @@ if not "%_KOTLIN_RUNNER%"=="" ( "%_JAVACMD%" %JAVA_OPTS% "-Dkotlin.home=%_KOTLIN_HOME%" -cp "%_KOTLIN_HOME%\lib\kotlin-runner.jar" ^ org.jetbrains.kotlin.runner.Main %* ) else ( + SET _ADDITIONAL_CLASSPATH="" + + if not "%_KOTLIN_TOOL%"=="" ( + set _ADDITIONAL_CLASSPATH=;%_KOTLIN_HOME%\lib\%_KOTLIN_TOOL% + ) + "%_JAVACMD%" %JAVA_OPTS% -noverify -cp "%_KOTLIN_HOME%\lib\kotlin-preloader.jar" ^ - org.jetbrains.kotlin.preloading.Preloader -cp "%_KOTLIN_HOME%\lib\kotlin-compiler.jar" ^ + org.jetbrains.kotlin.preloading.Preloader -cp "%_KOTLIN_HOME%\lib\kotlin-compiler.jar%_ADDITIONAL_CLASSPATH%" ^ %_KOTLIN_COMPILER% %* ) diff --git a/compiler/cli/cli-common/src/org/jetbrains/kotlin/cli/common/arguments/preprocessCommandLineArguments.kt b/compiler/cli/cli-common/src/org/jetbrains/kotlin/cli/common/arguments/preprocessCommandLineArguments.kt index b349a4d811a..59b83ba0bb7 100644 --- a/compiler/cli/cli-common/src/org/jetbrains/kotlin/cli/common/arguments/preprocessCommandLineArguments.kt +++ b/compiler/cli/cli-common/src/org/jetbrains/kotlin/cli/common/arguments/preprocessCommandLineArguments.kt @@ -22,7 +22,7 @@ private const val BACKSLASH = '\\' * This is done prior to *any* arguments parsing, and result of preprocessing * will be used instead of actual passed arguments. */ -internal fun preprocessCommandLineArguments(args: List, errors: ArgumentParseErrors): List = +fun preprocessCommandLineArguments(args: List, errors: ArgumentParseErrors): List = args.flatMap { arg -> if (arg.isArgfileArgument) { File(arg.argfilePath).expand(errors) diff --git a/compiler/plugin-api/src/org/jetbrains/kotlin/compiler/plugin/CliOptions.kt b/compiler/plugin-api/src/org/jetbrains/kotlin/compiler/plugin/CliOptions.kt index 7d6bc774770..eba5ef29919 100644 --- a/compiler/plugin-api/src/org/jetbrains/kotlin/compiler/plugin/CliOptions.kt +++ b/compiler/plugin-api/src/org/jetbrains/kotlin/compiler/plugin/CliOptions.kt @@ -24,6 +24,9 @@ interface AbstractCliOption { val description: String val required: Boolean val allowMultipleOccurrences: Boolean + + val deprecatedName: String? + get() = null } class CliOption( diff --git a/libraries/tools/kotlin-annotation-processing-maven/pom.xml b/libraries/tools/kotlin-annotation-processing-maven/pom.xml index 88438ed7cd5..a098e6dd654 100755 --- a/libraries/tools/kotlin-annotation-processing-maven/pom.xml +++ b/libraries/tools/kotlin-annotation-processing-maven/pom.xml @@ -7,6 +7,7 @@ 3.0.4 ${basedir}/../../../plugins/kapt3/kapt3-compiler/src + ${basedir}/../../../plugins/kapt3/kapt3-cli/src ${basedir}/../../../plugins/kapt3/kapt3-base/src ${basedir}/../../../plugins/kapt3/kapt3-runtime/src ${basedir}/target/src/main/kotlin @@ -70,6 +71,7 @@ ${annotation-processing.target-src} ${annotation-processing.src} + ${annotation-processing.cli.src} ${annotation-processing.base.src} ${annotation-processing.runtime.src} diff --git a/plugins/kapt3/kapt3-cli/build.gradle.kts b/plugins/kapt3/kapt3-cli/build.gradle.kts new file mode 100644 index 00000000000..13d6238a53b --- /dev/null +++ b/plugins/kapt3/kapt3-cli/build.gradle.kts @@ -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") +} \ No newline at end of file diff --git a/plugins/kapt3/kapt3-cli/src/KaptCli.kt b/plugins/kapt3/kapt3-cli/src/KaptCli.kt new file mode 100644 index 00000000000..f123d8c0bb0 --- /dev/null +++ b/plugins/kapt3/kapt3-cli/src/KaptCli.kt @@ -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) { + 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, messageCollector: MessageCollector, isTest: Boolean): List { + 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, messageCollector: MessageCollector, isTest: Boolean): List { + val transformed = mutableListOf() + + 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}=)") + 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=") + + if (arg.length < (optionName.length + 2)) err() + arg.drop(optionName.length + 1) + } + KEY_VALUE -> { + fun err(): Nothing = argError("Invalid option format, should be $optionName:=") + + 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 { + 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() } +} \ No newline at end of file diff --git a/plugins/kapt3/kapt3-cli/src/KaptCliOption.kt b/plugins/kapt3/kapt3-cli/src/KaptCliOption.kt new file mode 100644 index 00000000000..7a14ebc7ff8 --- /dev/null +++ b/plugins/kapt3/kapt3-cli/src/KaptCliOption.kt @@ -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", "", + "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 configuration"), + + SOURCE_OUTPUT_DIR_OPTION( + "sources", + "", + "Output path for generated sources", + cliToolOption = CliToolOption("-Kapt-sources", VALUE) + ), + + CLASS_OUTPUT_DIR_OPTION( + "classes", + "", + "Output path for generated classes", + cliToolOption = CliToolOption("-Kapt-classes", VALUE) + ), + + STUBS_OUTPUT_DIR_OPTION( + "stubs", + "", + "Output path for Java stubs", + cliToolOption = CliToolOption("-Kapt-stubs", VALUE) + ), + + INCREMENTAL_DATA_OUTPUT_DIR_OPTION("incrementalData", "", "Output path for incremental data"), + + ANNOTATION_PROCESSOR_CLASSPATH_OPTION( + "apclasspath", + "", + "Annotation processor classpath", + true, + cliToolOption = CliToolOption("-Kapt-classpath", VALUE) + ), + + ANNOTATION_PROCESSORS_OPTION( + "processors", + "", + "Annotation processor qualified names", + true, + cliToolOption = CliToolOption("-Kapt-processors", VALUE) + ), + + APT_OPTION_OPTION( + "apOption", + ":=", + "Annotation processor options", + cliToolOption = CliToolOption("-Kapt-option", KEY_VALUE) + ), + + JAVAC_OPTION_OPTION( + "javacOption", + ":=", + "Javac options", + cliToolOption = CliToolOption("-Kapt-javac-option", KEY_VALUE) + ), + + TOOLS_JAR_OPTION( + "toolsJarLocation", + "", + "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" + } +} diff --git a/plugins/kapt3/kapt3-cli/src/help.kt b/plugins/kapt3/kapt3-cli/src/help.kt new file mode 100644 index 00000000000..db4f8a1da2a --- /dev/null +++ b/plugins/kapt3/kapt3-cli/src/help.kt @@ -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 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 + "=" + VALUE -> cliToolOption.name + "=" + valueDescription + KEY_VALUE -> cliToolOption.name + valueDescription + } +} \ No newline at end of file diff --git a/plugins/kapt3/kapt3-cli/src/javacUtils.kt b/plugins/kapt3/kapt3-cli/src/javacUtils.kt new file mode 100644 index 00000000000..e0eaf40f645 --- /dev/null +++ b/plugins/kapt3/kapt3-cli/src/javacUtils.kt @@ -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 +} \ No newline at end of file diff --git a/plugins/kapt3/kapt3-compiler/build.gradle.kts b/plugins/kapt3/kapt3-compiler/build.gradle.kts index ee8d088f122..cd29ba78aa3 100644 --- a/plugins/kapt3/kapt3-compiler/build.gradle.kts +++ b/plugins/kapt3/kapt3-compiler/build.gradle.kts @@ -21,6 +21,7 @@ dependencies { compile(project(":compiler:frontend")) compile(project(":compiler:frontend.java")) compile(project(":compiler:plugin-api")) + compileOnly(project(":kotlin-annotation-processing-cli")) compileOnly(project(":kotlin-annotation-processing-base")) compileOnly(project(":kotlin-annotation-processing-runtime")) compileOnly(intellijCoreDep()) { includeJars("intellij-core") } @@ -33,6 +34,7 @@ dependencies { testCompile(project(":kotlin-annotation-processing-runtime")) embeddedComponents(project(":kotlin-annotation-processing-runtime")) { isTransitive = false } + embeddedComponents(project(":kotlin-annotation-processing-cli")) { isTransitive = false } embeddedComponents(project(":kotlin-annotation-processing-base")) { isTransitive = false } } diff --git a/plugins/kapt3/kapt3-compiler/src/org/jetbrains/kotlin/kapt3/Kapt3Plugin.kt b/plugins/kapt3/kapt3-compiler/src/org/jetbrains/kotlin/kapt3/Kapt3Plugin.kt index f1ec3a54960..b456ef9a33f 100644 --- a/plugins/kapt3/kapt3-compiler/src/org/jetbrains/kotlin/kapt3/Kapt3Plugin.kt +++ b/plugins/kapt3/kapt3-compiler/src/org/jetbrains/kotlin/kapt3/Kapt3Plugin.kt @@ -35,11 +35,14 @@ import org.jetbrains.kotlin.container.useInstance import org.jetbrains.kotlin.context.ProjectContext import org.jetbrains.kotlin.descriptors.ModuleDescriptor import org.jetbrains.kotlin.extensions.StorageComponentContainerContributor -import org.jetbrains.kotlin.kapt3.KaptCliOptions.* +import org.jetbrains.kotlin.kapt.cli.KaptCliOption +import org.jetbrains.kotlin.kapt.cli.KaptCliOption.Companion.ANNOTATION_PROCESSING_COMPILER_PLUGIN_ID +import org.jetbrains.kotlin.kapt.cli.KaptCliOption.* import org.jetbrains.kotlin.kapt3.Kapt3ConfigurationKeys.ANNOTATION_PROCESSOR_CLASSPATH +import org.jetbrains.kotlin.kapt3.Kapt3ConfigurationKeys.APT_OPTION import org.jetbrains.kotlin.kapt3.Kapt3ConfigurationKeys.APT_OPTIONS import org.jetbrains.kotlin.kapt3.Kapt3ConfigurationKeys.JAVAC_CLI_OPTIONS -import org.jetbrains.kotlin.kapt3.KaptCliOptions.Companion.ANNOTATION_PROCESSING_COMPILER_PLUGIN_ID +import org.jetbrains.kotlin.kapt3.Kapt3ConfigurationKeys.JAVAC_OPTION import org.jetbrains.kotlin.kapt3.base.Kapt import org.jetbrains.kotlin.kapt3.base.KaptPaths import org.jetbrains.kotlin.kapt3.base.log @@ -71,12 +74,20 @@ object Kapt3ConfigurationKeys { val ANNOTATION_PROCESSOR_CLASSPATH: CompilerConfigurationKey> = CompilerConfigurationKey.create>(ANNOTATION_PROCESSOR_CLASSPATH_OPTION.description) + @Deprecated("Use APT_OPTION instead.") val APT_OPTIONS: CompilerConfigurationKey = CompilerConfigurationKey.create(APT_OPTIONS_OPTION.description) + val APT_OPTION: CompilerConfigurationKey> = + CompilerConfigurationKey.create>(APT_OPTION_OPTION.description) + + @Deprecated("Use JAVAC_OPTION instead.") val JAVAC_CLI_OPTIONS: CompilerConfigurationKey = CompilerConfigurationKey.create(JAVAC_CLI_OPTIONS_OPTION.description) + val JAVAC_OPTION: CompilerConfigurationKey> = + CompilerConfigurationKey.create>(JAVAC_OPTION_OPTION.description) + val ANNOTATION_PROCESSORS: CompilerConfigurationKey> = CompilerConfigurationKey.create>(ANNOTATION_PROCESSORS_OPTION.description) @@ -106,61 +117,13 @@ object Kapt3ConfigurationKeys { CompilerConfigurationKey.create(STRICT_MODE_OPTION.description) } -enum class KaptCliOptions( - override val optionName: String, - override val valueDescription: String, - override val description: String, - override val allowMultipleOccurrences: Boolean = false -) : AbstractCliOption { - CONFIGURATION("configuration", "", "Encoded configuration"), - SOURCE_OUTPUT_DIR_OPTION("sources", "", "Output path for the generated files"), - CLASS_OUTPUT_DIR_OPTION("classes", "", "Output path for the class files"), - STUBS_OUTPUT_DIR_OPTION("stubs", "", "Output path for the Java stubs"), - INCREMENTAL_DATA_OUTPUT_DIR_OPTION("incrementalData", "", "Output path for the incremental data"), - ANNOTATION_PROCESSOR_CLASSPATH_OPTION("apclasspath", "", "Annotation processor classpath", true), - APT_OPTIONS_OPTION("apoptions", "options map", "Encoded annotation processor options", false), - JAVAC_CLI_OPTIONS_OPTION("javacArguments", "javac CLI options map", "Encoded javac CLI options", false), - ANNOTATION_PROCESSORS_OPTION("processors", "", "Annotation processor qualified names", true), - VERBOSE_MODE_OPTION("verbose", "true | false", "Enable verbose output"), - INFO_AS_WARNINGS_OPTION("infoAsWarnings", "true | false", "Show information messages as warnings"), - STRICT_MODE_OPTION("strict", "true | false", "Show errors on incompatibilities during stub generation"), - APT_MODE_OPTION( - "aptMode", "apt | stubs | stubsAndApt | compile", - "Annotation processing mode: only apt, only stub generation, both, or with the subsequent compilation" - ), - USE_LIGHT_ANALYSIS_OPTION( - "useLightAnalysis", - "true | false", - "Do not analyze declaration bodies if can" - ), - CORRECT_ERROR_TYPES_OPTION( - "correctErrorTypes", - "true | false", - "Replace generated or error types with ones from the generated sources" - ), - MAP_DIAGNOSTIC_LOCATIONS_OPTION( - "mapDiagnosticLocations", - "true | false", - "Map diagnostic reported on kapt stubs to original locations in Kotlin sources" - ), - - @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" - } -} - class Kapt3CommandLineProcessor : CommandLineProcessor { override val pluginId: String = ANNOTATION_PROCESSING_COMPILER_PLUGIN_ID - override val pluginOptions: Collection = KaptCliOptions.values().asList() + override val pluginOptions: Collection = values().asList() override fun processOption(option: AbstractCliOption, value: String, configuration: CompilerConfiguration) { - if (option !is KaptCliOptions) { + if (option !is KaptCliOption) { throw CliOptionProcessingException("Unknown option: ${option.optionName}") } @@ -168,8 +131,10 @@ class Kapt3CommandLineProcessor : CommandLineProcessor { ANNOTATION_PROCESSOR_CLASSPATH_OPTION -> configuration.appendList(ANNOTATION_PROCESSOR_CLASSPATH, value) ANNOTATION_PROCESSORS_OPTION -> configuration.appendList( Kapt3ConfigurationKeys.ANNOTATION_PROCESSORS, value.split(',').map { it.trim() }.filter { it.isNotEmpty() }) - APT_OPTIONS_OPTION -> configuration.put(Kapt3ConfigurationKeys.APT_OPTIONS, value) - JAVAC_CLI_OPTIONS_OPTION -> configuration.put(Kapt3ConfigurationKeys.JAVAC_CLI_OPTIONS, value) + APT_OPTIONS_OPTION -> configuration.put(APT_OPTIONS, value) + JAVAC_CLI_OPTIONS_OPTION -> configuration.put(JAVAC_CLI_OPTIONS, value) + APT_OPTION_OPTION -> configuration.appendMap(APT_OPTION, option, value) + JAVAC_OPTION_OPTION -> configuration.appendMap(JAVAC_OPTION, option, value) SOURCE_OUTPUT_DIR_OPTION -> configuration.put(Kapt3ConfigurationKeys.SOURCE_OUTPUT_DIR, value) CLASS_OUTPUT_DIR_OPTION -> configuration.put(Kapt3ConfigurationKeys.CLASS_OUTPUT_DIR, value) STUBS_OUTPUT_DIR_OPTION -> configuration.put(Kapt3ConfigurationKeys.STUBS_OUTPUT_DIR, value) @@ -183,12 +148,34 @@ class Kapt3CommandLineProcessor : CommandLineProcessor { INFO_AS_WARNINGS_OPTION -> configuration.put(Kapt3ConfigurationKeys.INFO_AS_WARNINGS, value) STRICT_MODE_OPTION -> configuration.put(Kapt3ConfigurationKeys.STRICT_MODE, value) CONFIGURATION -> configuration.applyOptionsFrom(decodePluginOptions(value), pluginOptions) + TOOLS_JAR_OPTION -> throw CliOptionProcessingException("'${TOOLS_JAR_OPTION.optionName}' is only supported in the kapt CLI tool") } } + + private fun CompilerConfiguration.appendMap( + key: CompilerConfigurationKey>, + option: AbstractCliOption, + keyValuePair: String + ) { + val keyValueDecoded = keyValuePair.split('=', limit = 2) + if (keyValueDecoded.size != 2) { + throw CliOptionProcessingException("Invalid option format for ${option.optionName}: key=value expected") + } + + val oldMap = getMap(key) + val newMap = (oldMap as? MutableMap) ?: oldMap.toMutableMap() + + newMap[keyValueDecoded[0]] = keyValueDecoded[1] + put(key, newMap) + } } class Kapt3ComponentRegistrar : ComponentRegistrar { - private fun decodeList(options: String): Map { + private fun decodeMap(options: String): Map { + if (options.isEmpty()) { + return emptyMap() + } + val map = LinkedHashMap() val decodedBytes = Base64.getDecoder().decode(options) @@ -264,8 +251,11 @@ class Kapt3ComponentRegistrar : ComponentRegistrar { return } - val apOptions = configuration.get(APT_OPTIONS)?.let { decodeList(it) } ?: emptyMap() - val javacCliOptions = configuration.get(JAVAC_CLI_OPTIONS)?.let { decodeList(it) } ?: emptyMap() + val apOptions = (configuration.get(APT_OPTIONS)?.let { decodeMap(it) } ?: emptyMap()).toMutableMap() + configuration.get(APT_OPTION)?.let { apOptions += it } + + val javacOptions = (configuration.get(JAVAC_CLI_OPTIONS)?.let { decodeMap(it) } ?: emptyMap()).toMutableMap() + configuration.get(JAVAC_OPTION)?.let { javacOptions += it } sourcesOutputDir.mkdirs() @@ -300,7 +290,7 @@ class Kapt3ComponentRegistrar : ComponentRegistrar { } val kapt3AnalysisCompletedHandlerExtension = ClasspathBasedKapt3Extension( - paths, apOptions, javacCliOptions, annotationProcessors, + paths, apOptions, javacOptions, annotationProcessors, aptMode, useLightAnalysis, correctErrorTypes, mapDiagnosticLocations, strictMode, System.currentTimeMillis(), logger, configuration ) @@ -359,6 +349,8 @@ enum class AptMode(val optionName: String) { get() = this != APT_ONLY companion object { + fun supports(mode: String?) = AptMode.values().any { it.optionName == mode } + // Supports both deprecated APT_ONLY and new APT_MODE options fun parse(mode: String?): AptMode { return when (mode) { diff --git a/settings.gradle b/settings.gradle index 2f9b2a41fa0..c806bd774c7 100644 --- a/settings.gradle +++ b/settings.gradle @@ -168,6 +168,7 @@ include ":kotlin-build-common", ":examples:annotation-processor-example", ":kotlin-script-util", ":kotlin-annotation-processing", + ":kotlin-annotation-processing-cli", ":kotlin-annotation-processing-base", ":kotlin-annotation-processing-runtime", ":kotlin-annotation-processing-gradle", @@ -305,6 +306,7 @@ project(':kotlin-script-util').projectDir = "$rootDir/libraries/tools/kotlin-scr project(':kotlin-annotation-processing-gradle').projectDir = "$rootDir/libraries/tools/kotlin-annotation-processing" as File project(':kotlin-annotation-processing-embeddable').projectDir = "$rootDir/prepare/kotlin-annotation-processing-embeddable" as File project(':kotlin-annotation-processing').projectDir = "$rootDir/plugins/kapt3/kapt3-compiler" as File +project(':kotlin-annotation-processing-cli').projectDir = "$rootDir/plugins/kapt3/kapt3-cli" as File project(':kotlin-annotation-processing-base').projectDir = "$rootDir/plugins/kapt3/kapt3-base" as File project(':kotlin-annotation-processing-runtime').projectDir = "$rootDir/plugins/kapt3/kapt3-runtime" as File project(':plugins:kapt3-idea').projectDir = "$rootDir/plugins/kapt3/kapt3-idea" as File