You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@groovy.apache.org by pa...@apache.org on 2026/04/21 06:13:12 UTC

(groovy) 01/34: GROOVY-9057 — non-zero exit code on errors

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

commit e3e5648837d731c972a1111ac165182aa926c82b
Author: Paul King <pa...@asert.com.au>
AuthorDate: Mon Apr 20 07:12:24 2026 +1000

    GROOVY-9057 — non-zero exit code on errors
---
 .../java/org/codehaus/groovy/ant/Groovydoc.java    |  6 ++++-
 .../codehaus/groovy/tools/groovydoc/Main.groovy    | 18 ++++++++++++---
 .../groovy/tools/groovydoc/GroovyDocTool.java      |  7 ++++++
 .../tools/groovydoc/GroovyRootDocBuilder.java      |  6 +++++
 .../groovy/tools/groovydoc/GroovyDocToolTest.java  | 10 +++++++++
 .../src/test/resources/broken/BrokenSyntax.groovy  | 26 ++++++++++++++++++++++
 6 files changed, 69 insertions(+), 4 deletions(-)

diff --git a/subprojects/groovy-ant/src/main/java/org/codehaus/groovy/ant/Groovydoc.java b/subprojects/groovy-ant/src/main/java/org/codehaus/groovy/ant/Groovydoc.java
index b23f4b0c95..ec4b53fb90 100644
--- a/subprojects/groovy-ant/src/main/java/org/codehaus/groovy/ant/Groovydoc.java
+++ b/subprojects/groovy-ant/src/main/java/org/codehaus/groovy/ant/Groovydoc.java
@@ -488,7 +488,11 @@ public class Groovydoc extends Task {
             FileOutputTool output = new FileOutputTool();
             htmlTool.renderToOutput(output, destDir.getCanonicalPath()); // TODO push destDir through APIs?
         } catch (Exception e) {
-            e.printStackTrace();
+            throw new BuildException("Groovydoc generation failed: " + e.getMessage(), e);
+        }
+        int errorCount = htmlTool.getErrorCount();
+        if (errorCount > 0) {
+            throw new BuildException("Groovydoc failed to parse " + errorCount + " source file(s).");
         }
         // try to override the default stylesheet with custom specified one if needed
         if (styleSheetFile != null) {
diff --git a/subprojects/groovy-groovydoc/src/main/groovy/org/codehaus/groovy/tools/groovydoc/Main.groovy b/subprojects/groovy-groovydoc/src/main/groovy/org/codehaus/groovy/tools/groovydoc/Main.groovy
index 1ccc2c024b..a18ddb5555 100644
--- a/subprojects/groovy-groovydoc/src/main/groovy/org/codehaus/groovy/tools/groovydoc/Main.groovy
+++ b/subprojects/groovy-groovydoc/src/main/groovy/org/codehaus/groovy/tools/groovydoc/Main.groovy
@@ -148,7 +148,7 @@ class Main {
         } else if (scopeCount > 1) {
             System.err.println "groovydoc: Error - More than one of -public, -private, -package, or -protected specified."
             cli.usage()
-            return
+            System.exit(1)
         }
 
         windowTitle = options.windowtitle ?: ''
@@ -181,12 +181,23 @@ class Main {
         if (!remainingArgs) {
             System.err.println "groovydoc: Error - No packages or classes specified."
             cli.usage()
+            System.exit(1)
+        }
+        int errorCount
+        try {
+            errorCount = execute()
+        } catch (Throwable t) {
+            t.printStackTrace(System.err)
+            System.exit(1)
             return
         }
-        execute()
+        if (errorCount > 0) {
+            System.err.println "groovydoc: Error - $errorCount source file(s) failed to parse."
+            System.exit(1)
+        }
     }
 
-    static void execute() {
+    static int execute() {
         Properties properties = new Properties()
         properties.put("windowTitle", windowTitle)
         properties.put("docTitle", docTitle)
@@ -232,6 +243,7 @@ class Main {
                 println "Warning: Unable to copy specified stylesheet '" + styleSheetFile.absolutePath + "'. Using default stylesheet instead. Due to: " + e.message
             }
         }
+        return htmlTool.errorCount
     }
 
     static collectSourceFileNames(List<String> remainingArgs, String[] sourceDirs, List<String> exclusions) {
diff --git a/subprojects/groovy-groovydoc/src/main/java/org/codehaus/groovy/tools/groovydoc/GroovyDocTool.java b/subprojects/groovy-groovydoc/src/main/java/org/codehaus/groovy/tools/groovydoc/GroovyDocTool.java
index bf8f40b120..77b2417b4c 100644
--- a/subprojects/groovy-groovydoc/src/main/java/org/codehaus/groovy/tools/groovydoc/GroovyDocTool.java
+++ b/subprojects/groovy-groovydoc/src/main/java/org/codehaus/groovy/tools/groovydoc/GroovyDocTool.java
@@ -139,6 +139,13 @@ public class GroovyDocTool {
         return rootDocBuilder.getRootDoc();
     }
 
+    /**
+     * @return the number of source files that could not be parsed
+     */
+    public int getErrorCount() {
+        return rootDocBuilder.getErrorCount();
+    }
+
     public void renderToOutput(OutputTool output, String destdir) throws Exception {
         // expect just one scope to be set on the way in but now also set higher levels of visibility
         if ("true".equals(properties.getProperty("privateScope"))) properties.setProperty("packageScope", "true");
diff --git a/subprojects/groovy-groovydoc/src/main/java/org/codehaus/groovy/tools/groovydoc/GroovyRootDocBuilder.java b/subprojects/groovy-groovydoc/src/main/java/org/codehaus/groovy/tools/groovydoc/GroovyRootDocBuilder.java
index 1c36ecee37..a3090b0251 100644
--- a/subprojects/groovy-groovydoc/src/main/java/org/codehaus/groovy/tools/groovydoc/GroovyRootDocBuilder.java
+++ b/subprojects/groovy-groovydoc/src/main/java/org/codehaus/groovy/tools/groovydoc/GroovyRootDocBuilder.java
@@ -52,6 +52,7 @@ public class GroovyRootDocBuilder {
     private final String[] sourcepaths;
     private final SimpleGroovyRootDoc rootDoc;
     private final Properties properties;
+    private int errorCount;
 
     @Deprecated
     public GroovyRootDocBuilder(GroovyDocTool tool, String[] sourcepaths, List<LinkArgument> links, Properties properties) {
@@ -142,12 +143,17 @@ public class GroovyRootDocBuilder {
             packageDoc.putAll(classDocs);
             rootDoc.put(packagePath, packageDoc);
         } catch (RuntimeException e) {
+            errorCount++;
             e.printStackTrace(System.err);
             log.error("ignored due to parsing exception: " + filename + " [" + e.getMessage() + "]");
             log.debug("ignored due to parsing exception: " + filename + " [" + e.getMessage() + "]", e);
         }
     }
 
+    public int getErrorCount() {
+        return errorCount;
+    }
+
     /* package private */ void processPackageInfo(String src, String filename, SimpleGroovyPackageDoc packageDoc) {
         String relPath = packageDoc.getRelativeRootPath();
         String description = calcThenSetPackageDescription(src, filename, relPath);
diff --git a/subprojects/groovy-groovydoc/src/test/groovy/org/codehaus/groovy/tools/groovydoc/GroovyDocToolTest.java b/subprojects/groovy-groovydoc/src/test/groovy/org/codehaus/groovy/tools/groovydoc/GroovyDocToolTest.java
index 3033c0142c..6e9b3b0ba2 100644
--- a/subprojects/groovy-groovydoc/src/test/groovy/org/codehaus/groovy/tools/groovydoc/GroovyDocToolTest.java
+++ b/subprojects/groovy-groovydoc/src/test/groovy/org/codehaus/groovy/tools/groovydoc/GroovyDocToolTest.java
@@ -94,6 +94,16 @@ public class GroovyDocToolTest extends GroovyTestCase {
         );
     }
 
+    // GROOVY-9057
+    public void testParseErrorIsTrackedInErrorCount() throws Exception {
+        assertEquals("Initial error count should be zero", 0, xmlToolForTests.getErrorCount());
+        List<String> srcList = new ArrayList<>();
+        srcList.add("broken/BrokenSyntax.groovy");
+        xmlToolForTests.add(srcList);
+        assertTrue("Expected errorCount > 0 after parsing malformed source, was "
+                + xmlToolForTests.getErrorCount(), xmlToolForTests.getErrorCount() > 0);
+    }
+
     public void testPlainGroovyDocTool() throws Exception {
         List<String> srcList = new ArrayList<>();
         srcList.add("org/codehaus/groovy/tools/groovydoc/GroovyDocToolTest.java");
diff --git a/subprojects/groovy-groovydoc/src/test/resources/broken/BrokenSyntax.groovy b/subprojects/groovy-groovydoc/src/test/resources/broken/BrokenSyntax.groovy
new file mode 100644
index 0000000000..9f2a0c0bfa
--- /dev/null
+++ b/subprojects/groovy-groovydoc/src/test/resources/broken/BrokenSyntax.groovy
@@ -0,0 +1,26 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+// Intentionally malformed source used by
+// org.codehaus.groovy.tools.groovydoc.GroovyDocToolTest.testParseErrorIsTrackedInErrorCount
+// (GROOVY-9057) to verify that parse failures bump the error count.
+package broken
+
+class BrokenSyntax {
+    def foo(
+}