Skip to content
This repository was archived by the owner on Jul 6, 2026. It is now read-only.

Commit b588ef5

Browse files
authored
[java-source-utils] Add response file support (#744)
Add support for ["response files"][0] to `java-source-utils.jar`, allowing `java-source-utils.jar` options to be present within a plain- text file, and the file provided as an option: $ cat >response-file <<EOF --help EOF $ java -jar java-source-utils.jar @response-file # equivalent to: java -jar java-source-utils.jar --help In this case, the "scare quotes" is because of limitations when parsing the response file: only one value per line. Blank lines are ignored, as are lines starting with `#`. Thus, a response file with contents: --source /path/to/source/directory is fine, while a response file with the contents: --source /path/to/source/directory will not be parsed properly. [0]: https://docs.microsoft.com/en-us/windows/win32/midl/response-files
1 parent c3c3575 commit b588ef5

2 files changed

Lines changed: 118 additions & 35 deletions

File tree

‎tools/java-source-utils/src/main/java/com/microsoft/android/JavaSourceUtilsOptions.java‎

Lines changed: 65 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,10 @@
1010
importjava.util.Comparator;
1111
importjava.util.Enumeration;
1212
importjava.util.HashSet;
13+
importjava.util.Iterator;
1314
importjava.util.List;
1415
importjava.util.ArrayList;
16+
importjava.util.Arrays;
1517
importjava.util.zip.ZipEntry;
1618
importjava.util.zip.ZipFile;
1719

@@ -40,7 +42,8 @@
4042
publicclassJavaSourceUtilsOptionsimplementsAutoCloseable {
4143
publicstaticfinalStringHELP_STRING = "[-v] [<-a|--aar> AAR]* [<-j|--jar> JAR]* [<-s|--source> DIRS]*\n" +
4244
"\t[--bootclasspath CLASSPATH]\n" +
43-
"\t[<-P|--output-params> OUT.params.txt] [<-D|--output-javadoc> OUT.xml] FILES";
45+
"\t[<-P|--output-params> OUT.params.txt] [<-D|--output-javadoc> OUT.xml]\n" +
46+
"\t[@RESPONSE-FILE]* FILES";
4447

4548
publicstaticbooleanverboseOutput;
4649

@@ -111,14 +114,15 @@ private final TypeSolver createTypeSolver(ParserConfiguration config) throws IOE
111114
returntypeSolver;
112115
}
113116

114-
publicstaticJavaSourceUtilsOptionsparse(finalString[] args) throwsIOException {
115-
finalJavaSourceUtilsOptionsoptions = newJavaSourceUtilsOptions();
117+
privatefinalJavaSourceUtilsOptionsparse(Iterator<String> args) throwsIOException {
118+
if (args == null || !args.hasNext())
119+
returnthis;
116120

117-
for (inti = 0; i < args.length; ++i) {
118-
finalStringarg= args[i];
121+
while (args.hasNext()) {
122+
Stringarg = args.next();
119123
switch (arg) {
120124
case"-bootclasspath": {
121-
finalStringbootClassPath = getOptionValue(args, ++i, arg);
125+
finalStringbootClassPath = getNextOptionValue(args, arg);
122126
finalArrayList<File> files = newArrayList<File>();
123127
for (finalStringcp : bootClassPath.split(File.pathSeparator)) {
124128
finalFilefile = newFile(cp);
@@ -129,46 +133,46 @@ public static JavaSourceUtilsOptions parse(final String[] args) throws IOExcepti
129133
files.add(file);
130134
}
131135
for (intj = files.size(); j > 0; --j) {
132-
options.jarFiles.add(0, files.get(j-1));
136+
jarFiles.add(0, files.get(j-1));
133137
}
134-
options.haveBootClassPath = true;
138+
haveBootClassPath = true;
135139
break;
136140
}
137141
case"-a":
138142
case"--aar": {
139-
finalFilefile = getOptionFile(args, ++i, arg);
143+
finalFilefile = getNextOptionFile(args, arg);
140144
if (file == null) {
141145
break;
142146
}
143-
options.aarFiles.add(file);
147+
aarFiles.add(file);
144148
break;
145149
}
146150
case"-j":
147151
case"--jar": {
148-
finalFilefile = getOptionFile(args, ++i, arg);
152+
finalFilefile = getNextOptionFile(args, arg);
149153
if (file == null) {
150154
break;
151155
}
152-
options.jarFiles.add(file);
156+
jarFiles.add(file);
153157
break;
154158
}
155159
case"-s":
156160
case"--source": {
157-
finalFiledir = getOptionFile(args, ++i, arg);
161+
finalFiledir = getNextOptionFile(args, arg);
158162
if (dir == null) {
159163
break;
160164
}
161-
options.sourceDirectoryFiles.add(dir);
165+
sourceDirectoryFiles.add(dir);
162166
break;
163167
}
164168
case"-D":
165169
case"--output-javadoc": {
166-
options.outputJavadocXml = getOptionValue(args, ++i, arg);
170+
outputJavadocXml = getNextOptionValue(args, arg);
167171
break;
168172
}
169173
case"-P":
170174
case"--output-params": {
171-
options.outputParamsTxt = getOptionValue(args, ++i, arg);
175+
outputParamsTxt = getNextOptionValue(args, arg);
172176
break;
173177
}
174178
case"-v": {
@@ -180,37 +184,63 @@ public static JavaSourceUtilsOptions parse(final String[] args) throws IOExcepti
180184
returnnull;
181185
}
182186
default: {
183-
finalFilefile = getOptionFile(args, i, "FILES");
184-
if (file == null)
187+
if (arg.startsWith("@")) {
188+
// response file?
189+
finalStringresponseFileName = arg.substring(1);
190+
finalFileresponseFile = newFile(responseFileName);
191+
if (responseFile.exists()) {
192+
finalIterator<String> lines =
193+
Files.readAllLines(responseFile.toPath())
194+
.stream()
195+
.filter(line -> line.length() > 0 && !line.startsWith("#"))
196+
.iterator();
197+
198+
finalJavaSourceUtilsOptionsr = parse(lines);
199+
if (r == null)
200+
returnnull;
201+
break;
202+
}
203+
}
204+
finalFilefile = newFile(arg);
205+
if (!file.exists()) {
206+
System.err.println(App.APP_NAME + ": warning: invalid file path for option `FILES`: " + arg);
185207
break;
208+
}
186209

187210
if (file.isDirectory()) {
188-
options.sourceDirectoryFiles.add(file);
211+
sourceDirectoryFiles.add(file);
189212
Files.walk(file.toPath())
190213
.filter(f -> Files.isRegularFile(f) && f.getFileName().toString().endsWith(".java"))
191214
.map(Path::toFile)
192-
.forEach(f -> options.inputFiles.add(f));
215+
.forEach(f -> inputFiles.add(f));
193216
break;
194217
}
195218
if (file.getName().endsWith(".java")) {
196-
options.inputFiles.add(file);
219+
inputFiles.add(file);
197220
break;
198221
}
199222
if (!file.getName().endsWith(".jar") && !file.getName().endsWith(".zip")) {
200223
System.err.println(App.APP_NAME + ": warning: ignoring input file `" + file.getAbsolutePath() +"`.");
201224
break;
202225
}
203-
if (options.extractedTempDir == null) {
204-
options.extractedTempDir = Files.createTempDirectory("ji-jst").toFile();
226+
if (extractedTempDir == null) {
227+
extractedTempDir = Files.createTempDirectory("ji-jst").toFile();
205228
}
206-
FiletoDir = newFile(options.extractedTempDir, file.getName());
207-
options.sourceDirectoryFiles.add(toDir);
208-
extractTo(file, toDir, options.inputFiles);
229+
FiletoDir = newFile(extractedTempDir, file.getName());
230+
sourceDirectoryFiles.add(toDir);
231+
extractTo(file, toDir, inputFiles);
209232
break;
210233
}
211234
}
212235
}
213-
returnoptions;
236+
returnthis;
237+
}
238+
239+
publicstaticJavaSourceUtilsOptionsparse(finalString[] args) throwsIOException {
240+
finalJavaSourceUtilsOptionsoptions = newJavaSourceUtilsOptions();
241+
finalIterator<String> a = Arrays.stream(args).iterator();
242+
243+
returnoptions.parse(a);
214244
}
215245

216246
privatestaticvoidextractTo(finalFilezipFilePath, finalFiletoDir, finalCollection<File> inputFiles) throwsIOException {
@@ -235,18 +265,18 @@ private static void extractTo(final File zipFilePath, final File toDir, final Co
235265
}
236266
}
237267

238-
staticStringgetOptionValue(finalString[]args, finalintindex, finalStringoption) {
239-
if (index >= args.length)
268+
staticStringgetNextOptionValue(finalIterator<String>args, finalStringoption) {
269+
if (!args.hasNext())
240270
thrownewIllegalArgumentException(
241-
"Expected required value for option `" + option + "` at index " + index + ".");
242-
returnargs[index];
271+
"Expected required value for option `" + option + "`.");
272+
returnargs.next();
243273
}
244274

245-
staticFilegetOptionFile(finalString[]args, finalintindex, finalStringoption) {
246-
if (index >= args.length)
275+
staticFilegetNextOptionFile(finalIterator<String>args, finalStringoption) {
276+
if (!args.hasNext())
247277
thrownewIllegalArgumentException(
248-
"Expected required value for option `" + option + "` at index " + index + ".");
249-
finalStringfileName = args[index];
278+
"Expected required value for option `" + option + "`.");
279+
finalStringfileName = args.next();
250280
finalFilefile = newFile(fileName);
251281
if (!file.exists()) {
252282
System.err.println(App.APP_NAME + ": warning: invalid file path for option `" + option + "`: " + fileName);

‎tools/java-source-utils/src/test/java/com/microsoft/android/JavaSourceUtilsOptionsTest.java‎

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,14 @@
33
*/
44
packagecom.microsoft.android;
55

6+
importjava.io.File;
67
importjava.io.IOException;
8+
importjava.io.*;
79

810
importorg.junit.Test;
11+
12+
importjdk.nashorn.internal.AssertsEnabled;
13+
914
importstaticorg.junit.Assert.*;
1015

1116
publicclassJavaSourceUtilsOptionsTest {
@@ -17,4 +22,52 @@ public class JavaSourceUtilsOptionsTest {
1722
options = JavaSourceUtilsOptions.parse(newString[]{"-h"});
1823
assertNull(options);
1924
}
25+
26+
@TestpublicvoidtestParse_ResponseFiles() throwsIOException {
27+
JavaSourceUtilsOptionsoptions;
28+
29+
finalFileresponseFile = File.createTempFile("jsu-test", ".java");
30+
finalStringresponseFilePath = responseFile.getAbsolutePath();
31+
32+
try (PrintWritercontents = newPrintWriter(responseFile)) {
33+
contents.println("--help");
34+
}
35+
36+
options = JavaSourceUtilsOptions.parse(newString[]{"@" + responseFilePath});
37+
responseFile.delete();
38+
assertNull(options);
39+
40+
try (PrintWritercontents = newPrintWriter(responseFile)) {
41+
contents.println("# aar?");
42+
contents.println("-a");
43+
contents.println(responseFilePath);
44+
contents.println("# jar?");
45+
contents.println("-j");
46+
contents.println(responseFilePath);
47+
contents.println("# source?");
48+
contents.println("-s");
49+
contents.println(responseFilePath);
50+
contents.println("-bootclasspath");
51+
contents.println(responseFilePath + File.pathSeparator + responseFilePath);
52+
contents.println("# params output?");
53+
contents.println("-P");
54+
contents.println("params.txt");
55+
contents.println("# xml javadoc output?");
56+
contents.println("-D");
57+
contents.println("javadoc.xml");
58+
contents.println("# comment; FILEs…");
59+
contents.println(responseFilePath);
60+
}
61+
62+
options = JavaSourceUtilsOptions.parse(newString[]{"@" + responseFilePath});
63+
responseFile.delete();
64+
65+
assertEquals(responseFilePath, options.aarFiles.get(0).getAbsolutePath());
66+
assertEquals(responseFilePath, options.jarFiles.get(0).getAbsolutePath());
67+
assertEquals("params.txt", options.outputParamsTxt);
68+
assertEquals("javadoc.xml", options.outputJavadocXml);
69+
assertEquals(1, options.inputFiles.size());
70+
assertEquals(responseFilePath, options.inputFiles.iterator().next().getAbsolutePath());
71+
assertTrue(options.haveBootClassPath);
72+
}
2073
}

0 commit comments

Comments
 (0)