You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@groovy.apache.org by su...@apache.org on 2019/11/22 02:23:37 UTC
[groovy] branch GROOVY_2_5_X updated: GROOVY-8775,
GROOVY-9197: Ant: separate JVM and compilation classpaths
This is an automated email from the ASF dual-hosted git repository.
sunlan pushed a commit to branch GROOVY_2_5_X
in repository https://gitbox.apache.org/repos/asf/groovy.git
The following commit(s) were added to refs/heads/GROOVY_2_5_X by this push:
new 4ba8777 GROOVY-8775, GROOVY-9197: Ant: separate JVM and compilation classpaths
4ba8777 is described below
commit 4ba877777610001da45a96c6e46570f25039b71d
Author: Eric Milles <er...@thomsonreuters.com>
AuthorDate: Fri Nov 15 10:23:46 2019 -0600
GROOVY-8775, GROOVY-9197: Ant: separate JVM and compilation classpaths
---
.../codehaus/groovy/tools/FileSystemCompiler.java | 242 +++++++------
src/spec/doc/tools-groovyc.adoc | 70 ++--
.../main/java/org/codehaus/groovy/ant/Groovyc.java | 393 ++++++++++++---------
3 files changed, 393 insertions(+), 312 deletions(-)
diff --git a/src/main/java/org/codehaus/groovy/tools/FileSystemCompiler.java b/src/main/java/org/codehaus/groovy/tools/FileSystemCompiler.java
index 9a0d042..8c80956 100644
--- a/src/main/java/org/codehaus/groovy/tools/FileSystemCompiler.java
+++ b/src/main/java/org/codehaus/groovy/tools/FileSystemCompiler.java
@@ -41,7 +41,6 @@ import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.io.PrintWriter;
-import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
@@ -54,6 +53,8 @@ import static groovy.ui.GroovyMain.processConfigScripts;
* Command-line compiler (aka. <tt>groovyc</tt>).
*/
public class FileSystemCompiler {
+
+ private static boolean displayStackTraceOnError;
private final CompilationUnit unit;
public FileSystemCompiler(CompilerConfiguration configuration) throws ConfigurationException {
@@ -70,38 +71,40 @@ public class FileSystemCompiler {
}
}
- public void compile(String[] paths) throws Exception {
- unit.addSources(paths);
- unit.compile();
- }
-
- public void compile(File[] files) throws Exception {
- unit.addSources(files);
- unit.compile();
- }
-
- /** Prints the usage help message for {@link CompilationOptions} to stderr.
+ /**
+ * Prints the usage help message for {@link CompilationOptions} to stderr.
+ *
* @see #displayHelp(PrintWriter)
- * @since 2.5 */
+ * @since 2.5
+ */
public static void displayHelp() {
displayHelp(new PrintWriter(System.err, true));
}
- /** Prints the usage help message for the {@link CompilationOptions} to the specified PrintWriter.
- * @since 2.5 */
- public static void displayHelp(final PrintWriter writer) {
+ /**
+ * Prints the usage help message for the {@link CompilationOptions} to the specified PrintWriter.
+ *
+ * @since 2.5
+ */
+ public static void displayHelp(PrintWriter writer) {
configureParser(new CompilationOptions()).usage(writer);
}
- /** Prints version information to stderr.
- * @see #displayVersion(PrintWriter) */
+ /**
+ * Prints version information to stderr.
+ *
+ * @see #displayVersion(PrintWriter)
+ */
public static void displayVersion() {
displayVersion(new PrintWriter(System.err, true));
}
- /** Prints version information to the specified PrintWriter.
- * @since 2.5 */
- public static void displayVersion(final PrintWriter writer) {
+ /**
+ * Prints version information to the specified PrintWriter.
+ *
+ * @since 2.5
+ */
+ public static void displayVersion(PrintWriter writer) {
for (String line : new VersionProvider().getVersion()) {
writer.println(line);
}
@@ -114,10 +117,10 @@ public class FileSystemCompiler {
File file = new File(filename);
if (!file.exists()) {
System.err.println("error: file not found: " + file);
- ++errors;
+ errors += 1;
} else if (!file.canRead()) {
System.err.println("error: file not readable: " + file);
- ++errors;
+ errors += 1;
}
}
@@ -128,8 +131,6 @@ public class FileSystemCompiler {
return checkFiles(filenames) == 0;
}
- private static boolean displayStackTraceOnError = false;
-
/**
* Same as main(args) except that exceptions are thrown out instead of causing
* the VM to exit.
@@ -152,18 +153,18 @@ public class FileSystemCompiler {
displayStackTraceOnError = options.printStack;
CompilerConfiguration configuration = options.toCompilerConfiguration();
- // Load the file name list
- String[] filenames = options.generateFileNames();
- boolean fileNameErrors = filenames == null;
- if (!fileNameErrors && (filenames.length == 0)) {
+ // load the file name list
+ String[] fileNames = options.generateFileNames();
+ boolean fileNameErrors = fileNames == null;
+ if (!fileNameErrors && (fileNames.length == 0)) {
parser.usage(System.err);
return;
}
- fileNameErrors = fileNameErrors && !validateFiles(filenames);
+ fileNameErrors = fileNameErrors && !validateFiles(fileNames);
if (!fileNameErrors) {
- doCompilation(configuration, null, filenames, lookupUnnamedFiles);
+ doCompilation(configuration, null, fileNames, lookupUnnamedFiles);
}
}
@@ -219,8 +220,7 @@ public class FileSystemCompiler {
// if there are any joint compilation options set stubDir if not set
try {
if ((configuration.getJointCompilationOptions() != null)
- && !configuration.getJointCompilationOptions().containsKey("stubDir"))
- {
+ && !configuration.getJointCompilationOptions().containsKey("stubDir")) {
tmpDir = DefaultGroovyStaticMethods.createTempDir(null, "groovy-generated-", "-java-source");
configuration.getJointCompilationOptions().put("stubDir", tmpDir);
}
@@ -235,7 +235,8 @@ public class FileSystemCompiler {
}
} else {
compiler.unit.getClassLoader().setResourceLoader(new GroovyResourceLoader() {
- public URL loadGroovySource(String filename) throws MalformedURLException {
+ @Override
+ public URL loadGroovySource(String filename) {
return null;
}
});
@@ -254,7 +255,7 @@ public class FileSystemCompiler {
if (filenames == null) {
return new String[0];
}
- List<String> fileList = new ArrayList<String>(filenames.size());
+ List<String> fileList = new ArrayList<>(filenames.size());
boolean errors = false;
for (String filename : filenames) {
if (filename.startsWith("@")) {
@@ -289,12 +290,72 @@ public class FileSystemCompiler {
}
}
+ public static void deleteRecursive(File file) {
+ if (!file.exists()) {
+ return;
+ }
+ if (file.isDirectory()) {
+ for (File f : file.listFiles()) {
+ deleteRecursive(f);
+ }
+ }
+ file.delete();
+ }
+
+ public void compile(String[] paths) throws Exception {
+ unit.addSources(paths);
+ unit.compile();
+ }
+
+ public void compile(File[] files) throws Exception {
+ unit.addSources(files);
+ unit.compile();
+ }
+
/**
- * @since 2.5 */
+ * @deprecated use {@link #displayHelp(PrintWriter)} instead
+ */
+ @Deprecated
+ public static void displayHelp(Options options) {
+ final HelpFormatter formatter = new HelpFormatter();
+ formatter.printHelp(80, "groovyc [options] <source-files>", "options:", options, "");
+ }
+
+ // some methods to avoid binary incompatibility - don't gain us a lot but gives the user
+ // something slightly less cryptic than a NoSuchMethodError or an IncompatibleClassChangeError
+ @Deprecated
+ public static CompilerConfiguration generateCompilerConfigurationFromOptions(org.apache.commons.cli.CommandLine cli) {
+ throw new DeprecationException("This method is not supported for Groovy 2.5+. Consider instead using the FileSystemCompiler.CompilationOptions class.");
+ }
+
+ @Deprecated
+ public static String[] generateFileNamesFromOptions(org.apache.commons.cli.CommandLine cli) {
+ throw new DeprecationException("This method is not supported for Groovy 2.5+. Consider instead using the FileSystemCompiler.CompilationOptions class.");
+ }
+
+ @Deprecated
+ public static Options createCompilationOptions() {
+ throw new DeprecationException("This method is not supported for Groovy 2.5+. Consider instead using the FileSystemCompiler.CompilationOptions class.");
+ }
+
+ /**
+ * Creates a temporary directory in the default temporary directory (as specified by the system
+ * property <i>java.io.tmpdir</i>.
+ *
+ * @deprecated Use {@link DefaultGroovyStaticMethods#createTempDir(java.io.File, String, String)} instead.
+ */
+ @Deprecated
+ public static File createTempDir() throws IOException {
+ return DefaultGroovyStaticMethods.createTempDir(null);
+ }
+
+ /**
+ * @since 2.5
+ */
static class VersionProvider implements IVersionProvider {
@Override
public String[] getVersion() {
- return new String[] {
+ return new String[]{
"Groovy compiler version " + GroovySystem.getVersion(),
"Copyright 2003-2019 The Apache Software Foundation. http://groovy-lang.org/",
"",
@@ -303,11 +364,9 @@ public class FileSystemCompiler {
}
/**
- * @since 2.5 */
- @Command(name = "groovyc",
- customSynopsis = "groovyc [options] <source-files>",
- sortOptions = false,
- versionProvider = VersionProvider.class)
+ * @since 2.5
+ */
+ @Command(name = "groovyc", customSynopsis = "groovyc [options] <source-files>", sortOptions = false, versionProvider = VersionProvider.class)
public static class CompilationOptions {
// IMPLEMENTATION NOTE:
// classpath must be the first argument, so that the `startGroovy(.bat)` script
@@ -361,43 +420,41 @@ public class FileSystemCompiler {
@Option(names = {"-v", "--version"}, versionHelp = true, description = "Print version information and exit")
private boolean versionRequested;
- @Parameters(description = "The groovy source files to compile, or @-files containing a list of source files to compile",
- paramLabel = "<source-files>")
+ @Parameters(description = "The groovy source files to compile, or @-files containing a list of source files to compile", paramLabel = "<source-files>")
private List<String> files;
public CompilerConfiguration toCompilerConfiguration() throws IOException {
- // Setup the configuration data
CompilerConfiguration configuration = new CompilerConfiguration();
if (classpath != null) {
configuration.setClasspath(classpath);
}
- if (targetDir != null && targetDir.getName().length() > 0) {
+ if (targetDir != null && !targetDir.getName().isEmpty()) {
configuration.setTargetDirectory(targetDir);
}
configuration.setParameters(parameterMetadata);
configuration.setPreviewFeatures(previewFeatures);
- configuration.setSourceEncoding(encoding);
configuration.setScriptBaseClass(scriptBaseClass);
+ configuration.setSourceEncoding(encoding);
// joint compilation parameters
if (jointCompilation) {
- Map<String, Object> compilerOptions = new HashMap<String, Object>();
- compilerOptions.put("namedValues", javacOptionsList());
- compilerOptions.put("flags", flagsWithParameterMetaData());
+ Map<String, Object> compilerOptions = new HashMap<>();
+ compilerOptions.put("flags", javacFlags());
+ compilerOptions.put("namedValues", javacNamedValues());
configuration.setJointCompilationOptions(compilerOptions);
}
if (indy) {
- configuration.getOptimizationOptions().put("int", false);
- configuration.getOptimizationOptions().put("indy", true);
+ configuration.getOptimizationOptions().put("int", Boolean.FALSE);
+ configuration.getOptimizationOptions().put("indy", Boolean.TRUE);
}
String configScripts = System.getProperty("groovy.starter.configscripts", null);
if (configScript != null || (configScripts != null && !configScripts.isEmpty())) {
- List<String> scripts = new ArrayList<String>();
+ List<String> scripts = new ArrayList<>();
if (configScript != null) {
scripts.add(configScript);
}
@@ -414,76 +471,29 @@ public class FileSystemCompiler {
return generateFileNamesFromOptions(files);
}
- String[] javacOptionsList() {
- if (javacOptionsMap == null) {
- return null;
- }
- List<String> result = new ArrayList<String>();
- for (Map.Entry<String, String> entry : javacOptionsMap.entrySet()) {
- result.add(entry.getKey());
- result.add(entry.getValue());
+ private String[] javacNamedValues() {
+ List<String> result = new ArrayList<>();
+ if (javacOptionsMap != null) {
+ for (Map.Entry<String, String> entry : javacOptionsMap.entrySet()) {
+ result.add(entry.getKey());
+ result.add(entry.getValue());
+ }
}
- return result.toArray(new String[0]);
+ return result.isEmpty() ? null : result.toArray(new String[0]);
}
- String[] flagsWithParameterMetaData() {
- if (flags == null) {
- return null;
+ private String[] javacFlags() {
+ List<String> result = new ArrayList<>();
+ if (flags != null) {
+ result.addAll(flags);
}
if (parameterMetadata) {
- flags.add("parameters");
+ result.add("parameters");
}
- return flags.toArray(new String[0]);
- }
- }
-
- /** @deprecated use {@link #displayHelp(PrintWriter)} instead */
- @Deprecated
- public static void displayHelp(final Options options) {
- final HelpFormatter formatter = new HelpFormatter();
- formatter.printHelp(80, "groovyc [options] <source-files>", "options:", options, "");
- }
-
- // some methods to avoid binary incompatibility - don't gain us a lot but gives the user
- // something slightly less cryptic than a NoSuchMethodError or an IncompatibleClassChangeError
- @Deprecated
- public static CompilerConfiguration generateCompilerConfigurationFromOptions(org.apache.commons.cli.CommandLine cli) throws IOException {
- throw new DeprecationException("This method is not supported for Groovy 2.5+. Consider instead using the FileSystemCompiler.CompilationOptions class.");
- }
-
- @Deprecated
- public static String[] generateFileNamesFromOptions(org.apache.commons.cli.CommandLine cli) {
- throw new DeprecationException("This method is not supported for Groovy 2.5+. Consider instead using the FileSystemCompiler.CompilationOptions class.");
- }
-
- @Deprecated
- public static Options createCompilationOptions() {
- throw new DeprecationException("This method is not supported for Groovy 2.5+. Consider instead using the FileSystemCompiler.CompilationOptions class.");
- }
-
- /**
- * Creates a temporary directory in the default temporary directory (as specified by the system
- * property <i>java.io.tmpdir</i>.
- *
- * @deprecated Use {@link DefaultGroovyStaticMethods#createTempDir(java.io.File, String, String)} instead.
- */
- @Deprecated
- public static File createTempDir() throws IOException {
- return DefaultGroovyStaticMethods.createTempDir(null);
- }
-
- public static void deleteRecursive(File file) {
- if (!file.exists()) {
- return;
- }
- if (file.isFile()) {
- file.delete();
- } else if (file.isDirectory()) {
- File[] files = file.listFiles();
- for (int i = 0; i < files.length; i++) {
- deleteRecursive(files[i]);
+ if (previewFeatures) {
+ result.add("-enable-preview");
}
- file.delete();
+ return result.isEmpty() ? null : result.toArray(new String[0]);
}
}
}
diff --git a/src/spec/doc/tools-groovyc.adoc b/src/spec/doc/tools-groovyc.adoc
index 7871561..6d81049 100644
--- a/src/spec/doc/tools-groovyc.adoc
+++ b/src/spec/doc/tools-groovyc.adoc
@@ -75,16 +75,18 @@ Compiles Groovy source files and, if joint compilation option is used, Java sour
Required taskdef
^^^^^^^^^^^^^^^^
-Assuming all the groovy jars you need are in _my.classpath_ (this will be `groovy-VERSION.jar`,
-`groovy-ant-VERSION.jar` plus any modules and transitive dependencies you might be using)
-you will need to declare this task at some point in the `build.xml` prior to the `groovyc` task being invoked.
+Assuming the groovy jars are in _groovy.libs_, you will need to declare this task
+at some point in the `build.xml` prior to the `groovyc` task being invoked.
[source,xml]
-----------------------------------------------------
-<taskdef name="groovyc"
- classname="org.codehaus.groovy.ant.Groovyc"
- classpathref="my.classpath"/>
-----------------------------------------------------
+-----------------------------------------------------------------------
+<taskdef name="groovyc" classname="org.codehaus.groovy.ant.Groovyc">
+ <classpath>
+ <fileset file="${groovy.libs}/groovy-ant-VERSION.jar"/>
+ <fileset file="${groovy.libs}/groovy-VERSION.jar"/>
+ </classpath>
+</taskdef>
+-----------------------------------------------------------------------
[[ThegroovycAntTask-groovycAttributes]]
<groovyc> Attributes
@@ -166,6 +168,9 @@ you need to set this flag to true. Defaults to false. |No
|parameters |Generates metadata for reflection on method parameter names on JDK 8 and above.
Defaults to false. |No
+|previewFeatures |Enables the JEP preview features on JDK 12 and above.
+Defaults to false. |No
+
|targetBytecode |Sets the bytecode compatibility level. |No
|javahome |Sets the `java.home` value to use, default is the current JDK's home. |No
@@ -187,10 +192,14 @@ the compilation fails. |No
*Example:*
[source,xml]
-----
-<groovyc srcdir="src" destdir="target/classes">
-</groovyc>
-----
+-----------------------------------------------------------------------
+<path id="classpath.main">
+ <fileset dir="${groovy.libs}" includes="*.jar" excludes="groovy-ant-*.jar"/>
+ ...
+</path>
+<groovyc srcdir="${dir.sources}" destdir="${dir.classes}" classpathref="classpath.main"
+ fork="true" includeantruntime="false" configscript="config.groovy" targetBytecode="1.8"/>
+-----------------------------------------------------------------------
[[ThegroovycAntTask-groovycNestedElements]]
@@ -201,48 +210,41 @@ the compilation fails. |No
|==========================================================
|element |kind |Required |Replaces Attribute
|src |a path structure |Yes (unless srcdir is used) |srcdir
-|classpath |a path structure |No |classpath
-|javac |javac task |No |jointCompilationOptions
+|classpath |a path structure |No |classpath or classpathref
+|javac |javac task |No |N/A
|==========================================================
*Notes:*
* For path structures see for example
http://ant.apache.org/manual/using.html#path
-* For usages of the javac task see
+* For usages of the `javac` task see
https://ant.apache.org/manual/Tasks/javac.html
-* The nested javac task behaves more or less as documented for the
-top-level `javac` task. `srcdir`, `destdir`, `classpath`, `encoding` for the
-nested `javac` task are taken from the enclosing `groovyc` task. If these
-attributes are specified then they are added, they do not replace. In
-fact, you should not attempt to overwrite the destination. Other
-attributes and nested elements are unaffected, for example `fork`,
-`memoryMaximumSize`, etc. may be used freely.
+* The nested `javac` task behaves more or less as documented for the top-level
+`javac` task. `srcdir`, `destdir`, `classpath`, `encoding` and `parameters`
+for the nested `javac` task are taken from the enclosing `groovyc` task. If
+these attributes are specified then they are added, they do not replace. In fact,
+you should not attempt to overwrite the destination. Other attributes and nested
+elements are unaffected, for example `fork`, `memoryMaximumSize`, etc. may be
+used freely.
[[ThegroovycAntTask-JointCompilation]]
Joint Compilation
^^^^^^^^^^^^^^^^^
-Joint compilation is enabled by using an embedded `javac` element, as shown in
-the following example:
+Joint compilation is enabled by using an embedded `javac` element, as shown in the following example:
[source,xml]
-----
-<groovyc srcdir="${testSourceDirectory}" destdir="${testClassesDirectory}">
+-----------------------------------------------------------------------
+<groovyc srcdir="${testSourceDirectory}" destdir="${testClassesDirectory}" targetBytecode="1.8">
<classpath>
<pathelement path="${mainClassesDirectory}"/>
<pathelement path="${testClassesDirectory}"/>
<path refid="testPath"/>
</classpath>
- <javac source="1.7" target="1.7" debug="on" />
+ <javac debug="true" source="1.8" target="1.8" />
</groovyc>
-----
-
-It is rare to specify `srcdir` and `destdir`, the nested `javac` task is provided with the `srcdir`
-and `destdir` values from the enclosing `groovyc` task, and it is invariable
-the right thing to do just to leave this as is.
-To restate: the `javac` task gets the `srcdir`, `destdir` and `classpath` from
-the enclosing `groovyc` task.
+-----------------------------------------------------------------------
More details about joint compilation can be found in the <<section-jointcompilation,joint compilation>> section.
diff --git a/subprojects/groovy-ant/src/main/java/org/codehaus/groovy/ant/Groovyc.java b/subprojects/groovy-ant/src/main/java/org/codehaus/groovy/ant/Groovyc.java
index 899536d..dd9b054 100644
--- a/subprojects/groovy-ant/src/main/java/org/codehaus/groovy/ant/Groovyc.java
+++ b/subprojects/groovy-ant/src/main/java/org/codehaus/groovy/ant/Groovyc.java
@@ -32,6 +32,7 @@ import org.apache.tools.ant.types.Path;
import org.apache.tools.ant.types.Reference;
import org.apache.tools.ant.util.GlobPatternMapper;
import org.apache.tools.ant.util.SourceFileScanner;
+import org.codehaus.groovy.GroovyBugError;
import org.codehaus.groovy.control.CompilationUnit;
import org.codehaus.groovy.control.CompilerConfiguration;
import org.codehaus.groovy.control.SourceExtensionHandler;
@@ -39,8 +40,8 @@ import org.codehaus.groovy.runtime.DefaultGroovyMethods;
import org.codehaus.groovy.runtime.DefaultGroovyStaticMethods;
import org.codehaus.groovy.tools.ErrorReporter;
import org.codehaus.groovy.tools.FileSystemCompiler;
-import org.codehaus.groovy.tools.RootLoader;
import org.codehaus.groovy.tools.javac.JavaAwareCompilationUnit;
+import org.objectweb.asm.ClassVisitor;
import picocli.CommandLine;
import java.io.File;
@@ -48,13 +49,14 @@ import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.Writer;
-import java.net.MalformedURLException;
+import java.net.URI;
+import java.net.URISyntaxException;
import java.net.URL;
import java.nio.charset.Charset;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.ArrayList;
-import java.util.Enumeration;
+import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
@@ -68,17 +70,22 @@ import java.util.StringTokenizer;
* <pre>
* <?xml version="1.0"?>
* <project name="MyGroovyBuild" default="compile">
- * <property name="groovy.home" value="/Path/To/Groovy"/>
+ * <property name="groovy.home" location="/Path/To/Groovy"/>
* <property name="groovy.version" value="X.Y.Z"/>
- * <path id="groovy.classpath">
- * <fileset dir="${groovy.home}/lib">
- * <include name="groovy-*${groovy.version}.jar" />
- * </fileset>
- * </path>
- * <taskdef name="groovyc" classname="org.codehaus.groovy.ant.Groovyc" classpathref="groovy.classpath"/>
+ *
+ * <taskdef name="groovyc" classname="org.codehaus.groovy.ant.Groovyc">
+ * <classpath>
+ * <fileset file="${groovy.home}/lib/groovy-${groovy.version}.jar"/>
+ * <fileset file="${groovy.home}/lib/groovy-ant-${groovy.version}.jar"/>
+ * </classpath>
+ * </taskdef>
*
* <target name="compile" description="compile groovy sources">
- * <groovyc srcdir="src" listfiles="true" classpathref="groovy.classpath"/>
+ * <groovyc srcdir="src" destdir="bin" fork="true" listfiles="true" includeantruntime="false"/>
+ * <classpath>
+ * <fileset dir="${groovy.home}/lib" includes="groovy-*${groovy.version}.jar" excludes="groovy-ant-${groovy.version}.jar"/>
+ * </classpath>
+ * </groovyc>
* </target>
* </project>
* </pre>
@@ -86,13 +93,13 @@ import java.util.StringTokenizer;
* This task can take the following arguments:
* <ul>
* <li>srcdir</li>
- * <li>scriptExtension</li>
- * <li>targetBytecode</li>
* <li>destdir</li>
* <li>sourcepath</li>
* <li>sourcepathRef</li>
* <li>classpath</li>
* <li>classpathRef</li>
+ * <li>scriptExtension</li>
+ * <li>targetBytecode</li>
* <li>listfiles</li>
* <li>failonerror</li>
* <li>proceed</li>
@@ -124,39 +131,42 @@ import java.util.StringTokenizer;
* </ul>
* Of these arguments, the <b>srcdir</b> and <b>destdir</b> are required.
* <p>
- * <p>When this task executes, it will recursively scan srcdir and destdir looking for Groovy source files
- * to compile. This task makes its compile decision based on timestamp.
+ * When this task executes, it will recursively scan srcdir and destdir looking
+ * for Groovy source files to compile. This task makes its compile decision based
+ * on timestamp.
* <p>
* A more elaborate build file showing joint compilation:
* <pre>
* <?xml version="1.0"?>
* <project name="MyJointBuild" default="compile">
- * <property name="groovy.home" value="/Path/To/Groovy"/>
+ * <property name="groovy.home" location="/Path/To/Groovy"/>
* <property name="groovy.version" value="X.Y.Z"/>
*
- * <path id="groovy.classpath">
+ * <path id="classpath.main">
* <fileset dir="${groovy.home}/lib">
- * <include name="groovy-*${groovy.version}.jar" />
+ * <include name="groovy-*${groovy.version}.jar"/>
+ * <exclude name="groovy-ant-${groovy.version}.jar"/>
* </fileset>
* </path>
*
- * <target name="clean" description="remove all built files">
- * <delete dir="classes" />
- * </target>
+ * <taskdef name="groovyc" classname="org.codehaus.groovy.ant.Groovyc">
+ * <classpath>
+ * <fileset file="${groovy.home}/lib/groovy-${groovy.version}.jar"/>
+ * <fileset file="${groovy.home}/lib/groovy-ant-${groovy.version}.jar"/>
+ * </classpath>
+ * </taskdef>
*
- * <target name="compile" depends="init" description="compile java and groovy sources">
- * <mkdir dir="classes" />
- * <groovyc destdir="classes" srcdir="src" listfiles="true" keepStubs="true" stubdir="stubs">
- * <javac debug="on" deprecation="true"/>
- * <classpath>
- * <fileset dir="classes"/>
- * <path refid="groovy.classpath"/>
- * </classpath>
- * </groovyc>
+ * <target name="clean">
+ * <delete dir="bin" failonerror="false"/>
* </target>
*
- * <target name="init">
- * <taskdef name="groovyc" classname="org.codehaus.groovy.ant.Groovyc" classpathref="groovy.classpath"/>
+ * <target name="compile" depends="clean" description="compile java and groovy sources">
+ * <mkdir dir="bin"/>
+ *
+ * <groovyc srcdir="src" destdir="bin" stubdir="stubs" keepStubs="true"
+ * fork="true" includeantruntime="false" classpathref="classpath.main">
+ * <javac debug="true" source="1.8" target="1.8"/>
+ * </groovyc>
* </target>
* </project>
* </pre>
@@ -166,9 +176,10 @@ import java.util.StringTokenizer;
* Can also be used from {@link groovy.util.AntBuilder} to allow the build file to be scripted in Groovy.
*/
public class Groovyc extends MatchingTask {
- private static final URL[] EMPTY_URL_ARRAY = new URL[0];
+
private static final File[] EMPTY_FILE_ARRAY = new File[0];
private static final String[] EMPTY_STRING_ARRAY = new String[0];
+
private final LoggingHelper log = new LoggingHelper(this);
private Path src;
@@ -176,20 +187,20 @@ public class Groovyc extends MatchingTask {
private Path compileClasspath;
private Path compileSourcepath;
private String encoding;
- private boolean stacktrace = false;
- private boolean verbose = false;
+ private boolean stacktrace;
+ private boolean verbose;
private boolean includeAntRuntime = true;
- private boolean includeJavaRuntime = false;
- private boolean fork = false;
+ private boolean includeJavaRuntime;
+ private boolean fork;
private File forkJavaHome;
- private String forkedExecutable = null;
+ private String forkedExecutable;
private String memoryInitialSize;
private String memoryMaximumSize;
private String scriptExtension = "*.groovy";
- private String targetBytecode = null;
+ private String targetBytecode;
protected boolean failOnError = true;
- protected boolean listFiles = false;
+ protected boolean listFiles;
protected File[] compileList = EMPTY_FILE_ARRAY;
private String updatedProperty;
@@ -201,7 +212,7 @@ public class Groovyc extends MatchingTask {
private Javac javac;
private boolean jointCompilation;
- private final List<File> temporaryFiles = new ArrayList<File>(2);
+ private final List<File> temporaryFiles = new ArrayList<>(2);
private File stubDir;
private boolean keepStubs;
private boolean forceLookupUnnamedFiles;
@@ -209,17 +220,17 @@ public class Groovyc extends MatchingTask {
private String scriptBaseClass;
private String configscript;
- private Set<String> scriptExtensions = new LinkedHashSet<String>();
+ private Set<String> scriptExtensions = new LinkedHashSet<>();
/**
* If true, generates metadata for reflection on method parameter names (jdk8+ only). Defaults to false.
*/
- private boolean parameters = false;
+ private boolean parameters;
/**
* If true, enable preview Java features (JEP 12) (jdk12+ only). Defaults to false.
*/
- private boolean previewFeatures = false;
+ private boolean previewFeatures;
/**
* Adds a path for source compilation.
@@ -297,7 +308,6 @@ public class Groovyc extends MatchingTask {
* @param version the bytecode compatibility level
*/
public void setTargetBytecode(String version) {
-
for (String allowedJdk : CompilerConfiguration.ALLOWED_JDKS) {
if (allowedJdk.equals(version)) {
this.targetBytecode = version;
@@ -884,7 +894,7 @@ public class Groovyc extends MatchingTask {
*/
protected void resetFileLists() {
compileList = EMPTY_FILE_ARRAY;
- scriptExtensions = new LinkedHashSet<String>();
+ scriptExtensions = new LinkedHashSet<>();
}
/**
@@ -960,64 +970,109 @@ public class Groovyc extends MatchingTask {
}
}
+ /**
+ * If {@code groovyc} task includes a nested {@code javac} task, check for
+ * shareable configuration. {@code FileSystemCompiler} supports several
+ * command-line arguments for configuring joint compilation:
+ * <ul>
+ * <li><tt>-j</tt> enables joint compile
+ * <li><tt>-F</tt> is used to pass flags
+ * <li><tt>-J</tt> is used to pass name=value pairs
+ * </ul>
+ * Joint compilation options are transferred from {@link FileSystemCompiler}
+ * to {@link CompilerConfiguration}'s jointCompileOptions property. Flags
+ * are saved to key "flags" (with the inclusion of "parameters" if enabled
+ * on groovyc), pairs are saved to key "namedValues" and the key "memStub"
+ * may also be set to {@link Boolean#TRUE} to influence joint compilation.
+ *
+ * @see org.codehaus.groovy.tools.javac.JavacJavaCompiler
+ * @see javax.tools.JavaCompiler
+ */
private List<String> extractJointOptions(Path classpath) {
- List<String> jointOptions = new ArrayList<String>();
+ List<String> jointOptions = new ArrayList<>();
if (!jointCompilation) return jointOptions;
- // extract joint options, some get pushed up...
+ // map "debug" and "debuglevel" to "-Fg"
+ if (javac.getDebug()) {
+ String level = javac.getDebugLevel();
+ jointOptions.add("-Fg" + (level == null ? "" : ":" + level));
+ } else {
+ jointOptions.add("-Fg:none");
+ }
+
+ // map "deprecation" to "-Fdeprecation"
+ if (javac.getDeprecation()) {
+ jointOptions.add("-Fdeprecation");
+ }
+
+ // map "nowarn" to "-Fnowarn"
+ if (javac.getNowarn()) {
+ jointOptions.add("-Fnowarn");
+ }
+
+ // map "verbose" to "-Fverbose"
+ if (javac.getVerbose()) {
+ jointOptions.add("-Fverbose");
+ }
+
RuntimeConfigurable rc = javac.getRuntimeConfigurableWrapper();
- for (Object o1 : rc.getAttributeMap().entrySet()) {
- final Map.Entry e = (Map.Entry) o1;
- final String key = e.getKey().toString();
- final String value = getProject().replaceProperties(e.getValue().toString());
- if (key.contains("debug")) {
- String level = "";
- if (javac.getDebugLevel() != null) {
- level = ":" + javac.getDebugLevel();
+
+ for (Map.Entry<String, Object> e : rc.getAttributeMap().entrySet()) {
+ String key = e.getKey();
+ if (key.equals("depend")
+ || key.equals("encoding")
+ || key.equals("extdirs")
+ || key.equals("nativeheaderdir")
+ || key.equals("release")
+ || key.equals("source")
+ || key.equals("target")) {
+ switch (key) {
+ case "nativeheaderdir":
+ key = "h"; break;
+ case "release":
+ key = "-" + key; // to get "--" when passed to javac
}
- jointOptions.add("-Fg" + level);
- } else if (key.contains("debugLevel")) {
- // ignore, taken care of in debug
- } else if ((key.contains("nowarn"))
- || (key.contains("verbose"))
- || (key.contains("deprecation"))) {
- // false is default, so something to do only in true case
- if ("on".equalsIgnoreCase(value) || "true".equalsIgnoreCase(value) || "yes".equalsIgnoreCase(value))
- jointOptions.add("-F" + key);
+ // map "depend", "encoding", etc. to "-Jkey=val"
+ jointOptions.add("-J" + key + "=" + getProject().replaceProperties(e.getValue().toString()));
+
} else if (key.contains("classpath")) {
- classpath.add(javac.getClasspath());
- } else if ((key.contains("depend"))
- || (key.contains("extdirs"))
- || (key.contains("encoding"))
- || (key.contains("source"))
- || (key.contains("target"))
- || (key.contains("verbose"))) { // already handling verbose but pass on too
- jointOptions.add("-J" + key + "=" + value);
- } else {
- log.warn("The option " + key + " cannot be set on the contained <javac> element. The option will be ignored");
+ if (key.startsWith("boot")) {
+ // map "bootclasspath" or "bootclasspathref" to "-Jbootclasspath="
+ jointOptions.add("-Jbootclasspath=" + javac.getBootclasspath());
+ } else {
+ // map "classpath" or "classpathref" to "--classpath"
+ classpath.add(javac.getClasspath());
+ }
+ } else if (key.contains("module") && key.contains("path")) {
+ if (key.startsWith("upgrade")) {
+ // map "upgrademodulepath" or "upgrademodulepathref" to "-J-upgrade-module-path="
+ jointOptions.add("-J-upgrade-module-path=" + javac.getUpgrademodulepath());
+ } else if (key.contains("source")) {
+ // map "modulesourcepath" or "modulesourcepathref" to "-J-module-source-path="
+ jointOptions.add("-J-module-source-path=" + javac.getModulesourcepath());
+ } else {
+ // map "modulepath" or "modulepathref" to "-J-module-path="
+ jointOptions.add("-J-module-path=" + javac.getModulepath());
+ }
+ } else if (!key.contains("debug") && !key.equals("deprecation") && !key.equals("nowarn") && !key.equals("verbose")) {
+ log.warn("The option " + key + " cannot be set on the contained <javac> element. The option will be ignored.");
}
- // TODO includes? excludes?
+ // TODO: defaultexcludes, excludes(file)?, includes(file)?, includeDestClasses, tempdir
}
- // ant's <javac> supports nested <compilerarg value=""> elements (there can be multiple of them)
- // for additional options to be passed to javac.
- Enumeration children = rc.getChildren();
- while (children.hasMoreElements()) {
- RuntimeConfigurable childrc = (RuntimeConfigurable) children.nextElement();
+ // Ant's <javac> supports nested <compilerarg value=""> elements (there
+ // can be multiple of them) for additional options to be passed to javac.
+ for (RuntimeConfigurable childrc : Collections.list(rc.getChildren())) {
if (childrc.getElementTag().equals("compilerarg")) {
- for (Object o : childrc.getAttributeMap().entrySet()) {
- final Map.Entry e = (Map.Entry) o;
- final String key = e.getKey().toString();
+ for (Map.Entry<String, Object> e : childrc.getAttributeMap().entrySet()) {
+ String key = e.getKey();
if (key.equals("value")) {
- final String value = getProject().replaceProperties(e.getValue().toString());
+ String value = getProject().replaceProperties(e.getValue().toString());
StringTokenizer st = new StringTokenizer(value, " ");
while (st.hasMoreTokens()) {
- String optionStr = st.nextToken();
- String replaced = optionStr.replace("-X", "-FX");
- if (optionStr.equals(replaced)) {
- replaced = optionStr.replace("-W", "-FW"); // GROOVY-5063
- }
- jointOptions.add(replaced);
+ String option = st.nextToken();
+ // GROOVY-5063: map "-Werror", etc. to "-FWerror"
+ jointOptions.add(option.replaceFirst("^-(W|X|proc:)", "-F$1"));
}
}
}
@@ -1028,16 +1083,7 @@ public class Groovyc extends MatchingTask {
}
private void doForkCommandLineList(List<String> commandLineList, Path classpath, String separator) {
- if (!fork) return;
-
- if (includeAntRuntime) {
- classpath.addExisting((new Path(getProject())).concatSystemClasspath("last"));
- }
- if (includeJavaRuntime) {
- classpath.addJavaRuntime();
- }
-
- if (forkedExecutable != null && !forkedExecutable.equals("")) {
+ if (forkedExecutable != null && !forkedExecutable.isEmpty()) {
commandLineList.add(forkedExecutable);
} else {
String javaHome;
@@ -1048,39 +1094,64 @@ public class Groovyc extends MatchingTask {
}
commandLineList.add(javaHome + separator + "bin" + separator + "java");
}
- commandLineList.add("-classpath");
- commandLineList.add(getClasspathRelative(classpath));
- final String fileEncodingProp = System.getProperty("file.encoding");
- if ((fileEncodingProp != null) && !fileEncodingProp.equals("")) {
- commandLineList.add("-Dfile.encoding=" + fileEncodingProp);
+ String[] bootstrapClasspath;
+ ClassLoader loader = getClass().getClassLoader();
+ if (loader instanceof AntClassLoader) {
+ bootstrapClasspath = ((AntClassLoader) loader).getClasspath().split(File.pathSeparator);
+ } else {
+ Class<?>[] bootstrapClasses = {
+ FileSystemCompilerFacade.class,
+ FileSystemCompiler.class,
+ antlr.Parser.class,
+ ClassVisitor.class,
+ CommandLine.class,
+ };
+ Set<String> locations = new LinkedHashSet<>();
+ for (Class<?> clazz : bootstrapClasses) {
+ locations.add(new File(getLocation(clazz)).getAbsolutePath());
+ }
+ bootstrapClasspath = locations.toArray(new String[locations.size()]);
}
- if (targetBytecode != null) {
- commandLineList.add("-Dgroovy.target.bytecode=" + targetBytecode);
+ if (bootstrapClasspath.length > 0) {
+ commandLineList.add("-classpath");
+ commandLineList.add(getClasspathRelative(bootstrapClasspath));
}
- if ((memoryInitialSize != null) && !memoryInitialSize.equals("")) {
+ if (memoryInitialSize != null && !memoryInitialSize.isEmpty()) {
commandLineList.add("-Xms" + memoryInitialSize);
}
- if ((memoryMaximumSize != null) && !memoryMaximumSize.equals("")) {
+ if (memoryMaximumSize != null && !memoryMaximumSize.isEmpty()) {
commandLineList.add("-Xmx" + memoryMaximumSize);
}
+ if (targetBytecode != null) {
+ commandLineList.add("-Dgroovy.target.bytecode=" + targetBytecode);
+ }
if (!"*.groovy".equals(getScriptExtension())) {
String tmpExtension = getScriptExtension();
if (tmpExtension.startsWith("*."))
tmpExtension = tmpExtension.substring(1);
commandLineList.add("-Dgroovy.default.scriptExtension=" + tmpExtension);
}
+
commandLineList.add(FileSystemCompilerFacade.class.getName());
+ commandLineList.add("--classpath");
+ if (includeAntRuntime) {
+ classpath.addExisting(new Path(getProject()).concatSystemClasspath("last"));
+ }
+ if (includeJavaRuntime) {
+ classpath.addJavaRuntime();
+ }
+ commandLineList.add(getClasspathRelative(classpath.list()));
if (forceLookupUnnamedFiles) {
commandLineList.add("--forceLookupUnnamedFiles");
}
}
- private String getClasspathRelative(Path classpath) {
+ private String getClasspathRelative(String[] classpath) {
String baseDir = getProject().getBaseDir().getAbsolutePath();
StringBuilder sb = new StringBuilder();
- for (String next : classpath.list()) {
+ for (String next : classpath) {
if (sb.length() > 0) {
sb.append(File.pathSeparatorChar);
}
@@ -1093,6 +1164,14 @@ public class Groovyc extends MatchingTask {
return sb.toString();
}
+ private static URI getLocation(Class<?> clazz) {
+ try {
+ return clazz.getProtectionDomain().getCodeSource().getLocation().toURI();
+ } catch (URISyntaxException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
/**
* Add "groovyc" parameters to the commandLineList, based on the ant configuration.
*
@@ -1174,12 +1253,12 @@ public class Groovyc extends MatchingTask {
}
private String[] makeCommandLine(List<String> commandLineList) {
- log.verbose("Compilation arguments:\n" + DefaultGroovyMethods.join((Iterable)commandLineList, "\n"));
+ log.verbose("Compilation arguments:\n" + DefaultGroovyMethods.join((Iterable<String>) commandLineList, "\n"));
return commandLineList.toArray(EMPTY_STRING_ARRAY);
}
private void runForked(String[] commandLine) {
- final Execute executor = new Execute();
+ Execute executor = new Execute();
executor.setAntRun(getProject());
executor.setWorkingDirectory(getProject().getBaseDir());
executor.setCommandline(commandLine);
@@ -1188,7 +1267,7 @@ public class Groovyc extends MatchingTask {
} catch (final IOException ioe) {
throw new BuildException("Error running forked groovyc.", ioe);
}
- final int returnCode = executor.getExitValue();
+ int returnCode = executor.getExitValue();
if (returnCode != 0) {
taskSuccess = false;
if (errorProperty != null) {
@@ -1214,28 +1293,23 @@ public class Groovyc extends MatchingTask {
if (tmpExtension.startsWith("*."))
tmpExtension = tmpExtension.substring(1);
configuration.setDefaultScriptExtension(tmpExtension);
-
- // Load the file name list
- String[] filenames = options.generateFileNames();
- boolean fileNameErrors = filenames == null;
-
- fileNameErrors = fileNameErrors || !FileSystemCompiler.validateFiles(filenames);
-
if (targetBytecode != null) {
configuration.setTargetBytecode(targetBytecode);
}
+ // Load the file name list
+ String[] fileNames = options.generateFileNames();
+ boolean fileNameErrors = (fileNames == null || !FileSystemCompiler.validateFiles(fileNames));
if (!fileNameErrors) {
try (GroovyClassLoader loader = buildClassLoaderFor()) {
- FileSystemCompiler.doCompilation(configuration, makeCompileUnit(loader), filenames, forceLookupUnnamedFiles);
+ FileSystemCompiler.doCompilation(configuration, makeCompileUnit(loader), fileNames, forceLookupUnnamedFiles);
}
}
-
- } catch (Exception re) {
- Throwable t = re;
- if ((re.getClass() == RuntimeException.class) && (re.getCause() != null)) {
+ } catch (Exception e) {
+ Throwable t = e;
+ if (e.getClass() == RuntimeException.class && e.getCause() != null) {
// unwrap to the real exception
- t = re.getCause();
+ t = e.getCause();
}
Writer writer = new StringBuilderWriter();
new ErrorReporter(t, false).write(new PrintWriter(writer));
@@ -1264,13 +1338,14 @@ public class Groovyc extends MatchingTask {
+ (destDir != null ? " to " + destDir : ""));
listFiles();
- Path classpath = getClasspath() != null ? getClasspath() : new Path(getProject());
- List<String> jointOptions = extractJointOptions(classpath);
- String separator = System.getProperty("file.separator");
- List<String> commandLineList = new ArrayList<String>();
+ Path classpath = getClasspath();
+ if (classpath == null)
+ classpath = new Path(getProject());
+ List<String> jointOptions = extractJointOptions(classpath);
+ List<String> commandLineList = new ArrayList<>();
- doForkCommandLineList(commandLineList, classpath, separator);
+ if (fork) doForkCommandLineList(commandLineList, classpath, File.separator);
doNormalCommandLineList(commandLineList, jointOptions, classpath);
addSourceFiles(commandLineList);
@@ -1324,22 +1399,17 @@ public class Groovyc extends MatchingTask {
}
protected GroovyClassLoader buildClassLoaderFor() {
+ if (fork) {
+ throw new GroovyBugError("Cannot use Groovyc#buildClassLoaderFor() for forked compilation");
+ }
// GROOVY-5044
- if (!fork && !getIncludeantruntime()) {
+ if (!getIncludeantruntime()) {
throw new IllegalArgumentException("The includeAntRuntime=false option is not compatible with fork=false");
}
- final ClassLoader parent =
- AccessController.doPrivileged(
- new PrivilegedAction<ClassLoader>() {
- @Override
- public ClassLoader run() {
- return getIncludeantruntime()
- ? getClass().getClassLoader()
- : new AntClassLoader(new RootLoader(EMPTY_URL_ARRAY, null), getProject(), getClasspath());
- }
- });
- if (parent instanceof AntClassLoader) {
- AntClassLoader antLoader = (AntClassLoader) parent;
+
+ final ClassLoader loader = getClass().getClassLoader();
+ if (loader instanceof AntClassLoader) {
+ AntClassLoader antLoader = (AntClassLoader) loader;
String[] pathElm = antLoader.getClasspath().split(File.pathSeparator);
List<String> classpath = configuration.getClasspath();
/*
@@ -1370,23 +1440,22 @@ public class Groovyc extends MatchingTask {
}
}
- GroovyClassLoader loader =
- AccessController.doPrivileged(
- new PrivilegedAction<GroovyClassLoader>() {
- @Override
- public GroovyClassLoader run() {
- return new GroovyClassLoader(parent, configuration);
- }
- });
+ GroovyClassLoader groovyLoader = AccessController.doPrivileged(new PrivilegedAction<GroovyClassLoader>() {
+ @Override
+ public GroovyClassLoader run() {
+ return new GroovyClassLoader(loader, configuration);
+ }
+ });
if (!forceLookupUnnamedFiles) {
// in normal case we don't need to do script lookups
- loader.setResourceLoader(new GroovyResourceLoader() {
- public URL loadGroovySource(String filename) throws MalformedURLException {
+ groovyLoader.setResourceLoader(new GroovyResourceLoader() {
+ @Override
+ public URL loadGroovySource(String filename) {
return null;
}
});
}
- return loader;
+ return groovyLoader;
}
private Set<String> getScriptExtensions() {
@@ -1395,14 +1464,14 @@ public class Groovyc extends MatchingTask {
private void loadRegisteredScriptExtensions() {
if (scriptExtensions.isEmpty()) {
-
scriptExtensions.add(getScriptExtension().substring(2)); // first extension will be the one set explicitly on <groovyc>
- Path classpath = getClasspath() != null ? getClasspath() : new Path(getProject());
- final String[] pe = classpath.list();
+ Path classpath = getClasspath();
+ if (classpath == null)
+ classpath = new Path(getProject());
try (GroovyClassLoader loader = new GroovyClassLoader(getClass().getClassLoader())) {
- for (String file : pe) {
- loader.addClasspath(file);
+ for (String element : classpath.list()) {
+ loader.addClasspath(element);
}
scriptExtensions.addAll(SourceExtensionHandler.getRegisteredExtensions(loader));
} catch (IOException e) {