CB-7579 Fix run script's ability to use non-arch-specific APKs
diff --git a/bin/templates/cordova/lib/build.js b/bin/templates/cordova/lib/build.js
index 2e6a624..18a2f3b 100644
--- a/bin/templates/cordova/lib/build.js
+++ b/bin/templates/cordova/lib/build.js
@@ -32,23 +32,46 @@
     '# 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 @@
             return check_reqs.check_ant()
             .then(function() {
                 return spawn('ant', args);
-            }).then(function() {
-                return builder.getOutputFiles();
             });
         },
 
@@ -140,22 +161,9 @@
             });
         },
 
-        // 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 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 @@
             });
         },
 
-        // 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 @@
         },
         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 @@
 
     // 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 @@
     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 @@
     });
 };
 
-/*
- * 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 671f93d..b2df1d7 100644
--- a/bin/templates/cordova/lib/device.js
+++ b/bin/templates/cordova/lib/device.js
@@ -48,7 +48,7 @@
  * 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 @@
 
         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 88b35f7..ecc8b38 100644
--- a/bin/templates/cordova/lib/emulator.js
+++ b/bin/templates/cordova/lib/emulator.js
@@ -279,7 +279,7 @@
  * 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 @@
 
         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 da8fc60..16e8065 100644
--- a/bin/templates/cordova/lib/run.js
+++ b/bin/templates/cordova/lib/run.js
@@ -33,16 +33,16 @@
  * Returns a promise.
  */
  module.exports.run = function(args) {
-    var build_type;
+    var buildFlags = [];
     var install_target;
 
     for (var i=2; i<args.length; i++) {
         if (args[i] == '--debug') {
-            build_type = '--debug';
+            buildFlags.push('--debug');
         } else if (args[i] == '--release') {
-            build_type = '--release';
+            buildFlags.push('--release');
         } else if (args[i] == '--nobuild') {
-            build_type = '--nobuild';
+            buildFlags.push('--nobuild');
         } else if (args[i] == '--device') {
             install_target = '--device';
         } else if (args[i] == '--emulator') {
@@ -55,13 +55,13 @@
         }
     }
 
-    return build.run(build_type).then(function() {
+    return build.run(buildFlags).then(function(buildResults) {
         if (install_target == '--device') {
-            return device.install();
+            return device.install(null, buildResults);
         } else if (install_target == '--emulator') {
             return emulator.list_started().then(function(started) {
                 var p = started && started.length > 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 @@
             }).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 @@
             .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 @@
                                     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();