Support -Xargfile in all scenarios; refactor & prettify code
Perform command line argument preprocessing in the beginning of
parseCommandLineArguments, so that argfiles are expanded in all
scenarios, not just when the compiler is invoked via
K2{JVM,JS}Compiler.exec
This commit is contained in:
committed by
Dmitry Savvinov
parent
43467516ef
commit
61902e1fd5
+6
-1
@@ -54,11 +54,16 @@ data class ArgumentParseErrors(
|
|||||||
|
|
||||||
var argumentWithoutValue: String? = null,
|
var argumentWithoutValue: String? = null,
|
||||||
|
|
||||||
val argfileErrors: MutableList<String> = SmartList<String>()
|
val argfileErrors: MutableList<String> = SmartList()
|
||||||
)
|
)
|
||||||
|
|
||||||
// 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) {
|
||||||
|
val preprocessed = preprocessCommandLineArguments(args, result.errors)
|
||||||
|
parsePreprocessedCommandLineArguments(preprocessed, result)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun <A : CommonToolArguments> parsePreprocessedCommandLineArguments(args: List<String>, result: A) {
|
||||||
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")
|
||||||
|
|||||||
+17
-14
@@ -5,39 +5,42 @@
|
|||||||
|
|
||||||
package org.jetbrains.kotlin.cli.common.arguments
|
package org.jetbrains.kotlin.cli.common.arguments
|
||||||
|
|
||||||
import java.io.*
|
import java.io.File
|
||||||
import java.nio.charset.StandardCharsets
|
import java.io.FileNotFoundException
|
||||||
|
import java.io.IOException
|
||||||
|
import java.io.Reader
|
||||||
|
|
||||||
private val experimentalArgfileArgument = "-Xargfile"
|
private const val EXPERIMENTAL_ARGFILE_ARGUMENT = "-Xargfile"
|
||||||
private val QUOTATION_MARK = '"'
|
|
||||||
private val BACKSLASH = '\\'
|
private const val QUOTATION_MARK = '"'
|
||||||
private val WHITESPACE = ' '
|
private const val BACKSLASH = '\\'
|
||||||
private val NEWLINE = '\n'
|
private const val WHITESPACE = ' '
|
||||||
|
private const val NEWLINE = '\n'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Performs initial preprocessing of arguments, passed to the compiler.
|
* Performs initial preprocessing of arguments, passed to the compiler.
|
||||||
* This is done prior to *any* arguments parsing, and result of preprocessing
|
* This is done prior to *any* arguments parsing, and result of preprocessing
|
||||||
* will be used instead of actual passed arguments.
|
* will be used instead of actual passed arguments.
|
||||||
*/
|
*/
|
||||||
fun <A : CommonToolArguments> preprocessCommandLineArguments(args: List<String>, result: A): List<String> =
|
internal fun preprocessCommandLineArguments(args: List<String>, errors: ArgumentParseErrors): List<String> =
|
||||||
args.flatMap {
|
args.flatMap {
|
||||||
if (it.isArgumentForArgfile)
|
if (it.isArgumentForArgfile)
|
||||||
File(it.argfilePath).expand(result)
|
File(it.argfilePath).expand(errors)
|
||||||
else
|
else
|
||||||
listOf(it)
|
listOf(it)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun <A : CommonToolArguments> File.expand(result: A): List<String> {
|
private fun File.expand(errors: ArgumentParseErrors): List<String> {
|
||||||
return try {
|
return try {
|
||||||
bufferedReader(Charsets.UTF_8).use {
|
bufferedReader(Charsets.UTF_8).use {
|
||||||
generateSequence { it.parseNextArgument() }.toList()
|
generateSequence { it.parseNextArgument() }.toList()
|
||||||
}
|
}
|
||||||
} catch (e: FileNotFoundException) {
|
} catch (e: FileNotFoundException) {
|
||||||
// Process FNFE separately to render absolutePath in error message
|
// Process FNFE separately to render absolutePath in error message
|
||||||
result.errors.argfileErrors += "Argfile not found: $absolutePath"
|
errors.argfileErrors += "Argfile not found: $absolutePath"
|
||||||
emptyList()
|
emptyList()
|
||||||
} catch (e: IOException) {
|
} catch (e: IOException) {
|
||||||
result.errors.argfileErrors += "Error while reading argfile: $e"
|
errors.argfileErrors += "Error while reading argfile: $e"
|
||||||
emptyList()
|
emptyList()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -69,9 +72,9 @@ private fun Reader.consumeRestOfEscapedSequence(sb: StringBuilder) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private val String.argfilePath: String
|
private val String.argfilePath: String
|
||||||
get() = removePrefix("$experimentalArgfileArgument=")
|
get() = removePrefix("$EXPERIMENTAL_ARGFILE_ARGUMENT=")
|
||||||
|
|
||||||
// Note that currently we use only experimental syntax for passing argfiles
|
// Note that currently we use only experimental syntax for passing argfiles
|
||||||
// In 1.3 we can support also javac-like syntax `@argfile`
|
// In 1.3 we can support also javac-like syntax `@argfile`
|
||||||
private val String.isArgumentForArgfile: Boolean
|
private val String.isArgumentForArgfile: Boolean
|
||||||
get() = startsWith("$experimentalArgfileArgument=")
|
get() = startsWith("$EXPERIMENTAL_ARGFILE_ARGUMENT=")
|
||||||
|
|||||||
@@ -41,8 +41,7 @@ abstract class CLITool<A : CommonToolArguments> {
|
|||||||
args: Array<out String>
|
args: Array<out String>
|
||||||
): ExitCode {
|
): ExitCode {
|
||||||
val arguments = createArguments()
|
val arguments = createArguments()
|
||||||
val preprocessedArguments = preprocessCommandLineArguments(args.asList(), arguments)
|
parseCommandLineArguments(args.asList(), arguments)
|
||||||
parseCommandLineArguments(preprocessedArguments, arguments)
|
|
||||||
val collector = PrintingMessageCollector(errStream, messageRenderer, arguments.verbose)
|
val collector = PrintingMessageCollector(errStream, messageRenderer, arguments.verbose)
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@@ -106,8 +105,7 @@ abstract class CLITool<A : CommonToolArguments> {
|
|||||||
|
|
||||||
// Used in kotlin-maven-plugin (KotlinCompileMojoBase) and in kotlin-gradle-plugin (KotlinJvmOptionsImpl, KotlinJsOptionsImpl)
|
// Used in kotlin-maven-plugin (KotlinCompileMojoBase) and in kotlin-gradle-plugin (KotlinJvmOptionsImpl, KotlinJsOptionsImpl)
|
||||||
fun parseArguments(args: Array<out String>, arguments: A) {
|
fun parseArguments(args: Array<out String>, arguments: A) {
|
||||||
val preprocessed = preprocessCommandLineArguments(args.asList(), arguments)
|
parseCommandLineArguments(args.asList(), arguments)
|
||||||
parseCommandLineArguments(preprocessed, arguments)
|
|
||||||
val message = validateArguments(arguments.errors)
|
val message = validateArguments(arguments.errors)
|
||||||
if (message != null) {
|
if (message != null) {
|
||||||
throw IllegalArgumentException(message)
|
throw IllegalArgumentException(message)
|
||||||
|
|||||||
@@ -17,7 +17,6 @@
|
|||||||
package org.jetbrains.kotlin.cli;
|
package org.jetbrains.kotlin.cli;
|
||||||
|
|
||||||
import com.intellij.openapi.util.io.FileUtil;
|
import com.intellij.openapi.util.io.FileUtil;
|
||||||
import com.intellij.openapi.util.io.FileUtilKt;
|
|
||||||
import com.intellij.openapi.util.text.StringUtil;
|
import com.intellij.openapi.util.text.StringUtil;
|
||||||
import kotlin.Pair;
|
import kotlin.Pair;
|
||||||
import kotlin.collections.CollectionsKt;
|
import kotlin.collections.CollectionsKt;
|
||||||
@@ -31,7 +30,6 @@ import org.jetbrains.kotlin.cli.js.K2JSCompiler;
|
|||||||
import org.jetbrains.kotlin.cli.js.dce.K2JSDce;
|
import org.jetbrains.kotlin.cli.js.dce.K2JSDce;
|
||||||
import org.jetbrains.kotlin.cli.jvm.K2JVMCompiler;
|
import org.jetbrains.kotlin.cli.jvm.K2JVMCompiler;
|
||||||
import org.jetbrains.kotlin.cli.metadata.K2MetadataCompiler;
|
import org.jetbrains.kotlin.cli.metadata.K2MetadataCompiler;
|
||||||
import org.jetbrains.kotlin.codegen.TestUtilsKt;
|
|
||||||
import org.jetbrains.kotlin.config.KotlinCompilerVersion;
|
import org.jetbrains.kotlin.config.KotlinCompilerVersion;
|
||||||
import org.jetbrains.kotlin.metadata.jvm.deserialization.JvmMetadataVersion;
|
import org.jetbrains.kotlin.metadata.jvm.deserialization.JvmMetadataVersion;
|
||||||
import org.jetbrains.kotlin.test.CompilerTestUtil;
|
import org.jetbrains.kotlin.test.CompilerTestUtil;
|
||||||
@@ -44,14 +42,14 @@ import org.jetbrains.kotlin.utils.StringsKt;
|
|||||||
import org.junit.Assert;
|
import org.junit.Assert;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.nio.file.Files;
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.stream.Collectors;
|
|
||||||
|
|
||||||
public abstract class AbstractCliTest extends TestCaseWithTmpdir {
|
public abstract class AbstractCliTest extends TestCaseWithTmpdir {
|
||||||
private static final String TESTDATA_DIR = "$TESTDATA_DIR$";
|
private static final String TESTDATA_DIR = "$TESTDATA_DIR$";
|
||||||
|
|
||||||
|
private static final String EXPERIMENTAL_ARGFILE_ARGUMENT_PREFIX = "-Xargfile=";
|
||||||
|
|
||||||
public static Pair<String, ExitCode> executeCompilerGrabOutput(@NotNull CLITool<?> compiler, @NotNull List<String> args) {
|
public static Pair<String, ExitCode> executeCompilerGrabOutput(@NotNull CLITool<?> compiler, @NotNull List<String> args) {
|
||||||
StringBuilder output = new StringBuilder();
|
StringBuilder output = new StringBuilder();
|
||||||
|
|
||||||
@@ -202,21 +200,17 @@ public abstract class AbstractCliTest extends TestCaseWithTmpdir {
|
|||||||
|
|
||||||
String argWithTestPathsReplaced = replaceTestPaths(argWithColonsReplaced, testDataDir, tempDir);
|
String argWithTestPathsReplaced = replaceTestPaths(argWithColonsReplaced, testDataDir, tempDir);
|
||||||
|
|
||||||
if (isArgfileArgument(arg)) {
|
if (arg.startsWith(EXPERIMENTAL_ARGFILE_ARGUMENT_PREFIX)) {
|
||||||
return mockArgfile(argWithTestPathsReplaced, testDataDir, tempDir);
|
return mockArgfile(argWithTestPathsReplaced, testDataDir, tempDir);
|
||||||
} else {
|
}
|
||||||
|
else {
|
||||||
return argWithTestPathsReplaced;
|
return argWithTestPathsReplaced;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static boolean isArgfileArgument(@NotNull String arg) {
|
|
||||||
return arg.startsWith("-Xargfile=");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create new temp. argfile with all test paths replaced and return argfile-argument pointing to that file
|
// Create new temp. argfile with all test paths replaced and return argfile-argument pointing to that file
|
||||||
private static String mockArgfile(@NotNull String argfileArgument, @NotNull String testDataDir, @NotNull String tempDir) {
|
private static String mockArgfile(@NotNull String argfileArgument, @NotNull String testDataDir, @NotNull String tempDir) {
|
||||||
int firstIndexOfArgfilePath = "-Xargfile=".length();
|
String argfilePath = kotlin.text.StringsKt.substringAfter(argfileArgument, EXPERIMENTAL_ARGFILE_ARGUMENT_PREFIX, argfileArgument);
|
||||||
String argfilePath = argfileArgument.substring(firstIndexOfArgfilePath);
|
|
||||||
File argfile = new File(argfilePath);
|
File argfile = new File(argfilePath);
|
||||||
|
|
||||||
if (argfile.exists()) {
|
if (argfile.exists()) {
|
||||||
@@ -224,7 +218,7 @@ public abstract class AbstractCliTest extends TestCaseWithTmpdir {
|
|||||||
String oldArgfileContent = FilesKt.readText(argfile, Charsets.UTF_8);
|
String oldArgfileContent = FilesKt.readText(argfile, Charsets.UTF_8);
|
||||||
String newArgfileContent = replaceTestPaths(oldArgfileContent, testDataDir, tempDir);
|
String newArgfileContent = replaceTestPaths(oldArgfileContent, testDataDir, tempDir);
|
||||||
FilesKt.writeText(mockArgfile, newArgfileContent, Charsets.UTF_8);
|
FilesKt.writeText(mockArgfile, newArgfileContent, Charsets.UTF_8);
|
||||||
return "-Xargfile=" + mockArgfile.getAbsolutePath();
|
return EXPERIMENTAL_ARGFILE_ARGUMENT_PREFIX + mockArgfile.getAbsolutePath();
|
||||||
} else {
|
} else {
|
||||||
return argfileArgument;
|
return argfileArgument;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user