88 */
99'use strict' ;
1010
11- const adb = require ( './adb' ) ;
1211const chalk = require ( 'chalk' ) ;
1312const child_process = require ( 'child_process' ) ;
1413const fs = require ( 'fs' ) ;
15- const isPackagerRunning = require ( '../util/isPackagerRunning' ) ;
1614const path = require ( 'path' ) ;
15+ const isPackagerRunning = require ( '../util/isPackagerRunning' ) ;
16+ const Promise = require ( 'promise' ) ;
17+ const adb = require ( './adb' ) ;
1718
1819// Verifies this is an Android project
1920function checkAndroid ( root ) {
@@ -76,9 +77,98 @@ function tryRunAdbReverse(device) {
7677
7778// Builds the app and runs it on a connected emulator / device.
7879function buildAndRun ( args ) {
80+ process . chdir ( path . join ( args . root , 'android' ) ) ;
81+ const cmd = process . platform . startsWith ( 'win' )
82+ ? 'gradlew.bat'
83+ : './gradlew' ;
84+
85+ const packageName = fs . readFileSync (
86+ 'app/src/main/AndroidManifest.xml' ,
87+ 'utf8'
88+ ) . match ( / p a c k a g e = " ( .+ ?) " / ) [ 1 ] ;
89+
90+ const adbPath = getAdbPath ( ) ;
91+ if ( args . deviceId ) {
92+ runOnSpecificDevice ( args , cmd , packageName , adbPath ) ;
93+ } else {
94+ runOnAllDevices ( args , cmd , packageName , adbPath ) ;
95+ }
96+ }
97+
98+ function runOnSpecificDevice ( args , gradlew , packageName , adbPath ) {
99+ let devices = adb . getDevices ( ) ;
100+ if ( devices && devices . length > 0 ) {
101+ if ( devices . indexOf ( args . deviceId ) !== - 1 ) {
102+ buildApk ( gradlew ) ;
103+ installAndLaunchOnDevice ( args , args . deviceId , packageName , adbPath ) ;
104+ } else {
105+ console . log ( 'Could not find device with the id: "' + args . deviceId + '".' ) ;
106+ console . log ( 'Choose one of the following:' ) ;
107+ console . log ( devices ) ;
108+ }
109+ } else {
110+ console . log ( 'No Android devices connected.' ) ;
111+ }
112+ }
113+
114+ function buildApk ( gradlew ) {
79115 try {
80- adb . getDevices ( ) . map ( ( device ) => tryRunAdbReverse ( device ) ) ;
116+ console . log ( chalk . bold (
117+ 'Building the app...'
118+ ) ) ;
81119
120+ // using '-x lint' in order to ignore linting errors while building the apk
121+ child_process . execFileSync ( gradlew , [ 'build' , '-x' , 'lint' ] , {
122+ stdio : [ process . stdin , process . stdout , process . stderr ] ,
123+ } ) ;
124+ } catch ( e ) {
125+ console . log ( chalk . red (
126+ 'Could not build the app on the device, read the error above for details.\n'
127+ ) ) ;
128+ }
129+ }
130+
131+ function tryInstallAppOnDevice ( args , device ) {
132+ try {
133+ const pathToApk = 'app/build/outputs/apk/app-debug.apk' ;
134+ const adbPath = getAdbPath ( ) ;
135+ const adbArgs = [ '-s' , device , 'install' , pathToApk ] ;
136+ console . log ( chalk . bold (
137+ `Installing the app on the device (cd android && adb -s ${ device } install ${ pathToApk } `
138+ ) ) ;
139+ child_process . execFileSync ( adbPath , adbArgs , {
140+ stdio : [ process . stdin , process . stdout , process . stderr ] ,
141+ } ) ;
142+ } catch ( e ) {
143+ console . log ( e . message ) ;
144+ console . log ( chalk . red (
145+ 'Could not install the app on the device, read the error above for details.\n'
146+ ) ) ;
147+ }
148+ }
149+
150+ function tryLaunchAppOnDevice ( device , packageName , adbPath ) {
151+ try {
152+ const adbArgs = [ '-s' , device , 'shell' , 'am' , 'start' , '-n' , packageName + '/.MainActivity' ] ;
153+ console . log ( chalk . bold (
154+ `Starting the app on ${ device } (${ adbPath } ${ adbArgs . join ( ' ' ) } )...`
155+ ) ) ;
156+ child_process . spawnSync ( adbPath , adbArgs , { stdio : 'inherit' } ) ;
157+ } catch ( e ) {
158+ console . log ( chalk . red (
159+ 'adb invocation failed. Do you have adb in your PATH?'
160+ ) ) ;
161+ }
162+ }
163+
164+ function installAndLaunchOnDevice ( args , selectedDevice , packageName , adbPath ) {
165+ tryRunAdbReverse ( selectedDevice ) ;
166+ tryInstallAppOnDevice ( args , selectedDevice ) ;
167+ tryLaunchAppOnDevice ( selectedDevice , packageName , adbPath ) ;
168+ }
169+
170+ function runOnAllDevices ( args , cmd , packageName , adbPath ) {
171+ try {
82172 const gradleArgs = [ ] ;
83173 if ( args . variant ) {
84174 gradleArgs . push ( 'install' +
@@ -92,46 +182,15 @@ function buildAndRun(args) {
92182 args . flavor [ 0 ] . toUpperCase ( ) + args . flavor . slice ( 1 )
93183 ) ;
94184 } else {
95- gradleArgs . push ( 'install ' ) ;
185+ gradleArgs . push ( 'installDebug ' ) ;
96186 }
97187
98- // Append the build type to the current gradle install configuration.
99- // By default it will generate `installDebug`.
100- gradleArgs [ 0 ] =
101- gradleArgs [ 0 ] + args . configuration [ 0 ] . toUpperCase ( ) + args . configuration . slice ( 1 ) ;
102-
103- // Get the Android project directory.
104- const androidProjectDir = path . join ( args . root , 'android' ) ;
105-
106- if ( args . configuration . toUpperCase ( ) === 'RELEASE' ) {
107- console . log ( chalk . bold (
108- 'Generating the bundle for the release build...'
109- ) ) ;
110-
111- child_process . execSync (
112- 'react-native bundle ' +
113- '--platform android ' +
114- '--dev false ' +
115- '--entry-file index.android.js ' +
116- `--bundle-output ${ androidProjectDir } /app/src/main/assets/index.android.bundle ` +
117- `--assets-dest ${ androidProjectDir } /app/src/main/res/` ,
118- {
119- stdio : [ process . stdin , process . stdout , process . stderr ] ,
120- }
121- ) ;
188+ if ( args . installDebug ) {
189+ gradleArgs . push ( args . installDebug ) ;
122190 }
123191
124- // Change to the Android directory.
125- process . chdir ( androidProjectDir ) ;
126-
127- // Get the gradle binary for the current platform.
128- const cmd = process . platform . startsWith ( 'win' )
129- ? 'gradlew.bat'
130- : './gradlew' ;
131-
132192 console . log ( chalk . bold (
133- 'Building and installing the app on the device ' +
134- `(cd android && ${ cmd } ${ gradleArgs . join ( ' ' ) } )...`
193+ `Building and installing the app on the device (cd android && ${ cmd } ${ gradleArgs . join ( ' ' ) } ...`
135194 ) ) ;
136195
137196 child_process . execFileSync ( cmd , gradleArgs , {
@@ -149,50 +208,33 @@ function buildAndRun(args) {
149208 // `console.log(e.stderr)`
150209 return Promise . reject ( ) ;
151210 }
152-
153- try {
154- const packageName = fs . readFileSync (
155- 'app/src/main/AndroidManifest.xml' ,
156- 'utf8'
157- ) . match ( / p a c k a g e = " ( .+ ?) " / ) [ 1 ] ;
158-
159- const adbPath = getAdbPath ( ) ;
160-
161211 const devices = adb . getDevices ( ) ;
162-
163212 if ( devices && devices . length > 0 ) {
164213 devices . forEach ( ( device ) => {
165-
166- const adbArgs =
167- [ '-s' , device , 'shell' , 'am' , 'start' , '-n' , packageName + '/.' + args . mainActivity ] ;
168-
169- console . log ( chalk . bold (
170- `Starting the app on ${ device } (${ adbPath } ${ adbArgs . join ( ' ' ) } )...`
171- ) ) ;
172-
173- child_process . spawnSync ( adbPath , adbArgs , { stdio : 'inherit' } ) ;
214+ tryRunAdbReverse ( device ) ;
215+ tryLaunchAppOnDevice ( device , packageName , adbPath ) ;
174216 } ) ;
175217 } else {
176- // If we cannot execute based on adb devices output, fall back to
177- // shell am start
178- const fallbackAdbArgs = [
179- 'shell' , 'am' , 'start' , '-n' , packageName + '/.MainActivity'
180- ] ;
181- console . log ( chalk . bold (
182- `Starting the app (${ adbPath } ${ fallbackAdbArgs . join ( ' ' ) } ...`
183- ) ) ;
184- child_process . spawnSync ( adbPath , fallbackAdbArgs , { stdio : 'inherit' } ) ;
218+ try {
219+ // If we cannot execute based on adb devices output, fall back to
220+ // shell am start
221+ const fallbackAdbArgs = [
222+ 'shell' , 'am' , 'start' , '-n' , packageName + '/.MainActivity'
223+ ] ;
224+ console . log ( chalk . bold (
225+ `Starting the app (${ adbPath } ${ fallbackAdbArgs . join ( ' ' ) } ...`
226+ ) ) ;
227+ child_process . spawnSync ( adbPath , fallbackAdbArgs , { stdio : 'inherit' } ) ;
228+ } catch ( e ) {
229+ console . log ( chalk . red (
230+ 'adb invocation failed. Do you have adb in your PATH?'
231+ ) ) ;
232+ // stderr is automatically piped from the gradle process, so the user
233+ // should see the error already, there is no need to do
234+ // `console.log(e.stderr)`
235+ return Promise . reject ( ) ;
236+ }
185237 }
186-
187- } catch ( e ) {
188- console . log ( chalk . red (
189- 'adb invocation failed. Do you have adb in your PATH?'
190- ) ) ;
191- // stderr is automatically piped from the gradle process, so the user
192- // should see the error already, there is no need to do
193- // `console.log(e.stderr)`
194- return Promise . reject ( ) ;
195- }
196238}
197239
198240function startServerInNewWindow ( ) {
@@ -206,9 +248,7 @@ function startServerInNewWindow() {
206248
207249 if ( process . platform === 'darwin' ) {
208250 if ( yargV . open ) {
209- return (
210- child_process . spawnSync ( 'open' , [ '-a' , yargV . open , launchPackagerScript ] , procConfig )
211- ) ;
251+ return child_process . spawnSync ( 'open' , [ '-a' , yargV . open , launchPackagerScript ] , procConfig ) ;
212252 }
213253 return child_process . spawnSync ( 'open' , [ launchPackagerScript ] , procConfig ) ;
214254
@@ -233,27 +273,19 @@ module.exports = {
233273 description : 'builds your app and starts it on a connected Android emulator or device' ,
234274 func : runAndroid ,
235275 options : [ {
276+ command : '--install-debug' ,
277+ } , {
236278 command : '--root [string]' ,
237- description :
238- 'Override the root directory for the android build ' +
239- '(which contains the android directory)' ,
279+ description : 'Override the root directory for the android build (which contains the android directory)' ,
240280 default : '' ,
241281 } , {
242282 command : '--flavor [string]' ,
243283 description : '--flavor has been deprecated. Use --variant instead' ,
244- } , {
245- command : '--configuration [string]' ,
246- description :
247- 'You can use `Release` or `Debug`. ' +
248- 'This creates a build based on the selected configuration. ' +
249- 'If you want to use the `Release` configuration make sure you have the ' +
250- '`signingConfig` configured at `app/build.gradle`.' ,
251- default : 'Debug'
252284 } , {
253285 command : '--variant [string]' ,
254286 } , {
255- command : '--main-activity [string]' ,
256- description : 'Name of the activity to start' ,
257- default : 'MainActivity'
287+ command : '--deviceId [string]' ,
288+ description : 'builds your app and starts it on a specific device/simulator with the ' +
289+ 'given device id (listed by running "adb devices" on the command line).' ,
258290 } ] ,
259- } ;
291+ } ;
0 commit comments