wyvrnpm 2.8.1 → 2.10.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 +75 -4
- package/bin/wyvrn.js +93 -2
- package/claude/skills/wyvrnpm.skill +0 -0
- package/package.json +1 -1
- package/src/bootstrap/cookbook.js +196 -0
- package/src/bootstrap/detect.js +150 -0
- package/src/bootstrap/index.js +220 -0
- package/src/bootstrap/version.js +72 -0
- package/src/build/index.js +3 -2
- package/src/commands/add.js +2 -1
- package/src/commands/bootstrap.js +96 -0
- package/src/commands/build.js +109 -44
- package/src/commands/install.js +674 -665
- package/src/commands/link.js +320 -319
- package/src/commands/profile.js +237 -237
- package/src/commands/publish.js +435 -420
- package/src/commands/show.js +18 -13
- package/src/context.js +230 -0
- package/src/http-fetch.js +53 -0
- package/src/providers/file.js +5 -19
- package/src/providers/http.js +26 -26
- package/src/providers/s3.js +29 -25
package/README.md
CHANGED
|
@@ -4,7 +4,7 @@ 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.8.
|
|
7
|
+
> **README version: 2.8.3** — Highlights since 2.4.0: non-ABI build-time `conf` block (CMake cache variables via recipe / team profile / `wyvrn.local.json` / CLI `--conf`), per-package `source` pin for multi-source defense, profile inheritance via `extends`, pattern-based `settingsOverrides` (`boost*: {...}`), `wyvrnpm cache list` / `cache prune` for selective source-build-cache cleanup, `install --dry-run` (JSON plan without side effects), `install --build=always` to force source-rebuild, `--token-env` for CI credentials, CloudFront/WAF-compatible HTTP provider (sends `User-Agent`, surfaces non-404 errors), publish preserves dependency ranges verbatim with lock snapshot moved to informational `publishedLock`, and a raft of security hardening (Zip Slip, MSVC arch allow-list). Full list in `claude/EVALUATION.md`.
|
|
8
8
|
|
|
9
9
|
---
|
|
10
10
|
|
|
@@ -143,6 +143,62 @@ wyvrnpm init --root ./my-project
|
|
|
143
143
|
|
|
144
144
|
---
|
|
145
145
|
|
|
146
|
+
### `wyvrnpm bootstrap`
|
|
147
|
+
|
|
148
|
+
Scaffolds a first-draft `wyvrn.json` for an open-source C++ library so it can be packaged into your wyvrnpm registry with a single review pass instead of hand-writing a manifest from scratch. Clones the upstream git repo, detects `name` / `version` / `kind` from the checkout, applies known-library CMake flag hints (skip tests, skip examples, flip install gates), and prints a TODO report for everything a machine can't decide for you.
|
|
149
|
+
|
|
150
|
+
Designed for batch-converting a list of forked upstreams (fmt, spdlog, zlib, Catch2, etc.) into your private registry.
|
|
151
|
+
|
|
152
|
+
```bash
|
|
153
|
+
# Typical single-repo conversion
|
|
154
|
+
wyvrnpm bootstrap https://github.com/fmtlib/fmt.git --dest ./pkgs/fmt
|
|
155
|
+
cd ./pkgs/fmt # review wyvrn.json, settle TODOs
|
|
156
|
+
wyvrnpm install
|
|
157
|
+
wyvrnpm build --install --all-configs
|
|
158
|
+
wyvrnpm publish --source team-s3
|
|
159
|
+
|
|
160
|
+
# Override the derived name / version / kind
|
|
161
|
+
wyvrnpm bootstrap https://github.com/nlohmann/json.git \
|
|
162
|
+
--dest ./pkgs/nlohmann-json \
|
|
163
|
+
--name nlohmann-json \
|
|
164
|
+
--kind HeaderOnlyLib
|
|
165
|
+
|
|
166
|
+
# Work on a directory you already cloned
|
|
167
|
+
wyvrnpm bootstrap --no-clone --dest ./pkgs/fmt --force
|
|
168
|
+
|
|
169
|
+
# Preview without writing — TODO report only
|
|
170
|
+
wyvrnpm bootstrap https://github.com/google/re2.git --dest /tmp/re2 --dry-run
|
|
171
|
+
|
|
172
|
+
# Batch driver — one shell loop, no wyvrnpm subcommand needed
|
|
173
|
+
while read url; do
|
|
174
|
+
wyvrnpm bootstrap "$url" --dest "./pkgs/$(basename "$url" .git)" --force
|
|
175
|
+
done < urls.txt
|
|
176
|
+
# then review, build, and publish each in a loop of your own
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
**Options**
|
|
180
|
+
|
|
181
|
+
| Option | Default | Description |
|
|
182
|
+
|---|---|---|
|
|
183
|
+
| `<url>` positional | *(required)* | Upstream git URL. Accepts HTTPS or SSH form. |
|
|
184
|
+
| `--dest <dir>` | `basename(url)` | Destination directory for the clone. |
|
|
185
|
+
| `--ref <ref>` | *(remote HEAD)* | Clone at a specific branch / tag / SHA. |
|
|
186
|
+
| `--name` | *(from URL)* | Override the derived package name. |
|
|
187
|
+
| `--pkg-version` | *(nearest git tag)* | Override the version. Named oddly so yargs doesn't confuse it with the built-in `--version`. |
|
|
188
|
+
| `--kind` | *(auto-detect)* | Force `ConsoleApp` / `StaticLib` / `DynamicLib` / `HeaderOnlyLib` / etc. |
|
|
189
|
+
| `--description` | *(TODO placeholder)* | One-line description written into `wyvrn.json`. |
|
|
190
|
+
| `--no-clone` | `false` | Skip the clone step; operate on an already-populated `--dest`. |
|
|
191
|
+
| `--shallow` | `false` | `--depth 1` clone. Faster, but git tag detection degrades — pair with `--pkg-version` when using this. |
|
|
192
|
+
| `--dry-run` | `false` | Print the proposed manifest + TODOs; don't write `wyvrn.json`. |
|
|
193
|
+
| `--force` | `false` | Overwrite an existing destination and/or `wyvrn.json`. |
|
|
194
|
+
| `--format` | `text` | Output format: `text` (human) or `json` (CI — one object on stdout). |
|
|
195
|
+
|
|
196
|
+
The cookbook of per-library hints lives in [src/bootstrap/cookbook.js](src/bootstrap/cookbook.js). Current coverage is ~30 popular libraries (fmt, spdlog, nlohmann/json, yaml-cpp, zlib, Catch2, lz4, brotli, hiredis, redis-plus-plus, GLM, SQLiteCpp, re2, …). Adding a new entry is a two-line change — submit a PR.
|
|
197
|
+
|
|
198
|
+
The patterns bootstrap detects (header-only with INTERFACE target, standard CMake static lib, CMake with wyvrnpm dep, ABI-option'd lib, non-CMake upstream) are catalogued in the bundled Agent Skill at [claude/skills/wyvrnpm/references/oss-conversion-patterns.md](claude/skills/wyvrnpm/references/oss-conversion-patterns.md) — Claude reads it when you ask "how do I convert X to a wyvrnpm package?".
|
|
199
|
+
|
|
200
|
+
---
|
|
201
|
+
|
|
146
202
|
### `wyvrnpm add`
|
|
147
203
|
|
|
148
204
|
Adds one or more dependencies to `wyvrn.json` without downloading them. Useful when you know the package name but not the latest version.
|
|
@@ -285,6 +341,13 @@ wyvrnpm build
|
|
|
285
341
|
# Build Debug, verbose compiler output
|
|
286
342
|
wyvrnpm build --config Debug --verbose
|
|
287
343
|
|
|
344
|
+
# Build multiple configs in one go — configure runs once, build loops per config
|
|
345
|
+
wyvrnpm build --config Debug,Release --install
|
|
346
|
+
|
|
347
|
+
# Build every config the recipe declares (recipe's build.configs, or all four
|
|
348
|
+
# canonical configs if no recipe is present). Pairs naturally with --install.
|
|
349
|
+
wyvrnpm build --all-configs --install
|
|
350
|
+
|
|
288
351
|
# Use a specific profile's preset
|
|
289
352
|
wyvrnpm build --profile release
|
|
290
353
|
|
|
@@ -318,7 +381,8 @@ wyvrnpm build -- -j 8
|
|
|
318
381
|
|---|---|---|
|
|
319
382
|
| `--preset` / `-P` | `wyvrn-<profile>` | CMake configure preset name. |
|
|
320
383
|
| `--profile` / `-p` | *(config / "default")* | Build profile — determines the default preset name. |
|
|
321
|
-
| `--config` / `-c` | `Release` | Build configuration (`Debug` \| `Release` \| `RelWithDebInfo` \| `MinSizeRel`). |
|
|
384
|
+
| `--config` / `-c` | `Release` | Build configuration (`Debug` \| `Release` \| `RelWithDebInfo` \| `MinSizeRel`). Comma-separated to build multiple in one invocation (e.g. `Debug,Release`) — configure runs once, build (+ `--install`) loops per config. |
|
|
385
|
+
| `--all-configs` | `false` | Build every config from the recipe's `build.configs` (or all four canonical configs when no recipe is present). Takes precedence over `--config`. |
|
|
322
386
|
| `--target` / `-t` | *(all)* | Specific CMake target to build. |
|
|
323
387
|
| `--verbose` / `-v` | `false` | Pass `--verbose` to `cmake --build`. |
|
|
324
388
|
| `--clean` | `false` | Remove the build directory before configuring. |
|
|
@@ -546,8 +610,6 @@ You only need this if you want Claude Code to pick up the skill. The CLI itself
|
|
|
546
610
|
| `--force` | `false` | Overwrite an existing skill install. Without it, the command refuses to clobber. |
|
|
547
611
|
| `--dry-run` | `false` | Print the destination paths and intended actions without writing. |
|
|
548
612
|
|
|
549
|
-
**CLAUDE.md §9 note for contributors:** the tested path for deploying a locally-edited skill is `node scripts/repack-skill.js && node bin/wyvrn.js install-skill --claude --force` — never `rm -rf` + `cp -r`.
|
|
550
|
-
|
|
551
613
|
---
|
|
552
614
|
|
|
553
615
|
### `wyvrnpm link`
|
|
@@ -1736,6 +1798,15 @@ wyvrnpm build --install --install-prefix C:\out\stage
|
|
|
1736
1798
|
|
|
1737
1799
|
# Install a specific config
|
|
1738
1800
|
wyvrnpm build --install --config Debug
|
|
1801
|
+
|
|
1802
|
+
# Install multiple configs in one invocation — configure runs once,
|
|
1803
|
+
# build + install loops per config. Before 2.9.0 this required two
|
|
1804
|
+
# separate `wyvrnpm build --install ...` invocations.
|
|
1805
|
+
wyvrnpm build --install --config Debug,Release
|
|
1806
|
+
|
|
1807
|
+
# Install every config declared in the recipe's build.configs
|
|
1808
|
+
# (or all four canonical configs when no recipe is present).
|
|
1809
|
+
wyvrnpm build --install --all-configs
|
|
1739
1810
|
```
|
|
1740
1811
|
|
|
1741
1812
|
### What it does, in order
|
package/bin/wyvrn.js
CHANGED
|
@@ -14,6 +14,7 @@ const build = require('../src/commands/build');
|
|
|
14
14
|
const show = require('../src/commands/show');
|
|
15
15
|
const installSkill = require('../src/commands/install-skill');
|
|
16
16
|
const cache = require('../src/commands/cache');
|
|
17
|
+
const bootstrap = require('../src/commands/bootstrap');
|
|
17
18
|
const { link, unlink } = require('../src/commands/link');
|
|
18
19
|
|
|
19
20
|
yargs
|
|
@@ -37,6 +38,83 @@ yargs
|
|
|
37
38
|
(argv) => init(argv),
|
|
38
39
|
)
|
|
39
40
|
|
|
41
|
+
// ── bootstrap ─────────────────────────────────────────────────────────────
|
|
42
|
+
.command(
|
|
43
|
+
'bootstrap <url> [dest]',
|
|
44
|
+
'Scaffold a first-draft wyvrn.json for an OSS C++ library. Shallow-clones ' +
|
|
45
|
+
'(or reuses with --no-clone), detects name/version/kind, applies known-library ' +
|
|
46
|
+
'cookbook hints, and prints TODOs for gotchas worth a human look before publish. ' +
|
|
47
|
+
'Designed for batch-converting upstream repos to wyvrnpm packages.',
|
|
48
|
+
(y) => {
|
|
49
|
+
y
|
|
50
|
+
.positional('url', {
|
|
51
|
+
type: 'string',
|
|
52
|
+
describe: 'Upstream git URL (e.g. https://github.com/fmtlib/fmt.git)',
|
|
53
|
+
})
|
|
54
|
+
.positional('dest', {
|
|
55
|
+
type: 'string',
|
|
56
|
+
describe: 'Destination directory (default: basename(url))',
|
|
57
|
+
})
|
|
58
|
+
.option('dest', {
|
|
59
|
+
type: 'string',
|
|
60
|
+
description: 'Destination directory (alias for the positional).',
|
|
61
|
+
})
|
|
62
|
+
.option('ref', {
|
|
63
|
+
type: 'string',
|
|
64
|
+
description: 'Clone at this branch/tag/SHA (default: the remote HEAD).',
|
|
65
|
+
})
|
|
66
|
+
.option('name', {
|
|
67
|
+
type: 'string',
|
|
68
|
+
description: 'Override the derived package name.',
|
|
69
|
+
})
|
|
70
|
+
.option('pkg-version', {
|
|
71
|
+
type: 'string',
|
|
72
|
+
description: 'Override the wyvrnpm package version (otherwise: nearest git tag padded to 4 parts). Not named --version to avoid colliding with yargs\'s built-in --version flag.',
|
|
73
|
+
})
|
|
74
|
+
.option('shallow', {
|
|
75
|
+
type: 'boolean',
|
|
76
|
+
default: false,
|
|
77
|
+
description: 'Use a shallow (`--depth 1`) clone. Faster, but git tag detection degrades to a TODO — pass --pkg-version when combining with this.',
|
|
78
|
+
})
|
|
79
|
+
.option('kind', {
|
|
80
|
+
type: 'string',
|
|
81
|
+
choices: ['ConsoleApp', 'StaticLib', 'DynamicLib', 'HeaderOnlyLib', 'WebApp', 'Utility', 'Service', 'TestProject'],
|
|
82
|
+
description: 'Override the detected kind.',
|
|
83
|
+
})
|
|
84
|
+
.option('description', {
|
|
85
|
+
type: 'string',
|
|
86
|
+
description: 'One-line description written into the manifest.',
|
|
87
|
+
})
|
|
88
|
+
.option('no-clone', {
|
|
89
|
+
type: 'boolean',
|
|
90
|
+
default: false,
|
|
91
|
+
description: 'Skip cloning — operate on an existing directory at --dest.',
|
|
92
|
+
})
|
|
93
|
+
.option('dry-run', {
|
|
94
|
+
type: 'boolean',
|
|
95
|
+
default: false,
|
|
96
|
+
description: 'Print the proposed manifest + TODO report, do not write wyvrn.json.',
|
|
97
|
+
})
|
|
98
|
+
.option('force', {
|
|
99
|
+
type: 'boolean',
|
|
100
|
+
default: false,
|
|
101
|
+
description: 'Overwrite an existing destination or existing wyvrn.json.',
|
|
102
|
+
})
|
|
103
|
+
.option('format', {
|
|
104
|
+
type: 'string',
|
|
105
|
+
choices: ['text', 'json'],
|
|
106
|
+
default: 'text',
|
|
107
|
+
description: 'Output format for CI pipelines. `json` emits the manifest + TODOs as one JSON object on stdout.',
|
|
108
|
+
});
|
|
109
|
+
},
|
|
110
|
+
(argv) => bootstrap({
|
|
111
|
+
...argv,
|
|
112
|
+
version: argv['pkg-version'],
|
|
113
|
+
noClone: argv['no-clone'],
|
|
114
|
+
dryRun: argv['dry-run'],
|
|
115
|
+
}),
|
|
116
|
+
)
|
|
117
|
+
|
|
40
118
|
// ── add ───────────────────────────────────────────────────────────────────
|
|
41
119
|
.command(
|
|
42
120
|
'add <packages...>',
|
|
@@ -191,9 +269,21 @@ yargs
|
|
|
191
269
|
.option('config', {
|
|
192
270
|
alias: 'c',
|
|
193
271
|
type: 'string',
|
|
194
|
-
choices: ['Debug', 'Release', 'RelWithDebInfo', 'MinSizeRel'],
|
|
195
272
|
default: 'Release',
|
|
196
|
-
description:
|
|
273
|
+
description:
|
|
274
|
+
'Build configuration. Accepts a single value (Release) or a ' +
|
|
275
|
+
'comma-separated list (Debug,Release) to build multiple configs ' +
|
|
276
|
+
'in one invocation — configure runs once, build (+ --install) ' +
|
|
277
|
+
'loops per config. Valid: Debug | Release | RelWithDebInfo | MinSizeRel.',
|
|
278
|
+
})
|
|
279
|
+
.option('all-configs', {
|
|
280
|
+
type: 'boolean',
|
|
281
|
+
default: false,
|
|
282
|
+
description:
|
|
283
|
+
'Build every config declared in the recipe\'s build.configs ' +
|
|
284
|
+
'(falls back to all four canonical configs when no recipe is ' +
|
|
285
|
+
'present). Equivalent to spelling out --config Debug,Release,... ' +
|
|
286
|
+
'by hand. Pairs naturally with --install.',
|
|
197
287
|
})
|
|
198
288
|
.option('target', {
|
|
199
289
|
alias: 't',
|
|
@@ -262,6 +352,7 @@ yargs
|
|
|
262
352
|
...argv,
|
|
263
353
|
autoInstall: argv['auto-install'],
|
|
264
354
|
installPrefix: argv['install-prefix'],
|
|
355
|
+
allConfigs: argv['all-configs'],
|
|
265
356
|
}),
|
|
266
357
|
)
|
|
267
358
|
|
|
Binary file
|
package/package.json
CHANGED
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Known-library cookbook for `wyvrnpm bootstrap`.
|
|
5
|
+
*
|
|
6
|
+
* Each entry captures the non-obvious tweaks a wyvrnpm recipe needs for a
|
|
7
|
+
* popular upstream — the CMake cache variables that disable tests /
|
|
8
|
+
* examples / docs so the install tree doesn't balloon, install gates the
|
|
9
|
+
* upstream hides behind a flag (the `ZLIB_INSTALL=ON` class of gotcha,
|
|
10
|
+
* see SKILL.md references/manifest-authoring.md §build), and any
|
|
11
|
+
* transitive wyvrnpm deps the upstream already depends on.
|
|
12
|
+
*
|
|
13
|
+
* Keyed by a case-insensitive match on the repo tail. Matching is
|
|
14
|
+
* restricted to exact tails so `wyvrnpm bootstrap` never surprises a
|
|
15
|
+
* user whose fork name collides coincidentally — unmatched tails get
|
|
16
|
+
* the safe-default recipe.
|
|
17
|
+
*
|
|
18
|
+
* Keep this file short and dumb. Rich per-library behaviour belongs in
|
|
19
|
+
* the author's actual `wyvrn.json` — the cookbook is just the "everyone
|
|
20
|
+
* hits these flags on day one" cheat sheet.
|
|
21
|
+
*
|
|
22
|
+
* @typedef {Object} CookbookEntry
|
|
23
|
+
* @property {'HeaderOnlyLib'|'StaticLib'|'DynamicLib'} [kind]
|
|
24
|
+
* Override `detect.js` when upstream's CMakeLists lies (e.g. fmt has
|
|
25
|
+
* both a STATIC lib and an INTERFACE `fmt-header-only`).
|
|
26
|
+
* @property {string[]} [configure]
|
|
27
|
+
* `-DKEY=VAL` args threaded into build.configure. Preserves order so a
|
|
28
|
+
* `-DBUILD_SHARED_LIBS=OFF` comes before a `-DXYZ_OPTION=...` override.
|
|
29
|
+
* @property {Record<string,string>} [dependencies]
|
|
30
|
+
* Known wyvrnpm deps — merged into manifest.dependencies. Versions
|
|
31
|
+
* use `"latest"` so the user picks up whatever the team's registry
|
|
32
|
+
* publishes; they can pin in review.
|
|
33
|
+
* @property {string[]} [notes]
|
|
34
|
+
* Human-readable reminders appended to the TODO report.
|
|
35
|
+
*/
|
|
36
|
+
|
|
37
|
+
/** @type {Record<string, CookbookEntry>} */
|
|
38
|
+
const KNOWN_LIBRARIES = Object.freeze({
|
|
39
|
+
// ── Formatting / logging ─────────────────────────────────────────────
|
|
40
|
+
fmt: {
|
|
41
|
+
kind: 'StaticLib',
|
|
42
|
+
configure: ['-DFMT_TEST=OFF', '-DFMT_DOC=OFF'],
|
|
43
|
+
notes: [
|
|
44
|
+
'fmt also ships a `fmt-header-only` INTERFACE target. If you want ' +
|
|
45
|
+
'the header-only variant, declare kind=HeaderOnlyLib and add -DFMT_INSTALL=ON.',
|
|
46
|
+
],
|
|
47
|
+
},
|
|
48
|
+
spdlog: {
|
|
49
|
+
kind: 'StaticLib',
|
|
50
|
+
configure: [
|
|
51
|
+
'-DSPDLOG_BUILD_TESTS=OFF',
|
|
52
|
+
'-DSPDLOG_BUILD_EXAMPLE=OFF',
|
|
53
|
+
'-DSPDLOG_INSTALL=ON',
|
|
54
|
+
'-DSPDLOG_FMT_EXTERNAL=ON',
|
|
55
|
+
],
|
|
56
|
+
dependencies: { fmt: 'latest' },
|
|
57
|
+
notes: [
|
|
58
|
+
'SPDLOG_FMT_EXTERNAL=ON makes spdlog link against the wyvrnpm fmt package ' +
|
|
59
|
+
'instead of its bundled fork. Drop if you want the bundled copy.',
|
|
60
|
+
],
|
|
61
|
+
},
|
|
62
|
+
|
|
63
|
+
// ── JSON / config / serialization ────────────────────────────────────
|
|
64
|
+
json: { kind: 'HeaderOnlyLib', configure: ['-DJSON_BuildTests=OFF'] },
|
|
65
|
+
rapidjson: { kind: 'HeaderOnlyLib', configure: ['-DRAPIDJSON_BUILD_TESTS=OFF', '-DRAPIDJSON_BUILD_EXAMPLES=OFF', '-DRAPIDJSON_BUILD_DOC=OFF'] },
|
|
66
|
+
jsoncpp: { kind: 'StaticLib', configure: ['-DJSONCPP_WITH_TESTS=OFF', '-DJSONCPP_WITH_POST_BUILD_UNITTEST=OFF'] },
|
|
67
|
+
'yaml-cpp':{ kind: 'StaticLib', configure: ['-DYAML_CPP_BUILD_TESTS=OFF', '-DYAML_CPP_BUILD_TOOLS=OFF', '-DYAML_CPP_INSTALL=ON'] },
|
|
68
|
+
pugixml: { kind: 'StaticLib', configure: ['-DPUGIXML_BUILD_TESTS=OFF'] },
|
|
69
|
+
tinyxml2: { kind: 'StaticLib', configure: ['-Dtinyxml2_BUILD_TESTING=OFF'] },
|
|
70
|
+
cereal: { kind: 'HeaderOnlyLib', configure: ['-DJUST_INSTALL_CEREAL=ON'] },
|
|
71
|
+
'msgpack-c': { kind: 'StaticLib', configure: ['-DMSGPACK_BUILD_TESTS=OFF', '-DMSGPACK_BUILD_EXAMPLES=OFF'] },
|
|
72
|
+
|
|
73
|
+
// ── CLI / utilities ──────────────────────────────────────────────────
|
|
74
|
+
cli11: { kind: 'HeaderOnlyLib', configure: ['-DCLI11_BUILD_TESTS=OFF', '-DCLI11_BUILD_EXAMPLES=OFF'] },
|
|
75
|
+
cxxopts: { kind: 'HeaderOnlyLib', configure: ['-DCXXOPTS_BUILD_TESTS=OFF', '-DCXXOPTS_BUILD_EXAMPLES=OFF'] },
|
|
76
|
+
magic_enum: { kind: 'HeaderOnlyLib', configure: ['-DMAGIC_ENUM_OPT_BUILD_EXAMPLES=OFF', '-DMAGIC_ENUM_OPT_BUILD_TESTS=OFF'] },
|
|
77
|
+
date: { kind: 'StaticLib', configure: ['-DBUILD_TZ_LIB=ON', '-DUSE_SYSTEM_TZ_DB=OFF'] },
|
|
78
|
+
utfcpp: { kind: 'HeaderOnlyLib', configure: ['-DUTF8_TESTS=OFF', '-DUTF8_SAMPLES=OFF'] },
|
|
79
|
+
|
|
80
|
+
// ── Testing / benchmarking ──────────────────────────────────────────
|
|
81
|
+
catch2: { kind: 'StaticLib', configure: ['-DCATCH_INSTALL_DOCS=OFF', '-DCATCH_BUILD_TESTING=OFF'] },
|
|
82
|
+
doctest: { kind: 'HeaderOnlyLib', configure: ['-DDOCTEST_WITH_TESTS=OFF', '-DDOCTEST_WITH_MAIN_IN_STATIC_LIB=OFF'] },
|
|
83
|
+
benchmark: {
|
|
84
|
+
kind: 'StaticLib',
|
|
85
|
+
configure: ['-DBENCHMARK_ENABLE_TESTING=OFF', '-DBENCHMARK_ENABLE_GTEST_TESTS=OFF'],
|
|
86
|
+
notes: ['google/benchmark — only one of several projects named "benchmark"; confirm the repo URL.'],
|
|
87
|
+
},
|
|
88
|
+
|
|
89
|
+
// ── Compression ──────────────────────────────────────────────────────
|
|
90
|
+
zlib: {
|
|
91
|
+
kind: 'StaticLib',
|
|
92
|
+
configure: ['-DZLIB_BUILD_EXAMPLES=OFF'],
|
|
93
|
+
notes: [
|
|
94
|
+
'zlib is the textbook example for an `options.shared` toggle — see ' +
|
|
95
|
+
'references/worked-example-zlib.md. Consider declaring it before you publish.',
|
|
96
|
+
],
|
|
97
|
+
},
|
|
98
|
+
lz4: { kind: 'StaticLib', configure: ['-DLZ4_BUILD_LEGACY_LZ4C=OFF', '-DLZ4_BUILD_CLI=OFF', '-DBUILD_SHARED_LIBS=OFF'] },
|
|
99
|
+
snappy: { kind: 'StaticLib', configure: ['-DSNAPPY_BUILD_TESTS=OFF', '-DSNAPPY_BUILD_BENCHMARKS=OFF'] },
|
|
100
|
+
brotli: { kind: 'StaticLib', configure: ['-DBROTLI_DISABLE_TESTS=ON'] },
|
|
101
|
+
bzip2: {
|
|
102
|
+
notes: [
|
|
103
|
+
'bzip2 uses autotools (no CMakeLists.txt). Either ship a wrapper CMakeLists.txt ' +
|
|
104
|
+
'in your fork or prebuild + publish as a binary-only artefact.',
|
|
105
|
+
],
|
|
106
|
+
},
|
|
107
|
+
|
|
108
|
+
// ── Networking / HTTP ────────────────────────────────────────────────
|
|
109
|
+
'cpp-httplib': { kind: 'HeaderOnlyLib', configure: ['-DHTTPLIB_TEST=OFF'] },
|
|
110
|
+
cpr: {
|
|
111
|
+
kind: 'StaticLib',
|
|
112
|
+
configure: ['-DCPR_BUILD_TESTS=OFF', '-DCPR_USE_SYSTEM_CURL=ON'],
|
|
113
|
+
notes: ['cpr needs libcurl — add a wyvrnpm `libcurl` dep if your registry publishes one, or rely on system curl.'],
|
|
114
|
+
},
|
|
115
|
+
websocketpp: {
|
|
116
|
+
kind: 'HeaderOnlyLib',
|
|
117
|
+
notes: ['websocketpp requires boost::asio or standalone Asio — add the appropriate dep.'],
|
|
118
|
+
},
|
|
119
|
+
cppzmq: {
|
|
120
|
+
kind: 'HeaderOnlyLib',
|
|
121
|
+
notes: ['cppzmq needs libzmq — add a wyvrnpm dep for libzmq before publish.'],
|
|
122
|
+
},
|
|
123
|
+
hiredis: { kind: 'StaticLib', configure: ['-DDISABLE_TESTS=ON', '-DENABLE_EXAMPLES=OFF'] },
|
|
124
|
+
'redis-plus-plus': {
|
|
125
|
+
kind: 'StaticLib',
|
|
126
|
+
configure: ['-DREDIS_PLUS_PLUS_BUILD_TEST=OFF', '-DREDIS_PLUS_PLUS_USE_TLS=OFF'],
|
|
127
|
+
dependencies: { hiredis: 'latest' },
|
|
128
|
+
},
|
|
129
|
+
|
|
130
|
+
// ── Concurrency ──────────────────────────────────────────────────────
|
|
131
|
+
concurrentqueue: { kind: 'HeaderOnlyLib' },
|
|
132
|
+
readerwriterqueue: { kind: 'HeaderOnlyLib' },
|
|
133
|
+
|
|
134
|
+
// ── Graphics / image ─────────────────────────────────────────────────
|
|
135
|
+
stb: {
|
|
136
|
+
kind: 'HeaderOnlyLib',
|
|
137
|
+
notes: [
|
|
138
|
+
'stb is a header drop — no CMakeLists.txt upstream. Ship a small wrapper ' +
|
|
139
|
+
'CMakeLists.txt in your fork that declares an INTERFACE target with ' +
|
|
140
|
+
'target_include_directories(... INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}) and install() rules.',
|
|
141
|
+
],
|
|
142
|
+
},
|
|
143
|
+
glm: { kind: 'HeaderOnlyLib', configure: ['-DGLM_TEST_ENABLE=OFF', '-DGLM_BUILD_LIBRARY=OFF'] },
|
|
144
|
+
imgui: {
|
|
145
|
+
notes: [
|
|
146
|
+
'imgui ships no CMakeLists.txt. Ship a wrapper CMakeLists.txt in your fork ' +
|
|
147
|
+
'that picks the backends you need (OpenGL3/Win32/DX11/etc) and declares a ' +
|
|
148
|
+
'StaticLib with them.',
|
|
149
|
+
],
|
|
150
|
+
},
|
|
151
|
+
nuklear: {
|
|
152
|
+
kind: 'HeaderOnlyLib',
|
|
153
|
+
notes: [
|
|
154
|
+
'Nuklear is a single-header drop. Same pattern as stb: ship an INTERFACE ' +
|
|
155
|
+
'wrapper CMakeLists.txt in your fork.',
|
|
156
|
+
],
|
|
157
|
+
},
|
|
158
|
+
|
|
159
|
+
// ── Database ─────────────────────────────────────────────────────────
|
|
160
|
+
sqlitecpp: { kind: 'StaticLib', configure: ['-DSQLITECPP_RUN_CPPLINT=OFF', '-DSQLITECPP_RUN_CPPCHECK=OFF', '-DSQLITECPP_BUILD_TESTS=OFF', '-DSQLITECPP_BUILD_EXAMPLES=OFF'] },
|
|
161
|
+
|
|
162
|
+
// ── Crypto ───────────────────────────────────────────────────────────
|
|
163
|
+
libsodium: {
|
|
164
|
+
notes: [
|
|
165
|
+
'libsodium uses autotools (configure.ac, Makefile.am). wyvrnpm source-build ' +
|
|
166
|
+
'supports cmake only — ship a wrapper CMakeLists.txt in your fork, or ' +
|
|
167
|
+
'prebuild + publish as a binary-only artefact.',
|
|
168
|
+
],
|
|
169
|
+
},
|
|
170
|
+
|
|
171
|
+
// ── Text / regex ─────────────────────────────────────────────────────
|
|
172
|
+
re2: { kind: 'StaticLib', configure: ['-DRE2_BUILD_TESTING=OFF'] },
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* Look up cookbook hints for a given package tail (e.g. "fmt", "zlib").
|
|
177
|
+
* Case-insensitive; returns a defensive copy so callers can merge freely.
|
|
178
|
+
*
|
|
179
|
+
* @param {string} key
|
|
180
|
+
* @returns {CookbookEntry | null}
|
|
181
|
+
*/
|
|
182
|
+
function lookupCookbook(key) {
|
|
183
|
+
if (typeof key !== 'string') return null;
|
|
184
|
+
const normal = key.trim().toLowerCase();
|
|
185
|
+
if (!normal) return null;
|
|
186
|
+
const entry = KNOWN_LIBRARIES[normal];
|
|
187
|
+
if (!entry) return null;
|
|
188
|
+
return {
|
|
189
|
+
...entry,
|
|
190
|
+
configure: entry.configure ? [...entry.configure] : undefined,
|
|
191
|
+
dependencies: entry.dependencies ? { ...entry.dependencies } : undefined,
|
|
192
|
+
notes: entry.notes ? [...entry.notes] : undefined,
|
|
193
|
+
};
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
module.exports = { KNOWN_LIBRARIES, lookupCookbook };
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const fs = require('fs');
|
|
4
|
+
const path = require('path');
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Inspect a cloned repo directory and infer the wyvrnpm `kind`, plus
|
|
8
|
+
* anything else a bootstrap flow can read off the filesystem cheaply.
|
|
9
|
+
*
|
|
10
|
+
* We explicitly do NOT fully parse CMakeLists.txt — a regex scan is good
|
|
11
|
+
* enough for the 80% case, and the bootstrap command's job is to produce
|
|
12
|
+
* a *first draft* that the human reviews before publish. When the signal
|
|
13
|
+
* is ambiguous we return `unknown` and let the TODO report surface it.
|
|
14
|
+
*
|
|
15
|
+
* @typedef {Object} DetectResult
|
|
16
|
+
* @property {'HeaderOnlyLib' | 'StaticLib' | 'DynamicLib' | 'unknown'} kind
|
|
17
|
+
* @property {boolean} hasCMakeLists — root CMakeLists.txt present?
|
|
18
|
+
* @property {boolean} hasConfigureAc — autotools (libsodium, bzip2)?
|
|
19
|
+
* @property {boolean} hasMeson — meson.build present?
|
|
20
|
+
* @property {string[]} notes — human-readable signals for the TODO report.
|
|
21
|
+
*/
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* @param {string} repoDir
|
|
25
|
+
* @returns {DetectResult}
|
|
26
|
+
*/
|
|
27
|
+
function detectFromRepo(repoDir) {
|
|
28
|
+
const notes = [];
|
|
29
|
+
const result = {
|
|
30
|
+
kind: 'unknown',
|
|
31
|
+
hasCMakeLists: false,
|
|
32
|
+
hasConfigureAc: false,
|
|
33
|
+
hasMeson: false,
|
|
34
|
+
notes,
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
if (!fs.existsSync(repoDir) || !fs.statSync(repoDir).isDirectory()) {
|
|
38
|
+
notes.push(`directory does not exist: ${repoDir}`);
|
|
39
|
+
return result;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const cmakePath = path.join(repoDir, 'CMakeLists.txt');
|
|
43
|
+
result.hasCMakeLists = fs.existsSync(cmakePath);
|
|
44
|
+
result.hasConfigureAc = fs.existsSync(path.join(repoDir, 'configure.ac'))
|
|
45
|
+
|| fs.existsSync(path.join(repoDir, 'configure'));
|
|
46
|
+
result.hasMeson = fs.existsSync(path.join(repoDir, 'meson.build'));
|
|
47
|
+
|
|
48
|
+
if (!result.hasCMakeLists) {
|
|
49
|
+
if (result.hasConfigureAc) {
|
|
50
|
+
notes.push(
|
|
51
|
+
'no CMakeLists.txt — upstream uses autotools. wyvrnpm source-build ' +
|
|
52
|
+
'supports cmake only; ship a wrapper CMakeLists.txt in your fork or ' +
|
|
53
|
+
'prebuild and publish as a binary-only artefact.',
|
|
54
|
+
);
|
|
55
|
+
} else if (result.hasMeson) {
|
|
56
|
+
notes.push(
|
|
57
|
+
'no CMakeLists.txt — upstream uses meson. Same workaround as ' +
|
|
58
|
+
'autotools: ship a wrapper CMakeLists.txt in your fork.',
|
|
59
|
+
);
|
|
60
|
+
} else {
|
|
61
|
+
notes.push(
|
|
62
|
+
'no CMakeLists.txt and no recognised build system — likely a header-' +
|
|
63
|
+
'drop upstream (e.g. imgui, nuklear, stb). Add a small CMakeLists.txt ' +
|
|
64
|
+
'in the fork that declares an INTERFACE target + install() rules.',
|
|
65
|
+
);
|
|
66
|
+
}
|
|
67
|
+
return result;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const kindFromCmake = detectKindFromCMakeLists(cmakePath);
|
|
71
|
+
result.kind = kindFromCmake.kind;
|
|
72
|
+
if (kindFromCmake.note) notes.push(kindFromCmake.note);
|
|
73
|
+
return result;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Scan a CMakeLists.txt for the most specific `add_library(<name> <type>)`
|
|
78
|
+
* signal we can find. In order of preference we look for:
|
|
79
|
+
* INTERFACE → HeaderOnlyLib
|
|
80
|
+
* SHARED → DynamicLib
|
|
81
|
+
* STATIC → StaticLib
|
|
82
|
+
* MODULE → StaticLib (rare; treat as static for now)
|
|
83
|
+
*
|
|
84
|
+
* If we find `add_library(<name>)` with no explicit type, CMake's default
|
|
85
|
+
* depends on `BUILD_SHARED_LIBS`. wyvrnpm's convention is to default to
|
|
86
|
+
* `StaticLib` — the consumer can flip via an `options.shared` toggle later.
|
|
87
|
+
*
|
|
88
|
+
* `add_executable(...)` with no `add_library(...)` → the repo ships only a
|
|
89
|
+
* tool, not a library. We return `unknown` so the TODO report surfaces it.
|
|
90
|
+
*
|
|
91
|
+
* @param {string} cmakePath
|
|
92
|
+
* @returns {{ kind: DetectResult['kind'], note?: string }}
|
|
93
|
+
*/
|
|
94
|
+
function detectKindFromCMakeLists(cmakePath) {
|
|
95
|
+
let text;
|
|
96
|
+
try { text = fs.readFileSync(cmakePath, 'utf8'); }
|
|
97
|
+
catch { return { kind: 'unknown', note: `could not read ${cmakePath}` }; }
|
|
98
|
+
|
|
99
|
+
// Strip line comments so an old "# add_library(foo SHARED ...)" doesn't
|
|
100
|
+
// poison detection. Block comments in CMake (#[[ ... ]]) are rare; not
|
|
101
|
+
// worth handling here.
|
|
102
|
+
const stripped = text.replace(/#[^\n]*/g, '');
|
|
103
|
+
|
|
104
|
+
const has = (re) => re.test(stripped);
|
|
105
|
+
|
|
106
|
+
// add_library(<name> [STATIC|SHARED|MODULE|OBJECT|INTERFACE|IMPORTED] ...)
|
|
107
|
+
if (has(/add_library\s*\(\s*[A-Za-z0-9_.+-]+\s+INTERFACE\b/i)) {
|
|
108
|
+
return { kind: 'HeaderOnlyLib' };
|
|
109
|
+
}
|
|
110
|
+
if (has(/add_library\s*\(\s*[A-Za-z0-9_.+-]+\s+SHARED\b/i)) {
|
|
111
|
+
return {
|
|
112
|
+
kind: 'DynamicLib',
|
|
113
|
+
note: 'detected SHARED library — consider declaring an `options.shared` toggle if the upstream respects BUILD_SHARED_LIBS.',
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
if (has(/add_library\s*\(\s*[A-Za-z0-9_.+-]+\s+STATIC\b/i)) {
|
|
117
|
+
return { kind: 'StaticLib' };
|
|
118
|
+
}
|
|
119
|
+
if (has(/add_library\s*\(\s*[A-Za-z0-9_.+-]+\s+(MODULE|OBJECT)\b/i)) {
|
|
120
|
+
return {
|
|
121
|
+
kind: 'StaticLib',
|
|
122
|
+
note: 'detected MODULE/OBJECT library — treating as StaticLib. Review.',
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
// Implicit-default form: `add_library(<name> <sources>...)` — no type
|
|
126
|
+
// keyword after the name. Negative lookahead on the recognised types
|
|
127
|
+
// distinguishes this from the explicit-type cases above (which we've
|
|
128
|
+
// already checked and returned from).
|
|
129
|
+
if (has(/add_library\s*\(\s*[A-Za-z0-9_.+-]+\s+(?!(STATIC|SHARED|MODULE|OBJECT|INTERFACE|IMPORTED)\b)/i)) {
|
|
130
|
+
return {
|
|
131
|
+
kind: 'StaticLib',
|
|
132
|
+
note: 'add_library() with no explicit type — defaulting to StaticLib. ' +
|
|
133
|
+
'If the upstream respects BUILD_SHARED_LIBS, add an `options.shared` toggle.',
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
if (has(/add_executable\s*\(/i)) {
|
|
137
|
+
return {
|
|
138
|
+
kind: 'unknown',
|
|
139
|
+
note: 'repo declares add_executable() but no add_library() — looks like ' +
|
|
140
|
+
'a tool, not a library. Set kind: ConsoleApp in the manifest.',
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
return {
|
|
144
|
+
kind: 'unknown',
|
|
145
|
+
note: 'no add_library() / add_executable() found at the root CMakeLists.txt. ' +
|
|
146
|
+
'The library may be declared in a subdirectory — inspect and set kind manually.',
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
module.exports = { detectFromRepo, detectKindFromCMakeLists };
|