From 92c77f6c3a1a6ed519dd0903f2b70125b8cebc78 Mon Sep 17 00:00:00 2001 From: Jonathan Pryor Date: Fri, 30 Oct 2020 14:30:59 -0400 Subject: [PATCH] [java-source-utils] Add response file support 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 < OUT.params.txt] [<-D|--output-javadoc> OUT.xml] FILES"; + "\t[<-P|--output-params> OUT.params.txt] [<-D|--output-javadoc> OUT.xml]\n" + + "\t[@RESPONSE-FILE]* FILES"; public static boolean verboseOutput; @@ -111,14 +114,15 @@ private final TypeSolver createTypeSolver(ParserConfiguration config) throws IOE return typeSolver; } - public static JavaSourceUtilsOptions parse(final String[] args) throws IOException { - final JavaSourceUtilsOptions options = new JavaSourceUtilsOptions(); + private final JavaSourceUtilsOptions parse(Iterator args) throws IOException { + if (args == null || !args.hasNext()) + return this; - for (int i = 0; i < args.length; ++i) { - final String arg = args[i]; + while (args.hasNext()) { + String arg = args.next(); switch (arg) { case "-bootclasspath": { - final String bootClassPath = getOptionValue(args, ++i, arg); + final String bootClassPath = getNextOptionValue(args, arg); final ArrayList files = new ArrayList(); for (final String cp : bootClassPath.split(File.pathSeparator)) { final File file = new File(cp); @@ -129,46 +133,46 @@ public static JavaSourceUtilsOptions parse(final String[] args) throws IOExcepti files.add(file); } for (int j = files.size(); j > 0; --j) { - options.jarFiles.add(0, files.get(j-1)); + jarFiles.add(0, files.get(j-1)); } - options.haveBootClassPath = true; + haveBootClassPath = true; break; } case "-a": case "--aar": { - final File file = getOptionFile(args, ++i, arg); + final File file = getNextOptionFile(args, arg); if (file == null) { break; } - options.aarFiles.add(file); + aarFiles.add(file); break; } case "-j": case "--jar": { - final File file = getOptionFile(args, ++i, arg); + final File file = getNextOptionFile(args, arg); if (file == null) { break; } - options.jarFiles.add(file); + jarFiles.add(file); break; } case "-s": case "--source": { - final File dir = getOptionFile(args, ++i, arg); + final File dir = getNextOptionFile(args, arg); if (dir == null) { break; } - options.sourceDirectoryFiles.add(dir); + sourceDirectoryFiles.add(dir); break; } case "-D": case "--output-javadoc": { - options.outputJavadocXml = getOptionValue(args, ++i, arg); + outputJavadocXml = getNextOptionValue(args, arg); break; } case "-P": case "--output-params": { - options.outputParamsTxt = getOptionValue(args, ++i, arg); + outputParamsTxt = getNextOptionValue(args, arg); break; } case "-v": { @@ -180,37 +184,63 @@ public static JavaSourceUtilsOptions parse(final String[] args) throws IOExcepti return null; } default: { - final File file = getOptionFile(args, i, "FILES"); - if (file == null) + if (arg.startsWith("@")) { + // response file? + final String responseFileName = arg.substring(1); + final File responseFile = new File(responseFileName); + if (responseFile.exists()) { + final Iterator lines = + Files.readAllLines(responseFile.toPath()) + .stream() + .filter(line -> line.length() > 0 && !line.startsWith("#")) + .iterator(); + + final JavaSourceUtilsOptions r = parse(lines); + if (r == null) + return null; + break; + } + } + final File file = new File(arg); + if (!file.exists()) { + System.err.println(App.APP_NAME + ": warning: invalid file path for option `FILES`: " + arg); break; + } if (file.isDirectory()) { - options.sourceDirectoryFiles.add(file); + sourceDirectoryFiles.add(file); Files.walk(file.toPath()) .filter(f -> Files.isRegularFile(f) && f.getFileName().toString().endsWith(".java")) .map(Path::toFile) - .forEach(f -> options.inputFiles.add(f)); + .forEach(f -> inputFiles.add(f)); break; } if (file.getName().endsWith(".java")) { - options.inputFiles.add(file); + inputFiles.add(file); break; } if (!file.getName().endsWith(".jar") && !file.getName().endsWith(".zip")) { System.err.println(App.APP_NAME + ": warning: ignoring input file `" + file.getAbsolutePath() +"`."); break; } - if (options.extractedTempDir == null) { - options.extractedTempDir = Files.createTempDirectory("ji-jst").toFile(); + if (extractedTempDir == null) { + extractedTempDir = Files.createTempDirectory("ji-jst").toFile(); } - File toDir = new File(options.extractedTempDir, file.getName()); - options.sourceDirectoryFiles.add(toDir); - extractTo(file, toDir, options.inputFiles); + File toDir = new File(extractedTempDir, file.getName()); + sourceDirectoryFiles.add(toDir); + extractTo(file, toDir, inputFiles); break; } } } - return options; + return this; + } + + public static JavaSourceUtilsOptions parse(final String[] args) throws IOException { + final JavaSourceUtilsOptions options = new JavaSourceUtilsOptions(); + final Iterator a = Arrays.stream(args).iterator(); + + return options.parse(a); } private static void extractTo(final File zipFilePath, final File toDir, final Collection inputFiles) throws IOException { @@ -235,18 +265,18 @@ private static void extractTo(final File zipFilePath, final File toDir, final Co } } - static String getOptionValue(final String[] args, final int index, final String option) { - if (index >= args.length) + static String getNextOptionValue(final Iterator args, final String option) { + if (!args.hasNext()) throw new IllegalArgumentException( - "Expected required value for option `" + option + "` at index " + index + "."); - return args[index]; + "Expected required value for option `" + option + "`."); + return args.next(); } - static File getOptionFile(final String[] args, final int index, final String option) { - if (index >= args.length) + static File getNextOptionFile(final Iterator args, final String option) { + if (!args.hasNext()) throw new IllegalArgumentException( - "Expected required value for option `" + option + "` at index " + index + "."); - final String fileName = args[index]; + "Expected required value for option `" + option + "`."); + final String fileName = args.next(); final File file = new File(fileName); if (!file.exists()) { System.err.println(App.APP_NAME + ": warning: invalid file path for option `" + option + "`: " + fileName); diff --git a/tools/java-source-utils/src/test/java/com/microsoft/android/JavaSourceUtilsOptionsTest.java b/tools/java-source-utils/src/test/java/com/microsoft/android/JavaSourceUtilsOptionsTest.java index 8484cbe74..771b5f85c 100644 --- a/tools/java-source-utils/src/test/java/com/microsoft/android/JavaSourceUtilsOptionsTest.java +++ b/tools/java-source-utils/src/test/java/com/microsoft/android/JavaSourceUtilsOptionsTest.java @@ -3,9 +3,14 @@ */ package com.microsoft.android; +import java.io.File; import java.io.IOException; +import java.io.*; import org.junit.Test; + +import jdk.nashorn.internal.AssertsEnabled; + import static org.junit.Assert.*; public class JavaSourceUtilsOptionsTest { @@ -17,4 +22,52 @@ public class JavaSourceUtilsOptionsTest { options = JavaSourceUtilsOptions.parse(new String[]{"-h"}); assertNull(options); } + + @Test public void testParse_ResponseFiles() throws IOException { + JavaSourceUtilsOptions options; + + final File responseFile = File.createTempFile("jsu-test", ".java"); + final String responseFilePath = responseFile.getAbsolutePath(); + + try (PrintWriter contents = new PrintWriter(responseFile)) { + contents.println("--help"); + } + + options = JavaSourceUtilsOptions.parse(new String[]{"@" + responseFilePath}); + responseFile.delete(); + assertNull(options); + + try (PrintWriter contents = new PrintWriter(responseFile)) { + contents.println("# aar?"); + contents.println("-a"); + contents.println(responseFilePath); + contents.println("# jar?"); + contents.println("-j"); + contents.println(responseFilePath); + contents.println("# source?"); + contents.println("-s"); + contents.println(responseFilePath); + contents.println("-bootclasspath"); + contents.println(responseFilePath + File.pathSeparator + responseFilePath); + contents.println("# params output?"); + contents.println("-P"); + contents.println("params.txt"); + contents.println("# xml javadoc output?"); + contents.println("-D"); + contents.println("javadoc.xml"); + contents.println("# comment; FILEs…"); + contents.println(responseFilePath); + } + + options = JavaSourceUtilsOptions.parse(new String[]{"@" + responseFilePath}); + responseFile.delete(); + + assertEquals(responseFilePath, options.aarFiles.get(0).getAbsolutePath()); + assertEquals(responseFilePath, options.jarFiles.get(0).getAbsolutePath()); + assertEquals("params.txt", options.outputParamsTxt); + assertEquals("javadoc.xml", options.outputJavadocXml); + assertEquals(1, options.inputFiles.size()); + assertEquals(responseFilePath, options.inputFiles.iterator().next().getAbsolutePath()); + assertTrue(options.haveBootClassPath); + } }