mirror of
https://github.com/vercel-labs/scriptc.git
synced 2026-10-31 07:23:06 +08:00
A build with an empty cache type-checked scriptc's own declaration files, the TypeScript libraries and scriptc's declarations, before reusing the answer in later builds. That check was 18 ms of a 105 ms first build of hello world. - build-native-cli.mts has the built compiler build a module without imports and a module with a type error, each in an empty cache, and records the content keys of the declaration file sets the checker reported no diagnostics in: the checker release, the compiler options, and each file's name and sha256. They ship in lib/declarations/lib-diagnostics.json, named by the toolchain manifest's lib_diagnostics. - A build whose cache has no answer computes the files' content key and, when the shipped answers hold it, uses no diagnostics for those files and stores that answer in the cache. The shipped answers apply only while the checker is the distribution's own tsc-ts and its package version is the release they were recorded with; a changed file has another key. - A compiler the build host cannot run ships without them.
116 lines
5.1 KiB
JavaScript
116 lines
5.1 KiB
JavaScript
import { execFileSync } from "node:child_process";
|
|
import { existsSync, readFileSync, readdirSync, realpathSync, statSync } from "node:fs";
|
|
import { dirname, isAbsolute, join, relative, resolve, sep, win32 } from "node:path";
|
|
import { verifyLinuxAbi } from "./verify-linux-abi.mjs";
|
|
|
|
const root = realpathSync(resolve(process.argv[2] ?? "."));
|
|
const identity = JSON.parse(readFileSync(join(root, "package.json"), "utf8"));
|
|
const target = identity.name.replace("@scriptc/cli-", "");
|
|
const windows = target.startsWith("win32-");
|
|
const hostPath = (value) => (windows ? value.replaceAll("\\", "/") : value);
|
|
const absolutePath = (value) => isAbsolute(value) || (windows && win32.parse(value).root !== "");
|
|
const distribution = join(root, "dist");
|
|
const binary = join(distribution, "bin", target.startsWith("win32-") ? "scriptc.exe" : "scriptc");
|
|
const toolchain = JSON.parse(readFileSync(binary + ".json", "utf8"));
|
|
if (
|
|
toolchain.schema !== "scriptc.native-toolchain.v1" ||
|
|
toolchain.compiler_version !== identity.version
|
|
) {
|
|
throw new Error("native CLI manifest version does not match the package");
|
|
}
|
|
if (statSync(binary).size < 1024) throw new Error("native compiler payload is missing");
|
|
if (!target.startsWith("win32-") && !(statSync(binary).mode & 0o111))
|
|
throw new Error("native compiler is not executable");
|
|
const within = (path) => {
|
|
const child = relative(distribution, realpathSync(path));
|
|
if (child === ".." || child.startsWith(".." + sep) || isAbsolute(child))
|
|
throw new Error(`distribution references external asset: ${path}`);
|
|
};
|
|
for (const name of [
|
|
"ts7",
|
|
"tsc_ts",
|
|
"llvm_package",
|
|
"runtime_pack",
|
|
"runtime_sources",
|
|
"declarations",
|
|
"comptime",
|
|
"wasi_node_runner",
|
|
]) {
|
|
const value = toolchain[name];
|
|
if (typeof value !== "string" || absolutePath(value))
|
|
throw new Error(`native toolchain needs a relative ${name}`);
|
|
within(resolve(dirname(binary), hostPath(value)));
|
|
}
|
|
// Optional: a compiler this host could not run while building ships without it.
|
|
if (toolchain.lib_diagnostics !== undefined) {
|
|
const value = toolchain.lib_diagnostics;
|
|
if (typeof value !== "string" || absolutePath(value))
|
|
throw new Error("native toolchain needs a relative lib_diagnostics");
|
|
within(resolve(dirname(binary), hostPath(value)));
|
|
}
|
|
const pack = JSON.parse(
|
|
readFileSync(
|
|
resolve(dirname(binary), hostPath(toolchain.runtime_pack), "runtime-pack.json"),
|
|
"utf8",
|
|
),
|
|
);
|
|
if (pack.version !== identity.version || pack.target.name !== toolchain.target)
|
|
throw new Error("native compiler runtime identity mismatch");
|
|
const seen = new Set();
|
|
for (const runtime of toolchain.runtime_packs ?? []) {
|
|
if (seen.has(runtime.target)) throw new Error(`duplicate runtime target: ${runtime.target}`);
|
|
seen.add(runtime.target);
|
|
if (typeof runtime.path !== "string" || absolutePath(runtime.path))
|
|
throw new Error("native runtime pack paths must be relative");
|
|
const directory = resolve(dirname(binary), hostPath(runtime.path));
|
|
within(directory);
|
|
const manifest = JSON.parse(readFileSync(join(directory, "runtime-pack.json"), "utf8"));
|
|
const packageIdentity = JSON.parse(readFileSync(join(directory, "package.json"), "utf8"));
|
|
if (
|
|
manifest.schema !== "scriptc.runtime-pack.v1" ||
|
|
manifest.target.name !== runtime.target ||
|
|
manifest.version !== identity.version ||
|
|
packageIdentity.version !== identity.version ||
|
|
packageIdentity.name !== manifest.package
|
|
)
|
|
throw new Error(`native runtime pack identity mismatch: ${runtime.target}`);
|
|
}
|
|
for (const name of ["LICENSE", "THIRD_PARTY_NOTICES"]) {
|
|
if (!existsSync(join(distribution, name))) throw new Error(`native compiler is missing ${name}`);
|
|
}
|
|
for (const directory of [distribution, join(distribution, "lib/runtime-sources/vendor")]) {
|
|
if (readdirSync(directory).some((name) => ["node_modules", ".cache", "seed"].includes(name)))
|
|
throw new Error(`development files present in ${directory}`);
|
|
}
|
|
if (process.argv.includes("--run")) {
|
|
if (target.startsWith("linux-")) {
|
|
for (const executable of [
|
|
binary,
|
|
resolve(dirname(binary), toolchain.ts7),
|
|
resolve(dirname(binary), toolchain.comptime),
|
|
]) {
|
|
verifyLinuxAbi(executable, { staticOnly: target.endsWith("-musl") });
|
|
}
|
|
verifyLinuxAbi(resolve(dirname(binary), toolchain.llvm_package, "bin/scriptc-llvm-codegen"), {
|
|
staticOnly: true,
|
|
});
|
|
}
|
|
const version = execFileSync(binary, ["--version"], {
|
|
encoding: "utf8",
|
|
env: { ...process.env, PATH: "" },
|
|
}).trim();
|
|
if (version !== identity.version)
|
|
throw new Error(`native command reported ${version}, expected ${identity.version}`);
|
|
// tsc-ts, the default checker, runs on the distribution's own platform.
|
|
const tscTs = resolve(dirname(binary), hostPath(toolchain.tsc_ts));
|
|
if (windows && !tscTs.endsWith("tsc-ts.exe"))
|
|
throw new Error(`tsc_ts is not tsc-ts.exe: ${tscTs}`);
|
|
const tscTsVersion = execFileSync(tscTs, ["--version"], {
|
|
encoding: "utf8",
|
|
env: { ...process.env, PATH: "" },
|
|
}).trim();
|
|
if (!tscTsVersion.startsWith("Version "))
|
|
throw new Error(`the distribution's tsc-ts reported ${JSON.stringify(tscTsVersion)}`);
|
|
}
|
|
console.log(`${identity.name}@${identity.version}: native distribution verified`);
|