feat(windows): JDK 8 超长 classpath 自动改用临时 manifest JAR - #1012
Merged
Merged
Conversation
JDK 8 不支持 @argfile,Windows 普通 Run 遇到超长 classpath 时 CreateProcessW 直接失败。现在已确认 JDK < 9 时由 Core 规划一个只含 META-INF/MANIFEST.MF 的临时 classpath JAR(Class-Path 写绝对、 百分号编码的 file: URL,目录以 / 结尾,72 字节折行),宿主写到 temp_dir()/lithe-run 并沿用 argfile 的独占创建与 RAII 清理。 JDK 9+ 仍走 argfile,版本未知保持原命令。 Co-Authored-By: Claude <noreply@anthropic.com>
自审发现 \\?\C:\... 和 \\?\UNC\... 形式的条目或工作目录会被当成主机名 为 "?" 的 UNC 路径,生成错误的 file: URL。现在先还原为普通盘符/UNC 路径;其他 \\?\ 与 \\.\ 设备路径无法表示为 file: URL,保持原命令启动。 Co-Authored-By: Claude <noreply@anthropic.com>
Guard pending launches against stop/restart races, preserve execution and window ownership, and clean reservations on failure. Register per-execution Java files as non-reusable resources with tests and lifecycle documentation.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Refs #955
只实现 #955 中维护者已承诺的 JDK 8 部分,不增加 IDEA 式的手动“缩短命令行”选项。
背景
v0.5.7(#746、#888)已在 Windows 普通 Java Run 中为 JDK 9+ 自动把超长 classpath/module-path 写入临时
@argfile。JDK 8 不支持@argfile,依赖很多的项目在 JDK 8 下仍会因命令行超过CreateProcessW的 32767 字符上限而无法启动。方案
参考 IDEA 对 JDK 8 的 “JAR manifest” 缩短方式:已确认 JDK 版本 < 9 且命令接近 Windows 上限时,生成一个只含
META-INF/MANIFEST.MF的临时 classpath JAR,用Class-Path列出原 classpath 的全部条目,再用-cp <该 JAR>启动。@argfile;JDK 版本无法识别时保持原命令(与现状一致)。temp_dir()/lithe-run/launch-<pid>-<n>.classpath.jar,create_new独占创建,复用 argfile 的LaunchArgumentFileRAII 所有者:写入失败、启动失败时立即删除,启动成功后由该进程的退出线程删除。不写 JDK 目录和安装目录。manifest 规则:
./..按 Windows 词法规则折叠。file:/C:/...,UNC 写成file:////host/share/...,\\?\C:\和\\?\UNC\长路径前缀先还原。[A-Za-z0-9-._~/:]字节一律按 UTF-8 百分号编码(空格、中文、#、%、+等)。manifest 因此是纯 ASCII,和 Windows 系统代码页无关。/结尾,否则 JDK 会把它当 JAR 打开。-cp/-classpath(重复出现时取最后一个,且只看主类之前的启动器选项),JVM 选项、主类、程序参数不动。lib/*通配符、C:lib这类依赖每盘当前目录的路径、\\.\设备路径、-jar启动。改动明细
rust/lithe-core/src/execution/classpath_jar.rs(新增):plan_classpath_jar_launch、ClasspathJarRequest、ClasspathJarPlan、ClasspathStyle,包括路径绝对化、file:URL 编码、manifest 72 字节折行,以及 8 个单元测试。rust/lithe-core/src/execution/launch_command.rs:COMMAND_LINE_MARGIN和command_line_length改为pub(super),供 JDK 8 规划复用同一长度预算;模块文档补充 JDK 8 走向。逻辑无变化。rust/lithe-core/src/execution/mod.rs:注册新模块并导出新 API。windows/tauri/src-tauri/src/run/launch_arguments.rs:prepare新增working_directory参数;确认 JDK < 9 时调用新规划并写入只含 manifest 的 JAR(zipcrate 已是该 crate 依赖,条目不压缩);临时路径生成函数改名为next_temporary_path(extension),argfile 与 JAR 共用;新增 3 个测试,原有 2 个测试适配新签名。windows/tauri/src-tauri/src/run.rs:把working_directory传给参数规划;目录探测、JDK release 读取、临时文件写入、进程创建及终止移到后台阻塞任务。按窗口/会话登记独立启动请求,停止会使待启动请求失效,重新运行会替换旧请求,准备完成后检查归属并原子发布进程;旧 executionId 的停止请求不能取消新执行。新增 4 个无等待生命周期测试,覆盖停止、重启乱序、多窗口与相同 executionId、所有者清理。scripts/worktree-resources.json、scripts/reuse-worktree-resources.mjs、scripts/test-reuse-worktree-resources.mjs、docs/ci-builds.md:把临时 argfile 与 classpath JAR 登记为不可跨工作树复用的进程私有资源,复用脚本显式拒绝,并测试列表排除与复制拒绝。pending。shared/contracts/rust-core-api.md:记录plan_classpath_jar_launch的契约(仅 Rust API,暂无 JSON 命令及原因)。.agents/notes/implemented/bug-fix/2026-09-20-oversized-java-launch-command.md:新增 JDK 8 classpath JAR 决策、规则、代价(java.class.path只剩 JAR)、被否方案(所有 JDK 都用 JAR、相对 URL、环境变量)、未覆盖路径和验证命令。shared/platform-feature-matrix.json:run-java-long-classpath行的 Windows evidence 加入新文件,verification 补充 JDK 8/JDK 17 对照验收步骤;状态仍为pending(未做 Windows 实机验证)。docs/development/platform-parity-matrix.md、.csv:由生成脚本重新生成。覆盖范围
run_start_process,包括 main 运行和使用同一启动入口的步骤)。ARG_MAX远大于 Windows 上限,JDK 8 直接启动不会超限,所以不接入,也没有新增 JSON 命令。System.getProperty("java.class.path")只返回临时 JAR;只经类加载器加载类的代码不受影响,个别直接解析该属性扫描类路径的工具可能表现不同。只有在原本就会因超长而启动失败时才会触发。最新修复验证(94efa552,Linux)
cargo check --manifest-path windows/tauri/src-tauri/Cargo.toml --target x86_64-pc-windows-gnu --tests:通过,包含 Windows 宿主与新增测试的交叉编译检查。run-rust-tests-with-timing.mjs逐例执行,全部通过,耗时 7–15ms。HTML/JUnit 已生成;此隔离测试不等同于 Windows 完整宿主运行。node --test scripts/test-reuse-worktree-resources.mjs:通过,包含新增 Java 临时文件排除测试与原有资源复用场景。原实现验证
本机为 macOS(arm64),以下命令实际运行:
cargo test --manifest-path rust/Cargo.toml -p lithe-core --lib:754 passed。其中execution::19 个(新增 8 个classpath_jar用例,覆盖 JDK 8 触发、JDK 9+/未知版本/短命令/非 Java 不变、空格/中文/#%+编码、目录/、相对/UNC/空条目/\\?\前缀、72 字节折行、只替换生效的-cp、通配符/-jar/设备路径不改写、POSIX 分隔符)。cargo test --manifest-path windows/tauri/src-tauri/Cargo.toml:199 passed,5 failed。这 5 个(debug::tests::derives_adapter_identifier_from_the_executable_name、project_window_registry::tests::follows_directory_links_without_folding_case_sensitive_names、run::tests::{workspace_relative_paths_use_forward_slashes, write_generated_creates_lithe_documents, toolchain_probe_captures_both_version_streams})在未改动的origin/preview上于 macOS 同样失败,属于依赖 Windows 路径语义的既有用例,与本 PR 无关。新增/修改的 5 个run::launch_arguments用例全部通过:JDK 8 生成 JAR 于临时目录、manifest 内容与目录/、释放后删除;JDK 17 仍用 argfile;版本未知不写文件。node .agents/skills/write-stable-tests/scripts/run-rust-tests-with-timing.mjs --manifest windows/tauri/src-tauri/Cargo.toml --keep-going:逐用例计时,变更用例最慢 17ms,失败项同上 5 个。prepare生成的-cp <classpath.jar> demo.Main,类路径含“示例 classes”目录、“lib dir/依赖 a#b%c.jar”以及 700 个不存在的 JAR;JVM 成功加载主类和依赖并输出预期内容,JAR 随后被删除。./scripts/verify-rust-core-comments.sh:通过./.agents/skills/write-stable-tests/scripts/verify-test-stability.sh:通过./scripts/verify-runtime-bundle-immutability.sh:通过./scripts/verify-windows-boundaries.sh:通过./scripts/verify-shared-contracts.sh:通过./scripts/verify-agent-notes.sh:通过./scripts/verify-platform-feature-matrix-change.sh origin/preview HEAD、./scripts/verify-platform-feature-matrix.sh:通过cargo fmt --check(两个 crate),以及cargo clippy --all-targets(lithe-core、src-tauri):新增代码无告警./scripts/verify-rust-core.sh:失败于scripts/test-rust-core-comments.mjs的 8 个用例,原因是本机沙箱中/usr/bin/ruby找不到uname,与本改动无关;其中的注释校验步骤单独运行通过。CI(Windows runner):
Test Windows Rust implementation首次运行失败,失败项是与本 PR 无关的lithe_core::tests::git::git_write_executes_checkout_preflight_clone_and_validation:它在默认 15s 预算下跑了 21047ms;重跑后同一用例 3058ms 通过,属于 runner 波动。重跑后该 job 通过,Windows 上 8 个classpath_jar用例和 6 个run::launch_arguments用例(含cfg(windows)的ClasspathStyle::Windows宿主路径与native_encoding_handles_chinese_and_rejects_lossy_paths)全部通过。本地 macOS 失败的 5 个既有 Windows 路径用例在 Windows CI 上也通过。未能验证:
pending。🤖 Generated with Claude Code