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:07 UTC

[01/19] android commit: Change in test's AndroidManifest.xml needed for the test to run properly

Repository: cordova-android
Updated Branches:
  refs/heads/4.0.x a7ccb9243 -> 4c1942e3f


Change in test's AndroidManifest.xml needed for the test to run properly


Project: http://git-wip-us.apache.org/repos/asf/cordova-android/repo
Commit: http://git-wip-us.apache.org/repos/asf/cordova-android/commit/9c239804
Tree: http://git-wip-us.apache.org/repos/asf/cordova-android/tree/9c239804
Diff: http://git-wip-us.apache.org/repos/asf/cordova-android/diff/9c239804

Branch: refs/heads/4.0.x
Commit: 9c239804d3ab8fdbbbc76808eef2f50cc113516b
Parents: 320e31b
Author: Joe Bowser <bo...@apache.org>
Authored: Thu Aug 14 14:29:54 2014 -0700
Committer: Joe Bowser <bo...@apache.org>
Committed: Thu Aug 14 14:29:54 2014 -0700

----------------------------------------------------------------------
 test/src/org/apache/cordova/test/junit/IntentUriOverrideTest.java | 1 +
 1 file changed, 1 insertion(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cordova-android/blob/9c239804/test/src/org/apache/cordova/test/junit/IntentUriOverrideTest.java
----------------------------------------------------------------------
diff --git a/test/src/org/apache/cordova/test/junit/IntentUriOverrideTest.java b/test/src/org/apache/cordova/test/junit/IntentUriOverrideTest.java
index aea06af..381e0ed 100644
--- a/test/src/org/apache/cordova/test/junit/IntentUriOverrideTest.java
+++ b/test/src/org/apache/cordova/test/junit/IntentUriOverrideTest.java
@@ -25,6 +25,7 @@ public class IntentUriOverrideTest extends ActivityInstrumentationTestCase2<Sabo
     private String BAD_URL = "file:///sdcard/download/wl-exploit.htm";
 
 
+    @SuppressWarnings("deprecation")
     public IntentUriOverrideTest()
     {
         super("org.apache.cordova.test",SabotagedActivity.class);


[18/19] android commit: CB-7335 Add a .gitignore to android project template

Posted by ag...@apache.org.
CB-7335 Add a .gitignore to android project template


Project: http://git-wip-us.apache.org/repos/asf/cordova-android/repo
Commit: http://git-wip-us.apache.org/repos/asf/cordova-android/commit/71e72f21
Tree: http://git-wip-us.apache.org/repos/asf/cordova-android/tree/71e72f21
Diff: http://git-wip-us.apache.org/repos/asf/cordova-android/diff/71e72f21

Branch: refs/heads/4.0.x
Commit: 71e72f215dd7472ac2be4aabd0154b41a24df6e5
Parents: 58cdfd8
Author: Andrew Grieve <ag...@chromium.org>
Authored: Tue Aug 19 11:59:18 2014 -0400
Committer: Andrew Grieve <ag...@chromium.org>
Committed: Tue Aug 19 11:59:18 2014 -0400

----------------------------------------------------------------------
 bin/lib/create.js               |  1 +
 bin/templates/project/gitignore | 14 ++++++++++++++
 2 files changed, 15 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cordova-android/blob/71e72f21/bin/lib/create.js
----------------------------------------------------------------------
diff --git a/bin/lib/create.js b/bin/lib/create.js
index 13447da..0e929e3 100755
--- a/bin/lib/create.js
+++ b/bin/lib/create.js
@@ -246,6 +246,7 @@ exports.createProject = function(project_path, package_name, project_name, proje
             shell.cp('-r', path.join(project_template_dir, 'assets'), project_path);
             shell.cp('-r', path.join(project_template_dir, 'res'), project_path);
             shell.cp('-r', path.join(ROOT, 'framework', 'res', 'xml'), path.join(project_path, 'res'));
+            shell.cp(path.join(project_template_dir, 'gitignore'), path.join(project_path, '.gitignore'));
 
             // Manually create directories that would be empty within the template (since git doesn't track directories).
             shell.mkdir(path.join(project_path, 'libs'));

http://git-wip-us.apache.org/repos/asf/cordova-android/blob/71e72f21/bin/templates/project/gitignore
----------------------------------------------------------------------
diff --git a/bin/templates/project/gitignore b/bin/templates/project/gitignore
new file mode 100644
index 0000000..a1c8ff7
--- /dev/null
+++ b/bin/templates/project/gitignore
@@ -0,0 +1,14 @@
+# Non-project-specific build files:
+build.xml
+local.properties
+/gradlew
+/gradlew.bat
+/gradle
+# Ant builds
+ant-built
+ant-gen
+# Eclipse builds
+gen
+out
+# Gradle builds
+/build


[10/19] android commit: CB-3445 Speed up gradle building (incremental builds go from 10s -> 1.5s for me)

Posted by ag...@apache.org.
CB-3445 Speed up gradle building (incremental builds go from 10s -> 1.5s for me)

Biggest win is disabling the linter.


Project: http://git-wip-us.apache.org/repos/asf/cordova-android/repo
Commit: http://git-wip-us.apache.org/repos/asf/cordova-android/commit/effffcba
Tree: http://git-wip-us.apache.org/repos/asf/cordova-android/tree/effffcba
Diff: http://git-wip-us.apache.org/repos/asf/cordova-android/diff/effffcba

Branch: refs/heads/4.0.x
Commit: effffcba1db5aa847b2dc9d382d8680f6873ce29
Parents: 404ce8b
Author: Andrew Grieve <ag...@chromium.org>
Authored: Mon Aug 18 14:16:27 2014 -0400
Committer: Andrew Grieve <ag...@chromium.org>
Committed: Mon Aug 18 14:17:34 2014 -0400

----------------------------------------------------------------------
 bin/templates/cordova/lib/build.js | 15 +++++++++++++++
 1 file changed, 15 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cordova-android/blob/effffcba/bin/templates/cordova/lib/build.js
----------------------------------------------------------------------
diff --git a/bin/templates/cordova/lib/build.js b/bin/templates/cordova/lib/build.js
index 3d0ae87..1f0cd23 100644
--- a/bin/templates/cordova/lib/build.js
+++ b/bin/templates/cordova/lib/build.js
@@ -102,7 +102,22 @@ module.exports.builders = {
     },
     gradle: {
         getArgs: function(cmd) {
+            var lintSteps = [
+                'lint',
+                'lintVitalRelease',
+                'compileLint',
+                'copyReleaseLint',
+                'copyDebugLint'
+            ];
             var args = [cmd, '-b', path.join(ROOT, 'build.gradle')];
+            // 10 seconds -> 6 seconds
+            args.push('-Dorg.gradle.daemon=true');
+            // Excluding lint: 6s-> 1.6s
+            for (var i = 0; i < lintSteps.length; ++i) {
+                args.push('-x', lintSteps[i]);
+            }
+            // Shaves another 100ms, but produces a "try at own risk" warning. Not worth it (yet):
+            // args.push('-Dorg.gradle.parallel=true');
             return args;
         },
 


[16/19] android commit: CB-7330 Don't run "android update" during creation

Posted by ag...@apache.org.
CB-7330 Don't run "android update" during creation

Instead, have the build script copy do the equivalent logic on each
build.

Advantages:
- Scripts run much faster
- No more duplicate CordovaLib entries in project.properties
- Building is more independent from create/update script (more robust)


Project: http://git-wip-us.apache.org/repos/asf/cordova-android/repo
Commit: http://git-wip-us.apache.org/repos/asf/cordova-android/commit/dfa66b9d
Tree: http://git-wip-us.apache.org/repos/asf/cordova-android/tree/dfa66b9d
Diff: http://git-wip-us.apache.org/repos/asf/cordova-android/diff/dfa66b9d

Branch: refs/heads/4.0.x
Commit: dfa66b9dd44e52e96fc2c8b4d1c8661c8b0074bb
Parents: d56ea25
Author: Andrew Grieve <ag...@chromium.org>
Authored: Mon Aug 18 23:21:26 2014 -0400
Committer: Andrew Grieve <ag...@chromium.org>
Committed: Mon Aug 18 23:24:29 2014 -0400

----------------------------------------------------------------------
 bin/lib/check_reqs.js                    | 68 ++++++++++++---------
 bin/lib/create.js                        | 51 ++++++++++++----
 bin/templates/cordova/lib/build.js       | 87 ++++++++++++++++++++-------
 bin/templates/project/project.properties | 15 +++++
 4 files changed, 159 insertions(+), 62 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cordova-android/blob/dfa66b9d/bin/lib/check_reqs.js
----------------------------------------------------------------------
diff --git a/bin/lib/check_reqs.js b/bin/lib/check_reqs.js
index 7794594..40f3ade 100644
--- a/bin/lib/check_reqs.js
+++ b/bin/lib/check_reqs.js
@@ -67,7 +67,18 @@ module.exports.get_target = function() {
 // Returns a promise. Called only by build and clean commands.
 module.exports.check_ant = function() {
     return tryCommand('ant -version', 'Failed to run "ant -version", make sure you have ant installed and added to your PATH.');
-}
+};
+
+// Returns a promise. Called only by build and clean commands.
+module.exports.check_gradle = function() {
+    var sdkDir = process.env['ANDROID_HOME'];
+    var wrapperDir = path.join(sdkDir, 'tools', 'templates', 'gradle', 'wrapper');
+    if (!fs.existsSync(wrapperDir)) {
+        return Q.reject(new Error('Could not find gradle wrapper within android sdk. Might need to update your Android SDK.\n' +
+            'Looked here: ' + wrapperDir));
+    }
+    return Q.when();
+};
 
 // Returns a promise.
 module.exports.check_java = function() {
@@ -126,41 +137,44 @@ module.exports.check_java = function() {
 
 // Returns a promise.
 module.exports.check_android = function() {
-    var androidCmdPath = forgivingWhichSync('android');
-    var adbInPath = !!forgivingWhichSync('adb');
-    var hasAndroidHome = !!process.env['ANDROID_HOME'] && fs.existsSync(process.env['ANDROID_HOME']);
-    if (hasAndroidHome && !androidCmdPath) {
-        process.env['PATH'] += path.delimiter + path.join(process.env['ANDROID_HOME'], 'tools');
-    }
-    if (androidCmdPath && !hasAndroidHome) {
-        var parentDir = path.dirname(androidCmdPath);
-        if (path.basename(parentDir) == 'tools') {
-            process.env['ANDROID_HOME'] = path.dirname(parentDir);
-            hasAndroidHome = true;
+    return Q().then(function() {
+        var androidCmdPath = forgivingWhichSync('android');
+        var adbInPath = !!forgivingWhichSync('adb');
+        var hasAndroidHome = !!process.env['ANDROID_HOME'] && fs.existsSync(process.env['ANDROID_HOME']);
+        if (hasAndroidHome && !androidCmdPath) {
+            process.env['PATH'] += path.delimiter + path.join(process.env['ANDROID_HOME'], 'tools');
         }
-    }
-    if (hasAndroidHome && !adbInPath) {
-        process.env['PATH'] += path.delimiter + path.join(process.env['ANDROID_HOME'], 'platform-tools');
-    }
-
-    var valid_target = this.get_target();
-    var msg = 'Failed to run "android". Make sure you have the latest Android SDK installed, and that the "android" command (inside the tools/ folder) is added to your PATH.';
-    return tryCommand('android list targets', msg)
-    .then(function(output) {
-        if (!output.match(valid_target)) {
-            return Q.reject(new Error('Please install Android target ' + valid_target.split('-')[1] +
-                ' (the Android newest SDK). Make sure you have the latest Android tools installed as well.' +
-                ' Run "android" from your command-line to install/update any missing SDKs or tools.'));
+        if (androidCmdPath && !hasAndroidHome) {
+            var parentDir = path.dirname(androidCmdPath);
+            if (path.basename(parentDir) == 'tools') {
+                process.env['ANDROID_HOME'] = path.dirname(parentDir);
+                hasAndroidHome = true;
+            }
+        }
+        if (hasAndroidHome && !adbInPath) {
+            process.env['PATH'] += path.delimiter + path.join(process.env['ANDROID_HOME'], 'platform-tools');
         }
-    }).then(function() {
         if (!process.env['ANDROID_HOME']) {
             throw new Error('ANDROID_HOME is not set and "android" command not in your PATH. You must fulfill at least one of these conditions.');
         }
         if (!fs.existsSync(process.env['ANDROID_HOME'])) {
             throw new Error('ANDROID_HOME is set to a non-existant path: ' + process.env['ANDROID_HOME']);
         }
+        // Check that the target sdk level is installed.
+        return module.exports.check_android_target(module.exports.get_target());
     });
-}
+};
+
+module.exports.check_android_target = function(valid_target) {
+    var msg = 'Failed to run "android". Make sure you have the latest Android SDK installed, and that the "android" command (inside the tools/ folder) is added to your PATH.';
+    return tryCommand('android list targets', msg)
+    .then(function(output) {
+        if (!output.match(valid_target)) {
+            throw new Error('Please install Android target "' + valid_target + '".\n' +
+                'Hint: Run "android" from your command-line to open the SDK manager.');
+        }
+    });
+};
 
 // Returns a promise.
 module.exports.run = function() {

http://git-wip-us.apache.org/repos/asf/cordova-android/blob/dfa66b9d/bin/lib/create.js
----------------------------------------------------------------------
diff --git a/bin/lib/create.js b/bin/lib/create.js
index cc42f19..13447da 100755
--- a/bin/lib/create.js
+++ b/bin/lib/create.js
@@ -83,9 +83,38 @@ function copyJsAndLibrary(projectPath, shared, projectName) {
     }
 }
 
-function runAndroidUpdate(projectPath, target_api, shared) {
-    var targetFrameworkDir = getFrameworkDir(projectPath, shared);
-    return exec('android update project --subprojects --path "' + projectPath + '" --target ' + target_api + ' --library "' + path.relative(projectPath, targetFrameworkDir) + '"');
+function extractSubProjectPaths(data) {
+    var ret = {};
+    var r = /^\s*android\.library\.reference\.\d+=(.*)(?:\s|$)/mg
+    var m;
+    while (m = r.exec(data)) {
+        ret[m[1]] = 1;
+    }
+    return Object.keys(ret);
+}
+
+function writeProjectProperties(projectPath, target_api, shared) {
+    var dstPath = path.join(projectPath, 'project.properties');
+    var templatePath = path.join(ROOT, 'bin', 'templates', 'project', 'project.properties');
+    var srcPath = fs.existsSync(dstPath) ? dstPath : templatePath;
+    var data = fs.readFileSync(srcPath, 'utf8');
+    data = data.replace(/^target=.*/m, 'target=' + target_api);
+    var subProjects = extractSubProjectPaths(data);
+    subProjects = subProjects.filter(function(p) {
+        return !(/^CordovaLib$/m.exec(p) ||
+                 /[\\\/]cordova-android[\\\/]framework$/m.exec(p) ||
+                 /^(\.\.[\\\/])+framework$/m.exec(p)
+                 );
+    });
+    subProjects.unshift(shared ? path.relative(projectPath, path.join(ROOT, 'framework')) : 'CordovaLib');
+    data = data.replace(/^\s*android\.library\.reference\.\d+=.*\n/mg, '');
+    if (!/\n$/.exec(data)) {
+        data += '\n';
+    }
+    for (var i = 0; i < subProjects.length; ++i) {
+        data += 'android.library.reference.' + (i+1) + '=' + subProjects[i] + '\n';
+    }
+    fs.writeFileSync(dstPath, data);
 }
 
 function copyBuildRules(projectPath) {
@@ -251,7 +280,7 @@ exports.createProject = function(project_path, package_name, project_name, proje
             copyBuildRules(project_path);
         });
         // Link it to local android install.
-        return runAndroidUpdate(project_path, target_api, use_shared_project);
+        writeProjectProperties(project_path, target_api);
     }).then(function() {
         console.log('Project successfully created.');
     });
@@ -274,22 +303,20 @@ function extractProjectNameFromManifest(projectPath) {
 }
 
 // Returns a promise.
-exports.updateProject = function(projectPath) {
-    var version = fs.readFileSync(path.join(ROOT, 'VERSION'), 'utf-8').trim();
+exports.updateProject = function(projectPath, shared) {
+    var newVersion = fs.readFileSync(path.join(ROOT, 'VERSION'), 'utf-8').trim();
     // Check that requirements are met and proper targets are installed
     return check_reqs.run()
     .then(function() {
         var projectName = extractProjectNameFromManifest(projectPath);
         var target_api = check_reqs.get_target();
-        copyJsAndLibrary(projectPath, false, projectName);
+        copyJsAndLibrary(projectPath, shared, projectName);
         copyScripts(projectPath);
         copyBuildRules(projectPath);
         removeDebuggableFromManifest(projectPath);
-        return runAndroidUpdate(projectPath, target_api, false)
-        .then(function() {
-            console.log('Android project is now at version ' + version);
-            console.log('If you updated from a pre-3.2.0 version and use an IDE, we now require that you import the "CordovaLib" library project.');
-        });
+        writeProjectProperties(projectPath, target_api, shared);
+        console.log('Android project is now at version ' + newVersion);
+        console.log('If you updated from a pre-3.2.0 version and use an IDE, we now require that you import the "CordovaLib" library project.');
     });
 };
 

http://git-wip-us.apache.org/repos/asf/cordova-android/blob/dfa66b9d/bin/templates/cordova/lib/build.js
----------------------------------------------------------------------
diff --git a/bin/templates/cordova/lib/build.js b/bin/templates/cordova/lib/build.js
index 071990f..7830bd6 100644
--- a/bin/templates/cordova/lib/build.js
+++ b/bin/templates/cordova/lib/build.js
@@ -27,9 +27,9 @@ var shell   = require('shelljs'),
     ROOT    = path.join(__dirname, '..', '..');
 var check_reqs = require('./check_reqs');
 
-// Globals
-var build_type,
-    build_method;
+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) {
     if (fs.existsSync(directory)) {
@@ -51,22 +51,25 @@ function hasCustomRules() {
     return fs.existsSync(path.join(ROOT, 'custom_rules.xml'));
 }
 
-// Copy the gradle wrapper files on each build so that:
-// A) We don't require the Android SDK at project creation time, and
-// B) So that they are always up-to-date.
-function copyGradleWrapper() {
-    var projectPath = ROOT;
-    // check_reqs ensures that this is set.
-    var sdkDir = process.env['ANDROID_HOME'];
-    var wrapperDir = path.join(sdkDir, 'tools', 'templates', 'gradle', 'wrapper');
-    if (process.platform == 'win32') {
-        shell.cp('-f', path.join(wrapperDir, 'gradlew.bat'), projectPath);
-    } else {
-        shell.cp('-f', path.join(wrapperDir, 'gradlew'), projectPath);
+function extractProjectNameFromManifest(projectPath) {
+    var manifestPath = path.join(projectPath, 'AndroidManifest.xml');
+    var manifestData = fs.readFileSync(manifestPath, 'utf8');
+    var m = /<activity[\s\S]*?android:name\s*=\s*"(.*?)"/i.exec(manifestData);
+    if (!m) {
+        throw new Error('Could not find activity name in ' + manifestPath);
+    }
+    return m[1];
+}
+
+function extractSubProjectPaths() {
+    var data = fs.readFileSync(path.join(ROOT, 'project.properties'), 'utf8');
+    var ret = {};
+    var r = /^\s*android\.library\.reference\.\d+=(.*)(?:\s|$)/mg
+    var m;
+    while (m = r.exec(data)) {
+        ret[m[1]] = 1;
     }
-    shell.rm('-rf', path.join(projectPath, 'gradle', 'wrapper'));
-    shell.mkdir('-p', path.join(projectPath, 'gradle'));
-    shell.cp('-r', path.join(wrapperDir, 'gradle', 'wrapper'), path.join(projectPath, 'gradle'));
+    return Object.keys(ret);
 }
 
 var builders = {
@@ -83,6 +86,23 @@ var builders = {
         prepEnv: function() {
             return check_reqs.check_ant()
             .then(function() {
+                // Copy in build.xml on each build so that:
+                // A) we don't require the Android SDK at project creation time, and
+                // B) we always use the SDK's latest version of it.
+                var sdkDir = process.env['ANDROID_HOME'];
+                var buildTemplate = fs.readFileSync(path.join(sdkDir, 'tools', 'lib', 'build.template'), 'utf8');
+                function writeBuildXml(projectPath) {
+                    var newData = buildTemplate.replace('PROJECT_NAME', extractProjectNameFromManifest(ROOT));
+                    fs.writeFileSync(path.join(projectPath, 'build.xml'), newData);
+                    if (!fs.existsSync(path.join(projectPath, 'local.properties'))) {
+                        fs.writeFileSync(path.join(projectPath, 'local.properties'), LOCAL_PROPERTIES_TEMPLATE);
+                    }
+                }
+                var subProjects = extractSubProjectPaths();
+                writeBuildXml(ROOT);
+                for (var i = 0; i < subProjects.length; ++i) {
+                    writeBuildXml(path.join(ROOT, subProjects[i]));
+                }
             });
         },
 
@@ -156,7 +176,24 @@ var builders = {
         },
 
         prepEnv: function() {
-            return Q();
+            return check_reqs.check_gradle()
+            .then(function() {
+                // Copy the gradle wrapper on each build so that:
+                // A) we don't require the Android SDK at project creation time, and
+                // B) we always use the SDK's latest version of it.
+                var projectPath = ROOT;
+                // check_reqs ensures that this is set.
+                var sdkDir = process.env['ANDROID_HOME'];
+                var wrapperDir = path.join(sdkDir, 'tools', 'templates', 'gradle', 'wrapper');
+                if (process.platform == 'win32') {
+                    shell.cp('-f', path.join(wrapperDir, 'gradlew.bat'), projectPath);
+                } else {
+                    shell.cp('-f', path.join(wrapperDir, 'gradlew'), projectPath);
+                }
+                shell.rm('-rf', path.join(projectPath, 'gradle', 'wrapper'));
+                shell.mkdir('-p', path.join(projectPath, 'gradle'));
+                shell.cp('-r', path.join(wrapperDir, 'gradle', 'wrapper'), path.join(projectPath, 'gradle'));
+            });
         },
 
         /*
@@ -182,8 +219,6 @@ var builders = {
             copyGradleWrapper();
             return Q().then(function() {
                 return spawn(wrapper, args);
-            }).then(function() {
-                return builder.getOutputFiles(build_type);
             });
         },
 
@@ -261,9 +296,11 @@ function parseOpts(options) {
 module.exports.runClean = function(options) {
     var opts = parseOpts(options);
     var builder = builders[opts.buildMethod];
-    return builder.prepEnv(opts.buildType)
+    return builder.prepEnv()
     .then(function() {
         return builder.clean();
+    }).then(function() {
+        shell.rm('-rf', path.join(ROOT, 'out'));
     });
 };
 
@@ -275,10 +312,13 @@ module.exports.run = function(options) {
     var opts = parseOpts(options);
 
     var builder = builders[opts.buildMethod];
-    return builder.prepEnv(opts.buildType)
+    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);
         for (var i=0; i < apkFiles.length; ++i) {
@@ -299,6 +339,7 @@ module.exports.get_apk = function(build_type) {
         console.error('ERROR : No .apk found in ' + outputDir + ' directory');
         process.exit(2);
     }
+    // TODO: Use build_type here.
     console.log('Using apk: ' + candidates[0]);
     return candidates[0];
 };

http://git-wip-us.apache.org/repos/asf/cordova-android/blob/dfa66b9d/bin/templates/project/project.properties
----------------------------------------------------------------------
diff --git a/bin/templates/project/project.properties b/bin/templates/project/project.properties
new file mode 100644
index 0000000..ddd3a06
--- /dev/null
+++ b/bin/templates/project/project.properties
@@ -0,0 +1,15 @@
+# This file is automatically generated by Android Tools.
+# Do not modify this file -- YOUR CHANGES WILL BE ERASED!
+#
+# This file must be checked in Version Control Systems.
+#
+# To customize properties used by the Ant build system edit
+# "ant.properties", and override values to adapt the script to your
+# project structure.
+#
+# To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home):
+#proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt
+
+android.library.reference.1=CordovaLib
+# Project target.
+target=This_gets_replaced


[13/19] android commit: CB-3445 Copy gradle wrapper in build instead of create

Posted by ag...@apache.org.
CB-3445 Copy gradle wrapper in build instead of create

This should play nicer with updates to the android SDK.


Project: http://git-wip-us.apache.org/repos/asf/cordova-android/repo
Commit: http://git-wip-us.apache.org/repos/asf/cordova-android/commit/ca8bb75b
Tree: http://git-wip-us.apache.org/repos/asf/cordova-android/tree/ca8bb75b
Diff: http://git-wip-us.apache.org/repos/asf/cordova-android/diff/ca8bb75b

Branch: refs/heads/4.0.x
Commit: ca8bb75b4021566667743ac2b8a3da268b05d6be
Parents: 36eab71
Author: Andrew Grieve <ag...@chromium.org>
Authored: Mon Aug 18 14:51:10 2014 -0400
Committer: Andrew Grieve <ag...@chromium.org>
Committed: Mon Aug 18 14:51:40 2014 -0400

----------------------------------------------------------------------
 bin/lib/check_reqs.js              | 17 -----------------
 bin/lib/create.js                  | 17 -----------------
 bin/templates/cordova/lib/build.js | 19 +++++++++++++++++++
 3 files changed, 19 insertions(+), 34 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cordova-android/blob/ca8bb75b/bin/lib/check_reqs.js
----------------------------------------------------------------------
diff --git a/bin/lib/check_reqs.js b/bin/lib/check_reqs.js
index 6f20c19..61bd449 100644
--- a/bin/lib/check_reqs.js
+++ b/bin/lib/check_reqs.js
@@ -64,23 +64,6 @@ module.exports.get_target = function() {
     }
 }
 
-// Returns a promise.
-module.exports.sdk_dir = function() {
-    var d = Q.defer();
-    which('android', function(err, path) {
-        if (err) {
-            d.reject(new Error('ERROR: Cannot find Android SDK. android command not found.'));
-        } else {
-            var toolsDir = path.substring(0, path.lastIndexOf('/'));
-            if (toolsDir.substring(toolsDir.length-6) != "/tools") {
-                d.reject(new Error('ERROR: Cannot find Android SDK. android command not found in tools dir.'));
-            }
-            d.resolve(toolsDir.substring(0, toolsDir.length-6));
-        }
-    });
-    return d.promise;
-};
-
 // Returns a promise. Called only by build and clean commands.
 module.exports.check_ant = function() {
     return tryCommand('ant -version', 'Failed to run "ant -version", make sure you have ant installed and added to your PATH.');

http://git-wip-us.apache.org/repos/asf/cordova-android/blob/ca8bb75b/bin/lib/create.js
----------------------------------------------------------------------
diff --git a/bin/lib/create.js b/bin/lib/create.js
index d1f43e6..cc42f19 100755
--- a/bin/lib/create.js
+++ b/bin/lib/create.js
@@ -111,13 +111,6 @@ function copyScripts(projectPath) {
     shell.cp(path.join(ROOT, 'bin', 'lib', 'android_sdk_version.js'), path.join(projectPath, 'cordova', 'lib', 'android_sdk_version.js'));
 }
 
-function copyGradleWrapper(sdkPath, projectPath) {
-    var wrapperDir = path.join(sdkPath, 'tools', 'templates','gradle','wrapper');
-    shell.cp(path.join(wrapperDir, 'gradlew'), projectPath);
-    shell.cp(path.join(wrapperDir, 'gradlew.bat'), projectPath);
-    shell.cp('-r', path.join(wrapperDir, 'gradle'), projectPath);
-}
-
 /**
  * Test whether a package name is acceptable for use as an android project.
  * Returns a promise, fulfilled if the package name is acceptable; rejected
@@ -225,16 +218,6 @@ exports.createProject = function(project_path, package_name, project_name, proje
             shell.cp('-r', path.join(project_template_dir, 'res'), project_path);
             shell.cp('-r', path.join(ROOT, 'framework', 'res', 'xml'), path.join(project_path, 'res'));
 
-            shell.cp('-f', path.join(project_template_dir, 'build.gradle'), project_path);
-            shell.cp('-f', path.join(project_template_dir, 'libraries.gradle'), project_path);
-            shell.cp('-f', path.join(project_template_dir, 'settings.gradle'), project_path);
-            check_reqs.sdk_dir().then(function(dir) {
-                console.log("Copying Gradle wrapper from " + dir);
-                copyGradleWrapper(dir, project_path);
-            }).catch(function(err) {
-                console.log("Cannot find Android SDK. Not installing Gradle wrapper.");
-            });
-
             // Manually create directories that would be empty within the template (since git doesn't track directories).
             shell.mkdir(path.join(project_path, 'libs'));
 

http://git-wip-us.apache.org/repos/asf/cordova-android/blob/ca8bb75b/bin/templates/cordova/lib/build.js
----------------------------------------------------------------------
diff --git a/bin/templates/cordova/lib/build.js b/bin/templates/cordova/lib/build.js
index 1f0cd23..80e2282 100644
--- a/bin/templates/cordova/lib/build.js
+++ b/bin/templates/cordova/lib/build.js
@@ -51,6 +51,24 @@ function hasCustomRules() {
     return fs.existsSync(path.join(ROOT, 'custom_rules.xml'));
 }
 
+// Copy the gradle wrapper files on each build so that:
+// A) We don't require the Android SDK at project creation time, and
+// B) So that they are always up-to-date.
+function copyGradleWrapper() {
+    var projectPath = ROOT;
+    // check_reqs ensures that this is set.
+    var sdkDir = process.env['ANDROID_HOME'];
+    var wrapperDir = path.join(sdkDir, 'tools', 'templates', 'gradle', 'wrapper');
+    if (process.platform == 'win32') {
+        shell.cp('-f', path.join(wrapperDir, 'gradlew.bat'), projectPath);
+    } else {
+        shell.cp('-f', path.join(wrapperDir, 'gradlew'), projectPath);
+    }
+    shell.rm('-rf', path.join(projectPath, 'gradle', 'wrapper'));
+    shell.mkdir('-p', path.join(projectPath, 'gradle'));
+    shell.cp('-r', path.join(wrapperDir, 'gradle', 'wrapper'), path.join(projectPath, 'gradle'));
+}
+
 module.exports.builders = {
     ant: {
         getArgs: function(cmd) {
@@ -129,6 +147,7 @@ module.exports.builders = {
             var builder = this;
             var wrapper = path.join(ROOT, 'gradlew');
             var args = builder.getArgs('build');
+            copyGradleWrapper();
             return Q().then(function() {
                 return spawn(wrapper, args);
             }).then(function() {


[04/19] android commit: CB-7321 Don't require ant for create script

Posted by ag...@apache.org.
CB-7321 Don't require ant for create script


Project: http://git-wip-us.apache.org/repos/asf/cordova-android/repo
Commit: http://git-wip-us.apache.org/repos/asf/cordova-android/commit/95aa5c9f
Tree: http://git-wip-us.apache.org/repos/asf/cordova-android/tree/95aa5c9f
Diff: http://git-wip-us.apache.org/repos/asf/cordova-android/diff/95aa5c9f

Branch: refs/heads/4.0.x
Commit: 95aa5c9f1c45b79151dc9650447faeaf12a75d96
Parents: 4319447
Author: Andrew Grieve <ag...@chromium.org>
Authored: Fri Aug 15 13:58:53 2014 -0400
Committer: Andrew Grieve <ag...@chromium.org>
Committed: Fri Aug 15 13:58:53 2014 -0400

----------------------------------------------------------------------
 bin/lib/check_reqs.js              | 4 ++--
 bin/templates/cordova/lib/build.js | 6 +++++-
 bin/templates/cordova/lib/clean.js | 6 +++++-
 3 files changed, 12 insertions(+), 4 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cordova-android/blob/95aa5c9f/bin/lib/check_reqs.js
----------------------------------------------------------------------
diff --git a/bin/lib/check_reqs.js b/bin/lib/check_reqs.js
index 17a3bd4..db45474 100644
--- a/bin/lib/check_reqs.js
+++ b/bin/lib/check_reqs.js
@@ -55,7 +55,7 @@ module.exports.get_target = function() {
     }
 }
 
-// Returns a promise.
+// Returns a promise. Called only by build and clean commands.
 module.exports.check_ant = function() {
     return tryCommand('ant -version', 'Failed to run "ant -version", make sure you have ant installed and added to your PATH.');
 }
@@ -123,6 +123,6 @@ module.exports.check_android = function() {
 
 // Returns a promise.
 module.exports.run = function() {
-    return Q.all([this.check_ant(), this.check_java(), this.check_android()]);
+    return Q.all([this.check_java(), this.check_android()]);
 }
 

http://git-wip-us.apache.org/repos/asf/cordova-android/blob/95aa5c9f/bin/templates/cordova/lib/build.js
----------------------------------------------------------------------
diff --git a/bin/templates/cordova/lib/build.js b/bin/templates/cordova/lib/build.js
index 6336ecf..5134f41 100644
--- a/bin/templates/cordova/lib/build.js
+++ b/bin/templates/cordova/lib/build.js
@@ -25,6 +25,7 @@ var shell   = require('shelljs'),
     path    = require('path'),
     fs      = require('fs'),
     ROOT    = path.join(__dirname, '..', '..');
+var check_reqs = require('./check_reqs');
 
 
 function hasCustomRules() {
@@ -60,9 +61,12 @@ module.exports.run = function(build_type) {
             return Q.reject('Build option \'' + build_type + '\' not recognized.');
     }
     // Without our custom_rules.xml, we need to clean before building.
-    var ret = Q();
+    var ret;
     if (!hasCustomRules()) {
+        // clean will call check_ant() for us.
         ret = require('./clean').run();
+    } else {
+        ret = check_reqs.check_ant();
     }
     return ret.then(function() {
         return spawn('ant', args);

http://git-wip-us.apache.org/repos/asf/cordova-android/blob/95aa5c9f/bin/templates/cordova/lib/clean.js
----------------------------------------------------------------------
diff --git a/bin/templates/cordova/lib/clean.js b/bin/templates/cordova/lib/clean.js
index 0a2e0ce..f215829 100644
--- a/bin/templates/cordova/lib/clean.js
+++ b/bin/templates/cordova/lib/clean.js
@@ -22,6 +22,7 @@
 var build = require('./build'),
     spawn = require('./spawn'),
     path  = require('path');
+var check_reqs = require('./check_reqs');
 
 /*
  * Cleans the project using ant
@@ -29,7 +30,10 @@ var build = require('./build'),
  */
 module.exports.run = function() {
     var args = build.getAntArgs('clean');
-    return spawn('ant', args);
+    return check_reqs.check_ant()
+    .then(function() {
+        return spawn('ant', args);
+    });
 }
 
 module.exports.help = function() {


[07/19] android commit: CB-3445: Add an initial set of Gradle build scripts

Posted by ag...@apache.org.
CB-3445: Add an initial set of Gradle build scripts

These scripts will build an android project, in debug and release mode.
They also support additional library projects, such as Crosswalk, being
added to libraries.gradle (and settings.gradle). A flag can be set in
libraries.gradle to enable multi-architecture builds.


Project: http://git-wip-us.apache.org/repos/asf/cordova-android/repo
Commit: http://git-wip-us.apache.org/repos/asf/cordova-android/commit/8aa813b8
Tree: http://git-wip-us.apache.org/repos/asf/cordova-android/tree/8aa813b8
Diff: http://git-wip-us.apache.org/repos/asf/cordova-android/diff/8aa813b8

Branch: refs/heads/4.0.x
Commit: 8aa813b8625ec771e05977ca366220fb8f2388e2
Parents: 95aa5c9
Author: Ian Clelland <ic...@chromium.org>
Authored: Mon Aug 18 09:40:33 2014 -0400
Committer: Ian Clelland <ic...@chromium.org>
Committed: Mon Aug 18 09:48:31 2014 -0400

----------------------------------------------------------------------
 bin/lib/create.js                               |  12 +-
 bin/templates/project/build.gradle              |  81 +++++++++
 .../gradle/wrapper/gradle-wrapper.properties    |   6 +
 bin/templates/project/gradlew                   | 164 +++++++++++++++++++
 bin/templates/project/gradlew.bat               |  90 ++++++++++
 bin/templates/project/libraries.gradle          |   7 +
 bin/templates/project/settings.gradle           |   1 +
 framework/build.gradle                          |  33 ++++
 8 files changed, 393 insertions(+), 1 deletion(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cordova-android/blob/8aa813b8/bin/lib/create.js
----------------------------------------------------------------------
diff --git a/bin/lib/create.js b/bin/lib/create.js
index 7834581..ad3c21c 100755
--- a/bin/lib/create.js
+++ b/bin/lib/create.js
@@ -71,6 +71,7 @@ function copyJsAndLibrary(projectPath, shared, projectName) {
         shell.mkdir('-p', nestedCordovaLibPath);
         shell.cp('-f', path.join(ROOT, 'framework', 'AndroidManifest.xml'), nestedCordovaLibPath);
         shell.cp('-f', path.join(ROOT, 'framework', 'project.properties'), nestedCordovaLibPath);
+        shell.cp('-f', path.join(ROOT, 'framework', 'build.gradle'), nestedCordovaLibPath);
         shell.cp('-r', path.join(ROOT, 'framework', 'src'), nestedCordovaLibPath);
         // Create an eclipse project file and set the name of it to something unique.
         // Without this, you can't import multiple CordovaLib projects into the same workspace.
@@ -89,7 +90,9 @@ function runAndroidUpdate(projectPath, target_api, shared) {
 
 function copyAntRules(projectPath) {
     var srcDir = path.join(ROOT, 'bin', 'templates', 'project');
-    shell.cp('-f', path.join(srcDir, 'custom_rules.xml'), projectPath);
+    if (fs.existsSync(path.join(srcDir, 'custom_rules.xml'))) {
+        shell.cp('-f', path.join(srcDir, 'custom_rules.xml'), projectPath);
+    }
 }
 
 function copyScripts(projectPath) {
@@ -213,6 +216,13 @@ exports.createProject = function(project_path, package_name, project_name, proje
             shell.cp('-r', path.join(project_template_dir, 'res'), project_path);
             shell.cp('-r', path.join(ROOT, 'framework', 'res', 'xml'), path.join(project_path, 'res'));
 
+            shell.cp('-f', path.join(project_template_dir, 'build.gradle'), project_path);
+            shell.cp('-f', path.join(project_template_dir, 'libraries.gradle'), project_path);
+            shell.cp('-f', path.join(project_template_dir, 'settings.gradle'), project_path);
+            shell.cp('-f', path.join(project_template_dir, 'gradlew'), project_path);
+            shell.cp('-f', path.join(project_template_dir, 'gradlew.bat'), project_path);
+            shell.cp('-r', path.join(project_template_dir, 'gradle'), project_path);
+
             // Manually create directories that would be empty within the template (since git doesn't track directories).
             shell.mkdir(path.join(project_path, 'libs'));
 

http://git-wip-us.apache.org/repos/asf/cordova-android/blob/8aa813b8/bin/templates/project/build.gradle
----------------------------------------------------------------------
diff --git a/bin/templates/project/build.gradle b/bin/templates/project/build.gradle
new file mode 100644
index 0000000..4b34e48
--- /dev/null
+++ b/bin/templates/project/build.gradle
@@ -0,0 +1,81 @@
+import java.util.regex.Pattern
+
+apply plugin: 'android'
+
+buildscript {
+    repositories {
+        mavenCentral()
+    }
+
+    dependencies {
+        classpath 'com.android.tools.build:gradle:0.10.+'
+    }
+}
+
+ext.multiarch=false
+
+dependencies {
+    compile project(':CordovaLib')
+}
+apply from: 'libraries.gradle'
+
+android {
+    sourceSets {
+        main {
+            manifest.srcFile 'AndroidManifest.xml'
+            java.srcDirs = ['src']
+            resources.srcDirs = ['src']
+            aidl.srcDirs = ['src']
+            renderscript.srcDirs = ['src']
+            res.srcDirs = ['res']
+            assets.srcDirs = ['assets']
+        }
+    }
+
+    defaultConfig {
+        versionCode Integer.parseInt("" + getVersionCodeFromManifest() + "0")
+    }
+
+    compileSdkVersion 19
+    buildToolsVersion "19.0.0"
+
+    if (multiarch) {
+        productFlavors {
+            armv7 {
+                versionCode defaultConfig.versionCode + 2
+                ndk {
+                    abiFilters "armeabi-v7a", ""
+                }
+            }
+            x86 {
+                versionCode defaultConfig.versionCode + 4
+                ndk {
+                    abiFilters "x86", ""
+                }
+            }
+            all {
+                ndk {
+                    abiFilters "all", ""
+                }
+            }
+        }
+    }
+
+    compileOptions {
+        sourceCompatibility JavaVersion.VERSION_1_7
+        targetCompatibility JavaVersion.VERSION_1_7
+    }
+
+}
+
+task wrapper(type: Wrapper) {
+    gradleVersion = '1.12'
+}
+
+def getVersionCodeFromManifest() {
+    def manifestFile = file(android.sourceSets.main.manifest.srcFile)
+    def pattern = Pattern.compile("versionCode=\"(\\d+)\"")
+    def matcher = pattern.matcher(manifestFile.getText())
+    matcher.find()
+    return Integer.parseInt(matcher.group(1))
+}

http://git-wip-us.apache.org/repos/asf/cordova-android/blob/8aa813b8/bin/templates/project/gradle/wrapper/gradle-wrapper.properties
----------------------------------------------------------------------
diff --git a/bin/templates/project/gradle/wrapper/gradle-wrapper.properties b/bin/templates/project/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 0000000..497dc9b
--- /dev/null
+++ b/bin/templates/project/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,6 @@
+#Fri Jun 13 09:52:43 EDT 2014
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-1.12-bin.zip

http://git-wip-us.apache.org/repos/asf/cordova-android/blob/8aa813b8/bin/templates/project/gradlew
----------------------------------------------------------------------
diff --git a/bin/templates/project/gradlew b/bin/templates/project/gradlew
new file mode 100755
index 0000000..91a7e26
--- /dev/null
+++ b/bin/templates/project/gradlew
@@ -0,0 +1,164 @@
+#!/usr/bin/env bash
+
+##############################################################################
+##
+##  Gradle start up script for UN*X
+##
+##############################################################################
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS=""
+
+APP_NAME="Gradle"
+APP_BASE_NAME=`basename "$0"`
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD="maximum"
+
+warn ( ) {
+    echo "$*"
+}
+
+die ( ) {
+    echo
+    echo "$*"
+    echo
+    exit 1
+}
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+case "`uname`" in
+  CYGWIN* )
+    cygwin=true
+    ;;
+  Darwin* )
+    darwin=true
+    ;;
+  MINGW* )
+    msys=true
+    ;;
+esac
+
+# For Cygwin, ensure paths are in UNIX format before anything is touched.
+if $cygwin ; then
+    [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
+fi
+
+# Attempt to set APP_HOME
+# Resolve links: $0 may be a link
+PRG="$0"
+# Need this for relative symlinks.
+while [ -h "$PRG" ] ; do
+    ls=`ls -ld "$PRG"`
+    link=`expr "$ls" : '.*-> \(.*\)$'`
+    if expr "$link" : '/.*' > /dev/null; then
+        PRG="$link"
+    else
+        PRG=`dirname "$PRG"`"/$link"
+    fi
+done
+SAVED="`pwd`"
+cd "`dirname \"$PRG\"`/" >&-
+APP_HOME="`pwd -P`"
+cd "$SAVED" >&-
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+    if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+        # IBM's JDK on AIX uses strange locations for the executables
+        JAVACMD="$JAVA_HOME/jre/sh/java"
+    else
+        JAVACMD="$JAVA_HOME/bin/java"
+    fi
+    if [ ! -x "$JAVACMD" ] ; then
+        die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+    fi
+else
+    JAVACMD="java"
+    which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
+    MAX_FD_LIMIT=`ulimit -H -n`
+    if [ $? -eq 0 ] ; then
+        if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
+            MAX_FD="$MAX_FD_LIMIT"
+        fi
+        ulimit -n $MAX_FD
+        if [ $? -ne 0 ] ; then
+            warn "Could not set maximum file descriptor limit: $MAX_FD"
+        fi
+    else
+        warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
+    fi
+fi
+
+# For Darwin, add options to specify how the application appears in the dock
+if $darwin; then
+    GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
+fi
+
+# For Cygwin, switch paths to Windows format before running java
+if $cygwin ; then
+    APP_HOME=`cygpath --path --mixed "$APP_HOME"`
+    CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
+
+    # We build the pattern for arguments to be converted via cygpath
+    ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
+    SEP=""
+    for dir in $ROOTDIRSRAW ; do
+        ROOTDIRS="$ROOTDIRS$SEP$dir"
+        SEP="|"
+    done
+    OURCYGPATTERN="(^($ROOTDIRS))"
+    # Add a user-defined pattern to the cygpath arguments
+    if [ "$GRADLE_CYGPATTERN" != "" ] ; then
+        OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
+    fi
+    # Now convert the arguments - kludge to limit ourselves to /bin/sh
+    i=0
+    for arg in "$@" ; do
+        CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
+        CHECK2=`echo "$arg"|egrep -c "^-"`                                 ### Determine if an option
+
+        if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then                    ### Added a condition
+            eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
+        else
+            eval `echo args$i`="\"$arg\""
+        fi
+        i=$((i+1))
+    done
+    case $i in
+        (0) set -- ;;
+        (1) set -- "$args0" ;;
+        (2) set -- "$args0" "$args1" ;;
+        (3) set -- "$args0" "$args1" "$args2" ;;
+        (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
+        (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
+        (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
+        (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
+        (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
+        (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
+    esac
+fi
+
+# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
+function splitJvmOpts() {
+    JVM_OPTS=("$@")
+}
+eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
+JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
+
+exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"

http://git-wip-us.apache.org/repos/asf/cordova-android/blob/8aa813b8/bin/templates/project/gradlew.bat
----------------------------------------------------------------------
diff --git a/bin/templates/project/gradlew.bat b/bin/templates/project/gradlew.bat
new file mode 100644
index 0000000..aec9973
--- /dev/null
+++ b/bin/templates/project/gradlew.bat
@@ -0,0 +1,90 @@
+@if "%DEBUG%" == "" @echo off
+@rem ##########################################################################
+@rem
+@rem  Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS=
+
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if "%ERRORLEVEL%" == "0" goto init
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto init
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:init
+@rem Get command-line arguments, handling Windowz variants
+
+if not "%OS%" == "Windows_NT" goto win9xME_args
+if "%@eval[2+2]" == "4" goto 4NT_args
+
+:win9xME_args
+@rem Slurp the command line arguments.
+set CMD_LINE_ARGS=
+set _SKIP=2
+
+:win9xME_args_slurp
+if "x%~1" == "x" goto execute
+
+set CMD_LINE_ARGS=%*
+goto execute
+
+:4NT_args
+@rem Get arguments from the 4NT Shell from JP Software
+set CMD_LINE_ARGS=%$
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
+
+:end
+@rem End local scope for the variables with windows NT shell
+if "%ERRORLEVEL%"=="0" goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+if  not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
+exit /b 1
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega

http://git-wip-us.apache.org/repos/asf/cordova-android/blob/8aa813b8/bin/templates/project/libraries.gradle
----------------------------------------------------------------------
diff --git a/bin/templates/project/libraries.gradle b/bin/templates/project/libraries.gradle
new file mode 100644
index 0000000..f497543
--- /dev/null
+++ b/bin/templates/project/libraries.gradle
@@ -0,0 +1,7 @@
+dependencies {
+// This file contains no plugins by default.
+// To add a third-party library project, add a line to this file like
+//    compile project(':library_dir_name')
+}
+// If multiple ndk architectures are provided, uncomment this line
+//ext.multiarch=true

http://git-wip-us.apache.org/repos/asf/cordova-android/blob/8aa813b8/bin/templates/project/settings.gradle
----------------------------------------------------------------------
diff --git a/bin/templates/project/settings.gradle b/bin/templates/project/settings.gradle
new file mode 100644
index 0000000..b938ccc
--- /dev/null
+++ b/bin/templates/project/settings.gradle
@@ -0,0 +1 @@
+include ':CordovaLib', ':'

http://git-wip-us.apache.org/repos/asf/cordova-android/blob/8aa813b8/framework/build.gradle
----------------------------------------------------------------------
diff --git a/framework/build.gradle b/framework/build.gradle
new file mode 100644
index 0000000..82c1ac2
--- /dev/null
+++ b/framework/build.gradle
@@ -0,0 +1,33 @@
+buildscript {
+    repositories {
+        mavenCentral()
+    }
+
+    dependencies {
+        classpath 'com.android.tools.build:gradle:0.10.+'
+    }
+}
+
+apply plugin: 'android-library'
+
+android {
+    compileSdkVersion 19
+    buildToolsVersion "19.0.0"
+
+    compileOptions {
+        sourceCompatibility JavaVersion.VERSION_1_7
+        targetCompatibility JavaVersion.VERSION_1_7
+    }
+
+    sourceSets {
+        main {
+            manifest.srcFile 'AndroidManifest.xml'
+            java.srcDirs = ['src']
+            resources.srcDirs = ['src']
+            aidl.srcDirs = ['src']
+            renderscript.srcDirs = ['src']
+            res.srcDirs = ['res']
+            assets.srcDirs = ['assets']
+        }
+    }
+}


[11/19] android commit: CB-7044 Add JAVA_HOME when not set. Be stricter about ANDROID_HOME

Posted by ag...@apache.org.
CB-7044 Add JAVA_HOME when not set. Be stricter about ANDROID_HOME

Also switches to using the which module over shelljs.which (better
support for .bat files)


Project: http://git-wip-us.apache.org/repos/asf/cordova-android/repo
Commit: http://git-wip-us.apache.org/repos/asf/cordova-android/commit/7133576f
Tree: http://git-wip-us.apache.org/repos/asf/cordova-android/tree/7133576f
Diff: http://git-wip-us.apache.org/repos/asf/cordova-android/diff/7133576f

Branch: refs/heads/4.0.x
Commit: 7133576fe9ca8e8a3c91938eba524a843ccf8365
Parents: effffcb
Author: Andrew Grieve <ag...@chromium.org>
Authored: Mon Aug 18 14:45:23 2014 -0400
Committer: Andrew Grieve <ag...@chromium.org>
Committed: Mon Aug 18 14:45:23 2014 -0400

----------------------------------------------------------------------
 bin/lib/check_reqs.js | 102 +++++++++++++++++++++++++++++++--------------
 1 file changed, 71 insertions(+), 31 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cordova-android/blob/7133576f/bin/lib/check_reqs.js
----------------------------------------------------------------------
diff --git a/bin/lib/check_reqs.js b/bin/lib/check_reqs.js
index f616035..6f20c19 100644
--- a/bin/lib/check_reqs.js
+++ b/bin/lib/check_reqs.js
@@ -29,6 +29,14 @@ var shelljs = require('shelljs'),
 
 var isWindows = process.platform == 'win32';
 
+function forgivingWhichSync(cmd) {
+    try {
+        return which.sync(path);
+    } catch (e) {
+        return '';
+    }
+}
+
 function tryCommand(cmd, errMsg) {
     var d = Q.defer();
     child_process.exec(cmd, function(err, stdout, stderr) {
@@ -80,32 +88,54 @@ module.exports.check_ant = function() {
 
 // Returns a promise.
 module.exports.check_java = function() {
-    var javacInPath = !!shelljs.which('javac');
-    var hasJavaHome = !!process.env.JAVA_HOME;
-    // Windows java installer doesn't add javac to PATH, nor set JAVA_HOME (ugh).
-    if (hasJavaHome && !javacInPath) {
-        process.env.PATH += path.delimiter + path.join(process.env.JAVA_HOME, 'bin');
-    } else if (isWindows && (!hasJavaHome || !javacInPath)) {
-        // Try to auto-detect java in the default install paths.
-        var firstJdkDir =
-            shelljs.ls(process.env.ProgramFiles + '\\java\\jdk*')[0] ||
-            shelljs.ls('C:\\Program Files\\java\\jdk*')[0] ||
-            shelljs.ls('C:\\Program Files (x86)\\java\\jdk*')[0];
-        if (firstJdkDir) {
-            // shelljs always uses / in paths.
-            firstJdkDir = firstJdkDir.replace(/\//g, path.sep);
-            if (!javacInPath) {
-                process.env.PATH += path.delimiter + path.join(firstJdkDir, 'bin');
+    var javacPath = forgivingWhichSync('javac');
+    var hasJavaHome = !!process.env['JAVA_HOME'];
+    return Q().then(function() {
+        if (hasJavaHome) {
+            // Windows java installer doesn't add javac to PATH, nor set JAVA_HOME (ugh).
+            if (!javacPath) {
+                process.env['PATH'] += path.delimiter + path.join(process.env['JAVA_HOME'], 'bin');
+            }
+        } else {
+            if (javacPath) {
+                // OS X has a command for finding JAVA_HOME.
+                if (fs.existsSync('/usr/libexec/java_home')) {
+                    return tryCommand('/usr/libexec/java_home', 'Failed to run: /usr/libexec/java_home')
+                    .then(function(stdout) {
+                        process.env['JAVA_HOME'] = stdout.trim();
+                    });
+                } else {
+                    // See if we can derive it from javac's location.
+                    var maybeJavaHome = path.dirname(path.dirname(javacPath));
+                    if (fs.existsSync(path.join(maybeJavaHome, 'lib', 'tools.jar'))) {
+                        process.env['JAVA_HOME'] = maybeJavaHome;
+                    } else {
+                        throw new Error('Could not find JAVA_HOME. Try setting the environment variable manually');
+                    }
+                }
+            } else if (isWindows) {
+                // Try to auto-detect java in the default install paths.
+                var firstJdkDir =
+                    shelljs.ls(process.env['ProgramFiles'] + '\\java\\jdk*')[0] ||
+                    shelljs.ls('C:\\Program Files\\java\\jdk*')[0] ||
+                    shelljs.ls('C:\\Program Files (x86)\\java\\jdk*')[0];
+                if (firstJdkDir) {
+                    // shelljs always uses / in paths.
+                    firstJdkDir = firstJdkDir.replace(/\//g, path.sep);
+                    if (!javacPath) {
+                        process.env['PATH'] += path.delimiter + path.join(firstJdkDir, 'bin');
+                    }
+                    process.env['JAVA_HOME'] = firstJdkDir;
+                }
             }
-            process.env.JAVA_HOME = firstJdkDir;
         }
-    }
-    var msg =
-        'Failed to run "java -version", make sure your java environment is set up\n' +
-        'including JDK and JRE.\n' +
-        'Your JAVA_HOME variable is: ' + process.env.JAVA_HOME;
-    return tryCommand('java -version', msg)
-    .then(function() {
+    }).then(function() {
+        var msg =
+            'Failed to run "java -version", make sure your java environment is set up\n' +
+            'including JDK and JRE.\n' +
+            'Your JAVA_HOME variable is: ' + process.env['JAVA_HOME'];
+        return tryCommand('java -version', msg)
+    }).then(function() {
         msg = 'Failed to run "javac -version", make sure you have a Java JDK (not just a JRE) installed.';
         return tryCommand('javac -version', msg)
     });
@@ -113,18 +143,21 @@ module.exports.check_java = function() {
 
 // Returns a promise.
 module.exports.check_android = function() {
-    var androidCmdPath = !!shelljs.which('android');
-    var adbInPath = !!shelljs.which('adb');
-    var hasAndroidHome = !!process.env.ANDROID_HOME;
+    var androidCmdPath = forgivingWhichSync('android');
+    var adbInPath = !!forgivingWhichSync('adb');
+    var hasAndroidHome = !!process.env['ANDROID_HOME'] && fs.existsSync(process.env['ANDROID_HOME']);
     if (hasAndroidHome && !androidCmdPath) {
-        process.env.PATH += path.delimiter + path.join(process.env.ANDROID_HOME, 'tools');
+        process.env['PATH'] += path.delimiter + path.join(process.env['ANDROID_HOME'], 'tools');
     }
     if (androidCmdPath && !hasAndroidHome) {
-        process.env.ANDROID_HOME = path.dirname(path.dirname(androidCmdPath));
-        hasAndroidHome = true;
+        var parentDir = path.dirname(androidCmdPath);
+        if (path.basename(parentDir) == 'tools') {
+            process.env['ANDROID_HOME'] = path.dirname(parentDir);
+            hasAndroidHome = true;
+        }
     }
     if (hasAndroidHome && !adbInPath) {
-        process.env.PATH += path.delimiter + path.join(process.env.ANDROID_HOME, 'platform-tools');
+        process.env['PATH'] += path.delimiter + path.join(process.env['ANDROID_HOME'], 'platform-tools');
     }
 
     var valid_target = this.get_target();
@@ -136,6 +169,13 @@ module.exports.check_android = function() {
                 ' (the Android newest SDK). Make sure you have the latest Android tools installed as well.' +
                 ' Run "android" from your command-line to install/update any missing SDKs or tools.'));
         }
+    }).then(function() {
+        if (!process.env['ANDROID_HOME']) {
+            throw new Error('ANDROID_HOME is not set and "android" command not in your PATH. You must fulfill at least one of these conditions.');
+        }
+        if (!fs.existsSync(process.env['ANDROID_HOME'])) {
+            throw new Error('ANDROID_HOME is set to a non-existant path: ' + process.env['ANDROID_HOME']);
+        }
     });
 }
 


[06/19] android commit: CB-3445: Add option to build and install with gradle

Posted by ag...@apache.org.
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);
-}
+};


[09/19] android commit: Merge branch 'gradle-build-3.x'

Posted by ag...@apache.org.
Merge branch 'gradle-build-3.x'

This introduces the gradle build system to Cordova-android, behind a flag currently


Project: http://git-wip-us.apache.org/repos/asf/cordova-android/repo
Commit: http://git-wip-us.apache.org/repos/asf/cordova-android/commit/404ce8bc
Tree: http://git-wip-us.apache.org/repos/asf/cordova-android/tree/404ce8bc
Diff: http://git-wip-us.apache.org/repos/asf/cordova-android/diff/404ce8bc

Branch: refs/heads/4.0.x
Commit: 404ce8bc3e1225d3a4a2789e9b9b4689943d0f96
Parents: 95aa5c9 a91bd09
Author: Ian Clelland <ic...@chromium.org>
Authored: Mon Aug 18 09:58:01 2014 -0400
Committer: Ian Clelland <ic...@chromium.org>
Committed: Mon Aug 18 09:58:01 2014 -0400

----------------------------------------------------------------------
 bin/lib/check_reqs.js                  |  18 +++
 bin/lib/create.js                      |  22 ++-
 bin/node_modules/which/LICENSE         |  23 +++
 bin/node_modules/which/README.md       |   5 +
 bin/node_modules/which/bin/which       |  14 ++
 bin/node_modules/which/package.json    |  31 ++++
 bin/node_modules/which/which.js        | 104 +++++++++++++
 bin/templates/cordova/build            |  10 +-
 bin/templates/cordova/lib/build.js     | 231 +++++++++++++++++++++-------
 bin/templates/project/build.gradle     |  81 ++++++++++
 bin/templates/project/libraries.gradle |   7 +
 bin/templates/project/settings.gradle  |   1 +
 framework/build.gradle                 |  33 ++++
 13 files changed, 522 insertions(+), 58 deletions(-)
----------------------------------------------------------------------



[17/19] android commit: CB-7330 Fix dangling function call in last commit (broke gradle builds)

Posted by ag...@apache.org.
CB-7330 Fix dangling function call in last commit (broke gradle builds)


Project: http://git-wip-us.apache.org/repos/asf/cordova-android/repo
Commit: http://git-wip-us.apache.org/repos/asf/cordova-android/commit/58cdfd86
Tree: http://git-wip-us.apache.org/repos/asf/cordova-android/tree/58cdfd86
Diff: http://git-wip-us.apache.org/repos/asf/cordova-android/diff/58cdfd86

Branch: refs/heads/4.0.x
Commit: 58cdfd86d0b6fabba3ce0e505a361225dca3b3c6
Parents: dfa66b9
Author: Andrew Grieve <ag...@chromium.org>
Authored: Tue Aug 19 11:53:53 2014 -0400
Committer: Andrew Grieve <ag...@chromium.org>
Committed: Tue Aug 19 11:53:53 2014 -0400

----------------------------------------------------------------------
 bin/templates/cordova/lib/build.js | 2 --
 1 file changed, 2 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cordova-android/blob/58cdfd86/bin/templates/cordova/lib/build.js
----------------------------------------------------------------------
diff --git a/bin/templates/cordova/lib/build.js b/bin/templates/cordova/lib/build.js
index 7830bd6..1f4810d 100644
--- a/bin/templates/cordova/lib/build.js
+++ b/bin/templates/cordova/lib/build.js
@@ -204,7 +204,6 @@ var builders = {
             var builder = this;
             var wrapper = path.join(ROOT, 'gradlew');
             var args = builder.getArgs('build');
-            copyGradleWrapper();
             return Q().then(function() {
                 return spawn(wrapper, args);
             }).then(function() {
@@ -216,7 +215,6 @@ var builders = {
             var builder = this;
             var wrapper = path.join(ROOT, 'gradlew');
             var args = builder.getArgs('clean');
-            copyGradleWrapper();
             return Q().then(function() {
                 return spawn(wrapper, args);
             });


[19/19] android commit: Merge branch 'master' into 4.0.x (build & create script updates)

Posted by ag...@apache.org.
Merge branch 'master' into 4.0.x (build & create script updates)

Conflicts:
	bin/lib/check_reqs.js
	bin/lib/create.js
	bin/node_modules/which/package.json
	bin/templates/cordova/lib/build.js


Project: http://git-wip-us.apache.org/repos/asf/cordova-android/repo
Commit: http://git-wip-us.apache.org/repos/asf/cordova-android/commit/4c1942e3
Tree: http://git-wip-us.apache.org/repos/asf/cordova-android/tree/4c1942e3
Diff: http://git-wip-us.apache.org/repos/asf/cordova-android/diff/4c1942e3

Branch: refs/heads/4.0.x
Commit: 4c1942e3fe57d2898a7eba7341fb22a485bd339c
Parents: a7ccb92 71e72f2
Author: Andrew Grieve <ag...@chromium.org>
Authored: Tue Aug 19 12:02:36 2014 -0400
Committer: Andrew Grieve <ag...@chromium.org>
Committed: Tue Aug 19 12:02:36 2014 -0400

----------------------------------------------------------------------
 bin/lib/check_reqs.js                           | 175 ++++++---
 bin/lib/create.js                               |  68 +++-
 bin/node_modules/which/package.json             |   2 +-
 bin/templates/cordova/build                     |  14 +-
 bin/templates/cordova/clean                     |  20 +-
 bin/templates/cordova/lib/build.js              | 357 ++++++++++++++++---
 bin/templates/cordova/lib/clean.js              |  39 --
 bin/templates/project/gitignore                 |  14 +
 bin/templates/project/project.properties        |  15 +
 test/AndroidManifest.xml                        |  10 +
 .../test/junit/IntentUriOverrideTest.java       |   1 +
 11 files changed, 537 insertions(+), 178 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cordova-android/blob/4c1942e3/bin/lib/create.js
----------------------------------------------------------------------
diff --cc bin/lib/create.js
index 485c60e,0e929e3..f20c6a0
--- a/bin/lib/create.js
+++ b/bin/lib/create.js
@@@ -222,17 -246,8 +253,18 @@@ exports.createProject = function(projec
              shell.cp('-r', path.join(project_template_dir, 'assets'), project_path);
              shell.cp('-r', path.join(project_template_dir, 'res'), project_path);
              shell.cp('-r', path.join(ROOT, 'framework', 'res', 'xml'), path.join(project_path, 'res'));
+             shell.cp(path.join(project_template_dir, 'gitignore'), path.join(project_path, '.gitignore'));
  
 +            shell.cp('-f', path.join(project_template_dir, 'build.gradle'), project_path);
 +            shell.cp('-f', path.join(project_template_dir, 'libraries.gradle'), project_path);
 +            shell.cp('-f', path.join(project_template_dir, 'settings.gradle'), project_path);
 +            check_reqs.sdk_dir().then(function(dir) {
 +                console.log("Copying Gradle wrapper from " + dir);
 +                copyGradleWrapper(dir, project_path);
 +            }).catch(function(err) {
 +                console.log("Cannot find Android SDK. Not installing Gradle wrapper.");
 +            });
 +
              // Manually create directories that would be empty within the template (since git doesn't track directories).
              shell.mkdir(path.join(project_path, 'libs'));
  

http://git-wip-us.apache.org/repos/asf/cordova-android/blob/4c1942e3/bin/templates/cordova/lib/build.js
----------------------------------------------------------------------
diff --cc bin/templates/cordova/lib/build.js
index 5f100e2,1f4810d..7d8828d
--- a/bin/templates/cordova/lib/build.js
+++ b/bin/templates/cordova/lib/build.js
@@@ -24,46 -24,306 +24,307 @@@ var shell   = require('shelljs')
      Q       = require('q'),
      path    = require('path'),
      fs      = require('fs'),
 +    which   = require('which'),
      ROOT    = path.join(__dirname, '..', '..');
+ var check_reqs = require('./check_reqs');
  
+ var LOCAL_PROPERTIES_TEMPLATE =
+     '# This file is automatically generated.\n' +
+     '# Do not modify this file -- YOUR CHANGES WILL BE ERASED!\n';
  
- module.exports.getAntArgs = function(cmd) {
-     var args = [cmd, '-f', path.join(ROOT, 'build.xml')];
-     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
+ 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'));
+ }
+ 
+ function extractProjectNameFromManifest(projectPath) {
+     var manifestPath = path.join(projectPath, 'AndroidManifest.xml');
+     var manifestData = fs.readFileSync(manifestPath, 'utf8');
+     var m = /<activity[\s\S]*?android:name\s*=\s*"(.*?)"/i.exec(manifestData);
+     if (!m) {
+         throw new Error('Could not find activity name in ' + manifestPath);
+     }
+     return m[1];
+ }
+ 
+ function extractSubProjectPaths() {
+     var data = fs.readFileSync(path.join(ROOT, 'project.properties'), 'utf8');
+     var ret = {};
+     var r = /^\s*android\.library\.reference\.\d+=(.*)(?:\s|$)/mg
+     var m;
+     while (m = r.exec(data)) {
+         ret[m[1]] = 1;
+     }
+     return Object.keys(ret);
+ }
+ 
+ var 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');
+             }
+             return args;
+         },
+ 
+         prepEnv: function() {
+             return check_reqs.check_ant()
+             .then(function() {
+                 // Copy in build.xml on each build so that:
+                 // A) we don't require the Android SDK at project creation time, and
+                 // B) we always use the SDK's latest version of it.
+                 var sdkDir = process.env['ANDROID_HOME'];
+                 var buildTemplate = fs.readFileSync(path.join(sdkDir, 'tools', 'lib', 'build.template'), 'utf8');
+                 function writeBuildXml(projectPath) {
+                     var newData = buildTemplate.replace('PROJECT_NAME', extractProjectNameFromManifest(ROOT));
+                     fs.writeFileSync(path.join(projectPath, 'build.xml'), newData);
+                     if (!fs.existsSync(path.join(projectPath, 'local.properties'))) {
+                         fs.writeFileSync(path.join(projectPath, 'local.properties'), LOCAL_PROPERTIES_TEMPLATE);
+                     }
+                 }
+                 var subProjects = extractSubProjectPaths();
+                 writeBuildXml(ROOT);
+                 for (var i = 0; i < subProjects.length; ++i) {
+                     writeBuildXml(path.join(ROOT, subProjects[i]));
+                 }
+             });
+         },
+ 
+         /*
+          * Builds the project with ant.
+          * Returns a promise.
+          */
+         build: function(build_type) {
+             // Without our custom_rules.xml, we need to clean before building.
+             var ret = Q();
+             if (!hasCustomRules()) {
+                 // clean will call check_ant() for us.
+                 ret = this.clean();
+             }
+ 
+             var builder = this;
+             var args = this.getArgs(build_type == 'debug' ? 'debug' : 'release');
+             return check_reqs.check_ant()
+             .then(function() {
+                 return spawn('ant', args);
+             }).then(function() {
+                 return builder.getOutputFiles();
+             });
+         },
+ 
+         clean: function() {
+             var args = this.getArgs('clean');
+             return check_reqs.check_ant()
+             .then(function() {
+                 return spawn('ant', args);
+             });
+         },
+ 
+         // 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 lintSteps = [
+                 'lint',
+                 'lintVitalRelease',
+                 'compileLint',
+                 'copyReleaseLint',
+                 'copyDebugLint'
+             ];
+             var args = [cmd, '-b', path.join(ROOT, 'build.gradle')];
+             // 10 seconds -> 6 seconds
+             args.push('-Dorg.gradle.daemon=true');
+             // Excluding lint: 6s-> 1.6s
+             for (var i = 0; i < lintSteps.length; ++i) {
+                 args.push('-x', lintSteps[i]);
+             }
+             // Shaves another 100ms, but produces a "try at own risk" warning. Not worth it (yet):
+             // args.push('-Dorg.gradle.parallel=true');
+             return args;
+         },
+ 
+         prepEnv: function() {
+             return check_reqs.check_gradle()
+             .then(function() {
+                 // Copy the gradle wrapper on each build so that:
+                 // A) we don't require the Android SDK at project creation time, and
+                 // B) we always use the SDK's latest version of it.
+                 var projectPath = ROOT;
+                 // check_reqs ensures that this is set.
+                 var sdkDir = process.env['ANDROID_HOME'];
+                 var wrapperDir = path.join(sdkDir, 'tools', 'templates', 'gradle', 'wrapper');
+                 if (process.platform == 'win32') {
+                     shell.cp('-f', path.join(wrapperDir, 'gradlew.bat'), projectPath);
+                 } else {
+                     shell.cp('-f', path.join(wrapperDir, 'gradlew'), projectPath);
+                 }
+                 shell.rm('-rf', path.join(projectPath, 'gradle', 'wrapper'));
+                 shell.mkdir('-p', path.join(projectPath, 'gradle'));
+                 shell.cp('-r', path.join(wrapperDir, 'gradle', 'wrapper'), path.join(projectPath, 'gradle'));
+             });
+         },
+ 
+         /*
+          * 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);
+             });
+         },
+ 
+         clean: function() {
+             var builder = this;
+             var wrapper = path.join(ROOT, 'gradlew');
+             var args = builder.getArgs('clean');
+             return Q().then(function() {
+                 return spawn(wrapper, args);
+             });
+         },
+ 
+         // 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;
+         }
+     },
+ 
+     none: {
+         prepEnv: function() {
+             return Q();
+         },
+         build: function() {
+             console.log('Skipping build...');
+             return Q();
+         },
+         clean: function() {
+             return Q();
+         },
      }
-     return args;
  };
  
+ function parseOpts(options) {
+     // Backwards-compatibility: Allow a single string argument
+     if (typeof options == "string") options = [options];
+ 
+     var ret = {
+         buildType: 'debug',
+         buildMethod: process.env['ANDROID_BUILD'] || 'ant'
+     };
+ 
+     // 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':
+                     ret.buildType = option;
+                     break;
+                 case 'ant':
+                 case 'gradle':
+                     ret.buildMethod = option;
+                     break;
+                 case 'nobuild' :
+                     ret.buildMethod = 'none';
+                     break;
+                 default :
+                     return Q.reject('Build option \'' + options[i] + '\' not recognized.');
+             }
+         } else {
+             return Q.reject('Build option \'' + options[i] + '\' not recognized.');
+         }
+     }
+     return ret;
+ }
+ 
  /*
-  * 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.');
-     }
-     var ret = Q();
-     return ret.then(function() {
-         return spawn('ant', args);
+ module.exports.runClean = function(options) {
+     var opts = parseOpts(options);
+     var builder = builders[opts.buildMethod];
+     return builder.prepEnv()
+     .then(function() {
+         return builder.clean();
+     }).then(function() {
+         shell.rm('-rf', path.join(ROOT, 'out'));
      });
- }
+ };
+ 
+ /*
+  * Builds the project with the specifed options
+  * Returns a promise.
+  */
+ module.exports.run = function(options) {
+     var opts = parseOpts(options);
+ 
+     var builder = builders[opts.buildMethod];
+     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);
+         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


[08/19] android commit: CB-3445: android: Copy Gradle wrapper from Android SDK rather than bundling a JAR

Posted by ag...@apache.org.
CB-3445: android: Copy Gradle wrapper from Android SDK rather than bundling a JAR


Project: http://git-wip-us.apache.org/repos/asf/cordova-android/repo
Commit: http://git-wip-us.apache.org/repos/asf/cordova-android/commit/a91bd095
Tree: http://git-wip-us.apache.org/repos/asf/cordova-android/tree/a91bd095
Diff: http://git-wip-us.apache.org/repos/asf/cordova-android/diff/a91bd095

Branch: refs/heads/4.0.x
Commit: a91bd095b0ebe1e4ed85c69248f376df7cbc626a
Parents: fd6a1e5
Author: Ian Clelland <ic...@chromium.org>
Authored: Thu Jun 19 16:12:40 2014 -0400
Committer: Ian Clelland <ic...@chromium.org>
Committed: Mon Aug 18 09:48:31 2014 -0400

----------------------------------------------------------------------
 bin/lib/check_reqs.js                           |  18 ++
 bin/lib/create.js                               |  16 +-
 .../gradle/wrapper/gradle-wrapper.properties    |   6 -
 bin/templates/project/gradlew                   | 164 -------------------
 bin/templates/project/gradlew.bat               |  90 ----------
 5 files changed, 31 insertions(+), 263 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cordova-android/blob/a91bd095/bin/lib/check_reqs.js
----------------------------------------------------------------------
diff --git a/bin/lib/check_reqs.js b/bin/lib/check_reqs.js
index db45474..f616035 100644
--- a/bin/lib/check_reqs.js
+++ b/bin/lib/check_reqs.js
@@ -24,6 +24,7 @@ var shelljs = require('shelljs'),
     Q     = require('q'),
     path  = require('path'),
     fs    = require('fs'),
+    which = require('which'),
     ROOT  = path.join(__dirname, '..', '..');
 
 var isWindows = process.platform == 'win32';
@@ -55,6 +56,23 @@ module.exports.get_target = function() {
     }
 }
 
+// Returns a promise.
+module.exports.sdk_dir = function() {
+    var d = Q.defer();
+    which('android', function(err, path) {
+        if (err) {
+            d.reject(new Error('ERROR: Cannot find Android SDK. android command not found.'));
+        } else {
+            var toolsDir = path.substring(0, path.lastIndexOf('/'));
+            if (toolsDir.substring(toolsDir.length-6) != "/tools") {
+                d.reject(new Error('ERROR: Cannot find Android SDK. android command not found in tools dir.'));
+            }
+            d.resolve(toolsDir.substring(0, toolsDir.length-6));
+        }
+    });
+    return d.promise;
+};
+
 // Returns a promise. Called only by build and clean commands.
 module.exports.check_ant = function() {
     return tryCommand('ant -version', 'Failed to run "ant -version", make sure you have ant installed and added to your PATH.');

http://git-wip-us.apache.org/repos/asf/cordova-android/blob/a91bd095/bin/lib/create.js
----------------------------------------------------------------------
diff --git a/bin/lib/create.js b/bin/lib/create.js
index ad3c21c..312881c 100755
--- a/bin/lib/create.js
+++ b/bin/lib/create.js
@@ -109,6 +109,13 @@ function copyScripts(projectPath) {
     shell.cp(path.join(ROOT, 'bin', 'lib', 'android_sdk_version.js'), path.join(projectPath, 'cordova', 'lib', 'android_sdk_version.js'));
 }
 
+function copyGradleWrapper(sdkPath, projectPath) {
+    var wrapperDir = path.join(sdkPath, 'tools', 'templates','gradle','wrapper');
+    shell.cp(path.join(wrapperDir, 'gradlew'), projectPath);
+    shell.cp(path.join(wrapperDir, 'gradlew.bat'), projectPath);
+    shell.cp('-r', path.join(wrapperDir, 'gradle'), projectPath);
+}
+
 /**
  * Test whether a package name is acceptable for use as an android project.
  * Returns a promise, fulfilled if the package name is acceptable; rejected
@@ -219,9 +226,12 @@ exports.createProject = function(project_path, package_name, project_name, proje
             shell.cp('-f', path.join(project_template_dir, 'build.gradle'), project_path);
             shell.cp('-f', path.join(project_template_dir, 'libraries.gradle'), project_path);
             shell.cp('-f', path.join(project_template_dir, 'settings.gradle'), project_path);
-            shell.cp('-f', path.join(project_template_dir, 'gradlew'), project_path);
-            shell.cp('-f', path.join(project_template_dir, 'gradlew.bat'), project_path);
-            shell.cp('-r', path.join(project_template_dir, 'gradle'), project_path);
+            check_reqs.sdk_dir().then(function(dir) {
+                console.log("Copying Gradle wrapper from " + dir);
+                copyGradleWrapper(dir, project_path);
+            }).catch(function(err) {
+                console.log("Cannot find Android SDK. Not installing Gradle wrapper.");
+            });
 
             // Manually create directories that would be empty within the template (since git doesn't track directories).
             shell.mkdir(path.join(project_path, 'libs'));

http://git-wip-us.apache.org/repos/asf/cordova-android/blob/a91bd095/bin/templates/project/gradle/wrapper/gradle-wrapper.properties
----------------------------------------------------------------------
diff --git a/bin/templates/project/gradle/wrapper/gradle-wrapper.properties b/bin/templates/project/gradle/wrapper/gradle-wrapper.properties
deleted file mode 100644
index 497dc9b..0000000
--- a/bin/templates/project/gradle/wrapper/gradle-wrapper.properties
+++ /dev/null
@@ -1,6 +0,0 @@
-#Fri Jun 13 09:52:43 EDT 2014
-distributionBase=GRADLE_USER_HOME
-distributionPath=wrapper/dists
-zipStoreBase=GRADLE_USER_HOME
-zipStorePath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-1.12-bin.zip

http://git-wip-us.apache.org/repos/asf/cordova-android/blob/a91bd095/bin/templates/project/gradlew
----------------------------------------------------------------------
diff --git a/bin/templates/project/gradlew b/bin/templates/project/gradlew
deleted file mode 100755
index 91a7e26..0000000
--- a/bin/templates/project/gradlew
+++ /dev/null
@@ -1,164 +0,0 @@
-#!/usr/bin/env bash
-
-##############################################################################
-##
-##  Gradle start up script for UN*X
-##
-##############################################################################
-
-# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
-DEFAULT_JVM_OPTS=""
-
-APP_NAME="Gradle"
-APP_BASE_NAME=`basename "$0"`
-
-# Use the maximum available, or set MAX_FD != -1 to use that value.
-MAX_FD="maximum"
-
-warn ( ) {
-    echo "$*"
-}
-
-die ( ) {
-    echo
-    echo "$*"
-    echo
-    exit 1
-}
-
-# OS specific support (must be 'true' or 'false').
-cygwin=false
-msys=false
-darwin=false
-case "`uname`" in
-  CYGWIN* )
-    cygwin=true
-    ;;
-  Darwin* )
-    darwin=true
-    ;;
-  MINGW* )
-    msys=true
-    ;;
-esac
-
-# For Cygwin, ensure paths are in UNIX format before anything is touched.
-if $cygwin ; then
-    [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
-fi
-
-# Attempt to set APP_HOME
-# Resolve links: $0 may be a link
-PRG="$0"
-# Need this for relative symlinks.
-while [ -h "$PRG" ] ; do
-    ls=`ls -ld "$PRG"`
-    link=`expr "$ls" : '.*-> \(.*\)$'`
-    if expr "$link" : '/.*' > /dev/null; then
-        PRG="$link"
-    else
-        PRG=`dirname "$PRG"`"/$link"
-    fi
-done
-SAVED="`pwd`"
-cd "`dirname \"$PRG\"`/" >&-
-APP_HOME="`pwd -P`"
-cd "$SAVED" >&-
-
-CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
-
-# Determine the Java command to use to start the JVM.
-if [ -n "$JAVA_HOME" ] ; then
-    if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
-        # IBM's JDK on AIX uses strange locations for the executables
-        JAVACMD="$JAVA_HOME/jre/sh/java"
-    else
-        JAVACMD="$JAVA_HOME/bin/java"
-    fi
-    if [ ! -x "$JAVACMD" ] ; then
-        die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
-
-Please set the JAVA_HOME variable in your environment to match the
-location of your Java installation."
-    fi
-else
-    JAVACMD="java"
-    which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
-
-Please set the JAVA_HOME variable in your environment to match the
-location of your Java installation."
-fi
-
-# Increase the maximum file descriptors if we can.
-if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
-    MAX_FD_LIMIT=`ulimit -H -n`
-    if [ $? -eq 0 ] ; then
-        if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
-            MAX_FD="$MAX_FD_LIMIT"
-        fi
-        ulimit -n $MAX_FD
-        if [ $? -ne 0 ] ; then
-            warn "Could not set maximum file descriptor limit: $MAX_FD"
-        fi
-    else
-        warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
-    fi
-fi
-
-# For Darwin, add options to specify how the application appears in the dock
-if $darwin; then
-    GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
-fi
-
-# For Cygwin, switch paths to Windows format before running java
-if $cygwin ; then
-    APP_HOME=`cygpath --path --mixed "$APP_HOME"`
-    CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
-
-    # We build the pattern for arguments to be converted via cygpath
-    ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
-    SEP=""
-    for dir in $ROOTDIRSRAW ; do
-        ROOTDIRS="$ROOTDIRS$SEP$dir"
-        SEP="|"
-    done
-    OURCYGPATTERN="(^($ROOTDIRS))"
-    # Add a user-defined pattern to the cygpath arguments
-    if [ "$GRADLE_CYGPATTERN" != "" ] ; then
-        OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
-    fi
-    # Now convert the arguments - kludge to limit ourselves to /bin/sh
-    i=0
-    for arg in "$@" ; do
-        CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
-        CHECK2=`echo "$arg"|egrep -c "^-"`                                 ### Determine if an option
-
-        if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then                    ### Added a condition
-            eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
-        else
-            eval `echo args$i`="\"$arg\""
-        fi
-        i=$((i+1))
-    done
-    case $i in
-        (0) set -- ;;
-        (1) set -- "$args0" ;;
-        (2) set -- "$args0" "$args1" ;;
-        (3) set -- "$args0" "$args1" "$args2" ;;
-        (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
-        (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
-        (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
-        (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
-        (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
-        (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
-    esac
-fi
-
-# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
-function splitJvmOpts() {
-    JVM_OPTS=("$@")
-}
-eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
-JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
-
-exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"

http://git-wip-us.apache.org/repos/asf/cordova-android/blob/a91bd095/bin/templates/project/gradlew.bat
----------------------------------------------------------------------
diff --git a/bin/templates/project/gradlew.bat b/bin/templates/project/gradlew.bat
deleted file mode 100644
index aec9973..0000000
--- a/bin/templates/project/gradlew.bat
+++ /dev/null
@@ -1,90 +0,0 @@
-@if "%DEBUG%" == "" @echo off
-@rem ##########################################################################
-@rem
-@rem  Gradle startup script for Windows
-@rem
-@rem ##########################################################################
-
-@rem Set local scope for the variables with windows NT shell
-if "%OS%"=="Windows_NT" setlocal
-
-@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
-set DEFAULT_JVM_OPTS=
-
-set DIRNAME=%~dp0
-if "%DIRNAME%" == "" set DIRNAME=.
-set APP_BASE_NAME=%~n0
-set APP_HOME=%DIRNAME%
-
-@rem Find java.exe
-if defined JAVA_HOME goto findJavaFromJavaHome
-
-set JAVA_EXE=java.exe
-%JAVA_EXE% -version >NUL 2>&1
-if "%ERRORLEVEL%" == "0" goto init
-
-echo.
-echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
-echo.
-echo Please set the JAVA_HOME variable in your environment to match the
-echo location of your Java installation.
-
-goto fail
-
-:findJavaFromJavaHome
-set JAVA_HOME=%JAVA_HOME:"=%
-set JAVA_EXE=%JAVA_HOME%/bin/java.exe
-
-if exist "%JAVA_EXE%" goto init
-
-echo.
-echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
-echo.
-echo Please set the JAVA_HOME variable in your environment to match the
-echo location of your Java installation.
-
-goto fail
-
-:init
-@rem Get command-line arguments, handling Windowz variants
-
-if not "%OS%" == "Windows_NT" goto win9xME_args
-if "%@eval[2+2]" == "4" goto 4NT_args
-
-:win9xME_args
-@rem Slurp the command line arguments.
-set CMD_LINE_ARGS=
-set _SKIP=2
-
-:win9xME_args_slurp
-if "x%~1" == "x" goto execute
-
-set CMD_LINE_ARGS=%*
-goto execute
-
-:4NT_args
-@rem Get arguments from the 4NT Shell from JP Software
-set CMD_LINE_ARGS=%$
-
-:execute
-@rem Setup the command line
-
-set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
-
-@rem Execute Gradle
-"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
-
-:end
-@rem End local scope for the variables with windows NT shell
-if "%ERRORLEVEL%"=="0" goto mainEnd
-
-:fail
-rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
-rem the _cmd.exe /c_ return code!
-if  not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
-exit /b 1
-
-:mainEnd
-if "%OS%"=="Windows_NT" endlocal
-
-:omega


[02/19] android commit: Change in test's AndroidManifest.xml needed for the test to run properly. Forgot the manifest.

Posted by ag...@apache.org.
Change in test's AndroidManifest.xml needed for the test to run properly. Forgot the manifest.


Project: http://git-wip-us.apache.org/repos/asf/cordova-android/repo
Commit: http://git-wip-us.apache.org/repos/asf/cordova-android/commit/50ea1622
Tree: http://git-wip-us.apache.org/repos/asf/cordova-android/tree/50ea1622
Diff: http://git-wip-us.apache.org/repos/asf/cordova-android/diff/50ea1622

Branch: refs/heads/4.0.x
Commit: 50ea1622518f4d8a4c90db52603576fcf7f24236
Parents: 9c23980
Author: Joe Bowser <bo...@apache.org>
Authored: Thu Aug 14 14:30:31 2014 -0700
Committer: Joe Bowser <bo...@apache.org>
Committed: Thu Aug 14 14:30:31 2014 -0700

----------------------------------------------------------------------
 test/AndroidManifest.xml | 10 ++++++++++
 1 file changed, 10 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cordova-android/blob/50ea1622/test/AndroidManifest.xml
----------------------------------------------------------------------
diff --git a/test/AndroidManifest.xml b/test/AndroidManifest.xml
index 6dec1ae..e058a42 100755
--- a/test/AndroidManifest.xml
+++ b/test/AndroidManifest.xml
@@ -255,5 +255,15 @@
                 <category android:name="android.intent.category.SAMPLE_CODE" />
             </intent-filter>
         </activity>
+           <activity
+            android:windowSoftInputMode="adjustPan"
+            android:label="@string/app_name" 
+            android:configChanges="orientation|keyboardHidden"
+            android:name="org.apache.cordova.test.SabotagedActivity" >
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.SAMPLE_CODE" />
+            </intent-filter>
+        </activity>
     </application>
 </manifest> 


[03/19] android commit: CB-7044, CB-7299 Fix up PATH problems when possible.

Posted by ag...@apache.org.
CB-7044, CB-7299 Fix up PATH problems when possible.

Uses heuristics:
- Adds javac to PATH based on default install paths on Windows
- Adds javac to PATH based on JAVA_HOME
- Adds android and adb to PATH based on ANDROID_HOME
- Sets ANDROID_HOME based on location of "android"


Project: http://git-wip-us.apache.org/repos/asf/cordova-android/repo
Commit: http://git-wip-us.apache.org/repos/asf/cordova-android/commit/4319447c
Tree: http://git-wip-us.apache.org/repos/asf/cordova-android/tree/4319447c
Diff: http://git-wip-us.apache.org/repos/asf/cordova-android/diff/4319447c

Branch: refs/heads/4.0.x
Commit: 4319447cb5157e5137465bcdc16bd6e3dcd99570
Parents: 50ea162
Author: Andrew Grieve <ag...@chromium.org>
Authored: Fri Aug 15 13:42:43 2014 -0400
Committer: Andrew Grieve <ag...@chromium.org>
Committed: Fri Aug 15 13:46:20 2014 -0400

----------------------------------------------------------------------
 bin/lib/check_reqs.js       | 100 +++++++++++++++++++++++++--------------
 bin/lib/create.js           |   2 +-
 bin/templates/cordova/build |   4 +-
 3 files changed, 67 insertions(+), 39 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cordova-android/blob/4319447c/bin/lib/check_reqs.js
----------------------------------------------------------------------
diff --git a/bin/lib/check_reqs.js b/bin/lib/check_reqs.js
index a4ad3ad..17a3bd4 100644
--- a/bin/lib/check_reqs.js
+++ b/bin/lib/check_reqs.js
@@ -19,26 +19,37 @@
        under the License.
 */
 
-var shell = require('shelljs'),
+var shelljs = require('shelljs'),
     child_process = require('child_process'),
     Q     = require('q'),
     path  = require('path'),
     fs    = require('fs'),
     ROOT  = path.join(__dirname, '..', '..');
 
+var isWindows = process.platform == 'win32';
+
+function tryCommand(cmd, errMsg) {
+    var d = Q.defer();
+    child_process.exec(cmd, function(err, stdout, stderr) {
+        if (err) d.reject(new Error(errMsg));
+        else d.resolve(stdout);
+    });
+    return d.promise;
+}
+
 // Get valid target from framework/project.properties
 module.exports.get_target = function() {
     if(fs.existsSync(path.join(ROOT, 'framework', 'project.properties'))) {
-        var target = shell.grep(/target=android-[\d+]/, path.join(ROOT, 'framework', 'project.properties'));
+        var target = shelljs.grep(/target=android-[\d+]/, path.join(ROOT, 'framework', 'project.properties'));
         return target.split('=')[1].replace('\n', '').replace('\r', '').replace(' ', '');
     } else if (fs.existsSync(path.join(ROOT, 'project.properties'))) {
         // if no target found, we're probably in a project and project.properties is in ROOT.
         // this is called on the project itself, and can support Google APIs AND Vanilla Android
-        var target = shell.grep(/target=android-[\d+]/, path.join(ROOT, 'project.properties')) ||
-          shell.grep(/target=Google Inc.:Google APIs:[\d+]/, path.join(ROOT, 'project.properties'));
+        var target = shelljs.grep(/target=android-[\d+]/, path.join(ROOT, 'project.properties')) ||
+          shelljs.grep(/target=Google Inc.:Google APIs:[\d+]/, path.join(ROOT, 'project.properties'));
         if(target == "" || !target) {
           // Try Google Glass APIs
-          target = shell.grep(/target=Google Inc.:Glass Development Kit Preview:[\d+]/, path.join(ROOT, 'project.properties'));
+          target = shelljs.grep(/target=Google Inc.:Glass Development Kit Preview:[\d+]/, path.join(ROOT, 'project.properties'));
         }
         return target.split('=')[1].replace('\n', '').replace('\r', '');
     }
@@ -46,49 +57,66 @@ module.exports.get_target = function() {
 
 // Returns a promise.
 module.exports.check_ant = function() {
-    var d = Q.defer();
-    child_process.exec('ant -version', function(err, stdout, stderr) {
-        if (err) d.reject(new Error('ERROR : executing command \'ant\', make sure you have ant installed and added to your path.'));
-        else d.resolve();
-    });
-    return d.promise;
+    return tryCommand('ant -version', 'Failed to run "ant -version", make sure you have ant installed and added to your PATH.');
 }
 
 // Returns a promise.
 module.exports.check_java = function() {
-    var d = Q.defer();
-    child_process.exec('java -version', function(err, stdout, stderr) {
-        if(err) {
-            var msg =
-                'Failed to run \'java -version\', make sure your java environment is set up\n' +
-                'including JDK and JRE.\n' +
-                'Your JAVA_HOME variable is ' + process.env.JAVA_HOME + '\n';
-            d.reject(new Error(msg + err));
+    var javacInPath = !!shelljs.which('javac');
+    var hasJavaHome = !!process.env.JAVA_HOME;
+    // Windows java installer doesn't add javac to PATH, nor set JAVA_HOME (ugh).
+    if (hasJavaHome && !javacInPath) {
+        process.env.PATH += path.delimiter + path.join(process.env.JAVA_HOME, 'bin');
+    } else if (isWindows && (!hasJavaHome || !javacInPath)) {
+        // Try to auto-detect java in the default install paths.
+        var firstJdkDir =
+            shelljs.ls(process.env.ProgramFiles + '\\java\\jdk*')[0] ||
+            shelljs.ls('C:\\Program Files\\java\\jdk*')[0] ||
+            shelljs.ls('C:\\Program Files (x86)\\java\\jdk*')[0];
+        if (firstJdkDir) {
+            // shelljs always uses / in paths.
+            firstJdkDir = firstJdkDir.replace(/\//g, path.sep);
+            if (!javacInPath) {
+                process.env.PATH += path.delimiter + path.join(firstJdkDir, 'bin');
+            }
+            process.env.JAVA_HOME = firstJdkDir;
         }
-        else d.resolve();
+    }
+    var msg =
+        'Failed to run "java -version", make sure your java environment is set up\n' +
+        'including JDK and JRE.\n' +
+        'Your JAVA_HOME variable is: ' + process.env.JAVA_HOME;
+    return tryCommand('java -version', msg)
+    .then(function() {
+        msg = 'Failed to run "javac -version", make sure you have a Java JDK (not just a JRE) installed.';
+        return tryCommand('javac -version', msg)
     });
-    return d.promise;
 }
 
 // Returns a promise.
 module.exports.check_android = function() {
-    var valid_target = this.get_target();
-    var d = Q.defer();
-    child_process.exec('android list targets', function(err, stdout, stderr) {
-        if (err) d.reject(stderr);
-        else d.resolve(stdout);
-    });
+    var androidCmdPath = !!shelljs.which('android');
+    var adbInPath = !!shelljs.which('adb');
+    var hasAndroidHome = !!process.env.ANDROID_HOME;
+    if (hasAndroidHome && !androidCmdPath) {
+        process.env.PATH += path.delimiter + path.join(process.env.ANDROID_HOME, 'tools');
+    }
+    if (androidCmdPath && !hasAndroidHome) {
+        process.env.ANDROID_HOME = path.dirname(path.dirname(androidCmdPath));
+        hasAndroidHome = true;
+    }
+    if (hasAndroidHome && !adbInPath) {
+        process.env.PATH += path.delimiter + path.join(process.env.ANDROID_HOME, 'platform-tools');
+    }
 
-    return d.promise.then(function(output) {
+    var valid_target = this.get_target();
+    var msg = 'Failed to run "android". Make sure you have the latest Android SDK installed, and that the "android" command (inside the tools/ folder) is added to your PATH.';
+    return tryCommand('android list targets', msg)
+    .then(function(output) {
         if (!output.match(valid_target)) {
-            return Q.reject(new Error('Please install Android target ' + valid_target.split('-')[1] + ' (the Android newest SDK). Make sure you have the latest Android tools installed as well. Run \"android\" from your command-line to install/update any missing SDKs or tools.'));
-        }
-        return Q();
-    }, function(stderr) {
-        if (stderr.match(/command\snot\sfound/) || stderr.match(/is not recognized as an internal or external command/)) {
-            return Q.reject(new Error('The command \"android\" failed. Make sure you have the latest Android SDK installed, and the \"android\" command (inside the tools/ folder) is added to your path.'));
-        } else {
-            return Q.reject(new Error('An error occurred while listing Android targets. Error: ' + stderr ));
+            return Q.reject(new Error('Please install Android target ' + valid_target.split('-')[1] +
+                ' (the Android newest SDK). Make sure you have the latest Android tools installed as well.' +
+                ' Run "android" from your command-line to install/update any missing SDKs or tools.'));
         }
     });
 }

http://git-wip-us.apache.org/repos/asf/cordova-android/blob/4319447c/bin/lib/create.js
----------------------------------------------------------------------
diff --git a/bin/lib/create.js b/bin/lib/create.js
index b2490f2..7834581 100755
--- a/bin/lib/create.js
+++ b/bin/lib/create.js
@@ -196,7 +196,7 @@ exports.createProject = function(project_path, package_name, project_name, proje
     })
     // Check that requirements are met and proper targets are installed
     .then(function() {
-        check_reqs.run();
+        return check_reqs.run();
     }).then(function() {
         // Log the given values for the project
         console.log('Creating Cordova project for the Android platform:');

http://git-wip-us.apache.org/repos/asf/cordova-android/blob/4319447c/bin/templates/cordova/build
----------------------------------------------------------------------
diff --git a/bin/templates/cordova/build b/bin/templates/cordova/build
index a38f3b6..2d58901 100755
--- a/bin/templates/cordova/build
+++ b/bin/templates/cordova/build
@@ -28,9 +28,9 @@ if(args[2] == '--help' || args[2] == '/?' || args[2] == '-h' ||
                     args[2] == 'help' || args[2] == '-help' || args[2] == '/help') {
     build.help();
 } else {
-    reqs.run().then(function() {
+    reqs.run().done(function() {
         return build.run(args[2]);
-    }).done(null, function(err) {
+    }, function(err) {
         console.error(err);
         process.exit(2);
     });


[15/19] android commit: CB-3445 Add gradle support clean command (plus some code cleanup)

Posted by ag...@apache.org.
CB-3445 Add gradle support clean command (plus some code cleanup)

* Don't run ant clean for gradle ever
* Don't set sdk.dir since ANDROID_HOME is not always set
* Don't export builders


Project: http://git-wip-us.apache.org/repos/asf/cordova-android/repo
Commit: http://git-wip-us.apache.org/repos/asf/cordova-android/commit/d56ea258
Tree: http://git-wip-us.apache.org/repos/asf/cordova-android/tree/d56ea258
Diff: http://git-wip-us.apache.org/repos/asf/cordova-android/diff/d56ea258

Branch: refs/heads/4.0.x
Commit: d56ea25816a72b21e7a15d88ae0dfcb62742a66d
Parents: c91b272
Author: Andrew Grieve <ag...@chromium.org>
Authored: Mon Aug 18 16:19:40 2014 -0400
Committer: Andrew Grieve <ag...@chromium.org>
Committed: Mon Aug 18 16:19:40 2014 -0400

----------------------------------------------------------------------
 bin/templates/cordova/build        |   2 +-
 bin/templates/cordova/clean        |  20 +++--
 bin/templates/cordova/lib/build.js | 137 +++++++++++++++++++++-----------
 bin/templates/cordova/lib/clean.js |  43 ----------
 4 files changed, 104 insertions(+), 98 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cordova-android/blob/d56ea258/bin/templates/cordova/build
----------------------------------------------------------------------
diff --git a/bin/templates/cordova/build b/bin/templates/cordova/build
index 367bf7d..3c3aee4 100755
--- a/bin/templates/cordova/build
+++ b/bin/templates/cordova/build
@@ -33,7 +33,7 @@ if(args[2] == '--help' ||
     build.help();
 } else {
     reqs.run().done(function() {
-        return build.run.call(build.run, args.slice(2));
+        return build.run(args.slice(2));
     }, function(err) {
         console.error(err);
         process.exit(2);

http://git-wip-us.apache.org/repos/asf/cordova-android/blob/d56ea258/bin/templates/cordova/clean
----------------------------------------------------------------------
diff --git a/bin/templates/cordova/clean b/bin/templates/cordova/clean
index 4e0808b..d9a7d49 100755
--- a/bin/templates/cordova/clean
+++ b/bin/templates/cordova/clean
@@ -19,18 +19,26 @@
        under the License.
 */
 
-var clean = require('./lib/clean'),
+var build = require('./lib/build'),
     reqs  = require('./lib/check_reqs'),
     args  = process.argv;
+var path  = require('path');
 
-// Usage support for when args are given
-if(args.length > 2) {
-    clean.help();
+// Support basic help commands
+if(args[2] == '--help' ||
+   args[2] == '/?' ||
+   args[2] == '-h' ||
+   args[2] == 'help' ||
+   args[2] == '-help' ||
+   args[2] == '/help') {
+    console.log('Usage: ' + path.relative(process.cwd(), process.argv[1]));
+    console.log('Cleans the project directory.');
+    process.exit(0);
 } else {
     reqs.run().done(function() {
-        return clean.run();
+        return build.runClean(args.slice(2));
     }, function(err) {
-        console.error('ERROR: ' + err);
+        console.error(err);
         process.exit(2);
     });
 }

http://git-wip-us.apache.org/repos/asf/cordova-android/blob/d56ea258/bin/templates/cordova/lib/build.js
----------------------------------------------------------------------
diff --git a/bin/templates/cordova/lib/build.js b/bin/templates/cordova/lib/build.js
index 80e2282..071990f 100644
--- a/bin/templates/cordova/lib/build.js
+++ b/bin/templates/cordova/lib/build.js
@@ -69,7 +69,7 @@ function copyGradleWrapper() {
     shell.cp('-r', path.join(wrapperDir, 'gradle', 'wrapper'), path.join(projectPath, 'gradle'));
 }
 
-module.exports.builders = {
+var builders = {
     ant: {
         getArgs: function(cmd) {
             var args = [cmd, '-f', path.join(ROOT, 'build.xml')];
@@ -77,29 +77,45 @@ module.exports.builders = {
             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;
         },
 
+        prepEnv: function() {
+            return check_reqs.check_ant()
+            .then(function() {
+            });
+        },
+
         /*
          * Builds the project with ant.
          * Returns a promise.
          */
         build: function(build_type) {
+            // Without our custom_rules.xml, we need to clean before building.
+            var ret = Q();
+            if (!hasCustomRules()) {
+                // clean will call check_ant() for us.
+                ret = this.clean();
+            }
+
             var builder = this;
-            var args = builder.getArgs(build_type == "debug" ? 'debug' : 'release');
-            return Q().then(function() {
+            var args = this.getArgs(build_type == 'debug' ? 'debug' : 'release');
+            return check_reqs.check_ant()
+            .then(function() {
                 return spawn('ant', args);
             }).then(function() {
                 return builder.getOutputFiles();
             });
         },
 
+        clean: function() {
+            var args = this.getArgs('clean');
+            return check_reqs.check_ant()
+            .then(function() {
+                return spawn('ant', args);
+            });
+        },
+
         // Find the recently-generated output APK files
         // Ant only generates one output file; return it.
         getOutputFiles: function() {
@@ -139,6 +155,10 @@ module.exports.builders = {
             return args;
         },
 
+        prepEnv: function() {
+            return Q();
+        },
+
         /*
          * Builds the project with gradle.
          * Returns a promise.
@@ -155,6 +175,18 @@ module.exports.builders = {
             });
         },
 
+        clean: function() {
+            var builder = this;
+            var wrapper = path.join(ROOT, 'gradlew');
+            var args = builder.getArgs('clean');
+            copyGradleWrapper();
+            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) {
@@ -171,18 +203,31 @@ module.exports.builders = {
             });
             return candidates;
         }
+    },
+
+    none: {
+        prepEnv: function() {
+            return Q();
+        },
+        build: function() {
+            console.log('Skipping build...');
+            return Q();
+        },
+        clean: function() {
+            return Q();
+        },
     }
 };
 
-/*
- * Builds the project with the specifed options
- * Returns a promise.
- */
-module.exports.run = function(options) {
-
+function parseOpts(options) {
     // Backwards-compatibility: Allow a single string argument
     if (typeof options == "string") options = [options];
 
+    var ret = {
+        buildType: 'debug',
+        buildMethod: process.env['ANDROID_BUILD'] || 'ant'
+    };
+
     // Iterate through command line options
     for (var i=0; options && (i < options.length); ++i) {
         if (options[i].substring && options[i].substring(0,2) == "--") {
@@ -190,21 +235,15 @@ module.exports.run = function(options) {
             switch(option) {
                 case 'debug':
                 case 'release':
-                    if (build_type) {
-                        return Q.reject('Multiple build types (' + build_type + ' and ' + option + ') specified.');
-                    }
-                    build_type = option;
+                    ret.buildType = option;
                     break;
                 case 'ant':
                 case 'gradle':
-                    if (build_method) {
-                        return Q.reject('Multiple build methods (' + build_method + ' and ' + option + ') specified.');
-                    }
-                    build_method = option;
+                    ret.buildMethod = option;
                     break;
                 case 'nobuild' :
-                    console.log('Skipping build...');
-                    return Q();
+                    ret.buildMethod = 'none';
+                    break;
                 default :
                     return Q.reject('Build option \'' + options[i] + '\' not recognized.');
             }
@@ -212,34 +251,36 @@ module.exports.run = function(options) {
             return Q.reject('Build option \'' + options[i] + '\' not recognized.');
         }
     }
-    // Defaults
-    build_type = build_type || "debug";
-    build_method = build_method || process.env.ANDROID_BUILD || "ant";
+    return ret;
+}
 
-    // Get the builder
-    var builder = module.exports.builders[build_method];
+/*
+ * Builds the project with the specifed options
+ * Returns a promise.
+ */
+module.exports.runClean = function(options) {
+    var opts = parseOpts(options);
+    var builder = builders[opts.buildMethod];
+    return builder.prepEnv(opts.buildType)
+    .then(function() {
+        return builder.clean();
+    });
+};
 
-    // Without our custom_rules.xml, we need to clean before building.
-    var ret;
-    if (!hasCustomRules()) {
-        // clean will call check_ant() for us.
-        ret = require('./clean').run();
-    } else {
-        ret = check_reqs.check_ant();
-    }
+/*
+ * Builds the project with the specifed options
+ * Returns a promise.
+ */
+module.exports.run = function(options) {
+    var opts = parseOpts(options);
 
-    // Return a promise for the actual build
-    return ret.then(function() {
-        return builder.build.call(builder, build_type);
+    var builder = builders[opts.buildMethod];
+    return builder.prepEnv(opts.buildType)
+    .then(function() {
+        return builder.build(opts.buildType);
     }).then(function(apkFiles) {
         var outputDir = path.join(ROOT, 'out');
-        try {
-            fs.mkdirSync(outputDir);
-        } catch (e) {
-            if (e.code != "EEXIST") {
-                throw e;
-            }
-        }
+        shell.mkdir('-p', outputDir);
         for (var i=0; i < apkFiles.length; ++i) {
             shell.cp('-f', apkFiles[i], path.join(outputDir, path.basename(apkFiles[i])));
         }

http://git-wip-us.apache.org/repos/asf/cordova-android/blob/d56ea258/bin/templates/cordova/lib/clean.js
----------------------------------------------------------------------
diff --git a/bin/templates/cordova/lib/clean.js b/bin/templates/cordova/lib/clean.js
deleted file mode 100644
index f215829..0000000
--- a/bin/templates/cordova/lib/clean.js
+++ /dev/null
@@ -1,43 +0,0 @@
-#!/usr/bin/env node
-
-/*
-       Licensed to the Apache Software Foundation (ASF) under one
-       or more contributor license agreements.  See the NOTICE file
-       distributed with this work for additional information
-       regarding copyright ownership.  The ASF licenses this file
-       to you under the Apache License, Version 2.0 (the
-       "License"); you may not use this file except in compliance
-       with the License.  You may obtain a copy of the License at
-
-         http://www.apache.org/licenses/LICENSE-2.0
-
-       Unless required by applicable law or agreed to in writing,
-       software distributed under the License is distributed on an
-       "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-       KIND, either express or implied.  See the License for the
-       specific language governing permissions and limitations
-       under the License.
-*/
-
-var build = require('./build'),
-    spawn = require('./spawn'),
-    path  = require('path');
-var check_reqs = require('./check_reqs');
-
-/*
- * Cleans the project using ant
- * Returns a promise.
- */
-module.exports.run = function() {
-    var args = build.getAntArgs('clean');
-    return check_reqs.check_ant()
-    .then(function() {
-        return spawn('ant', args);
-    });
-}
-
-module.exports.help = function() {
-    console.log('Usage: ' + path.relative(process.cwd(), process.argv[1]));
-    console.log('Cleans the project directory.');
-    process.exit(0);
-}


[12/19] android commit: CB-3445 Add .gradle template files for "update" as well as "create"

Posted by ag...@apache.org.
CB-3445 Add .gradle template files for "update" as well as "create"


Project: http://git-wip-us.apache.org/repos/asf/cordova-android/repo
Commit: http://git-wip-us.apache.org/repos/asf/cordova-android/commit/36eab713
Tree: http://git-wip-us.apache.org/repos/asf/cordova-android/tree/36eab713
Diff: http://git-wip-us.apache.org/repos/asf/cordova-android/diff/36eab713

Branch: refs/heads/4.0.x
Commit: 36eab713a1837f315dc1719314d8370f1d5207f3
Parents: 7133576
Author: Andrew Grieve <ag...@chromium.org>
Authored: Mon Aug 18 14:50:27 2014 -0400
Committer: Andrew Grieve <ag...@chromium.org>
Committed: Mon Aug 18 14:50:27 2014 -0400

----------------------------------------------------------------------
 bin/lib/create.js | 14 ++++++++------
 1 file changed, 8 insertions(+), 6 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cordova-android/blob/36eab713/bin/lib/create.js
----------------------------------------------------------------------
diff --git a/bin/lib/create.js b/bin/lib/create.js
index 312881c..d1f43e6 100755
--- a/bin/lib/create.js
+++ b/bin/lib/create.js
@@ -88,11 +88,13 @@ function runAndroidUpdate(projectPath, target_api, shared) {
     return exec('android update project --subprojects --path "' + projectPath + '" --target ' + target_api + ' --library "' + path.relative(projectPath, targetFrameworkDir) + '"');
 }
 
-function copyAntRules(projectPath) {
+function copyBuildRules(projectPath) {
     var srcDir = path.join(ROOT, 'bin', 'templates', 'project');
-    if (fs.existsSync(path.join(srcDir, 'custom_rules.xml'))) {
-        shell.cp('-f', path.join(srcDir, 'custom_rules.xml'), projectPath);
-    }
+    shell.cp('-f', path.join(srcDir, 'custom_rules.xml'), projectPath);
+
+    shell.cp('-f', path.join(srcDir, 'build.gradle'), projectPath);
+    shell.cp('-f', path.join(srcDir, 'libraries.gradle'), projectPath);
+    shell.cp('-f', path.join(srcDir, 'settings.gradle'), projectPath);
 }
 
 function copyScripts(projectPath) {
@@ -263,7 +265,7 @@ exports.createProject = function(project_path, package_name, project_name, proje
             shell.sed('-i', /__PACKAGE__/, package_name, manifest_path);
             shell.sed('-i', /__APILEVEL__/, target_api.split('-')[1], manifest_path);
             copyScripts(project_path);
-            copyAntRules(project_path);
+            copyBuildRules(project_path);
         });
         // Link it to local android install.
         return runAndroidUpdate(project_path, target_api, use_shared_project);
@@ -298,7 +300,7 @@ exports.updateProject = function(projectPath) {
         var target_api = check_reqs.get_target();
         copyJsAndLibrary(projectPath, false, projectName);
         copyScripts(projectPath);
-        copyAntRules(projectPath);
+        copyBuildRules(projectPath);
         removeDebuggableFromManifest(projectPath);
         return runAndroidUpdate(projectPath, target_api, false)
         .then(function() {


[14/19] android commit: CB-7044 Fix typo in prev commit causing check_reqs to always fail.

Posted by ag...@apache.org.
CB-7044 Fix typo in prev commit causing check_reqs to always fail.


Project: http://git-wip-us.apache.org/repos/asf/cordova-android/repo
Commit: http://git-wip-us.apache.org/repos/asf/cordova-android/commit/c91b2726
Tree: http://git-wip-us.apache.org/repos/asf/cordova-android/tree/c91b2726
Diff: http://git-wip-us.apache.org/repos/asf/cordova-android/diff/c91b2726

Branch: refs/heads/4.0.x
Commit: c91b272648493cfaaad972b42c4868650323ad97
Parents: ca8bb75
Author: Andrew Grieve <ag...@chromium.org>
Authored: Mon Aug 18 15:26:05 2014 -0400
Committer: Andrew Grieve <ag...@chromium.org>
Committed: Mon Aug 18 15:26:52 2014 -0400

----------------------------------------------------------------------
 bin/lib/check_reqs.js | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cordova-android/blob/c91b2726/bin/lib/check_reqs.js
----------------------------------------------------------------------
diff --git a/bin/lib/check_reqs.js b/bin/lib/check_reqs.js
index 61bd449..7794594 100644
--- a/bin/lib/check_reqs.js
+++ b/bin/lib/check_reqs.js
@@ -31,7 +31,7 @@ var isWindows = process.platform == 'win32';
 
 function forgivingWhichSync(cmd) {
     try {
-        return which.sync(path);
+        return which.sync(cmd);
     } catch (e) {
         return '';
     }


[05/19] android commit: CB-3445: Add which to checked-in node_modules

Posted by ag...@apache.org.
CB-3445: Add which to checked-in node_modules


Project: http://git-wip-us.apache.org/repos/asf/cordova-android/repo
Commit: http://git-wip-us.apache.org/repos/asf/cordova-android/commit/fd6a1e5e
Tree: http://git-wip-us.apache.org/repos/asf/cordova-android/tree/fd6a1e5e
Diff: http://git-wip-us.apache.org/repos/asf/cordova-android/diff/fd6a1e5e

Branch: refs/heads/4.0.x
Commit: fd6a1e5ed00baba902279dbc52cb9639b0e78eb3
Parents: 7d6ac87
Author: Ian Clelland <ic...@chromium.org>
Authored: Fri Aug 15 13:35:50 2014 -0400
Committer: Ian Clelland <ic...@chromium.org>
Committed: Mon Aug 18 09:48:31 2014 -0400

----------------------------------------------------------------------
 bin/node_modules/which/LICENSE      |  23 +++++++
 bin/node_modules/which/README.md    |   5 ++
 bin/node_modules/which/bin/which    |  14 +++++
 bin/node_modules/which/package.json |  31 +++++++++
 bin/node_modules/which/which.js     | 104 +++++++++++++++++++++++++++++++
 5 files changed, 177 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cordova-android/blob/fd6a1e5e/bin/node_modules/which/LICENSE
----------------------------------------------------------------------
diff --git a/bin/node_modules/which/LICENSE b/bin/node_modules/which/LICENSE
new file mode 100644
index 0000000..05a4010
--- /dev/null
+++ b/bin/node_modules/which/LICENSE
@@ -0,0 +1,23 @@
+Copyright 2009, 2010, 2011 Isaac Z. Schlueter.
+All rights reserved.
+
+Permission is hereby granted, free of charge, to any person
+obtaining a copy of this software and associated documentation
+files (the "Software"), to deal in the Software without
+restriction, including without limitation the rights to use,
+copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following
+conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE.

http://git-wip-us.apache.org/repos/asf/cordova-android/blob/fd6a1e5e/bin/node_modules/which/README.md
----------------------------------------------------------------------
diff --git a/bin/node_modules/which/README.md b/bin/node_modules/which/README.md
new file mode 100644
index 0000000..ff1eb53
--- /dev/null
+++ b/bin/node_modules/which/README.md
@@ -0,0 +1,5 @@
+The "which" util from npm's guts.
+
+Finds the first instance of a specified executable in the PATH
+environment variable.  Does not cache the results, so `hash -r` is not
+needed when the PATH changes.

http://git-wip-us.apache.org/repos/asf/cordova-android/blob/fd6a1e5e/bin/node_modules/which/bin/which
----------------------------------------------------------------------
diff --git a/bin/node_modules/which/bin/which b/bin/node_modules/which/bin/which
new file mode 100755
index 0000000..8432ce2
--- /dev/null
+++ b/bin/node_modules/which/bin/which
@@ -0,0 +1,14 @@
+#!/usr/bin/env node
+var which = require("../")
+if (process.argv.length < 3) {
+  console.error("Usage: which <thing>")
+  process.exit(1)
+}
+
+which(process.argv[2], function (er, thing) {
+  if (er) {
+    console.error(er.message)
+    process.exit(er.errno || 127)
+  }
+  console.log(thing)
+})

http://git-wip-us.apache.org/repos/asf/cordova-android/blob/fd6a1e5e/bin/node_modules/which/package.json
----------------------------------------------------------------------
diff --git a/bin/node_modules/which/package.json b/bin/node_modules/which/package.json
new file mode 100644
index 0000000..6c5ccb3
--- /dev/null
+++ b/bin/node_modules/which/package.json
@@ -0,0 +1,31 @@
+{
+  "author": {
+    "name": "Isaac Z. Schlueter",
+    "email": "i@izs.me",
+    "url": "http://blog.izs.me"
+  },
+  "name": "which",
+  "description": "Like which(1) unix command. Find the first instance of an executable in the PATH.",
+  "version": "1.0.5",
+  "repository": {
+    "type": "git",
+    "url": "git://github.com/isaacs/node-which.git"
+  },
+  "main": "which.js",
+  "bin": {
+    "which": "./bin/which"
+  },
+  "engines": {
+    "node": "*"
+  },
+  "dependencies": {},
+  "devDependencies": {},
+  "readme": "The \"which\" util from npm's guts.\n\nFinds the first instance of a specified executable in the PATH\nenvironment variable.  Does not cache the results, so `hash -r` is not\nneeded when the PATH changes.\n",
+  "readmeFilename": "README.md",
+  "bugs": {
+    "url": "https://github.com/isaacs/node-which/issues"
+  },
+  "homepage": "https://github.com/isaacs/node-which",
+  "_id": "which@1.0.5",
+  "_from": "which@"
+}

http://git-wip-us.apache.org/repos/asf/cordova-android/blob/fd6a1e5e/bin/node_modules/which/which.js
----------------------------------------------------------------------
diff --git a/bin/node_modules/which/which.js b/bin/node_modules/which/which.js
new file mode 100644
index 0000000..db7e8f7
--- /dev/null
+++ b/bin/node_modules/which/which.js
@@ -0,0 +1,104 @@
+module.exports = which
+which.sync = whichSync
+
+var path = require("path")
+  , fs
+  , COLON = process.platform === "win32" ? ";" : ":"
+  , isExe
+
+try {
+  fs = require("graceful-fs")
+} catch (ex) {
+  fs = require("fs")
+}
+
+if (process.platform == "win32") {
+  // On windows, there is no good way to check that a file is executable
+  isExe = function isExe () { return true }
+} else {
+  isExe = function isExe (mod, uid, gid) {
+    //console.error(mod, uid, gid);
+    //console.error("isExe?", (mod & 0111).toString(8))
+    var ret = (mod & 0001)
+        || (mod & 0010) && process.getgid && gid === process.getgid()
+        || (mod & 0100) && process.getuid && uid === process.getuid()
+    //console.error("isExe?", ret)
+    return ret
+  }
+}
+
+
+
+function which (cmd, cb) {
+  if (isAbsolute(cmd)) return cb(null, cmd)
+  var pathEnv = (process.env.PATH || "").split(COLON)
+    , pathExt = [""]
+  if (process.platform === "win32") {
+    pathEnv.push(process.cwd())
+    pathExt = (process.env.PATHEXT || ".EXE").split(COLON)
+    if (cmd.indexOf(".") !== -1) pathExt.unshift("")
+  }
+  //console.error("pathEnv", pathEnv)
+  ;(function F (i, l) {
+    if (i === l) return cb(new Error("not found: "+cmd))
+    var p = path.resolve(pathEnv[i], cmd)
+    ;(function E (ii, ll) {
+      if (ii === ll) return F(i + 1, l)
+      var ext = pathExt[ii]
+      //console.error(p + ext)
+      fs.stat(p + ext, function (er, stat) {
+        if (!er &&
+            stat &&
+            stat.isFile() &&
+            isExe(stat.mode, stat.uid, stat.gid)) {
+          //console.error("yes, exe!", p + ext)
+          return cb(null, p + ext)
+        }
+        return E(ii + 1, ll)
+      })
+    })(0, pathExt.length)
+  })(0, pathEnv.length)
+}
+
+function whichSync (cmd) {
+  if (isAbsolute(cmd)) return cmd
+  var pathEnv = (process.env.PATH || "").split(COLON)
+    , pathExt = [""]
+  if (process.platform === "win32") {
+    pathEnv.push(process.cwd())
+    pathExt = (process.env.PATHEXT || ".EXE").split(COLON)
+    if (cmd.indexOf(".") !== -1) pathExt.unshift("")
+  }
+  for (var i = 0, l = pathEnv.length; i < l; i ++) {
+    var p = path.join(pathEnv[i], cmd)
+    for (var j = 0, ll = pathExt.length; j < ll; j ++) {
+      var cur = p + pathExt[j]
+      var stat
+      try { stat = fs.statSync(cur) } catch (ex) {}
+      if (stat &&
+          stat.isFile() &&
+          isExe(stat.mode, stat.uid, stat.gid)) return cur
+    }
+  }
+  throw new Error("not found: "+cmd)
+}
+
+var isAbsolute = process.platform === "win32" ? absWin : absUnix
+
+function absWin (p) {
+  if (absUnix(p)) return true
+  // pull off the device/UNC bit from a windows path.
+  // from node's lib/path.js
+  var splitDeviceRe =
+        /^([a-zA-Z]:|[\\\/]{2}[^\\\/]+[\\\/][^\\\/]+)?([\\\/])?/
+    , result = splitDeviceRe.exec(p)
+    , device = result[1] || ''
+    , isUnc = device && device.charAt(1) !== ':'
+    , isAbsolute = !!result[2] || isUnc // UNC paths are always absolute
+
+  return isAbsolute
+}
+
+function absUnix (p) {
+  return p.charAt(0) === "/" || p === ""
+}