wyvrnpm 2.19.0 → 2.20.0
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.js +27 -2
- package/src/build/default-generator.js +124 -0
- package/src/commands/build.js +12 -3
- package/src/commands/install.js +16 -0
package/package.json
CHANGED
package/src/build/cmake.js
CHANGED
|
@@ -5,6 +5,8 @@ const path = require('path');
|
|
|
5
5
|
const { spawn } = require('child_process');
|
|
6
6
|
|
|
7
7
|
const { loadMsvcEnv } = require('./msvc-env');
|
|
8
|
+
const { archToVsPlatform, isVisualStudioGenerator } = require('../toolchain');
|
|
9
|
+
const { detectDefaultGenerator } = require('./default-generator');
|
|
8
10
|
const log = require('../logger');
|
|
9
11
|
|
|
10
12
|
/**
|
|
@@ -190,15 +192,21 @@ function isMultiConfigGenerator(generatorName) {
|
|
|
190
192
|
* configureExtra: string[],
|
|
191
193
|
* buildType?: string|null, // single-config only
|
|
192
194
|
* configurations?: string[]|null, // multi-config only, pins CMAKE_CONFIGURATION_TYPES
|
|
195
|
+
* vsPlatform?: string|null, // VS generators only — emits -A <platform>
|
|
193
196
|
* }} args
|
|
194
197
|
* @returns {string[]}
|
|
195
198
|
*/
|
|
196
199
|
function buildConfigureArgs({
|
|
197
200
|
sourceDir, buildDir, installDir, toolchainFile, generator, configureExtra,
|
|
198
|
-
buildType, configurations,
|
|
201
|
+
buildType, configurations, vsPlatform = null,
|
|
199
202
|
}) {
|
|
200
203
|
const args = ['-S', sourceDir, '-B', buildDir];
|
|
201
204
|
if (generator) args.push('-G', generator);
|
|
205
|
+
// Visual Studio generators default to host x64 unless the target platform is
|
|
206
|
+
// pinned with -A. Without this, an x86/ARM profile silently produces x64
|
|
207
|
+
// binaries (PLAN-VS-ARCH-PIN gate 3). Mapped from the profile arch by the
|
|
208
|
+
// caller; null for non-VS generators.
|
|
209
|
+
if (vsPlatform) args.push('-A', vsPlatform);
|
|
202
210
|
args.push(`-DCMAKE_TOOLCHAIN_FILE=${toolchainFile}`);
|
|
203
211
|
args.push(`-DCMAKE_INSTALL_PREFIX=${installDir}`);
|
|
204
212
|
// Single-config generators bake CMAKE_BUILD_TYPE into the cache.
|
|
@@ -257,7 +265,22 @@ async function configureBuildInstall({ recipe, srcRoot, buildPaths, toolchainFil
|
|
|
257
265
|
|
|
258
266
|
// Resolve generator from the fallback chain (probes each candidate's
|
|
259
267
|
// backing tool). `null` → omit `-G`, let CMake pick its default.
|
|
260
|
-
|
|
268
|
+
let generator = await pickGenerator(recipe.generators);
|
|
269
|
+
|
|
270
|
+
// No recipe generator → CMake's Windows default is the newest VS, which
|
|
271
|
+
// builds host x64 unless `-A` is pinned. Resolve it to a concrete VS
|
|
272
|
+
// generator so the -A below applies (PLAN-VS-ARCH-PIN gate 3). Non-Windows /
|
|
273
|
+
// non-MSVC leaves `generator` null → CMake default, no -A (target arch is
|
|
274
|
+
// the compiler's job there).
|
|
275
|
+
if (!generator && profile && profile.os === 'Windows' && profile.compiler === 'msvc') {
|
|
276
|
+
generator = await detectDefaultGenerator();
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
// VS generators need -A <platform> to honour the profile arch — recipe-pinned
|
|
280
|
+
// VS generators get it too, not just the detected default.
|
|
281
|
+
const vsPlatform = isVisualStudioGenerator(generator)
|
|
282
|
+
? archToVsPlatform(profile && profile.arch)
|
|
283
|
+
: null;
|
|
261
284
|
|
|
262
285
|
// On Windows with an MSVC profile + non-VS generator (Ninja, Make, ...),
|
|
263
286
|
// auto-enter a Developer Command Prompt-equivalent env so `cl.exe` is on
|
|
@@ -281,6 +304,7 @@ async function configureBuildInstall({ recipe, srcRoot, buildPaths, toolchainFil
|
|
|
281
304
|
installDir: buildPaths.install,
|
|
282
305
|
toolchainFile,
|
|
283
306
|
generator,
|
|
307
|
+
vsPlatform,
|
|
284
308
|
configureExtra: recipe.configure,
|
|
285
309
|
buildType: null, // multi-config picks at build time
|
|
286
310
|
configurations: configList, // pin CMAKE_CONFIGURATION_TYPES so all 4 build-<Config>.ninja files get generated
|
|
@@ -313,6 +337,7 @@ async function configureBuildInstall({ recipe, srcRoot, buildPaths, toolchainFil
|
|
|
313
337
|
installDir: buildPaths.install,
|
|
314
338
|
toolchainFile,
|
|
315
339
|
generator,
|
|
340
|
+
vsPlatform,
|
|
316
341
|
configureExtra: recipe.configure,
|
|
317
342
|
buildType: cfg,
|
|
318
343
|
});
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const { spawn } = require('child_process');
|
|
4
|
+
|
|
5
|
+
const { isVisualStudioGenerator } = require('../toolchain');
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Parse `cmake --help` output for the platform's default generator. CMake
|
|
9
|
+
* marks the default with a leading `* ` in the "Generators" section, e.g.:
|
|
10
|
+
*
|
|
11
|
+
* The following generators are available on this platform (* marks default):
|
|
12
|
+
* * Visual Studio 17 2022 [arch] = Generates Visual Studio 2022 project files.
|
|
13
|
+
* Visual Studio 16 2019 [arch] = Generates Visual Studio 2019 project files.
|
|
14
|
+
* Ninja = Generates build.ninja files.
|
|
15
|
+
*
|
|
16
|
+
* Returns the default generator's name verbatim (with any trailing ` [arch]`
|
|
17
|
+
* annotation stripped), or null when no default marker is present. The name is
|
|
18
|
+
* returned as-is — deciding whether it's a Visual Studio generator is the
|
|
19
|
+
* caller's job.
|
|
20
|
+
*
|
|
21
|
+
* @param {string} helpText
|
|
22
|
+
* @returns {string|null}
|
|
23
|
+
*/
|
|
24
|
+
function parseDefaultGenerator(helpText) {
|
|
25
|
+
if (typeof helpText !== 'string') return null;
|
|
26
|
+
for (const line of helpText.split(/\r?\n/)) {
|
|
27
|
+
const m = line.match(/^\*\s+(.+?)\s*=/);
|
|
28
|
+
if (!m) continue;
|
|
29
|
+
return m[1].replace(/\s*\[arch\]\s*$/, '').trim();
|
|
30
|
+
}
|
|
31
|
+
return null;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Spawn `cmake --help` and resolve its stdout. Resolves null (never rejects)
|
|
36
|
+
* when cmake is absent from PATH or exits non-zero — generator detection is
|
|
37
|
+
* best-effort, and an unavailable cmake simply means the caller falls back to
|
|
38
|
+
* leaving the generator unpinned, exactly as before this feature.
|
|
39
|
+
*
|
|
40
|
+
* @returns {Promise<string|null>}
|
|
41
|
+
*/
|
|
42
|
+
function runCmakeHelp() {
|
|
43
|
+
return new Promise((resolve) => {
|
|
44
|
+
let out = '';
|
|
45
|
+
const proc = spawn('cmake', ['--help'], {
|
|
46
|
+
shell: process.platform === 'win32',
|
|
47
|
+
});
|
|
48
|
+
proc.stdout.on('data', (chunk) => { out += chunk.toString(); });
|
|
49
|
+
// cmake not on PATH → detection unavailable; recoverable, fall back to null.
|
|
50
|
+
proc.on('error', () => resolve(null));
|
|
51
|
+
proc.on('close', (code) => resolve(code === 0 ? out : null));
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// Per-process memo of the production (no-injection) probe result. The default
|
|
56
|
+
// generator can't change mid-run, so a single `cmake --help` spawn is enough
|
|
57
|
+
// for the whole process — important when one process drives many installs
|
|
58
|
+
// (e.g. a source-build that resolves several deps, or the test suite). Calls
|
|
59
|
+
// that inject `platform`/`runHelp` (tests) bypass this cache to stay hermetic.
|
|
60
|
+
let cachedDefault; // undefined = unprobed; string|null = probed result
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Detect the Visual Studio generator CMake would use by default on this
|
|
64
|
+
* machine. On Windows with VS installed, CMake's implicit default is the
|
|
65
|
+
* newest installed VS — which builds host x64 unless `-A`/architecture is
|
|
66
|
+
* pinned. Returns that VS generator name so callers can pin it (and the
|
|
67
|
+
* matching `-A`), or null when the default is not a VS generator
|
|
68
|
+
* (non-Windows, or VS not installed → today's behaviour, no pin).
|
|
69
|
+
*
|
|
70
|
+
* `platform` and `runHelp` are injectable for tests; the defaults probe the
|
|
71
|
+
* real machine and memoise the result per-process.
|
|
72
|
+
*
|
|
73
|
+
* @param {{ platform?: string, runHelp?: () => Promise<string|null> }} [opts]
|
|
74
|
+
* @returns {Promise<string|null>}
|
|
75
|
+
*/
|
|
76
|
+
async function detectDefaultGenerator({ platform, runHelp } = {}) {
|
|
77
|
+
const injected = platform !== undefined || runHelp !== undefined;
|
|
78
|
+
if (!injected && cachedDefault !== undefined) return cachedDefault;
|
|
79
|
+
|
|
80
|
+
const plat = platform ?? process.platform;
|
|
81
|
+
let result = null;
|
|
82
|
+
if (plat === 'win32') {
|
|
83
|
+
const helpText = await (runHelp ?? runCmakeHelp)();
|
|
84
|
+
const name = helpText ? parseDefaultGenerator(helpText) : null;
|
|
85
|
+
// Only VS generators need an arch pin; everything else (Ninja, NMake, …)
|
|
86
|
+
// already honours the active vcvarsall env for target arch.
|
|
87
|
+
result = isVisualStudioGenerator(name) ? name : null;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
if (!injected) cachedDefault = result;
|
|
91
|
+
return result;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Resolve the generator a CMake invocation will actually use, given an
|
|
96
|
+
* optional explicit choice (CLI `--generator` or a recipe `build.generator`)
|
|
97
|
+
* and the active profile.
|
|
98
|
+
*
|
|
99
|
+
* - An explicit non-empty generator always wins, verbatim.
|
|
100
|
+
* - Otherwise, on a Windows/MSVC profile, fall back to the detected default
|
|
101
|
+
* VS generator so callers can pin `-A`/architecture against it.
|
|
102
|
+
* - Otherwise null — the caller leaves the generator unpinned (CMake picks
|
|
103
|
+
* its default), exactly as before this feature. On non-Windows toolchains
|
|
104
|
+
* target arch is the compiler's job, not the generator's.
|
|
105
|
+
*
|
|
106
|
+
* @param {{ explicit?: string|null,
|
|
107
|
+
* profile?: { os?: string, compiler?: string }|null,
|
|
108
|
+
* detect?: () => Promise<string|null> }} [args]
|
|
109
|
+
* @returns {Promise<string|null>}
|
|
110
|
+
*/
|
|
111
|
+
async function resolveEffectiveGenerator({ explicit = null, profile = null, detect = detectDefaultGenerator } = {}) {
|
|
112
|
+
if (typeof explicit === 'string' && explicit.trim()) return explicit;
|
|
113
|
+
if (profile && profile.os === 'Windows' && profile.compiler === 'msvc') {
|
|
114
|
+
return detect();
|
|
115
|
+
}
|
|
116
|
+
return null;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
module.exports = {
|
|
120
|
+
resolveEffectiveGenerator,
|
|
121
|
+
detectDefaultGenerator,
|
|
122
|
+
// Exported for unit tests
|
|
123
|
+
parseDefaultGenerator,
|
|
124
|
+
};
|
package/src/commands/build.js
CHANGED
|
@@ -7,6 +7,7 @@ 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 { resolveEffectiveGenerator } = require('../build/default-generator');
|
|
10
11
|
const { LOCAL_OVERLAY_FILENAME, readLocalOverlay } = require('../conf');
|
|
11
12
|
const { resolveBinaryDirRoot, DEFAULT_BINARY_DIR_ROOT } = require('../binary-dir');
|
|
12
13
|
const { buildContext } = require('../context');
|
|
@@ -384,9 +385,17 @@ async function build(argv) {
|
|
|
384
385
|
if (recipe.generators.length > 0) [presetGenerator] = recipe.generators;
|
|
385
386
|
} catch { /* templates need options → skip env activation */ }
|
|
386
387
|
}
|
|
387
|
-
// CLI --generator wins
|
|
388
|
-
//
|
|
389
|
-
|
|
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,
|
|
398
|
+
});
|
|
390
399
|
cmakeEnv = await loadMsvcEnv({
|
|
391
400
|
profile: activeProfile,
|
|
392
401
|
skipForGenerator: effectiveGenerator,
|
package/src/commands/install.js
CHANGED
|
@@ -18,6 +18,7 @@ const { resolveBinaryDirRoot } = require('../binary
|
|
|
18
18
|
const { hashProfile, formatProfile, mergeProfile } = require('../profile');
|
|
19
19
|
const { generateToolchain, generateCMakePresets } = require('../toolchain');
|
|
20
20
|
const { normalizeRecipe, hasBuildRecipe } = require('../build/recipe');
|
|
21
|
+
const { resolveEffectiveGenerator } = require('../build/default-generator');
|
|
21
22
|
const { buildContext } = require('../context');
|
|
22
23
|
const {
|
|
23
24
|
normalizeOptionsDeclaration,
|
|
@@ -661,6 +662,21 @@ async function install(argv) {
|
|
|
661
662
|
}
|
|
662
663
|
}
|
|
663
664
|
|
|
665
|
+
// When no generator was pinned by a recipe (the common recipe-less consumer
|
|
666
|
+
// case), CMake falls back to its platform default. On Windows that's the
|
|
667
|
+
// newest Visual Studio, which silently targets host x64 unless
|
|
668
|
+
// `-A`/architecture is pinned — so an x86 (or any non-host-arch) profile
|
|
669
|
+
// would produce mismatched binaries that find_package() later rejects with a
|
|
670
|
+
// pointer-size error. Resolve the default to a concrete VS generator so the
|
|
671
|
+
// preset carries both `generator` and `architecture` (buildConfigurePreset
|
|
672
|
+
// pins arch only when it knows the generator is VS). Non-Windows / non-MSVC
|
|
673
|
+
// → null, leaving the preset generator-less exactly as before. See
|
|
674
|
+
// claude/PLAN-VS-ARCH-PIN.md.
|
|
675
|
+
presetGenerator = await resolveEffectiveGenerator({
|
|
676
|
+
explicit: presetGenerator,
|
|
677
|
+
profile: baseProfile,
|
|
678
|
+
});
|
|
679
|
+
|
|
664
680
|
// PLAN-CONF.md §4.3 / §5 presets sink: fold effectiveConf's
|
|
665
681
|
// cmake.cache namespace into the preset's cacheVariables. Runs
|
|
666
682
|
// regardless of whether the project has its own build recipe —
|