wyvrnpm 2.20.2 → 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 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.15.0**
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
- | `--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/`. |
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 source-build cache at `%LOCALAPPDATA%\wyvrnpm\bc\` (Unix: `~/.cache/wyvrnpm/bc/`). Useful once `--upload-built` adoption balloons cache size on dev rigs — `clean --build-cache` is the blunt alternative that wipes everything.
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
- # Drop entries older than 30 days
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', { type: 'boolean', description: 'List all globally registered packages', default: false })
1102
- .option('subdir', { type: 'string', description: 'Subdirectory within the package to link' });
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wyvrnpm",
3
- "version": "2.20.2",
3
+ "version": "2.21.0",
4
4
  "description": "A simple, static-hosting-compatible C++ package manager",
5
5
  "keywords": [
6
6
  "c++",
@@ -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 {string} [subdir] - Optional subdirectory within the package to link.
85
+ * @param {object} argv - Parsed yargs arguments (reads `subdir`, `profile`).
46
86
  */
47
- function registerGlobal(manifestPath, subdir) {
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
- if (subdir) {
62
- entry.subdir = subdir;
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.subdir);
315
+ registerGlobal(manifestPath, argv);
265
316
  return;
266
317
  }
267
318