diff --git a/bin.js b/bin.js index c778e0ab9fc691b429f5c46c56a801cf28ab3fcd..c1a3bc682cc3fe365c66b74585c5e0606437a873 100644 --- a/bin.js +++ b/bin.js @@ -3,6 +3,7 @@ var proc = require('child_process') var os = require('os') var path = require('path') +var isMingw = os.type().startsWith('MINGW32_NT') if (!buildFromSource()) { proc.exec('node-gyp-build-test', function (err, stdout, stderr) { @@ -21,11 +22,13 @@ function build () { var args = [win32 ? 'node-gyp.cmd' : 'node-gyp', 'rebuild'] try { - var pkg = require('node-gyp/package.json') + var pkg = isMingw ? require(path.join(path.dirname(process.execPath), '../lib/node_modules/npm/node_modules/node-gyp/package.json')) : require('node-gyp/package.json') args = [ process.execPath, - path.join(require.resolve('node-gyp/package.json'), '..', typeof pkg.bin === 'string' ? pkg.bin : pkg.bin['node-gyp']), - 'rebuild' + path.join(isMingw ? path.join(path.dirname(process.execPath), '../lib/node_modules/npm/node_modules/node-gyp') : path.join(require.resolve('node-gyp/package.json'), '..'), typeof pkg.bin === 'string' ? pkg.bin : pkg.bin['node-gyp']), + 'rebuild', + '-j', + 'max' ] shell = false } catch (_) {} diff --git a/node-gyp-build.js b/node-gyp-build.js index 76b96e107474cdd79394262b65ac286db36353e8..2770b858af8af0d622688c7e6854f13b346d1350 100644 --- a/node-gyp-build.js +++ b/node-gyp-build.js @@ -1,6 +1,7 @@ var fs = require('fs') var path = require('path') var os = require('os') +var {execSync, spawnSync } = require('child_process'); // Workaround to fix webpack's build warnings: 'the request of a dependency is an expression' var runtimeRequire = typeof __webpack_require__ === 'function' ? __non_webpack_require__ : require // eslint-disable-line @@ -12,14 +13,108 @@ var runtime = isElectron() ? 'electron' : (isNwjs() ? 'node-webkit' : 'node') var arch = process.env.npm_config_arch || os.arch() var platform = process.env.npm_config_platform || os.platform() -var libc = process.env.LIBC || (isAlpine(platform) ? 'musl' : 'glibc') +// var libc = process.env.LIBC || (isAlpine(platform) ? 'musl' : 'glibc') +var libc = process.env.LIBC || (isAlpine(platform) ? 'musl' : (os.type().startsWith('Windows_NT') ? '' : 'glibc')) var armv = process.env.ARM_VERSION || (arch === 'arm64' ? '8' : vars.arm_version) || '' var uv = (process.versions.uv || '').split('.')[0] +var isMingw = os.type().startsWith('MINGW32_NT') module.exports = load function load (dir) { - return runtimeRequire(load.resolve(dir)) + var resolved + try { + resolved = load.resolve(dir) + } catch (err) { + return rebuildTest(dir) + } + + // 先用探针测试是否能安全加载 + if (canLoadNativeModule(resolved)) { + return runtimeRequire(resolved); + } + + return rebuildTest(dir) +} + +function rebuildTest(dir) { + var resolved + + // 触发 rebuild + try { + var binPath = path.join(__dirname, 'bin.js'); + execSync(`node "${binPath}"`, { + stdio: 'inherit', + cwd: path.resolve(dir), + shell: os.platform() === 'win32' ? true : undefined + }); + + var filename = findBuild(path.join(dir, 'build', 'Release')) + // var buildFolder = path.join(dir, 'prebuilds', platform + '-' + arch + ((typeof libc === 'string' && rc.libc.trim() !== '') ? ('-' + libc) : '')) + var buildFolder = path.join(dir, 'prebuilds', platform + '-' + arch) + fs.mkdirSync(buildFolder, {recursive: true}) + var name = prebuildName(path.parse(filename).name) + var dest = path.join(buildFolder, name) + + fs.renameSync(filename, dest) + + // 重建后再次 resolve 并 probe + resolved = load.resolve(dir) + if (!canLoadNativeModule(resolved)) { + throw new Error(`Rebuild succeeded but module still fails to load: ${resolved}`) + } + + return runtimeRequire(resolved) + } catch (err) { + throw new Error(`Failed to rebuild native module: ${err.message}`) + } +} + +function canLoadNativeModule(nodePath) { + var code = ` + try { + require(${JSON.stringify(nodePath)}) + console.log('OK') + process.exit(0) + } catch (e) { + process.exit(1) + } + `; + var res = spawnSync(process.execPath, ['-e', code], { timeout: 3000 }); + return res.status === 0; +} + +function prebuildName (filename) { + var tags = [filename] + + if (abi) { + tags.push('abi' + abi) + } + + if (uv) { + tags.push('uv' + uv) + } + + if (armv) { + tags.push('armv' + armv) + } + + if (libc) { + tags.push(libc) + } + + return tags.join('.') + '.node' +} + +function findBuild(dir) { + const files = fs.readdirSync(dir); + const nodeFiles = files.filter(name => /\.node$/i.test(name)); + + if (nodeFiles.length === 0) { + throw new Error('Could not find build'); + } + + return path.join(dir, nodeFiles[0]); } load.resolve = load.path = function (dir) { @@ -30,6 +125,9 @@ load.resolve = load.path = function (dir) { if (process.env[name + '_PREBUILD']) dir = process.env[name + '_PREBUILD'] } catch (err) {} + var prebuild = resolve(dir) + if (prebuild) return prebuild + if (!prebuildsOnly) { var release = getFirst(path.join(dir, 'build/Release'), matchBuild) if (release) return release @@ -38,9 +136,6 @@ load.resolve = load.path = function (dir) { if (debug) return debug } - var prebuild = resolve(dir) - if (prebuild) return prebuild - var nearby = resolve(path.dirname(process.execPath)) if (nearby) return nearby @@ -158,7 +253,7 @@ function matchTags (runtime, abi) { if (tags.abi && tags.abi !== abi && !tags.napi) return false if (tags.uv && tags.uv !== uv) return false if (tags.armv && tags.armv !== armv) return false - if (tags.libc && tags.libc !== libc) return false + if (tags.libc && (tags.libc !== libc || (platform === 'win32' && !isMingw))) return false return true }