Skip to content

Commit b588ef5

Browse files
authored
[java-source-utils] Add response file support (dotnet#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

File tree

2 files changed

+118
-35
lines changed

2 files changed

+118
-35
lines changed

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
import java.util.Comparator;
1111
import java.util.Enumeration;
1212
import java.util.HashSet;
13+
import java.util.Iterator;
1314
import java.util.List;
1415
import java.util.ArrayList;
16+
import java.util.Arrays;
1517
import java.util.zip.ZipEntry;
1618
import java.util.zip.ZipFile;
1719

@@ -40,7 +42,8 @@
4042
public class JavaSourceUtilsOptions implements AutoCloseable {
4143
public static final String HELP_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
public static boolean verboseOutput;
4649

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

114-
public static JavaSourceUtilsOptions parse(final String[] args) throws IOException {
115-
final JavaSourceUtilsOptions options = new JavaSourceUtilsOptions();
117+
private final JavaSourceUtilsOptions parse(Iterator<String> args) throws IOException {
118+
if (args == null || !args.hasNext())
119+
return this;
116120

117-
for (int i = 0; i < args.length; ++i) {
118-
final String arg = args[i];
121+
while (args.hasNext()) {
122+
String arg = args.next();
119123
switch (arg) {
120124
case "-bootclasspath": {
121-
final String bootClassPath = getOptionValue(args, ++i, arg);
125+
final String bootClassPath = getNextOptionValue(args, arg);
122126
final ArrayList<File> files = new ArrayList<File>();
123127
for (final String cp : bootClassPath.split(File.pathSeparator)) {
124128
final File file = new File(cp);
@@ -129,46 +133,46 @@ public static JavaSourceUtilsOptions parse(final String[] args) throws IOExcepti
129133
files.add(file);
130134
}
131135
for (int j = 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-
final File file = getOptionFile(args, ++i, arg);
143+
final File file = 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-
final File file = getOptionFile(args, ++i, arg);
152+
final File file = 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-
final File dir = getOptionFile(args, ++i, arg);
161+
final File dir = 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
return null;
181185
}
182186
default: {
183-
final File file = getOptionFile(args, i, "FILES");
184-
if (file == null)
187+
if (arg.startsWith("@")) {
188+
// response file?
189+
final String responseFileName = arg.substring(1);
190+
final File responseFile = new File(responseFileName);
191+
if (responseFile.exists()) {
192+
final Iterator<String> lines =
193+
Files.readAllLines(responseFile.toPath())
194+
.stream()
195+
.filter(line -> line.length() > 0 && !line.startsWith("#"))
196+
.iterator();
197+
198+
final JavaSourceUtilsOptions r = parse(lines);
199+
if (r == null)
200+
return null;
201+
break;
202+
}
203+
}
204+
final File file = new File(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-
File toDir = new File(options.extractedTempDir, file.getName());
207-
options.sourceDirectoryFiles.add(toDir);
208-
extractTo(file, toDir, options.inputFiles);
229+
File toDir = new File(extractedTempDir, file.getName());
230+
sourceDirectoryFiles.add(toDir);
231+
extractTo(file, toDir, inputFiles);
209232
break;
210233
}
211234
}
212235
}
213-
return options;
236+
return this;
237+
}
238+
239+
public static JavaSourceUtilsOptions parse(final String[] args) throws IOException {
240+
final JavaSourceUtilsOptions options = new JavaSourceUtilsOptions();
241+
final Iterator<String> a = Arrays.stream(args).iterator();
242+
243+
return options.parse(a);
214244
}
215245

216246
private static void extractTo(final File zipFilePath, final File toDir, final Collection<File> inputFiles) throws IOException {
@@ -235,18 +265,18 @@ private static void extractTo(final File zipFilePath, final File toDir, final Co
235265
}
236266
}
237267

238-
static String getOptionValue(final String[] args, final int index, final String option) {
239-
if (index >= args.length)
268+
static String getNextOptionValue(final Iterator<String> args, final String option) {
269+
if (!args.hasNext())
240270
throw new IllegalArgumentException(
241-
"Expected required value for option `" + option + "` at index " + index + ".");
242-
return args[index];
271+
"Expected required value for option `" + option + "`.");
272+
return args.next();
243273
}
244274

245-
static File getOptionFile(final String[] args, final int index, final String option) {
246-
if (index >= args.length)
275+
static File getNextOptionFile(final Iterator<String> args, final String option) {
276+
if (!args.hasNext())
247277
throw new IllegalArgumentException(
248-
"Expected required value for option `" + option + "` at index " + index + ".");
249-
final String fileName = args[index];
278+
"Expected required value for option `" + option + "`.");
279+
final String fileName = args.next();
250280
final File file = new File(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
package com.microsoft.android;
55

6+
import java.io.File;
67
import java.io.IOException;
8+
import java.io.*;
79

810
import org.junit.Test;
11+
12+
import jdk.nashorn.internal.AssertsEnabled;
13+
914
import static org.junit.Assert.*;
1015

1116
public class JavaSourceUtilsOptionsTest {
@@ -17,4 +22,52 @@ public class JavaSourceUtilsOptionsTest {
1722
options = JavaSourceUtilsOptions.parse(new String[]{"-h"});
1823
assertNull(options);
1924
}
25+
26+
@Test public void testParse_ResponseFiles() throws IOException {
27+
JavaSourceUtilsOptions options;
28+
29+
final File responseFile = File.createTempFile("jsu-test", ".java");
30+
final String responseFilePath = responseFile.getAbsolutePath();
31+
32+
try (PrintWriter contents = new PrintWriter(responseFile)) {
33+
contents.println("--help");
34+
}
35+
36+
options = JavaSourceUtilsOptions.parse(new String[]{"@" + responseFilePath});
37+
responseFile.delete();
38+
assertNull(options);
39+
40+
try (PrintWriter contents = new PrintWriter(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(new String[]{"@" + 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)