jdaugherty commented on code in PR #16472:
URL: https://github.com/apache/grails-core/pull/16472#discussion_r4169666684
##########
grails-doc/src/en/guide/gettingStarted/developmentReloading.adoc:
##########
@@ -64,3 +64,35 @@ Hotswap Agent is an open-source tool that provides advanced
hot swapping capabil
Enables limited dynamic reloading via standard JVM hot swapping. This feature
is built into the JVM and does not require additional configuration beyond
starting the application with debugging enabled. Supports automatic reloading
of static content (such as CSS, JavaScript, or HTML templates) without
restarting the application. You can modify and reload Java code changes
without a full restart, but this is limited to non-structural modifications.
Changes that affect class or method signatures (e.g., adding new methods,
fields, or constructors; changing method parameters; or modifying class
hierarchies) are not supported and will require a restart. This limitation
stems from the JVM's hot swapping constraints.
*Reloading Mechanism:* standard JVM hot swapping
+
+=== Running More Than One Instance of a Checkout
+
+Two Gradle builds of one checkout share its `build/` directory. While an
application runs from `build/`, another build of the same checkout (a second
instance on another port, tests run alongside it, an IDE or a tool compiling in
the same tree) rewrites the classes it runs from, and Spring Boot Developer
Tools restarts the running application into a half-written build.
+
+Give each instance a build directory of its own instead, by setting
`layout.buildDirectory` from a Gradle property for every project in the build:
+
+[source,groovy]
+.build.gradle
+----
+allprojects {
+ def instanceBuildDir = providers.gradleProperty('instanceBuildDir')
+ if (instanceBuildDir.present) {
+ layout.buildDirectory =
layout.projectDirectory.dir(instanceBuildDir.get())
+ }
+}
+----
+
+Then start each instance with a build directory, a port and a Gradle project
cache of its own:
+
+[source,shell]
+----
+./gradlew bootRun -PinstanceBuildDir=build-parent/build-8081 \
+ --project-cache-dir=build-parent/build-8081/.gradle \
+ --args='--server.port=8081'
Review Comment:
Two problems with these paths, both from running this recipe:
1. `--project-cache-dir` is inside the root project's build directory, so
`clean` deletes the project cache of the build that is running it. `./gradlew
-PinstanceBuildDir=build-parent/build-8081
--project-cache-dir=build-parent/build-8081/.gradle clean` removes
`executionHistory`, `fileHashes`, `buildOutputCleanup` and their `.lock` files
from under the running build, and still reports success.
2. A generated app's `.gitignore` has `build/` and `.gradle`, which match at
any depth, but nothing matches `build-parent/build-8081/`. So every class an
instance compiles shows up as untracked in `git status`.
A per-instance directory that holds both, with the build directory still
named `build`, fixes both. The default `.gitignore` then covers
`instances/8081/build/` (in subprojects too) and `instances/8081/.gradle/`, and
`clean` leaves the sibling cache alone. I checked both layouts with `git
check-ignore` against the forge `.gitignore` template, and ran `clean` with
each.
```suggestion
./gradlew bootRun -PinstanceBuildDir=instances/8081/build \
--project-cache-dir=instances/8081/.gradle \
--args='--server.port=8081'
```
##########
grails-doc/src/en/guide/gettingStarted/developmentReloading.adoc:
##########
@@ -64,3 +64,35 @@ Hotswap Agent is an open-source tool that provides advanced
hot swapping capabil
Enables limited dynamic reloading via standard JVM hot swapping. This feature
is built into the JVM and does not require additional configuration beyond
starting the application with debugging enabled. Supports automatic reloading
of static content (such as CSS, JavaScript, or HTML templates) without
restarting the application. You can modify and reload Java code changes
without a full restart, but this is limited to non-structural modifications.
Changes that affect class or method signatures (e.g., adding new methods,
fields, or constructors; changing method parameters; or modifying class
hierarchies) are not supported and will require a restart. This limitation
stems from the JVM's hot swapping constraints.
*Reloading Mechanism:* standard JVM hot swapping
+
+=== Running More Than One Instance of a Checkout
+
+Two Gradle builds of one checkout share its `build/` directory. While an
application runs from `build/`, another build of the same checkout (a second
instance on another port, tests run alongside it, an IDE or a tool compiling in
the same tree) rewrites the classes it runs from, and Spring Boot Developer
Tools restarts the running application into a half-written build.
+
+Give each instance a build directory of its own instead, by setting
`layout.buildDirectory` from a Gradle property for every project in the build:
+
+[source,groovy]
+.build.gradle
+----
+allprojects {
+ def instanceBuildDir = providers.gradleProperty('instanceBuildDir')
+ if (instanceBuildDir.present) {
+ layout.buildDirectory =
layout.projectDirectory.dir(instanceBuildDir.get())
+ }
+}
+----
+
+Then start each instance with a build directory, a port and a Gradle project
cache of its own:
+
+[source,shell]
+----
+./gradlew bootRun -PinstanceBuildDir=build-parent/build-8081 \
+ --project-cache-dir=build-parent/build-8081/.gradle \
+ --args='--server.port=8081'
+----
+
+The project cache (the checkout's `.gradle/` directory) does not move with the
build directory, and two builds running at once contend for it, so
`--project-cache-dir` gives each build its own.
+
+The Grails Gradle plugin tells the application where its build directory is.
It passes `grails.project.class.dir` and `grails.project.resource.dir` to
`bootRun` and the other forked `JavaExec` and `Test` tasks, so development
reloading compiles a changed class and copies a changed message bundle into
that instance's build directory, and the application reads its resources from
there. An `@Integration` test without an `applicationClass` finds the
application class next to its own compiled classes, wherever the build
directory is.
Review Comment:
With this section applied, every start and every devtools restart logs an
ERROR with a stack trace:
```
ERROR grails.util.Environment : spring-dev-tools restart unable to write pid
file: {}
java.io.FileNotFoundException:
.../grails-test-examples/exploded/build/.grailspid (No such file or directory)
at grails.util.Environment.writeDevToolsPidFile(Environment.groovy:381)
at grails.util.Environment.isDevtoolsRestart(Environment.groovy:368)
at grails.boot.GrailsApp.run(GrailsApp.groovy:142)
```
`Environment.isDevtoolsRestart()` writes `.grailspid` to
`BuildSettings.TARGET_DIR`. That still resolves to `<app>/build`, which doesn't
exist in this setup. The plugin does pass a target directory, but it has no
effect. `configureForkSettings` passes `grails.project.target.dir` with only
the directory name (`build-8081`), and `BuildSettings` reads
`project.target.dir`.
Could `TARGET_DIR` follow the build directory the way the classes and
resources directories now do? For example, `BuildSettings` could read
`PROJECT_TARGET_DIR` joined to `BASE_DIR`, with `project.target.dir` kept as
the fallback, and the plugin could pass the build directory relative to the
project through `GrailsProjectOutputDirProvider` instead of its name. Each
instance's pid file would then go into its own build directory, not a shared
`build/.grailspid`. Any moved build directory already hits this, but this
section is what will lead people into that setup.
--
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]