221 lines
6.8 KiB
JavaScript
221 lines
6.8 KiB
JavaScript
/**
|
|
* 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 isPackagerRunning = require('../util/isPackagerRunning');
|
|
const Promise = require('promise');
|
|
const adb = require('./adb');
|
|
|
|
// Verifies this is an Android project
|
|
function checkAndroid(root) {
|
|
return fs.existsSync(path.join(root, 'android/gradlew'));
|
|
}
|
|
|
|
/**
|
|
* Starts the app on a connected Android emulator or device.
|
|
*/
|
|
function runAndroid(argv, config, args) {
|
|
if (!checkAndroid(args.root)) {
|
|
console.log(chalk.red('Android project not found. Maybe run react-native android first?'));
|
|
return;
|
|
}
|
|
|
|
return 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();
|
|
}
|
|
return buildAndRun(args);
|
|
});
|
|
}
|
|
|
|
function getAdbPath() {
|
|
return process.env.ANDROID_HOME
|
|
? process.env.ANDROID_HOME + '/platform-tools/adb'
|
|
: 'adb';
|
|
}
|
|
|
|
// Runs ADB reverse tcp:8081 tcp:8081 to allow loading the jsbundle from the packager
|
|
function tryRunAdbReverse(device) {
|
|
try {
|
|
const adbPath = getAdbPath();
|
|
const adbArgs = ['reverse', 'tcp:8081', 'tcp:8081'];
|
|
|
|
// If a device is specified then tell adb to use it
|
|
if (device) {
|
|
adbArgs.unshift('-s', device);
|
|
}
|
|
|
|
console.log(chalk.bold(
|
|
`Running ${adbPath} ${adbArgs.join(' ')}`
|
|
));
|
|
|
|
child_process.execFileSync(adbPath, adbArgs, {
|
|
stdio: [process.stdin, process.stdout, process.stderr],
|
|
});
|
|
} catch (e) {
|
|
console.log(chalk.yellow(
|
|
`Could not run adb reverse: ${e.message}`
|
|
));
|
|
}
|
|
}
|
|
|
|
// Builds the app and runs it on a connected emulator / device.
|
|
function buildAndRun(args) {
|
|
process.chdir(path.join(args.root, 'android'));
|
|
try {
|
|
adb.getDevices().map((device) => tryRunAdbReverse(device));
|
|
|
|
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.installDebug) {
|
|
gradleArgs.push(args.installDebug);
|
|
}
|
|
|
|
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' +
|
|
'Go to https://facebook.github.io/react-native/docs/getting-started.html\n' +
|
|
'and check the Android tab for setup instructions.'
|
|
));
|
|
// 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)`
|
|
return Promise.reject();
|
|
}
|
|
|
|
try {
|
|
const packageName = fs.readFileSync(
|
|
'app/src/main/AndroidManifest.xml',
|
|
'utf8'
|
|
).match(/package="(.+?)"/)[1];
|
|
|
|
const adbPath = getAdbPath();
|
|
|
|
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)`
|
|
return Promise.reject();
|
|
}
|
|
}
|
|
|
|
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 = {
|
|
name: 'run-android',
|
|
description: 'builds your app and starts it on a connected Android emulator or device',
|
|
func: runAndroid,
|
|
options: [{
|
|
command: '--install-debug',
|
|
}, {
|
|
command: '--root [string]',
|
|
description: 'Override the root directory for the android build (which contains the android directory)',
|
|
default: '',
|
|
}, {
|
|
command: '--flavor [string]',
|
|
description: '--flavor has been deprecated. Use --variant instead',
|
|
}, {
|
|
command: '--variant [string]',
|
|
}],
|
|
};
|