run-android-on-specific-device
Summary: At the moment the run-android command from the react-native cli does run all available devices at once. However it would be extreme useful to have the option to choose only one specific device/simulator. Therefore i've created a new flag for the command 'run-android': --deviceIdFromList 'deviceIdFromList' in order to install and launch apps on a specific device/simulator from the command line. 'deviceIdFromList' is the id listed on the output of the command 'adb devices'. I've tested my code with the following commands: react-native run-android --deviceIdFromList "Not existing id" react-native run-android --deviceIdFromList react-native run-android --deviceIdFromList "id of a simulator" react-native run-android --deviceIdFromList "id of a device" Output: ![not-existing-device](https://cloud.githubusercontent.com/assets/9102810/17931086/d843abc8-6a09-11e6-995d-8c737dd5ed5c.png) ![empty-flag](https://cloud.githubusercontent.com/assets/9102810/17931087/d8443930-6a09-11e6-94f3-d Closes https://github.com/facebook/react-native/pull/9568 Differential Revision: D4335133 Pulled By: mkonicek fbshipit-source-id: a827628316be1b5751225851323b1131f451574c
This commit is contained in:
parent
4394419b60
commit
85c8333bc8
|
@ -8,12 +8,13 @@
|
|||
*/
|
||||
'use strict';
|
||||
|
||||
const adb = require('./adb');
|
||||
const chalk = require('chalk');
|
||||
const child_process = require('child_process');
|
||||
const fs = require('fs');
|
||||
const isPackagerRunning = require('../util/isPackagerRunning');
|
||||
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) {
|
||||
|
@ -76,9 +77,98 @@ function tryRunAdbReverse(device) {
|
|||
|
||||
// Builds the app and runs it on a connected emulator / device.
|
||||
function buildAndRun(args) {
|
||||
try {
|
||||
adb.getDevices().map((device) => tryRunAdbReverse(device));
|
||||
process.chdir(path.join(args.root, 'android'));
|
||||
const cmd = process.platform.startsWith('win')
|
||||
? 'gradlew.bat'
|
||||
: './gradlew';
|
||||
|
||||
const packageName = fs.readFileSync(
|
||||
'app/src/main/AndroidManifest.xml',
|
||||
'utf8'
|
||||
).match(/package="(.+?)"/)[1];
|
||||
|
||||
const adbPath = getAdbPath();
|
||||
if (args.deviceId) {
|
||||
runOnSpecificDevice(args, cmd, packageName, adbPath);
|
||||
} else {
|
||||
runOnAllDevices(args, cmd, packageName, adbPath);
|
||||
}
|
||||
}
|
||||
|
||||
function runOnSpecificDevice(args, gradlew, packageName, adbPath) {
|
||||
let devices = adb.getDevices();
|
||||
if (devices && devices.length > 0) {
|
||||
if (devices.indexOf(args.deviceId) !== -1) {
|
||||
buildApk(gradlew);
|
||||
installAndLaunchOnDevice(args, args.deviceId, packageName, adbPath);
|
||||
} else {
|
||||
console.log('Could not find device with the id: "' + args.deviceId + '".');
|
||||
console.log('Choose one of the following:');
|
||||
console.log(devices);
|
||||
}
|
||||
} else {
|
||||
console.log('No Android devices connected.');
|
||||
}
|
||||
}
|
||||
|
||||
function buildApk(gradlew) {
|
||||
try {
|
||||
console.log(chalk.bold(
|
||||
'Building the app...'
|
||||
));
|
||||
|
||||
// using '-x lint' in order to ignore linting errors while building the apk
|
||||
child_process.execFileSync(gradlew, ['build', '-x', 'lint'], {
|
||||
stdio: [process.stdin, process.stdout, process.stderr],
|
||||
});
|
||||
} catch (e) {
|
||||
console.log(chalk.red(
|
||||
'Could not build the app on the device, read the error above for details.\n'
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
function tryInstallAppOnDevice(args, device) {
|
||||
try {
|
||||
const pathToApk = 'app/build/outputs/apk/app-debug.apk';
|
||||
const adbPath = getAdbPath();
|
||||
const adbArgs = ['-s', device, 'install', pathToApk];
|
||||
console.log(chalk.bold(
|
||||
`Installing the app on the device (cd android && adb -s ${device} install ${pathToApk}`
|
||||
));
|
||||
child_process.execFileSync(adbPath, adbArgs, {
|
||||
stdio: [process.stdin, process.stdout, process.stderr],
|
||||
});
|
||||
} catch (e) {
|
||||
console.log(e.message);
|
||||
console.log(chalk.red(
|
||||
'Could not install the app on the device, read the error above for details.\n'
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
function tryLaunchAppOnDevice(device, packageName, adbPath) {
|
||||
try {
|
||||
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'});
|
||||
} catch (e) {
|
||||
console.log(chalk.red(
|
||||
'adb invocation failed. Do you have adb in your PATH?'
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
function installAndLaunchOnDevice(args, selectedDevice, packageName, adbPath) {
|
||||
tryRunAdbReverse(selectedDevice);
|
||||
tryInstallAppOnDevice(args, selectedDevice);
|
||||
tryLaunchAppOnDevice(selectedDevice, packageName, adbPath);
|
||||
}
|
||||
|
||||
function runOnAllDevices(args, cmd, packageName, adbPath){
|
||||
try {
|
||||
const gradleArgs = [];
|
||||
if (args.variant) {
|
||||
gradleArgs.push('install' +
|
||||
|
@ -92,46 +182,15 @@ function buildAndRun(args) {
|
|||
args.flavor[0].toUpperCase() + args.flavor.slice(1)
|
||||
);
|
||||
} else {
|
||||
gradleArgs.push('install');
|
||||
gradleArgs.push('installDebug');
|
||||
}
|
||||
|
||||
// Append the build type to the current gradle install configuration.
|
||||
// By default it will generate `installDebug`.
|
||||
gradleArgs[0] =
|
||||
gradleArgs[0] + args.configuration[0].toUpperCase() + args.configuration.slice(1);
|
||||
|
||||
// Get the Android project directory.
|
||||
const androidProjectDir = path.join(args.root, 'android');
|
||||
|
||||
if (args.configuration.toUpperCase() === 'RELEASE') {
|
||||
console.log(chalk.bold(
|
||||
'Generating the bundle for the release build...'
|
||||
));
|
||||
|
||||
child_process.execSync(
|
||||
'react-native bundle ' +
|
||||
'--platform android ' +
|
||||
'--dev false ' +
|
||||
'--entry-file index.android.js ' +
|
||||
`--bundle-output ${androidProjectDir}/app/src/main/assets/index.android.bundle ` +
|
||||
`--assets-dest ${androidProjectDir}/app/src/main/res/`,
|
||||
{
|
||||
stdio: [process.stdin, process.stdout, process.stderr],
|
||||
}
|
||||
);
|
||||
if (args.installDebug) {
|
||||
gradleArgs.push(args.installDebug);
|
||||
}
|
||||
|
||||
// Change to the Android directory.
|
||||
process.chdir(androidProjectDir);
|
||||
|
||||
// Get the gradle binary for the current platform.
|
||||
const cmd = process.platform.startsWith('win')
|
||||
? 'gradlew.bat'
|
||||
: './gradlew';
|
||||
|
||||
console.log(chalk.bold(
|
||||
'Building and installing the app on the device ' +
|
||||
`(cd android && ${cmd} ${gradleArgs.join(' ')})...`
|
||||
`Building and installing the app on the device (cd android && ${cmd} ${gradleArgs.join(' ')}...`
|
||||
));
|
||||
|
||||
child_process.execFileSync(cmd, gradleArgs, {
|
||||
|
@ -149,50 +208,33 @@ function buildAndRun(args) {
|
|||
// `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 + '/.' + args.mainActivity];
|
||||
|
||||
console.log(chalk.bold(
|
||||
`Starting the app on ${device} (${adbPath} ${adbArgs.join(' ')})...`
|
||||
));
|
||||
|
||||
child_process.spawnSync(adbPath, adbArgs, {stdio: 'inherit'});
|
||||
tryRunAdbReverse(device);
|
||||
tryLaunchAppOnDevice(device, packageName, adbPath);
|
||||
});
|
||||
} 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'});
|
||||
try {
|
||||
// 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();
|
||||
}
|
||||
}
|
||||
|
||||
} 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() {
|
||||
|
@ -206,9 +248,7 @@ function startServerInNewWindow() {
|
|||
|
||||
if (process.platform === 'darwin') {
|
||||
if (yargV.open) {
|
||||
return (
|
||||
child_process.spawnSync('open', ['-a', yargV.open, launchPackagerScript], procConfig)
|
||||
);
|
||||
return child_process.spawnSync('open', ['-a', yargV.open, launchPackagerScript], procConfig);
|
||||
}
|
||||
return child_process.spawnSync('open', [launchPackagerScript], procConfig);
|
||||
|
||||
|
@ -233,27 +273,19 @@ module.exports = {
|
|||
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)',
|
||||
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: '--configuration [string]',
|
||||
description:
|
||||
'You can use `Release` or `Debug`. ' +
|
||||
'This creates a build based on the selected configuration. ' +
|
||||
'If you want to use the `Release` configuration make sure you have the ' +
|
||||
'`signingConfig` configured at `app/build.gradle`.',
|
||||
default: 'Debug'
|
||||
}, {
|
||||
command: '--variant [string]',
|
||||
}, {
|
||||
command: '--main-activity [string]',
|
||||
description: 'Name of the activity to start',
|
||||
default: 'MainActivity'
|
||||
command: '--deviceId [string]',
|
||||
description: 'builds your app and starts it on a specific device/simulator with the ' +
|
||||
'given device id (listed by running "adb devices" on the command line).',
|
||||
}],
|
||||
};
|
||||
};
|
Loading…
Reference in New Issue