emsdk-env 0.9.0 → 0.11.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/dist/build-BbwVl0T6.js +1379 -0
- package/dist/build-BbwVl0T6.js.map +1 -0
- package/dist/build-TpHGBYTu.cjs +1388 -0
- package/dist/build-TpHGBYTu.cjs.map +1 -0
- package/dist/build.d.ts +22 -0
- package/dist/build.d.ts.map +1 -0
- package/dist/commands.d.ts +14 -0
- package/dist/commands.d.ts.map +1 -0
- package/dist/emsdk.d.ts +22 -0
- package/dist/emsdk.d.ts.map +1 -0
- package/dist/env.d.ts +16 -0
- package/dist/env.d.ts.map +1 -0
- package/dist/fs-utils.d.ts +13 -0
- package/dist/fs-utils.d.ts.map +1 -0
- package/dist/generated/packageMetadata.d.ts +18 -0
- package/dist/generated/packageMetadata.d.ts.map +1 -0
- package/dist/index.cjs +11 -14
- package/dist/index.d.ts +17 -338
- package/dist/index.d.ts.map +1 -0
- package/dist/index.mjs +10 -15
- package/dist/logger.d.ts +13 -0
- package/dist/logger.d.ts.map +1 -0
- package/dist/{vite.d.ts → types.d.ts} +299 -268
- package/dist/types.d.ts.map +1 -0
- package/dist/vite/index.d.ts +24 -0
- package/dist/vite/index.d.ts.map +1 -0
- package/dist/vite/logger.d.ts +14 -0
- package/dist/vite/logger.d.ts.map +1 -0
- package/dist/vite/types.d.ts +17 -0
- package/dist/vite/types.d.ts.map +1 -0
- package/dist/vite.cjs +735 -712
- package/dist/vite.cjs.map +1 -1
- package/dist/vite.mjs +729 -712
- package/dist/vite.mjs.map +1 -1
- package/package.json +22 -21
- package/dist/build-BOZTStIM.js +0 -1660
- package/dist/build-BOZTStIM.js.map +0 -1
- package/dist/build-CgmcFNSR.cjs +0 -1681
- package/dist/build-CgmcFNSR.cjs.map +0 -1
- package/dist/index.cjs.map +0 -1
- package/dist/index.mjs.map +0 -1
|
@@ -0,0 +1,1379 @@
|
|
|
1
|
+
/*!
|
|
2
|
+
* name: emsdk-env
|
|
3
|
+
* version: 0.11.0
|
|
4
|
+
* description: Emscripten environment builder
|
|
5
|
+
* author: Kouji Matsui (@kekyo@mi.kekyo.net)
|
|
6
|
+
* license: MIT
|
|
7
|
+
* repository.url: https://github.com/kekyo/emsdk-env
|
|
8
|
+
* git.commit.hash: 0cf3fec6df406458fa2cf19fe561648e1ad4e369
|
|
9
|
+
*/
|
|
10
|
+
import { access, constants, mkdir, mkdtemp, readFile, rename, rm, writeFile } from "fs/promises";
|
|
11
|
+
import { homedir, tmpdir } from "os";
|
|
12
|
+
import { dirname, isAbsolute, join, parse, relative, resolve } from "path";
|
|
13
|
+
import { spawn } from "child_process";
|
|
14
|
+
import { glob } from "glob";
|
|
15
|
+
//#region node_modules/async-primitives/dist/index.mjs
|
|
16
|
+
/*!
|
|
17
|
+
* name: async-primitives
|
|
18
|
+
* version: 1.7.0
|
|
19
|
+
* description: A collection of primitive functions for asynchronous operations
|
|
20
|
+
* author: Kouji Matsui (@kekyo@mi.kekyo.net)
|
|
21
|
+
* license: MIT
|
|
22
|
+
* repository.url: https://github.com/kekyo/async-primitives.git
|
|
23
|
+
* git.commit.hash: 9472fbd5310b92690d84aaafb897429a04c013c5
|
|
24
|
+
*/
|
|
25
|
+
/**
|
|
26
|
+
* A no-op Releasable object that does nothing when released or disposed
|
|
27
|
+
*/
|
|
28
|
+
var __NOOP_HANDLER = () => {};
|
|
29
|
+
var __NOOP_RELEASABLE = {
|
|
30
|
+
release: __NOOP_HANDLER,
|
|
31
|
+
[Symbol.dispose]: __NOOP_HANDLER
|
|
32
|
+
};
|
|
33
|
+
var toAbortError = (reason) => {
|
|
34
|
+
if (reason instanceof Error) return reason;
|
|
35
|
+
if (typeof reason === "string") return new Error(reason);
|
|
36
|
+
return /* @__PURE__ */ new Error("Operation aborted");
|
|
37
|
+
};
|
|
38
|
+
/**
|
|
39
|
+
* Hooks up an abort handler to an AbortSignal and returns a handle for early cleanup
|
|
40
|
+
* @param signal - The AbortSignal to hook up to
|
|
41
|
+
* @param callback - The callback to call when the signal is aborted
|
|
42
|
+
* @returns A Releasable handle that can be used to remove the abort listener early
|
|
43
|
+
*/
|
|
44
|
+
var onAbort = (signal, callback) => {
|
|
45
|
+
if (!signal) return __NOOP_RELEASABLE;
|
|
46
|
+
if (signal.aborted) {
|
|
47
|
+
try {
|
|
48
|
+
callback(toAbortError(signal.reason));
|
|
49
|
+
} catch (error) {
|
|
50
|
+
console.warn("AbortHook callback error: ", error);
|
|
51
|
+
}
|
|
52
|
+
return __NOOP_RELEASABLE;
|
|
53
|
+
}
|
|
54
|
+
let abortHandler = () => {
|
|
55
|
+
if (abortHandler) {
|
|
56
|
+
const reason = signal.reason;
|
|
57
|
+
signal.removeEventListener("abort", abortHandler);
|
|
58
|
+
abortHandler = void 0;
|
|
59
|
+
try {
|
|
60
|
+
callback(toAbortError(reason));
|
|
61
|
+
} catch (error) {
|
|
62
|
+
console.warn("AbortHook callback error: ", error);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
};
|
|
66
|
+
const release = () => {
|
|
67
|
+
if (abortHandler) {
|
|
68
|
+
signal.removeEventListener("abort", abortHandler);
|
|
69
|
+
abortHandler = void 0;
|
|
70
|
+
}
|
|
71
|
+
};
|
|
72
|
+
signal.addEventListener("abort", abortHandler, { once: true });
|
|
73
|
+
return {
|
|
74
|
+
release,
|
|
75
|
+
[Symbol.dispose]: release
|
|
76
|
+
};
|
|
77
|
+
};
|
|
78
|
+
var runtimeGlobal$1 = globalThis;
|
|
79
|
+
var defer = (fn) => {
|
|
80
|
+
const setImmediateHandler = runtimeGlobal$1.setImmediate;
|
|
81
|
+
if (typeof setImmediateHandler === "function") {
|
|
82
|
+
setImmediateHandler(fn);
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
globalThis.setTimeout(fn, 0);
|
|
86
|
+
};
|
|
87
|
+
var ABORTED_ERROR$2 = () => /* @__PURE__ */ new Error("Lock acquisition was aborted");
|
|
88
|
+
/**
|
|
89
|
+
* Creates a new LockHandle instance
|
|
90
|
+
* @param releaseCallback Callback function to release the lock
|
|
91
|
+
* @returns A LockHandle object with release and dispose functionality
|
|
92
|
+
*/
|
|
93
|
+
var createLockHandle = (releaseCallback) => {
|
|
94
|
+
let isActive = true;
|
|
95
|
+
const release = () => {
|
|
96
|
+
if (!isActive) return;
|
|
97
|
+
isActive = false;
|
|
98
|
+
releaseCallback();
|
|
99
|
+
};
|
|
100
|
+
return {
|
|
101
|
+
get isActive() {
|
|
102
|
+
return isActive;
|
|
103
|
+
},
|
|
104
|
+
release,
|
|
105
|
+
[Symbol.dispose]: release
|
|
106
|
+
};
|
|
107
|
+
};
|
|
108
|
+
/**
|
|
109
|
+
* Creates a new Mutex instance
|
|
110
|
+
* @param maxConsecutiveCalls - The maximum number of consecutive calls to the lockAsync method before yielding control to the next item in the queue
|
|
111
|
+
* @returns A new Mutex for promise-based mutex operations
|
|
112
|
+
*/
|
|
113
|
+
var createMutex = (maxConsecutiveCalls = 20) => {
|
|
114
|
+
let isLocked = false;
|
|
115
|
+
const queue = [];
|
|
116
|
+
let count = 0;
|
|
117
|
+
const processQueue = () => {
|
|
118
|
+
var _item$signal;
|
|
119
|
+
if (isLocked || queue.length === 0) return;
|
|
120
|
+
const item = queue.shift();
|
|
121
|
+
if ((_item$signal = item.signal) === null || _item$signal === void 0 ? void 0 : _item$signal.aborted) {
|
|
122
|
+
item.reject(ABORTED_ERROR$2());
|
|
123
|
+
scheduleNextProcess();
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
isLocked = true;
|
|
127
|
+
const lockHandle = createLockHandle(releaseLock);
|
|
128
|
+
item.resolve(lockHandle);
|
|
129
|
+
};
|
|
130
|
+
const scheduleNextProcess = () => {
|
|
131
|
+
count++;
|
|
132
|
+
if (count >= maxConsecutiveCalls) {
|
|
133
|
+
count = 0;
|
|
134
|
+
defer(processQueue);
|
|
135
|
+
} else processQueue();
|
|
136
|
+
};
|
|
137
|
+
const releaseLock = () => {
|
|
138
|
+
if (!isLocked) return;
|
|
139
|
+
isLocked = false;
|
|
140
|
+
scheduleNextProcess();
|
|
141
|
+
};
|
|
142
|
+
const removeFromQueue = (item) => {
|
|
143
|
+
const index = queue.indexOf(item);
|
|
144
|
+
if (index !== -1) queue.splice(index, 1);
|
|
145
|
+
};
|
|
146
|
+
const lock = async (signal) => {
|
|
147
|
+
if (signal) {
|
|
148
|
+
if (signal.aborted) throw ABORTED_ERROR$2();
|
|
149
|
+
return new Promise((resolve, reject) => {
|
|
150
|
+
const queueItem = {
|
|
151
|
+
resolve: void 0,
|
|
152
|
+
reject: void 0,
|
|
153
|
+
signal
|
|
154
|
+
};
|
|
155
|
+
const abortHandle = onAbort(signal, () => {
|
|
156
|
+
removeFromQueue(queueItem);
|
|
157
|
+
reject(ABORTED_ERROR$2());
|
|
158
|
+
});
|
|
159
|
+
queueItem.resolve = (handle) => {
|
|
160
|
+
abortHandle.release();
|
|
161
|
+
resolve(handle);
|
|
162
|
+
};
|
|
163
|
+
queueItem.reject = (error) => {
|
|
164
|
+
abortHandle.release();
|
|
165
|
+
reject(error);
|
|
166
|
+
};
|
|
167
|
+
queue.push(queueItem);
|
|
168
|
+
processQueue();
|
|
169
|
+
});
|
|
170
|
+
} else return new Promise((resolve, reject) => {
|
|
171
|
+
queue.push({
|
|
172
|
+
resolve,
|
|
173
|
+
reject
|
|
174
|
+
});
|
|
175
|
+
processQueue();
|
|
176
|
+
});
|
|
177
|
+
};
|
|
178
|
+
return {
|
|
179
|
+
lock,
|
|
180
|
+
waiter: { wait: lock },
|
|
181
|
+
get isLocked() {
|
|
182
|
+
return isLocked;
|
|
183
|
+
},
|
|
184
|
+
get pendingCount() {
|
|
185
|
+
return queue.length;
|
|
186
|
+
}
|
|
187
|
+
};
|
|
188
|
+
};
|
|
189
|
+
var createLogicalContext = (id) => {
|
|
190
|
+
return {
|
|
191
|
+
id,
|
|
192
|
+
data: /* @__PURE__ */ new Map()
|
|
193
|
+
};
|
|
194
|
+
};
|
|
195
|
+
createLogicalContext(Symbol("[root]"));
|
|
196
|
+
//#endregion
|
|
197
|
+
//#region src/commands.ts
|
|
198
|
+
var createAbortError = () => {
|
|
199
|
+
const error = /* @__PURE__ */ new Error("The operation was aborted.");
|
|
200
|
+
error.name = "AbortError";
|
|
201
|
+
return error;
|
|
202
|
+
};
|
|
203
|
+
var runCommand = async (command, args, cwd, signal) => {
|
|
204
|
+
signal === null || signal === void 0 || signal.throwIfAborted();
|
|
205
|
+
return new Promise((resolvePromise, rejectPromise) => {
|
|
206
|
+
const child = spawn(command, args, {
|
|
207
|
+
cwd,
|
|
208
|
+
stdio: "inherit"
|
|
209
|
+
});
|
|
210
|
+
let settled = false;
|
|
211
|
+
const onAbort = () => {
|
|
212
|
+
if (settled) return;
|
|
213
|
+
settled = true;
|
|
214
|
+
child.kill();
|
|
215
|
+
rejectPromise(createAbortError());
|
|
216
|
+
};
|
|
217
|
+
signal === null || signal === void 0 || signal.addEventListener("abort", onAbort, { once: true });
|
|
218
|
+
const cleanup = () => {
|
|
219
|
+
signal === null || signal === void 0 || signal.removeEventListener("abort", onAbort);
|
|
220
|
+
};
|
|
221
|
+
child.once("error", (error) => {
|
|
222
|
+
if (settled) return;
|
|
223
|
+
settled = true;
|
|
224
|
+
cleanup();
|
|
225
|
+
rejectPromise(error);
|
|
226
|
+
});
|
|
227
|
+
child.once("close", (code) => {
|
|
228
|
+
if (settled) return;
|
|
229
|
+
settled = true;
|
|
230
|
+
cleanup();
|
|
231
|
+
if (code === 0) {
|
|
232
|
+
resolvePromise();
|
|
233
|
+
return;
|
|
234
|
+
}
|
|
235
|
+
rejectPromise(/* @__PURE__ */ new Error(`Command failed: ${command} ${args.join(" ")} (exit code ${code})`));
|
|
236
|
+
});
|
|
237
|
+
});
|
|
238
|
+
};
|
|
239
|
+
var runCommandWithEnv = async (command, args, cwd, env, signal) => {
|
|
240
|
+
signal === null || signal === void 0 || signal.throwIfAborted();
|
|
241
|
+
return new Promise((resolvePromise, rejectPromise) => {
|
|
242
|
+
const child = spawn(command, args, {
|
|
243
|
+
cwd,
|
|
244
|
+
env,
|
|
245
|
+
stdio: "inherit"
|
|
246
|
+
});
|
|
247
|
+
let settled = false;
|
|
248
|
+
const onAbort = () => {
|
|
249
|
+
if (settled) return;
|
|
250
|
+
settled = true;
|
|
251
|
+
child.kill();
|
|
252
|
+
rejectPromise(createAbortError());
|
|
253
|
+
};
|
|
254
|
+
signal === null || signal === void 0 || signal.addEventListener("abort", onAbort, { once: true });
|
|
255
|
+
const cleanup = () => {
|
|
256
|
+
signal === null || signal === void 0 || signal.removeEventListener("abort", onAbort);
|
|
257
|
+
};
|
|
258
|
+
child.once("error", (error) => {
|
|
259
|
+
if (settled) return;
|
|
260
|
+
settled = true;
|
|
261
|
+
cleanup();
|
|
262
|
+
rejectPromise(error);
|
|
263
|
+
});
|
|
264
|
+
child.once("close", (code) => {
|
|
265
|
+
if (settled) return;
|
|
266
|
+
settled = true;
|
|
267
|
+
cleanup();
|
|
268
|
+
if (code === 0) {
|
|
269
|
+
resolvePromise();
|
|
270
|
+
return;
|
|
271
|
+
}
|
|
272
|
+
rejectPromise(/* @__PURE__ */ new Error(`Command failed: ${command} ${args.join(" ")} (exit code ${code})`));
|
|
273
|
+
});
|
|
274
|
+
});
|
|
275
|
+
};
|
|
276
|
+
var runCommandCapture = async (command, args, cwd, signal) => {
|
|
277
|
+
signal === null || signal === void 0 || signal.throwIfAborted();
|
|
278
|
+
return new Promise((resolvePromise, rejectPromise) => {
|
|
279
|
+
const stdoutChunks = [];
|
|
280
|
+
const stderrChunks = [];
|
|
281
|
+
const child = spawn(command, args, {
|
|
282
|
+
cwd,
|
|
283
|
+
stdio: [
|
|
284
|
+
"ignore",
|
|
285
|
+
"pipe",
|
|
286
|
+
"pipe"
|
|
287
|
+
]
|
|
288
|
+
});
|
|
289
|
+
let settled = false;
|
|
290
|
+
const onAbort = () => {
|
|
291
|
+
if (settled) return;
|
|
292
|
+
settled = true;
|
|
293
|
+
child.kill();
|
|
294
|
+
rejectPromise(createAbortError());
|
|
295
|
+
};
|
|
296
|
+
signal === null || signal === void 0 || signal.addEventListener("abort", onAbort, { once: true });
|
|
297
|
+
const cleanup = () => {
|
|
298
|
+
signal === null || signal === void 0 || signal.removeEventListener("abort", onAbort);
|
|
299
|
+
};
|
|
300
|
+
child.stdout.on("data", (chunk) => {
|
|
301
|
+
stdoutChunks.push(chunk);
|
|
302
|
+
});
|
|
303
|
+
child.stderr.on("data", (chunk) => {
|
|
304
|
+
stderrChunks.push(chunk);
|
|
305
|
+
});
|
|
306
|
+
child.once("error", (error) => {
|
|
307
|
+
if (settled) return;
|
|
308
|
+
settled = true;
|
|
309
|
+
cleanup();
|
|
310
|
+
rejectPromise(error);
|
|
311
|
+
});
|
|
312
|
+
child.once("close", (code) => {
|
|
313
|
+
if (settled) return;
|
|
314
|
+
settled = true;
|
|
315
|
+
cleanup();
|
|
316
|
+
if (code === 0) {
|
|
317
|
+
resolvePromise(Buffer.concat(stdoutChunks));
|
|
318
|
+
return;
|
|
319
|
+
}
|
|
320
|
+
const stderrText = Buffer.concat(stderrChunks).toString("utf8");
|
|
321
|
+
rejectPromise(/* @__PURE__ */ new Error(`Command failed: ${command} ${args.join(" ")} (exit code ${code})${stderrText ? `\n${stderrText}` : ""}`));
|
|
322
|
+
});
|
|
323
|
+
});
|
|
324
|
+
};
|
|
325
|
+
//#endregion
|
|
326
|
+
//#region src/fs-utils.ts
|
|
327
|
+
var pathExists = async (targetPath) => {
|
|
328
|
+
try {
|
|
329
|
+
await access(targetPath, constants.F_OK);
|
|
330
|
+
return true;
|
|
331
|
+
} catch (_unused) {
|
|
332
|
+
return false;
|
|
333
|
+
}
|
|
334
|
+
};
|
|
335
|
+
var ensureDirectory = async (targetPath) => {
|
|
336
|
+
await mkdir(targetPath, { recursive: true });
|
|
337
|
+
};
|
|
338
|
+
//#endregion
|
|
339
|
+
//#region src/emsdk.ts
|
|
340
|
+
var DEFAULT_REPO_URL = "https://github.com/emscripten-core/emsdk.git";
|
|
341
|
+
var DEFAULT_GIT_REF = "main";
|
|
342
|
+
var DEFAULT_CACHE_DIR = join(homedir(), ".cache", "emsdk-env");
|
|
343
|
+
var DEFAULT_TARGET_VERSION = "latest";
|
|
344
|
+
var versionMutexes = /* @__PURE__ */ new Map();
|
|
345
|
+
var getVersionMutex = (key) => {
|
|
346
|
+
let mutex = versionMutexes.get(key);
|
|
347
|
+
if (!mutex) {
|
|
348
|
+
mutex = createMutex();
|
|
349
|
+
versionMutexes.set(key, mutex);
|
|
350
|
+
}
|
|
351
|
+
return mutex;
|
|
352
|
+
};
|
|
353
|
+
var ensureNonEmpty = (value, label) => {
|
|
354
|
+
if (value.trim().length === 0) throw new TypeError(`${label} must be a non-empty string.`);
|
|
355
|
+
};
|
|
356
|
+
var sanitizeSegment = (value) => {
|
|
357
|
+
const trimmed = value.trim();
|
|
358
|
+
ensureNonEmpty(trimmed, "targetVersion");
|
|
359
|
+
const sanitized = trimmed.replace(/[^A-Za-z0-9._-]/g, "_");
|
|
360
|
+
if (sanitized === "." || sanitized === ".." || sanitized.length === 0) throw new TypeError("targetVersion results in an unsafe path segment.");
|
|
361
|
+
return sanitized;
|
|
362
|
+
};
|
|
363
|
+
var resolveEmsdkCommand = () => process.platform === "win32" ? "emsdk.bat" : "./emsdk";
|
|
364
|
+
var runEmsdk = async (repoDir, args, signal) => {
|
|
365
|
+
if (process.platform === "win32") {
|
|
366
|
+
await runCommand("cmd", [
|
|
367
|
+
"/c",
|
|
368
|
+
"emsdk.bat",
|
|
369
|
+
...args
|
|
370
|
+
], repoDir, signal);
|
|
371
|
+
return;
|
|
372
|
+
}
|
|
373
|
+
await runCommand(resolveEmsdkCommand(), args, repoDir, signal);
|
|
374
|
+
};
|
|
375
|
+
var runGitClone = async (gitPath, repoUrl, targetDir, cwd, signal) => {
|
|
376
|
+
await runCommand(gitPath, [
|
|
377
|
+
"clone",
|
|
378
|
+
repoUrl,
|
|
379
|
+
targetDir,
|
|
380
|
+
"--depth",
|
|
381
|
+
"1",
|
|
382
|
+
"--branch",
|
|
383
|
+
DEFAULT_GIT_REF
|
|
384
|
+
], cwd, signal);
|
|
385
|
+
};
|
|
386
|
+
var isAlreadyExistsError = (error) => error instanceof Error && "code" in error && error.code !== void 0 && [
|
|
387
|
+
"EEXIST",
|
|
388
|
+
"ENOTEMPTY",
|
|
389
|
+
"EISDIR"
|
|
390
|
+
].includes(String(error.code));
|
|
391
|
+
/**
|
|
392
|
+
* Prepare the Emscripten SDK in the local cache and return the SDK root path.
|
|
393
|
+
*
|
|
394
|
+
* Clones the emsdk repository if needed, installs the requested version,
|
|
395
|
+
* and activates it in the cache directory.
|
|
396
|
+
*
|
|
397
|
+
* @param options - SDK preparation options.
|
|
398
|
+
* @returns Absolute path to the prepared SDK root.
|
|
399
|
+
*/
|
|
400
|
+
var prepareEmsdk = async (options) => {
|
|
401
|
+
var _options$targetVersio, _options$signal, _options$cacheDir, _options$repoUrl, _options$gitPath;
|
|
402
|
+
if (!options) throw new TypeError("options must be provided.");
|
|
403
|
+
if (options.targetVersion !== void 0 && typeof options.targetVersion !== "string") throw new TypeError("targetVersion must be a string.");
|
|
404
|
+
const targetVersion = (_options$targetVersio = options.targetVersion) !== null && _options$targetVersio !== void 0 ? _options$targetVersio : DEFAULT_TARGET_VERSION;
|
|
405
|
+
ensureNonEmpty(targetVersion, "targetVersion");
|
|
406
|
+
(_options$signal = options.signal) === null || _options$signal === void 0 || _options$signal.throwIfAborted();
|
|
407
|
+
const cacheDir = resolve((_options$cacheDir = options.cacheDir) !== null && _options$cacheDir !== void 0 ? _options$cacheDir : DEFAULT_CACHE_DIR);
|
|
408
|
+
const repoUrl = (_options$repoUrl = options.repoUrl) !== null && _options$repoUrl !== void 0 ? _options$repoUrl : DEFAULT_REPO_URL;
|
|
409
|
+
const gitPath = (_options$gitPath = options.gitPath) !== null && _options$gitPath !== void 0 ? _options$gitPath : "git";
|
|
410
|
+
const finalDir = resolve(cacheDir, sanitizeSegment(targetVersion));
|
|
411
|
+
const lock = await getVersionMutex(finalDir).lock(options.signal);
|
|
412
|
+
try {
|
|
413
|
+
var _options$signal2, _options$signal4;
|
|
414
|
+
(_options$signal2 = options.signal) === null || _options$signal2 === void 0 || _options$signal2.throwIfAborted();
|
|
415
|
+
if (await pathExists(finalDir)) return finalDir;
|
|
416
|
+
await ensureDirectory(cacheDir);
|
|
417
|
+
const tempRoot = await mkdtemp(join(cacheDir, ".tmp-"));
|
|
418
|
+
const tempRepoDir = join(tempRoot, "emsdk");
|
|
419
|
+
try {
|
|
420
|
+
var _options$signal3;
|
|
421
|
+
await runGitClone(gitPath, repoUrl, tempRepoDir, cacheDir, options.signal);
|
|
422
|
+
(_options$signal3 = options.signal) === null || _options$signal3 === void 0 || _options$signal3.throwIfAborted();
|
|
423
|
+
await runEmsdk(tempRepoDir, ["install", targetVersion], options.signal);
|
|
424
|
+
try {
|
|
425
|
+
await rename(tempRepoDir, finalDir);
|
|
426
|
+
} catch (error) {
|
|
427
|
+
if (isAlreadyExistsError(error)) return finalDir;
|
|
428
|
+
throw error;
|
|
429
|
+
}
|
|
430
|
+
} finally {
|
|
431
|
+
await rm(tempRoot, {
|
|
432
|
+
recursive: true,
|
|
433
|
+
force: true
|
|
434
|
+
});
|
|
435
|
+
}
|
|
436
|
+
(_options$signal4 = options.signal) === null || _options$signal4 === void 0 || _options$signal4.throwIfAborted();
|
|
437
|
+
await runEmsdk(finalDir, ["activate", targetVersion], options.signal);
|
|
438
|
+
return finalDir;
|
|
439
|
+
} finally {
|
|
440
|
+
lock.release();
|
|
441
|
+
}
|
|
442
|
+
};
|
|
443
|
+
//#endregion
|
|
444
|
+
//#region src/env.ts
|
|
445
|
+
var shellQuote = (value) => `'${String(value).replace(/'/g, `'\"'\"'`)}'`;
|
|
446
|
+
var parseEnvBuffer = (buffer) => {
|
|
447
|
+
const entries = buffer.toString("utf8").split("\0");
|
|
448
|
+
const env = {};
|
|
449
|
+
for (const entry of entries) {
|
|
450
|
+
if (!entry) continue;
|
|
451
|
+
const delimiterIndex = entry.indexOf("=");
|
|
452
|
+
if (delimiterIndex <= 0) continue;
|
|
453
|
+
const key = entry.slice(0, delimiterIndex);
|
|
454
|
+
env[key] = entry.slice(delimiterIndex + 1);
|
|
455
|
+
}
|
|
456
|
+
return env;
|
|
457
|
+
};
|
|
458
|
+
var loadEmsdkEnv = async (emsdkRoot, logger, signal) => {
|
|
459
|
+
if (process.platform === "win32") throw new Error("Emscripten environment extraction on Windows is not implemented yet.");
|
|
460
|
+
const envScript = resolve(emsdkRoot, "emsdk_env.sh");
|
|
461
|
+
if (!await pathExists(envScript)) throw new Error(`emsdk_env.sh not found: ${envScript}`);
|
|
462
|
+
const command = `. ${shellQuote(envScript)} >/dev/null 2>&1; env -0`;
|
|
463
|
+
logger.debug(`Loading emsdk environment: ${envScript}`);
|
|
464
|
+
return parseEnvBuffer(await runCommandCapture("bash", ["-lc", command], emsdkRoot, signal));
|
|
465
|
+
};
|
|
466
|
+
var resolveEmccCommand = async (env, emsdkRoot) => {
|
|
467
|
+
if (env.EMCC) return env.EMCC;
|
|
468
|
+
if (env.EMSCRIPTEN) {
|
|
469
|
+
const candidate = join(env.EMSCRIPTEN, "emcc");
|
|
470
|
+
if (await pathExists(candidate)) return candidate;
|
|
471
|
+
}
|
|
472
|
+
const fallback = join(emsdkRoot, "upstream", "emscripten", "emcc");
|
|
473
|
+
if (await pathExists(fallback)) return fallback;
|
|
474
|
+
return "emcc";
|
|
475
|
+
};
|
|
476
|
+
var resolveEmarCommand = async (env, emsdkRoot) => {
|
|
477
|
+
if (env.EMAR) return env.EMAR;
|
|
478
|
+
if (env.EMSCRIPTEN) {
|
|
479
|
+
const candidate = join(env.EMSCRIPTEN, "emar");
|
|
480
|
+
if (await pathExists(candidate)) return candidate;
|
|
481
|
+
}
|
|
482
|
+
const fallback = join(emsdkRoot, "upstream", "emscripten", "emar");
|
|
483
|
+
if (await pathExists(fallback)) return fallback;
|
|
484
|
+
return "emar";
|
|
485
|
+
};
|
|
486
|
+
var resolveWasmOptCommand = async (env, emsdkRoot) => {
|
|
487
|
+
var _env$BINARYEN_ROOT;
|
|
488
|
+
if (env.WASM_OPT) return env.WASM_OPT;
|
|
489
|
+
const binaryenRoot = (_env$BINARYEN_ROOT = env.BINARYEN_ROOT) !== null && _env$BINARYEN_ROOT !== void 0 ? _env$BINARYEN_ROOT : env.BINARYEN;
|
|
490
|
+
if (binaryenRoot) {
|
|
491
|
+
const candidate = join(binaryenRoot, "bin", "wasm-opt");
|
|
492
|
+
if (await pathExists(candidate)) return candidate;
|
|
493
|
+
}
|
|
494
|
+
const fallback = join(emsdkRoot, "upstream", "bin", "wasm-opt");
|
|
495
|
+
if (await pathExists(fallback)) return fallback;
|
|
496
|
+
return "wasm-opt";
|
|
497
|
+
};
|
|
498
|
+
//#endregion
|
|
499
|
+
//#region src/logger.ts
|
|
500
|
+
var createConsoleLogger = (prefix) => {
|
|
501
|
+
return {
|
|
502
|
+
debug: (msg) => console.debug(`[${prefix}]: ${msg}`),
|
|
503
|
+
info: (msg) => console.info(`[${prefix}]: ${msg}`),
|
|
504
|
+
warn: (msg) => console.warn(`[${prefix}]: ${msg}`),
|
|
505
|
+
error: (msg) => console.error(`[${prefix}]: ${msg}`)
|
|
506
|
+
};
|
|
507
|
+
};
|
|
508
|
+
//#endregion
|
|
509
|
+
//#region src/build.ts
|
|
510
|
+
var DEFAULT_WASM_SRC_DIR = "wasm";
|
|
511
|
+
var DEFAULT_WASM_INCLUDE_DIR = "include";
|
|
512
|
+
var DEFAULT_WASM_OUT_DIR = join("src", "wasm");
|
|
513
|
+
var DEFAULT_WASM_LIB_DIR = "lib";
|
|
514
|
+
var DEFAULT_IMPORT_INCLUDE_DIR = "include";
|
|
515
|
+
var DEFAULT_IMPORT_LIB_DIR = "lib";
|
|
516
|
+
var DEFAULT_WASM_BUILD_DIR = join(tmpdir(), "emsdk-env");
|
|
517
|
+
var DEFAULT_EMSDK_TARGET_VERSION = "latest";
|
|
518
|
+
var DEFAULT_WASM_OPT_ARGS = ["-Oz"];
|
|
519
|
+
var DEFAULT_GENERATED_LOADER_OUT_FILE = join("src", "generated", "wasm-loader.ts");
|
|
520
|
+
var buildSequence = 0;
|
|
521
|
+
var padNumber = (value, length = 2) => String(value).padStart(length, "0");
|
|
522
|
+
var formatTimestamp = (date) => {
|
|
523
|
+
return `${date.getFullYear()}${padNumber(date.getMonth() + 1)}${padNumber(date.getDate())}_${padNumber(date.getHours())}${padNumber(date.getMinutes())}${padNumber(date.getSeconds())}`;
|
|
524
|
+
};
|
|
525
|
+
var createBuildId = () => {
|
|
526
|
+
buildSequence += 1;
|
|
527
|
+
return `${formatTimestamp(/* @__PURE__ */ new Date())}_${String(buildSequence).padStart(4, "0")}_${process.pid}`;
|
|
528
|
+
};
|
|
529
|
+
var ensureArray = (value) => value !== null && value !== void 0 ? value : [];
|
|
530
|
+
var resolveTargetType = (value) => value !== null && value !== void 0 ? value : "wasm";
|
|
531
|
+
var normalizePrepareOptions = (options) => {
|
|
532
|
+
const { targetVersion, ...rest } = options !== null && options !== void 0 ? options : {};
|
|
533
|
+
return {
|
|
534
|
+
targetVersion: targetVersion !== null && targetVersion !== void 0 ? targetVersion : DEFAULT_EMSDK_TARGET_VERSION,
|
|
535
|
+
...rest
|
|
536
|
+
};
|
|
537
|
+
};
|
|
538
|
+
var parseStringKeyValueInput = (values) => {
|
|
539
|
+
const parsed = {};
|
|
540
|
+
for (const entry of values) {
|
|
541
|
+
const index = entry.indexOf("=");
|
|
542
|
+
if (index === -1) {
|
|
543
|
+
parsed[entry] = void 0;
|
|
544
|
+
continue;
|
|
545
|
+
}
|
|
546
|
+
const key = entry.slice(0, index);
|
|
547
|
+
parsed[key] = entry.slice(index + 1);
|
|
548
|
+
}
|
|
549
|
+
return parsed;
|
|
550
|
+
};
|
|
551
|
+
var isDefineMap = (input) => input instanceof Map;
|
|
552
|
+
var normalizeDefineInput = (input) => {
|
|
553
|
+
if (!input) return {};
|
|
554
|
+
if (Array.isArray(input)) return parseStringKeyValueInput(input);
|
|
555
|
+
if (isDefineMap(input)) return Object.fromEntries(input);
|
|
556
|
+
return { ...input };
|
|
557
|
+
};
|
|
558
|
+
var isLinkDirectiveMap = (input) => input instanceof Map;
|
|
559
|
+
var normalizeLinkDirectiveInput = (input) => {
|
|
560
|
+
if (!input) return {};
|
|
561
|
+
if (Array.isArray(input)) return parseStringKeyValueInput(input);
|
|
562
|
+
if (isLinkDirectiveMap(input)) return Object.fromEntries(input);
|
|
563
|
+
return { ...input };
|
|
564
|
+
};
|
|
565
|
+
var mergeDefines = (common, target) => ({
|
|
566
|
+
...normalizeDefineInput(common),
|
|
567
|
+
...normalizeDefineInput(target)
|
|
568
|
+
});
|
|
569
|
+
var mergeLinkDirectives = (common, target) => ({
|
|
570
|
+
...normalizeLinkDirectiveInput(common),
|
|
571
|
+
...normalizeLinkDirectiveInput(target)
|
|
572
|
+
});
|
|
573
|
+
var resolveWasmOptEnabled = (common, target) => {
|
|
574
|
+
var _ref, _target$enable;
|
|
575
|
+
return (_ref = (_target$enable = target === null || target === void 0 ? void 0 : target.enable) !== null && _target$enable !== void 0 ? _target$enable : common === null || common === void 0 ? void 0 : common.enable) !== null && _ref !== void 0 ? _ref : false;
|
|
576
|
+
};
|
|
577
|
+
var resolveWasmOptArgs = (common, target, env) => {
|
|
578
|
+
var _common$options, _target$options;
|
|
579
|
+
const commonArgs = (_common$options = common === null || common === void 0 ? void 0 : common.options) !== null && _common$options !== void 0 ? _common$options : DEFAULT_WASM_OPT_ARGS;
|
|
580
|
+
const targetArgs = (_target$options = target === null || target === void 0 ? void 0 : target.options) !== null && _target$options !== void 0 ? _target$options : [];
|
|
581
|
+
return expandArray([...commonArgs, ...targetArgs], env, "wasmOpt.options");
|
|
582
|
+
};
|
|
583
|
+
var stripOuterQuotes = (value) => {
|
|
584
|
+
const trimmed = value.trim();
|
|
585
|
+
if (trimmed.startsWith("\"") && trimmed.endsWith("\"") || trimmed.startsWith("'") && trimmed.endsWith("'")) return trimmed.slice(1, -1);
|
|
586
|
+
return trimmed;
|
|
587
|
+
};
|
|
588
|
+
var extractWasmBinaryFile = (value) => {
|
|
589
|
+
if (value.startsWith("WASM_BINARY_FILE=")) return value.slice(17);
|
|
590
|
+
const match = value.match(/^(?:-s|--settings)(?:=)?WASM_BINARY_FILE=(.+)$/);
|
|
591
|
+
if (match) return match[1];
|
|
592
|
+
};
|
|
593
|
+
var resolveWasmBinaryFileFromLinkOptions = (linkOptions) => {
|
|
594
|
+
for (let index = 0; index < linkOptions.length; index += 1) {
|
|
595
|
+
const option = linkOptions[index];
|
|
596
|
+
if (!option) continue;
|
|
597
|
+
if (option === "-s" || option === "--settings") {
|
|
598
|
+
const next = linkOptions[index + 1];
|
|
599
|
+
if (!next) continue;
|
|
600
|
+
const extracted = extractWasmBinaryFile(next);
|
|
601
|
+
if (extracted) return stripOuterQuotes(extracted);
|
|
602
|
+
}
|
|
603
|
+
const extracted = extractWasmBinaryFile(option);
|
|
604
|
+
if (extracted) return stripOuterQuotes(extracted);
|
|
605
|
+
}
|
|
606
|
+
};
|
|
607
|
+
var resolveWasmOptInputFile = (resolvedOutFile, resolvedLinkOptions) => {
|
|
608
|
+
const wasmBinaryFile = resolveWasmBinaryFileFromLinkOptions(resolvedLinkOptions);
|
|
609
|
+
if (wasmBinaryFile) return isAbsolute(wasmBinaryFile) ? wasmBinaryFile : resolve(dirname(resolvedOutFile), wasmBinaryFile);
|
|
610
|
+
const parsed = parse(resolvedOutFile);
|
|
611
|
+
if (parsed.ext.toLowerCase() === ".wasm") return resolvedOutFile;
|
|
612
|
+
const baseName = parsed.name.toLowerCase().endsWith(".wasm") ? parsed.name : `${parsed.name}.wasm`;
|
|
613
|
+
return join(parsed.dir, baseName);
|
|
614
|
+
};
|
|
615
|
+
var resolvePath = (rootDir, value) => isAbsolute(value) ? value : resolve(rootDir, value);
|
|
616
|
+
var expandPlaceholders = (value, env, label) => value.replace(/\{([A-Z0-9_]+)\}/g, (_match, key) => {
|
|
617
|
+
const replacement = env[key];
|
|
618
|
+
if (replacement === void 0) throw new Error(`Unknown placeholder {${key}} in ${label}.`);
|
|
619
|
+
return replacement;
|
|
620
|
+
});
|
|
621
|
+
var expandArray = (values, env, label) => values.map((value) => expandPlaceholders(value, env, label));
|
|
622
|
+
var resolveDefines = (defines, env) => {
|
|
623
|
+
const resolved = {};
|
|
624
|
+
for (const [key, value] of Object.entries(defines)) if (typeof value === "string") resolved[key] = expandPlaceholders(value, env, `defines.${key}`);
|
|
625
|
+
else resolved[key] = value;
|
|
626
|
+
return resolved;
|
|
627
|
+
};
|
|
628
|
+
var resolveLinkDirectiveValue = (value, env, label) => {
|
|
629
|
+
if (typeof value === "string") return expandPlaceholders(value, env, label);
|
|
630
|
+
if (Array.isArray(value)) return value.map((entry, index) => expandPlaceholders(entry, env, `${label}[${index}]`));
|
|
631
|
+
return value;
|
|
632
|
+
};
|
|
633
|
+
var resolveLinkDirectives = (directives, env) => {
|
|
634
|
+
const resolved = {};
|
|
635
|
+
for (const [key, value] of Object.entries(directives)) resolved[key] = resolveLinkDirectiveValue(value, env, `linkDirectives.${key}`);
|
|
636
|
+
return resolved;
|
|
637
|
+
};
|
|
638
|
+
var resolveIncludeDirs = (includeDirs, env, rootDir) => {
|
|
639
|
+
return expandArray(includeDirs, env, "includeDirs").map((value) => resolvePath(rootDir, value));
|
|
640
|
+
};
|
|
641
|
+
var resolveOutFile = (outFile, env, outDir) => {
|
|
642
|
+
return resolvePath(outDir, expandPlaceholders(outFile, env, "outFile"));
|
|
643
|
+
};
|
|
644
|
+
var resolveSourcesFromPatterns = async (patterns, env, srcDir, label) => {
|
|
645
|
+
const resolvedPatterns = expandArray(patterns, env, label).map((value) => resolvePath(srcDir, value));
|
|
646
|
+
const sources = (await Promise.all(resolvedPatterns.map((pattern) => glob(pattern, { nodir: true })))).flat();
|
|
647
|
+
sources.sort();
|
|
648
|
+
return sources;
|
|
649
|
+
};
|
|
650
|
+
var buildDefineFlags = (defines) => Object.entries(defines).flatMap(([key, value]) => value === null || value === void 0 ? [`-D${key}`] : [`-D${key}=${String(value)}`]);
|
|
651
|
+
var serializeLinkDirectiveValue = (value) => Array.isArray(value) ? JSON.stringify(value) : String(value);
|
|
652
|
+
var buildLinkDirectiveFlags = (directives) => {
|
|
653
|
+
if (Object.keys(directives).length === 0) return [];
|
|
654
|
+
return Object.entries(directives).flatMap(([key, value]) => value === null || value === void 0 ? ["-s", key] : ["-s", `${key}=${serializeLinkDirectiveValue(value)}`]);
|
|
655
|
+
};
|
|
656
|
+
var buildExportFlags = (exports) => {
|
|
657
|
+
if (exports.length === 0) return [];
|
|
658
|
+
return ["-s", `EXPORTED_FUNCTIONS=${JSON.stringify(exports)}`];
|
|
659
|
+
};
|
|
660
|
+
var createEnvForBuild = (baseEnv, overrides) => ({
|
|
661
|
+
...process.env,
|
|
662
|
+
...baseEnv,
|
|
663
|
+
...overrides
|
|
664
|
+
});
|
|
665
|
+
var resolveTargetOutFile = (targetName, targetOutFile, env, baseDir, extension) => {
|
|
666
|
+
if (targetOutFile) return resolveOutFile(targetOutFile, env, baseDir);
|
|
667
|
+
return resolve(baseDir, `${targetName}.${extension}`);
|
|
668
|
+
};
|
|
669
|
+
var resolveTargetSources = async (targetSources, env, srcDir) => {
|
|
670
|
+
return resolveSourcesFromPatterns(targetSources && targetSources.length > 0 ? targetSources : [join(srcDir, "**", "*.c"), join(srcDir, "**", "*.cpp")], env, srcDir, "sources");
|
|
671
|
+
};
|
|
672
|
+
var toSafeObjectName = (rootDir, sourcePath, groupIndex) => {
|
|
673
|
+
const baseName = relative(rootDir, sourcePath).replace(/[\\/]/g, "_").replace(/[^A-Za-z0-9._-]/g, "_");
|
|
674
|
+
if (groupIndex === void 0) return baseName;
|
|
675
|
+
return `${baseName}__g${groupIndex}`;
|
|
676
|
+
};
|
|
677
|
+
var dedupeSources = (sources) => {
|
|
678
|
+
const seen = /* @__PURE__ */ new Set();
|
|
679
|
+
const deduped = [];
|
|
680
|
+
for (const source of sources) {
|
|
681
|
+
if (seen.has(source)) continue;
|
|
682
|
+
seen.add(source);
|
|
683
|
+
deduped.push(source);
|
|
684
|
+
}
|
|
685
|
+
return deduped;
|
|
686
|
+
};
|
|
687
|
+
var dedupeValues = (values) => {
|
|
688
|
+
const seen = /* @__PURE__ */ new Set();
|
|
689
|
+
const deduped = [];
|
|
690
|
+
for (const value of values) {
|
|
691
|
+
if (seen.has(value)) continue;
|
|
692
|
+
seen.add(value);
|
|
693
|
+
deduped.push(value);
|
|
694
|
+
}
|
|
695
|
+
return deduped;
|
|
696
|
+
};
|
|
697
|
+
var isSubPath = (parentDir, targetPath) => {
|
|
698
|
+
const rel = relative(parentDir, targetPath);
|
|
699
|
+
if (rel === "") return true;
|
|
700
|
+
return !rel.startsWith("..") && !isAbsolute(rel);
|
|
701
|
+
};
|
|
702
|
+
var readTextIfExists = async (filePath) => {
|
|
703
|
+
try {
|
|
704
|
+
return await readFile(filePath, "utf8");
|
|
705
|
+
} catch (error) {
|
|
706
|
+
if (error.code === "ENOENT") return;
|
|
707
|
+
throw error;
|
|
708
|
+
}
|
|
709
|
+
};
|
|
710
|
+
var writeTextIfChanged = async (filePath, content) => {
|
|
711
|
+
if (await readTextIfExists(filePath) === content) return false;
|
|
712
|
+
await ensureDirectory(dirname(filePath));
|
|
713
|
+
await writeFile(filePath, content, "utf8");
|
|
714
|
+
return true;
|
|
715
|
+
};
|
|
716
|
+
var toPascalCaseIdentifier = (value) => {
|
|
717
|
+
const tokens = value.split(/[^A-Za-z0-9]+/).map((token) => token.trim()).filter((token) => token.length > 0);
|
|
718
|
+
if (tokens.length === 0) throw new Error(`Cannot derive loader function name from target: ${value}`);
|
|
719
|
+
const pascal = tokens.map((token) => token.charAt(0).toUpperCase() + token.slice(1)).join("");
|
|
720
|
+
return /^[0-9]/.test(pascal) ? `Target${pascal}` : pascal;
|
|
721
|
+
};
|
|
722
|
+
var toRelativeImportSpecifier = (fromFile, toFile) => {
|
|
723
|
+
const rel = relative(dirname(fromFile), toFile).replace(/\\/g, "/");
|
|
724
|
+
return rel.startsWith(".") ? rel : `./${rel}`;
|
|
725
|
+
};
|
|
726
|
+
var buildGeneratedLoaderContent = (generatedLoaderFile, targets) => {
|
|
727
|
+
return `// Generated by emsdk-env. DO NOT EDIT.
|
|
728
|
+
|
|
729
|
+
/**
|
|
730
|
+
* Supported input sources for loading a WASM binary.
|
|
731
|
+
*/
|
|
732
|
+
export type WasmSource =
|
|
733
|
+
| string
|
|
734
|
+
| URL
|
|
735
|
+
| ArrayBuffer
|
|
736
|
+
| ArrayBufferView
|
|
737
|
+
| Response;
|
|
738
|
+
|
|
739
|
+
/**
|
|
740
|
+
* Options for instantiating a WASM module.
|
|
741
|
+
*/
|
|
742
|
+
export interface WasmLoadOptions {
|
|
743
|
+
/**
|
|
744
|
+
* Imports passed to WebAssembly.instantiate().
|
|
745
|
+
*/
|
|
746
|
+
readonly imports?: WebAssembly.Imports;
|
|
747
|
+
}
|
|
748
|
+
|
|
749
|
+
/**
|
|
750
|
+
* Options for loading a generated WASM target.
|
|
751
|
+
*/
|
|
752
|
+
export interface TargetWasmLoadOptions extends WasmLoadOptions {
|
|
753
|
+
/**
|
|
754
|
+
* Override the generated default source for the target.
|
|
755
|
+
*/
|
|
756
|
+
readonly source?: WasmSource;
|
|
757
|
+
}
|
|
758
|
+
|
|
759
|
+
const isResponse = (value: WasmSource): value is Response =>
|
|
760
|
+
typeof Response !== 'undefined' && value instanceof Response;
|
|
761
|
+
|
|
762
|
+
const createWasmLoadOptions = (
|
|
763
|
+
imports: WebAssembly.Imports | undefined
|
|
764
|
+
): WasmLoadOptions | undefined => (imports ? { imports } : undefined);
|
|
765
|
+
|
|
766
|
+
/**
|
|
767
|
+
* Instantiated WASM module with typed export helpers.
|
|
768
|
+
*
|
|
769
|
+
* @typeParam TExports Function exports exposed through the exports property.
|
|
770
|
+
* @typeParam TAllExports Full WebAssembly export map exposed through the allExports property.
|
|
771
|
+
*/
|
|
772
|
+
export interface WasmInstance<
|
|
773
|
+
TExports extends object = Record<string, (...args: unknown[]) => unknown>,
|
|
774
|
+
TAllExports extends WebAssembly.Exports = WebAssembly.Exports> {
|
|
775
|
+
/**
|
|
776
|
+
* Exported functions only.
|
|
777
|
+
*/
|
|
778
|
+
readonly exports: TExports;
|
|
779
|
+
/**
|
|
780
|
+
* All exported values with caller-provided typing.
|
|
781
|
+
*/
|
|
782
|
+
readonly allExports: TAllExports;
|
|
783
|
+
/**
|
|
784
|
+
* Resolved linear memory used by the module.
|
|
785
|
+
*/
|
|
786
|
+
readonly memory: WebAssembly.Memory;
|
|
787
|
+
/**
|
|
788
|
+
* Resolved function table when the module uses one.
|
|
789
|
+
*/
|
|
790
|
+
readonly table: WebAssembly.Table | undefined;
|
|
791
|
+
/**
|
|
792
|
+
* Raw exports object returned by WebAssembly.instantiate().
|
|
793
|
+
*/
|
|
794
|
+
readonly rawExports: WebAssembly.Exports;
|
|
795
|
+
/**
|
|
796
|
+
* Compiled WebAssembly module.
|
|
797
|
+
*/
|
|
798
|
+
readonly module: WebAssembly.Module;
|
|
799
|
+
/**
|
|
800
|
+
* Instantiated WebAssembly instance.
|
|
801
|
+
*/
|
|
802
|
+
readonly instance: WebAssembly.Instance;
|
|
803
|
+
/**
|
|
804
|
+
* Optional Emscripten _initialize export.
|
|
805
|
+
*/
|
|
806
|
+
readonly initialize: (() => unknown) | undefined;
|
|
807
|
+
/**
|
|
808
|
+
* Optional WASI _start export.
|
|
809
|
+
*/
|
|
810
|
+
readonly start: (() => unknown) | undefined;
|
|
811
|
+
}
|
|
812
|
+
|
|
813
|
+
const resolveWasmBytes = async (source: WasmSource): Promise<ArrayBuffer> => {
|
|
814
|
+
if (isResponse(source)) {
|
|
815
|
+
return await source.arrayBuffer();
|
|
816
|
+
}
|
|
817
|
+
if (source instanceof URL || typeof source === 'string') {
|
|
818
|
+
const response = await fetch(source);
|
|
819
|
+
if (!response.ok) {
|
|
820
|
+
throw new Error(\`Failed to fetch wasm: \${response.url}\`);
|
|
821
|
+
}
|
|
822
|
+
return await response.arrayBuffer();
|
|
823
|
+
}
|
|
824
|
+
if (source instanceof ArrayBuffer) {
|
|
825
|
+
return source;
|
|
826
|
+
}
|
|
827
|
+
if (ArrayBuffer.isView(source)) {
|
|
828
|
+
const view = new Uint8Array(
|
|
829
|
+
source.buffer,
|
|
830
|
+
source.byteOffset,
|
|
831
|
+
source.byteLength
|
|
832
|
+
);
|
|
833
|
+
return view.slice().buffer;
|
|
834
|
+
}
|
|
835
|
+
throw new TypeError('Unsupported wasm source.');
|
|
836
|
+
};
|
|
837
|
+
|
|
838
|
+
const getImportValue = (
|
|
839
|
+
imports: WebAssembly.Imports | undefined,
|
|
840
|
+
moduleName: string,
|
|
841
|
+
name: string
|
|
842
|
+
) => {
|
|
843
|
+
const moduleImports = imports?.[moduleName];
|
|
844
|
+
if (!moduleImports || typeof moduleImports !== 'object') {
|
|
845
|
+
return undefined;
|
|
846
|
+
}
|
|
847
|
+
return (moduleImports as Record<string, unknown>)[name];
|
|
848
|
+
};
|
|
849
|
+
|
|
850
|
+
const resolveMemory = (
|
|
851
|
+
module: WebAssembly.Module,
|
|
852
|
+
instance: WebAssembly.Instance,
|
|
853
|
+
imports: WebAssembly.Imports | undefined
|
|
854
|
+
) => {
|
|
855
|
+
let memory: WebAssembly.Memory | undefined;
|
|
856
|
+
for (const entry of WebAssembly.Module.exports(module)) {
|
|
857
|
+
if (entry.kind !== 'memory') {
|
|
858
|
+
continue;
|
|
859
|
+
}
|
|
860
|
+
if (memory) {
|
|
861
|
+
throw new Error('Multiple wasm memories are not supported.');
|
|
862
|
+
}
|
|
863
|
+
const value = instance.exports[entry.name];
|
|
864
|
+
if (!(value instanceof WebAssembly.Memory)) {
|
|
865
|
+
throw new Error(\`Export is not a WebAssembly.Memory: \${entry.name}\`);
|
|
866
|
+
}
|
|
867
|
+
memory = value;
|
|
868
|
+
}
|
|
869
|
+
if (memory) {
|
|
870
|
+
return memory;
|
|
871
|
+
}
|
|
872
|
+
for (const entry of WebAssembly.Module.imports(module)) {
|
|
873
|
+
if (entry.kind !== 'memory') {
|
|
874
|
+
continue;
|
|
875
|
+
}
|
|
876
|
+
if (memory) {
|
|
877
|
+
throw new Error('Multiple wasm memories are not supported.');
|
|
878
|
+
}
|
|
879
|
+
const value = getImportValue(imports, entry.module, entry.name);
|
|
880
|
+
if (!(value instanceof WebAssembly.Memory)) {
|
|
881
|
+
throw new Error(
|
|
882
|
+
\`Imported value is not a WebAssembly.Memory: \${entry.module}.\${entry.name}\`
|
|
883
|
+
);
|
|
884
|
+
}
|
|
885
|
+
memory = value;
|
|
886
|
+
}
|
|
887
|
+
if (!memory) {
|
|
888
|
+
throw new Error('WASM memory export/import was not resolved.');
|
|
889
|
+
}
|
|
890
|
+
return memory;
|
|
891
|
+
};
|
|
892
|
+
|
|
893
|
+
const resolveTable = (
|
|
894
|
+
module: WebAssembly.Module,
|
|
895
|
+
instance: WebAssembly.Instance,
|
|
896
|
+
imports: WebAssembly.Imports | undefined
|
|
897
|
+
) => {
|
|
898
|
+
let table: WebAssembly.Table | undefined;
|
|
899
|
+
for (const entry of WebAssembly.Module.exports(module)) {
|
|
900
|
+
if (entry.kind !== 'table') {
|
|
901
|
+
continue;
|
|
902
|
+
}
|
|
903
|
+
if (table) {
|
|
904
|
+
throw new Error('Multiple wasm tables are not supported.');
|
|
905
|
+
}
|
|
906
|
+
const value = instance.exports[entry.name];
|
|
907
|
+
if (!(value instanceof WebAssembly.Table)) {
|
|
908
|
+
throw new Error(\`Export is not a WebAssembly.Table: \${entry.name}\`);
|
|
909
|
+
}
|
|
910
|
+
table = value;
|
|
911
|
+
}
|
|
912
|
+
if (table) {
|
|
913
|
+
return table;
|
|
914
|
+
}
|
|
915
|
+
for (const entry of WebAssembly.Module.imports(module)) {
|
|
916
|
+
if (entry.kind !== 'table') {
|
|
917
|
+
continue;
|
|
918
|
+
}
|
|
919
|
+
if (table) {
|
|
920
|
+
throw new Error('Multiple wasm tables are not supported.');
|
|
921
|
+
}
|
|
922
|
+
const value = getImportValue(imports, entry.module, entry.name);
|
|
923
|
+
if (!(value instanceof WebAssembly.Table)) {
|
|
924
|
+
throw new Error(
|
|
925
|
+
\`Imported value is not a WebAssembly.Table: \${entry.module}.\${entry.name}\`
|
|
926
|
+
);
|
|
927
|
+
}
|
|
928
|
+
table = value;
|
|
929
|
+
}
|
|
930
|
+
return table;
|
|
931
|
+
};
|
|
932
|
+
|
|
933
|
+
/**
|
|
934
|
+
* Load and instantiate a WASM module from the provided source.
|
|
935
|
+
*
|
|
936
|
+
* @typeParam TExports Function exports exposed through the exports property.
|
|
937
|
+
* @typeParam TAllExports Full WebAssembly export map exposed through the allExports property.
|
|
938
|
+
* @param source WASM source to load.
|
|
939
|
+
* @param options WebAssembly instantiation options.
|
|
940
|
+
* @returns The instantiated WASM module and resolved runtime handles.
|
|
941
|
+
*/
|
|
942
|
+
export const loadWasm = async <
|
|
943
|
+
TExports extends object = Record<string, (...args: unknown[]) => unknown>,
|
|
944
|
+
TAllExports extends WebAssembly.Exports = WebAssembly.Exports>(
|
|
945
|
+
source: WasmSource,
|
|
946
|
+
options?: WasmLoadOptions
|
|
947
|
+
): Promise<WasmInstance<TExports, TAllExports>> => {
|
|
948
|
+
const bytes = await resolveWasmBytes(source);
|
|
949
|
+
const module = await WebAssembly.compile(bytes);
|
|
950
|
+
const instance = await WebAssembly.instantiate(module, options?.imports ?? {});
|
|
951
|
+
|
|
952
|
+
const functionExports: Record<string, (...args: unknown[]) => unknown> = {};
|
|
953
|
+
for (const entry of WebAssembly.Module.exports(module)) {
|
|
954
|
+
if (entry.kind !== 'function') {
|
|
955
|
+
continue;
|
|
956
|
+
}
|
|
957
|
+
const value = instance.exports[entry.name];
|
|
958
|
+
if (typeof value !== 'function') {
|
|
959
|
+
throw new Error(\`Export is not a function: \${entry.name}\`);
|
|
960
|
+
}
|
|
961
|
+
functionExports[entry.name] = value as (...args: unknown[]) => unknown;
|
|
962
|
+
}
|
|
963
|
+
|
|
964
|
+
const initialize =
|
|
965
|
+
typeof instance.exports._initialize === 'function'
|
|
966
|
+
? (instance.exports._initialize as () => unknown)
|
|
967
|
+
: undefined;
|
|
968
|
+
const start =
|
|
969
|
+
typeof instance.exports._start === 'function'
|
|
970
|
+
? (instance.exports._start as () => unknown)
|
|
971
|
+
: undefined;
|
|
972
|
+
|
|
973
|
+
return {
|
|
974
|
+
exports: functionExports as TExports,
|
|
975
|
+
allExports: instance.exports as TAllExports,
|
|
976
|
+
memory: resolveMemory(module, instance, options?.imports),
|
|
977
|
+
table: resolveTable(module, instance, options?.imports),
|
|
978
|
+
rawExports: instance.exports,
|
|
979
|
+
module,
|
|
980
|
+
instance,
|
|
981
|
+
initialize,
|
|
982
|
+
start,
|
|
983
|
+
};
|
|
984
|
+
};
|
|
985
|
+
|
|
986
|
+
${targets.map((target) => {
|
|
987
|
+
const defaultSourceSpecifier = toRelativeImportSpecifier(generatedLoaderFile, target.outFile);
|
|
988
|
+
const specifier = JSON.stringify(defaultSourceSpecifier);
|
|
989
|
+
return `/**
|
|
990
|
+
* Load and instantiate the generated "${target.targetName}" WASM target.
|
|
991
|
+
*
|
|
992
|
+
* @typeParam TExports Function exports exposed through the exports property.
|
|
993
|
+
* @typeParam TAllExports Full WebAssembly export map exposed through the allExports property.
|
|
994
|
+
* @param options Override the target source or WebAssembly imports.
|
|
995
|
+
* @returns The instantiated WASM target and resolved runtime handles.
|
|
996
|
+
* @remarks When options.source is omitted, the loader resolves "${defaultSourceSpecifier}" relative to this file.
|
|
997
|
+
*/
|
|
998
|
+
export const ${target.functionName} = async <
|
|
999
|
+
TExports extends object = Record<string, (...args: unknown[]) => unknown>,
|
|
1000
|
+
TAllExports extends WebAssembly.Exports = WebAssembly.Exports>(
|
|
1001
|
+
options?: TargetWasmLoadOptions
|
|
1002
|
+
): Promise<WasmInstance<TExports, TAllExports>> => {
|
|
1003
|
+
const source = options?.source ?? new URL(${specifier}, import.meta.url);
|
|
1004
|
+
return await loadWasm<TExports, TAllExports>(source, createWasmLoadOptions(options?.imports));
|
|
1005
|
+
};`;
|
|
1006
|
+
}).join("\n\n")}
|
|
1007
|
+
`;
|
|
1008
|
+
};
|
|
1009
|
+
var isRecord = (value) => value !== null && typeof value === "object" && !Array.isArray(value);
|
|
1010
|
+
var resolvePackageJsonPath = async (startPath, packageName) => {
|
|
1011
|
+
let current = dirname(startPath);
|
|
1012
|
+
for (;;) {
|
|
1013
|
+
const candidate = join(current, "package.json");
|
|
1014
|
+
if (await pathExists(candidate)) return candidate;
|
|
1015
|
+
const parent = dirname(current);
|
|
1016
|
+
if (parent === current) throw new Error(`package.json not found for import: ${packageName}`);
|
|
1017
|
+
current = parent;
|
|
1018
|
+
}
|
|
1019
|
+
};
|
|
1020
|
+
var loadPackageJson = async (packageJsonPath, packageName) => {
|
|
1021
|
+
try {
|
|
1022
|
+
const raw = await readFile(packageJsonPath, "utf8");
|
|
1023
|
+
const parsed = JSON.parse(raw);
|
|
1024
|
+
if (!isRecord(parsed)) throw new Error("package.json must be an object.");
|
|
1025
|
+
return parsed;
|
|
1026
|
+
} catch (error) {
|
|
1027
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
1028
|
+
throw new Error(`Failed to read package.json for import ${packageName}: ${message}`);
|
|
1029
|
+
}
|
|
1030
|
+
};
|
|
1031
|
+
var resolveImportPaths = async (resolver, packageName) => {
|
|
1032
|
+
let resolvedEntry;
|
|
1033
|
+
try {
|
|
1034
|
+
resolvedEntry = resolver.resolve(packageName);
|
|
1035
|
+
} catch (error) {
|
|
1036
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
1037
|
+
throw new Error(`Failed to resolve import ${packageName}: ${message}`);
|
|
1038
|
+
}
|
|
1039
|
+
const packageJsonPath = await resolvePackageJsonPath(resolvedEntry, packageName);
|
|
1040
|
+
const packageRoot = dirname(packageJsonPath);
|
|
1041
|
+
const emsdkConfigRaw = (await loadPackageJson(packageJsonPath, packageName))["emsdk-env"];
|
|
1042
|
+
if (emsdkConfigRaw !== void 0 && !isRecord(emsdkConfigRaw)) throw new Error(`Invalid emsdk-env config for import ${packageName}: expected an object.`);
|
|
1043
|
+
const includeRaw = isRecord(emsdkConfigRaw) ? emsdkConfigRaw.include : void 0;
|
|
1044
|
+
if (includeRaw !== void 0 && typeof includeRaw !== "string") throw new Error(`Invalid emsdk-env include for import ${packageName}: expected a string.`);
|
|
1045
|
+
const libRaw = isRecord(emsdkConfigRaw) ? emsdkConfigRaw.lib : void 0;
|
|
1046
|
+
if (libRaw !== void 0 && typeof libRaw !== "string") throw new Error(`Invalid emsdk-env lib for import ${packageName}: expected a string.`);
|
|
1047
|
+
const includeRel = includeRaw !== null && includeRaw !== void 0 ? includeRaw : DEFAULT_IMPORT_INCLUDE_DIR;
|
|
1048
|
+
const libRel = libRaw !== null && libRaw !== void 0 ? libRaw : DEFAULT_IMPORT_LIB_DIR;
|
|
1049
|
+
const includeDir = resolve(packageRoot, includeRel);
|
|
1050
|
+
const libDir = resolve(packageRoot, libRel);
|
|
1051
|
+
const includeExists = await pathExists(includeDir);
|
|
1052
|
+
const libExists = await pathExists(libDir);
|
|
1053
|
+
if (!includeExists && !libExists) throw new Error(`Import ${packageName} does not provide include or lib directories.`);
|
|
1054
|
+
return {
|
|
1055
|
+
includeDir: includeExists ? includeDir : void 0,
|
|
1056
|
+
libDir: libExists ? libDir : void 0
|
|
1057
|
+
};
|
|
1058
|
+
};
|
|
1059
|
+
var resolveImportDirectories = async (rootDir, imports) => {
|
|
1060
|
+
if (imports.length === 0) return {
|
|
1061
|
+
includeDirs: [],
|
|
1062
|
+
libDirs: []
|
|
1063
|
+
};
|
|
1064
|
+
const resolver = (await import("node:module")).createRequire(resolve(rootDir, "package.json"));
|
|
1065
|
+
const includeDirs = [];
|
|
1066
|
+
const libDirs = [];
|
|
1067
|
+
for (const packageName of imports) {
|
|
1068
|
+
const resolved = await resolveImportPaths(resolver, packageName);
|
|
1069
|
+
if (resolved.includeDir) includeDirs.push(resolved.includeDir);
|
|
1070
|
+
if (resolved.libDir) libDirs.push(resolved.libDir);
|
|
1071
|
+
}
|
|
1072
|
+
return {
|
|
1073
|
+
includeDirs: dedupeValues(includeDirs),
|
|
1074
|
+
libDirs: dedupeValues(libDirs)
|
|
1075
|
+
};
|
|
1076
|
+
};
|
|
1077
|
+
var resolveGeneratedLoaderOutFile = (rootDir, env, generatedLoader) => {
|
|
1078
|
+
var _generatedLoader$outF;
|
|
1079
|
+
if (!(generatedLoader === null || generatedLoader === void 0 ? void 0 : generatedLoader.enable)) return;
|
|
1080
|
+
return resolvePath(rootDir, expandPlaceholders((_generatedLoader$outF = generatedLoader.outFile) !== null && _generatedLoader$outF !== void 0 ? _generatedLoader$outF : DEFAULT_GENERATED_LOADER_OUT_FILE, env, "generatedLoader.outFile"));
|
|
1081
|
+
};
|
|
1082
|
+
var resolveGeneratedLoaderWatchDirs = (rootDir, srcDir, commonIncludeDirs, env, targetEntries, importIncludeDirs) => {
|
|
1083
|
+
const dirs = [srcDir, ...resolveIncludeDirs(commonIncludeDirs, env, rootDir)];
|
|
1084
|
+
for (const [targetName, target] of targetEntries) {
|
|
1085
|
+
if (!target.includeDirs || target.includeDirs.length === 0) continue;
|
|
1086
|
+
const targetEnv = {
|
|
1087
|
+
...env,
|
|
1088
|
+
TARGET_NAME: targetName
|
|
1089
|
+
};
|
|
1090
|
+
dirs.push(...resolveIncludeDirs(target.includeDirs, targetEnv, rootDir));
|
|
1091
|
+
}
|
|
1092
|
+
dirs.push(...importIncludeDirs);
|
|
1093
|
+
return dedupeValues(dirs);
|
|
1094
|
+
};
|
|
1095
|
+
var validateGeneratedLoaderOutFile = (generatedLoaderFile, watchDirs) => {
|
|
1096
|
+
for (const dir of watchDirs) if (isSubPath(dir, generatedLoaderFile)) throw new Error(`generatedLoader.outFile must not be placed under watched directory: ${generatedLoaderFile}`);
|
|
1097
|
+
};
|
|
1098
|
+
var buildCompileArgs = (options, includeDirs, defines, env, rootDir) => {
|
|
1099
|
+
return {
|
|
1100
|
+
resolvedOptions: expandArray(options, env, "options"),
|
|
1101
|
+
includeArgs: resolveIncludeDirs(includeDirs, env, rootDir).map((dir) => `-I${dir}`),
|
|
1102
|
+
defineArgs: buildDefineFlags(resolveDefines(defines, env))
|
|
1103
|
+
};
|
|
1104
|
+
};
|
|
1105
|
+
/**
|
|
1106
|
+
* Build WASM binaries (and optional archives) from C/C++ sources using emsdk.
|
|
1107
|
+
*
|
|
1108
|
+
* Resolves the SDK via prepareEmsdk, expands build paths, compiles sources,
|
|
1109
|
+
* links targets, and returns output paths keyed by target name.
|
|
1110
|
+
*
|
|
1111
|
+
* @param options - Build options including rule definitions and shared settings.
|
|
1112
|
+
* @returns Build result with the resolved SDK root and output file paths.
|
|
1113
|
+
*/
|
|
1114
|
+
var buildWasm = async (options) => {
|
|
1115
|
+
var _options$logger, _options$root, _options$srcDir, _options$includeDir, _options$outDir, _options$libDir, _options$buildDir, _options$cleanupBuild, _options$parallel, _options$rule$common;
|
|
1116
|
+
if (!options) throw new TypeError("options must be provided.");
|
|
1117
|
+
if (!options.rule || !options.rule.targets) throw new TypeError("rule targets must be provided.");
|
|
1118
|
+
const targetEntries = Object.entries(options.rule.targets);
|
|
1119
|
+
if (targetEntries.length === 0) throw new TypeError("rule targets must not be empty.");
|
|
1120
|
+
const logger = (_options$logger = options.logger) !== null && _options$logger !== void 0 ? _options$logger : createConsoleLogger("emsdk-env");
|
|
1121
|
+
const rootDir = resolve((_options$root = options.root) !== null && _options$root !== void 0 ? _options$root : process.cwd());
|
|
1122
|
+
const emsdkOptions = normalizePrepareOptions(options.emsdk);
|
|
1123
|
+
const emsdkRoot = await prepareEmsdk(emsdkOptions);
|
|
1124
|
+
const emsdkEnv = await loadEmsdkEnv(emsdkRoot, logger, emsdkOptions.signal);
|
|
1125
|
+
const baseEnv = {
|
|
1126
|
+
...emsdkEnv,
|
|
1127
|
+
ROOT: rootDir
|
|
1128
|
+
};
|
|
1129
|
+
const rawSrcDir = expandPlaceholders((_options$srcDir = options.srcDir) !== null && _options$srcDir !== void 0 ? _options$srcDir : DEFAULT_WASM_SRC_DIR, baseEnv, "srcDir");
|
|
1130
|
+
const rawIncludeDir = expandPlaceholders((_options$includeDir = options.includeDir) !== null && _options$includeDir !== void 0 ? _options$includeDir : DEFAULT_WASM_INCLUDE_DIR, baseEnv, "includeDir");
|
|
1131
|
+
const rawOutDir = expandPlaceholders((_options$outDir = options.outDir) !== null && _options$outDir !== void 0 ? _options$outDir : DEFAULT_WASM_OUT_DIR, baseEnv, "outDir");
|
|
1132
|
+
const rawLibDir = expandPlaceholders((_options$libDir = options.libDir) !== null && _options$libDir !== void 0 ? _options$libDir : DEFAULT_WASM_LIB_DIR, baseEnv, "libDir");
|
|
1133
|
+
const rawBuildDir = expandPlaceholders((_options$buildDir = options.buildDir) !== null && _options$buildDir !== void 0 ? _options$buildDir : DEFAULT_WASM_BUILD_DIR, baseEnv, "buildDir");
|
|
1134
|
+
const srcDir = resolvePath(rootDir, rawSrcDir);
|
|
1135
|
+
const includeDir = resolvePath(rootDir, rawIncludeDir);
|
|
1136
|
+
const outDir = resolvePath(rootDir, rawOutDir);
|
|
1137
|
+
const libDir = resolvePath(rootDir, rawLibDir);
|
|
1138
|
+
const buildDir = resolvePath(rootDir, rawBuildDir);
|
|
1139
|
+
const buildId = createBuildId();
|
|
1140
|
+
const buildRunDir = resolve(buildDir, buildId);
|
|
1141
|
+
const cleanupBuildDir = (_options$cleanupBuild = options.cleanupBuildDir) !== null && _options$cleanupBuild !== void 0 ? _options$cleanupBuild : true;
|
|
1142
|
+
const parallel = (_options$parallel = options.parallel) !== null && _options$parallel !== void 0 ? _options$parallel : true;
|
|
1143
|
+
const envWithDirs = {
|
|
1144
|
+
...emsdkEnv,
|
|
1145
|
+
ROOT: rootDir,
|
|
1146
|
+
SRC_DIR: srcDir,
|
|
1147
|
+
INCLUDE_DIR: includeDir,
|
|
1148
|
+
OUT_DIR: outDir,
|
|
1149
|
+
LIB_DIR: libDir,
|
|
1150
|
+
BUILD_DIR: buildDir
|
|
1151
|
+
};
|
|
1152
|
+
const emccCommand = await resolveEmccCommand(envWithDirs, emsdkRoot);
|
|
1153
|
+
const common = (_options$rule$common = options.rule.common) !== null && _options$rule$common !== void 0 ? _options$rule$common : {};
|
|
1154
|
+
const commonIncludeDirs = common.includeDirs === void 0 ? [includeDir] : common.includeDirs;
|
|
1155
|
+
const importDirectories = await resolveImportDirectories(rootDir, ensureArray(options.imports));
|
|
1156
|
+
const importIncludeDirs = importDirectories.includeDirs;
|
|
1157
|
+
const importLibDirs = importDirectories.libDirs;
|
|
1158
|
+
const linkLibDirs = dedupeValues([libDir, ...importLibDirs]);
|
|
1159
|
+
const generatedLoaderFile = resolveGeneratedLoaderOutFile(rootDir, envWithDirs, options.generatedLoader);
|
|
1160
|
+
if (generatedLoaderFile) validateGeneratedLoaderOutFile(generatedLoaderFile, resolveGeneratedLoaderWatchDirs(rootDir, srcDir, commonIncludeDirs, envWithDirs, targetEntries, importIncludeDirs));
|
|
1161
|
+
logger.debug(`Detected rootDir: '${rootDir}'`);
|
|
1162
|
+
logger.debug(`Detected srcDir: '${srcDir}'`);
|
|
1163
|
+
logger.debug(`Detected outDir: '${outDir}'`);
|
|
1164
|
+
logger.debug(`Detected libDir: '${libDir}'`);
|
|
1165
|
+
logger.debug(`Detected buildDir: '${buildDir}'`);
|
|
1166
|
+
logger.debug(`Detected buildId: '${buildId}'`);
|
|
1167
|
+
logger.debug(`Detected buildRunDir: '${buildRunDir}'`);
|
|
1168
|
+
logger.debug(`Detected cleanupBuildDir: ${cleanupBuildDir}`);
|
|
1169
|
+
logger.debug(`Detected parallel: ${parallel}`);
|
|
1170
|
+
logger.debug(`Detected emccCommand: '${emccCommand}'`);
|
|
1171
|
+
logger.debug(`Detected importIncludeDirs: [${importIncludeDirs.map((p) => `'${p}'`).join(",")}]`);
|
|
1172
|
+
logger.debug(`Detected importLibDirs: [${importLibDirs.map((p) => `'${p}'`).join(",")}]`);
|
|
1173
|
+
if (generatedLoaderFile) logger.debug(`Detected generatedLoaderFile: '${generatedLoaderFile}'`);
|
|
1174
|
+
await ensureDirectory(outDir);
|
|
1175
|
+
await ensureDirectory(libDir);
|
|
1176
|
+
await ensureDirectory(buildDir);
|
|
1177
|
+
await rm(buildRunDir, {
|
|
1178
|
+
recursive: true,
|
|
1179
|
+
force: true
|
|
1180
|
+
});
|
|
1181
|
+
await ensureDirectory(buildRunDir);
|
|
1182
|
+
const emarCommand = targetEntries.some(([, target]) => resolveTargetType(target.type) === "archive") ? await resolveEmarCommand(envWithDirs, emsdkRoot) : void 0;
|
|
1183
|
+
if (emarCommand) logger.debug(`Detected emarCommand: '${emarCommand}'`);
|
|
1184
|
+
let wasmOptCommand;
|
|
1185
|
+
const getWasmOptCommand = async () => {
|
|
1186
|
+
if (wasmOptCommand) return wasmOptCommand;
|
|
1187
|
+
wasmOptCommand = await resolveWasmOptCommand(envWithDirs, emsdkRoot);
|
|
1188
|
+
logger.debug(`Detected wasmOptCommand: '${wasmOptCommand}'`);
|
|
1189
|
+
return wasmOptCommand;
|
|
1190
|
+
};
|
|
1191
|
+
const outFiles = {};
|
|
1192
|
+
let resultGeneratedLoaderFile;
|
|
1193
|
+
const buildTargets = async (expectedType) => {
|
|
1194
|
+
for (const [targetName, target] of targetEntries) {
|
|
1195
|
+
var _target$sourceGroups;
|
|
1196
|
+
const targetType = resolveTargetType(target.type);
|
|
1197
|
+
if (targetType !== expectedType) continue;
|
|
1198
|
+
if (targetType === "archive") {
|
|
1199
|
+
if (target.linkOptions !== void 0) throw new Error(`linkOptions is not supported for archive target: ${targetName}`);
|
|
1200
|
+
if (target.linkDirectives !== void 0) throw new Error(`linkDirectives is not supported for archive target: ${targetName}`);
|
|
1201
|
+
if (target.exports !== void 0) throw new Error(`exports is not supported for archive target: ${targetName}`);
|
|
1202
|
+
if (target.wasmOpt !== void 0) throw new Error(`wasmOpt is not supported for archive target: ${targetName}`);
|
|
1203
|
+
}
|
|
1204
|
+
const mergedLinkOptions = targetType === "archive" ? [] : [...ensureArray(common.linkOptions), ...ensureArray(target.linkOptions)];
|
|
1205
|
+
const mergedLinkDirectives = targetType === "archive" ? {} : mergeLinkDirectives(common.linkDirectives, target.linkDirectives);
|
|
1206
|
+
const mergedExports = targetType === "archive" ? [] : [...ensureArray(common.exports), ...ensureArray(target.exports)];
|
|
1207
|
+
const wasmOptEnabled = targetType === "archive" ? false : resolveWasmOptEnabled(common.wasmOpt, target.wasmOpt);
|
|
1208
|
+
const baseCompileOptions = [...ensureArray(common.options), ...ensureArray(target.options)];
|
|
1209
|
+
const baseIncludeDirs = [
|
|
1210
|
+
...ensureArray(commonIncludeDirs),
|
|
1211
|
+
...ensureArray(target.includeDirs),
|
|
1212
|
+
...importIncludeDirs
|
|
1213
|
+
];
|
|
1214
|
+
const baseDefines = mergeDefines(common.defines, target.defines);
|
|
1215
|
+
const sourceGroups = (_target$sourceGroups = target.sourceGroups) !== null && _target$sourceGroups !== void 0 ? _target$sourceGroups : [];
|
|
1216
|
+
const targetEnv = {
|
|
1217
|
+
...envWithDirs,
|
|
1218
|
+
TARGET_NAME: targetName
|
|
1219
|
+
};
|
|
1220
|
+
const buildEnv = createEnvForBuild(targetEnv, {});
|
|
1221
|
+
const resolvedOutFile = resolveTargetOutFile(targetName, target.outFile, targetEnv, targetType === "archive" ? libDir : outDir, targetType === "archive" ? "a" : "wasm");
|
|
1222
|
+
const sources = await resolveTargetSources(target.sources, targetEnv, srcDir);
|
|
1223
|
+
const groupSources = sourceGroups.map(() => []);
|
|
1224
|
+
const groupSourceSet = /* @__PURE__ */ new Set();
|
|
1225
|
+
for (let index = 0; index < sourceGroups.length; index += 1) {
|
|
1226
|
+
const group = sourceGroups[index];
|
|
1227
|
+
if (!group) continue;
|
|
1228
|
+
const deduped = dedupeSources(await resolveSourcesFromPatterns(group.sources, targetEnv, srcDir, `sourceGroups[${index}].sources`));
|
|
1229
|
+
groupSources[index] = deduped;
|
|
1230
|
+
for (const source of deduped) groupSourceSet.add(source);
|
|
1231
|
+
}
|
|
1232
|
+
const baseSources = sources.filter((source) => !groupSourceSet.has(source));
|
|
1233
|
+
const groupedSources = groupSources.flat();
|
|
1234
|
+
if (baseSources.length + groupedSources.length === 0) throw new Error(`No sources matched for target: ${targetName}`);
|
|
1235
|
+
const targetBuildDir = resolve(buildRunDir, targetName);
|
|
1236
|
+
await rm(targetBuildDir, {
|
|
1237
|
+
recursive: true,
|
|
1238
|
+
force: true
|
|
1239
|
+
});
|
|
1240
|
+
await ensureDirectory(targetBuildDir);
|
|
1241
|
+
const linkDirectiveArgs = buildLinkDirectiveFlags(targetType === "archive" ? {} : resolveLinkDirectives(mergedLinkDirectives, targetEnv));
|
|
1242
|
+
const resolvedLinkOptions = targetType === "archive" ? [] : [...linkDirectiveArgs, ...expandArray(mergedLinkOptions, targetEnv, "linkOptions")];
|
|
1243
|
+
const exportArgs = buildExportFlags(targetType === "archive" ? [] : expandArray(mergedExports, targetEnv, "exports"));
|
|
1244
|
+
const resolvedWasmOptArgs = wasmOptEnabled ? resolveWasmOptArgs(common.wasmOpt, target.wasmOpt, targetEnv) : [];
|
|
1245
|
+
const baseCompileArgs = buildCompileArgs(baseCompileOptions, baseIncludeDirs, baseDefines, targetEnv, rootDir);
|
|
1246
|
+
const groupCompileArgs = sourceGroups.map((group) => {
|
|
1247
|
+
return buildCompileArgs([...baseCompileOptions, ...ensureArray(group === null || group === void 0 ? void 0 : group.options)], [...baseIncludeDirs, ...ensureArray(group === null || group === void 0 ? void 0 : group.includeDirs)], mergeDefines(baseDefines, group === null || group === void 0 ? void 0 : group.defines), targetEnv, rootDir);
|
|
1248
|
+
});
|
|
1249
|
+
const compileSource = async (source, args, groupIndex) => {
|
|
1250
|
+
const objectName = toSafeObjectName(rootDir, source, groupIndex);
|
|
1251
|
+
const outputObject = resolve(targetBuildDir, `${objectName}.o`);
|
|
1252
|
+
const compileArgs = [
|
|
1253
|
+
"-c",
|
|
1254
|
+
source,
|
|
1255
|
+
"-o",
|
|
1256
|
+
outputObject,
|
|
1257
|
+
...args.resolvedOptions,
|
|
1258
|
+
...args.includeArgs,
|
|
1259
|
+
...args.defineArgs
|
|
1260
|
+
];
|
|
1261
|
+
const sourcePath = relative(rootDir, source);
|
|
1262
|
+
logger.info(`Compiling source: ${sourcePath} --> $tmp/${objectName}.o`);
|
|
1263
|
+
logger.debug(`emcc ${compileArgs.join(" ")}`);
|
|
1264
|
+
await runCommandWithEnv(emccCommand, compileArgs, rootDir, buildEnv, emsdkOptions.signal);
|
|
1265
|
+
return outputObject;
|
|
1266
|
+
};
|
|
1267
|
+
const buildObjectsSequential = async () => {
|
|
1268
|
+
const objectFiles = [];
|
|
1269
|
+
for (const source of baseSources) objectFiles.push(await compileSource(source, baseCompileArgs, void 0));
|
|
1270
|
+
for (let index = 0; index < groupSources.length; index += 1) {
|
|
1271
|
+
const sourcesInGroup = groupSources[index];
|
|
1272
|
+
if (!sourcesInGroup) continue;
|
|
1273
|
+
const groupArgs = groupCompileArgs[index];
|
|
1274
|
+
if (!groupArgs) continue;
|
|
1275
|
+
for (const source of sourcesInGroup) objectFiles.push(await compileSource(source, groupArgs, index));
|
|
1276
|
+
}
|
|
1277
|
+
return objectFiles;
|
|
1278
|
+
};
|
|
1279
|
+
const compileJobs = [];
|
|
1280
|
+
for (const source of baseSources) compileJobs.push({
|
|
1281
|
+
source,
|
|
1282
|
+
args: baseCompileArgs,
|
|
1283
|
+
groupIndex: void 0
|
|
1284
|
+
});
|
|
1285
|
+
for (let index = 0; index < groupSources.length; index += 1) {
|
|
1286
|
+
const sourcesInGroup = groupSources[index];
|
|
1287
|
+
if (!sourcesInGroup) continue;
|
|
1288
|
+
const groupArgs = groupCompileArgs[index];
|
|
1289
|
+
if (!groupArgs) continue;
|
|
1290
|
+
for (const source of sourcesInGroup) compileJobs.push({
|
|
1291
|
+
source,
|
|
1292
|
+
args: groupArgs,
|
|
1293
|
+
groupIndex: index
|
|
1294
|
+
});
|
|
1295
|
+
}
|
|
1296
|
+
logger.info(parallel ? `Building target: '${targetName}' [${compileJobs.length} files, in parallel]` : `Building target: '${targetName}' [${compileJobs.length} files]`);
|
|
1297
|
+
const objectFiles = parallel ? await Promise.all(compileJobs.map((job) => compileSource(job.source, job.args, job.groupIndex))) : await buildObjectsSequential();
|
|
1298
|
+
if (targetType === "archive") {
|
|
1299
|
+
if (!emarCommand) throw new Error("emar command is required for archive targets.");
|
|
1300
|
+
logger.info(`Archiving target: ${targetName}.a`);
|
|
1301
|
+
const archiveArgs = [
|
|
1302
|
+
"rcs",
|
|
1303
|
+
resolvedOutFile,
|
|
1304
|
+
...objectFiles
|
|
1305
|
+
];
|
|
1306
|
+
logger.debug(`emar ${archiveArgs.join(" ")}`);
|
|
1307
|
+
await runCommandWithEnv(emarCommand, archiveArgs, rootDir, buildEnv, emsdkOptions.signal);
|
|
1308
|
+
} else {
|
|
1309
|
+
logger.info(`Linking target: ${targetName}.wasm`);
|
|
1310
|
+
const linkArgs = [
|
|
1311
|
+
...objectFiles,
|
|
1312
|
+
"-o",
|
|
1313
|
+
resolvedOutFile,
|
|
1314
|
+
...linkLibDirs.map((dir) => `-L${dir}`),
|
|
1315
|
+
...resolvedLinkOptions,
|
|
1316
|
+
...exportArgs
|
|
1317
|
+
];
|
|
1318
|
+
logger.debug(`emcc ${linkArgs.join(" ")}`);
|
|
1319
|
+
await runCommandWithEnv(emccCommand, linkArgs, rootDir, buildEnv, emsdkOptions.signal);
|
|
1320
|
+
if (wasmOptEnabled) {
|
|
1321
|
+
const wasmOptInput = resolveWasmOptInputFile(resolvedOutFile, resolvedLinkOptions);
|
|
1322
|
+
if (!await pathExists(wasmOptInput)) throw new Error(`wasm-opt enabled but wasm binary not found: ${wasmOptInput}`);
|
|
1323
|
+
const tempOutFile = `${wasmOptInput}.opt`;
|
|
1324
|
+
const wasmOptArgs = [
|
|
1325
|
+
wasmOptInput,
|
|
1326
|
+
"-o",
|
|
1327
|
+
tempOutFile,
|
|
1328
|
+
...resolvedWasmOptArgs
|
|
1329
|
+
];
|
|
1330
|
+
const wasmOptCommand = await getWasmOptCommand();
|
|
1331
|
+
logger.info(`Optimizing target: ${targetName}.wasm`);
|
|
1332
|
+
logger.debug(`wasm-opt ${wasmOptArgs.join(" ")}`);
|
|
1333
|
+
await runCommandWithEnv(wasmOptCommand, wasmOptArgs, rootDir, buildEnv, emsdkOptions.signal);
|
|
1334
|
+
await rm(wasmOptInput, { force: true });
|
|
1335
|
+
await rename(tempOutFile, wasmOptInput);
|
|
1336
|
+
}
|
|
1337
|
+
}
|
|
1338
|
+
outFiles[targetName] = resolvedOutFile;
|
|
1339
|
+
}
|
|
1340
|
+
};
|
|
1341
|
+
try {
|
|
1342
|
+
await buildTargets("archive");
|
|
1343
|
+
await buildTargets("wasm");
|
|
1344
|
+
if (generatedLoaderFile) {
|
|
1345
|
+
const generatedTargets = [];
|
|
1346
|
+
const functionNames = /* @__PURE__ */ new Set();
|
|
1347
|
+
for (const [targetName, target] of targetEntries) {
|
|
1348
|
+
if (resolveTargetType(target.type) !== "wasm") continue;
|
|
1349
|
+
const outFile = outFiles[targetName];
|
|
1350
|
+
if (!outFile) continue;
|
|
1351
|
+
const functionName = `load${toPascalCaseIdentifier(targetName)}Wasm`;
|
|
1352
|
+
if (functionNames.has(functionName)) throw new Error(`Generated loader function name collision: ${functionName}`);
|
|
1353
|
+
functionNames.add(functionName);
|
|
1354
|
+
generatedTargets.push({
|
|
1355
|
+
targetName,
|
|
1356
|
+
functionName,
|
|
1357
|
+
outFile
|
|
1358
|
+
});
|
|
1359
|
+
}
|
|
1360
|
+
const didWrite = await writeTextIfChanged(generatedLoaderFile, buildGeneratedLoaderContent(generatedLoaderFile, generatedTargets));
|
|
1361
|
+
logger.info(didWrite ? `Generated loader: ${relative(rootDir, generatedLoaderFile)}` : `Generated loader unchanged: ${relative(rootDir, generatedLoaderFile)}`);
|
|
1362
|
+
resultGeneratedLoaderFile = generatedLoaderFile;
|
|
1363
|
+
}
|
|
1364
|
+
} finally {
|
|
1365
|
+
if (cleanupBuildDir) await rm(buildRunDir, {
|
|
1366
|
+
recursive: true,
|
|
1367
|
+
force: true
|
|
1368
|
+
});
|
|
1369
|
+
}
|
|
1370
|
+
return {
|
|
1371
|
+
emsdkRoot,
|
|
1372
|
+
outFiles,
|
|
1373
|
+
...resultGeneratedLoaderFile ? { generatedLoaderFile: resultGeneratedLoaderFile } : {}
|
|
1374
|
+
};
|
|
1375
|
+
};
|
|
1376
|
+
//#endregion
|
|
1377
|
+
export { createConsoleLogger as n, prepareEmsdk as r, buildWasm as t };
|
|
1378
|
+
|
|
1379
|
+
//# sourceMappingURL=build-BbwVl0T6.js.map
|