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:25 UTC
[19/19] android commit: Merge branch 'master' into 4.0.x (build &
create script updates)
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