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>