wyvrnpm 2.0.2 → 2.1.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.
@@ -0,0 +1,191 @@
1
+ 'use strict';
2
+
3
+ const fs = require('fs');
4
+ const path = require('path');
5
+
6
+ const VENDOR_KEY = 'wyvrnpm/generated';
7
+ const SCHEMA_VERSION = 3; // requires CMake ≥ 3.21
8
+
9
+ /**
10
+ * Safely parse a JSON file; returns null on any error (missing, malformed, …).
11
+ * @param {string} p
12
+ * @returns {object|null}
13
+ */
14
+ function safeReadJson(p) {
15
+ try {
16
+ return JSON.parse(fs.readFileSync(p, 'utf8'));
17
+ } catch {
18
+ return null;
19
+ }
20
+ }
21
+
22
+ /**
23
+ * True if the parsed presets object was produced by wyvrnpm (i.e. carries our
24
+ * vendor marker). Hand-edited or user-owned files return false.
25
+ *
26
+ * @param {object|null} presets
27
+ * @returns {boolean}
28
+ */
29
+ function isWyvrnGenerated(presets) {
30
+ return !!presets && typeof presets.vendor === 'object' && !!presets.vendor[VENDOR_KEY];
31
+ }
32
+
33
+ /**
34
+ * Build the configure preset object for the active profile.
35
+ *
36
+ * @param {object} args
37
+ * @param {string} args.profileName
38
+ * @param {string} args.profileHash
39
+ * @param {string} args.toolchainRelPath Relative from project root, CMake-style
40
+ * forward slashes.
41
+ */
42
+ function buildConfigurePreset({ profileName, profileHash, toolchainRelPath }) {
43
+ return {
44
+ name: `wyvrn-${profileName}`,
45
+ displayName: `wyvrnpm (${profileName})`,
46
+ description: `Generated by wyvrnpm install — profile hash ${profileHash}`,
47
+ binaryDir: `\${sourceDir}/build/wyvrn-${profileName}`,
48
+ cacheVariables: {
49
+ CMAKE_TOOLCHAIN_FILE: `\${sourceDir}/${toolchainRelPath}`,
50
+ },
51
+ };
52
+ }
53
+
54
+ /**
55
+ * Two build presets per configure preset: Debug and Release.
56
+ *
57
+ * @param {string} profileName
58
+ * @returns {Array<object>}
59
+ */
60
+ function buildBuildPresets(profileName) {
61
+ const config = `wyvrn-${profileName}`;
62
+ return [
63
+ {
64
+ name: `${config}-debug`,
65
+ displayName: `wyvrn ${profileName} (Debug)`,
66
+ configurePreset: config,
67
+ configuration: 'Debug',
68
+ },
69
+ {
70
+ name: `${config}-release`,
71
+ displayName: `wyvrn ${profileName} (Release)`,
72
+ configurePreset: config,
73
+ configuration: 'Release',
74
+ },
75
+ ];
76
+ }
77
+
78
+ /**
79
+ * Build a fresh presets file from scratch (no prior content to merge).
80
+ */
81
+ function buildFreshPresetsFile({ profileName, profileHash, toolchainRelPath }) {
82
+ return {
83
+ version: SCHEMA_VERSION,
84
+ vendor: {
85
+ [VENDOR_KEY]: {
86
+ profileName,
87
+ profileHash,
88
+ generatedAt: new Date().toISOString(),
89
+ },
90
+ },
91
+ configurePresets: [buildConfigurePreset({ profileName, profileHash, toolchainRelPath })],
92
+ buildPresets: buildBuildPresets(profileName),
93
+ };
94
+ }
95
+
96
+ /**
97
+ * Merge the current profile's presets into an existing wyvrnpm-generated
98
+ * file. Presets with matching `name` are replaced; others are preserved so
99
+ * `wyvrnpm install --profile release` followed by `wyvrnpm install --profile
100
+ * debug` leaves both profiles available in the file.
101
+ *
102
+ * @param {object} existing
103
+ * @param {object} args forwarded to the preset builders
104
+ * @returns {object}
105
+ */
106
+ function mergeIntoExisting(existing, { profileName, profileHash, toolchainRelPath }) {
107
+ const newConfigure = buildConfigurePreset({ profileName, profileHash, toolchainRelPath });
108
+ const newBuilds = buildBuildPresets(profileName);
109
+ const newConfigureNames = new Set([newConfigure.name]);
110
+ const newBuildNames = new Set(newBuilds.map((b) => b.name));
111
+
112
+ const configurePresets = (existing.configurePresets ?? []).filter((p) => !newConfigureNames.has(p.name));
113
+ configurePresets.push(newConfigure);
114
+
115
+ const buildPresets = (existing.buildPresets ?? []).filter((p) => !newBuildNames.has(p.name));
116
+ buildPresets.push(...newBuilds);
117
+
118
+ return {
119
+ ...existing,
120
+ version: existing.version ?? SCHEMA_VERSION,
121
+ vendor: {
122
+ ...(existing.vendor ?? {}),
123
+ [VENDOR_KEY]: {
124
+ profileName,
125
+ profileHash,
126
+ generatedAt: new Date().toISOString(),
127
+ },
128
+ },
129
+ configurePresets,
130
+ buildPresets,
131
+ };
132
+ }
133
+
134
+ /**
135
+ * Write a CMakePresets.json (or CMakeUserPresets.json, as fallback) under
136
+ * `projectRoot`. Never overwrites a user-owned file.
137
+ *
138
+ * Resolution order:
139
+ * 1. CMakePresets.json
140
+ * - Does not exist → create it.
141
+ * - Exists, wyvrn-gen → merge into it.
142
+ * - Exists, user-owned → leave alone, fall through.
143
+ * 2. CMakeUserPresets.json
144
+ * - Does not exist → create it.
145
+ * - Exists, wyvrn-gen → merge into it.
146
+ * - Exists, user-owned → skip (action: "skipped").
147
+ *
148
+ * @param {object} args
149
+ * @param {string} args.projectRoot Directory containing top-level CMakeLists.txt.
150
+ * @param {string} args.profileName
151
+ * @param {string} args.profileHash
152
+ * @param {string} args.toolchainRelPath CMake-style forward slashes, relative to projectRoot.
153
+ * @returns {{ path: string|null, action: 'created'|'updated'|'skipped', isUser: boolean }}
154
+ */
155
+ function generateCMakePresets({ projectRoot, profileName, profileHash, toolchainRelPath }) {
156
+ const candidates = [
157
+ { filePath: path.join(projectRoot, 'CMakePresets.json'), isUser: false },
158
+ { filePath: path.join(projectRoot, 'CMakeUserPresets.json'), isUser: true },
159
+ ];
160
+
161
+ for (const { filePath, isUser } of candidates) {
162
+ if (!fs.existsSync(filePath)) {
163
+ const content = buildFreshPresetsFile({ profileName, profileHash, toolchainRelPath });
164
+ fs.writeFileSync(filePath, JSON.stringify(content, null, 2) + '\n', 'utf8');
165
+ return { path: filePath, action: 'created', isUser };
166
+ }
167
+
168
+ const existing = safeReadJson(filePath);
169
+ if (isWyvrnGenerated(existing)) {
170
+ const merged = mergeIntoExisting(existing, { profileName, profileHash, toolchainRelPath });
171
+ fs.writeFileSync(filePath, JSON.stringify(merged, null, 2) + '\n', 'utf8');
172
+ return { path: filePath, action: 'updated', isUser };
173
+ }
174
+
175
+ // User-owned — do not touch, try next candidate.
176
+ }
177
+
178
+ return { path: null, action: 'skipped', isUser: false };
179
+ }
180
+
181
+ module.exports = {
182
+ generateCMakePresets,
183
+ // Exported for tests
184
+ isWyvrnGenerated,
185
+ buildConfigurePreset,
186
+ buildBuildPresets,
187
+ buildFreshPresetsFile,
188
+ mergeIntoExisting,
189
+ VENDOR_KEY,
190
+ SCHEMA_VERSION,
191
+ };
@@ -0,0 +1,66 @@
1
+ # Generated by wyvrnpm — do not edit.
2
+ # Regenerated on every `wyvrnpm install`.
3
+ #
4
+ # Usage:
5
+ # cmake -B build -S . -DCMAKE_TOOLCHAIN_FILE=wyvrn_internal/wyvrn_toolchain.cmake
6
+ #
7
+ # Profile : {{PROFILE_NAME}} [{{PROFILE_HASH}}]
8
+ # Generated: {{GENERATED_AT}}
9
+
10
+ if(WYVRNPM_TOOLCHAIN_INCLUDED)
11
+ return()
12
+ endif()
13
+ set(WYVRNPM_TOOLCHAIN_INCLUDED TRUE)
14
+
15
+ # ── Active build profile (copied verbatim from wyvrn_profile.json) ────────────
16
+ set(WYVRN_PROFILE_NAME "{{PROFILE_NAME}}")
17
+ set(WYVRN_PROFILE_HASH "{{PROFILE_HASH}}")
18
+ set(WYVRN_PROFILE_OS "{{PROFILE_OS}}")
19
+ set(WYVRN_PROFILE_ARCH "{{PROFILE_ARCH}}")
20
+ set(WYVRN_PROFILE_COMPILER "{{PROFILE_COMPILER}}")
21
+ set(WYVRN_PROFILE_COMPILER_VERSION "{{PROFILE_COMPILER_VERSION}}")
22
+ set(WYVRN_PROFILE_CPPSTD "{{PROFILE_CPPSTD}}")
23
+ set(WYVRN_PROFILE_RUNTIME "{{PROFILE_RUNTIME}}")
24
+
25
+ # ── Path to wyvrnpm's bundled CMake utilities (variables.cmake, etc.) ─────────
26
+ # Resolved at generation time so the toolchain works regardless of whether
27
+ # wyvrnpm was installed globally, locally, or via npx.
28
+ set(WYVRNPM_CMAKE_DIR "{{WYVRNPM_CMAKE_DIR}}")
29
+
30
+ # ── C++ standard ──────────────────────────────────────────────────────────────
31
+ if(NOT DEFINED CMAKE_CXX_STANDARD)
32
+ set(CMAKE_CXX_STANDARD {{PROFILE_CPPSTD}})
33
+ set(CMAKE_CXX_STANDARD_REQUIRED ON)
34
+ set(CMAKE_CXX_EXTENSIONS OFF)
35
+ endif()
36
+
37
+ # ── MSVC runtime library ──────────────────────────────────────────────────────
38
+ # Only meaningful when the actual compiler is MSVC. CMake ignores this on other
39
+ # compilers, so setting it unconditionally is safe.
40
+ if("${WYVRN_PROFILE_RUNTIME}" STREQUAL "dynamic")
41
+ set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>DLL")
42
+ elseif("${WYVRN_PROFILE_RUNTIME}" STREQUAL "static")
43
+ set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>")
44
+ endif()
45
+
46
+ # ── Position-independent code (sensible default for mixed static/shared) ──────
47
+ if(NOT DEFINED CMAKE_POSITION_INDEPENDENT_CODE)
48
+ set(CMAKE_POSITION_INDEPENDENT_CODE ON)
49
+ endif()
50
+
51
+ # ── CMAKE_PREFIX_PATH: so find_package(<dep> CONFIG) finds installed deps ─────
52
+ list(PREPEND CMAKE_PREFIX_PATH
53
+ {{PREFIX_PATH_ENTRIES}}
54
+ )
55
+
56
+ # ── CMAKE_MODULE_PATH: so include(<script>) finds bundled *.cmake files ───────
57
+ list(PREPEND CMAKE_MODULE_PATH
58
+ "${WYVRNPM_CMAKE_DIR}"
59
+ )
60
+
61
+ # ── Post-project() hook ───────────────────────────────────────────────────────
62
+ # CMAKE_PROJECT_INCLUDE runs after the first project() call, which is when
63
+ # CMAKE_SYSTEM_NAME / CMAKE_CXX_COMPILER_ID / CMAKE_SIZEOF_VOID_P become valid.
64
+ # The bundled utilities (variables.cmake etc.) rely on those — so they must be
65
+ # included from here, not from the toolchain body above.
66
+ set(CMAKE_PROJECT_INCLUDE "${CMAKE_CURRENT_LIST_DIR}/wyvrn_deps.cmake")