gnodet commented on PR #11394:
URL: https://github.com/apache/maven/pull/11394#issuecomment-3502348741

   ## Commit Description for Merge
   
   Here's a comprehensive commit message explaining the chosen fix:
   
   ---
   
   ```
   Fix resource targetPath resolution to maintain Maven 3 compatibility
   
   This commit fixes a regression in Maven 4 where resource targetPath was being
   resolved relative to the project base directory instead of the output 
directory,
   breaking compatibility with Maven 3 and causing resources to be copied to
   incorrect locations.
   
   ## Problem
   
   In Maven 3.x, when a resource has a relative targetPath, it is resolved 
relative
   to the output directory (target/classes for main, target/test-classes for 
test).
   
   Example configuration:
   ```xml
   <resource>
     <directory>${project.basedir}/rest</directory>
     <targetPath>target-dir</targetPath>
   </resource>
   ```
   
   Maven 3 behavior: Files copied to target/classes/target-dir ✓
   Maven 4 behavior (before fix): Files copied to target-dir (project root) ✗
   
   ## Root Cause
   
   The DefaultSourceRoot constructor that creates SourceRoot instances from 
Resource
   model objects was incorrectly resolving the targetPath against the project 
base
   directory instead of storing it as-is for later resolution against the output
   directory.
   
   ## Solution Design
   
   The fix implements a clear separation of concerns between storage and 
resolution:
   
   ### 1. Storage Layer (SourceRoot.targetPath())
   
   This method returns the targetPath exactly as specified in the configuration:
   - Relative paths (e.g., "META-INF/resources") are stored as relative paths
   - Absolute paths (e.g., "/tmp/custom") are stored as absolute paths
   - Empty/null means no explicit target path was specified
   
   No resolution happens at this layer. The path is stored as-is.
   
   ### 2. Resolution Layer (SourceRoot.targetPath(Project))
   
   This method performs the actual path resolution:
   
   Algorithm:
   1. Get the configured targetPath from targetPath()
   2. If absolute → return it unchanged
   3. Get the output directory for the source root's scope:
      - ProjectScope.MAIN → target/classes
      - ProjectScope.TEST → target/test-classes
   4. If targetPath is empty → return output directory
   5. If targetPath is relative → resolve against output directory
   
   This matches Maven 3 behavior exactly.
   
   ### 3. Implementation Changes
   
   **DefaultSourceRoot.java:**
   - Constructor from Resource now stores targetPath as-is (line 176):
     ```java
     nonBlank(resource.getTargetPath()).map(Path::of).orElse(null)
     ```
   - Does NOT resolve against baseDir (which would make it project-relative)
   - Added comprehensive javadoc explaining this preserves Maven 3 behavior
   
   **ConnectedResource.java:**
   - Extracts targetPath as a string (still relative) for maven-resources-plugin
   - The plugin receives the relative path and resolves it (Maven 3 behavior)
   
   **Project.getOutputDirectory():**
   - Returns the scope-specific output directory
   - Used by SourceRoot.targetPath(Project) for resolution
   - Documented as the base for targetPath resolution
   
   ### 4. Maven 4 API Documentation
   
   Extensive javadoc enhancements make the design explicit:
   
   **SourceRoot.targetPath():**
   - Clearly states it returns path AS CONFIGURED (no resolution)
   - Documents return value semantics for empty/relative/absolute cases
   - Provides usage guidance for different audiences:
     * Maven 4 API consumers: use targetPath(Project)
     * Maven 3 compatibility: use this method
     * Implementers: store as-is, don't resolve
   
   **SourceRoot.targetPath(Project):**
   - Documents complete resolution algorithm
   - Provides table with concrete examples
   - Explains relationship to targetPath() (storage vs resolution)
   
   **Project.getOutputDirectory():**
   - Documents scope-to-directory mapping
   - Explains role in targetPath resolution
   - Shows Maven 3 compatibility with XML examples
   
   ## Why This Approach
   
   This design was chosen because:
   
   1. **Preserves Maven 3 Compatibility:**
      - maven-resources-plugin receives relative paths (as in Maven 3)
      - Plugin resolves against output directory (as in Maven 3)
      - Existing POMs work without modification
   
   2. **Clean Maven 4 API:**
      - Clear separation: storage (targetPath) vs resolution 
(targetPath(Project))
      - Maven 4 consumers get absolute paths via targetPath(Project)
      - Legacy plugins get relative paths via targetPath()
   
   3. **No Premature Resolution:**
      - Paths stored as configured (relative or absolute)
      - Resolution happens when needed, with proper context (Project)
      - Allows different resolution strategies for different scopes
   
   4. **Explicit Contracts:**
      - Comprehensive javadoc explains semantics
      - Clear guidance for API consumers, plugin developers, implementers
      - Examples show expected behavior
   
   ## Testing
   
   Integration test MavenITgh11381ResourceTargetPathTest verifies:
   - Resources with relative targetPath are copied to correct location
   - Works for both main and test scopes
   - Maintains Maven 3 compatibility
   
   ## Impact
   
   This fix:
   ✓ Restores Maven 3 compatibility for resource targetPath
   ✓ Provides clean Maven 4 API with clear semantics
   ✓ Maintains backward compatibility with existing POMs
   ✓ Enables maven-resources-plugin to work correctly
   ✓ Documents the design for future maintainers
   
   Fixes #11381
   ```
   
   ---
   
   Feel free to use this as-is or adapt it for the merge commit!


-- 
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