You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cordova.apache.org by ag...@apache.org on 2014/08/19 18:03:12 UTC

[06/19] 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/4.0.x
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);
-}
+};