wyvrnpm 2.10.2 → 2.12.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (49) hide show
  1. package/README.md +1914 -1860
  2. package/bin/{wyvrn.js → wyvrnpm.js} +66 -0
  3. package/cmake/cpp.cmake +9 -9
  4. package/cmake/functions.cmake +224 -224
  5. package/cmake/macros.cmake +284 -284
  6. package/package.json +3 -2
  7. package/src/auth.js +66 -66
  8. package/src/binary-dir.js +95 -0
  9. package/src/bootstrap/cookbook.js +196 -196
  10. package/src/bootstrap/detect.js +150 -150
  11. package/src/bootstrap/index.js +220 -220
  12. package/src/bootstrap/version.js +72 -72
  13. package/src/build/cache.js +344 -344
  14. package/src/build/clone.js +170 -170
  15. package/src/build/cmake.js +342 -342
  16. package/src/build/index.js +299 -297
  17. package/src/build/msvc-env.js +260 -260
  18. package/src/build/recipe.js +188 -188
  19. package/src/commands/add.js +141 -141
  20. package/src/commands/bootstrap.js +96 -96
  21. package/src/commands/build.js +482 -452
  22. package/src/commands/cache.js +189 -189
  23. package/src/commands/clean.js +80 -80
  24. package/src/commands/configure.js +92 -92
  25. package/src/commands/init.js +70 -70
  26. package/src/commands/install-skill.js +115 -115
  27. package/src/commands/install.js +730 -674
  28. package/src/commands/link.js +320 -320
  29. package/src/commands/profile.js +237 -237
  30. package/src/commands/publish.js +584 -555
  31. package/src/commands/show.js +252 -252
  32. package/src/commands/version.js +187 -0
  33. package/src/compat.js +273 -273
  34. package/src/conf/index.js +415 -391
  35. package/src/conf/namespaces.js +94 -94
  36. package/src/context.js +230 -230
  37. package/src/http-fetch.js +53 -53
  38. package/src/ignore.js +118 -118
  39. package/src/logger.js +122 -122
  40. package/src/options.js +303 -303
  41. package/src/settings-overrides.js +152 -152
  42. package/src/toolchain/deps.js +164 -164
  43. package/src/toolchain/index.js +212 -212
  44. package/src/toolchain/presets.js +356 -340
  45. package/src/toolchain/template.cmake +77 -77
  46. package/src/upload-built.js +265 -265
  47. package/src/version-range.js +301 -301
  48. package/src/zip-safe.js +52 -52
  49. package/src/zip-stream.js +126 -0
@@ -1,260 +1,260 @@
1
- 'use strict';
2
-
3
- const fs = require('fs');
4
- const path = require('path');
5
- const { spawn } = require('child_process');
6
-
7
- const log = require('../logger');
8
-
9
- /**
10
- * Auto-activate the MSVC build environment (the moral equivalent of a
11
- * "Developer PowerShell for VS 2022") when a source-build on Windows picks
12
- * a non-VS generator and `cl.exe` isn't on PATH.
13
- *
14
- * Without this, Ninja/Make/etc. fail at CMake's compiler-detection step
15
- * because MSVC's `cl.exe` lives under the VS install tree and is only put
16
- * on PATH by `vcvarsall.bat`. VS generators sidestep this because MSBuild
17
- * locates MSVC via VS's own toolset resolution.
18
- *
19
- * Strategy:
20
- * 1. Check if cl.exe is already on PATH → return null (nothing to do).
21
- * 2. Locate the VS installation via `vswhere.exe -latest`.
22
- * 3. Run `vcvarsall.bat <arch>` in `cmd /s /c` and capture `set` output.
23
- * 4. Parse KEY=VALUE lines, return them as a shallow-merged env object
24
- * suitable for `child_process.spawn({ env })`.
25
- *
26
- * Returns `null` when nothing needs to be done OR when VS isn't found —
27
- * callers fall back to the ambient environment and CMake produces its own
28
- * error if that turns out to be missing a compiler.
29
- */
30
-
31
- const VSWHERE_DEFAULT = path.join(
32
- process.env['ProgramFiles(x86)'] || 'C:\\Program Files (x86)',
33
- 'Microsoft Visual Studio', 'Installer', 'vswhere.exe',
34
- );
35
-
36
- function tryCapture(cmd, args, opts = {}) {
37
- return new Promise((resolve) => {
38
- const proc = spawn(cmd, args, { stdio: ['ignore', 'pipe', 'ignore'], ...opts });
39
- let out = '';
40
- proc.stdout.on('data', (c) => { out += c.toString(); });
41
- proc.on('error', () => resolve(null));
42
- proc.on('close', (code) => resolve(code === 0 ? out : null));
43
- });
44
- }
45
-
46
- /**
47
- * @returns {Promise<boolean>}
48
- */
49
- async function isClOnPath() {
50
- if (process.platform !== 'win32') return false;
51
- // `where cl` is the Windows-native PATH probe.
52
- return new Promise((resolve) => {
53
- const proc = spawn('where', ['cl'], { stdio: 'ignore' });
54
- proc.on('error', () => resolve(false));
55
- proc.on('close', (code) => resolve(code === 0));
56
- });
57
- }
58
-
59
- /**
60
- * Return the latest VS installation path via vswhere, or null if not found.
61
- * @returns {Promise<string|null>}
62
- */
63
- async function findVsInstall() {
64
- if (!fs.existsSync(VSWHERE_DEFAULT)) return null;
65
- const out = await tryCapture(VSWHERE_DEFAULT, [
66
- '-latest',
67
- '-requires', 'Microsoft.VisualStudio.Component.VC.Tools.x86.x64',
68
- '-property', 'installationPath',
69
- ]);
70
- return out ? out.trim() : null;
71
- }
72
-
73
- function findVcvarsall(vsInstallPath) {
74
- const p = path.join(vsInstallPath, 'VC', 'Auxiliary', 'Build', 'vcvarsall.bat');
75
- return fs.existsSync(p) ? p : null;
76
- }
77
-
78
- /**
79
- * The complete set of arch arguments `vcvarsall.bat` accepts AND that we
80
- * are willing to splice into a shell command. Explicit allow-list — any
81
- * value outside this set is rejected (EVALUATION.md S5).
82
- */
83
- const VCVARSALL_ARCH_ALLOWLIST = new Set(['x64', 'x86', 'arm64', 'arm']);
84
-
85
- const ARCH_MAP = {
86
- x86_64: 'x64',
87
- x86: 'x86',
88
- armv8: 'arm64',
89
- armv7: 'arm',
90
- };
91
-
92
- /**
93
- * Map a wyvrnpm profile arch to the argument `vcvarsall.bat` expects.
94
- * Throws on an unknown profile arch — silently defaulting to `x64` would
95
- * (a) produce a build for the wrong target, and (b) hand any shell-unsafe
96
- * input to cmd.exe through captureVcvarsEnv's template.
97
- */
98
- function mapArch(profileArch) {
99
- const arg = ARCH_MAP[profileArch];
100
- if (!arg || !VCVARSALL_ARCH_ALLOWLIST.has(arg)) {
101
- throw new Error(
102
- `unsupported MSVC profile arch ${JSON.stringify(profileArch)}; ` +
103
- `expected one of ${Object.keys(ARCH_MAP).join(', ')}`,
104
- );
105
- }
106
- return arg;
107
- }
108
-
109
- /**
110
- * Capture the environment produced by running vcvarsall.bat.
111
- *
112
- * Strategy: shell out to `cmd`, run `vcvarsall.bat <arch>`, then print a
113
- * marker followed by the post-vcvars environment (`set` dumps KEY=VALUE
114
- * lines for every env var). Everything before the marker is vcvarsall's
115
- * own output; everything after is the env we want.
116
- *
117
- * Uses `shell: true` so Node hands the full command line to `cmd.exe`
118
- * verbatim — avoids the usual quoting pitfalls when the VS install path
119
- * contains spaces.
120
- *
121
- * @param {string} vcvarsallPath
122
- * @param {string} archArg
123
- * @returns {Promise<object|null>}
124
- */
125
- async function captureVcvarsEnv(vcvarsallPath, archArg) {
126
- // Defense-in-depth at the shell boundary (EVALUATION.md S5). mapArch
127
- // already restricts archArg to the allow-list; re-assert here so this
128
- // function is safe to call directly (e.g. from a future caller that
129
- // skips mapArch). Likewise refuse a vcvarsall path containing a
130
- // double-quote — it would break out of the quoted command template.
131
- if (!VCVARSALL_ARCH_ALLOWLIST.has(archArg)) {
132
- throw new Error(`refusing to spawn cmd.exe with unsafe arch arg ${JSON.stringify(archArg)}`);
133
- }
134
- if (vcvarsallPath.includes('"')) {
135
- throw new Error(`refusing to spawn cmd.exe with quote in vcvarsall path ${JSON.stringify(vcvarsallPath)}`);
136
- }
137
-
138
- const MARKER = '___WYVRN_ENV_MARKER___';
139
- // `call` is required so the batch file returns control instead of
140
- // `exit`-ing the cmd shell; otherwise the `echo` + `set` after it
141
- // never run.
142
- const cmdLine =
143
- `call "${vcvarsallPath}" ${archArg} ` +
144
- `&& echo ${MARKER} ` +
145
- `&& set`;
146
-
147
- return new Promise((resolve) => {
148
- const proc = spawn(cmdLine, {
149
- shell: true,
150
- stdio: ['ignore', 'pipe', 'pipe'],
151
- });
152
- let stdout = '';
153
- let stderr = '';
154
- proc.stdout.on('data', (c) => { stdout += c.toString(); });
155
- proc.stderr.on('data', (c) => { stderr += c.toString(); });
156
- proc.on('error', (err) => {
157
- log.warn(` vcvarsall spawn error: ${err.message}`);
158
- resolve(null);
159
- });
160
- proc.on('close', (code) => {
161
- if (code !== 0) {
162
- const tail = (stderr || stdout).trim().split(/\r?\n/).slice(-5).join(' | ');
163
- log.warn(` vcvarsall exited ${code}; last lines: ${tail}`);
164
- resolve(null);
165
- return;
166
- }
167
- const markerIdx = stdout.indexOf(MARKER);
168
- if (markerIdx === -1) {
169
- const tail = stdout.trim().split(/\r?\n/).slice(-5).join(' | ');
170
- log.warn(` vcvarsall marker not found; last stdout lines: ${tail}`);
171
- resolve(null);
172
- return;
173
- }
174
- // Everything after the marker line is the env dump.
175
- const envDump = stdout.slice(markerIdx + MARKER.length);
176
- const env = { ...process.env };
177
- let matched = 0;
178
- for (const line of envDump.split(/\r?\n/)) {
179
- const m = line.match(/^([A-Za-z_][A-Za-z0-9_()]*)=(.*)$/);
180
- if (m) { env[m[1]] = m[2]; matched += 1; }
181
- }
182
- if (matched < 5 || !env.VSINSTALLDIR) {
183
- log.warn(
184
- ` vcvarsall env dump looks incomplete ` +
185
- `(${matched} vars, VSINSTALLDIR=${env.VSINSTALLDIR ? 'set' : 'MISSING'})`,
186
- );
187
- resolve(null);
188
- return;
189
- }
190
- resolve(env);
191
- });
192
- });
193
- }
194
-
195
- /**
196
- * Compute a build-environment override for the given profile, or return
197
- * null to indicate "use the ambient environment". Callers pass the result
198
- * as `spawn(..., { env })`.
199
- *
200
- * Only activates when:
201
- * - Running on Windows
202
- * - Profile says compiler=msvc
203
- * - `cl.exe` is NOT already on PATH (so we're outside a Dev shell)
204
- * - `skipForGenerator` does not start with "Visual Studio" (VS generators
205
- * don't need this — MSBuild resolves MSVC itself)
206
- *
207
- * @param {{
208
- * profile: { compiler?: string, arch?: string },
209
- * skipForGenerator?: string|null,
210
- * }} args
211
- * @returns {Promise<object|null>}
212
- */
213
- async function loadMsvcEnv({ profile, skipForGenerator }) {
214
- if (process.platform !== 'win32') return null;
215
- if (!profile || profile.compiler !== 'msvc') return null;
216
- if (skipForGenerator && skipForGenerator.startsWith('Visual Studio')) return null;
217
- if (await isClOnPath()) return null;
218
-
219
- const vsPath = await findVsInstall();
220
- if (!vsPath) {
221
- log.warn(' MSVC profile requested but vswhere/VS not found — CMake will error if cl.exe is missing');
222
- return null;
223
- }
224
- const vcvarsall = findVcvarsall(vsPath);
225
- if (!vcvarsall) {
226
- log.warn(` MSVC profile requested but vcvarsall.bat not found under ${vsPath}`);
227
- return null;
228
- }
229
-
230
- let archArg;
231
- try {
232
- archArg = mapArch(profile.arch);
233
- } catch (err) {
234
- // Unknown arch for an MSVC profile is genuinely a configuration bug
235
- // (MSVC only ships toolsets for x64/x86/arm64/arm). Surface clearly
236
- // and fall through to the ambient env — CMake will then error with
237
- // a usable compiler-detection message instead of us silently
238
- // cross-targeting x64.
239
- log.warn(` ${err.message}`);
240
- return null;
241
- }
242
- log.info(` activating MSVC build env: vcvarsall.bat ${archArg} (${vsPath})`);
243
- const env = await captureVcvarsEnv(vcvarsall, archArg);
244
- if (!env) {
245
- log.warn(` vcvarsall.bat ${archArg} failed — CMake will error if cl.exe is missing`);
246
- return null;
247
- }
248
- return env;
249
- }
250
-
251
- module.exports = {
252
- loadMsvcEnv,
253
- // Exported for tests
254
- isClOnPath,
255
- findVsInstall,
256
- findVcvarsall,
257
- mapArch,
258
- captureVcvarsEnv,
259
- VCVARSALL_ARCH_ALLOWLIST,
260
- };
1
+ 'use strict';
2
+
3
+ const fs = require('fs');
4
+ const path = require('path');
5
+ const { spawn } = require('child_process');
6
+
7
+ const log = require('../logger');
8
+
9
+ /**
10
+ * Auto-activate the MSVC build environment (the moral equivalent of a
11
+ * "Developer PowerShell for VS 2022") when a source-build on Windows picks
12
+ * a non-VS generator and `cl.exe` isn't on PATH.
13
+ *
14
+ * Without this, Ninja/Make/etc. fail at CMake's compiler-detection step
15
+ * because MSVC's `cl.exe` lives under the VS install tree and is only put
16
+ * on PATH by `vcvarsall.bat`. VS generators sidestep this because MSBuild
17
+ * locates MSVC via VS's own toolset resolution.
18
+ *
19
+ * Strategy:
20
+ * 1. Check if cl.exe is already on PATH → return null (nothing to do).
21
+ * 2. Locate the VS installation via `vswhere.exe -latest`.
22
+ * 3. Run `vcvarsall.bat <arch>` in `cmd /s /c` and capture `set` output.
23
+ * 4. Parse KEY=VALUE lines, return them as a shallow-merged env object
24
+ * suitable for `child_process.spawn({ env })`.
25
+ *
26
+ * Returns `null` when nothing needs to be done OR when VS isn't found —
27
+ * callers fall back to the ambient environment and CMake produces its own
28
+ * error if that turns out to be missing a compiler.
29
+ */
30
+
31
+ const VSWHERE_DEFAULT = path.join(
32
+ process.env['ProgramFiles(x86)'] || 'C:\\Program Files (x86)',
33
+ 'Microsoft Visual Studio', 'Installer', 'vswhere.exe',
34
+ );
35
+
36
+ function tryCapture(cmd, args, opts = {}) {
37
+ return new Promise((resolve) => {
38
+ const proc = spawn(cmd, args, { stdio: ['ignore', 'pipe', 'ignore'], ...opts });
39
+ let out = '';
40
+ proc.stdout.on('data', (c) => { out += c.toString(); });
41
+ proc.on('error', () => resolve(null));
42
+ proc.on('close', (code) => resolve(code === 0 ? out : null));
43
+ });
44
+ }
45
+
46
+ /**
47
+ * @returns {Promise<boolean>}
48
+ */
49
+ async function isClOnPath() {
50
+ if (process.platform !== 'win32') return false;
51
+ // `where cl` is the Windows-native PATH probe.
52
+ return new Promise((resolve) => {
53
+ const proc = spawn('where', ['cl'], { stdio: 'ignore' });
54
+ proc.on('error', () => resolve(false));
55
+ proc.on('close', (code) => resolve(code === 0));
56
+ });
57
+ }
58
+
59
+ /**
60
+ * Return the latest VS installation path via vswhere, or null if not found.
61
+ * @returns {Promise<string|null>}
62
+ */
63
+ async function findVsInstall() {
64
+ if (!fs.existsSync(VSWHERE_DEFAULT)) return null;
65
+ const out = await tryCapture(VSWHERE_DEFAULT, [
66
+ '-latest',
67
+ '-requires', 'Microsoft.VisualStudio.Component.VC.Tools.x86.x64',
68
+ '-property', 'installationPath',
69
+ ]);
70
+ return out ? out.trim() : null;
71
+ }
72
+
73
+ function findVcvarsall(vsInstallPath) {
74
+ const p = path.join(vsInstallPath, 'VC', 'Auxiliary', 'Build', 'vcvarsall.bat');
75
+ return fs.existsSync(p) ? p : null;
76
+ }
77
+
78
+ /**
79
+ * The complete set of arch arguments `vcvarsall.bat` accepts AND that we
80
+ * are willing to splice into a shell command. Explicit allow-list — any
81
+ * value outside this set is rejected (EVALUATION.md S5).
82
+ */
83
+ const VCVARSALL_ARCH_ALLOWLIST = new Set(['x64', 'x86', 'arm64', 'arm']);
84
+
85
+ const ARCH_MAP = {
86
+ x86_64: 'x64',
87
+ x86: 'x86',
88
+ armv8: 'arm64',
89
+ armv7: 'arm',
90
+ };
91
+
92
+ /**
93
+ * Map a wyvrnpm profile arch to the argument `vcvarsall.bat` expects.
94
+ * Throws on an unknown profile arch — silently defaulting to `x64` would
95
+ * (a) produce a build for the wrong target, and (b) hand any shell-unsafe
96
+ * input to cmd.exe through captureVcvarsEnv's template.
97
+ */
98
+ function mapArch(profileArch) {
99
+ const arg = ARCH_MAP[profileArch];
100
+ if (!arg || !VCVARSALL_ARCH_ALLOWLIST.has(arg)) {
101
+ throw new Error(
102
+ `unsupported MSVC profile arch ${JSON.stringify(profileArch)}; ` +
103
+ `expected one of ${Object.keys(ARCH_MAP).join(', ')}`,
104
+ );
105
+ }
106
+ return arg;
107
+ }
108
+
109
+ /**
110
+ * Capture the environment produced by running vcvarsall.bat.
111
+ *
112
+ * Strategy: shell out to `cmd`, run `vcvarsall.bat <arch>`, then print a
113
+ * marker followed by the post-vcvars environment (`set` dumps KEY=VALUE
114
+ * lines for every env var). Everything before the marker is vcvarsall's
115
+ * own output; everything after is the env we want.
116
+ *
117
+ * Uses `shell: true` so Node hands the full command line to `cmd.exe`
118
+ * verbatim — avoids the usual quoting pitfalls when the VS install path
119
+ * contains spaces.
120
+ *
121
+ * @param {string} vcvarsallPath
122
+ * @param {string} archArg
123
+ * @returns {Promise<object|null>}
124
+ */
125
+ async function captureVcvarsEnv(vcvarsallPath, archArg) {
126
+ // Defense-in-depth at the shell boundary (EVALUATION.md S5). mapArch
127
+ // already restricts archArg to the allow-list; re-assert here so this
128
+ // function is safe to call directly (e.g. from a future caller that
129
+ // skips mapArch). Likewise refuse a vcvarsall path containing a
130
+ // double-quote — it would break out of the quoted command template.
131
+ if (!VCVARSALL_ARCH_ALLOWLIST.has(archArg)) {
132
+ throw new Error(`refusing to spawn cmd.exe with unsafe arch arg ${JSON.stringify(archArg)}`);
133
+ }
134
+ if (vcvarsallPath.includes('"')) {
135
+ throw new Error(`refusing to spawn cmd.exe with quote in vcvarsall path ${JSON.stringify(vcvarsallPath)}`);
136
+ }
137
+
138
+ const MARKER = '___WYVRN_ENV_MARKER___';
139
+ // `call` is required so the batch file returns control instead of
140
+ // `exit`-ing the cmd shell; otherwise the `echo` + `set` after it
141
+ // never run.
142
+ const cmdLine =
143
+ `call "${vcvarsallPath}" ${archArg} ` +
144
+ `&& echo ${MARKER} ` +
145
+ `&& set`;
146
+
147
+ return new Promise((resolve) => {
148
+ const proc = spawn(cmdLine, {
149
+ shell: true,
150
+ stdio: ['ignore', 'pipe', 'pipe'],
151
+ });
152
+ let stdout = '';
153
+ let stderr = '';
154
+ proc.stdout.on('data', (c) => { stdout += c.toString(); });
155
+ proc.stderr.on('data', (c) => { stderr += c.toString(); });
156
+ proc.on('error', (err) => {
157
+ log.warn(` vcvarsall spawn error: ${err.message}`);
158
+ resolve(null);
159
+ });
160
+ proc.on('close', (code) => {
161
+ if (code !== 0) {
162
+ const tail = (stderr || stdout).trim().split(/\r?\n/).slice(-5).join(' | ');
163
+ log.warn(` vcvarsall exited ${code}; last lines: ${tail}`);
164
+ resolve(null);
165
+ return;
166
+ }
167
+ const markerIdx = stdout.indexOf(MARKER);
168
+ if (markerIdx === -1) {
169
+ const tail = stdout.trim().split(/\r?\n/).slice(-5).join(' | ');
170
+ log.warn(` vcvarsall marker not found; last stdout lines: ${tail}`);
171
+ resolve(null);
172
+ return;
173
+ }
174
+ // Everything after the marker line is the env dump.
175
+ const envDump = stdout.slice(markerIdx + MARKER.length);
176
+ const env = { ...process.env };
177
+ let matched = 0;
178
+ for (const line of envDump.split(/\r?\n/)) {
179
+ const m = line.match(/^([A-Za-z_][A-Za-z0-9_()]*)=(.*)$/);
180
+ if (m) { env[m[1]] = m[2]; matched += 1; }
181
+ }
182
+ if (matched < 5 || !env.VSINSTALLDIR) {
183
+ log.warn(
184
+ ` vcvarsall env dump looks incomplete ` +
185
+ `(${matched} vars, VSINSTALLDIR=${env.VSINSTALLDIR ? 'set' : 'MISSING'})`,
186
+ );
187
+ resolve(null);
188
+ return;
189
+ }
190
+ resolve(env);
191
+ });
192
+ });
193
+ }
194
+
195
+ /**
196
+ * Compute a build-environment override for the given profile, or return
197
+ * null to indicate "use the ambient environment". Callers pass the result
198
+ * as `spawn(..., { env })`.
199
+ *
200
+ * Only activates when:
201
+ * - Running on Windows
202
+ * - Profile says compiler=msvc
203
+ * - `cl.exe` is NOT already on PATH (so we're outside a Dev shell)
204
+ * - `skipForGenerator` does not start with "Visual Studio" (VS generators
205
+ * don't need this — MSBuild resolves MSVC itself)
206
+ *
207
+ * @param {{
208
+ * profile: { compiler?: string, arch?: string },
209
+ * skipForGenerator?: string|null,
210
+ * }} args
211
+ * @returns {Promise<object|null>}
212
+ */
213
+ async function loadMsvcEnv({ profile, skipForGenerator }) {
214
+ if (process.platform !== 'win32') return null;
215
+ if (!profile || profile.compiler !== 'msvc') return null;
216
+ if (skipForGenerator && skipForGenerator.startsWith('Visual Studio')) return null;
217
+ if (await isClOnPath()) return null;
218
+
219
+ const vsPath = await findVsInstall();
220
+ if (!vsPath) {
221
+ log.warn(' MSVC profile requested but vswhere/VS not found — CMake will error if cl.exe is missing');
222
+ return null;
223
+ }
224
+ const vcvarsall = findVcvarsall(vsPath);
225
+ if (!vcvarsall) {
226
+ log.warn(` MSVC profile requested but vcvarsall.bat not found under ${vsPath}`);
227
+ return null;
228
+ }
229
+
230
+ let archArg;
231
+ try {
232
+ archArg = mapArch(profile.arch);
233
+ } catch (err) {
234
+ // Unknown arch for an MSVC profile is genuinely a configuration bug
235
+ // (MSVC only ships toolsets for x64/x86/arm64/arm). Surface clearly
236
+ // and fall through to the ambient env — CMake will then error with
237
+ // a usable compiler-detection message instead of us silently
238
+ // cross-targeting x64.
239
+ log.warn(` ${err.message}`);
240
+ return null;
241
+ }
242
+ log.info(` activating MSVC build env: vcvarsall.bat ${archArg} (${vsPath})`);
243
+ const env = await captureVcvarsEnv(vcvarsall, archArg);
244
+ if (!env) {
245
+ log.warn(` vcvarsall.bat ${archArg} failed — CMake will error if cl.exe is missing`);
246
+ return null;
247
+ }
248
+ return env;
249
+ }
250
+
251
+ module.exports = {
252
+ loadMsvcEnv,
253
+ // Exported for tests
254
+ isClOnPath,
255
+ findVsInstall,
256
+ findVcvarsall,
257
+ mapArch,
258
+ captureVcvarsEnv,
259
+ VCVARSALL_ARCH_ALLOWLIST,
260
+ };