You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@maven.apache.org by sj...@apache.org on 2026/08/25 19:57:19 UTC

(maven-compiler-plugin) branch maven-compiler-plugin-3.x updated: Fix incremental detection of empty sources, 3.x (#1075)

This is an automated email from the ASF dual-hosted git repository.

slawekjaranowski pushed a commit to branch maven-compiler-plugin-3.x
in repository https://gitbox.apache.org/repos/asf/maven-compiler-plugin.git


The following commit(s) were added to refs/heads/maven-compiler-plugin-3.x by this push:
     new 2e81228  Fix incremental detection of empty sources, 3.x (#1075)
2e81228 is described below

commit 2e8122841d9b10d2d83a90cc07530d7a09eebc47
Author: Václav Haisman <vh...@gmail.com>
AuthorDate: Tue Aug 25 21:57:14 2026 +0200

    Fix incremental detection of empty sources, 3.x (#1075)
    
    * Fix incremental detection of empty sources
    
    The stale source scanner assumes every Java source produces a mapped output, causing zero-byte compilation units to trigger recompilation on every invocation.
    
    Filter zero-byte stale candidates only for one-output-per-input compilers and only when no mapped output exists. This preserves detection when an existing source is truncated and leaves aggregate-output compilers unchanged.
    
    Add a regression test that compiles an empty source twice and verifies that the second invocation is up to date.
    
    Fixes #1000.
---
 .../plugin/compiler/AbstractCompilerMojo.java      | 26 ++++++++++++++--
 .../maven/plugin/compiler/CompilerMojoTest.java    | 23 ++++++++++++++
 .../plugin-config.xml                              | 36 ++++++++++++++++++++++
 3 files changed, 83 insertions(+), 2 deletions(-)

diff --git a/src/main/java/org/apache/maven/plugin/compiler/AbstractCompilerMojo.java b/src/main/java/org/apache/maven/plugin/compiler/AbstractCompilerMojo.java
index a09bfef..2066f0e 100644
--- a/src/main/java/org/apache/maven/plugin/compiler/AbstractCompilerMojo.java
+++ b/src/main/java/org/apache/maven/plugin/compiler/AbstractCompilerMojo.java
@@ -1585,8 +1585,30 @@ public abstract class AbstractCompilerMojo extends AbstractMojo {
             }
 
             try {
-                staleSources.addAll(scanner.getIncludedSources(rootFile, outputDirectory));
-            } catch (InclusionScanException e) {
+                Set<File> includedSources = scanner.getIncludedSources(rootFile, outputDirectory);
+                // The stale source scanner assumes that every source produces an output file. Filter its result only
+                // when the compiler provides an individual source-to-output mapping; aggregate outputs are ambiguous.
+                if (outputStyle == CompilerOutputStyle.ONE_OUTPUT_FILE_PER_INPUT_FILE) {
+                    for (File source : includedSources) {
+                        if (Files.size(source.toPath()) != 0) {
+                            staleSources.add(source);
+                        } else {
+                            String relativePath = rootFile.toPath()
+                                    .relativize(source.toPath())
+                                    .toString();
+                            boolean outputExists = mapping.getTargetFiles(outputDirectory, relativePath).stream()
+                                    .anyMatch(File::exists);
+                            // A zero-byte compilation unit legitimately produces no class. Keep it stale if an output
+                            // exists, however, so that truncating an existing source is still detected as a change.
+                            if (outputExists) {
+                                staleSources.add(source);
+                            }
+                        }
+                    }
+                } else {
+                    staleSources.addAll(includedSources);
+                }
+            } catch (InclusionScanException | IOException e) {
                 throw new MojoExecutionException(
                         "Error scanning source root: \'" + sourceRoot + "\' for stale files to recompile.", e);
             }
diff --git a/src/test/java/org/apache/maven/plugin/compiler/CompilerMojoTest.java b/src/test/java/org/apache/maven/plugin/compiler/CompilerMojoTest.java
index fff6276..a694cfa 100644
--- a/src/test/java/org/apache/maven/plugin/compiler/CompilerMojoTest.java
+++ b/src/test/java/org/apache/maven/plugin/compiler/CompilerMojoTest.java
@@ -19,6 +19,7 @@
 package org.apache.maven.plugin.compiler;
 
 import java.io.File;
+import java.nio.file.Files;
 import java.util.Arrays;
 import java.util.HashSet;
 import java.util.Set;
@@ -43,6 +44,7 @@ import static org.junit.jupiter.api.Assertions.assertNull;
 import static org.junit.jupiter.api.Assertions.assertTrue;
 import static org.junit.jupiter.api.Assertions.fail;
 import static org.mockito.ArgumentMatchers.startsWith;
+import static org.mockito.Mockito.clearInvocations;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.verify;
@@ -135,6 +137,27 @@ class CompilerMojoTest {
                 projectArtifact.getFile(), "MCOMPILER-94: artifact file should be null if there is nothing to compile");
     }
 
+    /**
+     * Tests that an empty source file does not cause compilation every time because it has no class file.
+     */
+    @Test
+    @InjectMojo(goal = COMPILE, pom = "classpath:/unit/compiler-empty-source-change-detection-test/plugin-config.xml")
+    void testCompilerEmptySourceChangeDetection(CompilerMojo compilerMojo) throws Exception {
+        setUpCompilerMojoTestEnv(compilerMojo);
+
+        File source = new File(compilerMojo.getCompileSourceRoots().get(0), "Empty.java");
+        Files.createDirectories(source.getParentFile().toPath());
+        Files.write(source.toPath(), new byte[0]);
+
+        Log log = mock(Log.class);
+        compilerMojo.setLog(log);
+        compilerMojo.execute();
+
+        clearInvocations(log);
+        compilerMojo.execute();
+        verify(log).info("Nothing to compile - all classes are up to date.");
+    }
+
     /**
      * tests the ability of the plugin to respond to includes and excludes correctly
      *
diff --git a/src/test/resources/unit/compiler-empty-source-change-detection-test/plugin-config.xml b/src/test/resources/unit/compiler-empty-source-change-detection-test/plugin-config.xml
new file mode 100644
index 0000000..c144fc6
--- /dev/null
+++ b/src/test/resources/unit/compiler-empty-source-change-detection-test/plugin-config.xml
@@ -0,0 +1,36 @@
+<!--
+  ~ 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.
+  -->
+
+<project>
+  <build>
+    <plugins>
+      <plugin>
+        <artifactId>maven-compiler-plugin</artifactId>
+        <configuration>
+          <compileSourceRoots>
+            <compileSourceRoot>${basedir}/target/test-classes/unit/compiler-empty-source-change-detection-test/src/main/java</compileSourceRoot>
+          </compileSourceRoots>
+          <compilerId>javac</compilerId>
+          <outputDirectory>${basedir}/target/test/unit/compiler-empty-source-change-detection-test/target/classes</outputDirectory>
+          <buildDirectory>${basedir}/target/test/unit/compiler-empty-source-change-detection-test/target</buildDirectory>
+        </configuration>
+      </plugin>
+    </plugins>
+  </build>
+</project>