gnodet-bot commented on code in PR #1144:
URL:
https://github.com/apache/maven-compiler-plugin/pull/1144#discussion_r4176538846
##########
src/main/java/org/apache/maven/plugin/compiler/ToolExecutor.java:
##########
@@ -915,6 +919,241 @@ private static boolean removeFirsts(Deque<Path> paths,
Integer count) {
}
}
+ /**
+ * Compiles using the ABI-fingerprint incremental strategy. This method
handles the full
+ * lifecycle: determining what to compile, running javac with the analysis
TaskListener,
+ * cascading on ABI changes, and persisting state.
+ *
+ * @param compiler the compiler
+ * @param configuration the options to give to the Java compiler
+ * @param mojo the MOJO for configuration access
+ * @throws IOException if an error occurred while reading or writing a file
+ * @throws MojoException if the compilation failed
+ */
+ void compileWithAbiIncremental(JavaCompiler compiler, final Options
configuration, final AbstractCompilerMojo mojo)
+ throws IOException {
+ var abiBuild = new AbiIncrementalBuild(outputDirectory);
+
+ // Collect classpath entries for cross-module tracking
+ var classpathPaths = new ArrayList<Path>();
+ var reactorPaths = new LinkedHashSet<Path>();
+ for (var entry : dependencies.entrySet()) {
+ if (entry.getKey() instanceof JavaPathType type) {
+ var location = type.location();
+ if (location.isPresent()
+ && (location.get() == StandardLocation.CLASS_PATH
+ || location.get() ==
StandardLocation.MODULE_PATH)) {
+ classpathPaths.addAll(entry.getValue());
+ if (location.get() == StandardLocation.MODULE_PATH) {
+ for (Path p : entry.getValue()) {
+ if (Files.isDirectory(p)) {
+ reactorPaths.add(p);
+ }
+ }
+ }
+ }
+ }
+ }
+ abiBuild.setClasspathEntries(classpathPaths);
+ if (!reactorPaths.isEmpty()) {
+ abiBuild.setReactorModulePaths(reactorPaths);
+ }
+
+ // Collect annotation processor path for processor classification
+ var processorPaths = new ArrayList<Path>();
+ for (var entry : dependencies.entrySet()) {
+ if (entry.getKey() instanceof JavaPathType type) {
+ var location = type.location();
+ if (location.isPresent()
+ && (location.get() ==
StandardLocation.ANNOTATION_PROCESSOR_PATH
+ || location.get() ==
StandardLocation.ANNOTATION_PROCESSOR_MODULE_PATH)) {
+ processorPaths.addAll(entry.getValue());
+ }
+ }
+ }
+ if (!processorPaths.isEmpty()) {
+ abiBuild.setProcessorPath(processorPaths);
+ }
+
+ // Hash module-info-patch.maven files for config change detection
+ abiBuild.setConfigHash(computeConfigHash(configuration));
+
+ // Collect all source file paths
+ var allSourcePaths = new ArrayList<Path>();
+ for (SourceFile sf : sourceFiles) {
+ allSourcePaths.add(sf.file);
+ }
+
+ Set<Path> toCompile = abiBuild.initialize(allSourcePaths);
+ if (toCompile.isEmpty()) {
+ logger.info("Nothing to compile - all classes are up to date (ABI
strategy).");
+ abiBuild.finish();
+ return;
+ }
+
+ logger.info(
+ abiBuild.isFullBuild()
+ ? "Compiling " + toCompile.size() + " source file(s)
(ABI: full build)."
+ : "Compiling " + toCompile.size() + " source file(s)
(ABI: incremental).");
+ if (mojo.showCompilationChanges && abiBuild.getRebuildCause() != null)
{
+ logger.info("Rebuild cause: " + abiBuild.getRebuildCause());
+ for (Path f : toCompile) {
+ logger.info(" " + f);
+ }
+ }
+
+ var originalSourceFiles = new ArrayList<>(sourceFiles);
+ boolean success = true;
+ // Safety bound: the compile set is monotonically growing (bounded by
total source count).
+ // If a bug causes processCompiledClasses to return files already
compiled, this prevents
+ // an infinite loop. In practice this limit should never be reached.
+ int maxRounds = originalSourceFiles.size() + 1;
+ int rounds = 0;
+
+ try {
+ while (!toCompile.isEmpty()) {
+ if (++rounds > maxRounds) {
+ throw new IllegalStateException("ABI cascade loop did not
converge after " + maxRounds
+ + " rounds — " + "possible dependency cycle or bug
in processCompiledClasses()");
+ }
+ Set<Path> compileSet = toCompile;
+ sourceFiles = originalSourceFiles.stream()
+ .filter(sf -> compileSet.contains(sf.file))
+ .collect(Collectors.toList());
+
+ if (sourceFiles.isEmpty()) {
+ break;
+ }
+
+ var compilerOutput = new StringWriter();
+ success = compileWithAbiAnalyzer(compiler, configuration,
compilerOutput, abiBuild);
+ String output = compilerOutput.toString();
+ if (!output.isBlank()) {
+ logger.warn(output);
+ }
+ if (!success) {
+ break;
+ }
+
+ toCompile = abiBuild.processCompiledClasses(compileSet);
+ if (!toCompile.isEmpty()) {
+ logger.info("ABI cascade: recompiling " + toCompile.size()
+ " additional file(s).");
+ if (mojo.showCompilationChanges) {
+ for (Path f : toCompile) {
+ logger.info(" " + f);
+ }
+ }
+ }
+ }
+ } finally {
+ sourceFiles = originalSourceFiles;
+ }
+
+ if (success) {
+ abiBuild.finish();
+ logger.info("Compiled " + abiBuild.compiledCount() + " file(s), "
+ abiBuild.unchangedCount()
+ + " unchanged (ABI strategy).");
+ } else {
+ abiBuild.invalidate();
+ throw new CompilationFailureException("Compilation failed (ABI
incremental strategy).");
+ }
+ }
+
+ private String computeConfigHash(Options configuration) {
+ var digest = new StringBuilder();
+
+ // Include compiler options in the config hash so changes to
-source/-target/-release
+ // etc. trigger a full rebuild under the ABI strategy.
+ String optionsRepr = String.join("|", configuration.options);
+ digest.append("opts:").append(optionsRepr).append(';');
+
+ for (SourceDirectory source : sourceDirectories) {
+ Path patchFile = source.root.resolve(ModuleInfoPatch.FILENAME);
+ if (Files.isRegularFile(patchFile)) {
+ try {
+ byte[] content = Files.readAllBytes(patchFile);
+ digest.append(patchFile)
+ .append(':')
+ .append(Sha256.hash(content))
+ .append(';');
+ } catch (IOException e) {
+ digest.append(patchFile).append(":unreadable;");
+ }
+ }
+ }
+ if (digest.isEmpty()) {
+ return "";
+ }
+ return Sha256.hash(digest.toString());
+ }
+
+ /**
+ * Compiles sources with the ABI analyzer attached as a TaskListener.
Review Comment:
💡 **Stale Javadoc.** This still says "Compiles sources with the ABI analyzer
attached as a TaskListener" — but the TaskListener approach
(`attachTo(JavacTask)`) was removed. The method now just compiles; the bytecode
analysis happens in `processCompiledClasses()` after this returns.
```suggestion
* Compiles sources for the ABI incremental strategy.
```
--
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]