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/build/recipe.js
CHANGED
|
@@ -1,188 +1,188 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
const { substituteTemplateAll } = require('../options');
|
|
4
|
-
const { readRecipeConf } = require('../conf');
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* The `build` section of a source-repo `wyvrn.json` tells wyvrnpm how to
|
|
8
|
-
* build the package from source when `--build=missing` fires. Only
|
|
9
|
-
* `system: "cmake"` is supported in phase 3.
|
|
10
|
-
*
|
|
11
|
-
* Example — single generator (strict; CMake errors if Ninja isn't installed):
|
|
12
|
-
* "build": {
|
|
13
|
-
* "system": "cmake",
|
|
14
|
-
* "generator": "Ninja",
|
|
15
|
-
* "configure": ["-DZLIB_BUILD_EXAMPLES=OFF"],
|
|
16
|
-
* "buildArgs": ["--parallel"],
|
|
17
|
-
* "installDir": "install",
|
|
18
|
-
* "sourceSubdir": "."
|
|
19
|
-
* }
|
|
20
|
-
*
|
|
21
|
-
* Example — generator fallback chain (tried in order, first available wins;
|
|
22
|
-
* falls through to CMake's default if none are present):
|
|
23
|
-
* "build": {
|
|
24
|
-
* "generator": ["Ninja", "Visual Studio 17 2022", "Visual Studio 16 2019"]
|
|
25
|
-
* }
|
|
26
|
-
*/
|
|
27
|
-
|
|
28
|
-
const DEFAULT_RECIPE = Object.freeze({
|
|
29
|
-
system: 'cmake',
|
|
30
|
-
// Null / empty array → let CMake pick its default
|
|
31
|
-
// (Visual Studio on Windows with VS installed, Unix Makefiles on Linux/macOS).
|
|
32
|
-
generators: Object.freeze([]),
|
|
33
|
-
// All four CMake configurations are built and installed by default so
|
|
34
|
-
// downstream consumers can link against whichever they need. Restrict via
|
|
35
|
-
// the recipe (e.g. `"configs": ["Release"]`) to cut source-build time.
|
|
36
|
-
configs: Object.freeze(['Debug', 'Release', 'RelWithDebInfo', 'MinSizeRel']),
|
|
37
|
-
configure: Object.freeze([]),
|
|
38
|
-
buildArgs: Object.freeze([]),
|
|
39
|
-
installDir: 'install', // relative to binary dir; becomes CMAKE_INSTALL_PREFIX
|
|
40
|
-
sourceSubdir: '.', // where CMakeLists.txt lives relative to the clone root
|
|
41
|
-
requiredTools: Object.freeze([]),
|
|
42
|
-
});
|
|
43
|
-
|
|
44
|
-
const VALID_CMAKE_CONFIGS = new Set(['Debug', 'Release', 'RelWithDebInfo', 'MinSizeRel']);
|
|
45
|
-
|
|
46
|
-
const SUPPORTED_SYSTEMS = new Set(['cmake']);
|
|
47
|
-
|
|
48
|
-
/**
|
|
49
|
-
* Normalise the `build` section of a manifest into a concrete, complete
|
|
50
|
-
* recipe. Missing fields fall back to `DEFAULT_RECIPE`.
|
|
51
|
-
*
|
|
52
|
-
* `generator` accepts either a string (strict — single generator) or an
|
|
53
|
-
* array of strings (fallback chain — tried in order; first available wins).
|
|
54
|
-
* Both forms produce a `generators: string[]` field in the normalised recipe.
|
|
55
|
-
*
|
|
56
|
-
* When `effectiveOptions` is supplied, `${options.<name>}` tokens inside
|
|
57
|
-
* `configure` and `buildArgs` are expanded using those values — bool → ON/OFF,
|
|
58
|
-
* strings verbatim. An unknown option in a template is a hard error (fail
|
|
59
|
-
* fast so the bad arg never reaches cmake). See src/options.js
|
|
60
|
-
* `substituteTemplate`. When `effectiveOptions` is null (recipe declares
|
|
61
|
-
* no options), any `${options.*}` token in the recipe also fails fast —
|
|
62
|
-
* recipes don't get to reference undeclared options.
|
|
63
|
-
*
|
|
64
|
-
* When `manifestForConfCollisionCheck` is supplied, PLAN-CONF.md §4.7's
|
|
65
|
-
* collision detection runs: any `-DKEY=...` in `build.configure` whose
|
|
66
|
-
* KEY is ALSO declared in recipe-level `conf.cmake.cache` is a hard
|
|
67
|
-
* error. Recipes pick ONE route, avoiding silent duplication.
|
|
68
|
-
*
|
|
69
|
-
* @param {object|null|undefined} rawBuild
|
|
70
|
-
* @param {Record<string, any>|null} [effectiveOptions]
|
|
71
|
-
* @param {object|null} [manifestForConfCollisionCheck]
|
|
72
|
-
* @returns {{ system: string, generators: string[], configure: string[],
|
|
73
|
-
* buildArgs: string[], installDir: string, sourceSubdir: string,
|
|
74
|
-
* requiredTools: string[] }}
|
|
75
|
-
* @throws {Error} when `system` is set but not supported, when a
|
|
76
|
-
* `${options.<name>}` template references an undeclared option,
|
|
77
|
-
* or when `build.configure` collides with `conf.cmake.cache`.
|
|
78
|
-
*/
|
|
79
|
-
function normalizeRecipe(rawBuild, effectiveOptions = null, manifestForConfCollisionCheck = null) {
|
|
80
|
-
const b = rawBuild ?? {};
|
|
81
|
-
const system = b.system ?? DEFAULT_RECIPE.system;
|
|
82
|
-
if (!SUPPORTED_SYSTEMS.has(system)) {
|
|
83
|
-
throw new Error(
|
|
84
|
-
`build.system "${system}" is not supported. ` +
|
|
85
|
-
`Supported: ${[...SUPPORTED_SYSTEMS].join(', ')}.`,
|
|
86
|
-
);
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
// Accept generator as string | string[] | null for user convenience;
|
|
90
|
-
// canonicalise to an array.
|
|
91
|
-
let generators;
|
|
92
|
-
if (typeof b.generator === 'string') {
|
|
93
|
-
generators = b.generator.trim() ? [b.generator] : [];
|
|
94
|
-
} else if (Array.isArray(b.generator)) {
|
|
95
|
-
generators = b.generator.filter((g) => typeof g === 'string' && g.trim());
|
|
96
|
-
} else {
|
|
97
|
-
generators = [];
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
// Drop empty-string entries so a stray `""` in the user's JSON doesn't
|
|
101
|
-
// become an empty-string argument to CMake / the build tool.
|
|
102
|
-
const cleanArray = (arr) =>
|
|
103
|
-
Array.isArray(arr)
|
|
104
|
-
? arr.filter((s) => typeof s === 'string' && s.trim().length > 0)
|
|
105
|
-
: [];
|
|
106
|
-
|
|
107
|
-
// Accept `configs` as string | string[]. Default to all four canonical
|
|
108
|
-
// CMake configurations. Unknown entries are silently dropped so a typo
|
|
109
|
-
// doesn't blow up the build loop.
|
|
110
|
-
let configs;
|
|
111
|
-
if (typeof b.configs === 'string') {
|
|
112
|
-
configs = [b.configs];
|
|
113
|
-
} else if (Array.isArray(b.configs)) {
|
|
114
|
-
configs = b.configs.filter((c) => typeof c === 'string' && c.trim());
|
|
115
|
-
} else {
|
|
116
|
-
configs = DEFAULT_RECIPE.configs.slice();
|
|
117
|
-
}
|
|
118
|
-
configs = configs.filter((c) => VALID_CMAKE_CONFIGS.has(c));
|
|
119
|
-
if (configs.length === 0) configs = DEFAULT_RECIPE.configs.slice();
|
|
120
|
-
|
|
121
|
-
// Expand ${options.<name>} tokens in args the recipe passes to cmake.
|
|
122
|
-
// Only `configure` and `buildArgs` are user-authored arg lists; the
|
|
123
|
-
// other fields are literals.
|
|
124
|
-
const configure = substituteTemplateAll(
|
|
125
|
-
cleanArray(b.configure),
|
|
126
|
-
effectiveOptions,
|
|
127
|
-
'build.configure',
|
|
128
|
-
);
|
|
129
|
-
const buildArgs = substituteTemplateAll(
|
|
130
|
-
cleanArray(b.buildArgs),
|
|
131
|
-
effectiveOptions,
|
|
132
|
-
'build.buildArgs',
|
|
133
|
-
);
|
|
134
|
-
|
|
135
|
-
// PLAN-CONF.md §4.7: recipe-side collision between `build.configure`
|
|
136
|
-
// `-DKEY=...` args and declared `conf.cmake.cache.KEY` is a hard
|
|
137
|
-
// error. Forces the author to pick one route; avoids silent
|
|
138
|
-
// duplication where one route wins opaquely.
|
|
139
|
-
if (manifestForConfCollisionCheck) {
|
|
140
|
-
const recipeConfFlat = readRecipeConf(manifestForConfCollisionCheck);
|
|
141
|
-
const confCacheKeys = new Set(
|
|
142
|
-
Object.keys(recipeConfFlat)
|
|
143
|
-
.filter((k) => k.startsWith('cmake.cache.'))
|
|
144
|
-
.map((k) => k.slice('cmake.cache.'.length)),
|
|
145
|
-
);
|
|
146
|
-
if (confCacheKeys.size > 0) {
|
|
147
|
-
for (const arg of configure) {
|
|
148
|
-
const m = arg.match(/^-D([^=]+)=/);
|
|
149
|
-
if (m && confCacheKeys.has(m[1])) {
|
|
150
|
-
throw new Error(
|
|
151
|
-
`recipe collision: "${m[1]}" is set in both ` +
|
|
152
|
-
`build.configure (as ${JSON.stringify(arg)}) and ` +
|
|
153
|
-
`conf.cmake.cache. Pick one route — see claude/PLAN-CONF.md §4.7.`,
|
|
154
|
-
);
|
|
155
|
-
}
|
|
156
|
-
}
|
|
157
|
-
}
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
return {
|
|
161
|
-
system,
|
|
162
|
-
generators,
|
|
163
|
-
configs,
|
|
164
|
-
configure,
|
|
165
|
-
buildArgs,
|
|
166
|
-
installDir: b.installDir ?? DEFAULT_RECIPE.installDir,
|
|
167
|
-
sourceSubdir: b.sourceSubdir ?? DEFAULT_RECIPE.sourceSubdir,
|
|
168
|
-
requiredTools: cleanArray(b.requiredTools),
|
|
169
|
-
};
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
/**
|
|
173
|
-
* Returns true if a manifest has enough info for source-build — i.e. the
|
|
174
|
-
* `build` section is present (even empty — defaults cover the rest).
|
|
175
|
-
* @param {object} manifest
|
|
176
|
-
* @returns {boolean}
|
|
177
|
-
*/
|
|
178
|
-
function hasBuildRecipe(manifest) {
|
|
179
|
-
return !!manifest && typeof manifest.build === 'object' && manifest.build !== null;
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
module.exports = {
|
|
183
|
-
normalizeRecipe,
|
|
184
|
-
hasBuildRecipe,
|
|
185
|
-
DEFAULT_RECIPE,
|
|
186
|
-
SUPPORTED_SYSTEMS,
|
|
187
|
-
VALID_CMAKE_CONFIGS,
|
|
188
|
-
};
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const { substituteTemplateAll } = require('../options');
|
|
4
|
+
const { readRecipeConf } = require('../conf');
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* The `build` section of a source-repo `wyvrn.json` tells wyvrnpm how to
|
|
8
|
+
* build the package from source when `--build=missing` fires. Only
|
|
9
|
+
* `system: "cmake"` is supported in phase 3.
|
|
10
|
+
*
|
|
11
|
+
* Example — single generator (strict; CMake errors if Ninja isn't installed):
|
|
12
|
+
* "build": {
|
|
13
|
+
* "system": "cmake",
|
|
14
|
+
* "generator": "Ninja",
|
|
15
|
+
* "configure": ["-DZLIB_BUILD_EXAMPLES=OFF"],
|
|
16
|
+
* "buildArgs": ["--parallel"],
|
|
17
|
+
* "installDir": "install",
|
|
18
|
+
* "sourceSubdir": "."
|
|
19
|
+
* }
|
|
20
|
+
*
|
|
21
|
+
* Example — generator fallback chain (tried in order, first available wins;
|
|
22
|
+
* falls through to CMake's default if none are present):
|
|
23
|
+
* "build": {
|
|
24
|
+
* "generator": ["Ninja", "Visual Studio 17 2022", "Visual Studio 16 2019"]
|
|
25
|
+
* }
|
|
26
|
+
*/
|
|
27
|
+
|
|
28
|
+
const DEFAULT_RECIPE = Object.freeze({
|
|
29
|
+
system: 'cmake',
|
|
30
|
+
// Null / empty array → let CMake pick its default
|
|
31
|
+
// (Visual Studio on Windows with VS installed, Unix Makefiles on Linux/macOS).
|
|
32
|
+
generators: Object.freeze([]),
|
|
33
|
+
// All four CMake configurations are built and installed by default so
|
|
34
|
+
// downstream consumers can link against whichever they need. Restrict via
|
|
35
|
+
// the recipe (e.g. `"configs": ["Release"]`) to cut source-build time.
|
|
36
|
+
configs: Object.freeze(['Debug', 'Release', 'RelWithDebInfo', 'MinSizeRel']),
|
|
37
|
+
configure: Object.freeze([]),
|
|
38
|
+
buildArgs: Object.freeze([]),
|
|
39
|
+
installDir: 'install', // relative to binary dir; becomes CMAKE_INSTALL_PREFIX
|
|
40
|
+
sourceSubdir: '.', // where CMakeLists.txt lives relative to the clone root
|
|
41
|
+
requiredTools: Object.freeze([]),
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
const VALID_CMAKE_CONFIGS = new Set(['Debug', 'Release', 'RelWithDebInfo', 'MinSizeRel']);
|
|
45
|
+
|
|
46
|
+
const SUPPORTED_SYSTEMS = new Set(['cmake']);
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Normalise the `build` section of a manifest into a concrete, complete
|
|
50
|
+
* recipe. Missing fields fall back to `DEFAULT_RECIPE`.
|
|
51
|
+
*
|
|
52
|
+
* `generator` accepts either a string (strict — single generator) or an
|
|
53
|
+
* array of strings (fallback chain — tried in order; first available wins).
|
|
54
|
+
* Both forms produce a `generators: string[]` field in the normalised recipe.
|
|
55
|
+
*
|
|
56
|
+
* When `effectiveOptions` is supplied, `${options.<name>}` tokens inside
|
|
57
|
+
* `configure` and `buildArgs` are expanded using those values — bool → ON/OFF,
|
|
58
|
+
* strings verbatim. An unknown option in a template is a hard error (fail
|
|
59
|
+
* fast so the bad arg never reaches cmake). See src/options.js
|
|
60
|
+
* `substituteTemplate`. When `effectiveOptions` is null (recipe declares
|
|
61
|
+
* no options), any `${options.*}` token in the recipe also fails fast —
|
|
62
|
+
* recipes don't get to reference undeclared options.
|
|
63
|
+
*
|
|
64
|
+
* When `manifestForConfCollisionCheck` is supplied, PLAN-CONF.md §4.7's
|
|
65
|
+
* collision detection runs: any `-DKEY=...` in `build.configure` whose
|
|
66
|
+
* KEY is ALSO declared in recipe-level `conf.cmake.cache` is a hard
|
|
67
|
+
* error. Recipes pick ONE route, avoiding silent duplication.
|
|
68
|
+
*
|
|
69
|
+
* @param {object|null|undefined} rawBuild
|
|
70
|
+
* @param {Record<string, any>|null} [effectiveOptions]
|
|
71
|
+
* @param {object|null} [manifestForConfCollisionCheck]
|
|
72
|
+
* @returns {{ system: string, generators: string[], configure: string[],
|
|
73
|
+
* buildArgs: string[], installDir: string, sourceSubdir: string,
|
|
74
|
+
* requiredTools: string[] }}
|
|
75
|
+
* @throws {Error} when `system` is set but not supported, when a
|
|
76
|
+
* `${options.<name>}` template references an undeclared option,
|
|
77
|
+
* or when `build.configure` collides with `conf.cmake.cache`.
|
|
78
|
+
*/
|
|
79
|
+
function normalizeRecipe(rawBuild, effectiveOptions = null, manifestForConfCollisionCheck = null) {
|
|
80
|
+
const b = rawBuild ?? {};
|
|
81
|
+
const system = b.system ?? DEFAULT_RECIPE.system;
|
|
82
|
+
if (!SUPPORTED_SYSTEMS.has(system)) {
|
|
83
|
+
throw new Error(
|
|
84
|
+
`build.system "${system}" is not supported. ` +
|
|
85
|
+
`Supported: ${[...SUPPORTED_SYSTEMS].join(', ')}.`,
|
|
86
|
+
);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// Accept generator as string | string[] | null for user convenience;
|
|
90
|
+
// canonicalise to an array.
|
|
91
|
+
let generators;
|
|
92
|
+
if (typeof b.generator === 'string') {
|
|
93
|
+
generators = b.generator.trim() ? [b.generator] : [];
|
|
94
|
+
} else if (Array.isArray(b.generator)) {
|
|
95
|
+
generators = b.generator.filter((g) => typeof g === 'string' && g.trim());
|
|
96
|
+
} else {
|
|
97
|
+
generators = [];
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// Drop empty-string entries so a stray `""` in the user's JSON doesn't
|
|
101
|
+
// become an empty-string argument to CMake / the build tool.
|
|
102
|
+
const cleanArray = (arr) =>
|
|
103
|
+
Array.isArray(arr)
|
|
104
|
+
? arr.filter((s) => typeof s === 'string' && s.trim().length > 0)
|
|
105
|
+
: [];
|
|
106
|
+
|
|
107
|
+
// Accept `configs` as string | string[]. Default to all four canonical
|
|
108
|
+
// CMake configurations. Unknown entries are silently dropped so a typo
|
|
109
|
+
// doesn't blow up the build loop.
|
|
110
|
+
let configs;
|
|
111
|
+
if (typeof b.configs === 'string') {
|
|
112
|
+
configs = [b.configs];
|
|
113
|
+
} else if (Array.isArray(b.configs)) {
|
|
114
|
+
configs = b.configs.filter((c) => typeof c === 'string' && c.trim());
|
|
115
|
+
} else {
|
|
116
|
+
configs = DEFAULT_RECIPE.configs.slice();
|
|
117
|
+
}
|
|
118
|
+
configs = configs.filter((c) => VALID_CMAKE_CONFIGS.has(c));
|
|
119
|
+
if (configs.length === 0) configs = DEFAULT_RECIPE.configs.slice();
|
|
120
|
+
|
|
121
|
+
// Expand ${options.<name>} tokens in args the recipe passes to cmake.
|
|
122
|
+
// Only `configure` and `buildArgs` are user-authored arg lists; the
|
|
123
|
+
// other fields are literals.
|
|
124
|
+
const configure = substituteTemplateAll(
|
|
125
|
+
cleanArray(b.configure),
|
|
126
|
+
effectiveOptions,
|
|
127
|
+
'build.configure',
|
|
128
|
+
);
|
|
129
|
+
const buildArgs = substituteTemplateAll(
|
|
130
|
+
cleanArray(b.buildArgs),
|
|
131
|
+
effectiveOptions,
|
|
132
|
+
'build.buildArgs',
|
|
133
|
+
);
|
|
134
|
+
|
|
135
|
+
// PLAN-CONF.md §4.7: recipe-side collision between `build.configure`
|
|
136
|
+
// `-DKEY=...` args and declared `conf.cmake.cache.KEY` is a hard
|
|
137
|
+
// error. Forces the author to pick one route; avoids silent
|
|
138
|
+
// duplication where one route wins opaquely.
|
|
139
|
+
if (manifestForConfCollisionCheck) {
|
|
140
|
+
const recipeConfFlat = readRecipeConf(manifestForConfCollisionCheck);
|
|
141
|
+
const confCacheKeys = new Set(
|
|
142
|
+
Object.keys(recipeConfFlat)
|
|
143
|
+
.filter((k) => k.startsWith('cmake.cache.'))
|
|
144
|
+
.map((k) => k.slice('cmake.cache.'.length)),
|
|
145
|
+
);
|
|
146
|
+
if (confCacheKeys.size > 0) {
|
|
147
|
+
for (const arg of configure) {
|
|
148
|
+
const m = arg.match(/^-D([^=]+)=/);
|
|
149
|
+
if (m && confCacheKeys.has(m[1])) {
|
|
150
|
+
throw new Error(
|
|
151
|
+
`recipe collision: "${m[1]}" is set in both ` +
|
|
152
|
+
`build.configure (as ${JSON.stringify(arg)}) and ` +
|
|
153
|
+
`conf.cmake.cache. Pick one route — see claude/PLAN-CONF.md §4.7.`,
|
|
154
|
+
);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
return {
|
|
161
|
+
system,
|
|
162
|
+
generators,
|
|
163
|
+
configs,
|
|
164
|
+
configure,
|
|
165
|
+
buildArgs,
|
|
166
|
+
installDir: b.installDir ?? DEFAULT_RECIPE.installDir,
|
|
167
|
+
sourceSubdir: b.sourceSubdir ?? DEFAULT_RECIPE.sourceSubdir,
|
|
168
|
+
requiredTools: cleanArray(b.requiredTools),
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* Returns true if a manifest has enough info for source-build — i.e. the
|
|
174
|
+
* `build` section is present (even empty — defaults cover the rest).
|
|
175
|
+
* @param {object} manifest
|
|
176
|
+
* @returns {boolean}
|
|
177
|
+
*/
|
|
178
|
+
function hasBuildRecipe(manifest) {
|
|
179
|
+
return !!manifest && typeof manifest.build === 'object' && manifest.build !== null;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
module.exports = {
|
|
183
|
+
normalizeRecipe,
|
|
184
|
+
hasBuildRecipe,
|
|
185
|
+
DEFAULT_RECIPE,
|
|
186
|
+
SUPPORTED_SYSTEMS,
|
|
187
|
+
VALID_CMAKE_CONFIGS,
|
|
188
|
+
};
|
package/src/commands/add.js
CHANGED
|
@@ -1,141 +1,141 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
const path = require('path');
|
|
4
|
-
|
|
5
|
-
const { readManifest, writeManifest } = require('../manifest');
|
|
6
|
-
const { resolveLatestVersion } = require('../resolve');
|
|
7
|
-
const { readConfig } = require('../config');
|
|
8
|
-
const { wyvrnFetch } = require('../http-fetch');
|
|
9
|
-
const log = require('../logger');
|
|
10
|
-
|
|
11
|
-
/**
|
|
12
|
-
* Parse a `<name>[@<version>]` positional into its parts.
|
|
13
|
-
*
|
|
14
|
-
* `@` inside the version (e.g. tags like `latest`) is allowed — only the
|
|
15
|
-
* first `@` is treated as the separator.
|
|
16
|
-
*
|
|
17
|
-
* @param {string} spec
|
|
18
|
-
* @returns {{ name: string, version: string | null }}
|
|
19
|
-
*/
|
|
20
|
-
function parseSpec(spec) {
|
|
21
|
-
const at = spec.indexOf('@');
|
|
22
|
-
if (at === -1) return { name: spec, version: null };
|
|
23
|
-
if (at === 0) throw new Error(`invalid package spec "${spec}" — missing name before "@"`);
|
|
24
|
-
const name = spec.slice(0, at);
|
|
25
|
-
const version = spec.slice(at + 1);
|
|
26
|
-
if (!version) throw new Error(`invalid package spec "${spec}" — missing version after "@"`);
|
|
27
|
-
return { name, version };
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
/**
|
|
31
|
-
* Add one or more dependencies to wyvrn.json.
|
|
32
|
-
*
|
|
33
|
-
* wyvrnpm add <name> → resolves latest, writes "^<latest>"
|
|
34
|
-
* wyvrnpm add <name>@<version> → writes "<version>" verbatim (exact or range)
|
|
35
|
-
*
|
|
36
|
-
* Existing entries are replaced. If the prior entry was an object
|
|
37
|
-
* (`{ version, settings, options }`), its settings/options are preserved —
|
|
38
|
-
* only `version` is updated.
|
|
39
|
-
*
|
|
40
|
-
* Does not run install. The user must `wyvrnpm install` afterwards.
|
|
41
|
-
*
|
|
42
|
-
* @param {object} argv
|
|
43
|
-
*/
|
|
44
|
-
async function add(argv) {
|
|
45
|
-
const manifestPath = path.resolve(argv.manifest);
|
|
46
|
-
const specs = (argv.packages ?? []).map(parseSpec);
|
|
47
|
-
|
|
48
|
-
if (specs.length === 0) {
|
|
49
|
-
log.error('no packages given. Usage: wyvrnpm add <name>[@<version>] [...]');
|
|
50
|
-
process.exit(1);
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
// Install sources — only needed when at least one spec omits @version and
|
|
54
|
-
// we have to hit `latest.json`.
|
|
55
|
-
let packageSources;
|
|
56
|
-
if (argv.source && argv.source.length > 0) {
|
|
57
|
-
packageSources = argv.source;
|
|
58
|
-
} else {
|
|
59
|
-
const config = readConfig();
|
|
60
|
-
packageSources = config.installSources.map((s) => s.url);
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
const needsResolve = specs.some((s) => s.version === null);
|
|
64
|
-
if (needsResolve && packageSources.length === 0) {
|
|
65
|
-
log.error(
|
|
66
|
-
'no install sources configured — cannot look up latest versions.\n' +
|
|
67
|
-
' Pass --source <url>, pin versions explicitly (`name@1.2.3.4`), or run:\n' +
|
|
68
|
-
' wyvrnpm configure add-source --kind install --name ... --url ...',
|
|
69
|
-
);
|
|
70
|
-
process.exit(1);
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
const manifest = readManifest(manifestPath);
|
|
74
|
-
|
|
75
|
-
// Preserve whichever dependencies key the manifest already uses. If neither
|
|
76
|
-
// exists, create lowercase `dependencies` (the v2 convention).
|
|
77
|
-
const depsKey =
|
|
78
|
-
Object.prototype.hasOwnProperty.call(manifest, 'dependencies') ? 'dependencies' :
|
|
79
|
-
Object.prototype.hasOwnProperty.call(manifest, 'Dependencies') ? 'Dependencies' :
|
|
80
|
-
'dependencies';
|
|
81
|
-
|
|
82
|
-
let deps = manifest[depsKey];
|
|
83
|
-
// Legacy array form → object. Editing by name is the whole point of `add`,
|
|
84
|
-
// and round-tripping as an array would silently drop per-dep settings.
|
|
85
|
-
if (Array.isArray(deps)) {
|
|
86
|
-
const obj = {};
|
|
87
|
-
for (const d of deps) {
|
|
88
|
-
const n = d.Name ?? d.name;
|
|
89
|
-
const v = d.Version ?? d.version;
|
|
90
|
-
if (n && v) obj[n] = v;
|
|
91
|
-
}
|
|
92
|
-
deps = obj;
|
|
93
|
-
} else if (!deps || typeof deps !== 'object') {
|
|
94
|
-
deps = {};
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
for (const spec of specs) {
|
|
98
|
-
let versionToWrite;
|
|
99
|
-
if (spec.version) {
|
|
100
|
-
versionToWrite = spec.version;
|
|
101
|
-
log.info(`${spec.name}: writing "${versionToWrite}" (as given)`);
|
|
102
|
-
} else {
|
|
103
|
-
const latest = await resolveLatestVersion(spec.name, packageSources, argv.platform, wyvrnFetch);
|
|
104
|
-
versionToWrite = `^${latest}`;
|
|
105
|
-
log.info(`${spec.name}: latest is ${latest} → writing "${versionToWrite}"`);
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
const prior = deps[spec.name];
|
|
109
|
-
if (prior !== undefined) {
|
|
110
|
-
const priorVer =
|
|
111
|
-
typeof prior === 'string' ? prior :
|
|
112
|
-
(prior && typeof prior === 'object') ? (prior.version ?? prior.Version ?? '(unknown)') :
|
|
113
|
-
'(unknown)';
|
|
114
|
-
log.info(` replacing existing entry: "${priorVer}"`);
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
// If the prior entry was an object with settings/options, keep those —
|
|
118
|
-
// only bump the version field. Otherwise write the short-form string.
|
|
119
|
-
if (prior && typeof prior === 'object' && !Array.isArray(prior)) {
|
|
120
|
-
const updated = { ...prior };
|
|
121
|
-
if ('Version' in updated && !('version' in updated)) {
|
|
122
|
-
updated.Version = versionToWrite;
|
|
123
|
-
} else {
|
|
124
|
-
updated.version = versionToWrite;
|
|
125
|
-
}
|
|
126
|
-
deps[spec.name] = updated;
|
|
127
|
-
} else {
|
|
128
|
-
deps[spec.name] = versionToWrite;
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
manifest[depsKey] = deps;
|
|
133
|
-
writeManifest(manifestPath, manifest);
|
|
134
|
-
|
|
135
|
-
const n = specs.length;
|
|
136
|
-
log.success(`Updated ${manifestPath} — ${n} package${n !== 1 ? 's' : ''}.`);
|
|
137
|
-
log.info('Run `wyvrnpm install` to download.');
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
module.exports = add;
|
|
141
|
-
module.exports.parseSpec = parseSpec;
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const path = require('path');
|
|
4
|
+
|
|
5
|
+
const { readManifest, writeManifest } = require('../manifest');
|
|
6
|
+
const { resolveLatestVersion } = require('../resolve');
|
|
7
|
+
const { readConfig } = require('../config');
|
|
8
|
+
const { wyvrnFetch } = require('../http-fetch');
|
|
9
|
+
const log = require('../logger');
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Parse a `<name>[@<version>]` positional into its parts.
|
|
13
|
+
*
|
|
14
|
+
* `@` inside the version (e.g. tags like `latest`) is allowed — only the
|
|
15
|
+
* first `@` is treated as the separator.
|
|
16
|
+
*
|
|
17
|
+
* @param {string} spec
|
|
18
|
+
* @returns {{ name: string, version: string | null }}
|
|
19
|
+
*/
|
|
20
|
+
function parseSpec(spec) {
|
|
21
|
+
const at = spec.indexOf('@');
|
|
22
|
+
if (at === -1) return { name: spec, version: null };
|
|
23
|
+
if (at === 0) throw new Error(`invalid package spec "${spec}" — missing name before "@"`);
|
|
24
|
+
const name = spec.slice(0, at);
|
|
25
|
+
const version = spec.slice(at + 1);
|
|
26
|
+
if (!version) throw new Error(`invalid package spec "${spec}" — missing version after "@"`);
|
|
27
|
+
return { name, version };
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Add one or more dependencies to wyvrn.json.
|
|
32
|
+
*
|
|
33
|
+
* wyvrnpm add <name> → resolves latest, writes "^<latest>"
|
|
34
|
+
* wyvrnpm add <name>@<version> → writes "<version>" verbatim (exact or range)
|
|
35
|
+
*
|
|
36
|
+
* Existing entries are replaced. If the prior entry was an object
|
|
37
|
+
* (`{ version, settings, options }`), its settings/options are preserved —
|
|
38
|
+
* only `version` is updated.
|
|
39
|
+
*
|
|
40
|
+
* Does not run install. The user must `wyvrnpm install` afterwards.
|
|
41
|
+
*
|
|
42
|
+
* @param {object} argv
|
|
43
|
+
*/
|
|
44
|
+
async function add(argv) {
|
|
45
|
+
const manifestPath = path.resolve(argv.manifest);
|
|
46
|
+
const specs = (argv.packages ?? []).map(parseSpec);
|
|
47
|
+
|
|
48
|
+
if (specs.length === 0) {
|
|
49
|
+
log.error('no packages given. Usage: wyvrnpm add <name>[@<version>] [...]');
|
|
50
|
+
process.exit(1);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// Install sources — only needed when at least one spec omits @version and
|
|
54
|
+
// we have to hit `latest.json`.
|
|
55
|
+
let packageSources;
|
|
56
|
+
if (argv.source && argv.source.length > 0) {
|
|
57
|
+
packageSources = argv.source;
|
|
58
|
+
} else {
|
|
59
|
+
const config = readConfig();
|
|
60
|
+
packageSources = config.installSources.map((s) => s.url);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const needsResolve = specs.some((s) => s.version === null);
|
|
64
|
+
if (needsResolve && packageSources.length === 0) {
|
|
65
|
+
log.error(
|
|
66
|
+
'no install sources configured — cannot look up latest versions.\n' +
|
|
67
|
+
' Pass --source <url>, pin versions explicitly (`name@1.2.3.4`), or run:\n' +
|
|
68
|
+
' wyvrnpm configure add-source --kind install --name ... --url ...',
|
|
69
|
+
);
|
|
70
|
+
process.exit(1);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
const manifest = readManifest(manifestPath);
|
|
74
|
+
|
|
75
|
+
// Preserve whichever dependencies key the manifest already uses. If neither
|
|
76
|
+
// exists, create lowercase `dependencies` (the v2 convention).
|
|
77
|
+
const depsKey =
|
|
78
|
+
Object.prototype.hasOwnProperty.call(manifest, 'dependencies') ? 'dependencies' :
|
|
79
|
+
Object.prototype.hasOwnProperty.call(manifest, 'Dependencies') ? 'Dependencies' :
|
|
80
|
+
'dependencies';
|
|
81
|
+
|
|
82
|
+
let deps = manifest[depsKey];
|
|
83
|
+
// Legacy array form → object. Editing by name is the whole point of `add`,
|
|
84
|
+
// and round-tripping as an array would silently drop per-dep settings.
|
|
85
|
+
if (Array.isArray(deps)) {
|
|
86
|
+
const obj = {};
|
|
87
|
+
for (const d of deps) {
|
|
88
|
+
const n = d.Name ?? d.name;
|
|
89
|
+
const v = d.Version ?? d.version;
|
|
90
|
+
if (n && v) obj[n] = v;
|
|
91
|
+
}
|
|
92
|
+
deps = obj;
|
|
93
|
+
} else if (!deps || typeof deps !== 'object') {
|
|
94
|
+
deps = {};
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
for (const spec of specs) {
|
|
98
|
+
let versionToWrite;
|
|
99
|
+
if (spec.version) {
|
|
100
|
+
versionToWrite = spec.version;
|
|
101
|
+
log.info(`${spec.name}: writing "${versionToWrite}" (as given)`);
|
|
102
|
+
} else {
|
|
103
|
+
const latest = await resolveLatestVersion(spec.name, packageSources, argv.platform, wyvrnFetch);
|
|
104
|
+
versionToWrite = `^${latest}`;
|
|
105
|
+
log.info(`${spec.name}: latest is ${latest} → writing "${versionToWrite}"`);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
const prior = deps[spec.name];
|
|
109
|
+
if (prior !== undefined) {
|
|
110
|
+
const priorVer =
|
|
111
|
+
typeof prior === 'string' ? prior :
|
|
112
|
+
(prior && typeof prior === 'object') ? (prior.version ?? prior.Version ?? '(unknown)') :
|
|
113
|
+
'(unknown)';
|
|
114
|
+
log.info(` replacing existing entry: "${priorVer}"`);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// If the prior entry was an object with settings/options, keep those —
|
|
118
|
+
// only bump the version field. Otherwise write the short-form string.
|
|
119
|
+
if (prior && typeof prior === 'object' && !Array.isArray(prior)) {
|
|
120
|
+
const updated = { ...prior };
|
|
121
|
+
if ('Version' in updated && !('version' in updated)) {
|
|
122
|
+
updated.Version = versionToWrite;
|
|
123
|
+
} else {
|
|
124
|
+
updated.version = versionToWrite;
|
|
125
|
+
}
|
|
126
|
+
deps[spec.name] = updated;
|
|
127
|
+
} else {
|
|
128
|
+
deps[spec.name] = versionToWrite;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
manifest[depsKey] = deps;
|
|
133
|
+
writeManifest(manifestPath, manifest);
|
|
134
|
+
|
|
135
|
+
const n = specs.length;
|
|
136
|
+
log.success(`Updated ${manifestPath} — ${n} package${n !== 1 ? 's' : ''}.`);
|
|
137
|
+
log.info('Run `wyvrnpm install` to download.');
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
module.exports = add;
|
|
141
|
+
module.exports.parseSpec = parseSpec;
|