Support argfiles in CLI with "@argfile"
#KT-24613 Fixed
This commit is contained in:
+21
-11
@@ -10,7 +10,8 @@ import java.io.FileNotFoundException
|
||||
import java.io.IOException
|
||||
import java.io.Reader
|
||||
|
||||
private const val EXPERIMENTAL_ARGFILE_ARGUMENT = "-Xargfile"
|
||||
const val ARGFILE_ARGUMENT = "@"
|
||||
private const val EXPERIMENTAL_ARGFILE_ARGUMENT = "-Xargfile="
|
||||
|
||||
private const val SINGLE_QUOTE = '\''
|
||||
private const val DOUBLE_QUOTE = '"'
|
||||
@@ -24,11 +25,16 @@ private const val NEWLINE = '\n'
|
||||
* will be used instead of actual passed arguments.
|
||||
*/
|
||||
internal fun preprocessCommandLineArguments(args: List<String>, errors: ArgumentParseErrors): List<String> =
|
||||
args.flatMap {
|
||||
if (it.isArgumentForArgfile)
|
||||
File(it.argfilePath).expand(errors)
|
||||
else
|
||||
listOf(it)
|
||||
args.flatMap { arg ->
|
||||
if (arg.isArgfileArgument) {
|
||||
File(arg.argfilePath).expand(errors)
|
||||
} else if (arg.isDeprecatedArgfileArgument) {
|
||||
errors.deprecatedArguments[EXPERIMENTAL_ARGFILE_ARGUMENT] = ARGFILE_ARGUMENT
|
||||
|
||||
File(arg.deprecatedArgfilePath).expand(errors)
|
||||
} else {
|
||||
listOf(arg)
|
||||
}
|
||||
}
|
||||
|
||||
private fun File.expand(errors: ArgumentParseErrors): List<String> {
|
||||
@@ -80,9 +86,13 @@ private fun Reader.nextChar(): Char? =
|
||||
read().takeUnless { it == -1 }?.toChar()
|
||||
|
||||
private val String.argfilePath: String
|
||||
get() = removePrefix("$EXPERIMENTAL_ARGFILE_ARGUMENT=")
|
||||
get() = removePrefix(ARGFILE_ARGUMENT)
|
||||
|
||||
// Note that currently we use only experimental syntax for passing argfiles
|
||||
// In 1.3 we can support also javac-like syntax `@argfile`
|
||||
private val String.isArgumentForArgfile: Boolean
|
||||
get() = startsWith("$EXPERIMENTAL_ARGFILE_ARGUMENT=")
|
||||
private val String.isArgfileArgument: Boolean
|
||||
get() = startsWith(ARGFILE_ARGUMENT)
|
||||
|
||||
private val String.deprecatedArgfilePath: String
|
||||
get() = removePrefix(EXPERIMENTAL_ARGFILE_ARGUMENT)
|
||||
|
||||
private val String.isDeprecatedArgfileArgument: Boolean
|
||||
get() = startsWith(EXPERIMENTAL_ARGFILE_ARGUMENT)
|
||||
|
||||
@@ -26,6 +26,7 @@ import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.kotlin.cli.common.arguments.Argument;
|
||||
import org.jetbrains.kotlin.cli.common.arguments.CommonToolArguments;
|
||||
import org.jetbrains.kotlin.cli.common.arguments.ParseCommandLineArgumentsKt;
|
||||
import org.jetbrains.kotlin.cli.common.arguments.PreprocessCommandLineArgumentsKt;
|
||||
|
||||
public class Usage {
|
||||
// The magic number 29 corresponds to the similar padding width in javac and scalac command line compilers
|
||||
@@ -33,19 +34,23 @@ public class Usage {
|
||||
|
||||
@NotNull
|
||||
public static <A extends CommonToolArguments> String render(@NotNull CLITool<A> tool, @NotNull A arguments) {
|
||||
boolean extraHelp = arguments.getExtraHelp();
|
||||
StringBuilder sb = new StringBuilder();
|
||||
appendln(sb, "Usage: " + tool.executableScriptFileName() + " <options> <source files>");
|
||||
appendln(sb, "where " + (arguments.getExtraHelp() ? "advanced" : "possible") + " options include:");
|
||||
appendln(sb, "where " + (extraHelp ? "advanced" : "possible") + " options include:");
|
||||
KClass<? extends CommonToolArguments> kClass = JvmClassMappingKt.getKotlinClass(arguments.getClass());
|
||||
for (KCallable<?> callable : kClass.getMembers()) {
|
||||
if (!(callable instanceof KProperty1)) continue;
|
||||
propertyUsage(sb, (KProperty1) callable, arguments.getExtraHelp());
|
||||
propertyUsage(sb, (KProperty1) callable, extraHelp);
|
||||
}
|
||||
|
||||
if (arguments.getExtraHelp()) {
|
||||
if (extraHelp) {
|
||||
appendln(sb, "");
|
||||
appendln(sb, "Advanced options are non-standard and may be changed or removed without any notice.");
|
||||
}
|
||||
else {
|
||||
renderArgfileUsage(sb);
|
||||
}
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
@@ -84,6 +89,17 @@ public class Usage {
|
||||
appendln(sb, argument.description().replace("\n", "\n" + StringsKt.repeat(" ", OPTION_NAME_PADDING_WIDTH)));
|
||||
}
|
||||
|
||||
private static void renderArgfileUsage(@NotNull StringBuilder sb) {
|
||||
int descriptionStart = sb.length() + OPTION_NAME_PADDING_WIDTH;
|
||||
sb.append(" ");
|
||||
sb.append(PreprocessCommandLineArgumentsKt.ARGFILE_ARGUMENT);
|
||||
sb.append("<argfile>");
|
||||
while (sb.length() < descriptionStart) {
|
||||
sb.append(" ");
|
||||
}
|
||||
appendln(sb, "Expand compiler arguments from the given file, containing one argument or file path per line");
|
||||
}
|
||||
|
||||
private static void appendln(@NotNull StringBuilder sb, @NotNull String string) {
|
||||
sb.append(string);
|
||||
StringsKt.appendln(sb);
|
||||
|
||||
+1
@@ -10,4 +10,5 @@ where possible options include:
|
||||
-nowarn Generate no warnings
|
||||
-verbose Enable verbose logging output
|
||||
-version Display compiler version
|
||||
@<argfile> Expand compiler arguments from the given file, containing one argument or file path per line
|
||||
OK
|
||||
|
||||
Vendored
+1
@@ -31,4 +31,5 @@ where possible options include:
|
||||
-nowarn Generate no warnings
|
||||
-verbose Enable verbose logging output
|
||||
-version Display compiler version
|
||||
@<argfile> Expand compiler arguments from the given file, containing one argument or file path per line
|
||||
OK
|
||||
|
||||
@@ -1 +1 @@
|
||||
-Xargfile=$TESTDATA_DIR$/apiVersionLessThanLanguage.argfile
|
||||
@$TESTDATA_DIR$/apiVersionLessThanLanguage.argfile
|
||||
|
||||
+1
-1
@@ -1 +1 @@
|
||||
-Xargfile=$TESTDATA_DIR$/argfileWithEscaping.argfile
|
||||
@$TESTDATA_DIR$/argfileWithEscaping.argfile
|
||||
|
||||
@@ -1 +1 @@
|
||||
-Xargfile=$TESTDATA_DIR$/argfileWithUnfinishedQuoteAndEscape.argfile
|
||||
@$TESTDATA_DIR$/argfileWithUnfinishedQuoteAndEscape.argfile
|
||||
|
||||
Vendored
+1
@@ -29,4 +29,5 @@ where possible options include:
|
||||
-nowarn Generate no warnings
|
||||
-verbose Enable verbose logging output
|
||||
-version Display compiler version
|
||||
@<argfile> Expand compiler arguments from the given file, containing one argument or file path per line
|
||||
OK
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
$TESTDATA_DIR$/apiVersion.kt
|
||||
-d
|
||||
-Xargfile=$TESTDATA_DIR$/mixingArgfilesAndUsualArgs.argfile
|
||||
@$TESTDATA_DIR$/mixingArgfilesAndUsualArgs.argfile
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
$TESTDATA_DIR$/simple.kt
|
||||
-d
|
||||
$TEMP_DIR$
|
||||
-Xargfile=$TESTDATA_DIR$/nonexisting.argfile
|
||||
@$TESTDATA_DIR$/nonexisting.argfile
|
||||
|
||||
@@ -45,10 +45,11 @@ import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import static org.jetbrains.kotlin.cli.common.arguments.PreprocessCommandLineArgumentsKt.ARGFILE_ARGUMENT;
|
||||
|
||||
public abstract class AbstractCliTest extends TestCaseWithTmpdir {
|
||||
private static final String TESTDATA_DIR = "$TESTDATA_DIR$";
|
||||
|
||||
private static final String EXPERIMENTAL_ARGFILE_ARGUMENT_PREFIX = "-Xargfile=";
|
||||
private static final String BUILD_FILE_ARGUMENT_PREFIX = "-Xbuild-file=";
|
||||
|
||||
public static Pair<String, ExitCode> executeCompilerGrabOutput(@NotNull CLITool<?> compiler, @NotNull List<String> args) {
|
||||
@@ -206,10 +207,8 @@ public abstract class AbstractCliTest extends TestCaseWithTmpdir {
|
||||
return createTempFileWithPathsReplaced(argWithTestPathsReplaced, BUILD_FILE_ARGUMENT_PREFIX, ".xml", testDataDir, tempDir);
|
||||
}
|
||||
|
||||
if (arg.startsWith(EXPERIMENTAL_ARGFILE_ARGUMENT_PREFIX)) {
|
||||
return createTempFileWithPathsReplaced(
|
||||
argWithTestPathsReplaced, EXPERIMENTAL_ARGFILE_ARGUMENT_PREFIX, "", testDataDir, tempDir
|
||||
);
|
||||
if (arg.startsWith(ARGFILE_ARGUMENT)) {
|
||||
return createTempFileWithPathsReplaced(argWithTestPathsReplaced, ARGFILE_ARGUMENT, "", testDataDir, tempDir);
|
||||
}
|
||||
|
||||
return argWithTestPathsReplaced;
|
||||
|
||||
Reference in New Issue
Block a user