Skip to content

Commit c2f5507

Browse files
committed
fix: PR #11 打磨——mcpp self env 加超时保护、回退探测支持注入、测试平台可移植、补 CHANGELOG
1 parent b11ce64 commit c2f5507

3 files changed

Lines changed: 59 additions & 14 deletions

File tree

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
# 更新日志
22

3+
## 0.2.7
4+
5+
- 修复「一键配置模块代码提示」在标准 mcpp 安装(install.sh / AUR)下无法发现 mcpp 内置
6+
xlings 的问题:xlings 发现以 `mcpp self env` 为权威来源(项目级契约),路径探测仅作回退;
7+
`mcpp self env` 调用增加超时保护,并补齐测试(PR #11)。
8+
39
## 0.2.6
410

511
- 新增 **mcpp: 一键配置模块代码提示** 向导:按「安装/切换工具链 → 构建 → 重载 → clangd

src/llvmTools.ts

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -142,17 +142,26 @@ const XLINGS_BINARY_LINE = /^\s*xlings binary\s*=\s*(.+?)\s*$/im;
142142
// environment; it owns its tool paths). Works for install.sh, AUR and any
143143
// custom MCPP_PREFIX layout. Falls back to the historical path heuristics for
144144
// standalone ~/.xlings installs and for mcpp versions without the line.
145+
//
146+
// The subprocess is bounded by MCPP_SELF_ENV_TIMEOUT_MS: the wizard reaches
147+
// this step only after mcpp is initialized (toolchain list / build already
148+
// ran), so 60s is generous while still guarding against an extreme hang.
149+
const MCPP_SELF_ENV_TIMEOUT_MS = 60_000;
150+
145151
export async function resolveXlingsExecutable(
146152
mcppExecutable: string,
147153
runner: ProcessRunner = runProcess,
154+
options?: FindXlingsOptions,
148155
): Promise<string | undefined> {
149-
const result = await runner(mcppExecutable, ["self", "env"]);
156+
const result = await runner(mcppExecutable, ["self", "env"], undefined, {
157+
timeoutMs: MCPP_SELF_ENV_TIMEOUT_MS,
158+
});
150159
const match = `${result.stdout}\n${result.stderr}`.match(XLINGS_BINARY_LINE);
151160
const reported = match?.[1]?.trim();
152161
if (reported !== undefined && reported.length > 0 && existsSync(reported)) {
153162
return reported;
154163
}
155-
return findXlingsExecutable();
164+
return findXlingsExecutable(options);
156165
}
157166

158167
export async function runXlingsCommand(

test/llvmTools.test.ts

Lines changed: 42 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,9 @@ import {
1313
resolveXlingsExecutable,
1414
} from "../src/llvmTools";
1515

16+
// `xlings` on POSIX, `xlings.exe` on Windows — mirrors mcpp's exe_suffix.
17+
const xlingsBinaryName = process.platform === "win32" ? "xlings.exe" : "xlings";
18+
1619
test("extracts version string from ToolIdentity", () => {
1720
assert.equal(
1821
llvmToolsVersionSpec({ major: 22, minor: 1, patch: 8, revision: "abc1234" }),
@@ -76,7 +79,7 @@ test("findXlingsExecutable returns a string or undefined", () => {
7679
test("findXlingsExecutable finds the xlings bundled in $MCPP_HOME/registry/bin", () => {
7780
const home = mkdtempSync(path.join(os.tmpdir(), "mcpp-vscode-mcpp-home-"));
7881
const registryBin = path.join(home, "registry", "bin");
79-
const xlingsPath = path.join(registryBin, "xlings");
82+
const xlingsPath = path.join(registryBin, xlingsBinaryName);
8083
mkdirSync(registryBin, { recursive: true });
8184
writeFileSync(xlingsPath, "#!/bin/sh\n");
8285
try {
@@ -92,7 +95,7 @@ test("findXlingsExecutable finds the xlings bundled in $MCPP_HOME/registry/bin",
9295
test("findXlingsExecutable falls back to $HOME/.mcpp/registry/bin when MCPP_HOME is unset", () => {
9396
const home = mkdtempSync(path.join(os.tmpdir(), "mcpp-vscode-home-"));
9497
const registryBin = path.join(home, ".mcpp", "registry", "bin");
95-
const xlingsPath = path.join(registryBin, "xlings");
98+
const xlingsPath = path.join(registryBin, xlingsBinaryName);
9699
mkdirSync(registryBin, { recursive: true });
97100
writeFileSync(xlingsPath, "#!/bin/sh\n");
98101
try {
@@ -107,7 +110,7 @@ test("findXlingsExecutable falls back to $HOME/.mcpp/registry/bin when MCPP_HOME
107110

108111
test("findXlingsExecutable honors MCPP_VENDORED_XLINGS", () => {
109112
const root = mkdtempSync(path.join(os.tmpdir(), "mcpp-vscode-vendored-"));
110-
const vendored = path.join(root, "opt-mcpp", "registry", "bin", "xlings");
113+
const vendored = path.join(root, "opt-mcpp", "registry", "bin", xlingsBinaryName);
111114
mkdirSync(path.dirname(vendored), { recursive: true });
112115
writeFileSync(vendored, "#!/bin/sh\n");
113116
try {
@@ -122,7 +125,7 @@ test("findXlingsExecutable honors MCPP_VENDORED_XLINGS", () => {
122125

123126
test("resolveXlingsExecutable reads the xlings binary from `mcpp self env`", async () => {
124127
const root = mkdtempSync(path.join(os.tmpdir(), "mcpp-vscode-selfenv-"));
125-
const xlingsPath = path.join(root, "registry", "bin", "xlings");
128+
const xlingsPath = path.join(root, "registry", "bin", xlingsBinaryName);
126129
mkdirSync(path.dirname(xlingsPath), { recursive: true });
127130
writeFileSync(xlingsPath, "#!/bin/sh\n");
128131
const runner = async () => ({
@@ -140,20 +143,47 @@ test("resolveXlingsExecutable reads the xlings binary from `mcpp self env`", asy
140143
}
141144
});
142145

143-
test("resolveXlingsExecutable falls back when the reported path does not exist", async () => {
146+
test("resolveXlingsExecutable passes a timeout to `mcpp self env`", async () => {
147+
let captured: { timeoutMs?: number } | undefined;
148+
const runner = async (
149+
_executable: string,
150+
_args: string[],
151+
_cwd?: string,
152+
options?: { timeoutMs?: number },
153+
) => {
154+
captured = options;
155+
return { exitCode: 0, stdout: "", stderr: "" };
156+
};
157+
await resolveXlingsExecutable("/tools/mcpp", runner);
158+
assert.equal(captured?.timeoutMs, 60_000);
159+
});
160+
161+
test("resolveXlingsExecutable falls back to path probing when the reported path does not exist", async () => {
162+
const home = mkdtempSync(path.join(os.tmpdir(), "mcpp-vscode-fallback-missing-"));
144163
const runner = async () => ({
145164
exitCode: 0,
146165
stdout: "xlings binary = /no/such/xlings\n",
147166
stderr: "",
148167
});
149-
const result = await resolveXlingsExecutable("/tools/mcpp", runner);
150-
// Fallback heuristics find nothing in this environment, so the result is
151-
// undefined unless a standalone ~/.xlings or PATH xlings happens to exist.
152-
assert.ok(result === undefined || typeof result === "string");
168+
try {
169+
assert.equal(
170+
await resolveXlingsExecutable("/tools/mcpp", runner, { home, env: {} }),
171+
undefined,
172+
);
173+
} finally {
174+
rmSync(home, { recursive: true, force: true });
175+
}
153176
});
154177

155-
test("resolveXlingsExecutable falls back when `mcpp self env` fails", async () => {
178+
test("resolveXlingsExecutable falls back to path probing when `mcpp self env` fails", async () => {
179+
const home = mkdtempSync(path.join(os.tmpdir(), "mcpp-vscode-fallback-fail-"));
156180
const runner = async () => ({ exitCode: 1, stdout: "", stderr: "boom\n" });
157-
const result = await resolveXlingsExecutable("/tools/mcpp", runner);
158-
assert.ok(result === undefined || typeof result === "string");
181+
try {
182+
assert.equal(
183+
await resolveXlingsExecutable("/tools/mcpp", runner, { home, env: {} }),
184+
undefined,
185+
);
186+
} finally {
187+
rmSync(home, { recursive: true, force: true });
188+
}
159189
});

0 commit comments

Comments
 (0)