Skip to content

fix: detect the project folder of a Gradle module from the classpath (#25632) (CP: 25.2) - #25648

Open
mcollovati wants to merge 1 commit into
25.2from
cherry/cherrypick-25632-to-25.2
Open

fix: detect the project folder of a Gradle module from the classpath (#25632) (CP: 25.2)#25648
mcollovati wants to merge 1 commit into
25.2from
cherry/cherrypick-25632-to-25.2

Conversation

@mcollovati

Copy link
Copy Markdown
Collaborator

This PR cherry-picks changes from the original PR #25632 to branch 25.2.

Original PR description

Summary

In development mode Vaadin finds the project folder from the classpath, but it only knew Maven's target/classes. A Gradle project fell back to the working directory, so an app started from the root of a multi-project build prepared its frontend in the root project instead of in the module being run. This change teaches the detection about Gradle's output folders.

Fixes #25630

What changed

  • FileIOUtils.getProjectFolderFromClasspath now also recognizes the two Gradle source set output layouts: <buildDir>/classes/<language>/<sourceSet> and <buildDir>/resources/<sourceSet>.
  • The build directory name is configurable in Gradle, so a candidate folder is accepted only when it really belongs to a Gradle build: it has a build.gradle/build.gradle.kts of its own, or it lies inside a build whose root has a settings.gradle/settings.gradle.kts. The settings check is needed because a subproject that the root project configures has no build script of its own.
  • The resources layout is checked before the classes one. A build directory named classes makes the two overlap (<project>/classes/resources/main), and the classes layout would climb one level too far.

Background: since the Gradle plugin no longer runs vaadinPrepareFrontend in development mode, there is no flow-build-info.json carrying project.basedir. With an IDE run configuration the working directory is the root project, which has a build script, so the old fallback accepted it and generated package.json, node_modules and src/main/frontend in the wrong place.

Test summary

# Status What the test verifies Why it matters
1 build/classes/java/main resolves to the project folder The main fix: Gradle classes output must be recognized
2 out/resources/main (custom build dir name) resolves to the project folder Resources-only output and non-default build dir names must work
3 With a build dir named classes, classes/resources/main still resolves to the project folder, not its parent Overlapping layouts must not climb one level too far
4 A module with no build script resolves when the root has settings.gradle or settings.gradle.kts Subprojects configured from the root are common and were previously missed
5 A matching folder outside any Gradle build returns null The build dir name is configurable, so name matching alone would claim unrelated paths
6 An app run with the build root as working directory reports the module folder as project folder and <module>/src/main/frontend as frontend folder End-to-end proof of the reported bug: frontend files must not land in the root project
  • FileIOUtilsTest.projectFolderForGradleClassesFolder → 1
  • FileIOUtilsTest.projectFolderForGradleResourcesFolderInCustomBuildFolder → 2
  • FileIOUtilsTest.projectFolderForGradleResourcesFolderInBuildFolderNamedClasses → 3
  • FileIOUtilsTest.projectFolderForGradleModuleWithoutBuildScript (parameterized over both settings script DSLs) → 4
  • FileIOUtilsTest.noProjectFolderForClassesFolderOutsideGradleBuild → 5
  • MiscMultiModuleTest.dev mode project folder is the module folder, not the working directory → 6

Left untested on purpose: the Maven target/classes path and the Windows/Unix path handling, which existing tests in FileIOUtilsTest already cover and this change does not touch.

🤖 Generated with Claude Code

…25632)

In development mode Vaadin finds the project folder from the classpath,
but it only knew Maven's `target/classes`. A Gradle project fell back to
the working directory, so an app started from the root of a
multi-project build prepared its frontend in the root project instead of
in the module being run. This change teaches the detection about
Gradle's output folders.

Fixes #25630

- `FileIOUtils.getProjectFolderFromClasspath` now also recognizes the
two Gradle source set output layouts:
`<buildDir>/classes/<language>/<sourceSet>` and
`<buildDir>/resources/<sourceSet>`.
- The build directory name is configurable in Gradle, so a candidate
folder is accepted only when it really belongs to a Gradle build: it has
a `build.gradle`/`build.gradle.kts` of its own, or it lies inside a
build whose root has a `settings.gradle`/`settings.gradle.kts`. The
settings check is needed because a subproject that the root project
configures has no build script of its own.
- The resources layout is checked before the classes one. A build
directory named `classes` makes the two overlap
(`<project>/classes/resources/main`), and the classes layout would climb
one level too far.

Background: since the Gradle plugin no longer runs
`vaadinPrepareFrontend` in development mode, there is no
`flow-build-info.json` carrying `project.basedir`. With an IDE run
configuration the working directory is the root project, which has a
build script, so the old fallback accepted it and generated
`package.json`, `node_modules` and `src/main/frontend` in the wrong
place.

| # | Status | What the test verifies | Why it matters |
|---|--------|------------------------|----------------|
| 1 | ✅ | `build/classes/java/main` resolves to the project folder | The
main fix: Gradle classes output must be recognized |
| 2 | ✅ | `out/resources/main` (custom build dir name) resolves to the
project folder | Resources-only output and non-default build dir names
must work |
| 3 | ✅ | With a build dir named `classes`, `classes/resources/main`
still resolves to the project folder, not its parent | Overlapping
layouts must not climb one level too far |
| 4 | ✅ | A module with no build script resolves when the root has
`settings.gradle` or `settings.gradle.kts` | Subprojects configured from
the root are common and were previously missed |
| 5 | ✅ | A matching folder outside any Gradle build returns `null` |
The build dir name is configurable, so name matching alone would claim
unrelated paths |
| 6 | ✅ | An app run with the build root as working directory reports
the module folder as project folder and `<module>/src/main/frontend` as
frontend folder | End-to-end proof of the reported bug: frontend files
must not land in the root project |

- `FileIOUtilsTest.projectFolderForGradleClassesFolder` → 1
-
`FileIOUtilsTest.projectFolderForGradleResourcesFolderInCustomBuildFolder`
→ 2
-
`FileIOUtilsTest.projectFolderForGradleResourcesFolderInBuildFolderNamedClasses`
→ 3
- `FileIOUtilsTest.projectFolderForGradleModuleWithoutBuildScript`
(parameterized over both settings script DSLs) → 4
- `FileIOUtilsTest.noProjectFolderForClassesFolderOutsideGradleBuild` →
5
- `MiscMultiModuleTest.dev mode project folder is the module folder, not
the working directory` → 6

Left untested on purpose: the Maven `target/classes` path and the
Windows/Unix path handling, which existing tests in `FileIOUtilsTest`
already cover and this change does not touch.

---------

Co-authored-by: totally-not-ai[bot] <290682512+totally-not-ai[bot]@users.noreply.github.com>
(cherry picked from commit b9c9ca9)
@github-actions

Copy link
Copy Markdown
Contributor

Test Results

 1 385 files  ±0   1 385 suites  ±0   1h 35m 56s ⏱️ - 3m 45s
10 422 tests +6  10 354 ✅ +6  68 💤 ±0  0 ❌ ±0 
10 777 runs  +6  10 708 ✅ +6  69 💤 ±0  0 ❌ ±0 

Results for commit 3393b71. ± Comparison against base commit d45ed41.

@sonarqubecloud

Copy link
Copy Markdown

@mcollovati
mcollovati marked this pull request as ready for review September 10, 2026 15:45
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant