diff options
Diffstat (limited to 'cordova/lib/run.js')
| -rwxr-xr-x | cordova/lib/run.js | 244 |
1 files changed, 244 insertions, 0 deletions
diff --git a/cordova/lib/run.js b/cordova/lib/run.js new file mode 100755 index 0000000..3a6246e --- /dev/null +++ b/cordova/lib/run.js @@ -0,0 +1,244 @@ +/* + Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you under the Apache License, Version 2.0 (the + "License"); you may not use this file except in compliance + with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, + software distributed under the License is distributed on an + "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + KIND, either express or implied. See the License for the + specific language governing permissions and limitations + under the License. +*/ + +var Q = require('q'); +var path = require('path'); +var iossim = require('ios-sim'); +var build = require('./build'); +var spawn = require('./spawn'); +var check_reqs = require('./check_reqs'); + +var events = require('cordova-common').events; + +var cordovaPath = path.join(__dirname, '..'); +var projectPath = path.join(__dirname, '..', '..'); + +module.exports.run = function (runOptions) { + + // Validate args + if (runOptions.device && runOptions.emulator) { + return Q.reject('Only one of "device"/"emulator" options should be specified'); + } + + // support for CB-8168 `cordova/run --list` + if (runOptions.list) { + if (runOptions.device) return module.exports.listDevices(); + if (runOptions.emulator) return module.exports.listEmulators(); + // if no --device or --emulator flag is specified, list both devices and emulators + return module.exports.listDevices().then(function () { + return module.exports.listEmulators(); + }); + } + + var useDevice = !!runOptions.device; + + return require('./list-devices').run() + .then(function (devices) { + if (devices.length > 0 && !(runOptions.emulator)) { + useDevice = true; + // we also explicitly set device flag in options as we pass + // those parameters to other api (build as an example) + runOptions.device = true; + return check_reqs.check_ios_deploy(); + } + }).then(function () { + if (!runOptions.nobuild) { + return build.run(runOptions); + } else { + return Q.resolve(); + } + }).then(function () { + return build.findXCodeProjectIn(projectPath); + }).then(function (projectName) { + var appPath = path.join(projectPath, 'build', 'emulator', projectName + '.app'); + var buildOutputDir = path.join(projectPath, 'build', 'device'); + + // select command to run and arguments depending whether + // we're running on device/emulator + if (useDevice) { + return module.exports.checkDeviceConnected() + .then(function () { + // Unpack IPA + var ipafile = path.join(buildOutputDir, projectName + '.ipa'); + + // unpack the existing platform/ios/build/device/appname.ipa (zipfile), will create a Payload folder + return spawn('unzip', [ '-o', '-qq', ipafile ], buildOutputDir); + }) + .then(function () { + // Uncompress IPA (zip file) + var appFileInflated = path.join(buildOutputDir, 'Payload', projectName + '.app'); + var appFile = path.join(buildOutputDir, projectName + '.app'); + var payloadFolder = path.join(buildOutputDir, 'Payload'); + + // delete the existing platform/ios/build/device/appname.app + return spawn('rm', [ '-rf', appFile ], buildOutputDir) + .then(function () { + // move the platform/ios/build/device/Payload/appname.app to parent + return spawn('mv', [ '-f', appFileInflated, buildOutputDir ], buildOutputDir); + }) + .then(function () { + // delete the platform/ios/build/device/Payload folder + return spawn('rm', [ '-rf', payloadFolder ], buildOutputDir); + }); + }) + .then(function () { + appPath = path.join(projectPath, 'build', 'device', projectName + '.app'); + var extraArgs = []; + if (runOptions.argv) { + // argv.slice(2) removes node and run.js, filterSupportedArgs removes the run.js args + extraArgs = module.exports.filterSupportedArgs(runOptions.argv.slice(2)); + } + return module.exports.deployToDevice(appPath, runOptions.target, extraArgs); + }, function () { + // if device connection check failed use emulator then + return module.exports.deployToSim(appPath, runOptions.target); + }); + } else { + return module.exports.deployToSim(appPath, runOptions.target); + } + }); +}; + +module.exports.filterSupportedArgs = filterSupportedArgs; +module.exports.checkDeviceConnected = checkDeviceConnected; +module.exports.deployToDevice = deployToDevice; +module.exports.deployToSim = deployToSim; +module.exports.startSim = startSim; +module.exports.listDevices = listDevices; +module.exports.listEmulators = listEmulators; + +/** + * Filters the args array and removes supported args for the 'run' command. + * + * @return {Array} array with unsupported args for the 'run' command + */ +function filterSupportedArgs (args) { + var filtered = []; + var sargs = ['--device', '--emulator', '--nobuild', '--list', '--target', '--debug', '--release']; + var re = new RegExp(sargs.join('|')); + + args.forEach(function (element) { + // supported args not found, we add + // we do a regex search because --target can be "--target=XXX" + if (element.search(re) === -1) { + filtered.push(element); + } + }, this); + + return filtered; +} + +/** + * Checks if any iOS device is connected + * @return {Promise} Fullfilled when any device is connected, rejected otherwise + */ +function checkDeviceConnected () { + return spawn('ios-deploy', ['-c', '-t', '1']); +} + +/** + * Deploy specified app package to connected device + * using ios-deploy command + * @param {String} appPath Path to application package + * @return {Promise} Resolves when deploy succeeds otherwise rejects + */ +function deployToDevice (appPath, target, extraArgs) { + // Deploying to device... + if (target) { + return spawn('ios-deploy', ['--justlaunch', '-d', '-b', appPath, '-i', target].concat(extraArgs)); + } else { + return spawn('ios-deploy', ['--justlaunch', '--no-wifi', '-d', '-b', appPath].concat(extraArgs)); + } +} + +/** + * Deploy specified app package to ios-sim simulator + * @param {String} appPath Path to application package + * @param {String} target Target device type + * @return {Promise} Resolves when deploy succeeds otherwise rejects + */ +function deployToSim (appPath, target) { + // Select target device for emulator. Default is 'iPhone-6' + if (!target) { + return require('./list-emulator-images').run() + .then(function (emulators) { + if (emulators.length > 0) { + target = emulators[0]; + } + emulators.forEach(function (emulator) { + if (emulator.indexOf('iPhone') === 0) { + target = emulator; + } + }); + events.emit('log', 'No target specified for emulator. Deploying to ' + target + ' simulator'); + return startSim(appPath, target); + }); + } else { + return startSim(appPath, target); + } +} + +function startSim (appPath, target) { + var logPath = path.join(cordovaPath, 'console.log'); + + return iossim.launch(appPath, 'com.apple.CoreSimulator.SimDeviceType.' + target, logPath, '--exit'); +} + +function listDevices () { + return require('./list-devices').run() + .then(function (devices) { + events.emit('log', 'Available iOS Devices:'); + devices.forEach(function (device) { + events.emit('log', '\t' + device); + }); + }); +} + +function listEmulators () { + return require('./list-emulator-images').run() + .then(function (emulators) { + events.emit('log', 'Available iOS Simulators:'); + emulators.forEach(function (emulator) { + events.emit('log', '\t' + emulator); + }); + }); +} + +module.exports.help = function () { + console.log('\nUsage: run [ --device | [ --emulator [ --target=<id> ] ] ] [ --debug | --release | --nobuild ]'); + // TODO: add support for building different archs + // console.log(" [ --archs=\"<list of target architectures>\" ] "); + console.log(' --device : Deploys and runs the project on the connected device.'); + console.log(' --emulator : Deploys and runs the project on an emulator.'); + console.log(' --target=<id> : Deploys and runs the project on the specified target.'); + console.log(' --debug : Builds project in debug mode. (Passed down to build command, if necessary)'); + console.log(' --release : Builds project in release mode. (Passed down to build command, if necessary)'); + console.log(' --nobuild : Uses pre-built package, or errors if project is not built.'); + // TODO: add support for building different archs + // console.log(" --archs : Specific chip architectures (`anycpu`, `arm`, `x86`, `x64`)."); + console.log(''); + console.log('Examples:'); + console.log(' run'); + console.log(' run --device'); + console.log(' run --emulator --target=\"iPhone-6-Plus\"'); /* eslint no-useless-escape : 0 */ + console.log(' run --device --release'); + console.log(' run --emulator --debug'); + console.log(''); + process.exit(0); +}; |
