This is an automated email from the ASF dual-hosted git repository.
paulk-asert pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/groovy.git
The following commit(s) were added to refs/heads/master by this push:
new 5f5bf922fd GROOVY-11995: groovyc ant task could support passing
through system properties or jvmargs
5f5bf922fd is described below
commit 5f5bf922fd92518312d7ac593c2f4257dd6c45c4
Author: Paul King <[email protected]>
AuthorDate: Tue May 5 23:30:14 2026 +1000
GROOVY-11995: groovyc ant task could support passing through system
properties or jvmargs
---
.../main/java/org/codehaus/groovy/ant/Groovyc.java | 96 +++++++++++++++++++++-
.../groovy-ant/src/spec/doc/groovyc-ant-task.adoc | 32 ++++++++
.../org/codehaus/groovy/ant/GroovycTest.java | 89 ++++++++++++++++++++
3 files changed, 216 insertions(+), 1 deletion(-)
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 ed87f5f5e3..93ce55c04c 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
@@ -29,7 +29,10 @@ import org.apache.tools.ant.RuntimeConfigurable;
import org.apache.tools.ant.taskdefs.Execute;
import org.apache.tools.ant.taskdefs.Javac;
import org.apache.tools.ant.taskdefs.MatchingTask;
+import org.apache.tools.ant.types.Commandline;
+import org.apache.tools.ant.types.Environment;
import org.apache.tools.ant.types.Path;
+import org.apache.tools.ant.types.PropertySet;
import org.apache.tools.ant.types.Reference;
import org.apache.tools.ant.util.GlobPatternMapper;
import org.apache.tools.ant.util.SourceFileScanner;
@@ -54,6 +57,7 @@ import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
+import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
@@ -233,6 +237,12 @@ public class Groovyc extends MatchingTask {
private String scriptBaseClass;
private String configscript;
+ // GROOVY-11995: forked-mode JVM args / system properties
+ private final Commandline jvmArgs = new Commandline();
+ private final Environment sysProperties = new Environment();
+ private final List<PropertySet> sysPropertySets = new ArrayList<>(0);
+ private boolean inheritAll;
+
private Set<String> scriptExtensions = new LinkedHashSet<>();
/**
@@ -702,6 +712,56 @@ public class Groovyc extends MatchingTask {
jointCompilation = true;
}
+ /**
+ * Adds a JVM argument to be passed to the forked compiler. Only takes
effect
+ * when {@code fork} is true. Use a nested {@code <jvmarg>} element, e.g.
+ * {@code <jvmarg value="--add-opens=java.base/java.lang=ALL-UNNAMED"/>}.
+ *
+ * @return a new {@code Commandline.Argument} that can be configured by Ant
+ * @since 6.0.0
+ */
+ public Commandline.Argument createJvmarg() {
+ return jvmArgs.createArgument();
+ }
+
+ /**
+ * Adds a system property to be passed to the forked compiler as
+ * {@code -Dkey=value}. Only takes effect when {@code fork} is true.
+ *
+ * @param sysp the system property
+ * @since 6.0.0
+ */
+ public void addSysproperty(Environment.Variable sysp) {
+ sysProperties.addVariable(sysp);
+ }
+
+ /**
+ * Adds a set of system properties (Ant {@code <syspropertyset>}) to be
+ * passed to the forked compiler. Only takes effect when {@code fork} is
true.
+ *
+ * @param sysp the property set
+ * @since 6.0.0
+ */
+ public void addSyspropertyset(PropertySet sysp) {
+ sysPropertySets.add(sysp);
+ }
+
+ /**
+ * If true, pass all properties from the parent Ant project to the forked
JVM
+ * as system properties so they can be read via {@code
System.getProperty(name)}.
+ * Only takes effect when {@code fork} is true. Defaults to false.
+ * <p>
+ * For fine-grained control, use a nested {@code <sysproperty>} or
+ * {@code <syspropertyset>} instead. Both may be combined; explicit nested
+ * entries take precedence on name collision.
+ *
+ * @param inheritAll true to inherit all Ant properties into the forked JVM
+ * @since 6.0.0
+ */
+ public void setInheritAll(boolean inheritAll) {
+ this.inheritAll = inheritAll;
+ }
+
/**
* Enable compiler to report stack trace information if a problem occurs
* during compilation. Default is false.
@@ -1092,7 +1152,8 @@ public class Groovyc extends MatchingTask {
return jointOptions;
}
- private void doForkCommandLineList(List<String> commandLineList, Path
classpath, String separator) {
+ // package-private (was private) so tests can verify GROOVY-11995 wiring
end-to-end
+ void doForkCommandLineList(List<String> commandLineList, Path classpath,
String separator) {
if (forkedExecutable != null && !forkedExecutable.isEmpty()) {
commandLineList.add(forkedExecutable);
} else {
@@ -1142,6 +1203,39 @@ public class Groovyc extends MatchingTask {
tmpExtension = tmpExtension.substring(1);
commandLineList.add("-Dgroovy.default.scriptExtension=" +
tmpExtension);
}
+ // GROOVY-11995: user-supplied JVM args / system properties
+ String[] userJvmArgs = jvmArgs.getArguments();
+ for (String arg : userJvmArgs) {
+ if ("-classpath".equals(arg) || "-cp".equals(arg) ||
"--class-path".equals(arg)
+ || arg.startsWith("--class-path=")) {
+ throw new BuildException("Setting the JVM classpath via
<jvmarg> is not supported "
+ + "(would override the forked compiler's bootstrap
classpath). Use the "
+ + "<classpath> nested element instead.",
getLocation());
+ }
+ }
+ Collections.addAll(commandLineList, userJvmArgs);
+ Set<String> sysPropertyNames = new HashSet<>();
+ for (Environment.Variable v : sysProperties.getVariablesVector()) {
+ sysPropertyNames.add(v.getKey());
+ commandLineList.add("-D" + v.getKey() + "=" + v.getValue());
+ }
+ for (PropertySet ps : sysPropertySets) {
+ for (Map.Entry<Object, Object> entry :
ps.getProperties().entrySet()) {
+ String key = entry.getKey().toString();
+ if (sysPropertyNames.add(key)) {
+ commandLineList.add("-D" + key + "=" + entry.getValue());
+ }
+ }
+ }
+ if (inheritAll) {
+ for (Map.Entry<String, Object> entry :
getProject().getProperties().entrySet()) {
+ Object value = entry.getValue();
+ if (value == null) continue;
+ if (sysPropertyNames.add(entry.getKey())) {
+ commandLineList.add("-D" + entry.getKey() + "=" + value);
+ }
+ }
+ }
commandLineList.add(FileSystemCompilerFacade.class.getName());
commandLineList.add("--classpath");
diff --git a/subprojects/groovy-ant/src/spec/doc/groovyc-ant-task.adoc
b/subprojects/groovy-ant/src/spec/doc/groovyc-ant-task.adoc
index 5632924056..559d970494 100644
--- a/subprojects/groovy-ant/src/spec/doc/groovyc-ant-task.adoc
+++ b/subprojects/groovy-ant/src/spec/doc/groovyc-ant-task.adoc
@@ -92,6 +92,10 @@ memory setting. (Examples: 83886080, 81920k, or 80m) |No
VM, if using fork mode; ignored otherwise. Defaults to the standard VM
memory setting. (Examples: 83886080, 81920k, or 80m) |No
+|inheritAll |If enabled and `fork` is true, all properties from the parent Ant
+project are passed to the forked compiler JVM as system properties. For
selective
+passing, use a nested `<sysproperty>` or `<syspropertyset>` instead. Defaults
to false. |No
+
|failonerror |Indicates whether compilation errors will fail the build;
defaults to true. |No
@@ -170,6 +174,9 @@ the compilation fails. |No
|src |a path structure |Yes (unless srcdir is used) |srcdir
|classpath |a path structure |No |classpath or classpathref
|javac |javac task |No |N/A
+|jvmarg |https://ant.apache.org/manual/using.html#arg[command line argument]
|No |N/A
+|sysproperty |an Ant
https://ant.apache.org/manual/Tasks/java.html[`<sysproperty>`] |No |N/A
+|syspropertyset |an Ant
https://ant.apache.org/manual/Types/propertyset.html[`<propertyset>`] |No |N/A
|==========================================================
*Notes:*
@@ -209,3 +216,28 @@ Joint compilation is enabled by using an embedded `javac`
element, as shown in t
-----------------------------------------------------------------------
More details about joint compilation can be found in the
<<{tools-groovyc}#section-jointcompilation,joint compilation>> section.
+
+[[groovyc-ant-task-forked-jvm-args]]
+=== Passing JVM arguments and system properties to a forked compiler
+
+When `fork="true"`, you can attach arbitrary JVM arguments and system
+properties to the spawned compiler JVM via nested `<jvmarg>`,
+`<sysproperty>`, and `<syspropertyset>` elements (mirroring Ant's
+https://ant.apache.org/manual/Tasks/java.html[`<java>`] task). This is
+useful for compile-time configuration consumed by AST transforms, the
+parser/lexer, or annotation processors.
+
+[source,xml]
+-----------------------------------------------------------------------
+<groovyc srcdir="src" destdir="build/classes" fork="true">
+ <jvmarg value="--add-opens=java.base/java.lang=ALL-UNNAMED"/>
+ <sysproperty key="my.compile.flag" value="true"/>
+ <syspropertyset>
+ <propertyref prefix="myapp."/>
+ </syspropertyset>
+</groovyc>
+-----------------------------------------------------------------------
+
+To pass _every_ parent project property at once, set `inheritAll="true"`.
+Explicit `<sysproperty>` and `<syspropertyset>` entries take precedence
+on name collision.
diff --git
a/subprojects/groovy-ant/src/test/groovy/org/codehaus/groovy/ant/GroovycTest.java
b/subprojects/groovy-ant/src/test/groovy/org/codehaus/groovy/ant/GroovycTest.java
index 2b6da8b2dc..e0fa6700f3 100644
---
a/subprojects/groovy-ant/src/test/groovy/org/codehaus/groovy/ant/GroovycTest.java
+++
b/subprojects/groovy-ant/src/test/groovy/org/codehaus/groovy/ant/GroovycTest.java
@@ -21,6 +21,9 @@ package org.codehaus.groovy.ant;
import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.Project;
import org.apache.tools.ant.ProjectHelper;
+import org.apache.tools.ant.types.Environment;
+import org.apache.tools.ant.types.Path;
+import org.apache.tools.ant.types.PropertySet;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
@@ -380,4 +383,90 @@ final class GroovycTest {
var e = assertThrows(BuildException.class, () ->
project.executeTarget("badJavacArgument"));
assertTrue(e.getCause().getMessage().contains("invalid flag:
-Xlint:xxx"));
}
+
+ // GROOVY-11995
+
+ private static Groovyc newForkedGroovyc(Project project) {
+ Groovyc gc = new Groovyc();
+ gc.setProject(project);
+ gc.setFork(true);
+ return gc;
+ }
+
+ private static java.util.List<String> buildForkCommandLine(Groovyc gc) {
+ java.util.List<String> cmd = new java.util.ArrayList<>();
+ gc.doForkCommandLineList(cmd, new Path(gc.getProject()),
File.separator);
+ return cmd;
+ }
+
+ @Test
+ void testJvmargAddedToForkedCommandLine() {
+ Groovyc gc = newForkedGroovyc(project);
+
gc.createJvmarg().setValue("--add-opens=java.base/java.lang=ALL-UNNAMED");
+
assertTrue(buildForkCommandLine(gc).contains("--add-opens=java.base/java.lang=ALL-UNNAMED"));
+ }
+
+ @Test
+ void testSyspropertyAddedToForkedCommandLine() {
+ Groovyc gc = newForkedGroovyc(project);
+ Environment.Variable v = new Environment.Variable();
+ v.setKey("my.compile.flag");
+ v.setValue("true");
+ gc.addSysproperty(v);
+
assertTrue(buildForkCommandLine(gc).contains("-Dmy.compile.flag=true"));
+ }
+
+ @Test
+ void testSyspropertysetAddedToForkedCommandLine() {
+ project.setProperty("myapp.url", "https://example.com");
+ project.setProperty("myapp.timeout", "30");
+ project.setProperty("other.value", "ignored");
+ Groovyc gc = newForkedGroovyc(project);
+ PropertySet ps = new PropertySet();
+ ps.setProject(project);
+ PropertySet.PropertyRef ref = new PropertySet.PropertyRef();
+ ref.setPrefix("myapp.");
+ ps.addPropertyref(ref);
+ gc.addSyspropertyset(ps);
+ java.util.List<String> cmd = buildForkCommandLine(gc);
+ assertTrue(cmd.contains("-Dmyapp.url=https://example.com"));
+ assertTrue(cmd.contains("-Dmyapp.timeout=30"));
+ assertTrue(cmd.stream().noneMatch(s ->
s.startsWith("-Dother.value=")));
+ }
+
+ @Test
+ void testInheritAllPassesProjectProperties() {
+ project.setProperty("alpha", "1");
+ project.setProperty("beta", "two");
+ Groovyc gc = newForkedGroovyc(project);
+ gc.setInheritAll(true);
+ java.util.List<String> cmd = buildForkCommandLine(gc);
+ assertTrue(cmd.contains("-Dalpha=1"));
+ assertTrue(cmd.contains("-Dbeta=two"));
+ }
+
+ @Test
+ void testExplicitSyspropertyTakesPrecedenceOverInheritAll() {
+ project.setProperty("shared", "fromProject");
+ Groovyc gc = newForkedGroovyc(project);
+ Environment.Variable explicit = new Environment.Variable();
+ explicit.setKey("shared");
+ explicit.setValue("fromSysproperty");
+ gc.addSysproperty(explicit);
+ gc.setInheritAll(true);
+ java.util.List<String> cmd = buildForkCommandLine(gc);
+ long count = cmd.stream().filter(s ->
s.startsWith("-Dshared=")).count();
+ assertEquals(1, count);
+ assertTrue(cmd.contains("-Dshared=fromSysproperty"));
+ }
+
+ @Test
+ void testJvmargClasspathRejected() {
+ for (String forbidden : new String[]{"-classpath", "-cp",
"--class-path", "--class-path=foo"}) {
+ Groovyc gc = newForkedGroovyc(project);
+ gc.createJvmarg().setValue(forbidden);
+ BuildException e = assertThrows(BuildException.class, () ->
buildForkCommandLine(gc));
+ assertTrue(e.getMessage().contains("classpath"), "expected
classpath error for " + forbidden + ", got: " + e.getMessage());
+ }
+ }
}