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]

Reply via email to