wyvrnpm 2.21.1 → 2.21.3

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/bin/wyvrnpm.js CHANGED
@@ -1,4 +1,4 @@
1
- #!/usr/bin/env node
1
+ #!/usr/bin/env node
2
2
 
3
3
  'use strict';
4
4
 
@@ -161,6 +161,128 @@ else()
161
161
  message(STATUS "Unknown architecture")
162
162
  endif()
163
163
 
164
+ # ── CPU architecture detection ────────────────────────────────────────────────
165
+ # A parallel tier to WYVRN_PLATFORM_* above. CPU family is orthogonal to OS
166
+ # family (an ARM Mac and an x86 Mac are both DARWIN), so it is detected on its
167
+ # own axis rather than nested under any platform. Same idiom as the platform
168
+ # tier: default every leaf to FALSE, derive from the environment, then emit the
169
+ # survivors as compile definitions.
170
+ #
171
+ # WYVRN_CPU_X86_FAMILY – x86 / x86_64 (Intel/AMD)
172
+ # WYVRN_CPU_ARM_FAMILY – ARM / AArch64
173
+ #
174
+ # Derived vector-intrinsics selector — the SSE-vs-NEON switch a VectorRegister
175
+ # dispatch header keys off:
176
+ #
177
+ # WYVRN_ENABLE_VECTORINTRINSICS – SSE/AVX path available (x86 family)
178
+ # WYVRN_ENABLE_VECTORINTRINSICS_NEON – NEON path available (64-bit ARM)
179
+
180
+ set(WYVRN_CPU_X86_FAMILY FALSE)
181
+ set(WYVRN_CPU_ARM_FAMILY FALSE)
182
+ set(WYVRN_ENABLE_VECTORINTRINSICS FALSE)
183
+ set(WYVRN_ENABLE_VECTORINTRINSICS_NEON FALSE)
184
+
185
+ # CMAKE_SYSTEM_PROCESSOR is valid post-project(); its spelling varies by host
186
+ # (AMD64 on MSVC, x86_64 on Linux/Mac, ARM64 on Windows-on-ARM, aarch64 on
187
+ # Linux, arm64 on Apple). Lower-case before matching so one pattern covers all.
188
+ string(TOLOWER "${CMAKE_SYSTEM_PROCESSOR}" _wyvrn_cpu_lc)
189
+ if(_wyvrn_cpu_lc MATCHES "^(x86_64|amd64|x64|em64t|i[3-6]86|x86)$")
190
+ set(WYVRN_CPU_X86_FAMILY TRUE)
191
+ elseif(_wyvrn_cpu_lc MATCHES "^(arm64|aarch64|armv8|armv7|arm)")
192
+ set(WYVRN_CPU_ARM_FAMILY TRUE)
193
+ endif()
194
+
195
+ if(WYVRN_CPU_X86_FAMILY)
196
+ set(WYVRN_ENABLE_VECTORINTRINSICS TRUE)
197
+ elseif(WYVRN_CPU_ARM_FAMILY AND WYVRN_64)
198
+ # 32-bit ARM makes NEON optional; only the 64-bit baseline guarantees it.
199
+ set(WYVRN_ENABLE_VECTORINTRINSICS_NEON TRUE)
200
+ endif()
201
+
202
+ # ── x86 minimum CPU architecture opt-in (analogue of UBT's -MinCpuArchX64) ────
203
+ # Raises the compile-time SIMD floor for the WHOLE build. Off by default (None)
204
+ # so produced binaries run on the broadest x86_64 baseline (SSE2). Selecting a
205
+ # level does two coupled things that MUST stay in lockstep:
206
+ # (a) emits the matching /arch (MSVC) or -m flags (Clang/GCC) so every
207
+ # translation unit is actually codegen'd for that ISA, and
208
+ # (b) raises WYVRN_ALWAYS_HAS_AVX[2|512]/_FMA3 so headers may assume the ISA
209
+ # unconditionally, with no runtime CPUID guard.
210
+ # Applied globally (add_compile_options / add_compile_definitions) because the
211
+ # macro and the codegen flag must agree across every TU — a header that emits an
212
+ # AVX intrinsic under WYVRN_ALWAYS_HAS_AVX must never reach a TU built without
213
+ # /arch:AVX. Runtime detection (GetCPUVendor / HasAVX2) is the separate,
214
+ # portable-dispatch mechanism and is unaffected by this floor.
215
+ # MINSPEC WARNING: a binary built with AVX2 will fault (#UD/SIGILL) on any CPU
216
+ # that lacks it. Raise this only when the deploy target's minimum spec
217
+ # guarantees the level.
218
+ set(WYVRN_MIN_CPU_ARCH_X64 "None" CACHE STRING
219
+ "Minimum x86_64 SIMD ISA the build may assume unconditionally (None|AVX|AVX2|AVX512)")
220
+ set_property(CACHE WYVRN_MIN_CPU_ARCH_X64 PROPERTY STRINGS None AVX AVX2 AVX512)
221
+
222
+ set(WYVRN_ALWAYS_HAS_AVX 0)
223
+ set(WYVRN_ALWAYS_HAS_AVX2 0)
224
+ set(WYVRN_ALWAYS_HAS_AVX512 0)
225
+ set(WYVRN_ALWAYS_HAS_FMA3 0)
226
+
227
+ if(WYVRN_CPU_X86_FAMILY AND NOT WYVRN_MIN_CPU_ARCH_X64 STREQUAL "None")
228
+ if(NOT WYVRN_MIN_CPU_ARCH_X64 MATCHES "^(AVX|AVX2|AVX512)$")
229
+ message(FATAL_ERROR "WYVRN_MIN_CPU_ARCH_X64 must be one of None|AVX|AVX2|AVX512 (got '${WYVRN_MIN_CPU_ARCH_X64}')")
230
+ endif()
231
+
232
+ set(_wyvrn_arch_flags)
233
+ if(MSVC)
234
+ # MSVC /arch is cumulative: /arch:AVX2 implies AVX, /arch:AVX512 implies AVX2.
235
+ list(APPEND _wyvrn_arch_flags /arch:${WYVRN_MIN_CPU_ARCH_X64})
236
+ else()
237
+ # Clang/GCC -m flags are not cumulative — name each level explicitly.
238
+ # FMA3 ships with Haswell-era AVX2, so -mfma rides along from AVX2 up.
239
+ if(WYVRN_MIN_CPU_ARCH_X64 STREQUAL "AVX")
240
+ list(APPEND _wyvrn_arch_flags -mavx)
241
+ elseif(WYVRN_MIN_CPU_ARCH_X64 STREQUAL "AVX2")
242
+ list(APPEND _wyvrn_arch_flags -mavx2 -mfma)
243
+ elseif(WYVRN_MIN_CPU_ARCH_X64 STREQUAL "AVX512")
244
+ list(APPEND _wyvrn_arch_flags -mavx512f -mavx2 -mfma)
245
+ endif()
246
+ endif()
247
+ add_compile_options(${_wyvrn_arch_flags})
248
+
249
+ # Raise the cumulative WYVRN_ALWAYS_HAS_* floor. FMA3 is guaranteed only from
250
+ # AVX2 up (both MSVC /arch:AVX2 and GCC/Clang -mfma).
251
+ set(WYVRN_ALWAYS_HAS_AVX 1)
252
+ if(WYVRN_MIN_CPU_ARCH_X64 STREQUAL "AVX2" OR WYVRN_MIN_CPU_ARCH_X64 STREQUAL "AVX512")
253
+ set(WYVRN_ALWAYS_HAS_AVX2 1)
254
+ set(WYVRN_ALWAYS_HAS_FMA3 1)
255
+ endif()
256
+ if(WYVRN_MIN_CPU_ARCH_X64 STREQUAL "AVX512")
257
+ set(WYVRN_ALWAYS_HAS_AVX512 1)
258
+ endif()
259
+ endif()
260
+
261
+ # -- Emit WYVRN_CPU_* + vector-intrinsics selector compile definitions --
262
+ foreach(_cpu X86_FAMILY ARM_FAMILY)
263
+ if(WYVRN_CPU_${_cpu})
264
+ add_compile_definitions(WYVRN_CPU_${_cpu}=1)
265
+ endif()
266
+ endforeach()
267
+ if(WYVRN_ENABLE_VECTORINTRINSICS)
268
+ add_compile_definitions(WYVRN_ENABLE_VECTORINTRINSICS=1)
269
+ endif()
270
+ if(WYVRN_ENABLE_VECTORINTRINSICS_NEON)
271
+ add_compile_definitions(WYVRN_ENABLE_VECTORINTRINSICS_NEON=1)
272
+ endif()
273
+
274
+ # The AVX floor is always emitted (0 or 1) so headers can `#if WYVRN_ALWAYS_HAS_AVX`
275
+ # without an #ifdef guard, mirroring UE's PLATFORM_ALWAYS_HAS_AVX.
276
+ add_compile_definitions(
277
+ WYVRN_ALWAYS_HAS_AVX=${WYVRN_ALWAYS_HAS_AVX}
278
+ WYVRN_ALWAYS_HAS_AVX2=${WYVRN_ALWAYS_HAS_AVX2}
279
+ WYVRN_ALWAYS_HAS_AVX512=${WYVRN_ALWAYS_HAS_AVX512}
280
+ WYVRN_ALWAYS_HAS_FMA3=${WYVRN_ALWAYS_HAS_FMA3}
281
+ )
282
+
283
+ message(STATUS "CPU family : X86=${WYVRN_CPU_X86_FAMILY} ARM=${WYVRN_CPU_ARM_FAMILY} (processor='${CMAKE_SYSTEM_PROCESSOR}')")
284
+ message(STATUS "CPU intrinsics : VECTORINTRINSICS=${WYVRN_ENABLE_VECTORINTRINSICS} NEON=${WYVRN_ENABLE_VECTORINTRINSICS_NEON} MIN_CPU_ARCH_X64=${WYVRN_MIN_CPU_ARCH_X64} (AVX=${WYVRN_ALWAYS_HAS_AVX} AVX2=${WYVRN_ALWAYS_HAS_AVX2} AVX512=${WYVRN_ALWAYS_HAS_AVX512} FMA3=${WYVRN_ALWAYS_HAS_FMA3})")
285
+
164
286
  # Put compiled artefacts inside the CMake binary dir so `wyvrnpm clean --all`
165
287
  # (which sweeps `<binaryDirRoot>/wyvrn-*/`) catches them. Pre-2.17 wyvrnpm
166
288
  # put these under `${CMAKE_SOURCE_DIR}/output/<bin|lib>${WYVRN_ARCH_SUFFIX}/`
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wyvrnpm",
3
- "version": "2.21.1",
3
+ "version": "2.21.3",
4
4
  "description": "A simple, static-hosting-compatible C++ package manager",
5
5
  "keywords": [
6
6
  "c++",
@@ -122,11 +122,21 @@ function registerGlobal(manifestPath, argv) {
122
122
  /**
123
123
  * Link a package into the current project's wyvrn_internal directory.
124
124
  *
125
+ * The junction always points at `sourcePath` (the install tree, so the
126
+ * consumer's `find_package(<name> CONFIG)` resolves). The wyvrn.lock entry,
127
+ * however, records `manifestPath` when given — the package's source root, which
128
+ * carries wyvrn.json. The install tree carries no manifest, so without this the
129
+ * resolver can't read the dep's declared options or transitive deps. The two
130
+ * are deliberately decoupled: link against the build output, resolve metadata
131
+ * from the source.
132
+ *
125
133
  * @param {string} name - Package name to link.
126
- * @param {string} sourcePath - Source directory path (absolute or relative).
134
+ * @param {string} sourcePath - Junction target (install tree, absolute or relative).
127
135
  * @param {string} rootDir - Project root directory.
136
+ * @param {string} [manifestPath] - Source root recorded in the lock for manifest
137
+ * resolution. Defaults to `sourcePath` when omitted (direct-path links).
128
138
  */
129
- function linkToProject(name, sourcePath, rootDir) {
139
+ function linkToProject(name, sourcePath, rootDir, manifestPath = null) {
130
140
  const absoluteSource = path.resolve(sourcePath);
131
141
 
132
142
  if (!fs.existsSync(absoluteSource)) {
@@ -144,6 +154,11 @@ function linkToProject(name, sourcePath, rootDir) {
144
154
  const versionFile = path.join(linkPath, '.wyvrn-version');
145
155
  fs.writeFileSync(versionFile, 'linked', 'utf8');
146
156
 
157
+ // Record the manifest location in the lock — the source root when known (it
158
+ // carries wyvrn.json), else the junction target. Decoupled from the junction
159
+ // so the resolver reads metadata from source while CMake links the build.
160
+ const lockTarget = manifestPath ? path.resolve(manifestPath) : absoluteSource;
161
+
147
162
  // Update the lock file to record the link
148
163
  const lockPath = path.join(rootDir, 'wyvrn.lock');
149
164
  let lockData = { packages: {} };
@@ -155,11 +170,14 @@ function linkToProject(name, sourcePath, rootDir) {
155
170
  }
156
171
  }
157
172
  lockData.packages = lockData.packages || {};
158
- lockData.packages[name] = `linked:${absoluteSource}`;
173
+ lockData.packages[name] = `linked:${lockTarget}`;
159
174
  lockData.generatedAt = new Date().toISOString();
160
175
  fs.writeFileSync(lockPath, JSON.stringify(lockData, null, 2) + '\n', 'utf8');
161
176
 
162
- log.info(`Linked: ${name} → ${absoluteSource}`);
177
+ log.info(
178
+ `Linked: ${name} → ${absoluteSource}` +
179
+ (lockTarget !== absoluteSource ? ` (manifest: ${lockTarget})` : ''),
180
+ );
163
181
  }
164
182
 
165
183
  /**
@@ -348,7 +366,10 @@ async function link(argv) {
348
366
  effectivePath = getEffectivePath(entry);
349
367
  }
350
368
 
351
- linkToProject(argv.name, effectivePath, rootDir);
369
+ // entry.path is the package's source root (where wyvrn.json was read at
370
+ // registration) — record it as the lock's manifest path while the junction
371
+ // targets the install tree (effectivePath).
372
+ linkToProject(argv.name, effectivePath, rootDir, entry.path);
352
373
  }
353
374
 
354
375
  /**
package/src/resolve.js CHANGED
@@ -50,6 +50,33 @@ function readLocalManifest(packagePath) {
50
50
  return null;
51
51
  }
52
52
 
53
+ /**
54
+ * Resolve a linked package's source-root (manifest) directory from the global
55
+ * link registry.
56
+ *
57
+ * A consumer's wyvrn.lock records a linked dep as `linked:<path>`. Since the
58
+ * link-install-dir change, `<path>` is the package's install tree
59
+ * (`build/wyvrn-<profile>/install`), which carries no wyvrn.json — only built
60
+ * headers/libs/CMake config. When the manifest can't be read from the lock
61
+ * path, this returns the source root recorded in `config.linkedPackages[name]`,
62
+ * where wyvrn.json actually lives, so the resolver can still read the dep's
63
+ * declared options + transitive deps. Returns null when the package isn't in
64
+ * the registry (e.g. a direct-path `wyvrnpm link <name> <path>`).
65
+ *
66
+ * @param {string} name
67
+ * @returns {string|null}
68
+ */
69
+ function linkedRegistrySourcePath(name) {
70
+ try {
71
+ const { readConfig } = require('./config');
72
+ const entry = readConfig().linkedPackages?.[name];
73
+ if (!entry) return null;
74
+ return typeof entry === 'string' ? entry : (entry.path ?? null);
75
+ } catch {
76
+ return null;
77
+ }
78
+ }
79
+
53
80
  /**
54
81
  * Compares two version strings of the form "major.minor.patch.build".
55
82
  * Each missing component is treated as 0.
@@ -447,8 +474,22 @@ async function resolveDependencies(
447
474
  // Handle linked packages: read local manifest instead of fetching from remote
448
475
  let manifest;
449
476
  if (isLinkedVersion(version)) {
450
- const localPath = getLinkedPath(version);
477
+ let localPath = getLinkedPath(version);
451
478
  manifest = readLocalManifest(localPath);
479
+ if (!manifest) {
480
+ // The lock path may be the install tree (no wyvrn.json). Fall back to
481
+ // the package's source root from the global link registry, where the
482
+ // manifest lives — so declared options + transitive deps still resolve
483
+ // without re-linking.
484
+ const sourceRoot = linkedRegistrySourcePath(name);
485
+ if (sourceRoot && sourceRoot !== localPath) {
486
+ const fromSource = readLocalManifest(sourceRoot);
487
+ if (fromSource) {
488
+ manifest = fromSource;
489
+ localPath = sourceRoot;
490
+ }
491
+ }
492
+ }
452
493
  if (manifest) {
453
494
  log.info(`Linked: ${name} → ${localPath}`);
454
495
  } else {