#!/usr/bin/env node
// The native usage core is a second implementation of the usage contract, so the repo
// checks it like the rest of the code. It lives in its own repository and arrives here as a
// submodule, so "not checked out" is a state this script has to name as clearly as a missing
// toolchain. A missing toolchain is reported with the pinned version and how to get it rather
// than as an unexplained failure.
import { execFileSync } from "node:child_process";
import { existsSync } from "node:fs";
import { homedir } from "node:os";
import { dirname, join, resolve } from "node:path";
import { fileURLToPath } from "node:url";
const PINNED = "0.16.0";
const repoRoot = resolve(dirname(fileURLToPath(import.meta.url)), "..");
const nativeDir = join(repoRoot, "native", "usage");
function findZig() {
if (process.env.ZIG) return process.env.ZIG;
const candidates = [
"zig",
join(homedir(), ".local", "share", "zig", PINNED, "zig"),
`/opt/homebrew/bin/zig`,
`/usr/local/bin/zig`,
];
for (const candidate of candidates) {
try {
execFileSync(candidate, ["version"], { stdio: "ignore" });
return candidate;
} catch {
// try the next candidate
}
}
return undefined;
}
const zig = findZig();
if (!zig) {
console.error(
[
`Zig ${PINNED} is required for the native usage core and was not found.`,
"",
"Install the pinned toolchain (see native/usage/README.md):",
" curl -L -o /tmp/zig.tar.xz https://ziglang.org/download/" + PINNED + "/zig-aarch64-macos-" + PINNED + ".tar.xz",
" shasum -a 256 /tmp/zig.tar.xz",
" mkdir -p ~/.local/share/zig && tar -xJf /tmp/zig.tar.xz -C ~/.local/share/zig",
"",
"or point ZIG at an existing zig binary.",
].join("\n"),
);
process.exit(1);
}
const version = execFileSync(zig, ["version"], { encoding: "utf8" }).trim();
if (version !== PINNED) {
console.error(`Zig ${version} is not the pinned ${PINNED} (native/usage/build.zig.zon); the 0.16 standard library changed args/stdio/fs/clock APIs.`);
process.exit(1);
}
if (!existsSync(join(nativeDir, "build.zig"))) {
console.error(
[
`The usage core is not checked out at ${nativeDir}.`,
"",
"It is a submodule (its own repository), so a plain clone leaves it empty:",
" git submodule update --init native/usage",
].join("\n"),
);
process.exit(1);
}
try {
execFileSync(zig, ["build", "test"], { cwd: nativeDir, stdio: "inherit" });
// The Swift wrapper links the *installed* static library, so the install step has to run before
// swift build does; `zig build test` alone leaves zig-out empty.
execFileSync(zig, ["build"], { cwd: nativeDir, stdio: "inherit" });
console.log(`Zig ${version} native usage core: tests and C ABI smoke test passed.`);
} catch {
process.exit(1);
}
// The Swift wrapper is only checked where a Swift toolchain exists (macOS dev machines). CI is Linux
// and skips it, which is honest as long as the skip is printed rather than silent.
const swiftDir = join(nativeDir, "swift");
if (existsSync(swiftDir)) {
try {
execFileSync("swift", ["build", "--package-path", swiftDir], { stdio: "inherit" });
execFileSync("swift", ["run", "--package-path", swiftDir, "usage-smoke", join(nativeDir, "tests", "fixtures")], { cwd: nativeDir, stdio: "inherit" });
console.log("Swift wrapper: built and passed its smoke test.");
} catch (error) {
if (error && error.code === "ENOENT") {
console.log("Swift wrapper: skipped, no swift toolchain on PATH.");
} else {
process.exit(1);
}
}
}