wyvrnpm 2.20.0 → 2.20.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.
- package/package.json +1 -1
- package/src/build/cmake-exe.js +72 -0
- package/src/build/cmake.js +2 -1
- package/src/build/default-generator.js +129 -124
- package/src/commands/build.js +65 -26
- package/src/context.js +1 -1
- package/src/toolchain/presets.js +31 -0
package/package.json
CHANGED
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const fs = require('fs');
|
|
4
|
+
const path = require('path');
|
|
5
|
+
|
|
6
|
+
// Default CMake install location on Windows (the Kitware MSI default). When
|
|
7
|
+
// CMake isn't on PATH — common in stripped CI agents or a fresh dev box where
|
|
8
|
+
// the installer's "add to PATH" box was unchecked — fall back to this so
|
|
9
|
+
// install/build/source-build still work instead of failing with "is CMake on
|
|
10
|
+
// PATH?".
|
|
11
|
+
const WINDOWS_DEFAULT_CMAKE = 'C:\\Program Files\\CMake\\bin\\cmake.exe';
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Decide which `cmake` command to spawn. Pure — all environment inputs are
|
|
15
|
+
* injected, so it's testable across platforms.
|
|
16
|
+
*
|
|
17
|
+
* 1. cmake found on PATH → `'cmake'` (let spawn / PATH resolve it; preserves
|
|
18
|
+
* the pre-fix behaviour and respects a user's chosen cmake).
|
|
19
|
+
* 2. else, on Windows, the default install location exists → its absolute
|
|
20
|
+
* path.
|
|
21
|
+
* 3. else → `'cmake'` so the caller's spawn fails with the normal
|
|
22
|
+
* "is CMake installed and on PATH?" error rather than a confusing ENOENT
|
|
23
|
+
* on a hard-coded path.
|
|
24
|
+
*
|
|
25
|
+
* @param {{ platform: string, pathValue: string|undefined,
|
|
26
|
+
* exists: (p: string) => boolean }} args
|
|
27
|
+
* @returns {string}
|
|
28
|
+
*/
|
|
29
|
+
function pickCmakeExe({ platform, pathValue, exists }) {
|
|
30
|
+
const isWin = platform === 'win32';
|
|
31
|
+
const pathLib = isWin ? path.win32 : path.posix;
|
|
32
|
+
const delim = isWin ? ';' : ':';
|
|
33
|
+
// .bat/.cmd cover shim installs (e.g. via package managers); .exe is the
|
|
34
|
+
// stock installer.
|
|
35
|
+
const names = isWin ? ['cmake.exe', 'cmake.bat', 'cmake.cmd'] : ['cmake'];
|
|
36
|
+
|
|
37
|
+
for (const dir of (pathValue || '').split(delim).filter(Boolean)) {
|
|
38
|
+
for (const name of names) {
|
|
39
|
+
if (exists(pathLib.join(dir, name))) return 'cmake';
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
if (isWin && exists(WINDOWS_DEFAULT_CMAKE)) return WINDOWS_DEFAULT_CMAKE;
|
|
44
|
+
return 'cmake';
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
let cached; // undefined = unprobed; string = resolved command (per-process)
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Resolve the cmake executable to spawn, memoised per-process (PATH and the
|
|
51
|
+
* install location don't change mid-run). Returns either `'cmake'` (on PATH /
|
|
52
|
+
* give-up) or an absolute path to the Windows-default cmake.exe.
|
|
53
|
+
*
|
|
54
|
+
* @returns {string}
|
|
55
|
+
*/
|
|
56
|
+
function resolveCmakeExe() {
|
|
57
|
+
if (cached === undefined) {
|
|
58
|
+
cached = pickCmakeExe({
|
|
59
|
+
platform: process.platform,
|
|
60
|
+
pathValue: process.env.PATH,
|
|
61
|
+
exists: fs.existsSync,
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
return cached;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
module.exports = {
|
|
68
|
+
resolveCmakeExe,
|
|
69
|
+
WINDOWS_DEFAULT_CMAKE,
|
|
70
|
+
// Exported for unit tests
|
|
71
|
+
pickCmakeExe,
|
|
72
|
+
};
|
package/src/build/cmake.js
CHANGED
|
@@ -7,6 +7,7 @@ const { spawn } = require('child_process');
|
|
|
7
7
|
const { loadMsvcEnv } = require('./msvc-env');
|
|
8
8
|
const { archToVsPlatform, isVisualStudioGenerator } = require('../toolchain');
|
|
9
9
|
const { detectDefaultGenerator } = require('./default-generator');
|
|
10
|
+
const { resolveCmakeExe } = require('./cmake-exe');
|
|
10
11
|
const log = require('../logger');
|
|
11
12
|
|
|
12
13
|
/**
|
|
@@ -144,7 +145,7 @@ async function pickGenerator(candidates) {
|
|
|
144
145
|
*/
|
|
145
146
|
function runCmake(args, opts = {}) {
|
|
146
147
|
return new Promise((resolve, reject) => {
|
|
147
|
-
const proc = spawn(
|
|
148
|
+
const proc = spawn(resolveCmakeExe(), args, {
|
|
148
149
|
stdio: 'inherit',
|
|
149
150
|
cwd: opts.cwd,
|
|
150
151
|
env: opts.env || process.env,
|
|
@@ -1,124 +1,129 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
const { spawn } = require('child_process');
|
|
4
|
-
|
|
5
|
-
const { isVisualStudioGenerator } = require('../toolchain');
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
* @
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
*
|
|
37
|
-
*
|
|
38
|
-
*
|
|
39
|
-
*
|
|
40
|
-
*
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
//
|
|
50
|
-
proc
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
*
|
|
69
|
-
*
|
|
70
|
-
*
|
|
71
|
-
*
|
|
72
|
-
*
|
|
73
|
-
*
|
|
74
|
-
*
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
*
|
|
101
|
-
*
|
|
102
|
-
*
|
|
103
|
-
*
|
|
104
|
-
*
|
|
105
|
-
*
|
|
106
|
-
*
|
|
107
|
-
*
|
|
108
|
-
*
|
|
109
|
-
*
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const { spawn } = require('child_process');
|
|
4
|
+
|
|
5
|
+
const { isVisualStudioGenerator } = require('../toolchain');
|
|
6
|
+
const { resolveCmakeExe } = require('./cmake-exe');
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Parse `cmake --help` output for the platform's default generator. CMake
|
|
10
|
+
* marks the default with a leading `* ` in the "Generators" section, e.g.:
|
|
11
|
+
*
|
|
12
|
+
* The following generators are available on this platform (* marks default):
|
|
13
|
+
* * Visual Studio 17 2022 [arch] = Generates Visual Studio 2022 project files.
|
|
14
|
+
* Visual Studio 16 2019 [arch] = Generates Visual Studio 2019 project files.
|
|
15
|
+
* Ninja = Generates build.ninja files.
|
|
16
|
+
*
|
|
17
|
+
* Returns the default generator's name verbatim (with any trailing ` [arch]`
|
|
18
|
+
* annotation stripped), or null when no default marker is present. The name is
|
|
19
|
+
* returned as-is — deciding whether it's a Visual Studio generator is the
|
|
20
|
+
* caller's job.
|
|
21
|
+
*
|
|
22
|
+
* @param {string} helpText
|
|
23
|
+
* @returns {string|null}
|
|
24
|
+
*/
|
|
25
|
+
function parseDefaultGenerator(helpText) {
|
|
26
|
+
if (typeof helpText !== 'string') return null;
|
|
27
|
+
for (const line of helpText.split(/\r?\n/)) {
|
|
28
|
+
const m = line.match(/^\*\s+(.+?)\s*=/);
|
|
29
|
+
if (!m) continue;
|
|
30
|
+
return m[1].replace(/\s*\[arch\]\s*$/, '').trim();
|
|
31
|
+
}
|
|
32
|
+
return null;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Spawn `cmake --help` and resolve its stdout. Resolves null (never rejects)
|
|
37
|
+
* when cmake is absent from PATH or exits non-zero — generator detection is
|
|
38
|
+
* best-effort, and an unavailable cmake simply means the caller falls back to
|
|
39
|
+
* leaving the generator unpinned, exactly as before this feature.
|
|
40
|
+
*
|
|
41
|
+
* @returns {Promise<string|null>}
|
|
42
|
+
*/
|
|
43
|
+
function runCmakeHelp() {
|
|
44
|
+
return new Promise((resolve) => {
|
|
45
|
+
let out = '';
|
|
46
|
+
const cmakeExe = resolveCmakeExe();
|
|
47
|
+
// shell:true (Windows) lets a bare `cmake` resolve .bat/.cmd shims on PATH.
|
|
48
|
+
// But a resolved absolute fallback path contains spaces ("Program Files"),
|
|
49
|
+
// which a shell would word-split — spawn it directly without a shell.
|
|
50
|
+
const proc = spawn(cmakeExe, ['--help'], {
|
|
51
|
+
shell: process.platform === 'win32' && cmakeExe === 'cmake',
|
|
52
|
+
});
|
|
53
|
+
proc.stdout.on('data', (chunk) => { out += chunk.toString(); });
|
|
54
|
+
// cmake not on PATH → detection unavailable; recoverable, fall back to null.
|
|
55
|
+
proc.on('error', () => resolve(null));
|
|
56
|
+
proc.on('close', (code) => resolve(code === 0 ? out : null));
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// Per-process memo of the production (no-injection) probe result. The default
|
|
61
|
+
// generator can't change mid-run, so a single `cmake --help` spawn is enough
|
|
62
|
+
// for the whole process — important when one process drives many installs
|
|
63
|
+
// (e.g. a source-build that resolves several deps, or the test suite). Calls
|
|
64
|
+
// that inject `platform`/`runHelp` (tests) bypass this cache to stay hermetic.
|
|
65
|
+
let cachedDefault; // undefined = unprobed; string|null = probed result
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Detect the Visual Studio generator CMake would use by default on this
|
|
69
|
+
* machine. On Windows with VS installed, CMake's implicit default is the
|
|
70
|
+
* newest installed VS — which builds host x64 unless `-A`/architecture is
|
|
71
|
+
* pinned. Returns that VS generator name so callers can pin it (and the
|
|
72
|
+
* matching `-A`), or null when the default is not a VS generator
|
|
73
|
+
* (non-Windows, or VS not installed → today's behaviour, no pin).
|
|
74
|
+
*
|
|
75
|
+
* `platform` and `runHelp` are injectable for tests; the defaults probe the
|
|
76
|
+
* real machine and memoise the result per-process.
|
|
77
|
+
*
|
|
78
|
+
* @param {{ platform?: string, runHelp?: () => Promise<string|null> }} [opts]
|
|
79
|
+
* @returns {Promise<string|null>}
|
|
80
|
+
*/
|
|
81
|
+
async function detectDefaultGenerator({ platform, runHelp } = {}) {
|
|
82
|
+
const injected = platform !== undefined || runHelp !== undefined;
|
|
83
|
+
if (!injected && cachedDefault !== undefined) return cachedDefault;
|
|
84
|
+
|
|
85
|
+
const plat = platform ?? process.platform;
|
|
86
|
+
let result = null;
|
|
87
|
+
if (plat === 'win32') {
|
|
88
|
+
const helpText = await (runHelp ?? runCmakeHelp)();
|
|
89
|
+
const name = helpText ? parseDefaultGenerator(helpText) : null;
|
|
90
|
+
// Only VS generators need an arch pin; everything else (Ninja, NMake, …)
|
|
91
|
+
// already honours the active vcvarsall env for target arch.
|
|
92
|
+
result = isVisualStudioGenerator(name) ? name : null;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
if (!injected) cachedDefault = result;
|
|
96
|
+
return result;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Resolve the generator a CMake invocation will actually use, given an
|
|
101
|
+
* optional explicit choice (CLI `--generator` or a recipe `build.generator`)
|
|
102
|
+
* and the active profile.
|
|
103
|
+
*
|
|
104
|
+
* - An explicit non-empty generator always wins, verbatim.
|
|
105
|
+
* - Otherwise, on a Windows/MSVC profile, fall back to the detected default
|
|
106
|
+
* VS generator so callers can pin `-A`/architecture against it.
|
|
107
|
+
* - Otherwise null — the caller leaves the generator unpinned (CMake picks
|
|
108
|
+
* its default), exactly as before this feature. On non-Windows toolchains
|
|
109
|
+
* target arch is the compiler's job, not the generator's.
|
|
110
|
+
*
|
|
111
|
+
* @param {{ explicit?: string|null,
|
|
112
|
+
* profile?: { os?: string, compiler?: string }|null,
|
|
113
|
+
* detect?: () => Promise<string|null> }} [args]
|
|
114
|
+
* @returns {Promise<string|null>}
|
|
115
|
+
*/
|
|
116
|
+
async function resolveEffectiveGenerator({ explicit = null, profile = null, detect = detectDefaultGenerator } = {}) {
|
|
117
|
+
if (typeof explicit === 'string' && explicit.trim()) return explicit;
|
|
118
|
+
if (profile && profile.os === 'Windows' && profile.compiler === 'msvc') {
|
|
119
|
+
return detect();
|
|
120
|
+
}
|
|
121
|
+
return null;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
module.exports = {
|
|
125
|
+
resolveEffectiveGenerator,
|
|
126
|
+
detectDefaultGenerator,
|
|
127
|
+
// Exported for unit tests
|
|
128
|
+
parseDefaultGenerator,
|
|
129
|
+
};
|
package/src/commands/build.js
CHANGED
|
@@ -7,7 +7,9 @@ const { spawn } = require('child_process');
|
|
|
7
7
|
const { normalizeRecipe, hasBuildRecipe, VALID_CMAKE_CONFIGS, DEFAULT_RECIPE } = require('../build/recipe');
|
|
8
8
|
const { loadMsvcEnv } = require('../build/msvc-env');
|
|
9
9
|
const { archToVsPlatform, isVisualStudioGenerator } = require('../toolchain');
|
|
10
|
+
const { readConfigurePresetGenerator } = require('../toolchain/presets');
|
|
10
11
|
const { resolveEffectiveGenerator } = require('../build/default-generator');
|
|
12
|
+
const { resolveCmakeExe } = require('../build/cmake-exe');
|
|
11
13
|
const { LOCAL_OVERLAY_FILENAME, readLocalOverlay } = require('../conf');
|
|
12
14
|
const { resolveBinaryDirRoot, DEFAULT_BINARY_DIR_ROOT } = require('../binary-dir');
|
|
13
15
|
const { buildContext } = require('../context');
|
|
@@ -225,6 +227,54 @@ function buildInstallArgs({ binaryDir, config, prefix }) {
|
|
|
225
227
|
return args;
|
|
226
228
|
}
|
|
227
229
|
|
|
230
|
+
/**
|
|
231
|
+
* Resolve the generator `cmake --preset <presetName>` will actually use, so
|
|
232
|
+
* the caller can decide generator-dependent flags (the Visual Studio `-A`
|
|
233
|
+
* arch pin, and whether to activate vcvarsall) against reality.
|
|
234
|
+
*
|
|
235
|
+
* Precedence — each step mirrors what CMake itself does:
|
|
236
|
+
* 1. CLI `--generator` — `build` passes it as `-G`, which overrides the
|
|
237
|
+
* preset, so it wins here too.
|
|
238
|
+
* 2. The generator baked into the configure preset by `install`. This file,
|
|
239
|
+
* not the project's recipe, is the contract `cmake --preset` consumes;
|
|
240
|
+
* the recipe and the emitted preset can legitimately diverge (an
|
|
241
|
+
* install-time `--generator`, or a recipe edited after the last install).
|
|
242
|
+
* Trusting a re-derived recipe value is exactly the bug this guards
|
|
243
|
+
* against — it pins `-A` against a non-VS preset (a hard CMake error) and
|
|
244
|
+
* wrongly skips vcvarsall.
|
|
245
|
+
* 3. The detected default (Windows/MSVC → newest VS) for a generator-less
|
|
246
|
+
* preset — emitted by a pre-arch-pin client, or any non-Windows
|
|
247
|
+
* toolchain. Keeps the recipe-less arch pin firing. See
|
|
248
|
+
* claude/PLAN-VS-ARCH-PIN.md.
|
|
249
|
+
*
|
|
250
|
+
* `readPresetGenerator` and `detect` are injectable for hermetic tests.
|
|
251
|
+
*
|
|
252
|
+
* @param {{
|
|
253
|
+
* rootDir: string,
|
|
254
|
+
* presetName: string,
|
|
255
|
+
* cliGenerator?: string|null,
|
|
256
|
+
* profile?: { os?: string, compiler?: string, arch?: string }|null,
|
|
257
|
+
* readPresetGenerator?: (args: { projectRoot: string, presetName: string }) => string|null,
|
|
258
|
+
* detect?: () => Promise<string|null>,
|
|
259
|
+
* }} args
|
|
260
|
+
* @returns {Promise<string|null>}
|
|
261
|
+
*/
|
|
262
|
+
async function resolveConfigureGenerator({
|
|
263
|
+
rootDir,
|
|
264
|
+
presetName,
|
|
265
|
+
cliGenerator = null,
|
|
266
|
+
profile = null,
|
|
267
|
+
readPresetGenerator = readConfigurePresetGenerator,
|
|
268
|
+
detect,
|
|
269
|
+
}) {
|
|
270
|
+
if (typeof cliGenerator === 'string' && cliGenerator.trim()) return cliGenerator;
|
|
271
|
+
|
|
272
|
+
const fromPreset = readPresetGenerator({ projectRoot: rootDir, presetName });
|
|
273
|
+
if (fromPreset) return fromPreset;
|
|
274
|
+
|
|
275
|
+
return resolveEffectiveGenerator({ explicit: null, profile, detect });
|
|
276
|
+
}
|
|
277
|
+
|
|
228
278
|
// ---------------------------------------------------------------------------
|
|
229
279
|
// Spawn wrapper
|
|
230
280
|
// ---------------------------------------------------------------------------
|
|
@@ -244,7 +294,7 @@ function runCmake(args, env = null) {
|
|
|
244
294
|
return new Promise((resolve, reject) => {
|
|
245
295
|
const opts = { stdio: 'inherit' };
|
|
246
296
|
if (env) opts.env = env;
|
|
247
|
-
const proc = spawn(
|
|
297
|
+
const proc = spawn(resolveCmakeExe(), args, opts);
|
|
248
298
|
proc.on('error', (err) => {
|
|
249
299
|
reject(new Error(
|
|
250
300
|
`Failed to spawn cmake: ${err.message}\n` +
|
|
@@ -369,32 +419,20 @@ async function build(argv) {
|
|
|
369
419
|
//
|
|
370
420
|
// Same mechanism the source-build path already uses (src/build/msvc-env.js).
|
|
371
421
|
let cmakeEnv = null;
|
|
372
|
-
|
|
373
|
-
let
|
|
422
|
+
const effectiveProfile = activeProfile;
|
|
423
|
+
let effectiveGenerator = null;
|
|
374
424
|
try {
|
|
375
|
-
|
|
376
|
-
//
|
|
377
|
-
// project's
|
|
378
|
-
//
|
|
379
|
-
//
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
} catch { /* templates need options → skip env activation */ }
|
|
387
|
-
}
|
|
388
|
-
// CLI --generator wins, then the recipe's generator, then — on a
|
|
389
|
-
// Windows/MSVC profile with neither — CMake's detected default VS
|
|
390
|
-
// generator. Resolving the default here keeps the `-A` pin below and the
|
|
391
|
-
// vcvarsall-skip decision consistent with the generator `cmake --preset`
|
|
392
|
-
// will actually use (install pins the same generator into the preset).
|
|
393
|
-
// Non-Windows / non-MSVC → null, unchanged from before.
|
|
394
|
-
// See claude/PLAN-VS-ARCH-PIN.md.
|
|
395
|
-
effectiveGenerator = await resolveEffectiveGenerator({
|
|
396
|
-
explicit: effectiveGenerator ?? presetGenerator,
|
|
397
|
-
profile: activeProfile,
|
|
425
|
+
// The generator `cmake --preset` will actually use is the one baked into
|
|
426
|
+
// the configure preset by `install` (a CLI --generator overrides it). That
|
|
427
|
+
// value — not the project's recipe — drives both the `-A` pin below and the
|
|
428
|
+
// vcvarsall-skip decision, so they stay consistent with reality even when
|
|
429
|
+
// the recipe and the emitted preset disagree. See resolveConfigureGenerator
|
|
430
|
+
// and claude/PLAN-VS-ARCH-PIN.md.
|
|
431
|
+
effectiveGenerator = await resolveConfigureGenerator({
|
|
432
|
+
rootDir,
|
|
433
|
+
presetName,
|
|
434
|
+
cliGenerator: argv.generator,
|
|
435
|
+
profile: activeProfile,
|
|
398
436
|
});
|
|
399
437
|
cmakeEnv = await loadMsvcEnv({
|
|
400
438
|
profile: activeProfile,
|
|
@@ -485,6 +523,7 @@ module.exports._helpers = {
|
|
|
485
523
|
resolvePresetName,
|
|
486
524
|
resolveBinaryDir,
|
|
487
525
|
resolveConfigList,
|
|
526
|
+
resolveConfigureGenerator,
|
|
488
527
|
buildConfigureArgs,
|
|
489
528
|
buildBuildArgs,
|
|
490
529
|
buildInstallArgs,
|
package/src/context.js
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* re-read config, re-resolve auth, and each toggle JSON mode on the logger.
|
|
8
8
|
* The divergence was cheap at 11 commands; it starts costing real bugs
|
|
9
9
|
* once F3 (`tool_requires`) and F4 (build/host profile split) double the
|
|
10
|
-
* flag surface. See [claude/
|
|
10
|
+
* flag surface. See [.claude-wyvrn-local/plans/2026-04-23-command-context.md](../.claude-wyvrn-local/plans/2026-04-23-command-context.md).
|
|
11
11
|
*
|
|
12
12
|
* `buildContext(argv)` runs once at the top of each command. It:
|
|
13
13
|
* - resolves filesystem anchors (rootDir, manifestPath)
|
package/src/toolchain/presets.js
CHANGED
|
@@ -388,6 +388,36 @@ function generateCMakePresets({
|
|
|
388
388
|
return { path: null, action: 'skipped', isUser: false };
|
|
389
389
|
}
|
|
390
390
|
|
|
391
|
+
/**
|
|
392
|
+
* Read the `generator` of a named configure preset from the project's
|
|
393
|
+
* CMakePresets.json (falling back to CMakeUserPresets.json). This is the
|
|
394
|
+
* generator `cmake --preset <name>` will actually use, so callers deciding
|
|
395
|
+
* generator-dependent flags — the Visual Studio `-A` arch pin, or whether to
|
|
396
|
+
* activate vcvarsall — must trust it over a value re-derived from the recipe.
|
|
397
|
+
* The recipe and the emitted preset can legitimately diverge (an install-time
|
|
398
|
+
* `--generator`, or a recipe edited after the last install); trusting the
|
|
399
|
+
* recipe pins `-A` against a Ninja preset, which CMake rejects with a hard
|
|
400
|
+
* "does not support platform specification" error.
|
|
401
|
+
*
|
|
402
|
+
* Returns the generator string, or null when the file/preset is absent or the
|
|
403
|
+
* preset declares no generator — the latter means CMake will fall back to its
|
|
404
|
+
* platform default, which the caller's detection path handles.
|
|
405
|
+
*
|
|
406
|
+
* @param {{ projectRoot: string, presetName: string }} args
|
|
407
|
+
* @returns {string|null}
|
|
408
|
+
*/
|
|
409
|
+
function readConfigurePresetGenerator({ projectRoot, presetName }) {
|
|
410
|
+
for (const file of ['CMakePresets.json', 'CMakeUserPresets.json']) {
|
|
411
|
+
const parsed = safeReadJson(path.join(projectRoot, file));
|
|
412
|
+
const presets = parsed && Array.isArray(parsed.configurePresets) ? parsed.configurePresets : [];
|
|
413
|
+
const match = presets.find((p) => p && p.name === presetName);
|
|
414
|
+
if (match && typeof match.generator === 'string' && match.generator.trim()) {
|
|
415
|
+
return match.generator;
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
return null;
|
|
419
|
+
}
|
|
420
|
+
|
|
391
421
|
/**
|
|
392
422
|
* Strip every wyvrnpm-generated entry from a parsed presets object.
|
|
393
423
|
* Mirror of `mergeIntoExisting` — used by `wyvrnpm clean --all` to
|
|
@@ -475,6 +505,7 @@ function stripWyvrnPresets(existing) {
|
|
|
475
505
|
|
|
476
506
|
module.exports = {
|
|
477
507
|
generateCMakePresets,
|
|
508
|
+
readConfigurePresetGenerator,
|
|
478
509
|
stripWyvrnPresets,
|
|
479
510
|
safeReadJson,
|
|
480
511
|
// Exported for tests
|