8173308: JAVAC_OPTIONS should be updated to align with JAVA_OPTIONS
Reviewed-by: jjg
--- a/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/main/CommandLine.java Wed Feb 15 16:18:18 2017 -0800
+++ b/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/main/CommandLine.java Wed Feb 15 18:07:28 2017 -0800
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1999, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1999, 2017, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -29,8 +29,9 @@
import java.io.Reader;
import java.nio.file.Files;
import java.nio.file.Paths;
-
-import com.sun.tools.javac.util.ListBuffer;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
/**
* Various utility methods for processing Java tool command line arguments.
@@ -55,28 +56,80 @@
* @throws IOException if there is a problem reading any of the @files
*/
public static String[] parse(String[] args) throws IOException {
- ListBuffer<String> newArgs = new ListBuffer<>();
+ List<String> newArgs = new ArrayList<>();
+ appendParsedCommandArgs(newArgs, Arrays.asList(args));
+ return newArgs.toArray(new String[newArgs.size()]);
+ }
+
+ private static void appendParsedCommandArgs(List<String> newArgs, List<String> args) throws IOException {
for (String arg : args) {
if (arg.length() > 1 && arg.charAt(0) == '@') {
arg = arg.substring(1);
if (arg.charAt(0) == '@') {
- newArgs.append(arg);
+ newArgs.add(arg);
} else {
loadCmdFile(arg, newArgs);
}
} else {
- newArgs.append(arg);
+ newArgs.add(arg);
}
}
- return newArgs.toList().toArray(new String[newArgs.length()]);
}
- private static void loadCmdFile(String name, ListBuffer<String> args) throws IOException {
+ /**
+ * Process the given environment variable and appends any Win32-style
+ * command files for the specified command line arguments and return
+ * the resulting arguments. A command file argument
+ * is of the form '@file' where 'file' is the name of the file whose
+ * contents are to be parsed for additional arguments. The contents of
+ * the command file are parsed using StreamTokenizer and the original
+ * '@file' argument replaced with the resulting tokens. Recursive command
+ * files are not supported. The '@' character itself can be quoted with
+ * the sequence '@@'.
+ * @param envVariable the env variable to process
+ * @param args the arguments that may contain @files
+ * @return the arguments, with environment variable's content and expansion of @files
+ * @throws IOException if there is a problem reading any of the @files
+ * @throws com.sun.tools.javac.main.CommandLine.UnmatchedQuote
+ */
+ public static List<String> parse(String envVariable, List<String> args)
+ throws IOException, UnmatchedQuote {
+
+ List<String> inArgs = new ArrayList<>();
+ appendParsedEnvVariables(inArgs, envVariable);
+ inArgs.addAll(args);
+ List<String> newArgs = new ArrayList<>();
+ appendParsedCommandArgs(newArgs, inArgs);
+ return newArgs;
+ }
+
+ /**
+ * Process the given environment variable and appends any Win32-style
+ * command files for the specified command line arguments and return
+ * the resulting arguments. A command file argument
+ * is of the form '@file' where 'file' is the name of the file whose
+ * contents are to be parsed for additional arguments. The contents of
+ * the command file are parsed using StreamTokenizer and the original
+ * '@file' argument replaced with the resulting tokens. Recursive command
+ * files are not supported. The '@' character itself can be quoted with
+ * the sequence '@@'.
+ * @param envVariable the env variable to process
+ * @param args the arguments that may contain @files
+ * @return the arguments, with environment variable's content and expansion of @files
+ * @throws IOException if there is a problem reading any of the @files
+ * @throws com.sun.tools.javac.main.CommandLine.UnmatchedQuote
+ */
+ public static String[] parse(String envVariable, String[] args) throws IOException, UnmatchedQuote {
+ List<String> out = parse(envVariable, Arrays.asList(args));
+ return out.toArray(new String[out.size()]);
+ }
+
+ private static void loadCmdFile(String name, List<String> args) throws IOException {
try (Reader r = Files.newBufferedReader(Paths.get(name))) {
Tokenizer t = new Tokenizer(r);
String s;
while ((s = t.nextToken()) != null) {
- args.append(s);
+ args.add(s);
}
}
}
@@ -188,4 +241,75 @@
}
}
}
+
+ @SuppressWarnings("fallthrough")
+ private static void appendParsedEnvVariables(List<String> newArgs, String envVariable)
+ throws UnmatchedQuote {
+
+ if (envVariable == null) {
+ return;
+ }
+ String in = System.getenv(envVariable);
+ if (in == null || in.trim().isEmpty()) {
+ return;
+ }
+
+ final char NUL = (char)0;
+ final int len = in.length();
+
+ int pos = 0;
+ StringBuilder sb = new StringBuilder();
+ char quote = NUL;
+ char ch;
+
+ loop:
+ while (pos < len) {
+ ch = in.charAt(pos);
+ switch (ch) {
+ case '\"': case '\'':
+ if (quote == NUL) {
+ quote = ch;
+ } else if (quote == ch) {
+ quote = NUL;
+ } else {
+ sb.append(ch);
+ }
+ pos++;
+ break;
+ case '\f': case '\n': case '\r': case '\t': case ' ':
+ if (quote == NUL) {
+ newArgs.add(sb.toString());
+ sb.setLength(0);
+ while (ch == '\f' || ch == '\n' || ch == '\r' || ch == '\t' || ch == ' ') {
+ pos++;
+ if (pos >= len) {
+ break loop;
+ }
+ ch = in.charAt(pos);
+ }
+ break;
+ }
+ // fall through
+ default:
+ sb.append(ch);
+ pos++;
+ }
+ }
+ if (sb.length() != 0) {
+ newArgs.add(sb.toString());
+ }
+ if (quote != NUL) {
+ throw new UnmatchedQuote(envVariable);
+ }
+ }
+
+ public static class UnmatchedQuote extends Exception {
+ private static final long serialVersionUID = 0;
+
+ public final String variableName;
+
+ UnmatchedQuote(String variable) {
+ this.variableName = variable;
+ }
+ }
}
--- a/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/main/Main.java Wed Feb 15 16:18:18 2017 -0800
+++ b/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/main/Main.java Wed Feb 15 18:07:28 2017 -0800
@@ -43,6 +43,7 @@
import com.sun.tools.javac.file.BaseFileManager;
import com.sun.tools.javac.file.JavacFileManager;
import com.sun.tools.javac.jvm.Target;
+import com.sun.tools.javac.main.CommandLine.UnmatchedQuote;
import com.sun.tools.javac.platform.PlatformDescription;
import com.sun.tools.javac.processing.AnnotationProcessingError;
import com.sun.tools.javac.util.*;
@@ -80,6 +81,7 @@
*/
boolean apiMode;
+ private static final String ENV_OPT_NAME = "JDK_JAVAC_OPTIONS";
/** Result codes.
*/
@@ -201,19 +203,12 @@
return Result.CMDERR;
}
- // prefix argv with contents of _JAVAC_OPTIONS if set
- String envOpt = System.getenv("_JAVAC_OPTIONS");
- if (envOpt != null && !envOpt.trim().isEmpty()) {
- String[] envv = envOpt.split("\\s+");
- String[] result = new String[envv.length + argv.length];
- System.arraycopy(envv, 0, result, 0, envv.length);
- System.arraycopy(argv, 0, result, envv.length, argv.length);
- argv = result;
- }
-
- // expand @-files
+ // prefix argv with contents of environment variable and expand @-files
try {
- argv = CommandLine.parse(argv);
+ argv = CommandLine.parse(ENV_OPT_NAME, argv);
+ } catch (UnmatchedQuote ex) {
+ error("err.unmatched.quote", ex.variableName);
+ return Result.CMDERR;
} catch (FileNotFoundException | NoSuchFileException e) {
warning("err.file.not.found", e.getMessage());
return Result.SYSERR;
--- a/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/javac.properties Wed Feb 15 16:18:18 2017 -0800
+++ b/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/javac.properties Wed Feb 15 18:07:28 2017 -0800
@@ -377,6 +377,9 @@
javac.err.repeated.value.for.patch.module=\
--patch-module specified more than once for {0}
+javac.err.unmatched.quote=\
+ unmatched quote in environment variable %s
+
## messages
javac.msg.usage.header=\
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/tools/javac/main/EnvVariableTest.java Wed Feb 15 18:07:28 2017 -0800
@@ -0,0 +1,151 @@
+/*
+ * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test
+ * @bug 8173308
+ * @summary Check JDK_JAVA_OPTIONS parsing behavior
+ * @library /tools/lib
+ * @modules jdk.compiler/com.sun.tools.javac.main
+ * @build toolbox.ToolBox toolbox.TestRunner
+ * @run main EnvVariableTest
+ */
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.PrintStream;
+import java.nio.file.Path;
+
+import toolbox.*;
+
+import com.sun.tools.javac.main.CommandLine;
+
+public class EnvVariableTest extends TestRunner {
+ final String testClasses;
+ final ToolBox tb;
+ final Path javaExePath;
+ final ExecTask task;
+ final PrintStream ostream;
+ final ByteArrayOutputStream baos;
+
+ public EnvVariableTest() {
+ super(System.err);
+ ostream = System.err;
+ baos = new ByteArrayOutputStream();
+ testClasses = System.getProperty("test.classes");
+ tb = new ToolBox();
+ javaExePath = tb.getJDKTool("java");
+ task = new ExecTask(tb, javaExePath);
+ }
+
+ public static void main(String... args) throws Exception {
+ EnvVariableTest t = new EnvVariableTest();
+ t.runTests();
+ }
+
+ @Test
+ public void testDoubleQuote() throws Exception {
+ // white space quoted with double quotes
+ test("-version -cp \"c:\\\\java libs\\\\one.jar\" \n",
+ "-version", "-cp", "c:\\\\java libs\\\\one.jar");
+ }
+
+ @Test
+ public void testSingleQuote() throws Exception {
+ // white space quoted with single quotes
+ test("-version -cp \'c:\\\\java libs\\\\one.jar\' \n",
+ "-version", "-cp", "c:\\\\java libs\\\\one.jar");
+ }
+
+ @Test
+ public void testEscapeCharacters() throws Exception {
+ // escaped characters
+ test("escaped chars testing \"\\a\\b\\c\\f\\n\\r\\t\\v\\9\\6\\23\\82\\28\\377\\477\\278\\287\"",
+ "escaped", "chars", "testing", "\\a\\b\\c\\f\\n\\r\\t\\v\\9\\6\\23\\82\\28\\377\\477\\278\\287");
+ }
+
+ @Test
+ public void testMixedQuotes() throws Exception {
+ // more mixing of quote types
+ test("\"mix 'single quote' in double\" 'mix \"double quote\" in single' partial\"quote me\"this",
+ "mix 'single quote' in double", "mix \"double quote\" in single", "partialquote methis");
+ }
+
+ @Test
+ public void testWhiteSpaces() throws Exception {
+ // whitespace tests
+ test("line one #comment\n'line #2' #rest are comment\r\n#comment on line 3\fline 4 #comment to eof",
+ "line", "one", "#comment", "line #2", "#rest", "are", "comment", "#comment", "on", "line",
+ "3", "line", "4", "#comment", "to", "eof");
+ }
+
+ @Test
+ public void testMismatchedDoubleQuote() throws Exception {
+ // mismatched quote
+ test("This is an \"open quote \n across line\n\t, note for WS.",
+ "Exception: JDK_JAVAC_OPTIONS");
+ }
+
+ @Test
+ public void testMismatchedSingleQuote() throws Exception {
+ // mismatched quote
+ test("This is an \'open quote \n across line\n\t, note for WS.",
+ "Exception: JDK_JAVAC_OPTIONS");
+ }
+
+ void test(String full, String... expectedArgs) throws Exception {
+ task.envVar("JDK_JAVAC_OPTIONS", full);
+ task.args("--add-exports", "jdk.compiler/com.sun.tools.javac.main=ALL-UNNAMED",
+ "-cp", testClasses, "EnvVariableTest$Tester");
+ Task.Result tr = task.run(Task.Expect.SUCCESS);
+ String expected = Tester.arrayToString(expectedArgs);
+ String in = tr.getOutput(Task.OutputKind.STDOUT);
+ System.err.println("Matching...");
+ System.err.println("Obtained: " + in);
+ System.err.println("Expected: " + expected);
+ if (in.contains(expected)) {
+ System.err.println("....OK");
+ return;
+ }
+ throw new Exception("Expected strings not found");
+ }
+
+ /**
+ * A tester class that is invoked to invoke the CommandLine class, and
+ * print the result.
+ */
+ public static class Tester {
+ private static final String[] EMPTY_ARRAY = new String[0];
+ static String arrayToString(String... args) {
+ return String.join(", ", args);
+ }
+ public static void main(String... args) throws IOException {
+ try {
+ String[] argv = CommandLine.parse("JDK_JAVAC_OPTIONS", EMPTY_ARRAY);
+ System.out.print(arrayToString(argv));
+ } catch (CommandLine.UnmatchedQuote ex) {
+ System.out.print("Exception: " + ex.variableName);
+ }
+ }
+ }
+}
--- a/langtools/test/tools/javac/modules/EnvVarTest.java Wed Feb 15 16:18:18 2017 -0800
+++ b/langtools/test/tools/javac/modules/EnvVarTest.java Wed Feb 15 18:07:28 2017 -0800
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 2017, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -24,7 +24,7 @@
/*
* @test
* @bug 8156962
- * @summary Tests use of _JAVAC_OPTIONS env variable
+ * @summary Tests use of JDK_JAVAC_OPTIONS env variable
* @library /tools/lib
* @modules jdk.compiler/com.sun.tools.javac.api
* jdk.compiler/com.sun.tools.javac.main
@@ -71,7 +71,7 @@
tb.out.println("test that addExports can be provided with env variable");
new JavacTask(tb, Mode.EXEC)
- .envVar("_JAVAC_OPTIONS", "--add-exports jdk.compiler/com.sun.tools.javac.jvm=ALL-UNNAMED")
+ .envVar("JDK_JAVAC_OPTIONS", "--add-exports jdk.compiler/com.sun.tools.javac.jvm=ALL-UNNAMED")
.outdir(classes)
.files(findJavaFiles(src))
.run(Expect.SUCCESS)
@@ -83,7 +83,7 @@
"--add-exports jdk.compiler/com.sun.tools.javac.jvm=ALL-UNNAMED");
new JavacTask(tb, Mode.EXEC)
- .envVar("_JAVAC_OPTIONS", "@" + atFile)
+ .envVar("JDK_JAVAC_OPTIONS", "@" + atFile)
.outdir(classes)
.files(findJavaFiles(src))
.run(Expect.SUCCESS)
--- a/langtools/test/tools/javac/modules/InheritRuntimeEnvironmentTest.java Wed Feb 15 16:18:18 2017 -0800
+++ b/langtools/test/tools/javac/modules/InheritRuntimeEnvironmentTest.java Wed Feb 15 18:07:28 2017 -0800
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -238,7 +238,7 @@
Arrays.asList("--add-exports", "jdk.compiler/com.sun.tools.javac.main=ALL-UNNAMED");
List<Path> files = Arrays.asList(findJavaFiles(src));
- String envName = "_JAVAC_OPTIONS";
+ String envName = "JDK_JAVAC_OPTIONS";
String envValue = String.join(" ", testOpts);
out.println(" javac:");