Repository navigation
Fix excludedScanDirectories never matching on Windows - #1154
Conversation
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)
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)
|
Ported to the maintenance line in #1155. Full matrix is green on this branch, including all five |
There was a problem hiding this comment.
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.
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:A backslash is the escape character of the glob syntax, and
sun.nio.fs.Globs.toRegexPatternapplies that on Windows too — thecase '\\':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 asD:\proj\target\generated-sources/annotations, every separator is consumed, and the pattern degenerates to something likeD: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 aPatternSyntaxExceptionrather than merely failing to match.How it surfaced
The
gh-944-exclude-source-directoryIT never asserted that the exclusion took effect. #1150 changed it to exclude a root holding a mojo's Javadoc and added averify.groovyasserting the description comes out empty — which fails onwindows-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 oneFileSystemand the Windows behaviour is unit-testable off Windows. NewDefaultPluginToolsRequestTestcovers 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
git tag --containson 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.user.dirrather than the module basedir. That is pre-existing, out of scope here, and the documented usage is interpolated absolute paths.