Skip to content

Fix excludedScanDirectories never matching on Windows - #1154

Merged
slachiewicz merged 2 commits into
masterfrom
fix-excluded-scan-directories-windows
Aug 9, 2026
Merged

slachiewicz merged 2 commits into
masterfrom
fix-excluded-scan-directories-windows

Conversation

@slachiewicz

Copy link
Copy Markdown
Member

excludedScanDirectories (GH-944, @since 3.16.0) has never worked on Windows for the usage its own documentation shows.

DefaultPluginToolsRequest.isExcludedScanDirectory() built a glob straight out of the configured value:

sourceFs.getPathMatcher("glob:" + excludedScanDirectory).matches(sourcePath)

A backslash is the escape character of the glob syntax, and sun.nio.fs.Globs.toRegexPattern applies that on Windows too — the case '\\': branch is not conditional on the file system. So a value interpolated from ${project.basedir} or ${project.build.directory}, which is exactly what the parameter's javadoc recommends, arrives as D:\proj\target\generated-sources/annotations, every separator is consumed, and the pattern degenerates to something like D:projtargetgenerated-sources/annotations. It can never match a real path, so the exclusion silently does nothing. A value ending in a separator is worse: a glob may not end in an escape character, so it fails the build with a PatternSyntaxException rather than merely failing to match.

How it surfaced

The gh-944-exclude-source-directory IT never asserted that the exclusion took effect. #1150 changed it to exclude a root holding a mojo's Javadoc and added a verify.groovy asserting the description comes out empty — which fails on windows-latest, and is what led here. That IT is the end-to-end gate and is left untouched by this PR.

The fix

Compare as paths first. That is the documented form of the parameter ("this only accepts source roots"), and path comparison is separator-, case- and trailing-separator-safe on every file system.

Globs stay supported — the javadoc promises them ("Globs are also supported here") and they are the only way to exclude a subtree. To make them work, backslashes are translated to forward slashes before the pattern is compiled, but only on file systems that separate names with a backslash. Per Globs.toRegexPattern, a / in a glob compiles to the Windows separator, so this is the portable spelling; on file systems where a backslash is a legal name character it is left alone, since there it may be a deliberate escape such as \*.

Also skips null/empty entries, which previously resolved to the working directory and could exclude a source root by accident.

Testing

The matching logic moved into a package-private method taking a Path, so the whole decision is tied to one FileSystem and the Windows behaviour is unit-testable off Windows. New DefaultPluginToolsRequestTest covers exact-root matching, trailing separators, siblings, empty entries, glob matching, and the separator translation in both directions. Full reactor unit tests pass.

One deliberate limitation: these tests exercise the translation directly rather than running the whole method against a real Windows file system, since the JDK ships no alternative provider. A Jimfs Configuration.windows() test would reproduce the original bug end-to-end on Linux CI — Jimfs's glob compiler is a faithful port of the JDK's — but that means a new test-scoped dependency on this module. Happy to add it if you would rather have that coverage than avoid the dependency.

Notes

  • The feature is unreleased: git tag --contains on both Unable to parse generated classes during descriptor generation #944 commits comes back empty, so it is in neither the 3.x nor the 4.x line and no released behaviour changes here.
  • Relative configured values still resolve against user.dir rather than the module basedir. That is pre-existing, out of scope here, and the documented usage is interpolated absolute paths.

isExcludedScanDirectory() built a glob straight out of the configured value.
A backslash is the escape character of the glob syntax and sun.nio.fs.Globs
applies that on Windows too, so a value interpolated from ${project.basedir}
or ${project.build.directory} lost every separator: the pattern degenerated to
"D:aprojtarget" and could never match a real path, and a value ending in a
separator failed the build outright with a PatternSyntaxException. The feature
therefore did nothing on Windows for the documented usage.

Compare as paths first, which is separator-, case- and trailing-separator-safe,
and translate backslashes to forward slashes before building the glob on file
systems that separate names with a backslash. A forward slash is compiled back
into the Windows separator, so globs keep working as documented on both
platforms; on file systems where a backslash is a legal name character it is
left alone, since there it may be a deliberate escape.

The matching logic moves into a package-private method taking a Path so the
Windows behaviour can be exercised without running on Windows.

Generated-by: Claude Code (Claude Opus 5, 1M context)
@slachiewicz slachiewicz added java Pull requests that update Java code bug Something isn't working labels Aug 9, 2026
@slachiewicz
slachiewicz marked this pull request as draft August 9, 2026 10:28
Path rejects a wildcard on Windows, so constructing the pattern with
resolve("*") threw InvalidPathException there and failed the job. Assemble it
as a string with the platform separator instead, which on Windows also covers
a glob that has to survive the separator translation.

Generated-by: Claude Opus 5 (1M context)
@slachiewicz

Copy link
Copy Markdown
Member Author

Ported to the maintenance line in #1155. maven-plugin-tools-3.14.x and maven-plugin-tools-3.7.x predate the feature and need nothing.

Full matrix is green on this branch, including all five windows-latest jobs (run 31308469919) — note that the pull_request run is skipped wholesale for a head branch living in this repo rather than a fork, so the PR's own check list is not the place to look.

@slachiewicz
slachiewicz requested review from elharo and a lite review from Copilot August 9, 2026 10:51

Copilot AI 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.

Pull request overview

Fixes excludedScanDirectories path matching on Windows by avoiding broken raw-glob matching of backslash-separated absolute paths, while preserving glob support for subtree exclusions.

Changes:

  • Reworks exclusion matching to compare configured exclusions as normalized Paths first (exact source-root exclusion), then fall back to glob matching.
  • Makes Windows glob handling robust by translating \ to / before compiling the glob on backslash-separator file systems.
  • Adds unit tests covering exact matches, trailing separators, empty/null entries, glob behavior, and Windows-separator translation.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated no comments.

File Description
maven-plugin-tools-api/src/main/java/org/apache/maven/tools/plugin/DefaultPluginToolsRequest.java Implements path-first exclusion matching and Windows-safe glob pattern translation.
maven-plugin-tools-api/src/test/java/org/apache/maven/tools/plugin/DefaultPluginToolsRequestTest.java Adds focused unit tests to validate exact-path and glob exclusion behavior, including Windows separator handling.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

@slachiewicz
slachiewicz merged commit 6c3450c into master Aug 9, 2026
22 checks passed
@slachiewicz
slachiewicz deleted the fix-excluded-scan-directories-windows branch August 9, 2026 13:37
@github-actions github-actions Bot added this to the 4.0.0-beta-3 milestone Aug 9, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working java Pull requests that update Java code

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants