Repository navigation
Description
要解决的问题
Windows 的 CreateProcess 命令行上限是 32,767 个 UTF-16 码元。依赖多的 Java 项目,classpath 很容易超过这个长度,java 会启动失败。
这件事以前由宿主兜底:
- 桌面端
desktop/internal/pluginservice/runs.go在处理每个非调试的run/start时,调用 Core 的execution.planLaunchCommand。 - 如果启动的是 JDK 9 及以上的
java,并且命令行超长,Core 会把-cp/-classpath/--class-path/-p/--module-path的值移进@argfile。 - 桌面端再按系统 ANSI 代码页写出这个文件(
argfile_encoding_windows.go)。
Core 必须对 Java 零依赖、零特判,所以 Lithe-IDEA/Lithe-core-rust 分支 refactor/core-remove-java-special-cases 已经删除了 execution.planLaunchCommand。桌面端的 launch_plan.go、runargs.go、argfile_encoding_*.go 也会随之删除。之后宿主会原样启动插件给的命令行。
backend/src/run.rs:34 的说明是 “Long command lines are passed as they are”。这句话在 macOS 上成立,但在 Windows 上,长 classpath 的运行和测试会直接失败。
建议方案
由插件后端自己生成参数文件,交给 run/start 的 temporaryFiles。Core 在同一个分支里给 temporaryFiles 增加了 {name, base64} 形式:宿主按给定字节原样写出,不做转码。规则如下:
- 每项要么是
{name, contents}(UTF-8 文本),要么是{name, base64}(字节)。同时给出两者或两者都不给,都会被拒绝。 - 最多 4 个文件,每个不超过 4 MiB。
- 参数里写
${temporaryFile:<name>},宿主会替换成实际路径。
插件里已经有可以直接用的部分(origin/HEAD 28628cb):
crates/java-domain/src/execution/launch_command.rs的plan_launch_command:从 Core 移植过来,负责判断是否需要参数文件、把哪些选项移进文件,以及如何加引号。它已经作为同名插件函数execution.planLaunchCommand注册在backend/src/lib.rs里。crates/java-domain/src/execution/argfile.rs:读取 JDK 的release文件得到主版本号,并写出参数文件。crates/java-domain/src/execution/classpath_jar.rs:给 JDK 8 用的、只含清单的 classpath JAR。
缺的是两件事:
- 运行和测试流程(
backend/src/run.rs)没有调用上面这些代码; - 没有按 Windows 代码页编码参数文件。
插件需要:
- 在
backend/src/run.rs生成run/start请求之前调用plan_launch_command。命令行超过 Windows 上限时,把生成的文件放进temporaryFiles,参数里写${temporaryFile:<name>},不再自己选目录写文件。插件现在还没有自己的临时目录,见argfile.rs顶部的TODO(host-api: API-20 storage)。 - Windows 上按系统 ANSI 代码页编码参数文件,规则是:调用
GetACP取得代码页,再用WideCharToMultiByte并带上WC_NO_BEST_FIT_CHARS转换;代码页里放不下的字符直接报错,不做替换;双字节字符的尾字节如果是0x5C(\),要再写一个0x5C;代码页是 65001 时直接写 UTF-8。编码结果以{name, base64}交给宿主。可以参考将被删除的desktop/internal/pluginservice/argfile_encoding_windows.go。 - JDK 8 的 classpath JAR 也可以用
{name, base64}交给宿主,单个文件不能超过 4 MiB。 - macOS 和 Linux 上,命令行没有超长时维持现状。如果需要参数文件,用 UTF-8 的
{name, contents}。 - 同步修改
backend/src/run.rs:34的 “Not yet covered” 说明和 README 的日志表。表里的execution.planLaunchCommand是插件自己的函数,不是 Core 命令。 - 测试至少覆盖:
- 超长 classpath 会生成参数文件;
- 未超长时不生成;
- JDK 8 走 classpath JAR;
- 非 ASCII 路径;
- 双字节代码页下的尾字节加倍。
迁移顺序
- 合并顺序是:Core PR 先合入;桌面端删除兜底、并支持写出
base64临时文件的 PR 再合入;最后本插件发布支持 argfile 的版本。 - 在本插件发版之前,Windows 上的长 classpath 运行会失败。目前 Viva 桌面端在 Windows 上仍处于迁移验证阶段。
其他
Core 的模板值 ${field.<runtime>.major} 现在取版本号的第一个数字分量:1.8.0_412 得到 1,17.0.2 得到 17。本插件的 ${field.jdk.major} 只用在 minimumVersion: 17 的 JDK 字段上,不受影响。如果以后要支持 JDK 8 的选择,需要在插件里自己换算 1.x。
Activity
xiaoyumuxi commentedon Nov 13, 2026
宿主侧的两个 PR 已开(Draft):
execution.planLaunchCommand,run/start的temporaryFiles增加{name, base64},契约 fixture 已覆盖 base64 的拒绝用例。合并顺序:Core #23 → 桌面端 #1859 → 本插件发布 argfile 版本。插件发版前,Windows 上长 classpath 的运行会失败。