@scriptc/compiler 0.0.27 → 0.0.29
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/backend/cc.d.ts +27 -3
- package/dist/backend/cc.js +325 -31
- package/dist/backend/cc.js.map +1 -1
- package/dist/backend/object-localize.d.ts +16 -0
- package/dist/backend/object-localize.js +936 -0
- package/dist/backend/object-localize.js.map +1 -0
- package/dist/backend/object-localize.test.d.ts +1 -0
- package/dist/backend/object-localize.test.js +555 -0
- package/dist/backend/object-localize.test.js.map +1 -0
- package/dist/coverage/surface-manifest.js +1 -0
- package/dist/coverage/surface-manifest.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js +73 -8
- package/dist/index.js.map +1 -1
- package/dist/library/profile.js +29 -8
- package/dist/library/profile.js.map +1 -1
- package/package.json +2 -2
- package/surface-manifest.json +2 -1
package/dist/backend/cc.d.ts
CHANGED
|
@@ -208,11 +208,31 @@ export interface CcDriver {
|
|
|
208
208
|
argv: string[];
|
|
209
209
|
/** The SCRIPTC_TARGET triple, or null for a host-native build. */
|
|
210
210
|
target: string | null;
|
|
211
|
+
/** The spelling handed to `zig cc -target` (and the relocatable-merge
|
|
212
|
+
* link). Identical to `target` except for the mobile triples, whose
|
|
213
|
+
* canonical LLVM spellings map to zig's own (`aarch64-apple-ios` →
|
|
214
|
+
* `aarch64-ios.15.0`), pinning the minimum OS version in the same
|
|
215
|
+
* breath. Null for a host-native build. */
|
|
216
|
+
zigTarget: string | null;
|
|
211
217
|
/** Extra compile args the produced platform demands. */
|
|
212
218
|
targetArgs: string[];
|
|
213
219
|
/** Platform libraries appended after every object/archive input. */
|
|
214
220
|
linkArgs: string[];
|
|
215
221
|
}
|
|
222
|
+
export declare const IPHONEOS_MIN_VERSION = "15.0";
|
|
223
|
+
export declare const ANDROID_MIN_API = 26;
|
|
224
|
+
export declare function isIosTarget(target: string | null): boolean;
|
|
225
|
+
export declare function isAndroidTarget(target: string | null): boolean;
|
|
226
|
+
export declare function isMobileTarget(target: string | null): boolean;
|
|
227
|
+
/** The canonical mobile triple SCRIPTC_TARGET selects, or null when the
|
|
228
|
+
* environment names none. Pure string inspection — safe to consult before
|
|
229
|
+
* any toolchain discovery runs. */
|
|
230
|
+
export declare function mobileLibraryTarget(env?: NodeJS.ProcessEnv): string | null;
|
|
231
|
+
/** The admission verdict for a mobile-family triple: null when the spelling
|
|
232
|
+
* and host pairing are supported, otherwise the refusal text (the same text
|
|
233
|
+
* resolveCc throws and compileLibrary reports as SC3002). Pure string/host
|
|
234
|
+
* inspection — no discovery, no subprocess. */
|
|
235
|
+
export declare function mobileTargetRefusal(target: string, hostPlatform?: NodeJS.Platform): string | null;
|
|
216
236
|
export declare function resolveCc(env?: NodeJS.ProcessEnv, hostPlatform?: NodeJS.Platform): CcDriver;
|
|
217
237
|
/** Musl intentionally has no predefined libc macro. The explicit Zig target
|
|
218
238
|
* is therefore the source of truth for selecting its small runtime shim. */
|
|
@@ -255,9 +275,13 @@ export interface LibArchiveOptions {
|
|
|
255
275
|
* symbol. Toolchain sanitizer ABI remains external as required,
|
|
256
276
|
* so N archives built under pairwise-distinct prefixes link into one
|
|
257
277
|
* process with no symbol collisions and no shared mutable runtime state.
|
|
258
|
-
* Undefined references (
|
|
259
|
-
* builds) keep their global binding.
|
|
260
|
-
*
|
|
278
|
+
* Undefined references (the target C/math runtime and system APIs, plus
|
|
279
|
+
* sanitizer ABI in instrumented builds) keep their global binding. Windows
|
|
280
|
+
* embedders additionally link advapi32, iphlpapi, and ws2_32. Omitted = the
|
|
281
|
+
* classic archive, byte-for-byte. Admitted for darwin/linux/win32 native
|
|
282
|
+
* hosts, linux/android/windows cross triples from any host, and macos/ios
|
|
283
|
+
* cross triples from a darwin host (compileLibrary owns the refusal
|
|
284
|
+
* fence). */
|
|
261
285
|
localizeSymbols?: readonly string[];
|
|
262
286
|
/** Thread-instanced state (the profile's abi.instance_per_thread): every
|
|
263
287
|
* TU of the archive compiles with -DSCR_THREAD_INSTANCES, moving the
|
package/dist/backend/cc.js
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
|
-
import { execFile } from "node:child_process";
|
|
1
|
+
import { execFile, spawnSync } from "node:child_process";
|
|
2
2
|
import { createHash } from "node:crypto";
|
|
3
|
-
import { constants as fsConstants } from "node:fs";
|
|
3
|
+
import { constants as fsConstants, existsSync, readdirSync } from "node:fs";
|
|
4
4
|
import { createRequire } from "node:module";
|
|
5
5
|
import { access, chmod, copyFile, link, mkdir, mkdtemp, readdir, readFile, realpath, rename, rm, stat, unlink, utimes, writeFile } from "node:fs/promises";
|
|
6
6
|
import { availableParallelism, homedir, tmpdir } from "node:os";
|
|
7
7
|
import { basename, delimiter, dirname, extname, isAbsolute, join, resolve } from "node:path";
|
|
8
8
|
import { promisify } from "node:util";
|
|
9
|
+
import { localizeElfObject, mergeAndLocalizeCoffObjects } from "./object-localize.js";
|
|
9
10
|
const execFileAsync = promisify(execFile);
|
|
10
11
|
/** Test lanes run against one immutable checkout and one immutable toolchain
|
|
11
12
|
* for the lifetime of each Vitest worker. Production deliberately rediscovers
|
|
@@ -175,6 +176,181 @@ export function runtimeSrcDir() {
|
|
|
175
176
|
const require = createRequire(import.meta.url);
|
|
176
177
|
return join(dirname(require.resolve("@scriptc/runtime/package.json")), "src");
|
|
177
178
|
}
|
|
179
|
+
/* ── mobile targets (library mode) ─────────────────────────────────────────
|
|
180
|
+
* Three mobile triples are admitted, and only for LIBRARY-MODE archive
|
|
181
|
+
* builds — the consuming pattern is an embedding app linking the archive,
|
|
182
|
+
* never a standalone executable (compile()/compileC refuse the executable
|
|
183
|
+
* lane with the pointer to --lib):
|
|
184
|
+
*
|
|
185
|
+
* aarch64-apple-ios device archives; zig `aarch64-ios.15.0`
|
|
186
|
+
* aarch64-apple-ios-simulator simulator archives; zig
|
|
187
|
+
* `aarch64-ios.15.0-simulator`
|
|
188
|
+
* aarch64-linux-android zig `aarch64-linux-android.26`
|
|
189
|
+
*
|
|
190
|
+
* The minimum-version floors are part of the target contract: iOS archives
|
|
191
|
+
* build for iOS 15.0 (IPHONEOS_MIN_VERSION), Android archives for API level
|
|
192
|
+
* 26 (ANDROID_MIN_API) — LC_BUILD_VERSION minos and the bionic stub level
|
|
193
|
+
* both come from the pinned zig spelling above, so an embedder's deployment
|
|
194
|
+
* target at or above the floor links cleanly.
|
|
195
|
+
*
|
|
196
|
+
* Zig bundles no Apple or bionic libc, so both families compile against an
|
|
197
|
+
* explicit sysroot discovered here and spelled into targetArgs (where every
|
|
198
|
+
* cache tier already keys it):
|
|
199
|
+
*
|
|
200
|
+
* iOS darwin hosts only — `xcrun --show-sdk-path` selects the
|
|
201
|
+
* iPhoneOS/iPhoneSimulator SDK; compiles add `-isysroot <sdk>`
|
|
202
|
+
* plus `-isystem <sdk>/usr/include` (zig's driver manages libc
|
|
203
|
+
* header search itself and would otherwise find no headers).
|
|
204
|
+
* Android any host with an NDK — ANDROID_NDK_ROOT/ANDROID_NDK_HOME, or
|
|
205
|
+
* the newest ndk/<version> under ANDROID_HOME/ANDROID_SDK_ROOT
|
|
206
|
+
* or the platform-default SDK location; compiles add the NDK
|
|
207
|
+
* sysroot's generic and per-triple include directories.
|
|
208
|
+
*
|
|
209
|
+
* Library archives never link, so the sysroot's LIBRARIES are the
|
|
210
|
+
* embedder's side of the contract: Xcode links iOS archives against the
|
|
211
|
+
* selected SDK, and Gradle/NDK builds link Android archives against the
|
|
212
|
+
* API-26+ bionic stubs. */
|
|
213
|
+
export const IPHONEOS_MIN_VERSION = "15.0";
|
|
214
|
+
export const ANDROID_MIN_API = 26;
|
|
215
|
+
const MOBILE_LIBRARY_TARGETS = [
|
|
216
|
+
"aarch64-apple-ios",
|
|
217
|
+
"aarch64-apple-ios-simulator",
|
|
218
|
+
"aarch64-linux-android",
|
|
219
|
+
];
|
|
220
|
+
export function isIosTarget(target) {
|
|
221
|
+
return target === "aarch64-apple-ios" || target === "aarch64-apple-ios-simulator";
|
|
222
|
+
}
|
|
223
|
+
export function isAndroidTarget(target) {
|
|
224
|
+
return target === "aarch64-linux-android";
|
|
225
|
+
}
|
|
226
|
+
export function isMobileTarget(target) {
|
|
227
|
+
return isIosTarget(target) || isAndroidTarget(target);
|
|
228
|
+
}
|
|
229
|
+
/** The canonical mobile triple SCRIPTC_TARGET selects, or null when the
|
|
230
|
+
* environment names none. Pure string inspection — safe to consult before
|
|
231
|
+
* any toolchain discovery runs. */
|
|
232
|
+
export function mobileLibraryTarget(env = process.env) {
|
|
233
|
+
const target = env["SCRIPTC_TARGET"] ?? "";
|
|
234
|
+
return isMobileTarget(target) ? target : null;
|
|
235
|
+
}
|
|
236
|
+
/** The admission verdict for a mobile-family triple: null when the spelling
|
|
237
|
+
* and host pairing are supported, otherwise the refusal text (the same text
|
|
238
|
+
* resolveCc throws and compileLibrary reports as SC3002). Pure string/host
|
|
239
|
+
* inspection — no discovery, no subprocess. */
|
|
240
|
+
export function mobileTargetRefusal(target, hostPlatform = process.platform) {
|
|
241
|
+
if (isIosTarget(target)) {
|
|
242
|
+
return hostPlatform === "darwin"
|
|
243
|
+
? null
|
|
244
|
+
: `${target} library archives build on macOS hosts only (the Apple iOS SDK sysroot and Mach-O symbol localization live there); this host is ${hostPlatform}`;
|
|
245
|
+
}
|
|
246
|
+
if (isAndroidTarget(target))
|
|
247
|
+
return null;
|
|
248
|
+
// A near-miss mobile spelling must refuse with the supported set named,
|
|
249
|
+
// never reach zig with no sysroot wired (the compile would fail on the
|
|
250
|
+
// first libc header) or produce an artifact for an unverified device
|
|
251
|
+
// class.
|
|
252
|
+
if (/(?:^|-)(?:ios|tvos|watchos|visionos|android)/.test(target)) {
|
|
253
|
+
return `unsupported mobile target '${target}' (supported: ${MOBILE_LIBRARY_TARGETS.join(", ")})`;
|
|
254
|
+
}
|
|
255
|
+
return null;
|
|
256
|
+
}
|
|
257
|
+
/** The Apple SDK root for one mobile platform, discovered through xcrun the
|
|
258
|
+
* way Xcode's own build system selects it. Memoized per SDK name and
|
|
259
|
+
* selection environment: production rediscovers per process, and the two
|
|
260
|
+
* selection variables (DEVELOPER_DIR/SDKROOT) are already mutable-input
|
|
261
|
+
* keys that disable persistent caching. */
|
|
262
|
+
const appleSdkMemos = new Map();
|
|
263
|
+
function appleSdkRoot(sdk, env) {
|
|
264
|
+
const key = [sdk, env["PATH"] ?? "", env["DEVELOPER_DIR"] ?? "", env["SDKROOT"] ?? ""].join("\0");
|
|
265
|
+
const memo = appleSdkMemos.get(key);
|
|
266
|
+
if (memo !== undefined)
|
|
267
|
+
return memo;
|
|
268
|
+
const probe = spawnSync("xcrun", ["--sdk", sdk, "--show-sdk-path"], {
|
|
269
|
+
encoding: "utf8",
|
|
270
|
+
env,
|
|
271
|
+
});
|
|
272
|
+
const path = probe.status === 0 ? (probe.stdout ?? "").trim() : "";
|
|
273
|
+
if (path === "" || !existsSync(join(path, "usr", "include"))) {
|
|
274
|
+
throw new Error(`the ${sdk} SDK was not found (xcrun --sdk ${sdk} --show-sdk-path failed) — ` +
|
|
275
|
+
`iOS targets need Xcode with the ${sdk === "iphoneos" ? "iPhoneOS" : "iPhoneSimulator"} SDK installed`);
|
|
276
|
+
}
|
|
277
|
+
appleSdkMemos.set(key, path);
|
|
278
|
+
return path;
|
|
279
|
+
}
|
|
280
|
+
/** Compare dotted-numeric NDK version directory names, newest first. */
|
|
281
|
+
function compareNdkVersionsDesc(a, b) {
|
|
282
|
+
const as = a.split(".").map((s) => Number.parseInt(s, 10));
|
|
283
|
+
const bs = b.split(".").map((s) => Number.parseInt(s, 10));
|
|
284
|
+
for (let i = 0; i < Math.max(as.length, bs.length); i++) {
|
|
285
|
+
const d = (bs[i] ?? 0) - (as[i] ?? 0);
|
|
286
|
+
if (d !== 0 && Number.isFinite(d))
|
|
287
|
+
return d;
|
|
288
|
+
}
|
|
289
|
+
return a < b ? 1 : a > b ? -1 : 0;
|
|
290
|
+
}
|
|
291
|
+
/** The Android NDK sysroot for the selected environment: an explicit
|
|
292
|
+
* ANDROID_NDK_ROOT/ANDROID_NDK_HOME wins; otherwise the newest ndk/<version>
|
|
293
|
+
* under ANDROID_HOME, ANDROID_SDK_ROOT, or the platform-default SDK
|
|
294
|
+
* location. The prebuilt host directory is discovered rather than guessed —
|
|
295
|
+
* the NDK ships exactly one per host OS. Memoized per selection
|
|
296
|
+
* environment. */
|
|
297
|
+
const ndkSysrootMemos = new Map();
|
|
298
|
+
function androidNdkSysroot(env) {
|
|
299
|
+
const key = ["ANDROID_NDK_ROOT", "ANDROID_NDK_HOME", "ANDROID_HOME", "ANDROID_SDK_ROOT"]
|
|
300
|
+
.map((name) => env[name] ?? "")
|
|
301
|
+
.join("\0");
|
|
302
|
+
const memo = ndkSysrootMemos.get(key);
|
|
303
|
+
if (memo !== undefined)
|
|
304
|
+
return memo;
|
|
305
|
+
const ndkRoots = [];
|
|
306
|
+
const explicit = [env["ANDROID_NDK_ROOT"], env["ANDROID_NDK_HOME"]]
|
|
307
|
+
.find((root) => root !== undefined && root !== "");
|
|
308
|
+
if (explicit !== undefined) {
|
|
309
|
+
ndkRoots.push(explicit);
|
|
310
|
+
}
|
|
311
|
+
else {
|
|
312
|
+
const sdkRoots = [
|
|
313
|
+
env["ANDROID_HOME"],
|
|
314
|
+
env["ANDROID_SDK_ROOT"],
|
|
315
|
+
process.platform === "darwin"
|
|
316
|
+
? join(homedir(), "Library", "Android", "sdk")
|
|
317
|
+
: process.platform === "win32"
|
|
318
|
+
? join(env["LOCALAPPDATA"] ?? join(homedir(), "AppData", "Local"), "Android", "Sdk")
|
|
319
|
+
: join(homedir(), "Android", "Sdk"),
|
|
320
|
+
].filter((root) => root !== undefined && root !== "");
|
|
321
|
+
for (const root of sdkRoots) {
|
|
322
|
+
let versions = [];
|
|
323
|
+
try {
|
|
324
|
+
versions = readdirSync(join(root, "ndk")).filter((name) => /^\d/.test(name));
|
|
325
|
+
}
|
|
326
|
+
catch {
|
|
327
|
+
continue;
|
|
328
|
+
}
|
|
329
|
+
versions.sort(compareNdkVersionsDesc);
|
|
330
|
+
ndkRoots.push(...versions.map((version) => join(root, "ndk", version)));
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
for (const ndk of ndkRoots) {
|
|
334
|
+
const prebuilt = join(ndk, "toolchains", "llvm", "prebuilt");
|
|
335
|
+
let hosts = [];
|
|
336
|
+
try {
|
|
337
|
+
hosts = readdirSync(prebuilt).sort();
|
|
338
|
+
}
|
|
339
|
+
catch {
|
|
340
|
+
continue;
|
|
341
|
+
}
|
|
342
|
+
for (const host of hosts) {
|
|
343
|
+
const sysroot = join(prebuilt, host, "sysroot");
|
|
344
|
+
if (existsSync(join(sysroot, "usr", "include", "aarch64-linux-android"))) {
|
|
345
|
+
ndkSysrootMemos.set(key, sysroot);
|
|
346
|
+
return sysroot;
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
throw new Error("no Android NDK sysroot was found — install an NDK (sdkmanager 'ndk;<version>') and/or set " +
|
|
351
|
+
"ANDROID_NDK_ROOT to it (ANDROID_HOME with an ndk/ directory also works). " +
|
|
352
|
+
"aarch64-linux-android compiles against the NDK's bionic headers.");
|
|
353
|
+
}
|
|
178
354
|
/** Resolve native platform flags independently of the machine running tests,
|
|
179
355
|
* so the host-Linux contract remains pinned on every development host. */
|
|
180
356
|
function nativePlatformArgs(platform) {
|
|
@@ -190,22 +366,70 @@ export function resolveCc(env = process.env, hostPlatform = process.platform) {
|
|
|
190
366
|
if (target !== "") {
|
|
191
367
|
throw new Error(`SCRIPTC_TARGET=${target} requires SCRIPTC_CC=zigcc — the default clang path has no cross-target sysroots.`);
|
|
192
368
|
}
|
|
193
|
-
return { argv: ["clang"], target: null, ...hostArgs };
|
|
369
|
+
return { argv: ["clang"], target: null, zigTarget: null, ...hostArgs };
|
|
194
370
|
}
|
|
195
371
|
if (cc !== "zigcc") {
|
|
196
372
|
throw new Error(`unknown SCRIPTC_CC '${cc}' (supported: clang, zigcc)`);
|
|
197
373
|
}
|
|
198
374
|
if (target === "")
|
|
199
|
-
return { argv: ["zig", "cc"], target: null, ...hostArgs };
|
|
375
|
+
return { argv: ["zig", "cc"], target: null, zigTarget: null, ...hostArgs };
|
|
200
376
|
if (target.includes("wasi") && target !== "wasm32-wasi") {
|
|
201
377
|
throw new Error(`unsupported WASI target '${target}' (supported: wasm32-wasi)`);
|
|
202
378
|
}
|
|
379
|
+
const mobileRefusal = mobileTargetRefusal(target, hostPlatform);
|
|
380
|
+
if (mobileRefusal !== null)
|
|
381
|
+
throw new Error(mobileRefusal);
|
|
382
|
+
if (isIosTarget(target)) {
|
|
383
|
+
// Library-mode-only target (compile()/compileC own the executable-lane
|
|
384
|
+
// refusal). Zig bundles no Apple libc: the compile rides the selected
|
|
385
|
+
// SDK sysroot, with the libc header directory spelled explicitly
|
|
386
|
+
// because zig's driver manages libc search itself and consults no
|
|
387
|
+
// -isysroot for it. The zig spelling pins the iOS 15.0 floor into
|
|
388
|
+
// every object's LC_BUILD_VERSION minos.
|
|
389
|
+
const simulator = target === "aarch64-apple-ios-simulator";
|
|
390
|
+
const sdk = appleSdkRoot(simulator ? "iphonesimulator" : "iphoneos", env);
|
|
391
|
+
const zigTarget = `aarch64-ios.${IPHONEOS_MIN_VERSION}${simulator ? "-simulator" : ""}`;
|
|
392
|
+
return {
|
|
393
|
+
argv: ["zig", "cc"],
|
|
394
|
+
target,
|
|
395
|
+
zigTarget,
|
|
396
|
+
targetArgs: ["-target", zigTarget, "-isysroot", sdk, "-isystem", join(sdk, "usr", "include")],
|
|
397
|
+
linkArgs: [],
|
|
398
|
+
};
|
|
399
|
+
}
|
|
400
|
+
if (isAndroidTarget(target)) {
|
|
401
|
+
// Library-mode-only target. Zig bundles no bionic: compiles ride the
|
|
402
|
+
// NDK sysroot's generic and per-triple include directories. Bionic
|
|
403
|
+
// supports _GNU_SOURCE like glibc (and hides some POSIX declarations
|
|
404
|
+
// without it); the zig spelling pins the API 26 floor, which the NDK's
|
|
405
|
+
// versioned stub libraries enforce at the embedder's link. API 26
|
|
406
|
+
// bionic carries everything the library-lane units call (arc4random_buf
|
|
407
|
+
// included), so no shim TU joins the archive.
|
|
408
|
+
const sysroot = androidNdkSysroot(env);
|
|
409
|
+
const zigTarget = `aarch64-linux-android.${ANDROID_MIN_API}`;
|
|
410
|
+
return {
|
|
411
|
+
argv: ["zig", "cc"],
|
|
412
|
+
target,
|
|
413
|
+
zigTarget,
|
|
414
|
+
targetArgs: [
|
|
415
|
+
"-target",
|
|
416
|
+
zigTarget,
|
|
417
|
+
"-D_GNU_SOURCE",
|
|
418
|
+
"-isystem",
|
|
419
|
+
join(sysroot, "usr", "include"),
|
|
420
|
+
"-isystem",
|
|
421
|
+
join(sysroot, "usr", "include", "aarch64-linux-android"),
|
|
422
|
+
],
|
|
423
|
+
linkArgs: ["-lm"],
|
|
424
|
+
};
|
|
425
|
+
}
|
|
203
426
|
const linux = target.includes("linux");
|
|
204
427
|
const wasi = target.includes("wasi");
|
|
205
428
|
const musl = target.includes("linux-musl");
|
|
206
429
|
return {
|
|
207
430
|
argv: ["zig", "cc"],
|
|
208
431
|
target,
|
|
432
|
+
zigTarget: target,
|
|
209
433
|
targetArgs: [
|
|
210
434
|
"-target",
|
|
211
435
|
target,
|
|
@@ -236,6 +460,11 @@ export function targetPlatform(driver) {
|
|
|
236
460
|
return process.platform;
|
|
237
461
|
if (driver.target.includes("wasi"))
|
|
238
462
|
return "wasi";
|
|
463
|
+
// iOS is a darwin-family target: Mach-O objects, ld64 localization,
|
|
464
|
+
// POSIX path/EOL semantics. Android falls to the linux arm below —
|
|
465
|
+
// bionic is a linux libc and its archives are ordinary ELF.
|
|
466
|
+
if (isIosTarget(driver.target))
|
|
467
|
+
return "darwin";
|
|
239
468
|
if (driver.target.includes("linux"))
|
|
240
469
|
return "linux";
|
|
241
470
|
if (driver.target.includes("windows"))
|
|
@@ -712,9 +941,12 @@ async function ensureTlsArchive(sanitize, driver, buildIdentity, cacheRoot = ven
|
|
|
712
941
|
* units a library may reach are the pure-data ones (regex + the vendored matcher, assert,
|
|
713
942
|
* inspect, symbol, searchParams, emitter+dyn_handle, zlib); every
|
|
714
943
|
* loop-hooked or ambient unit was refused at SC4005 before emission.
|
|
715
|
-
* External-symbol contract: undefined references only to
|
|
716
|
-
*
|
|
717
|
-
*
|
|
944
|
+
* External-symbol contract: undefined references only to the target's C/math
|
|
945
|
+
* runtime and system APIs. Windows embedders additionally link advapi32,
|
|
946
|
+
* iphlpapi, and ws2_32; the platform driver supplies its ordinary CRT and
|
|
947
|
+
* kernel imports. Zlib rides the VENDORED per-flavor objects here even on
|
|
948
|
+
* hosts because the executable lane's system `-lz` cannot ride inside an
|
|
949
|
+
* archive. */
|
|
718
950
|
/** The library base: the executable lane's unconditional sources minus the
|
|
719
951
|
* fiber/loop and child-process units, plus the library-mode TU. */
|
|
720
952
|
const LIB_RUNTIME_SOURCES = [
|
|
@@ -817,9 +1049,10 @@ export async function compileLibArchive(opts) {
|
|
|
817
1049
|
}
|
|
818
1050
|
const vendorBuildIdentity = await currentVendorCacheBuildIdentity(driver, `${toolchainEnv}\0${implicitToolchain ?? "<uncached>"}`);
|
|
819
1051
|
// Runtime-localized archives skip the completed-archive tier: their bytes
|
|
820
|
-
// additionally depend on the
|
|
821
|
-
//
|
|
822
|
-
//
|
|
1052
|
+
// additionally depend on the localization toolchain's identity (host ld/
|
|
1053
|
+
// objcopy or the cross driver's lld), which the archive key does not
|
|
1054
|
+
// fingerprint. The runtime-object tier still serves them (localization
|
|
1055
|
+
// consumes the same per-flavor objects).
|
|
823
1056
|
const cacheCompleteArchive = opts.localizeSymbols === undefined &&
|
|
824
1057
|
root !== null && await archiverSupportsPersistentCache(arArgv, driver);
|
|
825
1058
|
let cachedArchive = null;
|
|
@@ -1054,9 +1287,11 @@ export async function compileLibArchive(opts) {
|
|
|
1054
1287
|
* renamed apart — they stop being visible to the embedder's linker at all,
|
|
1055
1288
|
* so a second archive built under a different prefix brings its own private
|
|
1056
1289
|
* copy of the whole runtime (allocator, collector, arena, panic sink) into
|
|
1057
|
-
* the same process. Undefined references (
|
|
1058
|
-
* instrumented builds) keep their global
|
|
1059
|
-
* sanitizer are the embedder's,
|
|
1290
|
+
* the same process. Undefined references (the target C/math runtime and
|
|
1291
|
+
* system APIs, plus sanitizer ABI in instrumented builds) keep their global
|
|
1292
|
+
* binding: those platform services and the sanitizer are the embedder's,
|
|
1293
|
+
* shared by design. Windows embedders additionally link advapi32, iphlpapi,
|
|
1294
|
+
* and ws2_32.
|
|
1060
1295
|
*
|
|
1061
1296
|
* Member selection matters: a classic archive's unused members (and their
|
|
1062
1297
|
* undefined references to units library mode excludes, like the
|
|
@@ -1064,26 +1299,40 @@ export async function compileLibArchive(opts) {
|
|
|
1064
1299
|
* blind merge of every object would carry those references into the one
|
|
1065
1300
|
* combined member. Staging the support objects into an intermediate
|
|
1066
1301
|
* archive keeps the linker's own member semantics: `ld -r` pulls only the
|
|
1067
|
-
* members the program object transitively needs
|
|
1302
|
+
* members the program object transitively needs (the COFF arm implements
|
|
1303
|
+
* the same member semantics in process).
|
|
1068
1304
|
*
|
|
1069
|
-
*
|
|
1305
|
+
* Mach-O — one host-ld64 invocation: -r merges program + needed members,
|
|
1070
1306
|
* -exported_symbols_list demotes every unlisted global to
|
|
1071
1307
|
* private extern, and -r without -keep_private_externs writes
|
|
1072
1308
|
* private externs out as non-external symbols. Apple ASan's
|
|
1073
1309
|
* image-registration COMMON remains shared so the final Mach-O
|
|
1074
|
-
* image registers its globals once.
|
|
1075
|
-
*
|
|
1076
|
-
*
|
|
1077
|
-
*
|
|
1078
|
-
*
|
|
1079
|
-
*
|
|
1080
|
-
*
|
|
1081
|
-
*
|
|
1082
|
-
*
|
|
1083
|
-
*
|
|
1084
|
-
*
|
|
1085
|
-
*
|
|
1086
|
-
*
|
|
1310
|
+
* image registers its globals once. ld64 reads every Apple
|
|
1311
|
+
* platform's objects — macOS architectures and the iOS/
|
|
1312
|
+
* iOS-simulator triples alike (LC_BUILD_VERSION platform and
|
|
1313
|
+
* minos survive the merge) — so darwin-host macos AND ios
|
|
1314
|
+
* targets ride the same arm; compileLibrary refuses those
|
|
1315
|
+
* targets elsewhere.
|
|
1316
|
+
* ELF, native linux host — ld -r merges with --force-group-allocation
|
|
1317
|
+
* (ASan's instrumented globals ride ELF section groups whose
|
|
1318
|
+
* signatures repeat across archives sharing runtime objects;
|
|
1319
|
+
* resolving the groups into the combined member keeps a later
|
|
1320
|
+
* multi-archive link from discarding one archive's copies),
|
|
1321
|
+
* then binutils objcopy --keep-global-symbols localizes every
|
|
1322
|
+
* other DEFINED global (objcopy leaves undefined symbols
|
|
1323
|
+
* global by its own rule). The exact historical recipe.
|
|
1324
|
+
* ELF, cross triples from any host — `zig cc -target <triple> -r`
|
|
1325
|
+
* merges (zig is already the cross driver's hard requirement),
|
|
1326
|
+
* then localizeElfObject demotes in process and resolves
|
|
1327
|
+
* section groups the way --force-group-allocation does, with
|
|
1328
|
+
* no host binutils/llvm-objcopy dependency.
|
|
1329
|
+
* COFF, native win32 hosts and windows cross triples from any host —
|
|
1330
|
+
* mergeAndLocalizeCoffObjects performs member selection, the
|
|
1331
|
+
* merge, cross-object symbol resolution, and demotion in
|
|
1332
|
+
* process: no linker offers a COFF relocatable mode (lld-link
|
|
1333
|
+
* mirrors MSVC link.exe; zig's COFF driver refuses multi-object
|
|
1334
|
+
* merges) and llvm-objcopy rejects symbol-scope flags for
|
|
1335
|
+
* COFF, so no tool pairing exists to shell out to. */
|
|
1087
1336
|
async function localizeLibraryObjects(driver, arArgv, buildDir, programObject, supportObjects, keepSymbols, stem) {
|
|
1088
1337
|
const platform = targetPlatform(driver);
|
|
1089
1338
|
const combined = join(buildDir, `${stem}.localized.o`);
|
|
@@ -1096,21 +1345,58 @@ async function localizeLibraryObjects(driver, arArgv, buildDir, programObject, s
|
|
|
1096
1345
|
catch (err) {
|
|
1097
1346
|
const stderr = err.stderr ?? String(err);
|
|
1098
1347
|
throw new Error(`${argv[0]} failed while localizing the library archive's runtime symbols (abi.localize_runtime).\n` +
|
|
1099
|
-
`Runtime symbol localization needs the
|
|
1348
|
+
`Runtime symbol localization needs the ${platform === "darwin" ? "host toolchain's ld" : driver.target === null ? "host toolchain's ld and objcopy" : "cross driver's relocatable link"} beside the C compiler.\n\n${stderr}`);
|
|
1100
1349
|
}
|
|
1101
1350
|
};
|
|
1351
|
+
if (platform === "win32") {
|
|
1352
|
+
// COFF has no relocatable-link tool to stage through; the member
|
|
1353
|
+
// selection and combine+demote happen in process over the object bytes.
|
|
1354
|
+
const [program, ...support] = await Promise.all([programObject, ...supportObjects].map((path) => readFile(path)));
|
|
1355
|
+
try {
|
|
1356
|
+
await writeFile(combined, mergeAndLocalizeCoffObjects(program, support, new Set(keepSymbols), {
|
|
1357
|
+
program: basename(programObject),
|
|
1358
|
+
support: supportObjects.map((path) => basename(path)),
|
|
1359
|
+
}));
|
|
1360
|
+
}
|
|
1361
|
+
catch (err) {
|
|
1362
|
+
throw new Error(`COFF symbol localization failed while localizing the library archive's runtime symbols (abi.localize_runtime).\n\n${err.message}`);
|
|
1363
|
+
}
|
|
1364
|
+
return combined;
|
|
1365
|
+
}
|
|
1102
1366
|
await run([arArgv[0] ?? "ar", ...arArgv.slice(1), "rcs", staging, ...supportObjects]);
|
|
1103
1367
|
if (platform === "darwin") {
|
|
1104
1368
|
await writeFile(keepFile, keepSymbols.map((s) => `_${s}\n`).join(""));
|
|
1105
1369
|
await run(["ld", "-r", programObject, staging, "-o", combined, "-exported_symbols_list", keepFile]);
|
|
1106
1370
|
}
|
|
1107
|
-
else if (platform === "linux") {
|
|
1371
|
+
else if (platform === "linux" && driver.target === null) {
|
|
1108
1372
|
await writeFile(keepFile, keepSymbols.map((s) => `${s}\n`).join(""));
|
|
1109
1373
|
await run(["ld", "-r", "--force-group-allocation", programObject, staging, "-o", combined]);
|
|
1110
1374
|
await run(["objcopy", `--keep-global-symbols=${keepFile}`, combined]);
|
|
1111
1375
|
}
|
|
1376
|
+
else if (platform === "linux") {
|
|
1377
|
+
// Cross ELF: the cross driver's own lld performs the relocatable merge
|
|
1378
|
+
// (with the staging archive's member semantics); demotion and section-
|
|
1379
|
+
// group resolution happen in process. -nostdlib keeps zig from feeding
|
|
1380
|
+
// libc/compiler-rt inputs into the merge. Android rides this arm
|
|
1381
|
+
// unchanged: bionic archives are ordinary aarch64 ELF64, and the merge
|
|
1382
|
+
// uses the driver's zig spelling (which pins the API floor).
|
|
1383
|
+
await run([
|
|
1384
|
+
driver.argv[0] ?? "zig",
|
|
1385
|
+
...driver.argv.slice(1),
|
|
1386
|
+
"-target", driver.zigTarget ?? driver.target,
|
|
1387
|
+
"-nostdlib",
|
|
1388
|
+
"-r", programObject, staging,
|
|
1389
|
+
"-o", combined,
|
|
1390
|
+
]);
|
|
1391
|
+
try {
|
|
1392
|
+
await writeFile(combined, localizeElfObject(await readFile(combined), new Set(keepSymbols)));
|
|
1393
|
+
}
|
|
1394
|
+
catch (err) {
|
|
1395
|
+
throw new Error(`ELF symbol localization failed while localizing the library archive's runtime symbols (abi.localize_runtime).\n\n${err.message}`);
|
|
1396
|
+
}
|
|
1397
|
+
}
|
|
1112
1398
|
else {
|
|
1113
|
-
throw new Error(`runtime symbol localization (abi.localize_runtime)
|
|
1399
|
+
throw new Error(`runtime symbol localization (abi.localize_runtime) has no ${platform} arm; compileLibrary admits darwin, linux, and win32 builds only`);
|
|
1114
1400
|
}
|
|
1115
1401
|
return combined;
|
|
1116
1402
|
}
|
|
@@ -2277,6 +2563,14 @@ export async function compileC(opts) {
|
|
|
2277
2563
|
const http = (opts.http ?? false) || nativeFetch || netIsland;
|
|
2278
2564
|
const tls = (opts.tls ?? false) || nativeFetch || netIsland;
|
|
2279
2565
|
const driver = resolveCc();
|
|
2566
|
+
// Mobile triples produce library archives, never standalone executables:
|
|
2567
|
+
// the executable-lane runtime (event loop, sockets, child processes) is
|
|
2568
|
+
// not verified on those device classes. compile() reports the SC3002
|
|
2569
|
+
// diagnostic first; this is the backstop for direct compileC callers.
|
|
2570
|
+
if (isMobileTarget(driver.target)) {
|
|
2571
|
+
throw new Error(`SCRIPTC_TARGET=${driver.target} builds library-mode static archives only — ` +
|
|
2572
|
+
`compile with a library profile (SCRIPTC_CC=zigcc scriptc build --lib --profile <profile.json>) and link the archive from the app project.`);
|
|
2573
|
+
}
|
|
2280
2574
|
const runtimeSources = targetPlatform(driver) === "wasi"
|
|
2281
2575
|
? RUNTIME_SOURCES.filter((source) => source !== "scr_child.c")
|
|
2282
2576
|
: RUNTIME_SOURCES;
|