/** * Copyright (c) 2015-present, Facebook, Inc. * All rights reserved. * * This source code is licensed under the BSD-style license found in the * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. */ 'use strict'; const chalk = require('chalk'); const child_process = require('child_process'); const fs = require('fs'); const path = require('path'); const parseCommandLine = require('../util/parseCommandLine'); const isPackagerRunning = require('../util/isPackagerRunning'); const Promise = require('promise'); const adb = require('./adb'); /** * Starts the app on a connected Android emulator or device. */ function runAndroid(argv, config) { return new Promise((resolve, reject) => { _runAndroid(argv, config, resolve, reject); }); } function _runAndroid(argv, config, resolve, reject) { const args = parseCommandLine([{ command: 'install-debug', type: 'string', required: false, }, { command: 'root', type: 'string', description: 'Override the root directory for the android build (which contains the android directory)', }, { command: 'flavor', type: 'string', required: false, }, { command: 'variant', type: 'string', required: false, }], argv); args.root = args.root || ''; if (!checkAndroid(args)) { console.log(chalk.red('Android project not found. Maybe run react-native android first?')); return; } resolve(isPackagerRunning().then(result => { if (result === 'running') { console.log(chalk.bold(`JS server already running.`)); } else if (result === 'unrecognized') { console.warn(chalk.yellow(`JS server not recognized, continuing with build...`)); } else { // result == 'not_running' console.log(chalk.bold(`Starting JS server...`)); startServerInNewWindow(); } buildAndRun(args, reject); })); } // Verifies this is an Android project function checkAndroid(args) { return fs.existsSync(path.join(args.root, 'android/gradlew')); } // Builds the app and runs it on a connected emulator / device. function buildAndRun(args, reject) { process.chdir(path.join(args.root, 'android')); try { const cmd = process.platform.startsWith('win') ? 'gradlew.bat' : './gradlew'; const gradleArgs = []; if (args['variant']) { gradleArgs.push('install' + args['variant'][0].toUpperCase() + args['variant'].slice(1) ); } else if (args['flavor']) { console.warn(chalk.yellow( `--flavor has been deprecated. Use --variant instead` )); gradleArgs.push('install' + args['flavor'][0].toUpperCase() + args['flavor'].slice(1) ); } else { gradleArgs.push('installDebug'); } if (args['install-debug']) { gradleArgs.push(args['install-debug']); } console.log(chalk.bold( `Building and installing the app on the device (cd android && ${cmd} ${gradleArgs.join(' ')}...` )); child_process.execFileSync(cmd, gradleArgs, { stdio: [process.stdin, process.stdout, process.stderr], }); } catch (e) { console.log(chalk.red( 'Could not install the app on the device, read the error above for details.\n' + 'Make sure you have an Android emulator running or a device connected and have\n' + 'set up your Android development environment:\n' + 'https://facebook.github.io/react-native/docs/android-setup.html' )); // stderr is automatically piped from the gradle process, so the user // should see the error already, there is no need to do // `console.log(e.stderr)` reject(); return; } try { const packageName = fs.readFileSync( 'app/src/main/AndroidManifest.xml', 'utf8' ).match(/package="(.+?)"/)[1]; const adbPath = process.env.ANDROID_HOME ? process.env.ANDROID_HOME + '/platform-tools/adb' : 'adb'; const devices = adb.getDevices(); if (devices && devices.length > 0) { devices.forEach((device) => { const adbArgs = ['-s', device, 'shell', 'am', 'start', '-n', packageName + '/.MainActivity']; console.log(chalk.bold( `Starting the app on ${device} (${adbPath} ${adbArgs.join(' ')})...` )); child_process.spawnSync(adbPath, adbArgs, {stdio: 'inherit'}); }); } else { // If we cannot execute based on adb devices output, fall back to // shell am start const fallbackAdbArgs = [ 'shell', 'am', 'start', '-n', packageName + '/.MainActivity' ]; console.log(chalk.bold( `Starting the app (${adbPath} ${fallbackAdbArgs.join(' ')}...` )); child_process.spawnSync(adbPath, fallbackAdbArgs, {stdio: 'inherit'}); } } catch (e) { console.log(chalk.red( `adb invocation failed. Do you have adb in your PATH?` )); // stderr is automatically piped from the gradle process, so the user // should see the error already, there is no need to do // `console.log(e.stderr)` reject(); return; } } function startServerInNewWindow() { const yargV = require('yargs').argv; const scriptFile = /^win/.test(process.platform) ? 'launchPackager.bat' : 'launchPackager.command'; const packagerDir = path.resolve(__dirname, '..', '..', 'packager'); const launchPackagerScript = path.resolve(packagerDir, scriptFile); const procConfig = {cwd: packagerDir}; if (process.platform === 'darwin') { if (yargV.open) { return child_process.spawnSync('open', ['-a', yargV.open, launchPackagerScript], procConfig); } return child_process.spawnSync('open', [launchPackagerScript], procConfig); } else if (process.platform === 'linux') { procConfig.detached = true; if (yargV.open){ return child_process.spawn(yargV.open,['-e', 'sh', launchPackagerScript], procConfig); } return child_process.spawn('sh', [launchPackagerScript], procConfig); } else if (/^win/.test(process.platform)) { procConfig.detached = true; procConfig.stdio = 'ignore'; return child_process.spawn('cmd.exe', ['/C', 'start', launchPackagerScript], procConfig); } else { console.log(chalk.red(`Cannot start the packager. Unknown platform ${process.platform}`)); } } module.exports = runAndroid;