mirror of
https://github.com/vercel-labs/scriptc.git
synced 2026-11-01 07:33:12 +08:00
Runtime packs: variants of a unit that do not read their defines, and flavors that compile a unit the same way, produce identical objects and bitcode. The pack build now points those manifest entries at one file (shareIdenticalArtifacts) and deletes the copies. Each link selects one flavor and one variant per unit, so no link names a shared file twice. The linux-x64-gnu pack keeps 1,191 of its 1,448 artifact files. Native CLI packages: - The comptime evaluator builds with -g0 and the executable strip flags, and the Windows build no longer ships its .pdb. - The copied tsc-ts executable is stripped with llvm-strip, which rewrites the linker-signed code signature of Mach-O executables. - On arm64 macOS, verify-native-cli.mjs --run checks the code signatures of the compiler, tsc-ts and the comptime evaluator. - SCRIPTC_NATIVE_KEEP_SYMBOLS=1 keeps the symbols of all three. - The copied runtime sources and declarations get LF line endings. A Windows checkout converts them to CRLF, so the Windows CLI shipped files that differed from @scriptc/runtime's.
49 lines
1.8 KiB
JavaScript
49 lines
1.8 KiB
JavaScript
import { readdir, rm, rmdir } from "node:fs/promises";
|
|
import { join } from "node:path";
|
|
|
|
/** Points the artifacts of one runtime unit that hold the same bytes at one
|
|
* file and deletes the copies. Variants whose defines a unit does not read,
|
|
* and flavors that compile a unit the same way, produce identical objects
|
|
* and bitcode. Each link selects one flavor and one variant of each unit, so
|
|
* no link names a shared file twice. `root` is the directory artifact paths
|
|
* are relative to; flavors keep the order they were built in, so the first
|
|
* flavor's path holds the shared file. */
|
|
export async function shareIdenticalArtifacts(flavors, root) {
|
|
const firstPaths = new Map();
|
|
for (const flavor of Object.values(flavors)) {
|
|
for (const unit of flavor.runtime_units) {
|
|
let paths = firstPaths.get(unit.source);
|
|
if (paths === undefined) {
|
|
paths = new Map();
|
|
firstPaths.set(unit.source, paths);
|
|
}
|
|
for (const variant of unit.variants) {
|
|
for (const artifact of variant.bitcode === undefined
|
|
? [variant]
|
|
: [variant, variant.bitcode]) {
|
|
const first = paths.get(artifact.sha256);
|
|
if (first === undefined) {
|
|
paths.set(artifact.sha256, artifact.path);
|
|
} else if (first !== artifact.path) {
|
|
await rm(join(root, artifact.path));
|
|
artifact.path = first;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
await removeEmptyDirectories(join(root, "artifacts"));
|
|
}
|
|
|
|
async function removeEmptyDirectories(directory) {
|
|
const entries = await readdir(directory, { withFileTypes: true });
|
|
let remaining = entries.length;
|
|
for (const entry of entries) {
|
|
if (entry.isDirectory() && (await removeEmptyDirectories(join(directory, entry.name))))
|
|
remaining--;
|
|
}
|
|
if (remaining > 0) return false;
|
|
await rmdir(directory);
|
|
return true;
|
|
}
|