Add incremental build context API and implementation - #12576
Draft
gnodet wants to merge 12 commits into
Draft
Conversation
Port the incremental build context from PR #1118 to Maven 4 conventions: - API in org.apache.maven.api.build.context (avoids collision with build report API) - Core impl in maven-impl using Maven 4 DI (org.apache.maven.api.di) - Maven integration (MavenBuildContext, ProjectWorkspace, digest) stays in maven-core - Replace plexus-utils dependencies (MatchPatterns, CachingOutputStream) with pure Java - Add Maven 4 annotations (@experimental, @nonnull, @nullable, @SInCE 4.0.0) - All 41 tests passing in maven-impl Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
gnodet
force-pushed
the
move-the-incremental-build-context-api-in-pr-1118
branch
from
July 28, 2026 21:53
e5a76c1 to
1e02718
Compare
- Add package-info.java for both context and context.spi packages with architecture overview, use case examples, and SPI integration guide - Enhance BuildContext Javadoc with three usage patterns: one-to-one transformation, two-pass status inspection, and aggregated builds - Add code examples to Input, Output, InputSet, Metadata, Resource - Add @see cross-references between all related types - Enrich Incremental annotation with configuration parameter example - Document SPI lifecycle: environment, workspace modes, finalizer, sink message flow Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…leMatcher package-private - Convert all javax.inject.* and org.eclipse.sisu.* annotations to Maven 4 DI equivalents (org.apache.maven.api.di.*) in maven-core integration files - Replace javax.inject.Provider with java.util.function.Supplier - Remove unnecessary public modifiers from all test classes and methods - Make FileMatcher package-private, add TODO noting overlap with PathSelector (createMatchers trie has no PathSelector equivalent) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Fix ClasspathDigester.digestZipOrFile() always re-throwing after non-ZIP fallback, and reset MessageDigest after partial update - Fix ClasspathDigester.digestDirectory() passing subdirectories to digestFile() by filtering to regular files only - Fix DefaultResource.equals() casting to wrong type (DefaultMetadata instead of DefaultResource) - Rename CommitableBuildContext → CommittableBuildContext (correct English spelling for public API type) - Fix commit() @nonnull → @nullable on Sink parameter to match implementation behavior - Rename getFailOnError() → isFailOnError() for boolean convention - Fix FileMatcher.getCanonicalPath() NPE when path has no parent - Fix readCollection() returning null for empty collections (NPE risk in invertMultimap) - Replace bare NullPointerException with Objects.requireNonNull - Clarify registerAndProcessInputs() Javadoc: returns all inputs, not just processed ones Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Closed
…V4 mojos - Add MavenITgh12576IncrementalBuildContextTest with a real V4 plugin (IncrementalCopyMojo) that uses @Inject BuildContext for incremental file copying across 3 build scenarios: initial, no-change, modified - Wire @MojoExecutionScoped scope, BuildContext impl bindings, and explicit BuildContext→MojoExecutionScopedBuildContext supplier in V4 mojo injector (configureV4MojoInjector) - Wrap V4 mojos to call MavenBuildContextFinalizer after execution since WeakMojoExecutionListener lifecycle doesn't apply to V4 mojos - Fix ClasspathDigester to skip artifacts with unresolved paths instead of throwing NoSuchElementException Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Replace ConcurrentHashMap with HashMap in V4 @MojoExecutionScoped scope cache: ConcurrentHashMap.computeIfAbsent throws "Recursive update" when scoped beans depend on each other and hash to the same bucket; V4 mojo execution is single-threaded so HashMap is safe - Add early return in MavenBuildContextFinalizer.afterMojoExecutionSuccess when no contexts are registered: prevents "Contexts FailOnError property have different values" error for V4 mojos that don't inject BuildContext (empty context list + null Sink triggered failBuild with an empty failOnError set) - Fix IT to use per-build log files (setLogFileName) and clean on first build to avoid stale incremental state from prior test runs Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace the standalone FileMatcher class (373 lines) with the existing PathSelector infrastructure, exposed through the PathMatcherFactory API. - Add createSubdirectoryMatchers() to PathMatcherFactory API for targeted directory walks using trie-based pattern decomposition - Port trie logic (IncludesTrie, subdirectory decomposition) into PathSelector.ofSubdirectories() - Update DefaultBuildContext to use PathMatcherFactory instead of FileMatcher - Move getCanonicalPath() from FileMatcher to DefaultBuildContext - Delete FileMatcher.java entirely This avoids duplicating Ant-to-glob pattern matching logic and centralizes all path matching in PathSelector, the single implementation behind the PathMatcherFactory service. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Instead of hardcoding `new DefaultPathMatcherFactory()`, accept PathMatcherFactory as a constructor parameter so it flows through Maven's DI container. MojoExecutionScopedBuildContext now receives the factory via @Inject alongside BuildContextEnvironment. The 4-arg test constructor retains a null-safe fallback to DefaultPathMatcherFactory for backward compatibility. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Change the protected multi-arg constructors to public so that downstream projects (maven-filtering, maven-resources-plugin) can create test instances with a FilesystemWorkspace and no state file to exercise incremental build context behavior in unit tests. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This was referenced Jul 29, 2026
Draft
…tern Expand the BuildContext API Javadoc to cover capabilities that were implemented but not documented: - Configuration change detection: explain that Maven automatically digests all mojo parameters and the plugin classpath, and that any change triggers escalation to a full build. Mojos do not need custom options-change detection. - Escalation semantics: document when and why the build context treats all inputs as modified (missing state file, config change, missing outputs, workspace escalation). - Two-pass processing pattern: add Use Case 5 showing how compilers and similar tools can register inputs, run the tool, then associate unpredictable outputs (inner classes) after processing. - registerInput(Path): clarify its use for individual dependency JARs and other non-directory-tree inputs. - BuildContextEnvironment.getParameters(): expand from a one-liner to a full explanation of the automatic mojo parameter digestion and plugin classpath hashing. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…ation Add comprehensive Javadoc to the implementation classes that were undocumented despite being key to the BuildContext's capabilities: - DefaultBuildContext: document the class, the main constructor's configuration comparison and escalation logic, getConfigurationChanged() key-by-key comparison, and the three-pass finalizeContext() stale output cleanup algorithm. - MojoConfigurationDigester: document the two-category digest (plugin classpath + mojo parameters), how each @parameter field is reflected and evaluated, and how unsupported types are reported as errors. - ClasspathDigester: expand the one-liner to explain SHA-1 content hashing (not timestamps), sorted ZIP entry ordering, session-level caching, and immunity to SNAPSHOT timestamp changes. - MavenBuildContextConfiguration: document how it wires together the digester, workspace, finalizer, and state file location (target/incremental/<groupId>_<artifactId>_<goal>_<executionId>). - MavenBuildContext + MojoExecutionScopedBuildContext: document the scoping pattern and per-execution isolation. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Port and modernize the incremental build context from PR #1118 for Maven 4.1.0. This gives mojo authors a first-class API for incremental builds — tracking which input files changed, managing input→output associations, skipping unnecessary work, and cleaning up stale outputs automatically.
Closes #1118.
What's in this PR
Public API (
maven-api-core) — 17 types in 2 packagesorg.apache.maven.api.build.context— mojo-facing API:BuildContextInput/OutputInputSetMetadata<R>ResourceStatusNEW,MODIFIED,UNMODIFIED,REMOVEDSeverityERROR,WARNING,INFO@IncrementalBuildContextExceptionMavenException)org.apache.maven.api.build.context.spi— integration/IDE SPI:WorkspaceCommittableBuildContextcommit(Sink)for state persistenceBuildContextEnvironmentBuildContextFinalizerSinkFileState/MessageImplementation (
maven-impl) — 12 classes + 10 test classesCore incremental build engine in
org.apache.maven.internal.build.context.impl:DefaultBuildContext— full lifecycle: register inputs → detect changes → manage outputs → persist stateFileMatcher— Ant-style glob patterns with trie-based subdirectory optimization for targeted workspace walksMaven integration (
maven-core) — 5 classesMaven-specific wiring in
org.apache.maven.internal.build.context.impl.maven:MavenBuildContext—@MojoExecutionScopedbuild context withSupplier-based delegationMavenBuildContextFinalizer—WeakMojoExecutionListenerthat batch-commits after mojo successMavenBuildContextConfiguration— resolves state file location fromMojoExecutionProjectWorkspace— dispatches in-project paths to IDE workspace, out-of-project to filesystemClasspathDigester,MojoConfigurationDigester,Digesters) — detect plugin classpath and mojo configuration changesKey design decisions
API in
o.a.m.api.build.context(noto.a.m.api.build) — avoids package collision with the build report API in PR Build Report Foundation — structured report, console modes, warning control #12572Workspace abstraction — the same mojo code works in CLI Maven (NORMAL mode with full filesystem scan), IDEs (DELTA mode with file-watcher), recovery scenarios (ESCALATED), and read-only analysis (SUPPRESSED)
Two-phase registration —
registerInputs()returnsMetadata<Input>for status inspection;process()commits to work.registerAndProcessInputs()provides a one-step convenienceMaven 4 DI — uses
org.apache.maven.api.di.Inject/Named/Typed/MojoExecutionScopedandjava.util.function.Supplierinstead ofjavax.inject.*/org.eclipse.sisu.*No external dependencies — replaced plexus-utils
MatchPatternswith regex-basedantPatternToRegex(), replacedCachingOutputStreamwithBufferedOutputStream(Files.newOutputStream())Maven 4 conventions followed
@Experimentalon all API types@Nonnull/@Nullableon all parameters and return types@Immutableon value objects (FileState,Message)@Consumer/@Provideron SPI interfaces@since 4.1.0on all public typesBuildContextException extends MavenExceptionisFailOnError()(notgetFailOnError())public)Known limitations (documented in Javadoc)
@Incrementalannotation lives inbuild.contextrather thanplugin.annotationsSeverityenum is separate fromBuilderProblem.Severity(different semantics — build context messages persist across builds and support INFO level)Test plan
maven-implmvn verifypasses formaven-implandmaven-core(checkstyle, RAT, all tests)🤖 Generated with Claude Code