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