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