olamy opened a new issue, #525:
URL: https://github.com/apache/maven-build-cache-extension/issues/525

   ### Summary
   
   When the input scanner probes plugin configuration for paths, a 
`<directory>` (or any path-valued tag) that points at **another module's 
directory** is walked recursively, and that module's `target/` is **not** 
excluded. The other module's build output — jar, `target/classes/*.class`, 
`maven-archiver/pom.properties`, `maven-status/**` — therefore becomes part of 
the consuming module's checksum.
   
   Build output is not stable across builds (jars carry timestamps unless 
`project.build.outputTimestamp` is set, and `maven-status` files change 
whenever the module is actually compiled rather than restored). The result is a 
module that can never get a cache hit, with no warning — it just looks like the 
remote cache is broken.
   
   `ExclusionResolver.addDefaultExcludes()` builds the `target` / 
`target/classes` / `target/test-classes` exclusions from **the current project 
only**:
   
   ```java
   Path buildDirectoryPath = absoluteNormalizedPath(build.getDirectory());
   Path outputDirectoryPath = 
absoluteNormalizedPath(build.getOutputDirectory());
   Path testOutputDirectoryPath = 
absoluteNormalizedPath(build.getTestOutputDirectory());
   ```
   
   They are absolute paths, so they only ever match the project being hashed. 
Any *other* reactor module reached through plugin-config probing has its build 
output treated as source input.
   
   ### Reproducer
   
   Note: create it **outside** `java.io.tmpdir` — `getPathOrNull()` silently 
skips anything under the temp dir, so a repro in `/tmp` will not trigger the 
scan.
   
   ```bash
   mkdir -p ~/mbc-repro/{.mvn,mod-a/src/main/java,mod-b/src/main/java} && cd 
~/mbc-repro
   
   cat > .mvn/extensions.xml <<'EOF'
   <extensions>
     <extension>
       <groupId>org.apache.maven.extensions</groupId>
       <artifactId>maven-build-cache-extension</artifactId>
       <version>1.3.0</version>
     </extension>
   </extensions>
   EOF
   
   cat > .mvn/maven-build-cache-config.xml <<'EOF'
   <cache xmlns="https://maven.apache.org/BUILD-CACHE-CONFIG/1.2.0";>
     <configuration>
       <enabled>true</enabled>
       <hashAlgorithm>XX</hashAlgorithm>
     </configuration>
     <input>
       <global>
         <glob>{*.java,*.xml,*.properties}</glob>
       </global>
     </input>
   </cache>
   EOF
   
   cat > pom.xml <<'EOF'
   <project xmlns="http://maven.apache.org/POM/4.0.0";>
     <modelVersion>4.0.0</modelVersion>
     <groupId>org.example</groupId>
     <artifactId>parent</artifactId>
     <version>1.0-SNAPSHOT</version>
     <packaging>pom</packaging>
     <properties>
       <maven.compiler.release>17</maven.compiler.release>
       <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
     </properties>
     <modules>
       <module>mod-a</module>
       <module>mod-b</module>
     </modules>
   </project>
   EOF
   
   cat > mod-a/pom.xml <<'EOF'
   <project xmlns="http://maven.apache.org/POM/4.0.0";>
     <modelVersion>4.0.0</modelVersion>
     <parent>
       <groupId>org.example</groupId>
       <artifactId>parent</artifactId>
       <version>1.0-SNAPSHOT</version>
     </parent>
     <artifactId>mod-a</artifactId>
   </project>
   EOF
   echo 'public class A {}' > mod-a/src/main/java/A.java
   echo 'shared note'      > mod-a/notes.txt
   
   # mod-b has NO dependency on mod-a. It only wants one file out of mod-a's 
directory.
   cat > mod-b/pom.xml <<'EOF'
   <project xmlns="http://maven.apache.org/POM/4.0.0";>
     <modelVersion>4.0.0</modelVersion>
     <parent>
       <groupId>org.example</groupId>
       <artifactId>parent</artifactId>
       <version>1.0-SNAPSHOT</version>
     </parent>
     <artifactId>mod-b</artifactId>
     <build>
       <plugins>
         <plugin>
           <groupId>org.apache.maven.plugins</groupId>
           <artifactId>maven-resources-plugin</artifactId>
           <executions>
             <execution>
               <id>copy-shared-note</id>
               <phase>prepare-package</phase>
               <goals><goal>copy-resources</goal></goals>
               <configuration>
                 <outputDirectory>${project.build.directory}</outputDirectory>
                 <resources>
                   <resource>
                     
<directory>${maven.multiModuleProjectDirectory}/mod-a</directory>
                     <includes><include>notes.txt</include></includes>
                   </resource>
                 </resources>
               </configuration>
             </execution>
           </executions>
         </plugin>
       </plugins>
     </build>
   </project>
   EOF
   echo 'public class B {}' > mod-b/src/main/java/B.java
   
   mvn -B -ntp -Dmaven.build.cache.location=$PWD/cache clean install   # warm
   mvn -B -ntp -Dmaven.build.cache.location=$PWD/cache clean install   # mod-b 
hits
   
   # a CI agent that has to rebuild mod-a. No source change anywhere.
   rm -rf cache/v1.2/org.example/mod-a
   mvn -B -ntp -Dmaven.build.cache.location=$PWD/cache clean install
   ```
   
   ### Actual
   
   The last build misses on `mod-b`, although nothing in `mod-b` — or in any 
*source* file — changed:
   
   ```
   [INFO] Local build was not found by checksum 65e6bd150bbbd545 for 
org.example:mod-b
   ```
   
   `mod-b`'s `buildinfo.xml` shows why:
   
   ```
   file  2eaf0644a54b6fa5  ../mod-a/target/classes/A.class
   file  d4bdf912ad00f859  ../mod-a/target/maven-archiver/pom.properties
   file  b12c0741ec50f3fc  
../mod-a/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst
   file  847b95fc10a3e8b6  
../mod-a/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst
   file  c918f0a5600bfbf0  ../mod-a/target/mod-a-1.0-SNAPSHOT.jar
   file  ...               ../mod-a/notes.txt, ../mod-a/pom.xml, 
../mod-a/src/main/java/A.java
   file  ...               src/main/java/B.java
   ```
   
   Editing anything in `mod-a` also invalidates `mod-b` the same way, even 
though `mod-b` does not depend on `mod-a`.
   
   The input set additionally differs depending on whether `mod-a` was 
**restored** (only the jar is present in `target/`) or **rebuilt** (whole 
`target/` tree present), so the two paths produce different checksums for the 
same sources.
   
   ### Expected
   
   Another module's build output directory should never be a checksum input. 
Either exclude every reactor project's `build.directory` when walking, or apply 
the owning project's default exclusions when the walked path belongs to a 
different `MavenProject` in the session — `MultiModuleSupport` already has the 
project list.
   
   ### Secondary: the configured `<global><glob>` is bypassed for plugin-config 
scans
   
   `notes.txt` above is collected even though the configured glob is 
`{*.java,*.xml,*.properties}`, because `DefaultPluginScanConfig` — used 
whenever a plugin has no explicit `<dirScan>` — hardcodes `*`:
   
   ```java
   // DefaultPluginScanConfig
   public ScanConfigProperties getTagScanProperties(String tagName) {
       return new ScanConfigProperties(true, "*");
   }
   ```
   
   whereas `PluginScanConfigImpl.defaultScanConfig()` returns `null`, which 
`MavenProjectInput` then resolves to the project glob:
   
   ```java
   final String glob = defaultIfEmpty(propertyConfig.getGlob(), projectGlob);
   ```
   
   So adding an otherwise-empty `<dirScan/>` for a plugin silently *narrows* 
the scanned file set from `*` to the project glob. That asymmetry looks 
unintentional.
   
   ### Real-world impact
   
   Found while investigating why Eclipse Jetty's CI never got a single cache 
hit. Jetty's parent pom has an inherited `maven-resources-plugin` execution 
pointing at `${maven.multiModuleProjectDirectory}/build`, which contains a 
helper module. Every one of the ~400 reactor modules therefore hashed 
`build/build-resources/target/build-resources-<version>.jar`, which is not 
reproducible — so the entire reactor invalidated itself on every build, on 
every agent, forever. Diagnosing it took diffing `buildinfo.xml` between two 
runs, because nothing in the log hints that an input lives under another 
module's `target/`.
   
   A warning when a collected input falls under any reactor project's build 
directory would make this class of problem self-diagnosing.
   
   ### Environment
   
   - maven-build-cache-extension 1.3.0
   - Maven 3.9.16
   - Java 25.0.3 (Temurin); also observed on 17 and 21
   


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to