You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cordova.apache.org by ia...@apache.org on 2014/08/18 16:01:02 UTC
[4/5] android commit: CB-3445: Add option to build and install with
gradle
CB-3445: Add option to build and install with gradle
This gives build/cordova two new command-line arguments: --ant and
--gradle, and will select the build type from those. As a fallback
for the Cordova CLI, the environment variable ANDROID_BUILD can also be
used, set to either "ant" or "gradle".
The default is currently "ant", but it is intended for this to change in
the future.
Project: http://git-wip-us.apache.org/repos/asf/cordova-android/repo
Commit: http://git-wip-us.apache.org/repos/asf/cordova-android/commit/7d6ac870
Tree: http://git-wip-us.apache.org/repos/asf/cordova-android/tree/7d6ac870
Diff: http://git-wip-us.apache.org/repos/asf/cordova-android/diff/7d6ac870
Branch: refs/heads/master
Commit: 7d6ac8703378cfd58b175de8483a2bf26242f799
Parents: 8aa813b
Author: Ian Clelland <ic...@chromium.org>
Authored: Mon Aug 18 09:44:00 2014 -0400
Committer: Ian Clelland <ic...@chromium.org>
Committed: Mon Aug 18 09:48:31 2014 -0400
----------------------------------------------------------------------
bin/templates/cordova/build | 10 +-
bin/templates/cordova/lib/build.js | 231 ++++++++++++++++++++++++--------
2 files changed, 184 insertions(+), 57 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/cordova-android/blob/7d6ac870/bin/templates/cordova/build
----------------------------------------------------------------------
diff --git a/bin/templates/cordova/build b/bin/templates/cordova/build
index 2d58901..367bf7d 100755
--- a/bin/templates/cordova/build
+++ b/bin/templates/cordova/build
@@ -24,12 +24,16 @@ var build = require('./lib/build'),
args = process.argv;
// Support basic help commands
-if(args[2] == '--help' || args[2] == '/?' || args[2] == '-h' ||
- args[2] == 'help' || args[2] == '-help' || args[2] == '/help') {
+if(args[2] == '--help' ||
+ args[2] == '/?' ||
+ args[2] == '-h' ||
+ args[2] == 'help' ||
+ args[2] == '-help' ||
+ args[2] == '/help') {
build.help();
} else {
reqs.run().done(function() {
- return build.run(args[2]);
+ return build.run.call(build.run, args.slice(2));
}, function(err) {
console.error(err);
process.exit(2);
http://git-wip-us.apache.org/repos/asf/cordova-android/blob/7d6ac870/bin/templates/cordova/lib/build.js
----------------------------------------------------------------------
diff --git a/bin/templates/cordova/lib/build.js b/bin/templates/cordova/lib/build.js
index 5134f41..3d0ae87 100644
--- a/bin/templates/cordova/lib/build.js
+++ b/bin/templates/cordova/lib/build.js
@@ -27,39 +27,164 @@ var shell = require('shelljs'),
ROOT = path.join(__dirname, '..', '..');
var check_reqs = require('./check_reqs');
+// Globals
+var build_type,
+ build_method;
+
+function find_files(directory, predicate) {
+ 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);
+ }
+}
function hasCustomRules() {
return fs.existsSync(path.join(ROOT, 'custom_rules.xml'));
}
-module.exports.getAntArgs = function(cmd) {
- var args = [cmd, '-f', path.join(ROOT, 'build.xml')];
- // custom_rules.xml is required for incremental builds.
- if (hasCustomRules()) {
- args.push('-Dout.dir=ant-build', '-Dgen.absolute.dir=ant-gen');
+
+module.exports.builders = {
+ ant: {
+ getArgs: function(cmd) {
+ var args = [cmd, '-f', path.join(ROOT, 'build.xml')];
+ // custom_rules.xml is required for incremental builds.
+ if (hasCustomRules()) {
+ args.push('-Dout.dir=ant-build', '-Dgen.absolute.dir=ant-gen');
+ }
+ try {
+ // Specify sdk dir in case local properties are missing
+ args.push('-Dsdk.dir='+path.join(which.sync('android'), '../..'));
+ } catch(e) {
+ // Can't find android; don't push arg: assume all is okay
+ }
+ return args;
+ },
+
+ /*
+ * Builds the project with ant.
+ * Returns a promise.
+ */
+ build: function(build_type) {
+ var builder = this;
+ var args = builder.getArgs(build_type == "debug" ? 'debug' : 'release');
+ return Q().then(function() {
+ return spawn('ant', args);
+ }).then(function() {
+ return builder.getOutputFiles();
+ });
+ },
+
+ // 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]];
+ }
+ },
+ gradle: {
+ getArgs: function(cmd) {
+ var args = [cmd, '-b', path.join(ROOT, 'build.gradle')];
+ return args;
+ },
+
+ /*
+ * Builds the project with gradle.
+ * Returns a promise.
+ */
+ build: function(build_type) {
+ var builder = this;
+ var wrapper = path.join(ROOT, 'gradlew');
+ var args = builder.getArgs('build');
+ return Q().then(function() {
+ return spawn(wrapper, args);
+ }).then(function() {
+ return builder.getOutputFiles(build_type);
+ });
+ },
+
+ // Find the recently-generated output APK files
+ // Gradle can generate multiple output files; return all of them.
+ getOutputFiles: 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 args;
};
/*
- * Builds the project with ant.
+ * Builds the project with the specifed options
* Returns a promise.
*/
-module.exports.run = function(build_type) {
- //default build type
- build_type = typeof build_type !== 'undefined' ? build_type : "--debug";
- var args = module.exports.getAntArgs('debug');
- switch(build_type) {
- case '--debug' :
- break;
- case '--release' :
- args[0] = 'release';
- break;
- case '--nobuild' :
- console.log('Skipping build...');
- return Q();
- default :
- return Q.reject('Build option \'' + build_type + '\' not recognized.');
+module.exports.run = function(options) {
+
+ // Backwards-compatibility: Allow a single string argument
+ if (typeof options == "string") options = [options];
+
+ // Iterate through command line options
+ for (var i=0; options && (i < options.length); ++i) {
+ if (options[i].substring && options[i].substring(0,2) == "--") {
+ var option = options[i].substring(2);
+ switch(option) {
+ case 'debug':
+ case 'release':
+ if (build_type) {
+ return Q.reject('Multiple build types (' + build_type + ' and ' + option + ') specified.');
+ }
+ build_type = option;
+ break;
+ case 'ant':
+ case 'gradle':
+ if (build_method) {
+ return Q.reject('Multiple build methods (' + build_method + ' and ' + option + ') specified.');
+ }
+ build_method = option;
+ break;
+ case 'nobuild' :
+ console.log('Skipping build...');
+ return Q();
+ default :
+ return Q.reject('Build option \'' + options[i] + '\' not recognized.');
+ }
+ } else {
+ return Q.reject('Build option \'' + options[i] + '\' not recognized.');
+ }
}
+ // Defaults
+ build_type = build_type || "debug";
+ build_method = build_method || process.env.ANDROID_BUILD || "ant";
+
+ // Get the builder
+ var builder = module.exports.builders[build_method];
+
// Without our custom_rules.xml, we need to clean before building.
var ret;
if (!hasCustomRules()) {
@@ -68,50 +193,48 @@ module.exports.run = function(build_type) {
} else {
ret = check_reqs.check_ant();
}
+
+ // Return a promise for the actual build
return ret.then(function() {
- return spawn('ant', args);
+ return builder.build.call(builder, build_type);
+ }).then(function(apkFiles) {
+ var outputDir = path.join(ROOT, 'out');
+ try {
+ fs.mkdirSync(outputDir);
+ } catch (e) {
+ if (e.code != "EEXIST") {
+ throw e;
+ }
+ }
+ for (var i=0; i < apkFiles.length; ++i) {
+ shell.cp('-f', apkFiles[i], path.join(outputDir, path.basename(apkFiles[i])));
+ }
});
-}
+};
/*
* 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() {
- var binDir = '';
- if(!hasCustomRules()) {
- binDir = path.join(ROOT, 'bin');
- } else {
- binDir = path.join(ROOT, 'ant-build');
- }
- if (fs.existsSync(binDir)) {
- var candidates = fs.readdirSync(binDir).filter(function(p) {
- // Need to choose between release and debug .apk.
- return path.extname(p) == '.apk';
- }).map(function(p) {
- p = path.join(binDir, 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;
- });
- if (candidates.length === 0) {
- console.error('ERROR : No .apk found in ' + binDir + ' directory');
- process.exit(2);
- }
- console.log('Using apk: ' + candidates[0].p);
- return candidates[0].p;
- } else {
- console.error('ERROR : unable to find project ' + binDir + ' directory, could not locate .apk');
+module.exports.get_apk = function(build_type) {
+ var outputDir = path.join(ROOT, 'out');
+ var candidates = find_files(outputDir, function() { return true; });
+ if (candidates.length === 0) {
+ console.error('ERROR : No .apk found in ' + outputDir + ' directory');
process.exit(2);
}
-}
+ console.log('Using apk: ' + candidates[0]);
+ return candidates[0];
+};
module.exports.help = function() {
console.log('Usage: ' + path.relative(process.cwd(), path.join(ROOT, 'cordova', 'build')) + ' [build_type]');
console.log('Build Types : ');
- console.log(' \'--debug\': Default build, will build project in using ant debug');
- console.log(' \'--release\': will build project using ant release');
+ console.log(' \'--debug\': Default build, will build project in debug mode');
+ console.log(' \'--release\': will build project for release');
+ console.log(' \'--ant\': Default build, will build project with ant');
+ console.log(' \'--gradle\': will build project with gradle');
console.log(' \'--nobuild\': will skip build process (can be used with run command)');
process.exit(0);
-}
+};