mirror of
https://github.com/vercel-labs/scriptc.git
synced 2026-10-02 08:35:07 +08:00
* feat(cli): expose native object link info - add versioned, cache-independent native link metadata for program objects - document the experimental runtime ABI and provide C-driver and Apple linker examples - verify ABI mismatch, FFI parity, packed installs, and external linking in CI * fix(example): validate native runtime pack identity
108 lines
4.1 KiB
JavaScript
108 lines
4.1 KiB
JavaScript
#!/usr/bin/env node
|
|
import { spawnSync } from "node:child_process";
|
|
import { mkdirSync, readFileSync, rmSync } from "node:fs";
|
|
import { basename, dirname, join, resolve } from "node:path";
|
|
|
|
const [mode, infoArg, outputArg] = process.argv.slice(2);
|
|
if ((mode !== "cc" && mode !== "ld") || !infoArg || !outputArg) {
|
|
console.error("usage: node link.mjs <cc|ld> <native-link-info.json> <output>");
|
|
process.exitCode = 2;
|
|
} else {
|
|
const info = JSON.parse(readFileSync(infoArg, "utf8"));
|
|
if (info.schema !== "scriptc.native-link-info.v1") {
|
|
throw new Error(`unsupported native link info schema: ${info.schema}`);
|
|
}
|
|
const runtimePackage = JSON.parse(
|
|
readFileSync(join(info.runtime_pack.root, "package.json"), "utf8"),
|
|
);
|
|
if (
|
|
runtimePackage.name !== info.runtime_pack.package ||
|
|
runtimePackage.version !== info.runtime_pack.version
|
|
) {
|
|
throw new Error(
|
|
`runtime pack identity mismatch: expected ${info.runtime_pack.package}@${info.runtime_pack.version} ` +
|
|
`at ${info.runtime_pack.root}, found ${runtimePackage.name ?? "<unnamed>"}@${runtimePackage.version ?? "<unversioned>"}`,
|
|
);
|
|
}
|
|
const run = (command, args, options = {}) => {
|
|
const result = spawnSync(command, args, { stdio: "inherit", ...options });
|
|
if (result.error) throw result.error;
|
|
if (result.status !== 0) throw new Error(`${command} exited ${result.status}`);
|
|
};
|
|
const capture = (command, args) => {
|
|
const result = spawnSync(command, args, { encoding: "utf8" });
|
|
if (result.error) throw result.error;
|
|
if (result.status !== 0) throw new Error(result.stderr || `${command} exited ${result.status}`);
|
|
return result.stdout.trim();
|
|
};
|
|
|
|
const infoPath = resolve(infoArg);
|
|
const output = resolve(outputArg);
|
|
const buildDir = join(dirname(infoPath), `.native-link-${mode}`);
|
|
rmSync(buildDir, { recursive: true, force: true });
|
|
mkdirSync(buildDir, { recursive: true });
|
|
const runtimeObjects = [];
|
|
const vendorArchives = [];
|
|
for (const set of info.runtime_pack.source_sets) {
|
|
const setDir = join(buildDir, set.name);
|
|
mkdirSync(setDir, { recursive: true });
|
|
const objects = [];
|
|
for (const source of set.sources) {
|
|
const object = join(setDir, `${source.replace(/[^A-Za-z0-9]+/g, "_")}.o`);
|
|
run("clang", [
|
|
...set.c_flags,
|
|
...set.defines.map((define) => `-D${define}`),
|
|
...set.include_directories.flatMap((path) => [
|
|
"-I", join(info.runtime_pack.root, path),
|
|
]),
|
|
"-c", join(info.runtime_pack.root, source), "-o", object,
|
|
]);
|
|
objects.push(object);
|
|
}
|
|
if (set.output === "objects") {
|
|
runtimeObjects.push(...objects);
|
|
} else {
|
|
const archive = join(buildDir, basename(set.suggested_output));
|
|
run("ar", ["rcs", archive, ...objects]);
|
|
vendorArchives.push(archive);
|
|
}
|
|
}
|
|
|
|
const inputs = [
|
|
info.program.object,
|
|
...info.ffi.libraries,
|
|
...runtimeObjects,
|
|
...vendorArchives,
|
|
];
|
|
const libraries = info.link.system_libraries.map((name) => `-l${name}`);
|
|
const frameworks = info.link.frameworks.flatMap((name) => ["-framework", name]);
|
|
if (mode === "cc") {
|
|
// Darwin compiler drivers add libSystem themselves. It remains explicit
|
|
// in the document because a direct ld invocation must name it.
|
|
const driverLibraries = info.link.system_libraries
|
|
.filter((name) => name !== "System")
|
|
.map((name) => `-l${name}`);
|
|
run("clang", [
|
|
...info.link.driver_flags,
|
|
...inputs,
|
|
...driverLibraries,
|
|
...frameworks,
|
|
"-o", output,
|
|
]);
|
|
} else {
|
|
const sdk = capture("xcrun", ["--sdk", "macosx", "--show-sdk-path"]);
|
|
const sdkVersion = capture("xcrun", ["--sdk", "macosx", "--show-sdk-version"]);
|
|
const linker = capture("xcrun", ["--sdk", "macosx", "--find", "ld"]);
|
|
run(linker, [
|
|
"-arch", info.target.architecture,
|
|
"-platform_version", "macos", info.target.minimum_os, sdkVersion,
|
|
"-syslibroot", sdk,
|
|
...(info.link.driver_flags.includes("-Wl,-dead_strip") ? ["-dead_strip"] : []),
|
|
...inputs,
|
|
...libraries,
|
|
...frameworks,
|
|
"-o", output,
|
|
]);
|
|
}
|
|
}
|