wyvrnpm 2.20.1 → 2.21.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/README.md +51 -7
- package/bin/wyvrnpm.js +3 -2
- package/package.json +1 -1
- package/src/commands/build.js +63 -25
- package/src/commands/link.js +56 -5
- package/src/context.js +1 -1
- package/src/toolchain/presets.js +31 -0
package/README.md
CHANGED
|
@@ -4,7 +4,21 @@ A simple, private C++ package manager that works with any static file hosting pr
|
|
|
4
4
|
|
|
5
5
|
There is no central registry. You control where packages are hosted.
|
|
6
6
|
|
|
7
|
-
> **README version: 2.
|
|
7
|
+
> **README version: 2.20.2**
|
|
8
|
+
>
|
|
9
|
+
> **New in 2.16.0 – 2.20.2:**
|
|
10
|
+
>
|
|
11
|
+
> - **Visual Studio architecture pinning (2.20.0).** A recipe-less Windows/MSVC consumer (one that only lists `dependencies`, no `build` recipe) used to get a generator-less `wyvrn-<profile>` preset, so `cmake --preset` fell through to CMake's implicit Visual Studio default — which targets the **host** arch (x64) when no `-A` is supplied. An `x86` / `armv8` profile therefore silently produced 64-bit binaries. `wyvrnpm install` now detects the default VS generator and bakes both `generator` and `architecture` into the emitted preset, and `wyvrnpm build` derives `-A` (`x86→Win32`, `x86_64→x64`, `armv8→ARM64`, `armv7→ARM`) for VS generators. Builds now match the profile's bitness without a recipe. (Already-published non-host-arch artefacts built via the old source-build path may still hold wrong-bitness binaries — those are rebuilt + republished out of band.)
|
|
12
|
+
> - **`wyvrnpm build` resolves the generator from the emitted preset, not the recipe (2.20.2).** The configure step now reads the generator that `cmake --preset` will *actually* use — precedence: CLI `--generator` > the `generator` baked into `CMakePresets.json` > the detected default for a generator-less preset. This drives both the `-A` pin and the vcvarsall-skip decision, so they stay consistent with reality. Fixes a class of failures where a Ninja Multi-Config preset got `-A x64` ("does not support platform specification") and skipped vcvarsall ("CMAKE_CXX_COMPILER not set") because the old code re-derived the generator from the recipe — which can legitimately diverge from the preset (an install-time `--generator`, or a recipe edited after the last install).
|
|
13
|
+
> - **CMake-path fallback (2.20.1).** `install` / `build` / source-build no longer fail outright on a machine where CMake isn't on `PATH` — `resolveCmakeExe()` falls back to the Windows default `C:\Program Files\CMake\bin\cmake.exe` before surfacing the usual "is CMake installed?" error.
|
|
14
|
+
> - **Machine-wide binary package cache (2.19.0).** Every successful v2 exact-match download is also cached under `%LOCALAPPDATA%\wyvrnpm\pkg\` (14-day TTL) so re-installs across projects skip the network. `wyvrnpm cache list` / `cache prune` gain a `--type src|pkg|all` selector covering both this and the source-build cache, and `wyvrnpm clean --pkg-cache` wipes it.
|
|
15
|
+
> - **`wyvrnpm test` (2.18.0).** A `ctest --preset wyvrn-<profile>-<config>` wrapper that auto-builds when stale, supports `-R` / `-L` filters and `--all-configs`, and emits a `--format json` summary for CI.
|
|
16
|
+
> - **Output directory restructure (2.17.0).** Compiled artefacts now land inside the CMake binary dir (`<binaryDir>/lib`, `<binaryDir>/bin`) instead of `${CMAKE_SOURCE_DIR}/output/`, so `wyvrnpm clean --all` actually catches them. Scripts that hard-coded `output/bin64/…` must switch paths — `clean --all` sweeps the legacy `output/` tree as a migration aid.
|
|
17
|
+
> - **`wyvrnpm clean` flags (2.16.0).** `--build-dir` (also wipe the active profile's CMake build tree), `--all` (scorched-earth: build dirs + `wyvrn-*` presets + legacy `output/`), and `--dry-run` (preview, write nothing).
|
|
18
|
+
>
|
|
19
|
+
> **Planned (not yet shipped):**
|
|
20
|
+
>
|
|
21
|
+
> - **Resolver → provider unification.** Today the dependency resolver (`resolve.js` / `download.js`) talks to registries through a bare `fetch` client that hard-codes the v2/v1 URL layouts, separate from the provider classes that handle publish + download. The plan is to route all metadata lookups through provider methods (`v2GetVersionsIndex` / `v2GetMeta` / `v2GetLatest` / `v1*`) so S3 SigV4, auth resolution, and provider-specific quirks live in one place. Design captured in [.claude-wyvrn-local/plans/2026-04-24-resolver-http-unify.md](.claude-wyvrn-local/plans/2026-04-24-resolver-http-unify.md). The 2.8.4 `Accept-Encoding: identity` hotfix covers the immediate CloudFront cache-split symptom in the meantime.
|
|
8
22
|
>
|
|
9
23
|
> **New in 2.15.0:**
|
|
10
24
|
>
|
|
@@ -371,7 +385,7 @@ After download, `install` also generates:
|
|
|
371
385
|
- `wyvrn_internal/wyvrn_toolchain.cmake` — CMake toolchain pointing at the resolved packages.
|
|
372
386
|
- `wyvrn_internal/wyvrn_deps.cmake` — post-`project()` include for `find_package()` wiring and runtime DLL copy.
|
|
373
387
|
- `wyvrn_internal/wyvrn_profile.json` — snapshot of the active build profile.
|
|
374
|
-
- `CMakePresets.json` (or `CMakeUserPresets.json` if the project root file is user-owned) with a `wyvrn-<profile>` configure preset. Presets for other profiles are preserved so multiple profiles coexist.
|
|
388
|
+
- `CMakePresets.json` (or `CMakeUserPresets.json` if the project root file is user-owned) with a `wyvrn-<profile>` configure preset. Presets for other profiles are preserved so multiple profiles coexist. On Windows/MSVC the preset is pinned to a concrete Visual Studio `generator` **and** `architecture` (derived from the profile arch) even when the project carries no `build` recipe — so a recipe-less `x86` / `armv8` consumer builds the correct bitness instead of CMake's implicit host-x64 default (2.20.0). Declare a `build.generator` (or a `CMakeUserPresets.json`) to opt into Ninja/Make instead.
|
|
375
389
|
|
|
376
390
|
**Toolchain variables exposed to your `CMakeLists.txt` (valid BEFORE `project()`)**:
|
|
377
391
|
|
|
@@ -471,6 +485,8 @@ wyvrnpm build -- -j 8
|
|
|
471
485
|
|
|
472
486
|
Everything after `--` is forwarded verbatim to `cmake --build` (e.g. `-j 8`).
|
|
473
487
|
|
|
488
|
+
**How the effective generator is resolved (2.20.2+).** `wyvrnpm build` decides the `-A` arch pin and the vcvarsall-skip from the generator `cmake --preset` will *actually* use — not the project's `build` recipe. Precedence: CLI `--generator` (wins, passed as `-G`) → the `generator` baked into `CMakePresets.json` at install time → the detected default Visual Studio generator for a generator-less preset. Trusting the preset matters because it can legitimately diverge from the recipe (an install-time `--generator`, or a recipe edited after the last install); pinning `-A` against a non-VS preset is a hard CMake error, so the two decisions must agree with what cmake runs.
|
|
489
|
+
|
|
474
490
|
---
|
|
475
491
|
|
|
476
492
|
### `wyvrnpm test` (2.18.0+)
|
|
@@ -620,16 +636,32 @@ Removes the `wyvrn_internal/` package cache and `wyvrn.lock`.
|
|
|
620
636
|
# Project-local cleanup
|
|
621
637
|
wyvrnpm clean
|
|
622
638
|
|
|
639
|
+
# Also wipe the active profile's CMake build tree
|
|
640
|
+
wyvrnpm clean --build-dir
|
|
641
|
+
|
|
642
|
+
# Scorched earth: build dirs + wyvrn-* presets + legacy output/ tree
|
|
643
|
+
wyvrnpm clean --all
|
|
644
|
+
|
|
623
645
|
# Also wipe the machine-wide source-build cache used by --build=missing
|
|
624
646
|
wyvrnpm clean --build-cache
|
|
647
|
+
|
|
648
|
+
# Also wipe the machine-wide binary package cache
|
|
649
|
+
wyvrnpm clean --pkg-cache
|
|
650
|
+
|
|
651
|
+
# Preview every action without deleting anything
|
|
652
|
+
wyvrnpm clean --all --dry-run
|
|
625
653
|
```
|
|
626
654
|
|
|
627
655
|
**Options**
|
|
628
656
|
|
|
629
657
|
| Option | Default | Description |
|
|
630
658
|
|---|---|---|
|
|
659
|
+
| `--build-dir` | `false` | Also remove the active profile's CMake build tree at `<binaryDirRoot>/wyvrn-<profile>/`. |
|
|
660
|
+
| `--all` | `false` | Default scrub **plus** every `<binaryDirRoot>/wyvrn-*/` build dir, the `wyvrn-*` entries stripped from `CMakePresets.json`, and the legacy `<root>/output/` tree (2.17.0 migration aid). |
|
|
631
661
|
| `--build-cache` | `false` | Also remove the source-build cache under `%LOCALAPPDATA%\wyvrnpm\bc\` (Unix: `~/.cache/wyvrnpm/bc/`). |
|
|
632
|
-
| `--
|
|
662
|
+
| `--pkg-cache` | `false` | Also remove the machine-wide binary package cache under `%LOCALAPPDATA%\wyvrnpm\pkg\` (2.19.0). Opt-in only — never wiped by the default scrub or `--all`. |
|
|
663
|
+
| `--uploaded-built` | `false` | Wipe only the local record of what this machine has uploaded via `--upload-built` (the `.uploaded-to.json` sidecars). Does **not** touch the registry, the artefacts, or `wyvrn_internal/`. When passed alone, the default scrub is skipped. |
|
|
664
|
+
| `--dry-run` | `false` | Log `Would remove …` for every action and write nothing. |
|
|
633
665
|
|
|
634
666
|
For selective cache pruning (keep last N variants, drop entries older than N days), use `wyvrnpm cache prune` instead — see below.
|
|
635
667
|
|
|
@@ -637,12 +669,22 @@ For selective cache pruning (keep last N variants, drop entries older than N day
|
|
|
637
669
|
|
|
638
670
|
### `wyvrnpm cache` (2.8.0+)
|
|
639
671
|
|
|
640
|
-
Inspect and selectively prune the machine-wide
|
|
672
|
+
Inspect and selectively prune the two machine-wide caches. Useful once cache size balloons on dev rigs — `clean --build-cache` / `clean --pkg-cache` are the blunt alternatives that wipe everything of one type.
|
|
673
|
+
|
|
674
|
+
| Type | Location | Populated by | TTL |
|
|
675
|
+
|---|---|---|---|
|
|
676
|
+
| `src` | `%LOCALAPPDATA%\wyvrnpm\bc\` (Unix: `~/.cache/wyvrnpm/bc/`) | source-builds (`--build=missing`) | none |
|
|
677
|
+
| `pkg` | `%LOCALAPPDATA%\wyvrnpm\pkg\` | every successful v2 exact-match download (2.19.0+) | 14 days |
|
|
678
|
+
|
|
679
|
+
`--type src|pkg|all` (default `all`) restricts both `list` and `prune` to one cache.
|
|
641
680
|
|
|
642
681
|
```bash
|
|
643
|
-
# List every cached entry as a table
|
|
682
|
+
# List every cached entry across both caches as a table
|
|
644
683
|
wyvrnpm cache list
|
|
645
684
|
|
|
685
|
+
# Only the binary package cache
|
|
686
|
+
wyvrnpm cache list --type pkg
|
|
687
|
+
|
|
646
688
|
# Filter by package name (glob)
|
|
647
689
|
wyvrnpm cache list "boost*"
|
|
648
690
|
|
|
@@ -652,8 +694,8 @@ wyvrnpm cache list --format json
|
|
|
652
694
|
# Keep the 2 most-recently-touched variants per (name, version)
|
|
653
695
|
wyvrnpm cache prune --keep-last 2
|
|
654
696
|
|
|
655
|
-
#
|
|
656
|
-
wyvrnpm cache prune --older-than 30d
|
|
697
|
+
# Prune only the source-build cache, dropping entries older than 30 days
|
|
698
|
+
wyvrnpm cache prune --type src --older-than 30d
|
|
657
699
|
|
|
658
700
|
# Combine: keep 1 recent per version AND drop anything stale
|
|
659
701
|
wyvrnpm cache prune --keep-last 1 --older-than 30d
|
|
@@ -667,12 +709,14 @@ wyvrnpm cache prune --keep-last 2 --dry-run
|
|
|
667
709
|
| Option | Default | Description |
|
|
668
710
|
|---|---|---|
|
|
669
711
|
| `[pattern]` / `--pattern` | *(none)* | Glob over package names (`*` for any chars, `?` for one). |
|
|
712
|
+
| `--type` | `all` | Which cache to inspect: `src` (source-build), `pkg` (binary), or `all`. |
|
|
670
713
|
| `--format` | `text` | `text` (table) or `json` (structured payload for CI). |
|
|
671
714
|
|
|
672
715
|
**`cache prune` options**
|
|
673
716
|
|
|
674
717
|
| Option | Default | Description |
|
|
675
718
|
|---|---|---|
|
|
719
|
+
| `--type` | `all` | Which cache to prune: `src` (source-build), `pkg` (binary), or `all`. |
|
|
676
720
|
| `--keep-last <N>` | *(none)* | Retain the N most-recently-touched entries **per (name, version) group**. A four-profile rig keeping-last-2 still retains the two newest profile variants of each version. |
|
|
677
721
|
| `--older-than <dur>` | *(none)* | Remove entries whose mtime is older than the threshold. Format: `<number><unit>`, unit ∈ `s` / `m` / `h` / `d` (e.g. `30d`, `72h`). |
|
|
678
722
|
| `--pattern <glob>` | *(none)* | Restrict scope to package names matching the glob. |
|
package/bin/wyvrnpm.js
CHANGED
|
@@ -1098,8 +1098,9 @@ yargs
|
|
|
1098
1098
|
y
|
|
1099
1099
|
.positional('name', { type: 'string', description: 'Package name to link' })
|
|
1100
1100
|
.positional('path', { type: 'string', description: 'Local path to package' })
|
|
1101
|
-
.option('list',
|
|
1102
|
-
.option('subdir',
|
|
1101
|
+
.option('list', { type: 'boolean', description: 'List all globally registered packages', default: false })
|
|
1102
|
+
.option('subdir', { type: 'string', description: 'Subdirectory within the package to link' })
|
|
1103
|
+
.option('profile', { type: 'string', description: 'Profile whose install tree to register (default: config default)' });
|
|
1103
1104
|
},
|
|
1104
1105
|
(argv) => link(argv),
|
|
1105
1106
|
)
|
package/package.json
CHANGED
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 { readConfigurePresetGenerator } = require('../toolchain/presets');
|
|
10
11
|
const { resolveEffectiveGenerator } = require('../build/default-generator');
|
|
11
12
|
const { resolveCmakeExe } = require('../build/cmake-exe');
|
|
12
13
|
const { LOCAL_OVERLAY_FILENAME, readLocalOverlay } = require('../conf');
|
|
@@ -226,6 +227,54 @@ function buildInstallArgs({ binaryDir, config, prefix }) {
|
|
|
226
227
|
return args;
|
|
227
228
|
}
|
|
228
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
|
+
|
|
229
278
|
// ---------------------------------------------------------------------------
|
|
230
279
|
// Spawn wrapper
|
|
231
280
|
// ---------------------------------------------------------------------------
|
|
@@ -370,32 +419,20 @@ async function build(argv) {
|
|
|
370
419
|
//
|
|
371
420
|
// Same mechanism the source-build path already uses (src/build/msvc-env.js).
|
|
372
421
|
let cmakeEnv = null;
|
|
373
|
-
|
|
374
|
-
let
|
|
422
|
+
const effectiveProfile = activeProfile;
|
|
423
|
+
let effectiveGenerator = null;
|
|
375
424
|
try {
|
|
376
|
-
|
|
377
|
-
//
|
|
378
|
-
// project's
|
|
379
|
-
//
|
|
380
|
-
//
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
} catch { /* templates need options → skip env activation */ }
|
|
388
|
-
}
|
|
389
|
-
// CLI --generator wins, then the recipe's generator, then — on a
|
|
390
|
-
// Windows/MSVC profile with neither — CMake's detected default VS
|
|
391
|
-
// generator. Resolving the default here keeps the `-A` pin below and the
|
|
392
|
-
// vcvarsall-skip decision consistent with the generator `cmake --preset`
|
|
393
|
-
// will actually use (install pins the same generator into the preset).
|
|
394
|
-
// Non-Windows / non-MSVC → null, unchanged from before.
|
|
395
|
-
// See claude/PLAN-VS-ARCH-PIN.md.
|
|
396
|
-
effectiveGenerator = await resolveEffectiveGenerator({
|
|
397
|
-
explicit: effectiveGenerator ?? presetGenerator,
|
|
398
|
-
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,
|
|
399
436
|
});
|
|
400
437
|
cmakeEnv = await loadMsvcEnv({
|
|
401
438
|
profile: activeProfile,
|
|
@@ -486,6 +523,7 @@ module.exports._helpers = {
|
|
|
486
523
|
resolvePresetName,
|
|
487
524
|
resolveBinaryDir,
|
|
488
525
|
resolveConfigList,
|
|
526
|
+
resolveConfigureGenerator,
|
|
489
527
|
buildConfigureArgs,
|
|
490
528
|
buildBuildArgs,
|
|
491
529
|
buildInstallArgs,
|
package/src/commands/link.js
CHANGED
|
@@ -8,6 +8,9 @@ const { createLink, isLink, removeLink, getLinkTarget } = require('../link-utils
|
|
|
8
8
|
const { downloadDependencies } = require('../download');
|
|
9
9
|
const { resolveDependencies } = require('../resolve');
|
|
10
10
|
const { wyvrnFetch } = require('../http-fetch');
|
|
11
|
+
const { resolveBinaryDirRoot } = require('../binary-dir');
|
|
12
|
+
const { readLocalOverlay } = require('../conf');
|
|
13
|
+
const { hasBuildRecipe, DEFAULT_RECIPE } = require('../build/recipe');
|
|
11
14
|
const log = require('../logger');
|
|
12
15
|
|
|
13
16
|
/**
|
|
@@ -37,14 +40,51 @@ function getEffectivePath(entry) {
|
|
|
37
40
|
return entry.path;
|
|
38
41
|
}
|
|
39
42
|
|
|
43
|
+
/**
|
|
44
|
+
* Compute the install-tree subdirectory for a package that declares a build
|
|
45
|
+
* recipe, relative to the package root. Mirrors how `publish` locates the
|
|
46
|
+
* install tree it overlays: `<binaryDirRoot>/wyvrn-<profile>/<installDir>`.
|
|
47
|
+
*
|
|
48
|
+
* The returned path is forward-slash separated and relative — `getEffectivePath`
|
|
49
|
+
* joins it onto the package root to yield the absolute install dir.
|
|
50
|
+
*
|
|
51
|
+
* @param {object} manifest - Parsed wyvrn.json (must have a build recipe).
|
|
52
|
+
* @param {object} config - Resolved config (for defaultProfile fallback).
|
|
53
|
+
* @param {object} argv - Parsed yargs arguments (reads `profile`).
|
|
54
|
+
* @param {string} packagePath - Absolute package root (for the local overlay).
|
|
55
|
+
* @returns {string} Relative, forward-slash install-tree subdir.
|
|
56
|
+
*/
|
|
57
|
+
function installTreeSubdir(manifest, config, argv, packagePath) {
|
|
58
|
+
const overlay = readLocalOverlay(packagePath);
|
|
59
|
+
const binaryDirRoot = resolveBinaryDirRoot({
|
|
60
|
+
localOverlayBinaryDirRoot: overlay.binaryDirRoot,
|
|
61
|
+
});
|
|
62
|
+
const profileName =
|
|
63
|
+
typeof argv.profile === 'string' && argv.profile.length > 0
|
|
64
|
+
? argv.profile
|
|
65
|
+
: config.defaultProfile || 'default';
|
|
66
|
+
const installDir = manifest.build.installDir ?? DEFAULT_RECIPE.installDir;
|
|
67
|
+
return [binaryDirRoot, `wyvrn-${profileName}`, installDir].join('/');
|
|
68
|
+
}
|
|
69
|
+
|
|
40
70
|
/**
|
|
41
71
|
* Register the current directory's package globally for linking.
|
|
42
72
|
* Reads wyvrn.json to get the package name, stores {name: {path, subdir}} in config.
|
|
43
73
|
*
|
|
74
|
+
* When the package declares a build recipe, the registered target is its
|
|
75
|
+
* install tree (`<binaryDirRoot>/wyvrn-<profile>/<installDir>`) rather than the
|
|
76
|
+
* raw source root. That tree carries the installed headers, libraries, and
|
|
77
|
+
* `<name>Config.cmake` at its root — the exact layout a consumer's
|
|
78
|
+
* `find_package(<name> CONFIG)` expects after the link lands in
|
|
79
|
+
* `wyvrn_internal/<name>/`. Linking the source root would bury the CMake
|
|
80
|
+
* config under `build/` and find_package would miss it, so the link must
|
|
81
|
+
* simulate the packaged artefact, not the working tree. An explicit
|
|
82
|
+
* `--subdir` always wins (monorepo: link a sub-package's own tree).
|
|
83
|
+
*
|
|
44
84
|
* @param {string} manifestPath - Path to wyvrn.json
|
|
45
|
-
* @param {
|
|
85
|
+
* @param {object} argv - Parsed yargs arguments (reads `subdir`, `profile`).
|
|
46
86
|
*/
|
|
47
|
-
function registerGlobal(manifestPath,
|
|
87
|
+
function registerGlobal(manifestPath, argv) {
|
|
48
88
|
const manifest = readManifest(manifestPath);
|
|
49
89
|
const packageName = manifest.name || manifest.Name;
|
|
50
90
|
|
|
@@ -58,14 +98,25 @@ function registerGlobal(manifestPath, subdir) {
|
|
|
58
98
|
config.linkedPackages = config.linkedPackages || {};
|
|
59
99
|
|
|
60
100
|
const entry = { path: packagePath };
|
|
61
|
-
|
|
62
|
-
|
|
101
|
+
let installTreeLinked = false;
|
|
102
|
+
if (argv.subdir) {
|
|
103
|
+
entry.subdir = argv.subdir;
|
|
104
|
+
} else if (hasBuildRecipe(manifest)) {
|
|
105
|
+
entry.subdir = installTreeSubdir(manifest, config, argv, packagePath);
|
|
106
|
+
installTreeLinked = true;
|
|
63
107
|
}
|
|
64
108
|
config.linkedPackages[packageName] = entry;
|
|
65
109
|
writeConfig(config);
|
|
66
110
|
|
|
67
111
|
const effectivePath = getEffectivePath(entry);
|
|
68
112
|
log.info(`Registered "${packageName}" → ${effectivePath}`);
|
|
113
|
+
if (installTreeLinked && !fs.existsSync(effectivePath)) {
|
|
114
|
+
log.warn(
|
|
115
|
+
`install tree not found at ${effectivePath}\n` +
|
|
116
|
+
` Build + install it so consumers' find_package(${packageName}) resolves:\n` +
|
|
117
|
+
` wyvrnpm build --all-configs --install`,
|
|
118
|
+
);
|
|
119
|
+
}
|
|
69
120
|
}
|
|
70
121
|
|
|
71
122
|
/**
|
|
@@ -261,7 +312,7 @@ async function link(argv) {
|
|
|
261
312
|
log.error(`no wyvrn.json found at ${manifestPath}`);
|
|
262
313
|
process.exit(1);
|
|
263
314
|
}
|
|
264
|
-
registerGlobal(manifestPath, argv
|
|
315
|
+
registerGlobal(manifestPath, argv);
|
|
265
316
|
return;
|
|
266
317
|
}
|
|
267
318
|
|
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
|