#!/usr/bin/env node var fs = require('fs'); var verifyRequirements = require('./utils/verify-requirements'); var safeExec = require('./utils/child-process-wrapper.js').safeExec; var path = require('path'); var userHomeDirectory = (process.platform === 'win32') ? process.env.USERPROFILE : process.env.HOME; var appPackageJSON = JSON.parse(fs.readFileSync(path.resolve(__dirname, '..', 'package.json'))); var targetElectronVersion = appPackageJSON['electronVersion']; var targetPlatform = require('os').platform(); // Executes an array of commands in series function executeCommands(commands, done, index) { index = (index == undefined ? 0 : index); if (index < commands.length) { var command = commands[index]; if (command.message) console.log(command.message); var options = null; if (typeof command !== 'string') { options = command.options; command = command.command; } safeExec(command, options, executeCommands.bind(this, commands, done, index + 1)); } else done(null); } function printArgs(args) { out = ""; for(key in args) { out += "--"+key+"="+args[key]+" "; } return out; } function makeSqlite3Command() { var nodeGypPath = '"' + path.resolve(__dirname, '..', 'build', 'node_modules', 'npm', 'node_modules', '.bin', 'node-gyp') + '"'; if (targetPlatform == "win32") { // As of Electron 0.29.2, all windows machines, even if they're 64 bit, // return "ia32" as the arch var targetArch = "ia32" } else { var targetArch = require('os').arch(); } // Use our local version of npm (npm 3x) to build sqlite var npmPath = '"' + path.resolve(__dirname, '..', 'build', 'node_modules', '.bin', 'npm') + '"'; return npmPath + " install https://github.com/bengotow/node-sqlite3/archive/bengotow/fts5.tar.gz --ignore-scripts && cd node_modules/sqlite3 && "+nodeGypPath+" configure rebuild --target="+targetElectronVersion+" --arch="+targetArch+" --target_platform="+targetPlatform+" --dist-url=https://atom.io/download/atom-shell --module_name=node_sqlite3 --module_path=../lib/binding/electron-v0.36-"+targetPlatform+"-"+targetArch } function bootstrap() { var apmDestinationPath = path.resolve(__dirname, '..', 'apm'); if (!fs.existsSync(apmDestinationPath)) fs.mkdirSync(apmDestinationPath); if (!fs.existsSync(path.join(apmDestinationPath, 'node_modules'))) fs.mkdirSync(path.join(apmDestinationPath, 'node_modules')); var apmPath = '"' + path.resolve(__dirname, '..', 'apm', 'node_modules', 'atom-package-manager', 'bin', 'apm') + '"'; var apmFlags = process.env.JANKY_SHA1 || process.argv.indexOf('--no-color') !== -1 ? ' --no-color' : ''; var npmFlags = ' --userconfig=' + '"' + path.resolve('.npmrc') + '" '; var gruntPath = '"' + path.resolve(__dirname, '..', 'build', 'node_modules', '.bin', 'grunt') + '"'; var packagesToDedupe = ['fs-plus', 'humanize-plus', 'roaster', 'season', 'grim']; // use the system version of npm to install build folder dependencies, including our // own copy of NPM 3.x, which we'll use for subsequent commands var buildInstallCommand = 'npm' + npmFlags + 'install'; var buildInstallOptions = {cwd: path.resolve(__dirname, '..', 'build')}; var npmPath = '"' + path.resolve(__dirname, '..', 'build', 'node_modules', '.bin', 'npm') + '"'; // Use our local version of npm in ./build to install apm. This ensures it gets a // flat dependency tree. var npmInstallApmCommand = npmPath + npmFlags + '--target=0.10.40 ' + 'install'; var npmInstallApmOptions = {cwd: apmDestinationPath}; var npmDedupeNpmCommand = npmPath + npmFlags + '--target=0.10.40 ' + 'dedupe'; var npmDedupeNpmOptions = {cwd: path.join(apmDestinationPath, 'node_modules', 'npm')}; var sqlite3Command = makeSqlite3Command(); var apmInstallCommand = apmPath + ' install' + apmFlags; var apmEnv = JSON.parse(JSON.stringify(process.env)) apmEnv['ATOM_ELECTRON_VERSION'] = targetElectronVersion; apmEnv['ATOM_HOME'] = path.join(userHomeDirectory, '.nylas'); var apmDedupeCommand = apmPath + ' dedupe' + apmFlags; var semverOptions = { cwd: path.resolve(__dirname, '..', 'apm', 'node_modules', 'atom-package-manager'), env: apmEnv }; var integrationCommand = npmPath + npmFlags + 'install'; var integrationOptions = {cwd: path.resolve(__dirname, '..', 'spec_integration')}; if (process.argv.indexOf('--no-quiet') === -1) { buildInstallCommand += ' --loglevel error'; npmInstallApmCommand += ' --loglevel error'; apmInstallCommand += ' --loglevel error'; integrationCommand += ' --loglevel error'; apmDedupeCommand += ' --quiet'; npmDedupeNpmOptions.ignoreStderr = true; npmDedupeNpmOptions.ignoreStdout = true; buildInstallOptions.ignoreStdout = true; npmInstallApmOptions.ignoreStdout = true; integrationOptions.ignoreStdout = true; } // apm ships with 32-bit node so make sure its native modules are compiled // for a 32-bit target architecture if (process.env.JANKY_SHA1 && process.platform === 'win32') npmInstallApmCommand += ' --arch=ia32'; m1 = "\n---> Installing N1 build tools\n" m1 += " This goes inside the `build` folder and runs `npm install`\n" m1 += " It will use the system `npm` to bootstrap our own N1 npm.\n" m1 += " Our build tools (like Grunt) need to be compiled against Node via `npm`.\n" m1 += " Everything else needs to be compiled against Chromium with `apm`.\n\n" m1 += " $ "+buildInstallCommand+" "+printArgs(buildInstallOptions)+"\n" m2 = "\n\n---> Installing apm\n" m2 += " This installs apm via N1's `npm`\n" m2 += " We use this local apm copy to install all N1 dependencies & packages\n\n" m2 += " $ "+npmInstallApmCommand+" "+printArgs(npmInstallApmOptions)+"\n" m2a = "\n\n---> Flattening apm package tree\n" m2a += " This runs `npm dedupe` on apm's `npm` dependency.\n" m2a += " We use this to prevent paths over 260 characters on Windows.\n\n" m2a += " $ "+npmDedupeNpmCommand+" "+printArgs(npmDedupeNpmOptions)+"\n" m3 = "\n\n---> Cleaning apm via `apm clean`\n" m4 = "\n\n---> Installing N1 dependencies & packages via `apm install`\n\n" m4 += " $ "+apmInstallCommand+"\n" m5 = "\n\n---> De-duping packages `apm dedupe`\n\n" m5 += " $ "+apmDedupeCommand+" "+packagesToDedupe.join(' ')+"\n" m6 = "\n\n---> Request version `apm dedupe`\n\n" m6 += " $ "+apmDedupeCommand+" request semver "+printArgs(semverOptions)+"\n" m7 = "\n\n---> Getting latest Electron\n\n" var gruntCmd = "" var downloadElectronCmd = gruntPath + " download-electron --gruntfile build/Gruntfile.coffee" m7 += " $ "+downloadElectronCmd m8 = "\n\n---> Installing integration test modules\n\n" m8 += " $ "+integrationCommand+" "+printArgs(integrationOptions)+"\n" var commands = [ { command: buildInstallCommand, message: m1, options: buildInstallOptions }, { command: gruntPath + " add-nylas-build-resources --gruntfile build/Gruntfile.coffee", message: "", options: {} } ] process.chdir(path.dirname(__dirname)); executeCommands(commands, function() { var commands = [ { command: npmInstallApmCommand, message: m2, options: npmInstallApmOptions }, { command: npmDedupeNpmCommand, message: m2a, options: npmDedupeNpmOptions }, { command: apmPath + ' clean' + apmFlags, options: { env: apmEnv }, message: m3 } ]; // we need this because we don't put our modules in node_modules and npm // install doesn't find them. Run APM install on each package directory manually. [ path.resolve(__dirname, '..', 'internal_packages') ].forEach(function(packagesDir) { fs.readdirSync(packagesDir).forEach(function(dir) { var dirPackageJSONPath = path.join(packagesDir, dir, 'package.json'); // On windows and linux, invoking the apm command is very slow even when there are no // dependencies. Make it faster by not calling unless we find there are deps. if (fs.existsSync(dirPackageJSONPath)) { var dirPackageJSON = JSON.parse(fs.readFileSync(dirPackageJSONPath)); if (dirPackageJSON.dependencies && (Object.keys(dirPackageJSON.dependencies).length > 0)) { commands.push({ command: apmInstallCommand, message: "Installing dependencies for "+dir, options: { cwd: path.join(packagesDir, dir), env: apmEnv } }); } } }); }); commands = commands.concat([ { command: apmInstallCommand, options: { env: apmEnv }, message: m4 }, { command: apmDedupeCommand + ' ' + packagesToDedupe.join(' '), options: { env: apmEnv }, message: m5 }, { command: apmDedupeCommand + ' request semver', options: semverOptions, message: m6 }, { command: downloadElectronCmd, message: m7 }, { command: integrationCommand, options: integrationOptions, message: m8 }, { command: sqlite3Command, message: "Building sqlite3 with command: "+sqlite3Command } ]); process.chdir(path.dirname(__dirname)); executeCommands(commands, function() { console.log("---------------------------------------------"); console.log("script/bootstrap completed successfully. You can start\nN1 via ./N1.sh --dev, or run tests with ./N1.sh --test"); process.exit(); }); }); } verifyRequirements(function(error, successMessage) { if (error) { console.log(error); process.exit(1); } console.log(successMessage); bootstrap(); });