Add banSplitPackages rule: module-path split-package detection - #1000
Draft
ascheman wants to merge 8 commits into
Draft
Add banSplitPackages rule: module-path split-package detection#1000ascheman wants to merge 8 commits into
ascheman wants to merge 8 commits into
Conversation
Add a small reader that parses a module-info.class into an immutable JavaModuleInfo (name, open flag, requires, exports, opens). It delegates to java.lang.module.ModuleDescriptor via reflection so the code keeps the plugin's Java 8 source baseline, and guards against a class file newer than the running JVM with a clear diagnostic instead of a silent failure. ASM is added in test scope only, to generate module-info.class fixtures; production stays dependency-free. Part of apache#995. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Signed-off-by: Gerd Aschemann <ascheman@apache.org>
Add requireExplicitModules (no automatic-module fallback), requireMinimalExports (do not export internal/impl packages) and banUnjustifiedOpens (no unqualified opens or open module). They share an AbstractModuleInfoRule base whose moduleOutputs() supports both the classic layout (one module-info.class in the output directory) and the Maven 4 module source hierarchy (one module per output subdirectory). Rules bind after compile (e.g. process-classes) since they read the compiled module-info.class. Unit tests are guarded to Java 9+. Part of apache#995. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Signed-off-by: Gerd Aschemann <ascheman@apache.org>
Four classic single-module ITs (one pass, three fail) plus two Maven 4 module source hierarchy ITs (pass and fail). The classic ITs are gated to JDK 11+; the module-source-hierarchy ITs to Maven 4.0.0-rc-5+ and JDK 17+, so they skip cleanly on older toolchains. Each binds the enforce goal to process-classes so module-info.class exists when the rule runs. Part of apache#995. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Signed-off-by: Gerd Aschemann <ascheman@apache.org>
Add site pages for requireExplicitModules, requireMinimalExports and banUnjustifiedOpens, and list them in the built-in rules index. Each page notes the two supported output layouts and that the enforce execution must run after compile (e.g. process-classes). Part of apache#995. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Signed-off-by: Gerd Aschemann <ascheman@apache.org>
Sort requires/exports/opens (and their targets) read from ModuleDescriptor sets so diagnostics are stable across JDKs; null-normalize allowedOpens/allowedExports setters; write test fixture classes to package-directory layout. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Implements the banSplitPackages rule requested in apache#997: a package must belong to exactly one Java module. The rule intersects the package sets of the project's compiled output(s) with the full declared-dependency set - deliberately not only the requires'd modules, which is what catches the "silent" split the resolver never sees. * Project packages come from walking the output directory, not the compiled descriptor: ModulePackages is only finalized at packaging time and offending classes are frequently added after compile (generation, copy). Classic and Maven 4 module source hierarchy layouts are supported via moduleOutputs(). * Dependency packages are read through java.lang.module.ModuleFinder (reflectively, keeping the Java 8 baseline), which derives automatic module names and reports complete package sets; artifacts the finder cannot model fall back to a plain class-file scan. * Overlaps between two artifacts that both end up on the module path fail the build; an automatic module counts as on the module path only when a project module requires its derived name. Overlaps involving a classpath-only artifact are reported at <classpathSeverity> (default warn) as a modularization hazard. * Configuration: classpathSeverity, allowedSplitPackages, ignoredModules, ignoredArtifacts, message. * ResolverUtil is made public: the enforcer mojo only performs dependency collection, so the rule resolves artifact files explicitly (same approach as enforceBytecodeVersion). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Four invoker ITs for banSplitPackages: * ban-split-packages-fail: classic two-artifact reactor; the consumer re-declares a dependency package without requiring the provider (the silent variant) and the build fails at process-classes. * ban-split-packages-pass: same shape with disjoint packages. * ban-split-packages-warn: non-modular consumer overlapping a non-required automatic module; the build succeeds with a warning (default classpathSeverity). * ban-split-packages-msh-fail: Maven 4 module source hierarchy variant, gated to Maven 4.0.0-rc-5+ / JDK 17+. The MSH reactor uses a 4.0.0 aggregator and provider POM: the invoker plugin discovers child POMs for @...@ interpolation only through <modules>; a 4.1.0 root POM with <subprojects> leaves the children unfiltered. Only the MSH consumer needs the 4.1.0 model. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Site page for the new rule plus its entry in the rules index. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
ascheman
force-pushed
the
feature/997-ban-split-packages
branch
from
July 19, 2026 19:38
9d77337 to
a284bdc
Compare
Contributor
|
Successfully tested locally on the AssertJ build, see output logs at assertj/assertj#4331 (comment). |
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.
Implements the
banSplitPackagesrule proposed in #997 — the follow-up announced in #996.Important
Stacked on #996. The first four commits are #996 (module-info reader + three rules); only the top three commits (
feat/test/docsforbanSplitPackages) are new here. Once #996 merges I will rebase onto master and the diff collapses to just the new rule. Draft until then.Rule
A package must belong to exactly one Java module — two modules on the module path containing the same package fail resolution (
java.lang.module.ResolutionException), typically at packaging,jlink, or first boot, with a symptom-not-cause message. And if the offending module is not evenrequiresd, the resolver never pulls it in: the build stays green while a type or endpoint is silently absent at runtime.ModulePackagesis only finalized at packaging time, and offending classes are frequently added after compile (code generation, copied resources). Both the classic layout and the Maven 4 module source hierarchy are supported (samemoduleOutputs()discovery as Add Java module-info rules: requireExplicitModules, requireMinimalExports, banUnjustifiedOpens #996).java.lang.module.ModuleFinder(reflectively — same Java 8 baseline rationale as the reader in Add Java module-info rules: requireExplicitModules, requireMinimalExports, banUnjustifiedOpens #996), which derives automatic-module names and reports complete package sets; artifacts the finder cannot model fall back to a plain class-file scan.requiresd modules — this is what catches the silent variant.requiresits derived name. Overlaps involving a classpath-only artifact are legal today and reported at<classpathSeverity>(defaultwarn) as a modularization hazard.classpathSeverity(warn/error/ignore),allowedSplitPackages,ignoredModules,ignoredArtifacts,message.Design note: dependency resolution
ResolverUtilis widened topublic: theenforcemojo declares onlyrequiresDependencyCollection, so artifact files are not resolved by default; the rule resolves them explicitly — the same approachenforceBytecodeVersionalready uses.Tests
maven-enforcer-plugin/src/it/projects/: a classic two-artifact reactor failing on the silent variant, a clean pass, a classpath-overlap warn case (build succeeds, warning names the automatic module), and a Maven 4 module-source-hierarchy fail case (gated4.0.0-rc-5+/ JDK 17+).mvn clean verify -P run-itsgreen on Maven 3.10.0-rc-1 (158 ITs passed); the four new ITs additionally verified on Maven 4.0.0-rc-5.mvn verifyto make sure basic checks pass.mvn -Prun-its verify).(Replaces #999, which GitHub auto-closed when the head branch was renamed to
feature/997-ban-split-packages.)