From 1c3f7e6f93e83da1c6bc477dceff05bd979a6eac Mon Sep 17 00:00:00 2001 From: Andrew Grieve Date: Mon, 22 Sep 2014 14:23:30 -0400 Subject: [PATCH] CB-7579 Fix run script's ability to use non-arch-specific APKs --- bin/templates/cordova/lib/build.js | 157 +++++++++++++++----------- bin/templates/cordova/lib/device.js | 23 ++-- bin/templates/cordova/lib/emulator.js | 14 ++- bin/templates/cordova/lib/run.js | 26 ++--- 4 files changed, 121 insertions(+), 99 deletions(-) diff --git a/bin/templates/cordova/lib/build.js b/bin/templates/cordova/lib/build.js index 30056f46..9e1353b5 100644 --- a/bin/templates/cordova/lib/build.js +++ b/bin/templates/cordova/lib/build.js @@ -20,6 +20,7 @@ */ var shell = require('shelljs'), + exec = require('./exec'), spawn = require('./spawn'), Q = require('q'), path = require('path'), @@ -31,20 +32,46 @@ var LOCAL_PROPERTIES_TEMPLATE = '# This file is automatically generated.\n' + '# Do not modify this file -- YOUR CHANGES WILL BE ERASED!\n'; -function find_files(directory, predicate) { +function findApks(directory) { + var ret = []; if (fs.existsSync(directory)) { - var candidates = fs.readdirSync(directory).filter(predicate).map(function(p) { - p = path.join(directory, p); - return { p: p, t: fs.statSync(p).mtime }; - }).sort(function(a,b) { - return a.t > b.t ? -1 : - a.t < b.t ? 1 : 0; - }).map(function(p) { return p.p; }); - return candidates; - } else { - console.error('ERROR : unable to find project ' + directory + ' directory, could not locate .apk'); - process.exit(2); + fs.readdirSync(directory).forEach(function(p) { + if (path.extname(p) == '.apk') { + ret.push(path.join(directory, p)); + } + }); } + return ret; +} + +function sortFilesByDate(files) { + return files.map(function(p) { + return { p: p, t: fs.statSync(p).mtime }; + }).sort(function(a, b) { + var timeDiff = b.t - a.t; + return timeDiff === 0 ? a.p.length - b.p.length : timeDiff; + }).map(function(p) { return p.p; }); +} + +function findOutputApksHelper(dir, build_type) { + var ret = findApks(dir).filter(function(candidate) { + // Need to choose between release and debug .apk. + if (build_type === 'debug') { + return /-debug/.exec(candidate) && !/-unaligned|-unsigned/.exec(candidate); + } + if (build_type === 'release') { + return /-release/.exec(candidate) && !/-unaligned/.exec(candidate); + } + return true; + }); + ret = sortFilesByDate(ret); + if (ret.length === 0) { + return ret; + } + var archSpecific = !!/-x86|-arm/.exec(ret[0]); + return ret.filter(function(p) { + return !!/-x86|-arm/.exec(p) == archSpecific; + }); } function hasCustomRules() { @@ -123,8 +150,6 @@ var builders = { return check_reqs.check_ant() .then(function() { return spawn('ant', args); - }).then(function() { - return builder.getOutputFiles(); }); }, @@ -136,22 +161,9 @@ var builders = { }); }, - // Find the recently-generated output APK files - // Ant only generates one output file; return it. - getOutputFiles: function() { - var binDir; - if(hasCustomRules()) { - binDir = path.join(ROOT, 'ant-build'); - } else { - binDir = path.join(ROOT, 'bin'); - } - var candidates = find_files(binDir, function(candidate) { return path.extname(candidate) == '.apk'; }); - if (candidates.length === 0) { - console.error('ERROR : No .apk found in ' + binDir + ' directory'); - process.exit(2); - } - console.log('Using apk: ' + candidates[0]); - return [candidates[0]]; + findOutputApks: function(build_type) { + var binDir = path.join(ROOT, hasCustomRules() ? 'ant-build' : 'bin'); + return findOutputApksHelper(binDir, build_type); } }, gradle: { @@ -218,8 +230,6 @@ var builders = { var args = builder.getArgs('build'); return Q().then(function() { return spawn(wrapper, args); - }).then(function() { - return builder.getOutputFiles(build_type); }); }, @@ -232,21 +242,9 @@ var builders = { }); }, - // Find the recently-generated output APK files - // Gradle can generate multiple output files; return all of them. - getOutputFiles: function(build_type) { + findOutputApks: function(build_type) { var binDir = path.join(ROOT, 'build', 'apk'); - var candidates = find_files(binDir, function(candidate) { - // Need to choose between release and debug .apk. - if (build_type === 'debug') { - return (path.extname(candidate) == '.apk' && candidate.indexOf('-debug-') >= 0); - } - if (build_type === 'release') { - return (path.extname(candidate) == '.apk' && candidate.indexOf('-release-') >= 0); - } - return path.extname(candidate) == '.apk'; - }); - return candidates; + return findOutputApksHelper(binDir, build_type); } }, @@ -256,11 +254,14 @@ var builders = { }, build: function() { console.log('Skipping build...'); - return Q(); + return Q(null); }, clean: function() { return Q(); }, + findOutputApks: function(build_type) { + return sortFilesByDate(builders.ant.findOutputApks(build_type).concat(builders.gradle.findOutputApks(build_type))); + } } }; @@ -275,7 +276,7 @@ function parseOpts(options) { // Iterate through command line options for (var i=0; options && (i < options.length); ++i) { - if (options[i].substring && options[i].substring(0,2) == "--") { + if (/^--/.exec(options[i])) { var option = options[i].substring(2); switch(option) { case 'debug': @@ -325,33 +326,51 @@ module.exports.run = function(options) { return builder.prepEnv() .then(function() { return builder.build(opts.buildType); - }).then(function(apkFiles) { - // TODO: Rather than copy apks to out, it might be better to - // just write out what the last .apk build was. These files - // are used by get_apk(). - var outputDir = path.join(ROOT, 'out'); - shell.mkdir('-p', outputDir); - for (var i=0; i < apkFiles.length; ++i) { - shell.cp('-f', apkFiles[i], path.join(outputDir, path.basename(apkFiles[i]))); - } + }).then(function() { + var apkPaths = builder.findOutputApks(opts.buildType); + console.log('Built the following apk(s):'); + console.log(' ' + apkPaths.join('\n ')); + return { + apkPaths: apkPaths, + buildType: opts.buildType, + buildMethod: opts.buildMethod + }; }); }; /* - * Gets the path to the apk file, if not such file exists then - * the script will error out. (should we error or just return undefined?) - * This is called by the run script to install the apk to the device + * Detects the architecture of a device/emulator + * Returns "arm" or "x86". */ -module.exports.get_apk = function(build_type, architecture) { - var outputDir = path.join(ROOT, 'out'); - var candidates = find_files(outputDir, function(filename) { return (!architecture) || filename.indexOf(architecture) >= 0; }); - if (candidates.length === 0) { - console.error('ERROR : No .apk found in ' + outputDir + ' directory'); - process.exit(2); +module.exports.detectArchitecture = function(target) { + return exec('adb -s ' + target + ' shell cat /proc/cpuinfo') + .then(function(output) { + if (/intel/i.exec(output)) { + return 'x86'; + } + return 'arm'; + }); +}; + +module.exports.findBestApkForArchitecture = function(buildResults, arch) { + var paths = buildResults.apkPaths.filter(function(p) { + if (buildResults.buildType == 'debug') { + return /-debug/.exec(p); + } + return !/-debug/.exec(p); + }); + var archPattern = new RegExp('-' + arch); + var hasArchPattern = /-x86|-arm/; + for (var i = 0; i < paths.length; ++i) { + if (hasArchPattern.exec(paths[i])) { + if (archPattern.exec(paths[i])) { + return paths[i]; + } + } else { + return paths[i]; + } } - // TODO: Use build_type here. - console.log('Using apk: ' + candidates[0]); - return candidates[0]; + throw new Error('Could not find apk architecture: ' + arch + ' build-type: ' + buildResults.buildType); }; module.exports.help = function() { diff --git a/bin/templates/cordova/lib/device.js b/bin/templates/cordova/lib/device.js index df212876..c3bd9a23 100644 --- a/bin/templates/cordova/lib/device.js +++ b/bin/templates/cordova/lib/device.js @@ -48,7 +48,7 @@ module.exports.list = function() { * and launches it. * Returns a promise. */ -module.exports.install = function(target) { +module.exports.install = function(target, buildResults) { var launchName; return this.list() .then(function(device_list) { @@ -56,21 +56,20 @@ module.exports.install = function(target) { return Q.reject('ERROR: Failed to deploy to device, no devices found.'); // default device - target = typeof target !== 'undefined' ? target : device_list[0]; + target = target || device_list[0]; if (device_list.indexOf(target) < 0) return Q.reject('ERROR: Unable to find target \'' + target + '\'.'); - var apk_path; - if (typeof process.env.DEPLOY_APK_ARCH == 'undefined') { - apk_path = build.get_apk(); - } else { - apk_path = build.get_apk(null, process.env.DEPLOY_APK_ARCH); - } - launchName = appinfo.getActivityName(); - console.log('Installing app on device...'); - var cmd = 'adb -s ' + target + ' install -r "' + apk_path + '"'; - return exec(cmd); + return build.detectArchitecture(target) + .then(function(arch) { + var apk_path = build.findBestApkForArchitecture(buildResults, arch); + launchName = appinfo.getActivityName(); + console.log('Using apk: ' + apk_path); + console.log('Installing app on device...'); + var cmd = 'adb -s ' + target + ' install -r "' + apk_path + '"'; + return exec(cmd); + }); }).then(function(output) { if (output.match(/Failure/)) return Q.reject('ERROR: Failed to install apk to device: ' + output); diff --git a/bin/templates/cordova/lib/emulator.js b/bin/templates/cordova/lib/emulator.js index 69f0e1b5..b46db6e3 100644 --- a/bin/templates/cordova/lib/emulator.js +++ b/bin/templates/cordova/lib/emulator.js @@ -283,7 +283,7 @@ module.exports.create_image = function(name, target) { * If no started emulators are found, error out. * Returns a promise. */ -module.exports.install = function(target) { +module.exports.install = function(target, buildResults) { var self = this; return this.list_started() .then(function(emulator_list) { @@ -292,14 +292,18 @@ module.exports.install = function(target) { } // default emulator - target = typeof target !== 'undefined' ? target : emulator_list[0]; + target = target || emulator_list[0]; if (emulator_list.indexOf(target) < 0) { return Q.reject('Unable to find target \'' + target + '\'. Failed to deploy to emulator.'); } - console.log('Installing app on emulator...'); - var apk_path = build.get_apk(); - return exec('adb -s ' + target + ' install -r "' + apk_path + '"'); + return build.detectArchitecture(target) + .then(function(arch) { + var apk_path = build.findBestApkForArchitecture(buildResults, arch); + console.log('Installing app on emulator...'); + console.log('Using apk: ' + apk_path); + return exec('adb -s ' + target + ' install -r "' + apk_path + '"'); + }); }).then(function(output) { if (output.match(/Failure/)) { return Q.reject('Failed to install apk to emulator: ' + output); diff --git a/bin/templates/cordova/lib/run.js b/bin/templates/cordova/lib/run.js index da8fc607..f22f32e3 100644 --- a/bin/templates/cordova/lib/run.js +++ b/bin/templates/cordova/lib/run.js @@ -33,16 +33,16 @@ var path = require('path'), * Returns a promise. */ module.exports.run = function(args) { - var build_type; + var buildFlags = []; var install_target; for (var i=2; i 0 ? Q() : emulator.start(); - return p.then(function() { emulator.install(); }); + return p.then(function() { return emulator.install(null, buildResults); }); }); } else if (install_target) { var devices, started_emulators, avds; @@ -75,16 +75,16 @@ var path = require('path'), }).then(function(res) { avds = res; if (devices.indexOf(install_target) > -1) { - return device.install(install_target); + return device.install(install_target, buildResults); } else if (started_emulators.indexOf(install_target) > -1) { - return emulator.install(install_target); + return emulator.install(install_target, buildResults); } else { // if target emulator isn't started, then start it. var emulator_ID; for(avd in avds) { if(avds[avd].name == install_target) { return emulator.start(install_target) - .then(function() { emulator.install(emulator_ID); }); + .then(function() { emulator.install(emulator_ID, buildResults); }); } } return Q.reject('Target \'' + install_target + '\' not found, unable to run project'); @@ -96,13 +96,13 @@ var path = require('path'), .then(function(device_list) { if (device_list.length > 0) { console.log('WARNING : No target specified, deploying to device \'' + device_list[0] + '\'.'); - return device.install(device_list[0]); + return device.install(device_list[0], buildResults); } else { return emulator.list_started() .then(function(emulator_list) { if (emulator_list.length > 0) { console.log('WARNING : No target specified, deploying to emulator \'' + emulator_list[0] + '\'.'); - return emulator.install(emulator_list[0]); + return emulator.install(emulator_list[0], buildResults); } else { console.log('WARNING : No started emulators found, starting an emulator.'); return emulator.best_image() @@ -111,7 +111,7 @@ var path = require('path'), return emulator.start(best_avd.name) .then(function(emulator_ID) { console.log('WARNING : No target specified, deploying to emulator \'' + emulator_ID + '\'.'); - return emulator.install(emulator_ID); + return emulator.install(emulator_ID, buildResults); }); } else { return emulator.start();