xbintsc 0.5.52 → 0.6.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +8 -2
- package/README.zh-CN.md +9 -3
- package/dist/scripts/fetch-tarball.d.ts +28 -0
- package/dist/scripts/fetch-tarball.js +57 -0
- package/dist/scripts/fetch-tarball.js.map +1 -0
- package/dist/scripts/gui-build-options.d.ts +77 -0
- package/dist/scripts/gui-build-options.js +134 -0
- package/dist/scripts/gui-build-options.js.map +1 -0
- package/dist/src/cli/help.d.ts +1 -1
- package/dist/src/cli/hints.d.ts +6 -2
- package/dist/src/cli/hints.js +16 -2
- package/dist/src/cli/hints.js.map +1 -1
- package/dist/src/cli/main.js +9 -0
- package/dist/src/cli/main.js.map +1 -1
- package/dist/src/driver/app-artifacts.d.ts +2 -0
- package/dist/src/driver/app-artifacts.js +3 -3
- package/dist/src/driver/app-artifacts.js.map +1 -1
- package/dist/src/driver/compiler.js +147 -52
- package/dist/src/driver/compiler.js.map +1 -1
- package/dist/src/driver/icon.d.ts +1 -1
- package/dist/src/driver/icon.js +3 -3
- package/dist/src/driver/icon.js.map +1 -1
- package/dist/src/driver/macos-sdk.d.ts +32 -0
- package/dist/src/driver/macos-sdk.js +50 -0
- package/dist/src/driver/macos-sdk.js.map +1 -0
- package/dist/src/driver/options.d.ts +6 -0
- package/dist/src/driver/platform-support.d.ts +45 -0
- package/dist/src/driver/platform-support.js +66 -0
- package/dist/src/driver/platform-support.js.map +1 -0
- package/dist/src/driver/prebuilt.d.ts +26 -0
- package/dist/src/driver/prebuilt.js +58 -0
- package/dist/src/driver/prebuilt.js.map +1 -0
- package/dist/src/driver/runtime-objects.d.ts +11 -0
- package/dist/src/driver/runtime-objects.js +14 -5
- package/dist/src/driver/runtime-objects.js.map +1 -1
- package/dist/src/driver/toolchain-provider.d.ts +8 -0
- package/dist/src/driver/toolchain-provider.js +8 -0
- package/dist/src/driver/toolchain-provider.js.map +1 -1
- package/dist/src/driver/toolchain.d.ts +30 -1
- package/dist/src/driver/toolchain.js +88 -8
- package/dist/src/driver/toolchain.js.map +1 -1
- package/dist/src/driver/version.d.ts +1 -1
- package/dist/src/driver/version.js +1 -1
- package/dist/src/driver/version.js.map +1 -1
- package/dist/src/driver/win-entry.d.ts +1 -1
- package/dist/src/driver/win-entry.js +4 -4
- package/dist/src/driver/win-entry.js.map +1 -1
- package/dist/src/extensions/gui/html.d.ts +10 -7
- package/dist/src/extensions/gui/html.js +53 -29
- package/dist/src/extensions/gui/html.js.map +1 -1
- package/dist/src/extensions/node/child_process/index.d.ts +4 -3
- package/dist/src/extensions/node/child_process/index.js +4 -3
- package/dist/src/extensions/node/child_process/index.js.map +1 -1
- package/dist/tests/cli/hints.test.js +9 -1
- package/dist/tests/cli/hints.test.js.map +1 -1
- package/dist/tests/cli/main.test.js +9 -0
- package/dist/tests/cli/main.test.js.map +1 -1
- package/dist/tests/driver/compiler.test.js +107 -0
- package/dist/tests/driver/compiler.test.js.map +1 -1
- package/dist/tests/driver/cxx-compile-flags.test.d.ts +1 -0
- package/dist/tests/driver/cxx-compile-flags.test.js +24 -0
- package/dist/tests/driver/cxx-compile-flags.test.js.map +1 -0
- package/dist/tests/driver/macos-sdk.test.d.ts +1 -0
- package/dist/tests/driver/macos-sdk.test.js +83 -0
- package/dist/tests/driver/macos-sdk.test.js.map +1 -0
- package/dist/tests/driver/platform-support.test.d.ts +1 -0
- package/dist/tests/driver/platform-support.test.js +41 -0
- package/dist/tests/driver/platform-support.test.js.map +1 -0
- package/dist/tests/driver/prebuilt.test.d.ts +1 -0
- package/dist/tests/driver/prebuilt.test.js +44 -0
- package/dist/tests/driver/prebuilt.test.js.map +1 -0
- package/dist/tests/driver/toolchain-provider.test.js +24 -0
- package/dist/tests/driver/toolchain-provider.test.js.map +1 -1
- package/dist/tests/driver/toolchain.test.js +46 -2
- package/dist/tests/driver/toolchain.test.js.map +1 -1
- package/dist/tests/e2e/async-await.test.js +4 -2
- package/dist/tests/e2e/async-await.test.js.map +1 -1
- package/dist/tests/e2e/compile-node.test.js +21 -0
- package/dist/tests/e2e/compile-node.test.js.map +1 -1
- package/dist/tests/e2e/fetch.test.js +34 -6
- package/dist/tests/e2e/fetch.test.js.map +1 -1
- package/dist/tests/e2e/gui-example-pelican3d.test.d.ts +10 -0
- package/dist/tests/e2e/gui-example-pelican3d.test.js +94 -0
- package/dist/tests/e2e/gui-example-pelican3d.test.js.map +1 -0
- package/dist/tests/e2e/gui-script.test.js +48 -0
- package/dist/tests/e2e/gui-script.test.js.map +1 -1
- package/dist/tests/e2e/unhandled-rejection.test.d.ts +14 -0
- package/dist/tests/e2e/unhandled-rejection.test.js +107 -0
- package/dist/tests/e2e/unhandled-rejection.test.js.map +1 -0
- package/dist/tests/extensions/gui.test.js +44 -0
- package/dist/tests/extensions/gui.test.js.map +1 -1
- package/dist/tests/scripts/fetch-tarball.test.d.ts +1 -0
- package/dist/tests/scripts/fetch-tarball.test.js +70 -0
- package/dist/tests/scripts/fetch-tarball.test.js.map +1 -0
- package/doc/ai/cli.md +6 -0
- package/doc/ai/language-support.md +1 -0
- package/doc/ai/troubleshooting.md +32 -2
- package/doc/ai/zh-CN/cli.md +5 -0
- package/doc/ai/zh-CN/language-support.md +1 -0
- package/doc/ai/zh-CN/troubleshooting.md +27 -2
- package/doc/gui/build.md +7 -3
- package/doc/gui/dom.md +3 -1
- package/doc/gui/webgl.md +4 -0
- package/doc/gui-scripts.md +33 -17
- package/doc/gui.md +17 -1
- package/doc/implemented/async.md +1 -0
- package/doc/implemented/modules.md +9 -6
- package/doc/implemented/node/child-process.md +12 -4
- package/doc/implemented/runtime-core.md +1 -1
- package/doc/npm-package.md +23 -2
- package/doc/post/make-js-great-again.md +30 -0
- package/doc/requirements.md +36 -3
- package/doc/unimplemented/deviations.md +1 -0
- package/doc/zh-CN/gui/build.md +4 -2
- package/doc/zh-CN/gui/dom.md +3 -1
- package/doc/zh-CN/gui/webgl.md +1 -0
- package/doc/zh-CN/gui-scripts.md +23 -11
- package/doc/zh-CN/gui.md +11 -1
- package/doc/zh-CN/implemented/async.md +1 -0
- package/doc/zh-CN/implemented/modules.md +8 -4
- package/doc/zh-CN/implemented/node/child-process.md +10 -3
- package/doc/zh-CN/implemented/runtime-core.md +1 -1
- package/doc/zh-CN/npm-package.md +18 -1
- package/doc/zh-CN/post/make-js-great-again.md +32 -0
- package/doc/zh-CN/requirements.md +30 -2
- package/doc/zh-CN/unimplemented/deviations.md +1 -0
- package/package.json +7 -7
- package/runtime/ext_node/child_process/child_process.c +149 -7
- package/runtime/rt.h +4 -0
- package/runtime/xt_async.c +4 -0
- package/runtime/xt_generator.c +2 -0
- package/runtime/xt_loop.c +8 -4
- package/runtime/xt_promise.c +103 -0
- package/scripts/build-gui.ts +17 -5
- package/scripts/build-media.ts +89 -10
- package/scripts/build-runtime.ts +7 -2
- package/scripts/fetch-tarball.ts +71 -0
- package/scripts/gui-build-cache.ts +28 -0
- package/scripts/gui-build-options.ts +26 -2
- package/scripts/package-release.ts +8 -0
- package/src/cli/hints.ts +19 -2
- package/src/cli/main.ts +11 -0
- package/src/driver/app-artifacts.ts +5 -3
- package/src/driver/compiler.ts +167 -56
- package/src/driver/icon.ts +3 -2
- package/src/driver/macos-sdk.ts +52 -0
- package/src/driver/options.ts +6 -0
- package/src/driver/platform-support.ts +69 -0
- package/src/driver/prebuilt.ts +61 -0
- package/src/driver/runtime-objects.ts +26 -5
- package/src/driver/toolchain-provider.ts +11 -0
- package/src/driver/toolchain.ts +105 -9
- package/src/driver/version.ts +1 -1
- package/src/driver/win-entry.ts +4 -3
- package/src/extensions/gui/html.ts +61 -31
- package/src/extensions/node/child_process/index.ts +4 -3
|
@@ -12,7 +12,7 @@ import type { ExtensionRegistry } from "../extensions/registry.js";
|
|
|
12
12
|
import type { ResolvedLinkFeature } from "./link-features.js";
|
|
13
13
|
import { hashParts } from "./cache.js";
|
|
14
14
|
import { findRuntimeLibrary } from "./runtime-lib.js";
|
|
15
|
-
import { compileC, optimizationFlags, RUNTIME_CFLAGS, type PgoMode, type Runner } from "./toolchain.js";
|
|
15
|
+
import { compileC, optimizationFlags, RUNTIME_CFLAGS, sysrootFlags, type PgoMode, type Runner } from "./toolchain.js";
|
|
16
16
|
import { COMPILER_VERSION } from "./version.js";
|
|
17
17
|
|
|
18
18
|
/** Core runtime translation units (each compiled and cached independently). */
|
|
@@ -51,6 +51,12 @@ export interface RuntimeObjects {
|
|
|
51
51
|
readonly featureObjects: readonly string[];
|
|
52
52
|
/** Pre-built C++/Rust objects and archives contributed by native extensions. */
|
|
53
53
|
readonly nativeObjects: readonly string[];
|
|
54
|
+
/**
|
|
55
|
+
* The prebuilt `core`/`ext_<name>` archives this link used. Empty when the
|
|
56
|
+
* runtime was compiled from source. The driver retries a failed link without
|
|
57
|
+
* them, so it has to know whether the first attempt used any.
|
|
58
|
+
*/
|
|
59
|
+
readonly prebuiltArchives: readonly string[];
|
|
54
60
|
}
|
|
55
61
|
|
|
56
62
|
export interface RuntimeObjectsOptions {
|
|
@@ -61,11 +67,16 @@ export interface RuntimeObjectsOptions {
|
|
|
61
67
|
readonly registry: ExtensionRegistry;
|
|
62
68
|
readonly preferPrebuilt: boolean;
|
|
63
69
|
readonly env: Record<string, string>;
|
|
70
|
+
/** macOS SDK for `-isysroot`; part of the object cache key. */
|
|
71
|
+
readonly sdkRoot?: string;
|
|
64
72
|
readonly lto: boolean;
|
|
65
73
|
readonly pgo: PgoMode | undefined;
|
|
66
74
|
readonly pgoDir: string;
|
|
67
75
|
/** Optional link features selected for this program. */
|
|
68
76
|
readonly features?: readonly ResolvedLinkFeature[];
|
|
77
|
+
/** Override the folders searched for prebuilt archives (defaults to the
|
|
78
|
+
* runtime's `lib/<os>-<arch>` plus the installed platform package). */
|
|
79
|
+
readonly runtimeLibDirs?: readonly string[];
|
|
69
80
|
}
|
|
70
81
|
|
|
71
82
|
/**
|
|
@@ -79,6 +90,9 @@ export interface RuntimeObjectsOptions {
|
|
|
79
90
|
export function ensureRuntimeObjects(options: RuntimeObjectsOptions): RuntimeObjects {
|
|
80
91
|
const { runner, clang, runtimeDir, cacheDir, registry, preferPrebuilt, env, lto, pgo, pgoDir } = options;
|
|
81
92
|
const optimization = optimizationFlags(lto, pgo, pgoDir);
|
|
93
|
+
/* The sysroot joins the extra flags (and therefore the cache key): objects
|
|
94
|
+
compiled against a different SDK are a different artifact. */
|
|
95
|
+
const compileFlags = [...optimization, ...sysrootFlags(options.sdkRoot)];
|
|
82
96
|
/* Runtime objects depend on the shared headers (rt.h/rt_internal.h/...) and
|
|
83
97
|
the `#include`d implementation fragments (`.inc`), so any change to them
|
|
84
98
|
must invalidate every cached object. Hash them all. */
|
|
@@ -99,7 +113,7 @@ export function ensureRuntimeObjects(options: RuntimeObjectsOptions): RuntimeObj
|
|
|
99
113
|
"runtime",
|
|
100
114
|
COMPILER_VERSION,
|
|
101
115
|
RUNTIME_CFLAGS.join(" "),
|
|
102
|
-
|
|
116
|
+
compileFlags.join(" "),
|
|
103
117
|
tag,
|
|
104
118
|
headerStamp,
|
|
105
119
|
body,
|
|
@@ -108,24 +122,30 @@ export function ensureRuntimeObjects(options: RuntimeObjectsOptions): RuntimeObj
|
|
|
108
122
|
]);
|
|
109
123
|
const objectPath = join(cacheDir, `${tag}-${key}.o`);
|
|
110
124
|
if (!existsSync(objectPath)) {
|
|
111
|
-
compileC(runner, clang, sourcePath, objectPath, runtimeDir, env,
|
|
125
|
+
compileC(runner, clang, sourcePath, objectPath, runtimeDir, env, compileFlags);
|
|
112
126
|
}
|
|
113
127
|
return objectPath;
|
|
114
128
|
};
|
|
115
129
|
|
|
116
|
-
const coreArchive = preferPrebuilt ? findRuntimeLibrary("core") : undefined;
|
|
130
|
+
const coreArchive = preferPrebuilt ? findRuntimeLibrary("core", options.runtimeLibDirs) : undefined;
|
|
117
131
|
const runtimeObjects = coreArchive
|
|
118
132
|
? [coreArchive]
|
|
119
133
|
: RUNTIME_SOURCES.map((source) => compileOne(join(runtimeDir, source), basename(source, ".c")));
|
|
120
134
|
|
|
135
|
+
/** Prebuilt archives this link uses; the driver retries without them. */
|
|
136
|
+
const prebuiltArchives: string[] = coreArchive ? [coreArchive] : [];
|
|
137
|
+
|
|
121
138
|
const extensionObjects: string[] = [];
|
|
122
139
|
const seen = new Set<string>();
|
|
123
140
|
for (const extension of registry.all()) {
|
|
124
141
|
const sources = extension.runtimeSources ? extension.runtimeSources() : [];
|
|
125
142
|
if (sources.length === 0) continue;
|
|
126
|
-
const archive = preferPrebuilt
|
|
143
|
+
const archive = preferPrebuilt
|
|
144
|
+
? findRuntimeLibrary(`ext_${extension.name}`, options.runtimeLibDirs)
|
|
145
|
+
: undefined;
|
|
127
146
|
if (archive) {
|
|
128
147
|
extensionObjects.push(archive);
|
|
148
|
+
prebuiltArchives.push(archive);
|
|
129
149
|
continue;
|
|
130
150
|
}
|
|
131
151
|
for (const sourcePath of sources) {
|
|
@@ -156,5 +176,6 @@ export function ensureRuntimeObjects(options: RuntimeObjectsOptions): RuntimeObj
|
|
|
156
176
|
extensionObjects,
|
|
157
177
|
featureObjects: [...new Set(featureObjects)],
|
|
158
178
|
nativeObjects: [...new Set(registry.nativeObjects())],
|
|
179
|
+
prebuiltArchives,
|
|
159
180
|
};
|
|
160
181
|
}
|
|
@@ -22,11 +22,17 @@
|
|
|
22
22
|
* skipped (or rejected, when named explicitly) instead of producing objects
|
|
23
23
|
* the linker cannot mix.
|
|
24
24
|
*
|
|
25
|
+
* On macOS the resolved driver also carries the SDK as `sdkRoot`, which the
|
|
26
|
+
* compile and link steps pass as `-isysroot` (see `macos-sdk.ts`): an Apple
|
|
27
|
+
* clang invoked by absolute path does not infer the SDK by itself, and without
|
|
28
|
+
* one not even `<stdio.h>` — nor the linker's `libm` stub — is found.
|
|
29
|
+
*
|
|
25
30
|
* Extra linker arguments (for example `-fuse-ld=lld`) can be supplied through
|
|
26
31
|
* `xbintsc_LINKER_ARGS`.
|
|
27
32
|
*/
|
|
28
33
|
|
|
29
34
|
import { locateClang, realRunner, type ClangVersion, type Runner } from "./toolchain.js";
|
|
35
|
+
import { macosSdkRoot } from "./macos-sdk.js";
|
|
30
36
|
|
|
31
37
|
export type ToolchainSource = "env" | "system";
|
|
32
38
|
|
|
@@ -42,6 +48,9 @@ export interface ResolvedToolchain {
|
|
|
42
48
|
/** Extra environment for the toolchain subprocesses, merged over the current
|
|
43
49
|
* environment. */
|
|
44
50
|
readonly env: Record<string, string>;
|
|
51
|
+
/** macOS SDK to pass as `-isysroot`; lost off macOS or when `SDKROOT` is
|
|
52
|
+
* already set. See `macos-sdk.ts`. */
|
|
53
|
+
readonly sdkRoot?: string;
|
|
45
54
|
}
|
|
46
55
|
|
|
47
56
|
function linkerArgs(fallback: readonly string[]): string[] {
|
|
@@ -53,11 +62,13 @@ function linkerArgs(fallback: readonly string[]): string[] {
|
|
|
53
62
|
/** Resolve the toolchain that `build` should use. See the module docblock. */
|
|
54
63
|
export function resolveToolchain(runner: Runner = realRunner): ResolvedToolchain {
|
|
55
64
|
const location = locateClang(runner);
|
|
65
|
+
const sdkRoot = macosSdkRoot(runner);
|
|
56
66
|
return {
|
|
57
67
|
clang: location.command,
|
|
58
68
|
source: location.source,
|
|
59
69
|
...(location.version ? { version: location.version } : {}),
|
|
60
70
|
linkerArgs: linkerArgs([]),
|
|
61
71
|
env: {},
|
|
72
|
+
...(sdkRoot === undefined ? {} : { sdkRoot }),
|
|
62
73
|
};
|
|
63
74
|
}
|
package/src/driver/toolchain.ts
CHANGED
|
@@ -144,6 +144,75 @@ export function probeClang(
|
|
|
144
144
|
}
|
|
145
145
|
}
|
|
146
146
|
|
|
147
|
+
/**
|
|
148
|
+
* Release years that name their install directory after the year, mapped to the
|
|
149
|
+
* major version each release actually ships (VS 2022 is version 17).
|
|
150
|
+
*/
|
|
151
|
+
const VISUAL_STUDIO_YEAR_MAJOR: Readonly<Record<string, number>> = {
|
|
152
|
+
"2015": 14,
|
|
153
|
+
"2017": 15,
|
|
154
|
+
"2019": 16,
|
|
155
|
+
"2022": 17,
|
|
156
|
+
};
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* Sort key for a directory under `%ProgramFiles%\Microsoft Visual Studio`.
|
|
160
|
+
* Releases up to 2022 use year names ("2022"); Visual Studio 2026 (version 18)
|
|
161
|
+
* and later use the plain major version ("18").
|
|
162
|
+
*/
|
|
163
|
+
function visualStudioMajor(directoryName: string): number {
|
|
164
|
+
const known = VISUAL_STUDIO_YEAR_MAJOR[directoryName];
|
|
165
|
+
if (known !== undefined) return known;
|
|
166
|
+
return /^\d+$/.test(directoryName) ? Number(directoryName) : -1;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/** Immediate subdirectory names, or `[]` when the directory cannot be read. */
|
|
170
|
+
function subdirectories(directory: string): string[] {
|
|
171
|
+
try {
|
|
172
|
+
return readdirSync(directory, { withFileTypes: true })
|
|
173
|
+
.filter((entry) => entry.isDirectory())
|
|
174
|
+
.map((entry) => entry.name);
|
|
175
|
+
} catch {
|
|
176
|
+
return [];
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/**
|
|
181
|
+
* Preference order when one release has several installs; channel names not
|
|
182
|
+
* listed here (for example `Insiders`) follow alphabetically.
|
|
183
|
+
*/
|
|
184
|
+
const VISUAL_STUDIO_EDITIONS: readonly string[] = ["Community", "Professional", "Enterprise", "BuildTools"];
|
|
185
|
+
|
|
186
|
+
function editionRank(edition: string): number {
|
|
187
|
+
const index = VISUAL_STUDIO_EDITIONS.indexOf(edition);
|
|
188
|
+
return index === -1 ? VISUAL_STUDIO_EDITIONS.length : index;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
/**
|
|
192
|
+
* The `VC\Tools\Llvm` directory of every Visual Studio installation under one
|
|
193
|
+
* `Microsoft Visual Studio` root, newest release first.
|
|
194
|
+
*
|
|
195
|
+
* Both layouts are discovered by enumerating directories — `<year>\<edition>`
|
|
196
|
+
* for 2019/2022 (for example `2022\BuildTools`) and `<major>\<channel>` for
|
|
197
|
+
* Visual Studio 2026 and later (for example `18\Insiders`) — so a future
|
|
198
|
+
* release needs no code change and a non-default channel still works.
|
|
199
|
+
*/
|
|
200
|
+
export function visualStudioLlvmDirs(visualStudioRoot: string): string[] {
|
|
201
|
+
const versions = subdirectories(visualStudioRoot)
|
|
202
|
+
.filter((name) => visualStudioMajor(name) >= 0)
|
|
203
|
+
.sort((a, b) => visualStudioMajor(b) - visualStudioMajor(a));
|
|
204
|
+
const dirs: string[] = [];
|
|
205
|
+
for (const version of versions) {
|
|
206
|
+
const editions = subdirectories(join(visualStudioRoot, version)).sort(
|
|
207
|
+
(a, b) => editionRank(a) - editionRank(b) || a.localeCompare(b),
|
|
208
|
+
);
|
|
209
|
+
for (const edition of editions) {
|
|
210
|
+
dirs.push(join(visualStudioRoot, version, edition, "VC", "Tools", "Llvm"));
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
return dirs;
|
|
214
|
+
}
|
|
215
|
+
|
|
147
216
|
/**
|
|
148
217
|
* Visual Studio ships its own LLVM build (`VC\Tools\Llvm\x64\bin\clang.exe`).
|
|
149
218
|
* It is a perfectly good driver but is not on `PATH` in a plain shell, so probe
|
|
@@ -152,17 +221,19 @@ export function probeClang(
|
|
|
152
221
|
*/
|
|
153
222
|
function visualStudioClangCandidates(): string[] {
|
|
154
223
|
if (process.platform !== "win32") return [];
|
|
155
|
-
const roots = [
|
|
156
|
-
|
|
224
|
+
const roots = [
|
|
225
|
+
...new Set(
|
|
226
|
+
[process.env.ProgramFiles, process.env["ProgramFiles(x86)"], process.env.ProgramW6432].filter(
|
|
227
|
+
(root): root is string => Boolean(root),
|
|
228
|
+
),
|
|
229
|
+
),
|
|
230
|
+
];
|
|
157
231
|
const arch = process.arch === "arm64" ? "ARM64" : "x64";
|
|
158
232
|
const candidates: string[] = [];
|
|
159
233
|
for (const root of roots) {
|
|
160
|
-
for (const
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
candidates.push(join(llvm, arch, "bin", "clang.exe"));
|
|
164
|
-
candidates.push(join(llvm, "bin", "clang.exe"));
|
|
165
|
-
}
|
|
234
|
+
for (const llvm of visualStudioLlvmDirs(join(root, "Microsoft Visual Studio"))) {
|
|
235
|
+
candidates.push(join(llvm, arch, "bin", "clang.exe"));
|
|
236
|
+
candidates.push(join(llvm, "bin", "clang.exe"));
|
|
166
237
|
}
|
|
167
238
|
candidates.push(join(root, "LLVM", "bin", "clang.exe"));
|
|
168
239
|
}
|
|
@@ -299,12 +370,25 @@ export function optimizationFlags(
|
|
|
299
370
|
return flags;
|
|
300
371
|
}
|
|
301
372
|
|
|
373
|
+
/**
|
|
374
|
+
* `-isysroot` for a resolved macOS SDK, or nothing off macOS.
|
|
375
|
+
*
|
|
376
|
+
* The sysroot travels on the command line and not as `SDKROOT` in the
|
|
377
|
+
* environment because the self-hosted compiler's `spawnSync` ignores
|
|
378
|
+
* `options.env`; flags survive that. See `macos-sdk.ts`.
|
|
379
|
+
*/
|
|
380
|
+
export function sysrootFlags(sdkRoot: string | undefined): string[] {
|
|
381
|
+
return sdkRoot === undefined ? [] : ["-isysroot", sdkRoot];
|
|
382
|
+
}
|
|
383
|
+
|
|
302
384
|
export interface CompileIrOptions {
|
|
303
385
|
readonly clang: string;
|
|
304
386
|
readonly irPath: string;
|
|
305
387
|
readonly objectPath: string;
|
|
306
388
|
readonly optimize: string;
|
|
307
389
|
readonly env?: Record<string, string>;
|
|
390
|
+
/** macOS SDK for `-isysroot`; see {@link sysrootFlags}. */
|
|
391
|
+
readonly sdkRoot?: string;
|
|
308
392
|
/** Emit debug info (`-g`) and keep frame pointers for crash backtraces. */
|
|
309
393
|
readonly debug?: boolean;
|
|
310
394
|
/** Compile to LTO bitcode and let the linker optimize across units. */
|
|
@@ -332,6 +416,7 @@ export function compileIr(runner: Runner, options: CompileIrOptions): void {
|
|
|
332
416
|
...SECTION_FLAGS,
|
|
333
417
|
...debugFlags,
|
|
334
418
|
...profileFlags,
|
|
419
|
+
...sysrootFlags(options.sdkRoot),
|
|
335
420
|
"-c",
|
|
336
421
|
options.irPath,
|
|
337
422
|
"-o",
|
|
@@ -349,6 +434,9 @@ export interface LinkOptions {
|
|
|
349
434
|
readonly linkerFlags: readonly string[];
|
|
350
435
|
readonly optimize: string;
|
|
351
436
|
readonly env?: Record<string, string>;
|
|
437
|
+
/** macOS SDK for `-isysroot`; the linker's stubs (`libm`, `libSystem`) come
|
|
438
|
+
* from it, so a link without one fails with `library 'm' not found`. */
|
|
439
|
+
readonly sdkRoot?: string;
|
|
352
440
|
readonly lto?: boolean;
|
|
353
441
|
readonly pgo?: PgoMode;
|
|
354
442
|
readonly pgoDir?: string;
|
|
@@ -361,7 +449,13 @@ function deadStripFlag(): string[] {
|
|
|
361
449
|
return ["-Wl,--gc-sections"];
|
|
362
450
|
}
|
|
363
451
|
|
|
364
|
-
|
|
452
|
+
/**
|
|
453
|
+
* Link the program and return the command result. A successful link can still
|
|
454
|
+
* carry a warning (for example ld64's "object file was built for newer macOS
|
|
455
|
+
* version"); the driver inspects `stderr` to decide whether a prebuilt archive
|
|
456
|
+
* should be rebuilt from source.
|
|
457
|
+
*/
|
|
458
|
+
export function link(runner: Runner, options: LinkOptions): CommandResult {
|
|
365
459
|
const profileFlags = optimizationFlags(options.lto, options.pgo, options.pgoDir);
|
|
366
460
|
const args = [
|
|
367
461
|
`-O${options.optimize}`,
|
|
@@ -370,6 +464,7 @@ export function link(runner: Runner, options: LinkOptions): void {
|
|
|
370
464
|
* understands both ELF/Mach-O and COFF; without it MSVC link.exe rejects
|
|
371
465
|
* the bitcode object on Windows. */
|
|
372
466
|
...(options.lto ? ["-fuse-ld=lld"] : []),
|
|
467
|
+
...sysrootFlags(options.sdkRoot),
|
|
373
468
|
...options.objectPaths,
|
|
374
469
|
"-o",
|
|
375
470
|
options.outputPath,
|
|
@@ -378,6 +473,7 @@ export function link(runner: Runner, options: LinkOptions): void {
|
|
|
378
473
|
];
|
|
379
474
|
const result = runner.run(options.clang, args, { env: options.env });
|
|
380
475
|
if (result.status !== 0) throw new ToolchainError(`${options.clang} link`, result.status, result.stderr);
|
|
476
|
+
return result;
|
|
381
477
|
}
|
|
382
478
|
|
|
383
479
|
/**
|
package/src/driver/version.ts
CHANGED
package/src/driver/win-entry.ts
CHANGED
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
import { existsSync, mkdirSync, writeFileSync } from "node:fs";
|
|
16
16
|
import { join } from "node:path";
|
|
17
17
|
import { hashParts } from "./cache.js";
|
|
18
|
-
import { compileC, type Runner } from "./toolchain.js";
|
|
18
|
+
import { compileC, sysrootFlags, type Runner } from "./toolchain.js";
|
|
19
19
|
|
|
20
20
|
/** The bridge source. `main` is the entry point the runtime emits. */
|
|
21
21
|
export const WINDOWS_ENTRY_SOURCE = `/* Generated by xbintsc - Windows GUI subsystem entry point. Do not edit. */
|
|
@@ -46,14 +46,15 @@ export function ensureWindowsEntryObject(
|
|
|
46
46
|
clang: string,
|
|
47
47
|
cacheDir: string,
|
|
48
48
|
env?: Record<string, string>,
|
|
49
|
+
sdkRoot?: string,
|
|
49
50
|
): string {
|
|
50
|
-
const key = hashParts(["win-gui-entry", process.platform, clang, WINDOWS_ENTRY_SOURCE]);
|
|
51
|
+
const key = hashParts(["win-gui-entry", process.platform, clang, sdkRoot ?? "", WINDOWS_ENTRY_SOURCE]);
|
|
51
52
|
const sourcePath = join(cacheDir, `winentry-${key}.c`);
|
|
52
53
|
const objectPath = join(cacheDir, `winentry-${key}.o`);
|
|
53
54
|
if (!existsSync(objectPath)) {
|
|
54
55
|
mkdirSync(cacheDir, { recursive: true });
|
|
55
56
|
writeFileSync(sourcePath, WINDOWS_ENTRY_SOURCE);
|
|
56
|
-
compileC(runner, clang, sourcePath, objectPath, cacheDir, env);
|
|
57
|
+
compileC(runner, clang, sourcePath, objectPath, cacheDir, env, sysrootFlags(sdkRoot));
|
|
57
58
|
}
|
|
58
59
|
return objectPath;
|
|
59
60
|
}
|
|
@@ -12,13 +12,16 @@
|
|
|
12
12
|
* This is how xbintsc supports `<script>` without a JavaScript engine: the body
|
|
13
13
|
* is ordinary TypeScript compiled by the existing front end, ahead of time.
|
|
14
14
|
*
|
|
15
|
-
*
|
|
16
|
-
* `<script
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
15
|
+
* Both forms are compiled the same way: an inline
|
|
16
|
+
* `<script lang="ts">body</script>` body and an external `<script src="./app.ts">`
|
|
17
|
+
* file are parsed as a module, their top-level `import`/`export … from`
|
|
18
|
+
* statements are hoisted to the generated module (with relative specifiers
|
|
19
|
+
* rewritten so they resolve from the HTML file), and the remaining statements
|
|
20
|
+
* are wrapped in the script function. An inline body therefore resolves imports
|
|
21
|
+
* relative to the page that contains it, exactly like `<script type="module">`
|
|
22
|
+
* in a browser — including a `node:`/bare specifier, which links like any other
|
|
23
|
+
* module import. All scripts run in document order after parsing (there is no
|
|
24
|
+
* "eager" execution point), i.e. every script is effectively `defer`red.
|
|
22
25
|
*/
|
|
23
26
|
|
|
24
27
|
import { existsSync, readFileSync } from "node:fs";
|
|
@@ -161,12 +164,18 @@ function deferredPrelude(alias: string, declaration: ImportDeclaration): string[
|
|
|
161
164
|
return lines;
|
|
162
165
|
}
|
|
163
166
|
|
|
164
|
-
/** Split
|
|
165
|
-
function compileModuleSource(
|
|
167
|
+
/** Split a script module into hoisted imports and a body. */
|
|
168
|
+
function compileModuleSource(
|
|
169
|
+
htmlPath: string,
|
|
170
|
+
scriptPath: string,
|
|
171
|
+
scriptSource: string,
|
|
172
|
+
scriptIndex: number,
|
|
173
|
+
): CompiledModule {
|
|
166
174
|
/* Mask the normalized text: the parser indexes the `SourceFile` text, where
|
|
167
175
|
* `\r\n` already became `\n`, so blanking statement spans in the raw file
|
|
168
|
-
* would remove the wrong characters on a CRLF checkout.
|
|
169
|
-
|
|
176
|
+
* would remove the wrong characters on a CRLF checkout. An inline body is
|
|
177
|
+
* already normalized (the page was), so this is the identity for it. */
|
|
178
|
+
const source = new SourceFile(scriptPath, scriptSource).text;
|
|
170
179
|
const bag = new DiagnosticBag();
|
|
171
180
|
const parsed = new Parser(new SourceFile(scriptPath, source), bag).parseSourceFile();
|
|
172
181
|
const chars = source.split("");
|
|
@@ -272,6 +281,35 @@ export function loadHtmlAsset(path: string, source: string): AssetLoadResult {
|
|
|
272
281
|
const importedText = new Set<string>();
|
|
273
282
|
const bound = new Set<string>();
|
|
274
283
|
|
|
284
|
+
/* Every script's hoisted imports live at module scope, so they are collected
|
|
285
|
+
* here and placed in front of the script functions. `element` and `hint` only
|
|
286
|
+
* shape the error for an eager binding claimed by two scripts. */
|
|
287
|
+
const hoistImports = (compiled: CompiledModule, element: string, hint: string): void => {
|
|
288
|
+
for (const text of compiled.imports) {
|
|
289
|
+
if (!importedText.has(text)) {
|
|
290
|
+
importedText.add(text);
|
|
291
|
+
imports.push(text);
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
lazyImports.push(...compiled.lazyImports);
|
|
295
|
+
for (const name of compiled.localNames) {
|
|
296
|
+
if (bound.has(name)) {
|
|
297
|
+
throw new Error(
|
|
298
|
+
`gui: ${element} imports '${name}', already imported by another <script>; ${hint}`,
|
|
299
|
+
);
|
|
300
|
+
}
|
|
301
|
+
bound.add(name);
|
|
302
|
+
}
|
|
303
|
+
};
|
|
304
|
+
const register = (compiled: CompiledModule, file?: string): string => {
|
|
305
|
+
/* Hash the wrapped body, prelude included: the engine keys the registry by
|
|
306
|
+
* id, so two scripts with the same statements but different deferred
|
|
307
|
+
* imports must not collapse into one entry. */
|
|
308
|
+
const id = hashString([...compiled.prelude, compiled.body].join("\n"));
|
|
309
|
+
scripts.push({ id, body: compiled.body, prelude: compiled.prelude, ...(file ? { file } : {}) });
|
|
310
|
+
return id;
|
|
311
|
+
};
|
|
312
|
+
|
|
275
313
|
/* The engine's cascade only reads `<style>` elements, so a linked stylesheet
|
|
276
314
|
* would be dropped at runtime. Inline it (and its `@import`s) now. */
|
|
277
315
|
const styles = inlineStylesheets(path, page);
|
|
@@ -284,32 +322,24 @@ export function loadHtmlAsset(path: string, source: string): AssetLoadResult {
|
|
|
284
322
|
if (!existsSync(scriptPath)) {
|
|
285
323
|
throw new Error(`gui: <script src="${src}"> not found (looked for ${scriptPath})`);
|
|
286
324
|
}
|
|
287
|
-
const compiled = compileModuleSource(path, scriptPath, scripts.length);
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
importedText.add(text);
|
|
291
|
-
imports.push(text);
|
|
292
|
-
}
|
|
293
|
-
}
|
|
294
|
-
lazyImports.push(...compiled.lazyImports);
|
|
295
|
-
for (const name of compiled.localNames) {
|
|
296
|
-
if (bound.has(name)) {
|
|
297
|
-
throw new Error(
|
|
298
|
-
`gui: <script src="${src}"> imports '${name}', already imported by another <script src>; alias it in ${scriptPath}`,
|
|
299
|
-
);
|
|
300
|
-
}
|
|
301
|
-
bound.add(name);
|
|
302
|
-
}
|
|
303
|
-
const id = hashString([...compiled.prelude, compiled.body].join("\n"));
|
|
304
|
-
scripts.push({ id, body: compiled.body, prelude: compiled.prelude, file: scriptPath });
|
|
325
|
+
const compiled = compileModuleSource(path, scriptPath, readFileSync(scriptPath, "utf8"), scripts.length);
|
|
326
|
+
hoistImports(compiled, `<script src="${src}">`, `alias it in ${scriptPath}`);
|
|
327
|
+
const id = register(compiled, scriptPath);
|
|
305
328
|
return `<script data-xt-id="${id}"></script>`;
|
|
306
329
|
}
|
|
307
330
|
|
|
308
331
|
/* A `src` URL, or a data block: leave the element untouched. */
|
|
309
332
|
if (src !== undefined) return match;
|
|
310
333
|
if (inlineBody.trim() === "") return match;
|
|
311
|
-
|
|
312
|
-
|
|
334
|
+
/* An inline body is compiled exactly like a `<script src>` file, so an
|
|
335
|
+
* `import` links the same way in both: a bare or `node:` specifier is
|
|
336
|
+
* hoisted to the generated module, a relative one is deferred, and both
|
|
337
|
+
* resolve from the HTML file as a browser module script would. Wrapping the
|
|
338
|
+
* body verbatim used to leave an inline `import` inside the script function,
|
|
339
|
+
* where codegen cannot link its binding (TS5001). */
|
|
340
|
+
const compiled = compileModuleSource(path, path, inlineBody, scripts.length);
|
|
341
|
+
hoistImports(compiled, "inline <script>", "alias it");
|
|
342
|
+
const id = register(compiled);
|
|
313
343
|
return `<script data-xt-id="${id}"></script>`;
|
|
314
344
|
});
|
|
315
345
|
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Node's `child_process` module: `spawnSync(command, args, options)`, backed by
|
|
3
3
|
* `runtime/ext_node/child_process`. Captures stdout/stderr as UTF-8 strings,
|
|
4
|
-
* honours `options.cwd
|
|
5
|
-
*
|
|
6
|
-
*
|
|
4
|
+
* honours `options.cwd` and `options.env` (replace semantics, like Node), and
|
|
5
|
+
* supports `options.stdio: "inherit"` (hand the child the parent's
|
|
6
|
+
* stdout/stderr) — which is all the compiler's toolchain wrapper and
|
|
7
|
+
* `xbintsc run` need.
|
|
7
8
|
*/
|
|
8
9
|
|
|
9
10
|
import type { NodeModule } from "../module.js";
|