From 879da03438130c0cf5f9cc70b6cd9ebab615345c 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 | 152 +++++++++++++------------- bin/templates/cordova/lib/device.js | 5 +- bin/templates/cordova/lib/emulator.js | 5 +- bin/templates/cordova/lib/run.js | 26 ++--- 4 files changed, 93 insertions(+), 95 deletions(-) diff --git a/bin/templates/cordova/lib/build.js b/bin/templates/cordova/lib/build.js index 2e6a624d..18a2f3bf 100644 --- a/bin/templates/cordova/lib/build.js +++ b/bin/templates/cordova/lib/build.js @@ -32,23 +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) { - var timeDiff = b.t - a.t; - if (timeDiff === 0) { - return a.p.length - b.p.length; + fs.readdirSync(directory).forEach(function(p) { + if (path.extname(p) == '.apk') { + ret.push(path.join(directory, p)); } - return timeDiff; - }).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); + }); } + 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() { @@ -127,8 +150,6 @@ var builders = { return check_reqs.check_ant() .then(function() { return spawn('ant', args); - }).then(function() { - return builder.getOutputFiles(); }); }, @@ -140,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); - } - var ret = candidates[0]; - return [ret]; + findOutputApks: function(build_type) { + var binDir = path.join(ROOT, hasCustomRules() ? 'ant-build' : 'bin'); + return findOutputApksHelper(binDir, build_type); } }, gradle: { @@ -227,8 +235,6 @@ var builders = { var args = this.getArgs(build_type == 'debug' ? 'debug' : 'release'); return Q().then(function() { return spawn(wrapper, args); - }).then(function() { - return builder.getOutputFiles(build_type); }); }, @@ -241,21 +247,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', 'outputs', '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); } }, @@ -265,11 +259,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))); + } } }; @@ -284,7 +281,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': @@ -334,20 +331,15 @@ 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); - var builtApks = []; - for (var i=0; i < apkFiles.length; ++i) { - var dst = path.join(outputDir, path.basename(apkFiles[i])); - builtApks.push(dst); - shell.cp('-f', apkFiles[i], dst); - } - console.log('Built the following APKs:\n' + builtApks.join('\n')); - return builtApks; + }).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 + }; }); }; @@ -365,21 +357,25 @@ module.exports.detectArchitecture = function(target) { }); }; -/* - * 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 - */ -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.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 671f93d0..b2df1d70 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) { @@ -64,8 +64,9 @@ module.exports.install = function(target) { return build.detectArchitecture(target) .then(function(arch) { - var apk_path = build.get_apk(null, 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); diff --git a/bin/templates/cordova/lib/emulator.js b/bin/templates/cordova/lib/emulator.js index 88b35f7b..ecc8b380 100644 --- a/bin/templates/cordova/lib/emulator.js +++ b/bin/templates/cordova/lib/emulator.js @@ -279,7 +279,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) { @@ -295,8 +295,9 @@ module.exports.install = function(target) { return build.detectArchitecture(target) .then(function(arch) { - var apk_path = build.get_apk(null, 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) { diff --git a/bin/templates/cordova/lib/run.js b/bin/templates/cordova/lib/run.js index da8fc607..16e8065b 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() { 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();