feat: Support multi-module Java projects in tasks#244
feat: Support multi-module Java projects in tasks#244drmaniac wants to merge 1 commit intozed-extensions:mainfrom
Conversation
|
Thanks for the PR, could you provide steps to test these changes or add how you tested them? |
5b53433 to
0e45c2c
Compare
Previously, Zed's Java tasks for running and testing applications did not correctly support multi-module Maven or Gradle projects. When executing a task from a file within a submodule, the commands would attempt to run at the root level, potentially leading to incorrect execution or failures. This change introduces logic to dynamically determine the nearest Maven `pom.xml` or Gradle `build.gradle`/`settings.gradle` file relative to the currently active file. It then modifies the Maven and Gradle commands to target the specific module. For Maven, this involves adding the `-pl <module>` and `-am` flags. For Gradle, it prepends the module path (e.g., `:module-name:`) to the `run` or `test` command. This ensures that tasks are executed within the context of the correct module, improving the developer experience for multi-module Java projects.
0e45c2c to
bb03226
Compare
|
Hi, I have added integration tests to this project (I wasn't sure if these are allowed/expected for extensions). In addition, I apologize that the original PR had an issue with the Maven part; I have fixed it. I also tested the changes in tasks.json by adding them as custom tasks in my local Zed config. I tried it out on a complex Gradle project with composite and multiple modules, and a Maven project with multiple modules. |
There was a problem hiding this comment.
I've never thought of creating a temp project like you are doing here. This is a really nice idea to make testing more robust. I would although expand the temp projects to cover all instances covered by our tasks, for example nesting and single module project
|
|
||
| let tasks_json = fs::read_to_string("languages/java/tasks.json").expect("Failed to read tasks.json"); | ||
| let tasks: Value = serde_json::from_str(&tasks_json).expect("Failed to parse tasks.json"); | ||
| let mut test_command = tasks[1]["command"].as_str().expect("Command is not a string").to_string(); |
There was a problem hiding this comment.
You are testing only tasks at indices 0 and 1 on multi module projects, missing the remaining tasks and not checking normal projects. Also let's consider the possibility of tasks being reordered, tests would start to fail. Maybe we could retrieve the command by tag to make them more robust.
| { | ||
| "label": "Run $ZED_CUSTOM_java_class_name", | ||
| "command": "pkg=\"${ZED_CUSTOM_java_package_name:}\"; cls=\"$ZED_CUSTOM_java_class_name\"; if [ -n \"$pkg\" ]; then c=\"$pkg.$cls\"; else c=\"$cls\"; fi; if [ -f pom.xml ]; then [ -f ./mvnw ] && CMD=\"./mvnw\" || CMD=\"mvn\"; $CMD clean compile exec:java -Dexec.mainClass=\"$c\"; elif [ -f build.gradle ] || [ -f build.gradle.kts ]; then [ -f ./gradlew ] && CMD=\"./gradlew\" || CMD=\"gradle\"; $CMD run -PmainClass=\"$c\"; else find . -name '*.java' -not -path './bin/*' -not -path './target/*' -not -path './build/*' -print0 | xargs -0 javac -d bin && java -cp bin \"$c\"; fi;", | ||
| "command": "pkg=\"$ZED_CUSTOM_java_package_name\"; cls=\"$ZED_CUSTOM_java_class_name\"; if [ -n \"$pkg\" ]; then c=\"$pkg.$cls\"; else c=\"$cls\"; fi; f=\"$ZED_FILE\"; p=\"$PWD\"; d=$(dirname \"${f#$p/}\"); if [ -f pom.xml ]; then m=\".\"; md=\"$d\"; while [ \"$md\" != \".\" ] && [ \"$md\" != \"/\" ]; do if [ -f \"$md/pom.xml\" ]; then m=\"$md\"; break; fi; md=$(dirname \"$md\"); done; [ -f ./mvnw ] && CMD=\"./mvnw\" || CMD=\"mvn\"; if [ \"$m\" = \".\" ]; then $CMD clean test-compile exec:java -Dexec.mainClass=\"$c\" -Dexec.classpathScope=test; else $CMD clean test-compile -pl \"$m\" -am && $CMD exec:java -pl \"$m\" -Dexec.mainClass=\"$c\" -Dexec.classpathScope=test; fi; elif [ -f build.gradle ] || [ -f build.gradle.kts ] || [ -f settings.gradle ] || [ -f settings.gradle.kts ]; then m=\".\"; md=\"$d\"; while [ \"$md\" != \".\" ] && [ \"$md\" != \"/\" ]; do if [ -f \"$md/build.gradle\" ] || [ -f \"$md/build.gradle.kts\" ]; then m=\"$md\"; break; fi; md=$(dirname \"$md\"); done; if [ \"$m\" = \".\" ]; then mp=\"\"; else mp=\":$(echo \"$m\" | tr '/' ':')\"; fi; [ -f ./gradlew ] && CMD=\"./gradlew\" || CMD=\"gradle\"; $CMD ${mp}:run -PmainClass=\"$c\"; else find . -name '*.java' -not -path './bin/*' -not -path './target/*' -not -path './build/*' -print0 | xargs -0 javac -d bin && java -cp bin \"$c\"; fi;", |
There was a problem hiding this comment.
Why are we using test-compile for running the main class alongside providing the tests' scope?
| { | ||
| "label": "Run tests", | ||
| "command": "if [ -f pom.xml ]; then [ -f ./mvnw ] && CMD=\"./mvnw\" || CMD=\"mvn\"; $CMD clean test; elif [ -f build.gradle ] || [ -f build.gradle.kts ]; then [ -f ./gradlew ] && CMD=\"./gradlew\" || CMD=\"gradle\"; $CMD test; else >&2 echo 'No build tool found'; exit 1; fi;", | ||
| "command": "f=\"$ZED_FILE\"; p=\"$PWD\"; d=$(dirname \"${f#$p/}\"); if [ -f pom.xml ]; then m=\".\"; md=\"$d\"; while [ \"$md\" != \".\" ] && [ \"$md\" != \"/\" ]; do if [ -f \"$md/pom.xml\" ]; then m=\"$md\"; break; fi; md=$(dirname \"$md\"); done; [ -f ./mvnw ] && CMD=\"./mvnw\" || CMD=\"mvn\"; if [ \"$m\" = \".\" ]; then $CMD clean test; else $CMD clean test-compile -pl \"$m\" -am && $CMD test -pl \"$m\"; fi; elif [ -f build.gradle ] || [ -f build.gradle.kts ] || [ -f settings.gradle ] || [ -f settings.gradle.kts ]; then m=\".\"; md=\"$d\"; while [ \"$md\" != \".\" ] && [ \"$md\" != \"/\" ]; do if [ -f \"$md/build.gradle\" ] || [ -f \"$md/build.gradle.kts\" ]; then m=\"$md\"; break; fi; md=$(dirname \"$md\"); done; if [ \"$m\" = \".\" ]; then mp=\"\"; else mp=\":$(echo \"$m\" | tr '/' ':')\"; fi; [ -f ./gradlew ] && CMD=\"./gradlew\" || CMD=\"gradle\"; $CMD ${mp}:test; else >&2 echo 'No build tool found'; exit 1; fi;", |
There was a problem hiding this comment.
Why are we first compiling the test to the run it for maven? Isn't that redundant?
Previously, Zed's Java tasks for running and testing applications did not correctly support multi-module Maven or Gradle projects. When executing a task from a file within a submodule, the commands would attempt to run at the root level, potentially leading to incorrect execution or failures.
This change introduces logic to dynamically determine the nearest Maven
pom.xmlor Gradlebuild.gradle/settings.gradlefile relative to the currently active file. It then modifies the Maven and Gradle commands to target the specific module.For Maven, this involves adding the
-pl <module>and-amflags. For Gradle, it prepends the module path (e.g.,:module-name:) to therunortestcommand. This ensures that tasks are executed within the context of the correct module, improving the developer experience for multi-module Java projects.