feat(cores): compile TypeScript cores for cross desktop targets (#340)
The external core compile driver now enforces the same host/target pairing matrix as the service compile lane: same-triple compiles keep the native lane, Linux and Windows GNU targets cross-compile from any macOS/Linux/Windows build host over the compiler's zig-cc lane, macOS targets need a macOS build host, and every refused pairing teaches before compiler work starts. The co-emitted contract sidecar is target-independent: a macOS-native, x86_64-windows-gnu, and x86_64-linux-musl compile of one staged tree emit byte-identical documents with identical integer-class decisions, and the COFF and ELF archives declare the same nsc_core_* symbol surface as the Mach-O one. The SDK's fixture graph compiles corewire for the build host, so the battery lanes configure under a cross -Dtarget, and a new stage-cross-e2e step installs the host-fixture, markup, and in-process service pool batteries under <prefix>/e2e for execution on the target machine. scripts/cross-e2e.sh drives the lane end to end: it cross-builds the batteries, the kanban example, and the service fixture app (in-process carrier — the core and service archives linked into one executable, with no defined-symbol overlap between the localized service archive and the core's contract surface) for x86_64-windows-gnu and x86_64-linux-musl, then executes the batteries on the Windows box over ssh and in an amd64 Alpine container. gate.sh runs the lane in either tier when NATIVE_SDK_CROSS=1 is set and skips it otherwise. All three batteries pass on both targets: 29 host-fixture, 10 markup, and 19 in-process pool tests on Windows (the three posix-spawn tests skip there), and 32/10/19 in the musl container. The TypeScript chapter and ts-core skill state the supported build matrix, including the Linux glibc spelling and the unchanged desktop-only scope for mobile.
This commit is contained in:
@@ -66,6 +66,10 @@ const argv0 = args.compiler
|
||||
// zig-cc lane as the service archive that links beside it; otherwise a cross
|
||||
// build mixes a host-format core archive with target-format Zig and service
|
||||
// objects. Standalone host-only callers may omit both platform arguments.
|
||||
// The pairing matrix is the pinned compiler's build matrix, shared with
|
||||
// run_external_service_compiler.mjs: same-triple compiles run the native
|
||||
// lane; Linux and Windows GNU targets cross-compile from any desktop host;
|
||||
// macOS targets need a macOS build host.
|
||||
const hostParts = args["host-platform"]?.split("-") ?? [];
|
||||
const targetParts = args["target-platform"]?.split("-") ?? [];
|
||||
const hostArch = hostParts[0] ?? "";
|
||||
@@ -81,9 +85,18 @@ const nativeWindows = hostOs === "windows" && targetOs === "windows" && hostArch
|
||||
const cross = args["host-platform"] !== undefined &&
|
||||
args["target-platform"] !== args["host-platform"] && !nativeWindows;
|
||||
if (cross) {
|
||||
if (targetOs === "windows" && targetAbi !== "gnu") {
|
||||
const desktopHost = ["macos", "linux", "windows"].includes(hostOs);
|
||||
const admitted = desktopHost &&
|
||||
(targetOs === "linux" ||
|
||||
(targetOs === "windows" && targetAbi === "gnu") ||
|
||||
(targetOs === "macos" && hostOs === "macos"));
|
||||
if (!admitted) {
|
||||
console.error(
|
||||
`TypeScript cores for a cross-target Windows build (${args["target-platform"]}) require the GNU ABI: Zig supplies that target's CRT and system libraries, while an MSVC target needs a native Windows toolchain. Build ${targetArch}-windows-msvc on a matching Windows host, or cross-compile as "${targetArch}-windows-gnu".`,
|
||||
targetOs === "windows" && targetAbi !== "gnu"
|
||||
? `TypeScript cores for a cross-target Windows build (${args["target-platform"]}) require the GNU ABI: Zig supplies that target's CRT and system libraries, while an MSVC target needs a native Windows toolchain. Build ${targetArch}-windows-msvc on a matching Windows host, or cross-compile as "${targetArch}-windows-gnu".`
|
||||
: targetOs === "macos"
|
||||
? `TypeScript cores for a macOS target (${args["target-platform"]}) compile on a macOS build host only — Apple linking needs the host toolchain's SDK — but this build host is ${args["host-platform"]}. Build macOS apps on a Mac.`
|
||||
: `TypeScript cores compile for desktop targets the pinned compiler covers — Linux and Windows GNU from a macOS/Linux/Windows build host, macOS from a macOS host — but this build pairs host ${args["host-platform"]} with target ${args["target-platform"]}.`,
|
||||
);
|
||||
process.exit(2);
|
||||
}
|
||||
|
||||
@@ -93,6 +93,72 @@ test("the external core compile lane refuses cross-target Windows MSVC before co
|
||||
}
|
||||
});
|
||||
|
||||
test("the external core compile lane refuses a macOS target from a non-macOS host", () => {
|
||||
const root = fs.mkdtempSync(path.join(os.tmpdir(), "native-core-macos-cross-"));
|
||||
try {
|
||||
const stage = path.join(root, "stage");
|
||||
fs.mkdirSync(stage);
|
||||
const manifest = path.join(root, "package.json");
|
||||
fs.writeFileSync(manifest, JSON.stringify({ dependencies: { scriptc: "0.0.28" } }));
|
||||
const frontendSidecar = path.join(root, "frontend.contract.json");
|
||||
fs.writeFileSync(frontendSidecar, JSON.stringify({
|
||||
model_fingerprint: "0123456789abcdef",
|
||||
has_migrate: false,
|
||||
}));
|
||||
const script = path.join(path.dirname(fileURLToPath(import.meta.url)), "..", "scripts", "run_external_core_compiler.mjs");
|
||||
const result = spawnSync(process.execPath, [
|
||||
script,
|
||||
"--stage", stage,
|
||||
"--name", "fixture_core",
|
||||
"--manifest", manifest,
|
||||
"--frontend-sidecar", frontendSidecar,
|
||||
"--out-archive", path.join(root, "libfixture_core.a"),
|
||||
"--out-sidecar", path.join(root, "compiled.contract.json"),
|
||||
"--host-platform", "x86_64-linux-gnu",
|
||||
"--target-platform", "aarch64-macos-none",
|
||||
"--compiler", process.execPath,
|
||||
], { encoding: "utf8" });
|
||||
assert.notEqual(result.status, 0);
|
||||
assert.match(result.stderr, /macOS build host only/);
|
||||
assert.doesNotMatch(result.stderr, /external core compiler did not report a version/);
|
||||
} finally {
|
||||
fs.rmSync(root, { recursive: true, force: true });
|
||||
}
|
||||
});
|
||||
|
||||
test("the external core compile lane refuses pairings outside the compiler's matrix", () => {
|
||||
const root = fs.mkdtempSync(path.join(os.tmpdir(), "native-core-matrix-"));
|
||||
try {
|
||||
const stage = path.join(root, "stage");
|
||||
fs.mkdirSync(stage);
|
||||
const manifest = path.join(root, "package.json");
|
||||
fs.writeFileSync(manifest, JSON.stringify({ dependencies: { scriptc: "0.0.28" } }));
|
||||
const frontendSidecar = path.join(root, "frontend.contract.json");
|
||||
fs.writeFileSync(frontendSidecar, JSON.stringify({
|
||||
model_fingerprint: "0123456789abcdef",
|
||||
has_migrate: false,
|
||||
}));
|
||||
const script = path.join(path.dirname(fileURLToPath(import.meta.url)), "..", "scripts", "run_external_core_compiler.mjs");
|
||||
const result = spawnSync(process.execPath, [
|
||||
script,
|
||||
"--stage", stage,
|
||||
"--name", "fixture_core",
|
||||
"--manifest", manifest,
|
||||
"--frontend-sidecar", frontendSidecar,
|
||||
"--out-archive", path.join(root, "libfixture_core.a"),
|
||||
"--out-sidecar", path.join(root, "compiled.contract.json"),
|
||||
"--host-platform", "aarch64-macos-none",
|
||||
"--target-platform", "aarch64-ios-none",
|
||||
"--compiler", process.execPath,
|
||||
], { encoding: "utf8" });
|
||||
assert.notEqual(result.status, 0);
|
||||
assert.match(result.stderr, /desktop targets the pinned compiler covers/);
|
||||
assert.doesNotMatch(result.stderr, /external core compiler did not report a version/);
|
||||
} finally {
|
||||
fs.rmSync(root, { recursive: true, force: true });
|
||||
}
|
||||
});
|
||||
|
||||
test("the external core compile lane preserves native Windows MSVC", () => {
|
||||
const root = fs.mkdtempSync(path.join(os.tmpdir(), "native-core-msvc-native-"));
|
||||
try {
|
||||
|
||||
Reference in New Issue
Block a user