diff --git a/task_helper/src/build_tool/maven.rs b/task_helper/src/build_tool/maven.rs index da7ffdd..5cf1f94 100644 --- a/task_helper/src/build_tool/maven.rs +++ b/task_helper/src/build_tool/maven.rs @@ -36,6 +36,19 @@ impl Maven { is_debug().then(|| "-Dmaven.surefire.debug".to_string()) } + fn application_args(full_name: &str) -> String { + let mut args = vec![]; + if is_debug() { + args.push(get_jdwp_args()); + } + args.extend([ + "-classpath".to_string(), + "%classpath".to_string(), + full_name.to_string(), + ]); + format!("-Dexec.args={}", args.join(" ")) + } + fn test_filter( package: &str, class: &str, @@ -82,12 +95,15 @@ impl BuildTool for Maven { let compile_goal = if is_test { "test-compile" } else { "compile" }; let classpath_scope = if is_test { "test" } else { "runtime" }; - let mut args = vec![compile_goal.to_string(), "exec:java".to_string()]; + let mut args = vec![compile_goal.to_string(), "exec:exec".to_string()]; args.extend(Self::module_prefix(&module)); - args.push(format!("-Dexec.mainClass={}", full_name)); + args.push("-Dexec.executable=java".to_string()); + args.push(Self::application_args(&full_name)); args.push(format!("-Dexec.classpathScope={}", classpath_scope)); + args.push("-Dexec.inheritIo=true".to_string()); + args.push("-Dexec.longClasspath=true".to_string()); - task(self.command(), args, self.cwd(), Self::debug_env()) + task(self.command(), args, self.cwd(), vec![]) } fn run_test_method( diff --git a/tests/task_verification_test.rs b/tests/task_verification_test.rs index 56a7ea8..532d8d2 100644 --- a/tests/task_verification_test.rs +++ b/tests/task_verification_test.rs @@ -228,6 +228,10 @@ impl<'a> TaskRunner<'a> { .push(("ZED_CUSTOM_java_outer_class_name", o.to_string())); self } + fn env(mut self, key: &'static str, value: &str) -> Self { + self.extra_env.push((key, value.to_string())); + self + } fn run(self) -> String { let mut cmd = std::process::Command::new("sh"); @@ -236,7 +240,11 @@ impl<'a> TaskRunner<'a> { // Create a temporary ZED_EXT directory structure matching what tasks.json expects let zed_ext_base = self.project.temp_dir.join("mock_zed_ext"); - let zed_ext_dir = zed_ext_base.join("zed/extensions/work/java/task-bin"); + let zed_ext_dir = if cfg!(target_os = "macos") { + zed_ext_base.join("Library/Application Support/Zed/extensions/work/java/task-bin") + } else { + zed_ext_base.join("zed/extensions/work/java/task-bin") + }; fs::create_dir_all(&zed_ext_dir).unwrap(); let dest_bin = zed_ext_dir.join("java-task-helper"); fs::copy(&found_bin, &dest_bin).unwrap(); @@ -253,6 +261,7 @@ impl<'a> TaskRunner<'a> { .env("ZED_CUSTOM_java_package_name", &self.package) .env("ZED_CUSTOM_java_class_name", &self.class) .env("PATH", &self.project.new_path) + .env("HOME", &zed_ext_base) .env("XDG_DATA_HOME", zed_ext_base.to_string_lossy().to_string()) .current_dir(&self.project.temp_dir); @@ -286,18 +295,26 @@ fn test_maven_single_module_command_logic() { .run(); assert!( - stdout.contains("MVN_CALLED: compile exec:java"), + stdout.contains("MVN_CALLED: compile exec:exec"), "Should run as single module. Got: {}", stdout ); + assert!( + stdout.contains("-Dexec.executable=java") + && stdout.contains("-Dexec.args=-classpath %classpath com.example.Main") + && stdout.contains("-Dexec.inheritIo=true") + && stdout.contains("-Dexec.longClasspath=true"), + "Should launch the main class in a standalone JVM. Got: {}", + stdout + ); assert!( stdout.contains("-Dexec.classpathScope=runtime"), "Should use runtime classpath scope. Got: {}", stdout ); assert!( - stdout_test - .contains("MVN_CALLED: test-compile exec:java -Dexec.mainClass=com.example.Main"), + stdout_test.contains("MVN_CALLED: test-compile exec:exec") + && stdout_test.contains("-Dexec.args=-classpath %classpath com.example.Main"), "Should run as single module. Got: {}", stdout_test ); @@ -319,12 +336,12 @@ fn test_maven_multi_module_command_logic() { .run(); assert!( - stdout.contains("MVN_CALLED: compile exec:java -pl module-a -am"), + stdout.contains("MVN_CALLED: compile exec:exec -pl module-a -am"), "Should build submodule with dependencies. Got: {}", stdout ); assert!( - stdout.contains("-Dexec.mainClass=com.example.Main"), + stdout.contains("-Dexec.args=-classpath %classpath com.example.Main"), "Should run only the submodule. Got: {}", stdout ); @@ -335,12 +352,12 @@ fn test_maven_multi_module_command_logic() { ); assert!( - stdout_test.contains("MVN_CALLED: test-compile exec:java -pl module-a -am"), + stdout_test.contains("MVN_CALLED: test-compile exec:exec -pl module-a -am"), "Should build submodule with dependencies. Got: {}", stdout_test ); assert!( - stdout_test.contains("-Dexec.mainClass=com.example.Main"), + stdout_test.contains("-Dexec.args=-classpath %classpath com.example.Main"), "Should run only the submodule. Got: {}", stdout_test ); @@ -361,12 +378,12 @@ fn test_maven_nested_module_command_logic() { .run(); assert!( - stdout.contains("MVN_CALLED: test-compile exec:java -pl nested/module-b -am"), + stdout.contains("MVN_CALLED: test-compile exec:exec -pl nested/module-b -am"), "Should build nested submodule with dependencies. Got: {}", stdout ); assert!( - stdout.contains("-Dexec.mainClass=com.example.Main"), + stdout.contains("-Dexec.args=-classpath %classpath com.example.Main"), "Should run only the nested submodule. Got: {}", stdout ); @@ -463,7 +480,7 @@ fn test_maven_single_level_package_logic() { .run(); assert!( - stdout.contains("-Dexec.mainClass=example.Main"), + stdout.contains("-Dexec.args=-classpath %classpath example.Main"), "Should include the single-level package in Maven. Got: {}", stdout ); @@ -475,12 +492,36 @@ fn test_maven_default_package_command_logic() { let stdout = project.task("java-main").package("").class("Main").run(); assert!( - stdout.contains("-Dexec.mainClass=Main"), + stdout.contains("-Dexec.args=-classpath %classpath Main"), "Should not include leading dot for default package in Maven. Got: {}", stdout ); } +#[test] +fn test_maven_debugs_standalone_jvm() { + let project = TestProject::new("maven_debug", "maven", None); + let stdout = project + .task("java-main") + .env("ZED_JAVA_DEBUG", "1") + .env("ZED_JAVA_DEBUG_PORT", "6123") + .run(); + + assert!( + stdout.contains( + "-Dexec.args=-agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=6123 \ + -classpath %classpath com.example.Main" + ), + "Should pass JDWP options to the standalone JVM. Got: {}", + stdout + ); + assert!( + !stdout.contains("MAVEN_OPTS"), + "Should not debug the Maven process. Got: {}", + stdout + ); +} + #[test] fn test_maven_default_package_test_method_logic() { let project = TestProject::new("maven_default_test", "maven", None);