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 05:27:49 UTC

android commit: CB-7330 Don't run "android update" during creation

Repository: cordova-android
Updated Branches:
  refs/heads/master d56ea2581 -> dfa66b9dd


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/master
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