91 lines
3.4 KiB
TypeScript
91 lines
3.4 KiB
TypeScript
|
|
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`,
|
|
);
|