[Compiler CLI] Implement reading language version settings from environment variable
^KT-51306 Fixed
This commit is contained in:
committed by
teamcity
parent
43a0876c26
commit
683a3e74a0
@@ -38,6 +38,7 @@ enum class CompilerSystemProperties(val property: String, val alwaysDirectAccess
|
|||||||
COMPILE_INCREMENTAL_WITH_CLASSPATH_SNAPSHOTS("kotlin.incremental.classpath.snapshot.enabled"),
|
COMPILE_INCREMENTAL_WITH_CLASSPATH_SNAPSHOTS("kotlin.incremental.classpath.snapshot.enabled"),
|
||||||
COMPILE_INCREMENTAL_WITH_ARTIFACT_TRANSFORM("kotlin.incremental.useClasspathSnapshot"),
|
COMPILE_INCREMENTAL_WITH_ARTIFACT_TRANSFORM("kotlin.incremental.useClasspathSnapshot"),
|
||||||
KOTLIN_COLORS_ENABLED_PROPERTY("kotlin.colors.enabled"),
|
KOTLIN_COLORS_ENABLED_PROPERTY("kotlin.colors.enabled"),
|
||||||
|
LANGUAGE_VERSION_SETTINGS("kotlin.language.settings"),
|
||||||
|
|
||||||
OS_NAME("os.name", alwaysDirectAccess = true),
|
OS_NAME("os.name", alwaysDirectAccess = true),
|
||||||
TMP_DIR("java.io.tmpdir"),
|
TMP_DIR("java.io.tmpdir"),
|
||||||
|
|||||||
+41
-7
@@ -16,6 +16,7 @@
|
|||||||
|
|
||||||
package org.jetbrains.kotlin.cli.common.arguments
|
package org.jetbrains.kotlin.cli.common.arguments
|
||||||
|
|
||||||
|
import org.jetbrains.kotlin.cli.common.CompilerSystemProperties
|
||||||
import org.jetbrains.kotlin.utils.SmartList
|
import org.jetbrains.kotlin.utils.SmartList
|
||||||
import kotlin.reflect.KClass
|
import kotlin.reflect.KClass
|
||||||
import kotlin.reflect.KMutableProperty1
|
import kotlin.reflect.KMutableProperty1
|
||||||
@@ -63,13 +64,24 @@ data class ArgumentParseErrors(
|
|||||||
)
|
)
|
||||||
|
|
||||||
// Parses arguments into the passed [result] object. Errors related to the parsing will be collected into [CommonToolArguments.errors].
|
// Parses arguments into the passed [result] object. Errors related to the parsing will be collected into [CommonToolArguments.errors].
|
||||||
fun <A : CommonToolArguments> parseCommandLineArguments(args: List<String>, result: A) {
|
fun <A : CommonToolArguments> parseCommandLineArguments(args: List<String>, result: A, overrideArguments: Boolean = false) {
|
||||||
val errors = result.errors ?: ArgumentParseErrors().also { result.errors = it }
|
val errors = result.errors ?: ArgumentParseErrors().also { result.errors = it }
|
||||||
val preprocessed = preprocessCommandLineArguments(args, errors)
|
val preprocessed = preprocessCommandLineArguments(args, errors)
|
||||||
parsePreprocessedCommandLineArguments(preprocessed, result, errors)
|
parsePreprocessedCommandLineArguments(preprocessed, result, errors, overrideArguments)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun <A : CommonToolArguments> parsePreprocessedCommandLineArguments(args: List<String>, result: A, errors: ArgumentParseErrors) {
|
fun <A : CommonToolArguments> parseCommandLineArgumentsFromEnvironment(arguments: A) {
|
||||||
|
val settingsFromEnvironment = CompilerSystemProperties.LANGUAGE_VERSION_SETTINGS.value?.takeIf { it.isNotEmpty() }
|
||||||
|
?.split(Regex("""\s""")) ?: return
|
||||||
|
parseCommandLineArguments(settingsFromEnvironment, arguments, overrideArguments = true)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun <A : CommonToolArguments> parsePreprocessedCommandLineArguments(
|
||||||
|
args: List<String>,
|
||||||
|
result: A,
|
||||||
|
errors: ArgumentParseErrors,
|
||||||
|
overrideArguments: Boolean
|
||||||
|
) {
|
||||||
data class ArgumentField(val property: KMutableProperty1<A, Any?>, val argument: Argument)
|
data class ArgumentField(val property: KMutableProperty1<A, Any?>, val argument: Argument)
|
||||||
|
|
||||||
@Suppress("UNCHECKED_CAST")
|
@Suppress("UNCHECKED_CAST")
|
||||||
@@ -183,14 +195,36 @@ private fun <A : CommonToolArguments> parsePreprocessedCommandLineArguments(args
|
|||||||
errors.duplicateArguments[argument.value] = value
|
errors.duplicateArguments[argument.value] = value
|
||||||
}
|
}
|
||||||
|
|
||||||
updateField(property, result, value, argument.delimiter)
|
updateField(property, result, value, argument.delimiter, overrideArguments)
|
||||||
}
|
}
|
||||||
|
|
||||||
result.freeArgs += freeArgs
|
result.freeArgs += freeArgs
|
||||||
result.internalArguments += internalArguments
|
result.updateInternalArguments(internalArguments, overrideArguments)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun <A : CommonToolArguments> updateField(property: KMutableProperty1<A, Any?>, result: A, value: Any, delimiter: String) {
|
private fun <A : CommonToolArguments> A.updateInternalArguments(
|
||||||
|
newInternalArguments: ArrayList<InternalArgument>,
|
||||||
|
overrideArguments: Boolean
|
||||||
|
) {
|
||||||
|
val filteredExistingArguments = if (overrideArguments) {
|
||||||
|
internalArguments.filter { existingArgument ->
|
||||||
|
existingArgument !is ManualLanguageFeatureSetting ||
|
||||||
|
newInternalArguments.none {
|
||||||
|
it is ManualLanguageFeatureSetting && it.languageFeature == existingArgument.languageFeature
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else internalArguments
|
||||||
|
|
||||||
|
internalArguments = filteredExistingArguments + newInternalArguments
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun <A : CommonToolArguments> updateField(
|
||||||
|
property: KMutableProperty1<A, Any?>,
|
||||||
|
result: A,
|
||||||
|
value: Any,
|
||||||
|
delimiter: String,
|
||||||
|
overrideArguments: Boolean
|
||||||
|
) {
|
||||||
when (property.returnType.classifier) {
|
when (property.returnType.classifier) {
|
||||||
Boolean::class, String::class -> property.set(result, value)
|
Boolean::class, String::class -> property.set(result, value)
|
||||||
Array<String>::class -> {
|
Array<String>::class -> {
|
||||||
@@ -201,7 +235,7 @@ private fun <A : CommonToolArguments> updateField(property: KMutableProperty1<A,
|
|||||||
}
|
}
|
||||||
@Suppress("UNCHECKED_CAST")
|
@Suppress("UNCHECKED_CAST")
|
||||||
val oldValue = property.get(result) as Array<String>?
|
val oldValue = property.get(result) as Array<String>?
|
||||||
property.set(result, if (oldValue != null) arrayOf(*oldValue, *newElements) else newElements)
|
property.set(result, if (oldValue != null && !overrideArguments) arrayOf(*oldValue, *newElements) else newElements)
|
||||||
}
|
}
|
||||||
else -> throw IllegalStateException("Unsupported argument type: ${property.returnType}")
|
else -> throw IllegalStateException("Unsupported argument type: ${property.returnType}")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ package org.jetbrains.kotlin.cli.common
|
|||||||
import org.fusesource.jansi.AnsiConsole
|
import org.fusesource.jansi.AnsiConsole
|
||||||
import org.jetbrains.kotlin.cli.common.arguments.CommonToolArguments
|
import org.jetbrains.kotlin.cli.common.arguments.CommonToolArguments
|
||||||
import org.jetbrains.kotlin.cli.common.arguments.parseCommandLineArguments
|
import org.jetbrains.kotlin.cli.common.arguments.parseCommandLineArguments
|
||||||
|
import org.jetbrains.kotlin.cli.common.arguments.parseCommandLineArgumentsFromEnvironment
|
||||||
import org.jetbrains.kotlin.cli.common.arguments.validateArguments
|
import org.jetbrains.kotlin.cli.common.arguments.validateArguments
|
||||||
import org.jetbrains.kotlin.cli.common.messages.*
|
import org.jetbrains.kotlin.cli.common.messages.*
|
||||||
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity.INFO
|
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity.INFO
|
||||||
@@ -47,6 +48,7 @@ abstract class CLITool<A : CommonToolArguments> {
|
|||||||
): ExitCode {
|
): ExitCode {
|
||||||
val arguments = createArguments()
|
val arguments = createArguments()
|
||||||
parseCommandLineArguments(args.asList(), arguments)
|
parseCommandLineArguments(args.asList(), arguments)
|
||||||
|
parseCommandLineArgumentsFromEnvironment(arguments)
|
||||||
val collector = PrintingMessageCollector(errStream, messageRenderer, arguments.verbose)
|
val collector = PrintingMessageCollector(errStream, messageRenderer, arguments.verbose)
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -0,0 +1,4 @@
|
|||||||
|
$TESTDATA_DIR$/appendingArgs.kt
|
||||||
|
-d
|
||||||
|
$TEMP_DIR$
|
||||||
|
-Xallow-kotlin-package
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
-Xrender-internal-diagnostic-names
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
package kotlin
|
||||||
|
|
||||||
|
fun main() {
|
||||||
|
val x = 1?.dec()
|
||||||
|
}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
compiler/testData/cli/jvm/readingConfigFromEnvironment/appendingArgs.kt:4:9: warning: [UNUSED_VARIABLE] Variable 'x' is never used
|
||||||
|
val x = 1?.dec()
|
||||||
|
^
|
||||||
|
compiler/testData/cli/jvm/readingConfigFromEnvironment/appendingArgs.kt:4:13: warning: [SAFE_CALL_WILL_CHANGE_NULLABILITY] Safe call on a non-null receiver will have nullable type in future releases
|
||||||
|
val x = 1?.dec()
|
||||||
|
^
|
||||||
|
compiler/testData/cli/jvm/readingConfigFromEnvironment/appendingArgs.kt:4:14: warning: [UNNECESSARY_SAFE_CALL] Unnecessary safe call on a non-null receiver of type Int
|
||||||
|
val x = 1?.dec()
|
||||||
|
^
|
||||||
|
OK
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
public class Annotated {
|
||||||
|
@org.jetbrains.annotations.Nullable
|
||||||
|
public static String bar() { return ""; }
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
$TESTDATA_DIR$/overridingArgs.kt
|
||||||
|
$TESTDATA_DIR$/java
|
||||||
|
$FOREIGN_ANNOTATIONS_DIR$
|
||||||
|
-d
|
||||||
|
$TEMP_DIR$
|
||||||
|
-Xnullability-annotations=@org.jetbrains.annotations\:warn
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
-Xnullability-annotations=@org.jetbrains.annotations:ignore
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
fun main() {
|
||||||
|
Annotated.bar().length
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
OK
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
$TESTDATA_DIR$/overridingLv.kt
|
||||||
|
-d
|
||||||
|
$TEMP_DIR$
|
||||||
|
-language-version
|
||||||
|
1.7
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
-language-version
|
||||||
|
1.1
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
fun main() {
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
error: language version 1.1 is no longer supported; please, use version 1.3 or greater.
|
||||||
|
COMPILATION_ERROR
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
$TESTDATA_DIR$/overridingXx.kt
|
||||||
|
-d
|
||||||
|
$TEMP_DIR$
|
||||||
|
-XXLanguage\:+ApproximateIntegerLiteralTypesInReceiverPosition
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
-XXLanguage:-ApproximateIntegerLiteralTypesInReceiverPosition
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
fun foo(ttlMillis: Long = 5 * 60 * 1000) {}
|
||||||
|
|
||||||
|
const val cacheSize: Long = 4096 * 4
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
warning: ATTENTION!
|
||||||
|
This build uses unsafe internal compiler arguments:
|
||||||
|
|
||||||
|
-XXLanguage:-ApproximateIntegerLiteralTypesInReceiverPosition
|
||||||
|
|
||||||
|
This mode is not recommended for production use,
|
||||||
|
as no stability/compatibility guarantees are given on
|
||||||
|
compiler or generated code. Use it at your own risk!
|
||||||
|
|
||||||
|
compiler/testData/cli/jvm/readingConfigFromEnvironment/overridingXx.kt:1:9: warning: parameter 'ttlMillis' is never used
|
||||||
|
fun foo(ttlMillis: Long = 5 * 60 * 1000) {}
|
||||||
|
^
|
||||||
|
OK
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
$TESTDATA_DIR$/simple.kt
|
||||||
|
-d
|
||||||
|
$TEMP_DIR$
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
-Xrender-internal-diagnostic-names
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
fun main() {
|
||||||
|
val x = 1 + ""
|
||||||
|
}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
compiler/testData/cli/jvm/readingConfigFromEnvironment/simple.kt:2:15: error: [NONE_APPLICABLE] None of the following functions can be called with the arguments supplied:
|
||||||
|
public final operator fun plus(other: Byte): Int defined in kotlin.Int
|
||||||
|
public final operator fun plus(other: Double): Double defined in kotlin.Int
|
||||||
|
public final operator fun plus(other: Float): Float defined in kotlin.Int
|
||||||
|
public final operator fun plus(other: Int): Int defined in kotlin.Int
|
||||||
|
public final operator fun plus(other: Long): Long defined in kotlin.Int
|
||||||
|
public final operator fun plus(other: Short): Int defined in kotlin.Int
|
||||||
|
val x = 1 + ""
|
||||||
|
^
|
||||||
|
COMPILATION_ERROR
|
||||||
@@ -27,6 +27,7 @@ import org.jetbrains.annotations.NotNull;
|
|||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
import org.jetbrains.kotlin.checkers.ThirdPartyAnnotationPathsKt;
|
import org.jetbrains.kotlin.checkers.ThirdPartyAnnotationPathsKt;
|
||||||
import org.jetbrains.kotlin.cli.common.CLITool;
|
import org.jetbrains.kotlin.cli.common.CLITool;
|
||||||
|
import org.jetbrains.kotlin.cli.common.CompilerSystemProperties;
|
||||||
import org.jetbrains.kotlin.cli.common.ExitCode;
|
import org.jetbrains.kotlin.cli.common.ExitCode;
|
||||||
import org.jetbrains.kotlin.cli.common.Usage;
|
import org.jetbrains.kotlin.cli.common.Usage;
|
||||||
import org.jetbrains.kotlin.cli.js.K2JSCompiler;
|
import org.jetbrains.kotlin.cli.js.K2JSCompiler;
|
||||||
@@ -102,6 +103,12 @@ public abstract class AbstractCliTest extends TestCaseWithTmpdir {
|
|||||||
|
|
||||||
private void doTest(@NotNull String fileName, @NotNull CLITool<?> compiler) {
|
private void doTest(@NotNull String fileName, @NotNull CLITool<?> compiler) {
|
||||||
System.setProperty("java.awt.headless", "true");
|
System.setProperty("java.awt.headless", "true");
|
||||||
|
|
||||||
|
File environmentTestConfig = new File(fileName.replaceFirst("\\.args$", ".env"));
|
||||||
|
if (environmentTestConfig.exists()) {
|
||||||
|
CompilerSystemProperties.LANGUAGE_VERSION_SETTINGS.setValue(FilesKt.readText(environmentTestConfig, Charsets.UTF_8));
|
||||||
|
}
|
||||||
|
|
||||||
Pair<String, ExitCode> outputAndExitCode = executeCompilerGrabOutput(compiler, readArgs(fileName, tmpdir.getPath()));
|
Pair<String, ExitCode> outputAndExitCode = executeCompilerGrabOutput(compiler, readArgs(fileName, tmpdir.getPath()));
|
||||||
String actual = getNormalizedCompilerOutput(
|
String actual = getNormalizedCompilerOutput(
|
||||||
outputAndExitCode.getFirst(), outputAndExitCode.getSecond(), new File(fileName).getParent()
|
outputAndExitCode.getFirst(), outputAndExitCode.getSecond(), new File(fileName).getParent()
|
||||||
|
|||||||
+1
@@ -250,6 +250,7 @@ fun generateJUnit3CompilerTests(args: Array<String>) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
testClass<AbstractCliTest> {
|
testClass<AbstractCliTest> {
|
||||||
|
model("cli/jvm/readingConfigFromEnvironment", extension = "args", testMethod = "doJvmTest", recursive = false)
|
||||||
model("cli/jvm", extension = "args", testMethod = "doJvmTest", recursive = false)
|
model("cli/jvm", extension = "args", testMethod = "doJvmTest", recursive = false)
|
||||||
model("cli/js", extension = "args", testMethod = "doJsTest", recursive = false)
|
model("cli/js", extension = "args", testMethod = "doJsTest", recursive = false)
|
||||||
model("cli/js-dce", extension = "args", testMethod = "doJsDceTest", recursive = false)
|
model("cli/js-dce", extension = "args", testMethod = "doJsDceTest", recursive = false)
|
||||||
|
|||||||
@@ -19,6 +19,44 @@ import java.util.regex.Pattern;
|
|||||||
@SuppressWarnings("all")
|
@SuppressWarnings("all")
|
||||||
@RunWith(JUnit3RunnerWithInners.class)
|
@RunWith(JUnit3RunnerWithInners.class)
|
||||||
public class CliTestGenerated extends AbstractCliTest {
|
public class CliTestGenerated extends AbstractCliTest {
|
||||||
|
@TestMetadata("compiler/testData/cli/jvm/readingConfigFromEnvironment")
|
||||||
|
@TestDataPath("$PROJECT_ROOT")
|
||||||
|
@RunWith(JUnit3RunnerWithInners.class)
|
||||||
|
public static class ReadingConfigFromEnvironment extends AbstractCliTest {
|
||||||
|
private void runTest(String testDataFilePath) throws Exception {
|
||||||
|
KotlinTestUtils.runTest(this::doJvmTest, this, testDataFilePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testAllFilesPresentInReadingConfigFromEnvironment() throws Exception {
|
||||||
|
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/cli/jvm/readingConfigFromEnvironment"), Pattern.compile("^(.+)\\.args$"), null, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("appendingArgs.args")
|
||||||
|
public void testAppendingArgs() throws Exception {
|
||||||
|
runTest("compiler/testData/cli/jvm/readingConfigFromEnvironment/appendingArgs.args");
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("overridingArgs.args")
|
||||||
|
public void testOverridingArgs() throws Exception {
|
||||||
|
runTest("compiler/testData/cli/jvm/readingConfigFromEnvironment/overridingArgs.args");
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("overridingLv.args")
|
||||||
|
public void testOverridingLv() throws Exception {
|
||||||
|
runTest("compiler/testData/cli/jvm/readingConfigFromEnvironment/overridingLv.args");
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("overridingXx.args")
|
||||||
|
public void testOverridingXx() throws Exception {
|
||||||
|
runTest("compiler/testData/cli/jvm/readingConfigFromEnvironment/overridingXx.args");
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("simple.args")
|
||||||
|
public void testSimple() throws Exception {
|
||||||
|
runTest("compiler/testData/cli/jvm/readingConfigFromEnvironment/simple.args");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("compiler/testData/cli/jvm")
|
@TestMetadata("compiler/testData/cli/jvm")
|
||||||
@TestDataPath("$PROJECT_ROOT")
|
@TestDataPath("$PROJECT_ROOT")
|
||||||
@RunWith(JUnit3RunnerWithInners.class)
|
@RunWith(JUnit3RunnerWithInners.class)
|
||||||
|
|||||||
Reference in New Issue
Block a user