From b092dfaed1229459681ced3eb7579728deb1094b Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 2 Jul 2026 14:24:42 +0000 Subject: [PATCH 1/8] fix(@stdlib/_tools/pkgs/addons): require `binding.gyp` to detect a native add-on MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The job `Run random benchmarks` in workflow `random_benchmarks` fails whenever its daily random package sample includes one of the WebAssembly-only packages under `blas/base/wasm/*`, with `gyp: binding.gyp not found`. 24 of the last 30 nightly runs on develop failed this way. Root cause: `inspect.js`/`sync.js` classify a package as a native add-on solely by checking for `src/Makefile`. WASM-only packages also ship a `src/Makefile` (used to build `.wasm`/`.wat` artifacts via `wasm2wat`/`wasm2js`), but have no `binding.gyp`, so `install-node-addons` runs `node-gyp rebuild` against them and fails immediately. This commit additionally requires a `binding.gyp` file — the file `node-gyp` itself needs to build — before classifying a package as a native add-on, applied to both the async (`inspect.js`) and sync (`sync.js`) detectors, which duplicate the same detection logic. Ref: https://github.com/stdlib-js/stdlib/actions/runs/28557481039 --- .../@stdlib/_tools/pkgs/addons/lib/inspect.js | 29 +++++++++++++++++-- .../@stdlib/_tools/pkgs/addons/lib/sync.js | 4 ++- 2 files changed, 30 insertions(+), 3 deletions(-) diff --git a/lib/node_modules/@stdlib/_tools/pkgs/addons/lib/inspect.js b/lib/node_modules/@stdlib/_tools/pkgs/addons/lib/inspect.js index 94e95c36c351..287cbcf6977f 100644 --- a/lib/node_modules/@stdlib/_tools/pkgs/addons/lib/inspect.js +++ b/lib/node_modules/@stdlib/_tools/pkgs/addons/lib/inspect.js @@ -68,11 +68,36 @@ function inspect( files, clbk ) { fpath = resolve( dir, 'src', 'Makefile' ); debug( 'Checking for add-on...' ); - exists( fpath, onExists ); + exists( fpath, onExistsMakefile ); } /** - * Callback invoked upon testing if a file exists. + * Callback invoked upon testing if a `src/Makefile` exists. + * + * @private + * @param {(Error|null)} error - error object + * @param {boolean} bool - boolean indicating whether a file exists + * @returns {void} + */ + function onExistsMakefile( error, bool ) { + var j = i + 1; + var gpath; + if ( error ) { + debug( 'Encountered an error when testing for add-on: %s (%d of %d). Error: %s', files[ i ], j, total, error.message ); + return done(); + } + if ( !bool ) { + debug( 'Unable to detect add-on.' ); + return done(); + } + // A `src/Makefile` is also present in WebAssembly-only packages, so additionally require a `binding.gyp` file, which is what `node-gyp` needs to build a native add-on: + gpath = resolve( dirname( files[ i ] ), 'binding.gyp' ); + debug( 'Checking for `binding.gyp`...' ); + exists( gpath, onExists ); + } + + /** + * Callback invoked upon testing if a `binding.gyp` file exists. * * @private * @param {(Error|null)} error - error object diff --git a/lib/node_modules/@stdlib/_tools/pkgs/addons/lib/sync.js b/lib/node_modules/@stdlib/_tools/pkgs/addons/lib/sync.js index 2e8d92d16b2e..977d1786c6ae 100644 --- a/lib/node_modules/@stdlib/_tools/pkgs/addons/lib/sync.js +++ b/lib/node_modules/@stdlib/_tools/pkgs/addons/lib/sync.js @@ -84,7 +84,9 @@ function findAddons( options ) { for ( i = 0; i < files.length; i++ ) { dir = dirname( files[ i ] ); fpath = resolve( dir, 'src', 'Makefile' ); - if ( exists( fpath ) ) { + + // A `src/Makefile` is also present in WebAssembly-only packages, so additionally require a `binding.gyp` file, which is what `node-gyp` needs to build a native add-on: + if ( exists( fpath ) && exists( resolve( dir, 'binding.gyp' ) ) ) { out.push( dir ); } } From bf35ca3815b6291f4069c5b4f87d359e419ceff5 Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 2 Jul 2026 14:36:24 +0000 Subject: [PATCH 2/8] style(@stdlib/_tools/pkgs/addons): fix variable declaration order in `inspect.js` `Lint Changed Files` failed on PR #13233 with `stdlib/vars-order`: variable declarations inside a function must be ordered by name length in decreasing order. `onExistsMakefile` declared `j` before the longer `gpath`. Swap the declaration order to fix. --- lib/node_modules/@stdlib/_tools/pkgs/addons/lib/inspect.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/node_modules/@stdlib/_tools/pkgs/addons/lib/inspect.js b/lib/node_modules/@stdlib/_tools/pkgs/addons/lib/inspect.js index 287cbcf6977f..370569403da2 100644 --- a/lib/node_modules/@stdlib/_tools/pkgs/addons/lib/inspect.js +++ b/lib/node_modules/@stdlib/_tools/pkgs/addons/lib/inspect.js @@ -80,8 +80,8 @@ function inspect( files, clbk ) { * @returns {void} */ function onExistsMakefile( error, bool ) { - var j = i + 1; var gpath; + var j = i + 1; if ( error ) { debug( 'Encountered an error when testing for add-on: %s (%d of %d). Error: %s', files[ i ], j, total, error.message ); return done(); From 244b1644820a6cb0782cbd787b93ec998089f132 Mon Sep 17 00:00:00 2001 From: Athan Date: Thu, 2 Jul 2026 15:42:01 -0700 Subject: [PATCH 3/8] refactor: add additional checks Signed-off-by: Athan --- .../@stdlib/_tools/pkgs/addons/lib/sync.js | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/lib/node_modules/@stdlib/_tools/pkgs/addons/lib/sync.js b/lib/node_modules/@stdlib/_tools/pkgs/addons/lib/sync.js index 977d1786c6ae..504f9a8541fe 100644 --- a/lib/node_modules/@stdlib/_tools/pkgs/addons/lib/sync.js +++ b/lib/node_modules/@stdlib/_tools/pkgs/addons/lib/sync.js @@ -38,12 +38,12 @@ var validate = require( './validate.js' ); * @param {Options} [options] - function options * @param {string} [options.dir] - root directory from which to search for add-ons * @param {string} [options.pattern='**\/package.json'] - glob pattern -* @param {StringArray} [options.ignore] - glob pattern(s) to exclude matches +* @param {ArrayLikeObject} [options.ignore] - glob pattern(s) to exclude matches * @throws {TypeError} options argument must be an object * @throws {TypeError} must provide valid options * @throws {Error} `pattern` option must end with `package.json` * @throws {Error} unable to parse `package.json` as JSON -* @returns {(EmptyArray|StringArray)} list of add-ons +* @returns {(EmptyArray|Array)} list of add-ons * * @example * var pkgs = findAddons(); @@ -83,10 +83,14 @@ function findAddons( options ) { out = []; for ( i = 0; i < files.length; i++ ) { dir = dirname( files[ i ] ); - fpath = resolve( dir, 'src', 'Makefile' ); - - // A `src/Makefile` is also present in WebAssembly-only packages, so additionally require a `binding.gyp` file, which is what `node-gyp` needs to build a native add-on: - if ( exists( fpath ) && exists( resolve( dir, 'binding.gyp' ) ) ) { + if ( + exists( resolve( dir, 'src', 'Makefile' ) ) && + exists( resolve( dir, 'binding.gyp' ) ) && + ( + exists( resolve( dir, 'src', 'addon.c' ) ) || + exists( resolve( dir, 'src', 'addon.cpp' ) ) + ) + ) { out.push( dir ); } } From 003b90cd64d03e59d504fcd34d1799c6b8f241ae Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 2 Jul 2026 22:45:28 +0000 Subject: [PATCH 4/8] style(@stdlib/_tools/pkgs/addons): remove unused `fpath` variable in `sync.js` `Lint Changed Files` failed with `no-unused-vars`: the preceding refactor (244b164) inlined the `resolve()` calls directly into the `if` condition but left the now-unused `var fpath;` declaration. --- lib/node_modules/@stdlib/_tools/pkgs/addons/lib/sync.js | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/node_modules/@stdlib/_tools/pkgs/addons/lib/sync.js b/lib/node_modules/@stdlib/_tools/pkgs/addons/lib/sync.js index 504f9a8541fe..066753843c38 100644 --- a/lib/node_modules/@stdlib/_tools/pkgs/addons/lib/sync.js +++ b/lib/node_modules/@stdlib/_tools/pkgs/addons/lib/sync.js @@ -50,7 +50,6 @@ var validate = require( './validate.js' ); * // returns [...] */ function findAddons( options ) { - var fpath; var gopts; var files; var opts; From b682a7166f3843d56005319101fab4c088e4c84e Mon Sep 17 00:00:00 2001 From: Athan Date: Thu, 2 Jul 2026 16:03:35 -0700 Subject: [PATCH 5/8] refactor: check for multiple files Signed-off-by: Athan --- .../@stdlib/_tools/pkgs/addons/lib/inspect.js | 67 ++++++++++--------- 1 file changed, 35 insertions(+), 32 deletions(-) diff --git a/lib/node_modules/@stdlib/_tools/pkgs/addons/lib/inspect.js b/lib/node_modules/@stdlib/_tools/pkgs/addons/lib/inspect.js index 370569403da2..c8ab6609659c 100644 --- a/lib/node_modules/@stdlib/_tools/pkgs/addons/lib/inspect.js +++ b/lib/node_modules/@stdlib/_tools/pkgs/addons/lib/inspect.js @@ -24,12 +24,23 @@ var resolve = require( 'path' ).resolve; var logger = require( 'debug' ); var dirname = require( '@stdlib/utils/dirname' ); var exists = require( '@stdlib/fs/exists' ); +var join = require( '@stdlib/array/base/join' ); // VARIABLES // var debug = logger( 'pkgs:add-ons:resolve' ); +var THRESHOLD = 3; +var FILES = [ + [ 'src', 'Makefile' ], + [ 'binding.gyp' ], + + // WARNING: we assume that a package does NOT have both an `addon.c` AND an `addon.cpp` file! + [ 'src', 'addon.c' ], + [ 'src', 'addon.cpp' ] +]; + // MAIN // @@ -42,7 +53,9 @@ var debug = logger( 'pkgs:add-ons:resolve' ); * @returns {void} */ function inspect( files, clbk ) { + var counter; var total; + var npass; var out; var i; @@ -60,44 +73,25 @@ function inspect( files, clbk ) { function next() { var fpath; var dir; + var j; i += 1; debug( 'Inspecting package %d of %d: %s', i+1, total, files[ i ] ); dir = dirname( files[ i ] ); - fpath = resolve( dir, 'src', 'Makefile' ); - + counter = 0; + npass = 0; + debug( 'Checking for add-on...' ); - exists( fpath, onExistsMakefile ); - } - - /** - * Callback invoked upon testing if a `src/Makefile` exists. - * - * @private - * @param {(Error|null)} error - error object - * @param {boolean} bool - boolean indicating whether a file exists - * @returns {void} - */ - function onExistsMakefile( error, bool ) { - var gpath; - var j = i + 1; - if ( error ) { - debug( 'Encountered an error when testing for add-on: %s (%d of %d). Error: %s', files[ i ], j, total, error.message ); - return done(); - } - if ( !bool ) { - debug( 'Unable to detect add-on.' ); - return done(); + for ( j = 0; j < FILES.length; j++ ) { + debug( 'Checking for add-on file: %s', join( FILES[ j ] ) ); + fpath = resolve.apply( null, [ dir ].concat( FILES[ j ] ) ); + exists( fpath, onExists ); } - // A `src/Makefile` is also present in WebAssembly-only packages, so additionally require a `binding.gyp` file, which is what `node-gyp` needs to build a native add-on: - gpath = resolve( dirname( files[ i ] ), 'binding.gyp' ); - debug( 'Checking for `binding.gyp`...' ); - exists( gpath, onExists ); } /** - * Callback invoked upon testing if a `binding.gyp` file exists. + * Callback invoked upon testing if a file exists. * * @private * @param {(Error|null)} error - error object @@ -106,13 +100,23 @@ function inspect( files, clbk ) { */ function onExists( error, bool ) { var j = i + 1; + + counter += 1; + debug( 'Finished checking for add-on file (%d of %d).', counter, FILES.length ); if ( error ) { debug( 'Encountered an error when testing for add-on: %s (%d of %d). Error: %s', files[ i ], j, total, error.message ); } else if ( bool ) { + debug( 'Detected add-on file.' ); + npass += 1; + } else { + debug( 'Unable to detect add-on file.' ); + } + if ( counter < FILES.length ) { + return; + } + if ( npass >= THRESHOLD ) { debug( 'Detected add-on.' ); out.push( dirname( files[ i ] ) ); - } else { - debug( 'Unable to detect add-on.' ); } return done(); } @@ -124,8 +128,7 @@ function inspect( files, clbk ) { * @returns {void} */ function done() { - var j; - j = i + 1; + var j = i + 1; if ( j < total ) { debug( 'Inspected %d of %d packages.', j, total ); return next(); From f5907de931e93721f19bdbf593e1422106d03320 Mon Sep 17 00:00:00 2001 From: Athan Date: Thu, 2 Jul 2026 16:04:57 -0700 Subject: [PATCH 6/8] refactor: add debug statement Signed-off-by: Athan --- lib/node_modules/@stdlib/_tools/pkgs/addons/lib/inspect.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/node_modules/@stdlib/_tools/pkgs/addons/lib/inspect.js b/lib/node_modules/@stdlib/_tools/pkgs/addons/lib/inspect.js index c8ab6609659c..e4e6fcd22e1a 100644 --- a/lib/node_modules/@stdlib/_tools/pkgs/addons/lib/inspect.js +++ b/lib/node_modules/@stdlib/_tools/pkgs/addons/lib/inspect.js @@ -117,6 +117,8 @@ function inspect( files, clbk ) { if ( npass >= THRESHOLD ) { debug( 'Detected add-on.' ); out.push( dirname( files[ i ] ) ); + } else { + debug( 'Unable to detect add-on.' ); } return done(); } From 42ccb96fad3b52422d56e65a0164fd8d42b76391 Mon Sep 17 00:00:00 2001 From: Athan Date: Thu, 2 Jul 2026 16:05:44 -0700 Subject: [PATCH 7/8] Apply suggestions from code review Co-authored-by: Athan Signed-off-by: Athan --- lib/node_modules/@stdlib/_tools/pkgs/addons/lib/inspect.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/node_modules/@stdlib/_tools/pkgs/addons/lib/inspect.js b/lib/node_modules/@stdlib/_tools/pkgs/addons/lib/inspect.js index e4e6fcd22e1a..f17ec9ae4261 100644 --- a/lib/node_modules/@stdlib/_tools/pkgs/addons/lib/inspect.js +++ b/lib/node_modules/@stdlib/_tools/pkgs/addons/lib/inspect.js @@ -48,7 +48,7 @@ var FILES = [ * Inspects packages for add-ons. * * @private -* @param {StringArray} files - list of `package.json` files +* @param {ArrayLikeObject} files - list of `package.json` files * @param {Callback} clbk - callback to invoke upon completion * @returns {void} */ From 5698779cab66eb8bc91dd0a4f3c78d516a0e3304 Mon Sep 17 00:00:00 2001 From: Athan Date: Thu, 2 Jul 2026 16:07:02 -0700 Subject: [PATCH 8/8] Apply suggestions from code review Co-authored-by: Athan Signed-off-by: Athan --- lib/node_modules/@stdlib/_tools/pkgs/addons/lib/inspect.js | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/node_modules/@stdlib/_tools/pkgs/addons/lib/inspect.js b/lib/node_modules/@stdlib/_tools/pkgs/addons/lib/inspect.js index f17ec9ae4261..7e2d9bb79afa 100644 --- a/lib/node_modules/@stdlib/_tools/pkgs/addons/lib/inspect.js +++ b/lib/node_modules/@stdlib/_tools/pkgs/addons/lib/inspect.js @@ -81,7 +81,6 @@ function inspect( files, clbk ) { dir = dirname( files[ i ] ); counter = 0; npass = 0; - debug( 'Checking for add-on...' ); for ( j = 0; j < FILES.length; j++ ) { debug( 'Checking for add-on file: %s', join( FILES[ j ] ) );