Files

91 lines
3.4 KiB
TypeScript
Raw Permalink Normal View History

2026-08-27 21:09:14 +00:00
import { availableParallelism } from "node:os";
import { existsSync } from "node:fs";
import { copyFile, mkdir, writeFile } from "node:fs/promises";
import { basename, dirname, join, relative, resolve } from "node:path";
import { build, buildWithNodeGyp, isBuilt, parseBindingGyp, warmNodeGyp } from "./harness";
const dirs = [...new Set(process.argv.slice(2).map(dir => resolve(dir)))];
if (dirs.length === 0) {
console.error("prebuild: no addon directories given, nothing to do");
process.exit(0);
}
const started = performance.now();
const pending = (await Promise.all(dirs.map(async dir => ((await isBuilt(dir)) ? null : dir)))).filter(
(dir): dir is string => dir !== null,
);
console.log(`prebuild: ${pending.length}/${dirs.length} addon(s) to build`);
let built = 0;
const failed: string[] = [];
if (pending.length) {
if (process.platform !== "win32") {
await warmNodeGyp();
const width = Math.max(2, Math.min(8, availableParallelism()));
let next = 0;
const worker = async () => {
for (;;) {
const dir = pending[next++];
if (dir === undefined) return;
try {
await build(dir);
built++;
} catch (error) {
failed.push(dir);
console.error(`prebuild: ${dir} failed:`, error);
}
}
};
await Promise.all(Array.from({ length: Math.min(width, pending.length) }, worker));
} else {
const combinedDir = join(import.meta.dir, ".prebuild");
await mkdir(combinedDir, { recursive: true });
const targets: { target_name: string; sources: string[]; defines?: string[] }[] = [];
const copies: { from: string; to: string }[] = [];
for (const dir of pending) {
const parsed = await parseBindingGyp(dir);
if (parsed === null) {
failed.push(dir); // not the trivial shape — its do.test.ts builds it alone
continue;
}
const ns = relative(join(import.meta.dir, "test"), dir)
.replaceAll("/", "_")
.replaceAll("\\", "_");
for (const target of parsed) {
const name = `${ns}__${target.target_name}`;
targets.push({
target_name: name,
sources: target.sources.map(source => relative(combinedDir, join(dir, source)).replaceAll("\\", "/")),
...(target.defines ? { defines: target.defines } : {}),
});
copies.push({
from: join(combinedDir, "build", "Debug", `${name}.node`),
to: join(dir, "build", "Debug", `${target.target_name}.node`),
});
}
}
if (targets.length) {
await writeFile(join(combinedDir, "binding.gyp"), JSON.stringify({ targets }, null, 2));
try {
await buildWithNodeGyp(combinedDir);
for (const { from, to } of copies) {
if (!existsSync(from)) throw new Error(`combined build did not produce ${basename(from)}`);
await mkdir(dirname(to), { recursive: true });
await copyFile(from, to);
}
built += pending.length - failed.length;
} catch (error) {
console.error("prebuild: combined node-gyp build failed; tests will build their own:", error);
failed.push(...pending.filter(dir => !failed.includes(dir)));
}
}
}
}
const seconds = ((performance.now() - started) / 1000).toFixed(1);
console.log(
`prebuild: ${built} built, ${dirs.length - pending.length} already built, ${failed.length} left to their tests, in ${seconds}s`,
);