Skip to content

Auto-detect module source hierarchy and skip main artifact at install - #468

Merged
gnodet merged 3 commits into
masterfrom
fix/module-source-hierarchy
Oct 12, 2026
Merged

gnodet merged 3 commits into
masterfrom
fix/module-source-hierarchy

Conversation

@gnodet

@gnodet gnodet commented Oct 10, 2026

Copy link
Copy Markdown
Contributor

Summary

  • When a project uses module source hierarchy (<source><module>…), the JAR plugin produces one JAR per JPMS module as attached artifacts and never assigns a file to the main artifact — the install plugin then crashes with "Is a directory"
  • Auto-detects this case by checking ProjectManager.getSourceRoots() for any SourceRoot with module().isPresent(), and removes the main artifact from the install request
  • Installs only the POM and per-module JARs — no flag required, no user configuration needed

How it differs from #466

PR #466 requires allowIncompleteProjects=true — a flag whose name and semantics don't match the module source hierarchy use case. This PR auto-detects instead:

#466 This PR
Requires allowIncompleteProjects=true Yes No
Detection Generic (attached artifacts exist) Specific (source roots declare modules)
Existing allowIncompleteProjects behavior Replaced Preserved unchanged

Context

This is a workaround for Maven 4.0 (API is frozen). The proper fix is tracked in apache/maven#13396 — a Packaging.producesMainArtifact() API for Maven 4.1.0 that would let the JAR plugin explicitly signal "no main artifact", eliminating the need for detection in the install plugin.

Test plan

  • New test moduleSourceHierarchySkipsMainArtifact — sets up source roots with a module, attaches a module JAR, sets main artifact path to a directory, verifies only POM + module JAR are installed
  • All 35 existing tests pass unchanged
  • Integration test with an actual module source hierarchy project (e.g. Apache SIS)

🤖 Generated with Claude Code

When a project uses module source hierarchy (<source><module>…), the
JAR plugin produces one JAR per JPMS module as attached artifacts and
never assigns a file to the main artifact. The install plugin then
fails because the main artifact path is a directory.

Auto-detect this case by checking whether any source root declares a
module (via ProjectManager.getSourceRoots() / SourceRoot.module()),
and skip the main artifact from the install request — installing only
the POM and the per-module JARs.

This is a workaround for Maven 4.0. The proper fix is tracked in
apache/maven#13396 (Packaging.producesMainArtifact() API for 4.1.0).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

@gnodet-bot gnodet-bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The detection logic in usesModuleSourceHierarchy is sound, but there is one concrete gap worth addressing.

This review was generated by an AI agent, Hermès on behalf of @gnodet.

Comment thread src/main/java/org/apache/maven/plugins/install/InstallMojo.java
Comment thread src/test/java/org/apache/maven/plugins/install/InstallMojoTest.java

@gnodet-bot gnodet-bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Both previous findings are addressed.

  • Detection logic is correct: filters enabled source roots, checks module presence, and is placed after the attachedArtifacts.isEmpty() guard — so multi-module projects with attached artifacts (e.g. uber-jars) still go through the normal path.
  • toInstall mutation is safe: the for-each loop iterates installables (immutable view) while removing from a separate ArrayList copy — no ConcurrentModificationException risk.
  • Warning consolidation preserves the original semantics.
  • Test validates the fix: pre-fix code would throw MojoException on the !allowIncompleteProjects branch; post-fix the new else if (usesModuleSourceHierarchy(...)) intercedes first.

This review was generated by an AI agent, Hermès on behalf of @gnodet.

@desruisseaux desruisseaux left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tested on my multi-modules project and it worked perfectly well. Thanks!

One small wish (unrelated to this issue): create hard links instead of copying the files.

@gnodet

gnodet commented Oct 10, 2026

Copy link
Copy Markdown
Contributor Author

Implemented in apache/maven-resolver#2198 — hard links are now the default in DefaultInstaller (the layer that actually moves bytes to the local repo), with automatic fallback to copy on cross-device or unsupported filesystems. Opt out with -Daether.installer.hardLink=false.

@gnodet
gnodet merged commit 4c98e73 into master Oct 12, 2026
11 checks passed
@gnodet
gnodet deleted the fix/module-source-hierarchy branch October 12, 2026 18:58
@github-actions github-actions Bot added this to the 4.0.0-beta-4 milestone Oct 12, 2026
@github-actions

Copy link
Copy Markdown

@gnodet Please assign appropriate label to PR according to the type of change.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants