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 2015/04/02 01:55:23 UTC
android commit: CB-8484 Add signing flags to `build` and `run` scripts
Repository: cordova-android
Updated Branches:
refs/heads/master 51adf8191 -> ad1c3d243
CB-8484 Add signing flags to `build` and `run` scripts
Parameters for creating signed archives can be specified using command line or build.json file as part of the --buildConfig argument.
close #164
Project: http://git-wip-us.apache.org/repos/asf/cordova-android/repo
Commit: http://git-wip-us.apache.org/repos/asf/cordova-android/commit/ad1c3d24
Tree: http://git-wip-us.apache.org/repos/asf/cordova-android/tree/ad1c3d24
Diff: http://git-wip-us.apache.org/repos/asf/cordova-android/diff/ad1c3d24
Branch: refs/heads/master
Commit: ad1c3d24386edcd7f1c240a7d5d06355b9680c53
Parents: 51adf81
Author: Nikhil Khandelwal <ni...@microsoft.com>
Authored: Tue Mar 10 18:13:13 2015 -0700
Committer: Andrew Grieve <ag...@chromium.org>
Committed: Wed Apr 1 19:53:56 2015 -0400
----------------------------------------------------------------------
bin/templates/cordova/lib/build.js | 193 +++++++++++++++++++++++++++-----
bin/templates/cordova/lib/run.js | 2 +-
2 files changed, 168 insertions(+), 27 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/cordova-android/blob/ad1c3d24/bin/templates/cordova/lib/build.js
----------------------------------------------------------------------
diff --git a/bin/templates/cordova/lib/build.js b/bin/templates/cordova/lib/build.js
index 75dc0c7..42be23e 100644
--- a/bin/templates/cordova/lib/build.js
+++ b/bin/templates/cordova/lib/build.js
@@ -31,9 +31,12 @@ var shell = require('shelljs'),
var check_reqs = require('./check_reqs');
var exec = require('./exec');
-var LOCAL_PROPERTIES_TEMPLATE =
+
+var SIGNING_PROPERTIES = '-signing.properties';
+var MARKER = 'YOUR CHANGES WILL BE ERASED!';
+var TEMPLATE =
'# This file is automatically generated.\n' +
- '# Do not modify this file -- YOUR CHANGES WILL BE ERASED!\n';
+ '# Do not modify this file -- ' + MARKER + '\n';
function findApks(directory) {
var ret = [];
@@ -56,6 +59,14 @@ function sortFilesByDate(files) {
}).map(function(p) { return p.p; });
}
+function isAutoGenerated(file) {
+ if(fs.existsSync(file)) {
+ var fileContents = fs.readFileSync(file, 'utf8');
+ return fileContents.indexOf(MARKER) > 0;
+ }
+ return false;
+}
+
function findOutputApksHelper(dir, build_type, arch) {
var ret = findApks(dir).filter(function(candidate) {
// Need to choose between release and debug .apk.
@@ -121,16 +132,19 @@ function readProjectProperties() {
var builders = {
ant: {
- getArgs: function(cmd) {
+ getArgs: function(cmd, opts) {
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');
}
+ if(opts.packageInfo) {
+ args.push('-propertyfile=' + path.join(ROOT, opts.buildType + SIGNING_PROPERTIES));
+ }
return args;
},
- prepEnv: function() {
+ prepEnv: function(opts) {
return check_reqs.check_ant()
.then(function() {
// Copy in build.xml on each build so that:
@@ -142,7 +156,7 @@ var builders = {
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);
+ fs.writeFileSync(path.join(projectPath, 'local.properties'), TEMPLATE);
}
}
writeBuildXml(ROOT);
@@ -154,6 +168,14 @@ var builders = {
if (propertiesObj.systemLibs.length > 0) {
throw new Error('Project contains at least one plugin that requires a system library. This is not supported with ANT. Please build using gradle.');
}
+
+ var propertiesFile = opts.buildType + SIGNING_PROPERTIES;
+ var propertiesFilePath = path.join(ROOT, propertiesFile);
+ if (opts.packageInfo) {
+ fs.writeFileSync(propertiesFilePath, TEMPLATE + opts.packageInfo.toProperties());
+ } else if(isAutoGenerated(propertiesFilePath)) {
+ shell.rm('-f', propertiesFilePath);
+ }
});
},
@@ -161,23 +183,24 @@ var builders = {
* Builds the project with ant.
* Returns a promise.
*/
- build: function(build_type) {
+ build: function(opts) {
// 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();
+ ret = this.clean(opts);
}
- var args = this.getArgs(build_type == 'debug' ? 'debug' : 'release');
+ var args = this.getArgs(opts.buildType == 'debug' ? 'debug' : 'release', opts);
return check_reqs.check_ant()
.then(function() {
+ console.log('Executing: ant ' + args.join(' '));
return spawn('ant', args);
});
},
- clean: function() {
- var args = this.getArgs('clean');
+ clean: function(opts) {
+ var args = this.getArgs('clean', opts);
return check_reqs.check_ant()
.then(function() {
return spawn('ant', args);
@@ -190,20 +213,20 @@ var builders = {
}
},
gradle: {
- getArgs: function(cmd, arch, extraArgs) {
+ getArgs: function(cmd, opts) {
if (cmd == 'release') {
cmd = 'cdvBuildRelease';
} else if (cmd == 'debug') {
cmd = 'cdvBuildDebug';
}
var args = [cmd, '-b', path.join(ROOT, 'build.gradle')];
- if (arch) {
- args.push('-PcdvBuildArch=' + arch);
+ if (opts.arch) {
+ args.push('-PcdvBuildArch=' + opts.arch);
}
// 10 seconds -> 6 seconds
args.push('-Dorg.gradle.daemon=true');
- args.push.apply(args, extraArgs);
+ args.push.apply(args, opts.extraArgs);
// Shaves another 100ms, but produces a "try at own risk" warning. Not worth it (yet):
// args.push('-Dorg.gradle.parallel=true');
return args;
@@ -268,7 +291,7 @@ var builders = {
fs.writeFileSync(path.join(projectPath, 'build.gradle'), buildGradle);
},
- prepEnv: function() {
+ prepEnv: function(opts) {
var self = this;
return check_reqs.check_gradle()
.then(function() {
@@ -297,6 +320,14 @@ var builders = {
var distributionUrl = 'distributionUrl=http\\://services.gradle.org/distributions/gradle-2.2.1-all.zip';
var gradleWrapperPropertiesPath = path.join(projectPath, 'gradle', 'wrapper', 'gradle-wrapper.properties');
shell.sed('-i', distributionUrlRegex, distributionUrl, gradleWrapperPropertiesPath);
+
+ var propertiesFile = opts.buildType + SIGNING_PROPERTIES;
+ var propertiesFilePath = path.join(ROOT, propertiesFile);
+ if (opts.packageInfo) {
+ fs.writeFileSync(propertiesFilePath, TEMPLATE + opts.packageInfo.toProperties());
+ } else if (isAutoGenerated(propertiesFilePath)) {
+ shell.rm('-f', propertiesFilePath);
+ }
});
},
@@ -304,19 +335,19 @@ var builders = {
* Builds the project with gradle.
* Returns a promise.
*/
- build: function(build_type, arch, extraArgs) {
+ build: function(opts) {
var wrapper = path.join(ROOT, 'gradlew');
- var args = this.getArgs(build_type == 'debug' ? 'debug' : 'release', arch, extraArgs);
+ var args = this.getArgs(opts.buildType == 'debug' ? 'debug' : 'release', opts);
return Q().then(function() {
console.log('Running: ' + wrapper + ' ' + args.join(' '));
return spawn(wrapper, args);
});
},
- clean: function(extraArgs) {
+ clean: function(opts) {
var builder = this;
var wrapper = path.join(ROOT, 'gradlew');
- var args = builder.getArgs('clean', null, extraArgs);
+ var args = builder.getArgs('clean', opts);
return Q().then(function() {
console.log('Running: ' + wrapper + ' ' + args.join(' '));
return spawn(wrapper, args);
@@ -346,6 +377,10 @@ var builders = {
}
};
+module.exports.isBuildFlag = function(flag) {
+ return /^--(debug|release|ant|gradle|nobuild|versionCode=|minSdkVersion=|gradleArg=|keystore=|alias=|password=|storePassword=|keystoreType=|buildConfig=)/.exec(flag);
+};
+
function parseOpts(options, resolvedTarget) {
// Backwards-compatibility: Allow a single string argument
if (typeof options == 'string') options = [options];
@@ -360,9 +395,16 @@ function parseOpts(options, resolvedTarget) {
var multiValueArgs = {
'versionCode': true,
'minSdkVersion': true,
- 'gradleArg': true
+ 'gradleArg': true,
+ 'keystore' : true,
+ 'alias' : true,
+ 'password' : true,
+ 'storePassword' : true,
+ 'keystoreType' : true,
+ 'buildConfig' : true
};
-
+ var packageArgs = {};
+ var buildConfig;
// Iterate through command line options
for (var i=0; options && (i < options.length); ++i) {
if (/^--/.exec(options[i])) {
@@ -402,6 +444,18 @@ function parseOpts(options, resolvedTarget) {
case 'gradleArg':
ret.extraArgs.push(flagValue);
break;
+ case 'keystore':
+ packageArgs.keystore = path.relative(ROOT, path.resolve(flagValue));
+ break;
+ case 'alias':
+ case 'storePassword':
+ case 'password':
+ case 'keystoreType':
+ packageArgs[flagName] = flagValue;
+ break;
+ case 'buildConfig':
+ buildConfig = flagValue;
+ break;
default :
console.warn('Build option --\'' + flagName + '\' not recognized (ignoring).');
}
@@ -410,6 +464,36 @@ function parseOpts(options, resolvedTarget) {
}
}
+ // If some values are not specified as command line arguments - use build config to supplement them.
+ // Command line arguemnts have precedence over build config.
+ if (buildConfig) {
+ console.log(path.resolve(buildConfig));
+ if (!fs.existsSync(buildConfig)) {
+ throw new Error('Specified build config file does not exist: ' + buildConfig);
+ }
+ console.log('Reading build config file: '+ buildConfig);
+ var config = JSON.parse(fs.readFileSync(buildConfig, 'utf8'));
+ if (config.android && config.android[ret.buildType]) {
+ var androidInfo = config.android[ret.buildType];
+ if(androidInfo.keystore) {
+ packageArgs.keystore = packageArgs.keystore || path.relative(ROOT, path.join(path.dirname(buildConfig), androidInfo.keystore));
+ }
+
+ ['alias', 'storePassword', 'password','keystoreType'].forEach(function (key){
+ packageArgs[key] = packageArgs[key] || androidInfo[key];
+ });
+ }
+ }
+ if (packageArgs.keystore && packageArgs.alias) {
+ ret.packageInfo = new PackageInfo(packageArgs.keystore, packageArgs.alias, packageArgs.storePassword,
+ packageArgs.password, packageArgs.keystoreType);
+ }
+
+ if(!ret.packageInfo) {
+ if(Object.keys(packageArgs).length > 0) {
+ console.warn('\'keystore\' and \'alias\' need to be specified to generate a signed archive.');
+ }
+ }
ret.arch = resolvedTarget && resolvedTarget.arch;
return ret;
@@ -422,11 +506,18 @@ function parseOpts(options, resolvedTarget) {
module.exports.runClean = function(options) {
var opts = parseOpts(options);
var builder = builders[opts.buildMethod];
- return builder.prepEnv()
+ return builder.prepEnv(opts)
.then(function() {
- return builder.clean(opts.extraArgs);
+ return builder.clean(opts);
}).then(function() {
shell.rm('-rf', path.join(ROOT, 'out'));
+
+ ['debug', 'release'].forEach(function(config) {
+ var propertiesFilePath = path.join(ROOT, config + SIGNING_PROPERTIES);
+ if(isAutoGenerated(propertiesFilePath)){
+ shell.rm('-f', propertiesFilePath);
+ }
+ });
});
};
@@ -437,13 +528,13 @@ module.exports.runClean = function(options) {
module.exports.run = function(options, optResolvedTarget) {
var opts = parseOpts(options, optResolvedTarget);
var builder = builders[opts.buildMethod];
- return builder.prepEnv()
+ return builder.prepEnv(opts)
.then(function() {
if (opts.prepEnv) {
console.log('Build file successfully prepared.');
return;
}
- return builder.build(opts.buildType, opts.arch, opts.extraArgs)
+ return builder.build(opts)
.then(function() {
var apkPaths = builder.findOutputApks(opts.buildType, opts.arch);
console.log('Built the following apk(s):');
@@ -530,8 +621,51 @@ module.exports.findBestApkForArchitecture = function(buildResults, arch) {
throw new Error('Could not find apk architecture: ' + arch + ' build-type: ' + buildResults.buildType);
};
+function PackageInfo(keystore, alias, storePassword, password, keystoreType) {
+ this.keystore = {
+ 'name': 'key.store',
+ 'value': keystore
+ };
+ this.alias = {
+ 'name': 'key.alias',
+ 'value': alias
+ };
+ if (storePassword) {
+ this.storePassword = {
+ 'name': 'key.store.password',
+ 'value': storePassword
+ };
+ }
+ if (password) {
+ this.password = {
+ 'name': 'key.alias.password',
+ 'value': password
+ };
+ }
+ if (keystoreType) {
+ this.keystoreType = {
+ 'name': 'key.store.type',
+ 'value': keystoreType
+ };
+ }
+}
+
+PackageInfo.prototype = {
+ toProperties: function() {
+ var self = this;
+ var result = '';
+ Object.keys(self).forEach(function(key) {
+ result += self[key].name;
+ result += '=';
+ result += self[key].value.replace(/\\/g, '\\\\');
+ result += '\n';
+ });
+ return result;
+ }
+};
+
module.exports.help = function() {
- console.log('Usage: ' + path.relative(process.cwd(), path.join(ROOT, 'cordova', 'build')) + ' [flags]');
+ console.log('Usage: ' + path.relative(process.cwd(), path.join(ROOT, 'cordova', 'build')) + ' [flags] [Signed APK flags]');
console.log('Flags:');
console.log(' \'--debug\': will build project in debug mode (default)');
console.log(' \'--release\': will build project for release');
@@ -542,5 +676,12 @@ module.exports.help = function() {
console.log(' \'--versionCode=#\': Override versionCode for this build. Useful for uploading multiple APKs. Requires --gradle.');
console.log(' \'--minSdkVersion=#\': Override minSdkVersion for this build. Useful for uploading multiple APKs. Requires --gradle.');
console.log(' \'--gradleArg=<gradle command line arg>\': Extra args to pass to the gradle command. Use one flag per arg. Ex. --gradleArg=-PcdvBuildMultipleApks=true');
+ console.log('');
+ console.log('Signed APK flags (overwrites debug/release-signing.proprties) :');
+ console.log(' \'--keystore=<path to keystore>\': Key store used to build a signed archive. (Required)');
+ console.log(' \'--alias=\': Alias for the key store. (Required)');
+ console.log(' \'--storePassword=\': Password for the key store. (Optional - prompted)');
+ console.log(' \'--password=\': Password for the key. (Optional - prompted)');
+ console.log(' \'--keystoreType\': Type of the keystore. (Optional)');
process.exit(0);
};
http://git-wip-us.apache.org/repos/asf/cordova-android/blob/ad1c3d24/bin/templates/cordova/lib/run.js
----------------------------------------------------------------------
diff --git a/bin/templates/cordova/lib/run.js b/bin/templates/cordova/lib/run.js
index d18c02e..7f15448 100644
--- a/bin/templates/cordova/lib/run.js
+++ b/bin/templates/cordova/lib/run.js
@@ -41,7 +41,7 @@ var path = require('path'),
var list = false;
for (var i=2; i<args.length; i++) {
- if (/^--(debug|release|ant|gradle|nobuild|versionCode=|minSdkVersion=|gradleArg=)/.exec(args[i])) {
+ if (build.isBuildFlag(args[i])) {
buildFlags.push(args[i]);
} else if (args[i] == '--device') {
install_target = '--device';
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@cordova.apache.org
For additional commands, e-mail: commits-help@cordova.apache.org