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());
+        }
+    }
 }

Reply via email to