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.
- package/README.md +1914 -1860
- package/bin/{wyvrn.js → wyvrnpm.js} +66 -0
- package/cmake/cpp.cmake +9 -9
- package/cmake/functions.cmake +224 -224
- package/cmake/macros.cmake +284 -284
- package/package.json +3 -2
- package/src/auth.js +66 -66
- package/src/binary-dir.js +95 -0
- package/src/bootstrap/cookbook.js +196 -196
- package/src/bootstrap/detect.js +150 -150
- package/src/bootstrap/index.js +220 -220
- package/src/bootstrap/version.js +72 -72
- package/src/build/cache.js +344 -344
- package/src/build/clone.js +170 -170
- package/src/build/cmake.js +342 -342
- package/src/build/index.js +299 -297
- package/src/build/msvc-env.js +260 -260
- package/src/build/recipe.js +188 -188
- package/src/commands/add.js +141 -141
- package/src/commands/bootstrap.js +96 -96
- package/src/commands/build.js +482 -452
- package/src/commands/cache.js +189 -189
- package/src/commands/clean.js +80 -80
- package/src/commands/configure.js +92 -92
- package/src/commands/init.js +70 -70
- package/src/commands/install-skill.js +115 -115
- package/src/commands/install.js +730 -674
- package/src/commands/link.js +320 -320
- package/src/commands/profile.js +237 -237
- package/src/commands/publish.js +584 -555
- package/src/commands/show.js +252 -252
- package/src/commands/version.js +187 -0
- package/src/compat.js +273 -273
- package/src/conf/index.js +415 -391
- package/src/conf/namespaces.js +94 -94
- package/src/context.js +230 -230
- package/src/http-fetch.js +53 -53
- package/src/ignore.js +118 -118
- package/src/logger.js +122 -122
- package/src/options.js +303 -303
- package/src/settings-overrides.js +152 -152
- package/src/toolchain/deps.js +164 -164
- package/src/toolchain/index.js +212 -212
- package/src/toolchain/presets.js +356 -340
- package/src/toolchain/template.cmake +77 -77
- package/src/upload-built.js +265 -265
- package/src/version-range.js +301 -301
- package/src/zip-safe.js +52 -52
- package/src/zip-stream.js +126 -0
package/src/commands/build.js
CHANGED
|
@@ -1,452 +1,482 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
const fs = require('fs');
|
|
4
|
-
const path = require('path');
|
|
5
|
-
const { spawn } = require('child_process');
|
|
6
|
-
|
|
7
|
-
const { normalizeRecipe, hasBuildRecipe, VALID_CMAKE_CONFIGS, DEFAULT_RECIPE } = require('../build/recipe');
|
|
8
|
-
const { loadMsvcEnv } = require('../build/msvc-env');
|
|
9
|
-
const { archToVsPlatform, isVisualStudioGenerator } = require('../toolchain');
|
|
10
|
-
const { LOCAL_OVERLAY_FILENAME } = require('../conf');
|
|
11
|
-
const {
|
|
12
|
-
const
|
|
13
|
-
const
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
//
|
|
17
|
-
//
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
* -
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
*
|
|
28
|
-
* @
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
const
|
|
33
|
-
const
|
|
34
|
-
const
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
const
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
//
|
|
51
|
-
//
|
|
52
|
-
//
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
const
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
*
|
|
67
|
-
*
|
|
68
|
-
* @
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
return
|
|
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
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
*
|
|
148
|
-
*
|
|
149
|
-
*
|
|
150
|
-
*
|
|
151
|
-
*
|
|
152
|
-
* config
|
|
153
|
-
*
|
|
154
|
-
*
|
|
155
|
-
*
|
|
156
|
-
*
|
|
157
|
-
*
|
|
158
|
-
*
|
|
159
|
-
*
|
|
160
|
-
*
|
|
161
|
-
*
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
*
|
|
193
|
-
*
|
|
194
|
-
*
|
|
195
|
-
*
|
|
196
|
-
*
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
*
|
|
213
|
-
*
|
|
214
|
-
* @
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
*
|
|
233
|
-
*
|
|
234
|
-
*
|
|
235
|
-
*
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
*
|
|
266
|
-
*
|
|
267
|
-
*
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
//
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
//
|
|
369
|
-
//
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
//
|
|
402
|
-
//
|
|
403
|
-
//
|
|
404
|
-
//
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const fs = require('fs');
|
|
4
|
+
const path = require('path');
|
|
5
|
+
const { spawn } = require('child_process');
|
|
6
|
+
|
|
7
|
+
const { normalizeRecipe, hasBuildRecipe, VALID_CMAKE_CONFIGS, DEFAULT_RECIPE } = require('../build/recipe');
|
|
8
|
+
const { loadMsvcEnv } = require('../build/msvc-env');
|
|
9
|
+
const { archToVsPlatform, isVisualStudioGenerator } = require('../toolchain');
|
|
10
|
+
const { LOCAL_OVERLAY_FILENAME, readLocalOverlay } = require('../conf');
|
|
11
|
+
const { resolveBinaryDirRoot, DEFAULT_BINARY_DIR_ROOT } = require('../binary-dir');
|
|
12
|
+
const { buildContext } = require('../context');
|
|
13
|
+
const install = require('./install');
|
|
14
|
+
const log = require('../logger');
|
|
15
|
+
|
|
16
|
+
// ---------------------------------------------------------------------------
|
|
17
|
+
// Pure helpers (unit-testable — no process spawning, no filesystem writes)
|
|
18
|
+
// ---------------------------------------------------------------------------
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Decide whether `wyvrn install` must run before build.
|
|
22
|
+
*
|
|
23
|
+
* - toolchain-missing : wyvrn_toolchain.cmake does not exist → definitely stale.
|
|
24
|
+
* - lock-newer : wyvrn.lock was modified after the toolchain was written,
|
|
25
|
+
* so the toolchain's package list is out of date.
|
|
26
|
+
* - fresh : no action needed.
|
|
27
|
+
*
|
|
28
|
+
* @param {{ rootDir: string, manifestPath: string }} args
|
|
29
|
+
* @returns {{ stale: boolean, reason: string }}
|
|
30
|
+
*/
|
|
31
|
+
function isToolchainStale({ rootDir, manifestPath }) {
|
|
32
|
+
const lockPath = path.join(path.dirname(manifestPath), 'wyvrn.lock');
|
|
33
|
+
const toolchainPath = path.join(rootDir, 'wyvrn_internal', 'wyvrn_toolchain.cmake');
|
|
34
|
+
const localOverlayPath = path.join(rootDir, LOCAL_OVERLAY_FILENAME);
|
|
35
|
+
const presetPath = path.join(rootDir, 'CMakePresets.json');
|
|
36
|
+
|
|
37
|
+
if (!fs.existsSync(toolchainPath)) {
|
|
38
|
+
return { stale: true, reason: 'toolchain-missing' };
|
|
39
|
+
}
|
|
40
|
+
if (!fs.existsSync(lockPath)) {
|
|
41
|
+
return { stale: false, reason: 'no-lock' };
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const lockMtime = fs.statSync(lockPath).mtimeMs;
|
|
45
|
+
const toolchainMtime = fs.statSync(toolchainPath).mtimeMs;
|
|
46
|
+
if (lockMtime > toolchainMtime) {
|
|
47
|
+
return { stale: true, reason: 'lock-newer-than-toolchain' };
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// PLAN-CONF.md §7: if wyvrn.local.json was edited after the preset was
|
|
51
|
+
// written, the preset's cacheVariables no longer reflect the consumer's
|
|
52
|
+
// intent. Re-run install (which regenerates the preset) rather than
|
|
53
|
+
// silently running a stale configure.
|
|
54
|
+
if (fs.existsSync(localOverlayPath) && fs.existsSync(presetPath)) {
|
|
55
|
+
const overlayMtime = fs.statSync(localOverlayPath).mtimeMs;
|
|
56
|
+
const presetMtime = fs.statSync(presetPath).mtimeMs;
|
|
57
|
+
if (overlayMtime > presetMtime) {
|
|
58
|
+
return { stale: true, reason: 'local-overlay-newer-than-preset' };
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
return { stale: false, reason: 'fresh' };
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Resolve the configure preset name from CLI override or profile convention.
|
|
67
|
+
*
|
|
68
|
+
* @param {{ presetOverride?: string, profileName: string }} args
|
|
69
|
+
* @returns {string}
|
|
70
|
+
*/
|
|
71
|
+
function resolvePresetName({ presetOverride, profileName }) {
|
|
72
|
+
if (presetOverride) return presetOverride;
|
|
73
|
+
return `wyvrn-${profileName}`;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Compute the build directory for a preset, matching the pattern emitted
|
|
78
|
+
* by [src/toolchain/presets.js](../toolchain/presets.js) —
|
|
79
|
+
* `${sourceDir}/<binaryDirRoot>/<preset>`. `binaryDirRoot` defaults to
|
|
80
|
+
* `"build"`; override via `--build-dir` or
|
|
81
|
+
* `wyvrn.local.json:binaryDirRoot`.
|
|
82
|
+
*
|
|
83
|
+
* @param {{ rootDir: string, presetName: string, binaryDirRoot?: string }} args
|
|
84
|
+
* @returns {string}
|
|
85
|
+
*/
|
|
86
|
+
function resolveBinaryDir({ rootDir, presetName, binaryDirRoot = DEFAULT_BINARY_DIR_ROOT }) {
|
|
87
|
+
const root = binaryDirRoot || DEFAULT_BINARY_DIR_ROOT;
|
|
88
|
+
return path.join(rootDir, root, presetName);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Args passed to `cmake` for the configure step.
|
|
93
|
+
*
|
|
94
|
+
* `generator`, when set, appends `-G <name>` after `--preset`. CMake 3.21+
|
|
95
|
+
* lets CLI args override a preset's declared fields, so this swaps the
|
|
96
|
+
* generator at configure time without touching CMakePresets.json. Note:
|
|
97
|
+
* CMake bakes the chosen generator into the cache, so switching generators
|
|
98
|
+
* for an existing build directory requires `--clean` (otherwise CMake
|
|
99
|
+
* errors with "CMAKE_GENERATOR has changed").
|
|
100
|
+
*
|
|
101
|
+
* `vsPlatform`, when set, appends `-A <platform>`. Only meaningful for
|
|
102
|
+
* Visual Studio generators — they default to x64 when unspecified, which
|
|
103
|
+
* silently produces a 64-bit build even for a 32-bit profile. The caller
|
|
104
|
+
* is responsible for deriving this from the profile arch only when the
|
|
105
|
+
* effective generator is VS (see `isVisualStudioGenerator` +
|
|
106
|
+
* `archToVsPlatform`).
|
|
107
|
+
*
|
|
108
|
+
* @param {{ presetName: string, generator?: string|null, vsPlatform?: string|null }} args
|
|
109
|
+
* @returns {string[]}
|
|
110
|
+
*/
|
|
111
|
+
function buildConfigureArgs({
|
|
112
|
+
presetName,
|
|
113
|
+
generator = null,
|
|
114
|
+
vsPlatform = null,
|
|
115
|
+
confSets = null,
|
|
116
|
+
confUnsets = null,
|
|
117
|
+
}) {
|
|
118
|
+
const args = ['--preset', presetName];
|
|
119
|
+
if (generator) args.push('-G', generator);
|
|
120
|
+
if (vsPlatform) args.push('-A', vsPlatform);
|
|
121
|
+
|
|
122
|
+
// PLAN-CONF.md §7: CLI --conf at build time applies as -D / -U
|
|
123
|
+
// overrides on top of whatever's baked into the preset. We do NOT
|
|
124
|
+
// silently regenerate the preset — that's a separate install.
|
|
125
|
+
// Confined to the cmake.cache namespace; other namespaces have
|
|
126
|
+
// different sinks (phase 3) and don't apply to `cmake --preset`.
|
|
127
|
+
if (confSets) {
|
|
128
|
+
const prefix = 'cmake.cache.';
|
|
129
|
+
for (const key of Object.keys(confSets).sort()) {
|
|
130
|
+
if (!key.startsWith(prefix)) continue;
|
|
131
|
+
const cmakeKey = key.slice(prefix.length);
|
|
132
|
+
args.push('-D', `${cmakeKey}=${confSets[key]}`);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
if (confUnsets) {
|
|
136
|
+
const prefix = 'cmake.cache.';
|
|
137
|
+
for (const key of [...confUnsets].sort()) {
|
|
138
|
+
if (!key.startsWith(prefix)) continue;
|
|
139
|
+
args.push('-U', key.slice(prefix.length));
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
return args;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Resolve the list of CMake configs to build (and optionally install).
|
|
148
|
+
*
|
|
149
|
+
* Precedence:
|
|
150
|
+
* 1. `--all-configs` → recipe.configs (falls back to all four canonical
|
|
151
|
+
* configs when no recipe declares any).
|
|
152
|
+
* 2. `--config` → comma-separated list, validated against
|
|
153
|
+
* VALID_CMAKE_CONFIGS. Order preserved. Duplicates removed.
|
|
154
|
+
* 3. default → `['Release']`.
|
|
155
|
+
*
|
|
156
|
+
* Configure runs once per `wyvrnpm build` invocation regardless — multi-
|
|
157
|
+
* config generators (VS, Xcode, Ninja Multi-Config) produce all configs
|
|
158
|
+
* from one configure step. Single-config generators (plain Ninja, Make)
|
|
159
|
+
* bake CMAKE_BUILD_TYPE into the cache, so `cmake --build --config X`
|
|
160
|
+
* effectively ignores X for them; building two configs against a single-
|
|
161
|
+
* config generator rebuilds the same binary twice. That's wasteful but
|
|
162
|
+
* correct; fixing it properly requires per-config build dirs, which is
|
|
163
|
+
* out of scope for this helper.
|
|
164
|
+
*
|
|
165
|
+
* @param {{ argv: object, recipeConfigs?: string[] }} args
|
|
166
|
+
* @returns {string[]}
|
|
167
|
+
*/
|
|
168
|
+
function resolveConfigList({ argv, recipeConfigs = null }) {
|
|
169
|
+
if (argv.allConfigs) {
|
|
170
|
+
const fromRecipe = (recipeConfigs ?? []).filter((c) => VALID_CMAKE_CONFIGS.has(c));
|
|
171
|
+
return fromRecipe.length > 0 ? [...fromRecipe] : [...DEFAULT_RECIPE.configs];
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
const raw = typeof argv.config === 'string' && argv.config.trim() ? argv.config : 'Release';
|
|
175
|
+
const list = raw.split(',').map((s) => s.trim()).filter(Boolean);
|
|
176
|
+
|
|
177
|
+
const invalid = list.filter((c) => !VALID_CMAKE_CONFIGS.has(c));
|
|
178
|
+
if (invalid.length > 0) {
|
|
179
|
+
const valid = [...VALID_CMAKE_CONFIGS].join(' | ');
|
|
180
|
+
throw new Error(`invalid --config "${invalid.join(', ')}" — expected ${valid}`);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
const seen = new Set();
|
|
184
|
+
const deduped = [];
|
|
185
|
+
for (const c of list) {
|
|
186
|
+
if (!seen.has(c)) { seen.add(c); deduped.push(c); }
|
|
187
|
+
}
|
|
188
|
+
return deduped.length > 0 ? deduped : ['Release'];
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
/**
|
|
192
|
+
* Args passed to `cmake` for the build step.
|
|
193
|
+
*
|
|
194
|
+
* @param {{
|
|
195
|
+
* binaryDir: string,
|
|
196
|
+
* config: string,
|
|
197
|
+
* target?: string,
|
|
198
|
+
* verbose?: boolean,
|
|
199
|
+
* passthru?: string[],
|
|
200
|
+
* }} args
|
|
201
|
+
* @returns {string[]}
|
|
202
|
+
*/
|
|
203
|
+
function buildBuildArgs({ binaryDir, config, target, verbose, passthru = [] }) {
|
|
204
|
+
const args = ['--build', binaryDir, '--config', config];
|
|
205
|
+
if (verbose) args.push('--verbose');
|
|
206
|
+
if (target) args.push('--target', target);
|
|
207
|
+
if (passthru.length > 0) args.push('--', ...passthru);
|
|
208
|
+
return args;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
/**
|
|
212
|
+
* Args passed to `cmake --install` for the install step.
|
|
213
|
+
*
|
|
214
|
+
* @param {{
|
|
215
|
+
* binaryDir: string,
|
|
216
|
+
* config: string,
|
|
217
|
+
* prefix?: string, // override CMAKE_INSTALL_PREFIX at install time
|
|
218
|
+
* }} args
|
|
219
|
+
* @returns {string[]}
|
|
220
|
+
*/
|
|
221
|
+
function buildInstallArgs({ binaryDir, config, prefix }) {
|
|
222
|
+
const args = ['--install', binaryDir, '--config', config];
|
|
223
|
+
if (prefix) args.push('--prefix', prefix);
|
|
224
|
+
return args;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
// ---------------------------------------------------------------------------
|
|
228
|
+
// Spawn wrapper
|
|
229
|
+
// ---------------------------------------------------------------------------
|
|
230
|
+
|
|
231
|
+
/**
|
|
232
|
+
* Run `cmake` with the given args. Inherits stdio for real-time output.
|
|
233
|
+
* Rejects if the process exits non-zero or fails to spawn.
|
|
234
|
+
*
|
|
235
|
+
* When `env` is provided, it replaces the ambient environment (used to
|
|
236
|
+
* activate MSVC via vcvarsall for non-VS generators on Windows).
|
|
237
|
+
*
|
|
238
|
+
* @param {string[]} args
|
|
239
|
+
* @param {NodeJS.ProcessEnv|null} [env]
|
|
240
|
+
* @returns {Promise<void>}
|
|
241
|
+
*/
|
|
242
|
+
function runCmake(args, env = null) {
|
|
243
|
+
return new Promise((resolve, reject) => {
|
|
244
|
+
const opts = { stdio: 'inherit' };
|
|
245
|
+
if (env) opts.env = env;
|
|
246
|
+
const proc = spawn('cmake', args, opts);
|
|
247
|
+
proc.on('error', (err) => {
|
|
248
|
+
reject(new Error(
|
|
249
|
+
`Failed to spawn cmake: ${err.message}\n` +
|
|
250
|
+
' Is CMake installed and on PATH?',
|
|
251
|
+
));
|
|
252
|
+
});
|
|
253
|
+
proc.on('close', (code) => {
|
|
254
|
+
if (code === 0) resolve();
|
|
255
|
+
else reject(new Error(`cmake ${args.join(' ')} exited with code ${code}`));
|
|
256
|
+
});
|
|
257
|
+
});
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
// ---------------------------------------------------------------------------
|
|
261
|
+
// Command entry
|
|
262
|
+
// ---------------------------------------------------------------------------
|
|
263
|
+
|
|
264
|
+
/**
|
|
265
|
+
* `wyvrnpm build` — configure + build via the generated CMake preset.
|
|
266
|
+
*
|
|
267
|
+
* Defaults:
|
|
268
|
+
* - preset : `wyvrn-<profileName>` (from --profile or config.defaultProfile)
|
|
269
|
+
* - config : Release
|
|
270
|
+
* - auto-install when toolchain is missing / older than wyvrn.lock
|
|
271
|
+
*
|
|
272
|
+
* @param {object} argv
|
|
273
|
+
*/
|
|
274
|
+
async function build(argv) {
|
|
275
|
+
// Shared per-invocation state. See src/context.js +
|
|
276
|
+
// claude/PLAN-COMMAND-CONTEXT.md. Parses --conf up front so a malformed
|
|
277
|
+
// flag fails here instead of after cmake's already configured.
|
|
278
|
+
let ctx;
|
|
279
|
+
try {
|
|
280
|
+
ctx = buildContext(argv);
|
|
281
|
+
} catch (err) {
|
|
282
|
+
log.error(err.message);
|
|
283
|
+
process.exit(1);
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
const { rootDir, manifestPath } = ctx;
|
|
287
|
+
const { name: profileName, profile: activeProfile } = ctx.profiles.host;
|
|
288
|
+
const presetName = resolvePresetName({ presetOverride: argv.preset, profileName });
|
|
289
|
+
|
|
290
|
+
// Resolve binaryDirRoot the same way install does (CLI > overlay >
|
|
291
|
+
// default) so `cmake --build <binaryDir>` lands at the same path the
|
|
292
|
+
// baked preset configured into. A mismatch (e.g. install ran with
|
|
293
|
+
// --build-dir foo but build did not) surfaces as a "no CMakeCache.txt"
|
|
294
|
+
// error from cmake — same failure mode as a --generator mismatch
|
|
295
|
+
// today. Persisting the value via wyvrn.local.json sidesteps the
|
|
296
|
+
// need to repeat the flag.
|
|
297
|
+
let binaryDirRoot;
|
|
298
|
+
try {
|
|
299
|
+
const overlay = readLocalOverlay(rootDir);
|
|
300
|
+
binaryDirRoot = resolveBinaryDirRoot({
|
|
301
|
+
cliBuildDir: argv.buildDir,
|
|
302
|
+
localOverlayBinaryDirRoot: overlay.binaryDirRoot,
|
|
303
|
+
});
|
|
304
|
+
} catch (err) {
|
|
305
|
+
log.error(err.message);
|
|
306
|
+
process.exit(1);
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
// Resolve the list of configs up front so a malformed --config fails
|
|
310
|
+
// before auto-install kicks off. `--all-configs` consults the recipe
|
|
311
|
+
// (if any); otherwise comma-separated --config, defaulting to Release.
|
|
312
|
+
let configList;
|
|
313
|
+
try {
|
|
314
|
+
const recipe = hasBuildRecipe(ctx.manifest)
|
|
315
|
+
? (() => { try { return normalizeRecipe(ctx.manifest.build, null); } catch { return null; } })()
|
|
316
|
+
: null;
|
|
317
|
+
configList = resolveConfigList({ argv, recipeConfigs: recipe?.configs ?? null });
|
|
318
|
+
} catch (err) {
|
|
319
|
+
log.error(err.message);
|
|
320
|
+
process.exit(1);
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
// ── Stale check / auto-install ────────────────────────────────────────────
|
|
324
|
+
// `--auto-install` gates the "run `wyvrnpm install` first when the
|
|
325
|
+
// toolchain is missing or older than wyvrn.lock" behaviour. The separate
|
|
326
|
+
// `--install` flag below runs `cmake --install` AFTER build; the two are
|
|
327
|
+
// deliberately distinct concepts.
|
|
328
|
+
const staleness = isToolchainStale({ rootDir, manifestPath });
|
|
329
|
+
if (staleness.stale) {
|
|
330
|
+
if (argv.autoInstall === false) {
|
|
331
|
+
log.error(
|
|
332
|
+
`toolchain is stale (${staleness.reason}) and --no-auto-install was passed.\n` +
|
|
333
|
+
' Run: wyvrnpm install',
|
|
334
|
+
);
|
|
335
|
+
process.exit(1);
|
|
336
|
+
}
|
|
337
|
+
log.info(`Toolchain ${staleness.reason} — running install first`);
|
|
338
|
+
await install({
|
|
339
|
+
manifest: argv.manifest,
|
|
340
|
+
root: argv.root,
|
|
341
|
+
profile: argv.profile,
|
|
342
|
+
source: argv.source,
|
|
343
|
+
platform: argv.platform ?? 'win_x64',
|
|
344
|
+
timeout: argv.timeout ?? 300,
|
|
345
|
+
// Forward --build-dir so the auto-install bakes the same
|
|
346
|
+
// binaryDir into the regenerated preset. Without this, `wyvrnpm
|
|
347
|
+
// build --build-dir foo` would auto-install with the default
|
|
348
|
+
// "build" root and then promptly fail when this command tries
|
|
349
|
+
// to read CMakeCache.txt out of `foo/wyvrn-<profile>`.
|
|
350
|
+
buildDir: argv.buildDir,
|
|
351
|
+
});
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
const binaryDir = resolveBinaryDir({ rootDir, presetName, binaryDirRoot });
|
|
355
|
+
|
|
356
|
+
// ── Clean ─────────────────────────────────────────────────────────────────
|
|
357
|
+
if (argv.clean && fs.existsSync(binaryDir)) {
|
|
358
|
+
log.info(`Removing ${binaryDir}`);
|
|
359
|
+
fs.rmSync(binaryDir, { recursive: true, force: true });
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
// ── MSVC env activation (Windows + non-VS generator) ───────────────────
|
|
363
|
+
// When the preset's generator is Ninja / Ninja Multi-Config / Make / etc.
|
|
364
|
+
// and the active profile is MSVC, we need `vcvarsall.bat` to have been
|
|
365
|
+
// sourced — otherwise cmake's compiler detection fails with "CXX compiler
|
|
366
|
+
// identification is unknown". VS generators sidestep this because MSBuild
|
|
367
|
+
// finds MSVC via its own toolset resolution.
|
|
368
|
+
//
|
|
369
|
+
// Same mechanism the source-build path already uses (src/build/msvc-env.js).
|
|
370
|
+
let cmakeEnv = null;
|
|
371
|
+
let effectiveGenerator = argv.generator ?? null;
|
|
372
|
+
let effectiveProfile = null;
|
|
373
|
+
try {
|
|
374
|
+
effectiveProfile = activeProfile;
|
|
375
|
+
// Which generator will the preset actually use? Read it from the
|
|
376
|
+
// project's own recipe if one exists. If not, default to null and let
|
|
377
|
+
// cmake/CMakePresets decide — in which case MSVC env activation is
|
|
378
|
+
// skipped (because VS is usually the default on Windows).
|
|
379
|
+
const manifest = ctx.manifest;
|
|
380
|
+
let presetGenerator = null;
|
|
381
|
+
if (hasBuildRecipe(manifest)) {
|
|
382
|
+
try {
|
|
383
|
+
const recipe = normalizeRecipe(manifest.build, null);
|
|
384
|
+
if (recipe.generators.length > 0) [presetGenerator] = recipe.generators;
|
|
385
|
+
} catch { /* templates need options → skip env activation */ }
|
|
386
|
+
}
|
|
387
|
+
// CLI --generator wins — that is the generator CMake will actually use,
|
|
388
|
+
// so it is the one that determines whether vcvarsall must be activated.
|
|
389
|
+
if (!effectiveGenerator) effectiveGenerator = presetGenerator;
|
|
390
|
+
cmakeEnv = await loadMsvcEnv({
|
|
391
|
+
profile: activeProfile,
|
|
392
|
+
skipForGenerator: effectiveGenerator,
|
|
393
|
+
});
|
|
394
|
+
} catch (err) {
|
|
395
|
+
log.warn(`Could not resolve MSVC env activation: ${err.message}`);
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
// ── Configure ─────────────────────────────────────────────────────────────
|
|
399
|
+
// Auto-emit `-A <platform>` for Visual Studio generators, mapped from the
|
|
400
|
+
// active profile's arch. VS otherwise defaults to x64 regardless of the
|
|
401
|
+
// profile, which breaks x86 builds: CMake produces 64-bit artefacts but
|
|
402
|
+
// find_package() rejects the 32-bit installed dependencies with "version
|
|
403
|
+
// not compatible" (actually a CMAKE_SIZEOF_VOID_P mismatch, not a
|
|
404
|
+
// version-number check).
|
|
405
|
+
let vsPlatform = null;
|
|
406
|
+
if (isVisualStudioGenerator(effectiveGenerator) && effectiveProfile) {
|
|
407
|
+
vsPlatform = archToVsPlatform(effectiveProfile.arch);
|
|
408
|
+
if (vsPlatform) {
|
|
409
|
+
log.info(`Visual Studio generator + arch=${effectiveProfile.arch} → passing -A ${vsPlatform}`);
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
// CLI --conf → -D / -U passthrough (PLAN-CONF.md §7). The preset's
|
|
414
|
+
// baked-in cacheVariables (from the last install) already reflect the
|
|
415
|
+
// recipe + local-overlay layers; CLI conf layers on top for this
|
|
416
|
+
// invocation only. ctx.cliConf is pre-parsed by buildContext; malformed
|
|
417
|
+
// values already threw above.
|
|
418
|
+
const cliConfSets = ctx.cliConf.sets;
|
|
419
|
+
const cliConfUnsets = ctx.cliConf.unsets;
|
|
420
|
+
|
|
421
|
+
const configureArgs = buildConfigureArgs({
|
|
422
|
+
presetName,
|
|
423
|
+
generator: argv.generator,
|
|
424
|
+
vsPlatform,
|
|
425
|
+
confSets: cliConfSets,
|
|
426
|
+
confUnsets: cliConfUnsets,
|
|
427
|
+
});
|
|
428
|
+
log.info(`cmake ${configureArgs.join(' ')}`);
|
|
429
|
+
await runCmake(configureArgs, cmakeEnv);
|
|
430
|
+
|
|
431
|
+
// ── Build (+ optional install) per config ─────────────────────────────────
|
|
432
|
+
// Configure ran once above. Multi-config generators (VS, Xcode, Ninja
|
|
433
|
+
// Multi-Config) produce every config from that one configure step; for
|
|
434
|
+
// single-config generators each iteration simply re-`cmake --build`s the
|
|
435
|
+
// same baked CMAKE_BUILD_TYPE (harmless duplication). `--install` runs
|
|
436
|
+
// per config too — the usual reason to want multi-config is so that a
|
|
437
|
+
// downstream `find_package()` can pick whichever it needs.
|
|
438
|
+
if (configList.length > 1) {
|
|
439
|
+
log.info(`Building ${configList.length} configs: ${configList.join(', ')}`);
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
for (const config of configList) {
|
|
443
|
+
const buildArgs = buildBuildArgs({
|
|
444
|
+
binaryDir,
|
|
445
|
+
config,
|
|
446
|
+
target: argv.target,
|
|
447
|
+
verbose: argv.verbose,
|
|
448
|
+
passthru: argv['--'] ?? [],
|
|
449
|
+
});
|
|
450
|
+
log.info(`cmake ${buildArgs.join(' ')}`);
|
|
451
|
+
await runCmake(buildArgs, cmakeEnv);
|
|
452
|
+
|
|
453
|
+
if (argv.install) {
|
|
454
|
+
const installArgs = buildInstallArgs({
|
|
455
|
+
binaryDir,
|
|
456
|
+
config,
|
|
457
|
+
prefix: argv.installPrefix,
|
|
458
|
+
});
|
|
459
|
+
log.info(`cmake ${installArgs.join(' ')}`);
|
|
460
|
+
await runCmake(installArgs, cmakeEnv);
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
const suffix = argv.install
|
|
465
|
+
? ` + installed${argv.installPrefix ? ` to ${argv.installPrefix}` : ''}`
|
|
466
|
+
: '';
|
|
467
|
+
log.success(`Build complete: ${binaryDir} (${configList.join(', ')})${suffix}`);
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
module.exports = build;
|
|
471
|
+
module.exports.default = build;
|
|
472
|
+
|
|
473
|
+
// Exported for unit tests
|
|
474
|
+
module.exports._helpers = {
|
|
475
|
+
isToolchainStale,
|
|
476
|
+
resolvePresetName,
|
|
477
|
+
resolveBinaryDir,
|
|
478
|
+
resolveConfigList,
|
|
479
|
+
buildConfigureArgs,
|
|
480
|
+
buildBuildArgs,
|
|
481
|
+
buildInstallArgs,
|
|
482
|
+
};
|