Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -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 的版本输出和发行包布局用于实机验证,机器路径不写入产品源码。
1 change: 1 addition & 0 deletions docs/development/platform-parity-matrix.csv
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
9 changes: 5 additions & 4 deletions docs/development/platform-parity-matrix.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 不适用

## 实现状态定义

Expand Down Expand Up @@ -112,11 +112,12 @@
</details>

<details>
<summary><strong>Java</strong> · 11 个能力点</summary>
<summary><strong>Java</strong> · 12 个能力点</summary>

| 功能组 | 能力点 | macOS | Windows | 负责人 | 验证方式 | 备注 |
| --- | --- | --- | --- | --- | --- | --- |
| Java/Maven | **JDK 与 Maven 工具链发现**<br><sub>java-runtime-discovery</sub> | ✅ 已实现<br><sub>🔍 待验证</sub><br><sub>`macos/Sources/Lithe/Services/Java`、`macos/Sources/Lithe/Views/Run`</sub> | ✅ 已实现<br><sub>🔍 待验证</sub><br><sub>`windows/tauri/src/features/maven`、`windows/tauri/src/features/workspace`</sub> | Java / Maven | 配置多个 JDK/Maven 候选,验证发现、选择、版本不匹配和错误提示。 | |
| Java/Maven | **Windows mvnd 工具链目录选择与 Maven 全局配置**<br><sub>windows-mvnd-toolchain</sub> | 🧩 平台专属<br><sub>— 不适用</sub><br><sub>`.agents/notes/implemented/feature/2026-09-25-windows-mvnd-toolchain.md`</sub> | ✅ 已实现<br><sub>🔍 待验证</sub><br><sub>`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`</sub> | Java / Maven | 在 Windows 选择 mvnd 根目录、bin 目录和启动文件,验证发现、版本、显式执行路径以及 JDT LS 全局配置;真实 mvnd 执行最小 Maven 构建。 自动入口仅显示“自动”,手动选择显示路径,未找到时提示选择安装目录。 | |
| Java/Maven | **项目根、模块与源码集识别**<br><sub>java-maven-project</sub> | ✅ 已实现<br><sub>🔍 待验证</sub><br><sub>`macos/Sources/Lithe/Services/Java`、`shared/contracts/application-boundary.md`</sub> | ✅ 已实现<br><sub>🔍 待验证</sub><br><sub>`windows/tauri/src/features/maven`、`windows/tauri/src/features/workspace`</sub> | Java / Maven | 使用单模块、多模块和非标准源码目录 fixture 对比项目模型。 | |
| Java/Maven | **Profile 与依赖树**<br><sub>java-maven-profiles-dependencies</sub> | ✅ 已实现<br><sub>🔍 待验证</sub><br><sub>`macos/Sources/Lithe/Views/Run`、`macos/Sources/Lithe/Services/Java`</sub> | ✅ 已实现<br><sub>🔍 待验证</sub><br><sub>`windows/tauri/src/features/maven`</sub> | Java / Maven | 切换 Maven profile 并刷新依赖树,确认模块归属、顺序和错误边界。 | |
| Java/Maven | **构建输出与编译诊断**<br><sub>java-build-diagnostics</sub> | ✅ 已实现<br><sub>🔍 待验证</sub><br><sub>`macos/Sources/Lithe/Views/Run`、`macos/Sources/Lithe/Services/Diagnostics`</sub> | ✅ 已实现<br><sub>🔍 待验证</sub><br><sub>`windows/tauri/src/features/maven`、`windows/tauri/src/features/diagnostics`</sub> | Java / Maven | 使用成功、编译失败和进程失败构建 fixture,对比诊断位置、输出和退出状态。 | |
Expand Down
29 changes: 27 additions & 2 deletions rust/lithe-core/src/project/maven.rs
Original file line number Diff line number Diff line change
Expand Up @@ -465,7 +465,15 @@ fn settings_parse_error(error: quick_xml::Error) -> CoreError {
fn installation_settings_path(maven_executable_path: Option<&str>) -> Option<String> {
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 `<home>/bin/mvn*` layout identifies an installation root.
let bin = configured.parent()?;
Expand All @@ -474,7 +482,24 @@ fn installation_settings_path(maven_executable_path: Option<&str>) -> Option<Str
}
bin.parent()?.to_path_buf()
};
let settings = home.join("conf").join("settings.xml");
// mvnd embeds Maven under `mvn`; its own `conf` contains daemon settings.
// Select the same global settings as its Maven engine, including when the
// user has saved the distribution root rather than the resolved launcher.
let mvnd_launchers = ["mvnd.exe", "mvnd.cmd", "mvnd.bat", "mvnd"];
let is_mvnd = if configured.is_dir() {
mvnd_launchers
.iter()
.any(|name| home.join("bin").join(name).is_file())
} else {
// An explicit Maven launcher must retain its own settings even when
// a daemon client is installed beside it.
let name = configured.file_name()?.to_str()?;
mvnd_launchers
.iter()
.any(|launcher| name.eq_ignore_ascii_case(launcher))
};
let maven_home = if is_mvnd { home.join("mvn") } else { home };
let settings = maven_home.join("conf").join("settings.xml");
settings
.is_file()
.then(|| settings.to_string_lossy().into_owned())
Expand Down
54 changes: 54 additions & 0 deletions rust/lithe-core/src/tests/languages.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1287,6 +1287,60 @@ fn maven_installation_settings_reach_jdt_for_a_home_or_a_launcher() {
fs::remove_dir_all(root).expect("Maven fixture should be removable");
}

#[test]
fn mvnd_installation_settings_reach_jdt_from_home_bin_and_launcher() {
struct Fixture(std::path::PathBuf);
impl Drop for Fixture {
fn drop(&mut self) {
if let Err(error) = fs::remove_dir_all(&self.0) {
eprintln!("Could not clean mvnd settings fixture: {error}");
}
}
}
let fixture = Fixture(maven_reactor_root("mvnd-jdt-settings"));
let home = fixture.0.join("maven daemon");
fs::create_dir_all(home.join("bin")).unwrap();
fs::create_dir_all(home.join("mvn/conf")).unwrap();
fs::create_dir_all(home.join("conf")).unwrap();
let launcher = home.join("bin/mvnd.exe");
fs::write(&launcher, "fixture; never executed").unwrap();
let settings = home.join("mvn").join("conf").join("settings.xml");
fs::write(&settings, "<settings/>").unwrap();
// The daemon's conf is not the embedded Maven installation's conf.
fs::write(home.join("conf/settings.xml"), "<settings/>").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");
Expand Down
26 changes: 26 additions & 0 deletions shared/platform-feature-matrix.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
Loading
Loading