From 1d1ff74d5104b1fb3855212737a679c6b4111dd9 Mon Sep 17 00:00:00 2001 From: Samuel Attard Date: Sun, 11 Dec 2016 12:08:17 +1100 Subject: [PATCH] feat(packager): rebuild native modules automatically in all the right places --- package.json | 1 + src/electron-forge-package.js | 5 +-- src/electron-forge-start.js | 5 +++ src/util/rebuild.js | 64 +++++++++++++++++++++++++++++++++++ 4 files changed, 73 insertions(+), 2 deletions(-) create mode 100644 src/util/rebuild.js diff --git a/package.json b/package.json index 936549ba0c..e2213a7621 100644 --- a/package.json +++ b/package.json @@ -68,6 +68,7 @@ "inquirer": "^1.2.1", "log-symbols": "^1.0.2", "mkdirp": "^0.5.1", + "node-gyp": "^3.4.0", "ora": "^0.3.0", "pify": "^2.3.0", "rimraf": "^2.5.4", diff --git a/src/electron-forge-package.js b/src/electron-forge-package.js index b8c6e9503d..98d093cc76 100644 --- a/src/electron-forge-package.js +++ b/src/electron-forge-package.js @@ -10,6 +10,7 @@ import rimraf from 'rimraf'; import './util/terminate'; import getForgeConfig from './util/forge-config'; import packagerCompileHook from './util/compile-hook'; +import rebuildHook from './util/rebuild'; import resolveDir from './util/resolve-dir'; const main = async () => { @@ -60,8 +61,8 @@ const main = async () => { }, async (...args) => { prepareSpinner.succeed(); await packagerCompileHook(dir, ...args); - packagerSpinner = ora.ora('Packaging Application').start(); - }].concat(forgeConfig.electronPackagerConfig.afterCopy ? forgeConfig.electronPackagerConfig.afterCopy.map(item => require(item)) : []), + packagerSpinner = ora.ora('Packaging Application'); + }, rebuildHook].concat(forgeConfig.electronPackagerConfig.afterCopy ? forgeConfig.electronPackagerConfig.afterCopy.map(item => require(item)) : []), afterExtract: forgeConfig.electronPackagerConfig.afterExtract ? forgeConfig.electronPackagerConfig.afterExtract.map(item => require(item)) : [], dir, arch, diff --git a/src/electron-forge-start.js b/src/electron-forge-start.js index b26a5df9c1..d2504c3359 100644 --- a/src/electron-forge-start.js +++ b/src/electron-forge-start.js @@ -4,8 +4,10 @@ import fs from 'fs-promise'; import path from 'path'; import program from 'commander'; import ora from 'ora'; +import pify from 'pify'; import './util/terminate'; +import rebuild from './util/rebuild'; import resolveDir from './util/resolve-dir'; const main = async () => { @@ -33,6 +35,9 @@ const main = async () => { process.exit(1); } + const packageJSON = JSON.parse(await fs.readFile(path.resolve(dir, 'package.json'), 'utf8')); + + await pify(rebuild)(dir, packageJSON.devDependencies['electron-prebuilt-compile'], process.platform, process.arch); spawn(process.execPath, [path.resolve(dir, 'node_modules/.bin/electron'), '.'].concat(process.argv.slice(2)), { cwd: dir, stdio: 'inherit', diff --git a/src/util/rebuild.js b/src/util/rebuild.js new file mode 100644 index 0000000000..e3e5f4bf53 --- /dev/null +++ b/src/util/rebuild.js @@ -0,0 +1,64 @@ +import { spawn } from 'child_process'; +import debug from 'debug'; +import fs from 'fs-promise'; +import mkdirp from 'mkdirp'; +import os from 'os'; +import path from 'path'; +import pify from 'pify'; + +const d = debug('electron-forge:rebuild'); + +export default async (buildPath, electronVersion, pPlatform, pArch, done) => { + const rebuilds = []; + const rebuildModuleAt = async (modulePath) => { + if (await fs.exists(path.resolve(modulePath, 'binding.gyp'))) { + const metaPath = path.resolve(modulePath, 'build', 'Release', '.forge-meta'); + if (await fs.exists(metaPath)) { + const meta = await fs.readFile(metaPath, 'utf8'); + if (meta === pArch) { + d(`skipping: ${path.basename(modulePath)} as it is already built`); + return; + } + } + d('rebuilding:', path.basename(modulePath)); + const rebuildArgs = [ + 'rebuild', + `--target=${electronVersion}`, + `--arch=${pArch}`, + '--dist-url=https://atom.io/download/electron', + ]; + await new Promise((resolve, reject) => { + const child = spawn(process.execPath, [path.resolve(__dirname, '../../node_modules/.bin/node-gyp')].concat(rebuildArgs), { + cwd: modulePath, + // stdio: 'inherit', + env: Object.assign({}, process.env, { + HOME: path.resolve(os.homedir(), '.electron-gyp'), + USERPROFILE: path.resolve(os.homedir(), '.electron-gyp'), + npm_config_disturl: 'https://atom.io/download/electron', + npm_config_runtime: 'electron', + npm_config_arch: pArch, + npm_config_target_arch: pArch, + }), + }); + child.on('exit', async (code) => { + d('built:', path.basename(modulePath)); + if (code !== 0) return reject(new Error(`Failed to rebuild: ${modulePath}`)); + await pify(mkdirp)(path.dirname(metaPath)); + await fs.writeFile(metaPath, pArch); + resolve(); + }); + }); + } + }; + const rebuildAllModulesIn = (nodeModulesPath) => { + for (const modulePath of fs.readdirSync(nodeModulesPath)) { + rebuilds.push(rebuildModuleAt(path.resolve(nodeModulesPath, modulePath))); + if (fs.existsSync(path.resolve(nodeModulesPath, modulePath, 'node_modules'))) { + rebuildAllModulesIn(path.resolve(nodeModulesPath, modulePath, 'node_modules')); + } + } + }; + rebuildAllModulesIn(path.resolve(buildPath, 'node_modules')); + await Promise.all(rebuilds); + done(); +};