From ba217ca9ad431bc53ee179e5f5bde0cdc66577d9 Mon Sep 17 00:00:00 2001 From: johnson Date: Sat, 26 Sep 2026 01:24:29 +0800 Subject: [PATCH] feat(windows): support Maven Daemon (mvnd) Relates to 1lck/Lithe-IDEA#889 --- .../2026-09-25-windows-mvnd-toolchain.md | 46 ++++++ docs/development/platform-parity-matrix.csv | 1 + docs/development/platform-parity-matrix.md | 9 +- rust/lithe-core/src/project/maven.rs | 29 +++- rust/lithe-core/src/tests/languages.rs | 54 +++++++ shared/platform-feature-matrix.json | 26 ++++ windows/tauri/src-tauri/src/run.rs | 138 +++++++++++++++--- windows/tauri/src/i18n/locale.ts | 20 +-- 8 files changed, 290 insertions(+), 33 deletions(-) create mode 100644 .agents/notes/implemented/feature/2026-09-25-windows-mvnd-toolchain.md diff --git a/.agents/notes/implemented/feature/2026-09-25-windows-mvnd-toolchain.md b/.agents/notes/implemented/feature/2026-09-25-windows-mvnd-toolchain.md new file mode 100644 index 000000000..ab5e48ae7 --- /dev/null +++ b/.agents/notes/implemented/feature/2026-09-25-windows-mvnd-toolchain.md @@ -0,0 +1,46 @@ +# Agent 笔记:Windows Maven 工具链识别 mvnd + +状态:已实现 + +## 先说结论 + +Windows 的 Maven 选择器接受 Maven Daemon(mvnd,复用后台 Java 进程执行 Maven 构建)的安装主目录、bin 目录和启动文件。它继续复用现有 Maven 导入、构建和运行流程,不增加第二套项目模型。Java 语言服务器使用 mvnd 内嵌 Maven 的全局配置,避免构建与代码分析读取不同的镜像设置。 + +## 问题 + +原目录解析只尝试 `bin/mvn.*`,选择 mvnd 的根目录无法得到启动文件。mvnd 的版本输出先包含 `Apache Maven Daemon`,旧解析器误把 `Daemon` 当成版本;它的 Maven 全局设置位于 `mvn/conf/settings.xml`,并非发行包根目录的 `conf/settings.xml`。 + +## 决策 + +- Windows 的工具链发现、无进程导入解析和执行解析共用目录候选生成器,支持 mvnd 原生客户端及脚本入口。自动模式仍优先考虑项目 Wrapper 和已有 Maven,之后补充 `MVND_HOME` 与 PATH 中的 mvnd。 +- 显式目录选择必须解析到该目录的客户端,不得回退到系统 Maven。正确示例是选择 mvnd 根目录后执行 `bin/mvnd.exe`;不要改为执行其内嵌的 `mvn/bin/mvn.cmd`,否则用户没有真正用到守护进程。 +- 版本字段保留已有 Maven 版本语义,从 mvnd 输出的内嵌 Maven 行解析版本,跳过 Daemon 标题。 +- Core 只负责确定性地解析全局设置路径,识别已安装 mvnd 的 `mvn/conf/settings.xml`。JDT LS(Java 语言服务器)的项目模型仍由上游管理;这不是让 JDT LS 经由 mvnd 导入项目。 +- 不增加全局 `mvnd --stop` 行为。mvnd 的守护进程和复用由上游客户端管理,不能关闭用户其他终端的构建服务。真实验证使用独立的临时守护进程注册目录,并在结束后清理。 + +## 考虑过的备选方案 + +仅让用户填写 `mvnd.exe` 会保留目录选择失败、错误版本和全局配置不一致的问题。退回内嵌普通 Maven 虽可执行相同目标,却违背用户选择 mvnd 的意图。另建 mvnd 项目模型会重复 Maven/JDT LS 的责任,因此均未采用。 + +## 后果 + +项目环境中的 Maven/mvnd 自动识别入口显示“自动”,手动选择后显示用户路径;没有检测到 Maven 或 mvnd 时,明确提示填写或选择安装目录。实际解析路径和失败提示继续由有效工具链状态展示。 + +既有 Maven 设置字段和运行配置无需迁移,用户可直接保存 mvnd 目录。代价是需要覆盖 Maven 与 mvnd 的不同布局;本次 Windows 发现入口之外的 macOS 自动发现尚未增加 mvnd 支持。mvnd 自身的 Java 配置和构建插件兼容性仍由该工具负责。 + +## 验证 + +- `.agents/skills/write-stable-tests/scripts/verify-test-stability.ps1` 检查新增测试的有界等待与资源清理。 +- `.agents/skills/write-stable-tests/scripts/test-stability-windows.ps1` 的 WindowsRust 与 SharedRust 范围记录独立测试时间。 +- `windows/tauri/src-tauri/src/run.rs` 的 mvnd 用例覆盖根目录、bin、启动文件、含空格路径、显式选择优先级、无效路径和版本解析;默认单测不启动外部工具。真实目录集成测试需要显式设置 `LITHE_TEST_MVND_HOME` 并运行 ignored 用例。 +- `rust/lithe-core/src/tests/languages.rs` 验证 mvnd 三种路径都向 JDT LS 提供内嵌 Maven 的设置;文件缺失时不使用错误的根目录设置。 +- `scripts/build-windows.ps1 -Configuration Release` 验证 Windows 宿主和前端构建。 + +## 适用范围 + +- `windows/tauri/src-tauri/src/run.rs` +- `windows/tauri/src/i18n/locale.ts` +- `rust/lithe-core/src/project/maven.rs` +- `rust/lithe-core/src/tests/languages.rs` + +上游依据:[Apache Maven Daemon](https://github.com/apache/maven-mvnd);本机 mvnd 1.0.6 的版本输出和发行包布局用于实机验证,机器路径不写入产品源码。 diff --git a/docs/development/platform-parity-matrix.csv b/docs/development/platform-parity-matrix.csv index 02d529709..827b5b13f 100644 --- a/docs/development/platform-parity-matrix.csv +++ b/docs/development/platform-parity-matrix.csv @@ -23,6 +23,7 @@ ai-chat-session,AI,AI 对话,对话会话与流式响应,未实现,不适用,已 ai-chat-history,AI,AI 对话,对话历史、置顶与归档,未实现,不适用,已实现,待验证,AI,Windows 验证历史加载、重命名、置顶、归档、删除和重启后持久化。,,macos/Sources/Lithe/Platform/MacOS/AI,windows/tauri/src/features/ai/services/ai-chat-history-service.ts; windows/tauri/src/features/layout/components/sidebar/sidebar-history.tsx ai-chat-providers,AI,AI 对话,多 Provider 与工作区范围,未实现,不适用,已实现,待验证,AI,Windows 验证 Provider 切换、凭据边界、工作区范围和模型错误提示。,,macos/Sources/Lithe/Platform/MacOS/AI,windows/tauri/src/features/ai/services/providers; windows/tauri/src/features/ai/lib/ai-workspace-scope.ts java-runtime-discovery,Java,Java/Maven,JDK 与 Maven 工具链发现,已实现,待验证,已实现,待验证,Java / Maven,配置多个 JDK/Maven 候选,验证发现、选择、版本不匹配和错误提示。,,macos/Sources/Lithe/Services/Java; macos/Sources/Lithe/Views/Run,windows/tauri/src/features/maven; windows/tauri/src/features/workspace +windows-mvnd-toolchain,Java,Java/Maven,Windows mvnd 工具链目录选择与 Maven 全局配置,平台专属,不适用,已实现,待验证,Java / Maven,在 Windows 选择 mvnd 根目录、bin 目录和启动文件,验证发现、版本、显式执行路径以及 JDT LS 全局配置;真实 mvnd 执行最小 Maven 构建。 自动入口仅显示“自动”,手动选择显示路径,未找到时提示选择安装目录。,,.agents/notes/implemented/feature/2026-09-25-windows-mvnd-toolchain.md,windows/tauri/src-tauri/src/run.rs; rust/lithe-core/src/project/maven.rs; rust/lithe-core/src/tests/languages.rs; .agents/notes/implemented/feature/2026-09-25-windows-mvnd-toolchain.md; windows/tauri/src/i18n/locale.ts java-maven-project,Java,Java/Maven,项目根、模块与源码集识别,已实现,待验证,已实现,待验证,Java / Maven,使用单模块、多模块和非标准源码目录 fixture 对比项目模型。,,macos/Sources/Lithe/Services/Java; shared/contracts/application-boundary.md,windows/tauri/src/features/maven; windows/tauri/src/features/workspace java-maven-profiles-dependencies,Java,Java/Maven,Profile 与依赖树,已实现,待验证,已实现,待验证,Java / Maven,切换 Maven profile 并刷新依赖树,确认模块归属、顺序和错误边界。,,macos/Sources/Lithe/Views/Run; macos/Sources/Lithe/Services/Java,windows/tauri/src/features/maven java-build-diagnostics,Java,Java/Maven,构建输出与编译诊断,已实现,待验证,已实现,待验证,Java / Maven,使用成功、编译失败和进程失败构建 fixture,对比诊断位置、输出和退出状态。,,macos/Sources/Lithe/Views/Run; macos/Sources/Lithe/Services/Diagnostics,windows/tauri/src/features/maven; windows/tauri/src/features/diagnostics diff --git a/docs/development/platform-parity-matrix.md b/docs/development/platform-parity-matrix.md index 8038ee37f..f23b37bd5 100644 --- a/docs/development/platform-parity-matrix.md +++ b/docs/development/platform-parity-matrix.md @@ -4,9 +4,9 @@ - 最后复核:2026-09-24 - 盘点状态:initial-static-inventory(根据 macOS Views/Application/Services、Windows features/extensions 和共享契约的代码入口进行初版盘点;未替代真实运行验收。) -- 功能项:76 -- macOS:实现:✅ 64 已实现,🟡 3 部分实现,❌ 7 未实现,🧩 2 平台专属;验证:✔️ 0 已验证,🔍 67 待验证,— 9 不适用 -- Windows:实现:✅ 71 已实现,🟡 3 部分实现,❌ 2 未实现,🧩 0 平台专属;验证:✔️ 0 已验证,🔍 74 待验证,— 2 不适用 +- 功能项:77 +- macOS:实现:✅ 64 已实现,🟡 3 部分实现,❌ 7 未实现,🧩 3 平台专属;验证:✔️ 0 已验证,🔍 67 待验证,— 10 不适用 +- Windows:实现:✅ 72 已实现,🟡 3 部分实现,❌ 2 未实现,🧩 0 平台专属;验证:✔️ 0 已验证,🔍 75 待验证,— 2 不适用 ## 实现状态定义 @@ -112,11 +112,12 @@
-Java · 11 个能力点 +Java · 12 个能力点 | 功能组 | 能力点 | macOS | Windows | 负责人 | 验证方式 | 备注 | | --- | --- | --- | --- | --- | --- | --- | | Java/Maven | **JDK 与 Maven 工具链发现**
java-runtime-discovery | ✅ 已实现
🔍 待验证
`macos/Sources/Lithe/Services/Java`、`macos/Sources/Lithe/Views/Run` | ✅ 已实现
🔍 待验证
`windows/tauri/src/features/maven`、`windows/tauri/src/features/workspace` | Java / Maven | 配置多个 JDK/Maven 候选,验证发现、选择、版本不匹配和错误提示。 | | +| Java/Maven | **Windows mvnd 工具链目录选择与 Maven 全局配置**
windows-mvnd-toolchain | 🧩 平台专属
— 不适用
`.agents/notes/implemented/feature/2026-09-25-windows-mvnd-toolchain.md` | ✅ 已实现
🔍 待验证
`windows/tauri/src-tauri/src/run.rs`、`rust/lithe-core/src/project/maven.rs`、`rust/lithe-core/src/tests/languages.rs`、`.agents/notes/implemented/feature/2026-09-25-windows-mvnd-toolchain.md`、`windows/tauri/src/i18n/locale.ts` | Java / Maven | 在 Windows 选择 mvnd 根目录、bin 目录和启动文件,验证发现、版本、显式执行路径以及 JDT LS 全局配置;真实 mvnd 执行最小 Maven 构建。 自动入口仅显示“自动”,手动选择显示路径,未找到时提示选择安装目录。 | | | Java/Maven | **项目根、模块与源码集识别**
java-maven-project | ✅ 已实现
🔍 待验证
`macos/Sources/Lithe/Services/Java`、`shared/contracts/application-boundary.md` | ✅ 已实现
🔍 待验证
`windows/tauri/src/features/maven`、`windows/tauri/src/features/workspace` | Java / Maven | 使用单模块、多模块和非标准源码目录 fixture 对比项目模型。 | | | Java/Maven | **Profile 与依赖树**
java-maven-profiles-dependencies | ✅ 已实现
🔍 待验证
`macos/Sources/Lithe/Views/Run`、`macos/Sources/Lithe/Services/Java` | ✅ 已实现
🔍 待验证
`windows/tauri/src/features/maven` | Java / Maven | 切换 Maven profile 并刷新依赖树,确认模块归属、顺序和错误边界。 | | | Java/Maven | **构建输出与编译诊断**
java-build-diagnostics | ✅ 已实现
🔍 待验证
`macos/Sources/Lithe/Views/Run`、`macos/Sources/Lithe/Services/Diagnostics` | ✅ 已实现
🔍 待验证
`windows/tauri/src/features/maven`、`windows/tauri/src/features/diagnostics` | Java / Maven | 使用成功、编译失败和进程失败构建 fixture,对比诊断位置、输出和退出状态。 | | diff --git a/rust/lithe-core/src/project/maven.rs b/rust/lithe-core/src/project/maven.rs index dccc63fa3..f01e75a53 100644 --- a/rust/lithe-core/src/project/maven.rs +++ b/rust/lithe-core/src/project/maven.rs @@ -465,7 +465,15 @@ fn settings_parse_error(error: quick_xml::Error) -> CoreError { fn installation_settings_path(maven_executable_path: Option<&str>) -> Option { let configured = Path::new(maven_executable_path?); let home = if configured.is_dir() { - configured.to_path_buf() + if configured + .file_name()? + .to_str()? + .eq_ignore_ascii_case("bin") + { + configured.parent()?.to_path_buf() + } else { + configured.to_path_buf() + } } else { // Only the `/bin/mvn*` layout identifies an installation root. let bin = configured.parent()?; @@ -474,7 +482,24 @@ fn installation_settings_path(maven_executable_path: Option<&str>) -> Option").unwrap(); + // The daemon's conf is not the embedded Maven installation's conf. + fs::write(home.join("conf/settings.xml"), "").unwrap(); + for configured in [home.clone(), home.join("bin"), launcher] { + let configuration = jdt_configuration( + fixture.0.to_str().unwrap(), + maven_jdt_context(Some(configured.to_string_lossy().into_owned())), + ) + .unwrap(); + assert_eq!( + configuration.global_settings_path.as_deref(), + settings.to_str() + ); + } + // Explicit ordinary Maven must not inherit the adjacent daemon's settings. + let maven_launcher = home.join("bin").join("mvn.cmd"); + fs::write(&maven_launcher, "fixture; never executed").unwrap(); + let configuration = jdt_configuration( + fixture.0.to_str().unwrap(), + maven_jdt_context(Some(maven_launcher.to_string_lossy().into_owned())), + ) + .unwrap(); + let maven_settings = home.join("conf").join("settings.xml"); + assert_eq!( + configuration.global_settings_path.as_deref(), + maven_settings.to_str() + ); + fs::remove_file(settings).unwrap(); + let configuration = jdt_configuration( + fixture.0.to_str().unwrap(), + maven_jdt_context(Some(home.to_string_lossy().into_owned())), + ) + .unwrap(); + assert_eq!(configuration.global_settings_path, None); +} + #[test] fn maven_installation_settings_are_absent_for_wrappers_and_bare_installations() { let root = maven_reactor_root("maven-jdt-global-settings-absent"); diff --git a/shared/platform-feature-matrix.json b/shared/platform-feature-matrix.json index 6e4500a00..ed623fa00 100644 --- a/shared/platform-feature-matrix.json +++ b/shared/platform-feature-matrix.json @@ -604,6 +604,32 @@ "owner": "Java / Maven", "verification": "配置多个 JDK/Maven 候选,验证发现、选择、版本不匹配和错误提示。" }, + { + "id": "windows-mvnd-toolchain", + "area": "Java", + "group": "Java/Maven", + "capability": "Windows mvnd 工具链目录选择与 Maven 全局配置", + "macos": { + "evidence": [ + ".agents/notes/implemented/feature/2026-09-25-windows-mvnd-toolchain.md" + ], + "implementationStatus": "platform-specific", + "verificationStatus": "not-applicable" + }, + "windows": { + "evidence": [ + "windows/tauri/src-tauri/src/run.rs", + "rust/lithe-core/src/project/maven.rs", + "rust/lithe-core/src/tests/languages.rs", + ".agents/notes/implemented/feature/2026-09-25-windows-mvnd-toolchain.md", + "windows/tauri/src/i18n/locale.ts" + ], + "implementationStatus": "implemented", + "verificationStatus": "pending" + }, + "owner": "Java / Maven", + "verification": "在 Windows 选择 mvnd 根目录、bin 目录和启动文件,验证发现、版本、显式执行路径以及 JDT LS 全局配置;真实 mvnd 执行最小 Maven 构建。 自动入口仅显示“自动”,手动选择显示路径,未找到时提示选择安装目录。" + }, { "id": "java-maven-project", "area": "Java", diff --git a/windows/tauri/src-tauri/src/run.rs b/windows/tauri/src-tauri/src/run.rs index 5ed1a5806..006e820a9 100644 --- a/windows/tauri/src-tauri/src/run.rs +++ b/windows/tauri/src-tauri/src/run.rs @@ -21,6 +21,9 @@ use tauri::{AppHandle, Emitter, Manager}; mod launch_arguments; +const MAVEN_LAUNCHERS: &[&str] = &["mvn.cmd", "mvn.bat", "mvn.exe", "mvn"]; +const MVND_LAUNCHERS: &[&str] = &["mvnd.exe", "mvnd.cmd", "mvnd.bat", "mvnd"]; + const CREATE_NO_WINDOW: u32 = 0x0800_0000; const RUN_OUTPUT_FLUSH_INTERVAL: Duration = Duration::from_millis(100); const RUN_OUTPUT_HIGH_WATER_BYTES: usize = 1_048_576; @@ -1218,7 +1221,17 @@ fn maven_executable_candidates(project_root: Option<&Path>) -> Vec { executables.push(home.join("bin").join("mvn.cmd")); executables.push(home.join("bin").join("mvn")); } - for name in ["mvn.cmd", "mvn.bat", "mvn.exe", "mvn"] { + // Preserve ordinary Maven discovery precedence; mvnd is an additional + // installation, not a reason to replace an existing project wrapper. + for name in MAVEN_LAUNCHERS { + if let Some(path) = lookup_on_path(name) { + executables.push(path); + } + } + if let Some(home) = std::env::var_os("MVND_HOME") { + executables.extend(custom_maven_executable_candidates(Path::new(&home))); + } + for name in MVND_LAUNCHERS { if let Some(path) = lookup_on_path(name) { executables.push(path); } @@ -1230,13 +1243,17 @@ fn custom_maven_executable_candidates(path: &Path) -> Vec { if path.is_file() { return vec![path.to_path_buf()]; } - let bin = path.join("bin"); - let mut candidates = ["mvn.cmd", "mvn.bat", "mvn.exe", "mvn"] + // Settings accept an installation root, its bin directory, or a launcher. + // Never substitute mvnd's embedded mvn: that silently disables the daemon. + [path.join("bin"), path.to_path_buf()] .into_iter() - .map(|name| bin.join(name)) - .collect::>(); - candidates.push(path.to_path_buf()); - candidates + .flat_map(|bin| { + MVND_LAUNCHERS + .iter() + .chain(MAVEN_LAUNCHERS) + .map(move |name| bin.join(name)) + }) + .collect() } pub(crate) fn probe_java_home(home: &Path) -> Option { @@ -1507,14 +1524,10 @@ fn resolve_maven_executable( } else { root.join(configured) }; - let candidates = [ - path.clone(), - path.join("bin").join("mvn.cmd"), - path.join("bin").join("mvn.bat"), - path.join("bin").join("mvn.exe"), - path.join("bin").join("mvn"), - ]; - if let Some(found) = candidates.into_iter().find(|candidate| candidate.is_file()) { + if let Some(found) = custom_maven_executable_candidates(&path) + .into_iter() + .find(|candidate| candidate.is_file()) + { return Ok(normalize_path(&found).to_string_lossy().into_owned()); } return Err("Maven executable path does not exist.".into()); @@ -1835,8 +1848,13 @@ fn java_version(output: &str) -> Option { fn maven_version(output: &str) -> Option { output.lines().find_map(|line| { - let rest = line.trim().strip_prefix("Apache Maven")?; - rest.split_whitespace().next().map(str::to_string) + // mvnd prints "Apache Maven Daemon ..." before the embedded Maven + // version. Do not report "Daemon" as the Maven version. + let rest = line.trim().strip_prefix("Apache Maven ")?; + let version = rest.split_whitespace().next()?; + version + .starts_with(|c: char| c.is_ascii_digit()) + .then(|| version.to_string()) }) } @@ -2314,6 +2332,92 @@ mod tests { fs::remove_dir_all(root).ok(); } + #[test] + fn mvnd_paths_resolve_consistently_for_import_discovery_and_execution() { + struct Fixture(PathBuf); + impl Drop for Fixture { + fn drop(&mut self) { + if let Err(error) = fs::remove_dir_all(&self.0) { + eprintln!("Could not clean mvnd fixture: {error}"); + } + } + } + let fixture = Fixture(temp_project()); + for name in ["mvnd.exe", "mvnd.cmd", "mvnd.bat", "mvnd"] { + let home = fixture.0.join(format!("maven daemon {name}")); + let launcher = home.join("bin").join(name); + fs::create_dir_all(launcher.parent().unwrap()).unwrap(); + fs::write(&launcher, "fixture; never executed").unwrap(); + // A distribution also contains ordinary Maven. Selecting mvnd must + // retain its daemon client, rather than silently selecting mvn. + fs::create_dir_all(home.join("mvn/bin")).unwrap(); + fs::write(home.join("mvn/bin/mvn.cmd"), "embedded Maven").unwrap(); + fs::write(fixture.0.join("mvnw.cmd"), "wrapper").unwrap(); + let expected = normalize_path(&launcher).to_string_lossy().into_owned(); + for configured in [home.clone(), home.join("bin"), launcher.clone()] { + let configured = configured.to_string_lossy(); + assert_eq!( + maven_executable_without_probing(&fixture.0, Some(&configured)), + Some(expected.clone()) + ); + assert_eq!( + resolve_maven_executable(&fixture.0, &fixture.0, &configured).unwrap(), + expected + ); + let discovered = + discover_maven_candidates(vec![launcher.clone()], Some(&configured), |path| { + path.is_file().then(|| MavenRuntime { + executable_path: normalize_path(path).to_string_lossy().into_owned(), + version: "3.9.16".into(), + }) + }); + assert_eq!(discovered.len(), 1); + assert_eq!(discovered[0].executable_path, expected); + } + } + let invalid = fixture + .0 + .join("missing-mvnd") + .to_string_lossy() + .into_owned(); + assert!(maven_executable_without_probing(&fixture.0, Some(&invalid)).is_none()); + assert!(resolve_maven_executable(&fixture.0, &fixture.0, &invalid).is_err()); + } + + #[test] + fn mvnd_version_uses_embedded_maven_instead_of_daemon_banner() { + let output = "Apache Maven Daemon (mvnd) 1.0.6 windows-amd64 native client\nTerminal: DumbTerminal\nApache Maven 3.9.16 (revision)\nMaven home: example/mvnd/mvn"; + assert_eq!(maven_version(output).as_deref(), Some("3.9.16")); + assert_eq!( + maven_version("Apache Maven 3.9.9 (revision)").as_deref(), + Some("3.9.9") + ); + assert_eq!(maven_version("Apache Maven Daemon (mvnd) 1.0.6"), None); + } + + #[test] + #[ignore = "integration: requires LITHE_TEST_MVND_HOME pointing to a real distribution"] + fn mvnd_installed_distribution_resolves_without_spawning_a_daemon() { + let home = PathBuf::from( + std::env::var_os("LITHE_TEST_MVND_HOME").expect("set LITHE_TEST_MVND_HOME"), + ); + let expected = normalize_path(&home.join("bin/mvnd.exe")) + .to_string_lossy() + .into_owned(); + assert!(Path::new(&expected).is_file()); + for configured in [home.clone(), home.join("bin"), home.join("bin/mvnd.exe")] { + let configured = configured.to_string_lossy(); + assert_eq!( + maven_executable_without_probing(&home, Some(&configured)), + Some(expected.clone()) + ); + assert_eq!( + resolve_maven_executable(&home, &home, &configured).unwrap(), + expected + ); + } + } + #[test] fn custom_maven_home_discovers_its_bin_executable() { struct Fixture(PathBuf); diff --git a/windows/tauri/src/i18n/locale.ts b/windows/tauri/src/i18n/locale.ts index 2281a4fce..9e99ddaeb 100644 --- a/windows/tauri/src/i18n/locale.ts +++ b/windows/tauri/src/i18n/locale.ts @@ -1556,9 +1556,9 @@ const catalogs = { "run.mainClass": "Main class", "run.jdkHome": "JDK Home", "run.jdkHomeHint": "Leave empty to use the detected JDK.", - "run.mavenExecutable": "Maven home or executable", + "run.mavenExecutable": "Maven / mvnd home or executable", "run.mavenExecutableHint": - "Choose a Maven home directory, or leave empty to use the project wrapper or detected Maven.", + "Choose a Maven or mvnd home/bin directory or executable; leave empty to use the project wrapper or a detected installation.", "run.nodeExecutable": "Node.js executable", "run.nodeExecutableHint": "Choose node.exe, or leave empty to use the detected Node.js runtime.", @@ -1625,7 +1625,7 @@ const catalogs = { "maven.settings": "Maven Settings", "maven.automatic": "Automatic", "maven.localRepository": "Local repository", - "maven.mavenExecutable": "Maven home or executable", + "maven.mavenExecutable": "Maven / mvnd home or executable", "maven.javaHome": "Maven JDK Home", "maven.stop": "Stop Maven task", "maven.rerunTest": "Rerun test", @@ -1773,7 +1773,7 @@ const catalogs = { "settings.run.description": "Select a service or task to configure its arguments, environment and project-environment overrides. Changes apply after Save.", "settings.project.openProject": "Open a project to configure its JDK and Maven.", "settings.project.scope": "Defaults for this project on this computer. Run configurations inherit these values unless explicitly overridden. Leave a path empty to use automatic selection.", - "settings.project.mavenAutomatic": "Automatic: project Maven Wrapper, then detected Maven. You can also enter a Maven home or executable path.", + "settings.project.mavenAutomatic": "Automatic", "settings.project.useProjectJdk": "Use project JDK", "settings.project.detected": "Detected installations", "settings.project.saved": "Project environment saved.", @@ -1796,7 +1796,7 @@ const catalogs = { "toolchain.maven": "Maven {version}", "toolchain.detecting": "Detecting…", "toolchain.javaNotFound": "No JDK found. Set JAVA_HOME, install a JDK, or choose a JDK directory.", - "toolchain.mavenNotFound": "No Maven found. Add a Maven Wrapper to the project or choose a Maven installation.", + "toolchain.mavenNotFound": "No Maven or mvnd found. Enter or choose its installation directory, or add a Maven Wrapper to the project.", "toolchain.invalid": "Cannot be used: {message}", "toolchain.failed": "Could not resolve: {message}", "settings.tabs.appearance": "Appearance", @@ -5969,8 +5969,8 @@ const catalogs = { "run.mainClass": "主类", "run.jdkHome": "JDK 主目录", "run.jdkHomeHint": "留空则使用自动检测到的 JDK。", - "run.mavenExecutable": "Maven 主目录 / 可执行文件", - "run.mavenExecutableHint": "可选择 Maven 主目录;留空则使用项目 Wrapper 或系统 Maven。", + "run.mavenExecutable": "Maven / mvnd 主目录或可执行文件", + "run.mavenExecutableHint": "可选择 Maven 或 mvnd 主目录、bin 目录或可执行文件;留空则使用项目 Wrapper 或已检测到的安装。", "run.nodeExecutable": "Node.js 可执行文件", "run.nodeExecutableHint": "可选择 node.exe;留空则使用自动检测到的 Node.js 运行时。", "run.mavenJdkHome": "Maven JDK 主目录", @@ -6034,7 +6034,7 @@ const catalogs = { "maven.settings": "Maven 设置", "maven.automatic": "自动检测", "maven.localRepository": "本地仓库", - "maven.mavenExecutable": "Maven 主目录 / 可执行文件", + "maven.mavenExecutable": "Maven / mvnd 主目录或可执行文件", "maven.javaHome": "Maven JDK 主目录", "maven.stop": "停止 Maven 任务", "maven.rerunTest": "重新运行测试", @@ -6175,7 +6175,7 @@ const catalogs = { "settings.run.description": "选择服务或任务,配置启动参数、环境变量和项目环境覆盖项;点击保存后生效。", "settings.project.openProject": "打开项目后,可在这里配置项目 JDK 和 Maven。", "settings.project.scope": "仅保存在当前电脑,作用于当前项目。运行配置默认继承这些值,单独设置的覆盖值保持不变。路径留空时使用自动选择。", - "settings.project.mavenAutomatic": "自动:优先使用项目 Maven Wrapper,其次使用检测到的 Maven。也可以填写 Maven 主目录或可执行文件路径。", + "settings.project.mavenAutomatic": "自动", "settings.project.useProjectJdk": "使用项目 JDK", "settings.project.detected": "检测到的安装", "settings.project.saved": "项目环境已保存。", @@ -6198,7 +6198,7 @@ const catalogs = { "toolchain.maven": "Maven {version}", "toolchain.detecting": "正在检测…", "toolchain.javaNotFound": "未找到 JDK。请设置 JAVA_HOME、安装 JDK,或选择 JDK 目录。", - "toolchain.mavenNotFound": "未找到 Maven。请为项目添加 Maven Wrapper,或选择 Maven 安装目录。", + "toolchain.mavenNotFound": "未找到 Maven 或 mvnd。请手动填写或选择安装目录,或为项目添加 Maven Wrapper。", "toolchain.invalid": "无法使用:{message}", "toolchain.failed": "无法解析:{message}", "settings.tabs.appearance": "外观",