• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1#!/usr/bin/env node
2
3// Usage: e.g. node build-addons.mjs <path to node-gyp> <directory>
4
5import child_process from 'node:child_process';
6import path from 'node:path';
7import fs from 'node:fs/promises';
8import util from 'node:util';
9import process from 'node:process';
10import os from 'node:os';
11
12const execFile = util.promisify(child_process.execFile);
13
14const parallelization = +process.env.JOBS || os.availableParallelism();
15const nodeGyp = process.argv[2];
16const directory = process.argv[3];
17
18async function buildAddon(dir) {
19  try {
20    // Only run for directories that have a `binding.gyp`.
21    // (https://github.com/nodejs/node/issues/14843)
22    await fs.stat(path.join(dir, 'binding.gyp'));
23  } catch (err) {
24    if (err.code === 'ENOENT' || err.code === 'ENOTDIR')
25      return;
26    throw err;
27  }
28
29  console.log(`Building addon in ${dir}`);
30  const { stdout, stderr } =
31    await execFile(process.execPath, [nodeGyp, 'rebuild', `--directory=${dir}`],
32                   {
33                     stdio: 'inherit',
34                     env: { ...process.env, MAKEFLAGS: '-j1' },
35                   });
36
37  // We buffer the output and print it out once the process is done in order
38  // to avoid interleaved output from multiple builds running at once.
39  process.stdout.write(stdout);
40  process.stderr.write(stderr);
41}
42
43async function parallel(jobQueue, limit) {
44  const next = async () => {
45    if (jobQueue.length === 0) {
46      return;
47    }
48    const job = jobQueue.shift();
49    await job();
50    await next();
51  };
52
53  const workerCnt = Math.min(limit, jobQueue.length);
54  await Promise.all(Array.from({ length: workerCnt }, next));
55}
56
57const jobs = [];
58for await (const dirent of await fs.opendir(directory)) {
59  if (dirent.isDirectory()) {
60    jobs.push(() => buildAddon(path.join(directory, dirent.name)));
61  }
62}
63await parallel(jobs, parallelization);
64