CB-7579 Fix run script's ability to use non-arch-specific APKs

This commit is contained in:
Andrew Grieve 2014-09-22 14:23:30 -04:00 committed by Vladimir Kotikov
parent e65bfa41a5
commit 1c3f7e6f93
4 changed files with 121 additions and 99 deletions

View File

@ -20,6 +20,7 @@
*/ */
var shell = require('shelljs'), var shell = require('shelljs'),
exec = require('./exec'),
spawn = require('./spawn'), spawn = require('./spawn'),
Q = require('q'), Q = require('q'),
path = require('path'), path = require('path'),
@ -31,20 +32,46 @@ var LOCAL_PROPERTIES_TEMPLATE =
'# This file is automatically generated.\n' + '# This file is automatically generated.\n' +
'# Do not modify this file -- YOUR CHANGES WILL BE ERASED!\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)) { if (fs.existsSync(directory)) {
var candidates = fs.readdirSync(directory).filter(predicate).map(function(p) { fs.readdirSync(directory).forEach(function(p) {
p = path.join(directory, p); if (path.extname(p) == '.apk') {
return { p: p, t: fs.statSync(p).mtime }; ret.push(path.join(directory, p));
}).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);
} }
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() { function hasCustomRules() {
@ -123,8 +150,6 @@ var builders = {
return check_reqs.check_ant() return check_reqs.check_ant()
.then(function() { .then(function() {
return spawn('ant', args); return spawn('ant', args);
}).then(function() {
return builder.getOutputFiles();
}); });
}, },
@ -136,22 +161,9 @@ var builders = {
}); });
}, },
// Find the recently-generated output APK files findOutputApks: function(build_type) {
// Ant only generates one output file; return it. var binDir = path.join(ROOT, hasCustomRules() ? 'ant-build' : 'bin');
getOutputFiles: function() { return findOutputApksHelper(binDir, build_type);
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]];
} }
}, },
gradle: { gradle: {
@ -218,8 +230,6 @@ var builders = {
var args = builder.getArgs('build'); var args = builder.getArgs('build');
return Q().then(function() { return Q().then(function() {
return spawn(wrapper, args); return spawn(wrapper, args);
}).then(function() {
return builder.getOutputFiles(build_type);
}); });
}, },
@ -232,21 +242,9 @@ var builders = {
}); });
}, },
// Find the recently-generated output APK files findOutputApks: function(build_type) {
// Gradle can generate multiple output files; return all of them.
getOutputFiles: function(build_type) {
var binDir = path.join(ROOT, 'build', 'apk'); var binDir = path.join(ROOT, 'build', 'apk');
var candidates = find_files(binDir, function(candidate) { return findOutputApksHelper(binDir, build_type);
// 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;
} }
}, },
@ -256,11 +254,14 @@ var builders = {
}, },
build: function() { build: function() {
console.log('Skipping build...'); console.log('Skipping build...');
return Q(); return Q(null);
}, },
clean: function() { clean: function() {
return Q(); 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 // Iterate through command line options
for (var i=0; options && (i < options.length); ++i) { 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); var option = options[i].substring(2);
switch(option) { switch(option) {
case 'debug': case 'debug':
@ -325,33 +326,51 @@ module.exports.run = function(options) {
return builder.prepEnv() return builder.prepEnv()
.then(function() { .then(function() {
return builder.build(opts.buildType); return builder.build(opts.buildType);
}).then(function(apkFiles) { }).then(function() {
// TODO: Rather than copy apks to out, it might be better to var apkPaths = builder.findOutputApks(opts.buildType);
// just write out what the last .apk build was. These files console.log('Built the following apk(s):');
// are used by get_apk(). console.log(' ' + apkPaths.join('\n '));
var outputDir = path.join(ROOT, 'out'); return {
shell.mkdir('-p', outputDir); apkPaths: apkPaths,
for (var i=0; i < apkFiles.length; ++i) { buildType: opts.buildType,
shell.cp('-f', apkFiles[i], path.join(outputDir, path.basename(apkFiles[i]))); buildMethod: opts.buildMethod
} };
}); });
}; };
/* /*
* Gets the path to the apk file, if not such file exists then * Detects the architecture of a device/emulator
* the script will error out. (should we error or just return undefined?) * Returns "arm" or "x86".
* This is called by the run script to install the apk to the device
*/ */
module.exports.get_apk = function(build_type, architecture) { module.exports.detectArchitecture = function(target) {
var outputDir = path.join(ROOT, 'out'); return exec('adb -s ' + target + ' shell cat /proc/cpuinfo')
var candidates = find_files(outputDir, function(filename) { return (!architecture) || filename.indexOf(architecture) >= 0; }); .then(function(output) {
if (candidates.length === 0) { if (/intel/i.exec(output)) {
console.error('ERROR : No .apk found in ' + outputDir + ' directory'); return 'x86';
process.exit(2); }
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. throw new Error('Could not find apk architecture: ' + arch + ' build-type: ' + buildResults.buildType);
console.log('Using apk: ' + candidates[0]);
return candidates[0];
}; };
module.exports.help = function() { module.exports.help = function() {

View File

@ -48,7 +48,7 @@ module.exports.list = function() {
* and launches it. * and launches it.
* Returns a promise. * Returns a promise.
*/ */
module.exports.install = function(target) { module.exports.install = function(target, buildResults) {
var launchName; var launchName;
return this.list() return this.list()
.then(function(device_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.'); return Q.reject('ERROR: Failed to deploy to device, no devices found.');
// default device // default device
target = typeof target !== 'undefined' ? target : device_list[0]; target = target || device_list[0];
if (device_list.indexOf(target) < 0) if (device_list.indexOf(target) < 0)
return Q.reject('ERROR: Unable to find target \'' + target + '\'.'); return Q.reject('ERROR: Unable to find target \'' + target + '\'.');
var apk_path; return build.detectArchitecture(target)
if (typeof process.env.DEPLOY_APK_ARCH == 'undefined') { .then(function(arch) {
apk_path = build.get_apk(); var apk_path = build.findBestApkForArchitecture(buildResults, arch);
} else { launchName = appinfo.getActivityName();
apk_path = build.get_apk(null, process.env.DEPLOY_APK_ARCH); console.log('Using apk: ' + apk_path);
} console.log('Installing app on device...');
launchName = appinfo.getActivityName(); var cmd = 'adb -s ' + target + ' install -r "' + apk_path + '"';
console.log('Installing app on device...'); return exec(cmd);
var cmd = 'adb -s ' + target + ' install -r "' + apk_path + '"'; });
return exec(cmd);
}).then(function(output) { }).then(function(output) {
if (output.match(/Failure/)) return Q.reject('ERROR: Failed to install apk to device: ' + output); if (output.match(/Failure/)) return Q.reject('ERROR: Failed to install apk to device: ' + output);

View File

@ -283,7 +283,7 @@ module.exports.create_image = function(name, target) {
* If no started emulators are found, error out. * If no started emulators are found, error out.
* Returns a promise. * Returns a promise.
*/ */
module.exports.install = function(target) { module.exports.install = function(target, buildResults) {
var self = this; var self = this;
return this.list_started() return this.list_started()
.then(function(emulator_list) { .then(function(emulator_list) {
@ -292,14 +292,18 @@ module.exports.install = function(target) {
} }
// default emulator // default emulator
target = typeof target !== 'undefined' ? target : emulator_list[0]; target = target || emulator_list[0];
if (emulator_list.indexOf(target) < 0) { if (emulator_list.indexOf(target) < 0) {
return Q.reject('Unable to find target \'' + target + '\'. Failed to deploy to emulator.'); return Q.reject('Unable to find target \'' + target + '\'. Failed to deploy to emulator.');
} }
console.log('Installing app on emulator...'); return build.detectArchitecture(target)
var apk_path = build.get_apk(); .then(function(arch) {
return exec('adb -s ' + target + ' install -r "' + apk_path + '"'); 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) { }).then(function(output) {
if (output.match(/Failure/)) { if (output.match(/Failure/)) {
return Q.reject('Failed to install apk to emulator: ' + output); return Q.reject('Failed to install apk to emulator: ' + output);

View File

@ -33,16 +33,16 @@ var path = require('path'),
* Returns a promise. * Returns a promise.
*/ */
module.exports.run = function(args) { module.exports.run = function(args) {
var build_type; var buildFlags = [];
var install_target; var install_target;
for (var i=2; i<args.length; i++) { for (var i=2; i<args.length; i++) {
if (args[i] == '--debug') { if (args[i] == '--debug') {
build_type = '--debug'; buildFlags.push('--debug');
} else if (args[i] == '--release') { } else if (args[i] == '--release') {
build_type = '--release'; buildFlags.push('--release');
} else if (args[i] == '--nobuild') { } else if (args[i] == '--nobuild') {
build_type = '--nobuild'; buildFlags.push('--nobuild');
} else if (args[i] == '--device') { } else if (args[i] == '--device') {
install_target = '--device'; install_target = '--device';
} else if (args[i] == '--emulator') { } else if (args[i] == '--emulator') {
@ -55,13 +55,13 @@ var path = require('path'),
} }
} }
return build.run(build_type).then(function() { return build.run(buildFlags).then(function(buildResults) {
if (install_target == '--device') { if (install_target == '--device') {
return device.install(); return device.install(null, buildResults);
} else if (install_target == '--emulator') { } else if (install_target == '--emulator') {
return emulator.list_started().then(function(started) { return emulator.list_started().then(function(started) {
var p = started && started.length > 0 ? Q() : emulator.start(); var p = started && started.length > 0 ? Q() : emulator.start();
return p.then(function() { emulator.install(); }); return p.then(function() { return emulator.install(null, buildResults); });
}); });
} else if (install_target) { } else if (install_target) {
var devices, started_emulators, avds; var devices, started_emulators, avds;
@ -75,16 +75,16 @@ var path = require('path'),
}).then(function(res) { }).then(function(res) {
avds = res; avds = res;
if (devices.indexOf(install_target) > -1) { 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) { } else if (started_emulators.indexOf(install_target) > -1) {
return emulator.install(install_target); return emulator.install(install_target, buildResults);
} else { } else {
// if target emulator isn't started, then start it. // if target emulator isn't started, then start it.
var emulator_ID; var emulator_ID;
for(avd in avds) { for(avd in avds) {
if(avds[avd].name == install_target) { if(avds[avd].name == install_target) {
return emulator.start(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'); return Q.reject('Target \'' + install_target + '\' not found, unable to run project');
@ -96,13 +96,13 @@ var path = require('path'),
.then(function(device_list) { .then(function(device_list) {
if (device_list.length > 0) { if (device_list.length > 0) {
console.log('WARNING : No target specified, deploying to device \'' + device_list[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 { } else {
return emulator.list_started() return emulator.list_started()
.then(function(emulator_list) { .then(function(emulator_list) {
if (emulator_list.length > 0) { if (emulator_list.length > 0) {
console.log('WARNING : No target specified, deploying to emulator \'' + emulator_list[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 { } else {
console.log('WARNING : No started emulators found, starting an emulator.'); console.log('WARNING : No started emulators found, starting an emulator.');
return emulator.best_image() return emulator.best_image()
@ -111,7 +111,7 @@ var path = require('path'),
return emulator.start(best_avd.name) return emulator.start(best_avd.name)
.then(function(emulator_ID) { .then(function(emulator_ID) {
console.log('WARNING : No target specified, deploying to emulator \'' + 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 { } else {
return emulator.start(); return emulator.start();