emsdk-env 0.1.0 → 0.3.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,890 @@
1
+ /*!
2
+ * name: emsdk-env
3
+ * version: 0.3.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: 4a03739b8c88111950fa216a7c10c7bfd65ea239
9
+ */
10
+
11
+ import { mkdir, access, constants, mkdtemp, rename, rm } from "fs/promises";
12
+ import { homedir, tmpdir } from "os";
13
+ import { glob } from "glob";
14
+ import { join, resolve, isAbsolute, relative } from "path";
15
+ import { spawn } from "child_process";
16
+ const __NOOP_HANDLER = () => {
17
+ };
18
+ const __NOOP_RELEASABLE = {
19
+ release: __NOOP_HANDLER,
20
+ [Symbol.dispose]: __NOOP_HANDLER
21
+ };
22
+ const toAbortError = (reason) => {
23
+ if (reason instanceof Error) {
24
+ return reason;
25
+ }
26
+ if (typeof reason === "string") {
27
+ return new Error(reason);
28
+ }
29
+ return new Error("Operation aborted");
30
+ };
31
+ const onAbort = (signal, callback) => {
32
+ if (!signal) {
33
+ return __NOOP_RELEASABLE;
34
+ }
35
+ if (signal.aborted) {
36
+ try {
37
+ callback(toAbortError(signal.reason));
38
+ } catch (error) {
39
+ console.warn("AbortHook callback error: ", error);
40
+ }
41
+ return __NOOP_RELEASABLE;
42
+ }
43
+ let abortHandler;
44
+ abortHandler = () => {
45
+ if (abortHandler) {
46
+ const reason = signal.reason;
47
+ signal.removeEventListener("abort", abortHandler);
48
+ abortHandler = void 0;
49
+ try {
50
+ callback(toAbortError(reason));
51
+ } catch (error) {
52
+ console.warn("AbortHook callback error: ", error);
53
+ }
54
+ }
55
+ };
56
+ const release = () => {
57
+ if (abortHandler) {
58
+ signal.removeEventListener("abort", abortHandler);
59
+ abortHandler = void 0;
60
+ }
61
+ };
62
+ signal.addEventListener("abort", abortHandler, { once: true });
63
+ const handle = {
64
+ release,
65
+ [Symbol.dispose]: release
66
+ };
67
+ return handle;
68
+ };
69
+ const defer = (fn) => {
70
+ if (typeof setImmediate === "function") {
71
+ setImmediate(fn);
72
+ } else {
73
+ setTimeout(fn, 0);
74
+ }
75
+ };
76
+ const ABORTED_ERROR$2 = () => new Error("Lock acquisition was aborted");
77
+ const createLockHandle = (releaseCallback) => {
78
+ let isActive = true;
79
+ const release = () => {
80
+ if (!isActive) {
81
+ return;
82
+ }
83
+ isActive = false;
84
+ releaseCallback();
85
+ };
86
+ return {
87
+ get isActive() {
88
+ return isActive;
89
+ },
90
+ release,
91
+ [Symbol.dispose]: release
92
+ };
93
+ };
94
+ const createMutex = (maxConsecutiveCalls = 20) => {
95
+ let isLocked = false;
96
+ const queue = [];
97
+ let count = 0;
98
+ const processQueue = () => {
99
+ var _a;
100
+ if (isLocked || queue.length === 0) {
101
+ return;
102
+ }
103
+ const item = queue.shift();
104
+ if ((_a = item.signal) == null ? void 0 : _a.aborted) {
105
+ item.reject(ABORTED_ERROR$2());
106
+ scheduleNextProcess();
107
+ return;
108
+ }
109
+ isLocked = true;
110
+ const lockHandle = createLockHandle(releaseLock);
111
+ item.resolve(lockHandle);
112
+ };
113
+ const scheduleNextProcess = () => {
114
+ count++;
115
+ if (count >= maxConsecutiveCalls) {
116
+ count = 0;
117
+ defer(processQueue);
118
+ } else {
119
+ processQueue();
120
+ }
121
+ };
122
+ const releaseLock = () => {
123
+ if (!isLocked) {
124
+ return;
125
+ }
126
+ isLocked = false;
127
+ scheduleNextProcess();
128
+ };
129
+ const removeFromQueue = (item) => {
130
+ const index = queue.indexOf(item);
131
+ if (index !== -1) {
132
+ queue.splice(index, 1);
133
+ }
134
+ };
135
+ const lock = async (signal) => {
136
+ if (signal) {
137
+ if (signal.aborted) {
138
+ throw ABORTED_ERROR$2();
139
+ }
140
+ return new Promise((resolve2, reject) => {
141
+ const queueItem = {
142
+ resolve: void 0,
143
+ reject: void 0,
144
+ signal
145
+ };
146
+ const abortHandle = onAbort(signal, () => {
147
+ removeFromQueue(queueItem);
148
+ reject(ABORTED_ERROR$2());
149
+ });
150
+ queueItem.resolve = (handle) => {
151
+ abortHandle.release();
152
+ resolve2(handle);
153
+ };
154
+ queueItem.reject = (error) => {
155
+ abortHandle.release();
156
+ reject(error);
157
+ };
158
+ queue.push(queueItem);
159
+ processQueue();
160
+ });
161
+ } else {
162
+ return new Promise((resolve2, reject) => {
163
+ queue.push({
164
+ resolve: resolve2,
165
+ reject
166
+ });
167
+ processQueue();
168
+ });
169
+ }
170
+ };
171
+ const result = {
172
+ lock,
173
+ waiter: {
174
+ wait: lock
175
+ },
176
+ get isLocked() {
177
+ return isLocked;
178
+ },
179
+ get pendingCount() {
180
+ return queue.length;
181
+ }
182
+ };
183
+ return result;
184
+ };
185
+ const createAbortError = () => {
186
+ const error = new Error("The operation was aborted.");
187
+ error.name = "AbortError";
188
+ return error;
189
+ };
190
+ const runCommand = async (command, args, cwd, signal) => {
191
+ signal == null ? void 0 : signal.throwIfAborted();
192
+ return new Promise((resolvePromise, rejectPromise) => {
193
+ const child = spawn(command, args, {
194
+ cwd,
195
+ stdio: "inherit"
196
+ });
197
+ let settled = false;
198
+ const onAbort2 = () => {
199
+ if (settled) {
200
+ return;
201
+ }
202
+ settled = true;
203
+ child.kill();
204
+ rejectPromise(createAbortError());
205
+ };
206
+ signal == null ? void 0 : signal.addEventListener("abort", onAbort2, { once: true });
207
+ const cleanup = () => {
208
+ signal == null ? void 0 : signal.removeEventListener("abort", onAbort2);
209
+ };
210
+ child.once("error", (error) => {
211
+ if (settled) {
212
+ return;
213
+ }
214
+ settled = true;
215
+ cleanup();
216
+ rejectPromise(error);
217
+ });
218
+ child.once("close", (code) => {
219
+ if (settled) {
220
+ return;
221
+ }
222
+ settled = true;
223
+ cleanup();
224
+ if (code === 0) {
225
+ resolvePromise();
226
+ return;
227
+ }
228
+ rejectPromise(
229
+ new Error(
230
+ `Command failed: ${command} ${args.join(" ")} (exit code ${code})`
231
+ )
232
+ );
233
+ });
234
+ });
235
+ };
236
+ const runCommandWithEnv = async (command, args, cwd, env, signal) => {
237
+ signal == null ? void 0 : signal.throwIfAborted();
238
+ return new Promise((resolvePromise, rejectPromise) => {
239
+ const child = spawn(command, args, {
240
+ cwd,
241
+ env,
242
+ stdio: "inherit"
243
+ });
244
+ let settled = false;
245
+ const onAbort2 = () => {
246
+ if (settled) {
247
+ return;
248
+ }
249
+ settled = true;
250
+ child.kill();
251
+ rejectPromise(createAbortError());
252
+ };
253
+ signal == null ? void 0 : signal.addEventListener("abort", onAbort2, { once: true });
254
+ const cleanup = () => {
255
+ signal == null ? void 0 : signal.removeEventListener("abort", onAbort2);
256
+ };
257
+ child.once("error", (error) => {
258
+ if (settled) {
259
+ return;
260
+ }
261
+ settled = true;
262
+ cleanup();
263
+ rejectPromise(error);
264
+ });
265
+ child.once("close", (code) => {
266
+ if (settled) {
267
+ return;
268
+ }
269
+ settled = true;
270
+ cleanup();
271
+ if (code === 0) {
272
+ resolvePromise();
273
+ return;
274
+ }
275
+ rejectPromise(
276
+ new Error(
277
+ `Command failed: ${command} ${args.join(" ")} (exit code ${code})`
278
+ )
279
+ );
280
+ });
281
+ });
282
+ };
283
+ const runCommandCapture = async (command, args, cwd, signal) => {
284
+ signal == null ? void 0 : signal.throwIfAborted();
285
+ return new Promise((resolvePromise, rejectPromise) => {
286
+ const stdoutChunks = [];
287
+ const stderrChunks = [];
288
+ const child = spawn(command, args, {
289
+ cwd,
290
+ stdio: ["ignore", "pipe", "pipe"]
291
+ });
292
+ let settled = false;
293
+ const onAbort2 = () => {
294
+ if (settled) {
295
+ return;
296
+ }
297
+ settled = true;
298
+ child.kill();
299
+ rejectPromise(createAbortError());
300
+ };
301
+ signal == null ? void 0 : signal.addEventListener("abort", onAbort2, { once: true });
302
+ const cleanup = () => {
303
+ signal == null ? void 0 : signal.removeEventListener("abort", onAbort2);
304
+ };
305
+ child.stdout.on("data", (chunk) => {
306
+ stdoutChunks.push(chunk);
307
+ });
308
+ child.stderr.on("data", (chunk) => {
309
+ stderrChunks.push(chunk);
310
+ });
311
+ child.once("error", (error) => {
312
+ if (settled) {
313
+ return;
314
+ }
315
+ settled = true;
316
+ cleanup();
317
+ rejectPromise(error);
318
+ });
319
+ child.once("close", (code) => {
320
+ if (settled) {
321
+ return;
322
+ }
323
+ settled = true;
324
+ cleanup();
325
+ if (code === 0) {
326
+ resolvePromise(Buffer.concat(stdoutChunks));
327
+ return;
328
+ }
329
+ const stderrText = Buffer.concat(stderrChunks).toString("utf8");
330
+ rejectPromise(
331
+ new Error(
332
+ `Command failed: ${command} ${args.join(" ")} (exit code ${code})${stderrText ? `
333
+ ${stderrText}` : ""}`
334
+ )
335
+ );
336
+ });
337
+ });
338
+ };
339
+ const pathExists = async (targetPath) => {
340
+ try {
341
+ await access(targetPath, constants.F_OK);
342
+ return true;
343
+ } catch (e) {
344
+ return false;
345
+ }
346
+ };
347
+ const ensureDirectory = async (targetPath) => {
348
+ await mkdir(targetPath, { recursive: true });
349
+ };
350
+ const DEFAULT_REPO_URL = "https://github.com/emscripten-core/emsdk.git";
351
+ const DEFAULT_GIT_REF = "main";
352
+ const DEFAULT_CACHE_DIR = join(homedir(), ".cache", "emsdk-env");
353
+ const DEFAULT_TARGET_VERSION = "latest";
354
+ const versionMutexes = /* @__PURE__ */ new Map();
355
+ const getVersionMutex = (key) => {
356
+ let mutex = versionMutexes.get(key);
357
+ if (!mutex) {
358
+ mutex = createMutex();
359
+ versionMutexes.set(key, mutex);
360
+ }
361
+ return mutex;
362
+ };
363
+ const ensureNonEmpty = (value, label) => {
364
+ if (value.trim().length === 0) {
365
+ throw new TypeError(`${label} must be a non-empty string.`);
366
+ }
367
+ };
368
+ const sanitizeSegment = (value) => {
369
+ const trimmed = value.trim();
370
+ ensureNonEmpty(trimmed, "targetVersion");
371
+ const sanitized = trimmed.replace(/[^A-Za-z0-9._-]/g, "_");
372
+ if (sanitized === "." || sanitized === ".." || sanitized.length === 0) {
373
+ throw new TypeError("targetVersion results in an unsafe path segment.");
374
+ }
375
+ return sanitized;
376
+ };
377
+ const resolveEmsdkCommand = () => process.platform === "win32" ? "emsdk.bat" : "./emsdk";
378
+ const runEmsdk = async (repoDir, args, signal) => {
379
+ if (process.platform === "win32") {
380
+ await runCommand("cmd", ["/c", "emsdk.bat", ...args], repoDir, signal);
381
+ return;
382
+ }
383
+ await runCommand(resolveEmsdkCommand(), args, repoDir, signal);
384
+ };
385
+ const runGitClone = async (gitPath, repoUrl, targetDir, cwd, signal) => {
386
+ await runCommand(
387
+ gitPath,
388
+ ["clone", repoUrl, targetDir, "--depth", "1", "--branch", DEFAULT_GIT_REF],
389
+ cwd,
390
+ signal
391
+ );
392
+ };
393
+ const isAlreadyExistsError = (error) => error instanceof Error && "code" in error && error.code !== void 0 && ["EEXIST", "ENOTEMPTY", "EISDIR"].includes(
394
+ String(error.code)
395
+ );
396
+ const prepareEmsdk = async (options) => {
397
+ var _a, _b, _c, _d, _e, _f, _g, _h;
398
+ if (!options) {
399
+ throw new TypeError("options must be provided.");
400
+ }
401
+ if (options.targetVersion !== void 0 && typeof options.targetVersion !== "string") {
402
+ throw new TypeError("targetVersion must be a string.");
403
+ }
404
+ const targetVersion = (_a = options.targetVersion) != null ? _a : DEFAULT_TARGET_VERSION;
405
+ ensureNonEmpty(targetVersion, "targetVersion");
406
+ (_b = options.signal) == null ? void 0 : _b.throwIfAborted();
407
+ const cacheDir = resolve((_c = options.cacheDir) != null ? _c : DEFAULT_CACHE_DIR);
408
+ const repoUrl = (_d = options.repoUrl) != null ? _d : DEFAULT_REPO_URL;
409
+ const gitPath = (_e = options.gitPath) != null ? _e : "git";
410
+ const versionDir = sanitizeSegment(targetVersion);
411
+ const finalDir = resolve(cacheDir, versionDir);
412
+ const mutex = getVersionMutex(finalDir);
413
+ const lock = await mutex.lock(options.signal);
414
+ try {
415
+ (_f = options.signal) == null ? void 0 : _f.throwIfAborted();
416
+ if (await pathExists(finalDir)) {
417
+ return finalDir;
418
+ }
419
+ await ensureDirectory(cacheDir);
420
+ const tempRoot = await mkdtemp(join(cacheDir, ".tmp-"));
421
+ const tempRepoDir = join(tempRoot, "emsdk");
422
+ try {
423
+ await runGitClone(
424
+ gitPath,
425
+ repoUrl,
426
+ tempRepoDir,
427
+ cacheDir,
428
+ options.signal
429
+ );
430
+ (_g = options.signal) == null ? void 0 : _g.throwIfAborted();
431
+ await runEmsdk(tempRepoDir, ["install", targetVersion], options.signal);
432
+ try {
433
+ await rename(tempRepoDir, finalDir);
434
+ } catch (error) {
435
+ if (isAlreadyExistsError(error)) {
436
+ return finalDir;
437
+ }
438
+ throw error;
439
+ }
440
+ } finally {
441
+ await rm(tempRoot, { recursive: true, force: true });
442
+ }
443
+ (_h = options.signal) == null ? void 0 : _h.throwIfAborted();
444
+ await runEmsdk(finalDir, ["activate", targetVersion], options.signal);
445
+ return finalDir;
446
+ } finally {
447
+ lock.release();
448
+ }
449
+ };
450
+ const shellQuote = (value) => `'${String(value).replace(/'/g, `'"'"'`)}'`;
451
+ const parseEnvBuffer = (buffer) => {
452
+ const entries = buffer.toString("utf8").split("\0");
453
+ const env = {};
454
+ for (const entry of entries) {
455
+ if (!entry) {
456
+ continue;
457
+ }
458
+ const delimiterIndex = entry.indexOf("=");
459
+ if (delimiterIndex <= 0) {
460
+ continue;
461
+ }
462
+ const key = entry.slice(0, delimiterIndex);
463
+ const value = entry.slice(delimiterIndex + 1);
464
+ env[key] = value;
465
+ }
466
+ return env;
467
+ };
468
+ const loadEmsdkEnv = async (emsdkRoot, logger, signal) => {
469
+ if (process.platform === "win32") {
470
+ throw new Error(
471
+ "Emscripten environment extraction on Windows is not implemented yet."
472
+ );
473
+ }
474
+ const envScript = resolve(emsdkRoot, "emsdk_env.sh");
475
+ if (!await pathExists(envScript)) {
476
+ throw new Error(`emsdk_env.sh not found: ${envScript}`);
477
+ }
478
+ const command = `. ${shellQuote(envScript)} >/dev/null 2>&1; env -0`;
479
+ logger.debug(`Loading emsdk environment: ${envScript}`);
480
+ const output = await runCommandCapture(
481
+ "bash",
482
+ ["-lc", command],
483
+ emsdkRoot,
484
+ signal
485
+ );
486
+ return parseEnvBuffer(output);
487
+ };
488
+ const resolveEmccCommand = async (env, emsdkRoot) => {
489
+ if (env.EMCC) {
490
+ return env.EMCC;
491
+ }
492
+ if (env.EMSCRIPTEN) {
493
+ const candidate = join(env.EMSCRIPTEN, "emcc");
494
+ if (await pathExists(candidate)) {
495
+ return candidate;
496
+ }
497
+ }
498
+ const fallback = join(emsdkRoot, "upstream", "emscripten", "emcc");
499
+ if (await pathExists(fallback)) {
500
+ return fallback;
501
+ }
502
+ return "emcc";
503
+ };
504
+ const createConsoleLogger = (prefix) => {
505
+ return {
506
+ debug: (msg) => console.debug(`[${prefix}]: ${msg}`),
507
+ info: (msg) => console.info(`[${prefix}]: ${msg}`),
508
+ warn: (msg) => console.warn(`[${prefix}]: ${msg}`),
509
+ error: (msg) => console.error(`[${prefix}]: ${msg}`)
510
+ };
511
+ };
512
+ const DEFAULT_WASM_SRC_DIR = "wasm";
513
+ const DEFAULT_WASM_OUT_DIR = join("src", "wasm");
514
+ const DEFAULT_WASM_BUILD_DIR = join(tmpdir(), "emsdk-env");
515
+ const DEFAULT_EMSDK_TARGET_VERSION = "latest";
516
+ let buildSequence = 0;
517
+ const padNumber = (value, length = 2) => String(value).padStart(length, "0");
518
+ const formatTimestamp = (date) => {
519
+ const year = date.getFullYear();
520
+ const month = padNumber(date.getMonth() + 1);
521
+ const day = padNumber(date.getDate());
522
+ const hour = padNumber(date.getHours());
523
+ const minute = padNumber(date.getMinutes());
524
+ const second = padNumber(date.getSeconds());
525
+ return `${year}${month}${day}_${hour}${minute}${second}`;
526
+ };
527
+ const createBuildId = () => {
528
+ buildSequence += 1;
529
+ const timestamp = formatTimestamp(/* @__PURE__ */ new Date());
530
+ const seq = String(buildSequence).padStart(4, "0");
531
+ return `${timestamp}_${seq}_${process.pid}`;
532
+ };
533
+ const ensureArray = (value) => value != null ? value : [];
534
+ const normalizePrepareOptions = (options) => {
535
+ const { targetVersion, ...rest } = options != null ? options : {};
536
+ return {
537
+ targetVersion: targetVersion != null ? targetVersion : DEFAULT_EMSDK_TARGET_VERSION,
538
+ ...rest
539
+ };
540
+ };
541
+ const mergeDefines = (common, target) => ({
542
+ ...common != null ? common : {},
543
+ ...target != null ? target : {}
544
+ });
545
+ const resolvePath = (rootDir, value) => isAbsolute(value) ? value : resolve(rootDir, value);
546
+ const expandPlaceholders = (value, env, label) => value.replace(/\{([A-Z0-9_]+)\}/g, (_match, key) => {
547
+ const replacement = env[key];
548
+ if (replacement === void 0) {
549
+ throw new Error(`Unknown placeholder {${key}} in ${label}.`);
550
+ }
551
+ return replacement;
552
+ });
553
+ const expandArray = (values, env, label) => values.map((value) => expandPlaceholders(value, env, label));
554
+ const resolveDefines = (defines, env) => {
555
+ const resolved = {};
556
+ for (const [key, value] of Object.entries(defines)) {
557
+ if (typeof value === "string") {
558
+ resolved[key] = expandPlaceholders(value, env, `defines.${key}`);
559
+ } else {
560
+ resolved[key] = value;
561
+ }
562
+ }
563
+ return resolved;
564
+ };
565
+ const resolveIncludeDirs = (includeDirs, env, rootDir) => {
566
+ const expanded = expandArray(includeDirs, env, "includeDirs");
567
+ return expanded.map((value) => resolvePath(rootDir, value));
568
+ };
569
+ const resolveOutFile = (outFile, env, outDir) => {
570
+ const expanded = expandPlaceholders(outFile, env, "outFile");
571
+ return resolvePath(outDir, expanded);
572
+ };
573
+ const resolveSourcesFromPatterns = async (patterns, env, srcDir, label) => {
574
+ const expanded = expandArray(patterns, env, label);
575
+ const resolvedPatterns = expanded.map((value) => resolvePath(srcDir, value));
576
+ const results = await Promise.all(
577
+ resolvedPatterns.map((pattern) => glob(pattern, { nodir: true }))
578
+ );
579
+ const sources = results.flat();
580
+ sources.sort();
581
+ return sources;
582
+ };
583
+ const buildDefineFlags = (defines) => Object.entries(defines).map(([key, value]) => `-D${key}=${String(value)}`);
584
+ const buildExportFlags = (exports$1) => {
585
+ if (exports$1.length === 0) {
586
+ return [];
587
+ }
588
+ return ["-s", `EXPORTED_FUNCTIONS=${JSON.stringify(exports$1)}`];
589
+ };
590
+ const createEnvForBuild = (baseEnv, overrides) => ({
591
+ ...process.env,
592
+ ...baseEnv,
593
+ ...overrides
594
+ });
595
+ const resolveTargetOutFile = (targetName, targetOutFile, env, outDir) => {
596
+ if (targetOutFile) {
597
+ return resolveOutFile(targetOutFile, env, outDir);
598
+ }
599
+ return resolve(outDir, `${targetName}.wasm`);
600
+ };
601
+ const resolveTargetSources = async (targetSources, env, srcDir) => {
602
+ const patterns = targetSources && targetSources.length > 0 ? targetSources : [join(srcDir, "**", "*.c"), join(srcDir, "**", "*.cpp")];
603
+ return resolveSourcesFromPatterns(patterns, env, srcDir, "sources");
604
+ };
605
+ const toSafeObjectName = (rootDir, sourcePath, groupIndex) => {
606
+ const baseName = relative(rootDir, sourcePath).replace(/[\\/]/g, "_").replace(/[^A-Za-z0-9._-]/g, "_");
607
+ if (groupIndex === void 0) {
608
+ return baseName;
609
+ }
610
+ return `${baseName}__g${groupIndex}`;
611
+ };
612
+ const dedupeSources = (sources) => {
613
+ const seen = /* @__PURE__ */ new Set();
614
+ const deduped = [];
615
+ for (const source of sources) {
616
+ if (seen.has(source)) {
617
+ continue;
618
+ }
619
+ seen.add(source);
620
+ deduped.push(source);
621
+ }
622
+ return deduped;
623
+ };
624
+ const buildCompileArgs = (options, includeDirs, defines, env, rootDir) => {
625
+ const resolvedOptions = expandArray(options, env, "options");
626
+ const includeArgs = resolveIncludeDirs(includeDirs, env, rootDir).map(
627
+ (dir) => `-I${dir}`
628
+ );
629
+ const defineArgs = buildDefineFlags(resolveDefines(defines, env));
630
+ return { resolvedOptions, includeArgs, defineArgs };
631
+ };
632
+ const buildWasm = async (options) => {
633
+ var _a, _b, _c, _d, _e, _f, _g, _h, _i;
634
+ if (!options) {
635
+ throw new TypeError("options must be provided.");
636
+ }
637
+ if (!options.rule || !options.rule.targets) {
638
+ throw new TypeError("rule targets must be provided.");
639
+ }
640
+ const targets = Object.entries(options.rule.targets);
641
+ if (targets.length === 0) {
642
+ throw new TypeError("rule targets must not be empty.");
643
+ }
644
+ const logger = (_a = options.logger) != null ? _a : createConsoleLogger("emsdk-env");
645
+ const rootDir = resolve((_b = options.root) != null ? _b : process.cwd());
646
+ const emsdkOptions = normalizePrepareOptions(options.emsdk);
647
+ const emsdkRoot = await prepareEmsdk(emsdkOptions);
648
+ const emsdkEnv = await loadEmsdkEnv(emsdkRoot, logger, emsdkOptions.signal);
649
+ const baseEnv = {
650
+ ...emsdkEnv,
651
+ ROOT: rootDir
652
+ };
653
+ const rawSrcDir = expandPlaceholders(
654
+ (_c = options.srcDir) != null ? _c : DEFAULT_WASM_SRC_DIR,
655
+ baseEnv,
656
+ "srcDir"
657
+ );
658
+ const rawOutDir = expandPlaceholders(
659
+ (_d = options.outDir) != null ? _d : DEFAULT_WASM_OUT_DIR,
660
+ baseEnv,
661
+ "outDir"
662
+ );
663
+ const rawBuildDir = expandPlaceholders(
664
+ (_e = options.buildDir) != null ? _e : DEFAULT_WASM_BUILD_DIR,
665
+ baseEnv,
666
+ "buildDir"
667
+ );
668
+ const srcDir = resolvePath(rootDir, rawSrcDir);
669
+ const outDir = resolvePath(rootDir, rawOutDir);
670
+ const buildDir = resolvePath(rootDir, rawBuildDir);
671
+ const buildId = createBuildId();
672
+ const buildRunDir = resolve(buildDir, buildId);
673
+ const cleanupBuildDir = (_f = options.cleanupBuildDir) != null ? _f : true;
674
+ const parallel = (_g = options.parallel) != null ? _g : true;
675
+ const envWithDirs = {
676
+ ...emsdkEnv,
677
+ ROOT: rootDir,
678
+ SRC_DIR: srcDir,
679
+ OUT_DIR: outDir,
680
+ BUILD_DIR: buildDir
681
+ };
682
+ const emccCommand = await resolveEmccCommand(envWithDirs, emsdkRoot);
683
+ const common = (_h = options.rule.common) != null ? _h : {};
684
+ await ensureDirectory(outDir);
685
+ await ensureDirectory(buildDir);
686
+ await rm(buildRunDir, { recursive: true, force: true });
687
+ await ensureDirectory(buildRunDir);
688
+ const outFiles = {};
689
+ try {
690
+ for (const [targetName, target] of targets) {
691
+ const mergedLinkOptions = [
692
+ ...ensureArray(common.linkOptions),
693
+ ...ensureArray(target.linkOptions)
694
+ ];
695
+ const mergedExports = [
696
+ ...ensureArray(common.exports),
697
+ ...ensureArray(target.exports)
698
+ ];
699
+ const baseCompileOptions = [
700
+ ...ensureArray(common.options),
701
+ ...ensureArray(target.options)
702
+ ];
703
+ const baseIncludeDirs = [
704
+ ...ensureArray(common.includeDirs),
705
+ ...ensureArray(target.includeDirs)
706
+ ];
707
+ const baseDefines = mergeDefines(common.defines, target.defines);
708
+ const sourceGroups = (_i = target.sourceGroups) != null ? _i : [];
709
+ const targetEnv = {
710
+ ...envWithDirs,
711
+ TARGET_NAME: targetName
712
+ };
713
+ const buildEnv = createEnvForBuild(targetEnv, {});
714
+ const resolvedOutFile = resolveTargetOutFile(
715
+ targetName,
716
+ target.outFile,
717
+ targetEnv,
718
+ outDir
719
+ );
720
+ const sources = await resolveTargetSources(
721
+ target.sources,
722
+ targetEnv,
723
+ srcDir
724
+ );
725
+ const groupSources = sourceGroups.map(() => []);
726
+ const groupSourceSet = /* @__PURE__ */ new Set();
727
+ for (let index = 0; index < sourceGroups.length; index += 1) {
728
+ const group = sourceGroups[index];
729
+ if (!group) {
730
+ continue;
731
+ }
732
+ const resolved = await resolveSourcesFromPatterns(
733
+ group.sources,
734
+ targetEnv,
735
+ srcDir,
736
+ `sourceGroups[${index}].sources`
737
+ );
738
+ const deduped = dedupeSources(resolved);
739
+ groupSources[index] = deduped;
740
+ for (const source of deduped) {
741
+ groupSourceSet.add(source);
742
+ }
743
+ }
744
+ const baseSources = sources.filter(
745
+ (source) => !groupSourceSet.has(source)
746
+ );
747
+ const groupedSources = groupSources.flat();
748
+ if (baseSources.length + groupedSources.length === 0) {
749
+ throw new Error(`No sources matched for target: ${targetName}`);
750
+ }
751
+ const targetBuildDir = resolve(buildRunDir, targetName);
752
+ await rm(targetBuildDir, { recursive: true, force: true });
753
+ await ensureDirectory(targetBuildDir);
754
+ const resolvedLinkOptions = expandArray(
755
+ mergedLinkOptions,
756
+ targetEnv,
757
+ "linkOptions"
758
+ );
759
+ const resolvedExports = expandArray(mergedExports, targetEnv, "exports");
760
+ const exportArgs = buildExportFlags(resolvedExports);
761
+ const baseCompileArgs = buildCompileArgs(
762
+ baseCompileOptions,
763
+ baseIncludeDirs,
764
+ baseDefines,
765
+ targetEnv,
766
+ rootDir
767
+ );
768
+ const groupCompileArgs = sourceGroups.map((group) => {
769
+ var _a2;
770
+ const groupOptions = [
771
+ ...baseCompileOptions,
772
+ ...ensureArray(group == null ? void 0 : group.options)
773
+ ];
774
+ const groupIncludeDirs = [
775
+ ...baseIncludeDirs,
776
+ ...ensureArray(group == null ? void 0 : group.includeDirs)
777
+ ];
778
+ const groupDefines = mergeDefines(baseDefines, (_a2 = group == null ? void 0 : group.defines) != null ? _a2 : {});
779
+ return buildCompileArgs(
780
+ groupOptions,
781
+ groupIncludeDirs,
782
+ groupDefines,
783
+ targetEnv,
784
+ rootDir
785
+ );
786
+ });
787
+ logger.info(`Compiling target: ${targetName}`);
788
+ const compileSource = async (source, args, groupIndex) => {
789
+ const objectName = toSafeObjectName(rootDir, source, groupIndex);
790
+ const outputObject = resolve(targetBuildDir, `${objectName}.o`);
791
+ const compileArgs = [
792
+ "-c",
793
+ source,
794
+ "-o",
795
+ outputObject,
796
+ ...args.resolvedOptions,
797
+ ...args.includeArgs,
798
+ ...args.defineArgs
799
+ ];
800
+ logger.debug(`emcc ${compileArgs.join(" ")}`);
801
+ await runCommandWithEnv(
802
+ emccCommand,
803
+ compileArgs,
804
+ rootDir,
805
+ buildEnv,
806
+ emsdkOptions.signal
807
+ );
808
+ return outputObject;
809
+ };
810
+ const buildObjectsSequential = async () => {
811
+ const objectFiles2 = [];
812
+ for (const source of baseSources) {
813
+ objectFiles2.push(
814
+ await compileSource(source, baseCompileArgs, void 0)
815
+ );
816
+ }
817
+ for (let index = 0; index < groupSources.length; index += 1) {
818
+ const sourcesInGroup = groupSources[index];
819
+ if (!sourcesInGroup) {
820
+ continue;
821
+ }
822
+ const groupArgs = groupCompileArgs[index];
823
+ if (!groupArgs) {
824
+ continue;
825
+ }
826
+ for (const source of sourcesInGroup) {
827
+ objectFiles2.push(await compileSource(source, groupArgs, index));
828
+ }
829
+ }
830
+ return objectFiles2;
831
+ };
832
+ const compileJobs = [];
833
+ for (const source of baseSources) {
834
+ compileJobs.push({
835
+ source,
836
+ args: baseCompileArgs,
837
+ groupIndex: void 0
838
+ });
839
+ }
840
+ for (let index = 0; index < groupSources.length; index += 1) {
841
+ const sourcesInGroup = groupSources[index];
842
+ if (!sourcesInGroup) {
843
+ continue;
844
+ }
845
+ const groupArgs = groupCompileArgs[index];
846
+ if (!groupArgs) {
847
+ continue;
848
+ }
849
+ for (const source of sourcesInGroup) {
850
+ compileJobs.push({ source, args: groupArgs, groupIndex: index });
851
+ }
852
+ }
853
+ const objectFiles = parallel ? await Promise.all(
854
+ compileJobs.map(
855
+ (job) => compileSource(job.source, job.args, job.groupIndex)
856
+ )
857
+ ) : await buildObjectsSequential();
858
+ logger.info(`Linking target: ${targetName}`);
859
+ const linkArgs = [
860
+ ...objectFiles,
861
+ "-o",
862
+ resolvedOutFile,
863
+ ...resolvedLinkOptions,
864
+ ...exportArgs
865
+ ];
866
+ logger.debug(`emcc ${linkArgs.join(" ")}`);
867
+ await runCommandWithEnv(
868
+ emccCommand,
869
+ linkArgs,
870
+ rootDir,
871
+ buildEnv,
872
+ emsdkOptions.signal
873
+ );
874
+ outFiles[targetName] = resolvedOutFile;
875
+ }
876
+ } finally {
877
+ if (cleanupBuildDir) {
878
+ await rm(buildRunDir, { recursive: true, force: true });
879
+ }
880
+ }
881
+ return {
882
+ emsdkRoot,
883
+ outFiles
884
+ };
885
+ };
886
+ export {
887
+ buildWasm as b,
888
+ prepareEmsdk as p
889
+ };
890
+ //# sourceMappingURL=build-CYHeaOdc.js.map