Made changes so cordova/build builds with the new project. Need to work on plugin installation.

This commit is contained in:
Joe Bowser 2017-04-11 13:47:40 -07:00
parent 8ead919fae
commit db87e0ae6a
7 changed files with 108 additions and 52 deletions

View File

@ -1,3 +1,5 @@
/* /*
Licensed to the Apache Software Foundation (ASF) under one Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file or more contributor license agreements. See the NOTICE file
@ -66,38 +68,65 @@ module.exports.version_string_to_api_level = {
'7.1.1': 25 '7.1.1': 25
}; };
function parse_targets(output) { module.exports.list_targets_with_android = function() {
var target_out = output.split('\n'); return superspawn.spawn('android', ['list', 'targets'])
.then(function(stdout) {
var target_out = stdout.split('\n');
var targets = []; var targets = [];
for (var i = target_out.length - 1; i >= 0; i--) { for (var i = target_out.length - 1; i >= 0; i--) {
if(target_out[i].match(/id:/)) { // if "id:" is in the line... if(target_out[i].match(/id:/)) {
targets.push(target_out[i].match(/"(.+)"/)[1]); //.. match whatever is in quotes. targets.push(target_out[i].match(/"(.+)"/)[1]);
} }
} }
return targets; return targets;
} });
module.exports.list_targets_with_android = function() {
return superspawn.spawn('android', ['list', 'target'])
.then(parse_targets);
}; };
module.exports.list_targets_with_avdmanager = function() { module.exports.list_targets_with_sdkmanager = function() {
return superspawn.spawn('avdmanager', ['list', 'target']) return superspawn.spawn('sdkmanager', ['--list'])
.then(parse_targets); .then(function(stdout) {
var parsing_installed_packages = false;
var lines = stdout.split('\n');
var targets = [];
for (var i = 0, l = lines.length; i < l; i++) {
var line = lines[i];
if (line.match(/Installed packages/)) {
parsing_installed_packages = true;
} else if (line.match(/Available Packages/) || line.match(/Available Updates/)) {
// we are done working through installed packages, exit
break;
}
if (parsing_installed_packages) {
// Match stock android platform
if (line.match(/platforms;android-\d+/)) {
targets.push(line.match(/(android-\d+)/)[1]);
}
// Match Google APIs
if (line.match(/addon-google_apis-google-\d+/)) {
var description = lines[i + 1];
// munge description to match output from old android sdk tooling
var api_level = description.match(/Android (\d+)/); //[1];
if (api_level) {
targets.push('Google Inc.:Google APIs:' + api_level[1]);
}
}
// TODO: match anything else?
}
}
return targets;
});
}; };
module.exports.list_targets = function() { module.exports.list_targets = function() {
return module.exports.list_targets_with_avdmanager() return module.exports.list_targets_with_android()
.catch(function(err) { .catch(function(err) {
// If there's an error, like avdmanager could not be found, we can try // there's a chance `android` no longer works.
// as a last resort, to run `android`, in case this is a super old // lets see if `sdkmanager` is available and we can figure it out
// SDK installation. var avail_regex = /"?android"? command is no longer available/;
if (err && (err.code == 'ENOENT' || (err.stderr && err.stderr.match(/not recognized/)))) { if (err.code && ((err.stdout && err.stdout.match(avail_regex)) || (err.stderr && err.stderr.match(avail_regex)))) {
return module.exports.list_targets_with_android(); return module.exports.list_targets_with_sdkmanager();
} else throw err; } else throw err;
}) }).then(function(targets) {
.then(function(targets) {
if (targets.length === 0) { if (targets.length === 0) {
return Q.reject(new Error('No android targets (SDKs) installed!')); return Q.reject(new Error('No android targets (SDKs) installed!'));
} }

View File

@ -35,7 +35,7 @@ function parseOpts(options, resolvedTarget, projectRoot) {
options = options || {}; options = options || {};
options.argv = nopt({ options.argv = nopt({
gradle: Boolean, gradle: Boolean,
ant: Boolean, studio: Boolean,
prepenv: Boolean, prepenv: Boolean,
versionCode: String, versionCode: String,
minSdkVersion: String, minSdkVersion: String,
@ -55,8 +55,8 @@ function parseOpts(options, resolvedTarget, projectRoot) {
extraArgs: [] extraArgs: []
}; };
if (options.argv.ant || options.argv.gradle) if (options.argv.gradle || options.argv.studio)
ret.buildMethod = options.argv.ant ? 'ant' : 'gradle'; ret.buildMethod = options.argv.studio ? 'studio' : 'gradle';
if (options.nobuild) ret.buildMethod = 'none'; if (options.nobuild) ret.buildMethod = 'none';

View File

@ -27,6 +27,7 @@ var CordovaError = require('cordova-common').CordovaError;
function GenericBuilder (projectDir) { function GenericBuilder (projectDir) {
this.root = projectDir || path.resolve(__dirname, '../../..'); this.root = projectDir || path.resolve(__dirname, '../../..');
this.binDirs = { this.binDirs = {
studio: path.join(this.root, 'app', 'build', 'outputs', 'apk'),
gradle: path.join(this.root, 'build', 'outputs', 'apk') gradle: path.join(this.root, 'build', 'outputs', 'apk')
}; };
} }
@ -52,6 +53,7 @@ GenericBuilder.prototype.findOutputApks = function(build_type, arch) {
var self = this; var self = this;
return Object.keys(this.binDirs) return Object.keys(this.binDirs)
.reduce(function (result, builderName) { .reduce(function (result, builderName) {
console.log('builderName:'+ builderName);
var binDir = self.binDirs[builderName]; var binDir = self.binDirs[builderName];
return result.concat(findOutputApksHelper(binDir, build_type, builderName === 'ant' ? null : arch)); return result.concat(findOutputApksHelper(binDir, build_type, builderName === 'ant' ? null : arch));
}, []) }, [])

View File

@ -79,6 +79,10 @@ GradleBuilder.prototype.runGradleWrapper = function(gradle_cmd) {
} }
}; };
/*
* We need to kill this in a fire.
*/
GradleBuilder.prototype.readProjectProperties = function () { GradleBuilder.prototype.readProjectProperties = function () {
function findAllUniq(data, r) { function findAllUniq(data, r) {
var s = {}; var s = {};
@ -117,6 +121,9 @@ GradleBuilder.prototype.prepBuildFiles = function() {
var pluginBuildGradle = path.join(this.root, 'cordova', 'lib', 'plugin-build.gradle'); var pluginBuildGradle = path.join(this.root, 'cordova', 'lib', 'plugin-build.gradle');
var propertiesObj = this.readProjectProperties(); var propertiesObj = this.readProjectProperties();
var subProjects = propertiesObj.libs; var subProjects = propertiesObj.libs;
// Check and copy the gradle file into the subproject.
// Called by the loop below this function def.
var checkAndCopy = function(subProject, root) { var checkAndCopy = function(subProject, root) {
var subProjectGradle = path.join(root, subProject, 'build.gradle'); var subProjectGradle = path.join(root, subProject, 'build.gradle');
// This is the future-proof way of checking if a file exists // This is the future-proof way of checking if a file exists
@ -127,11 +134,16 @@ GradleBuilder.prototype.prepBuildFiles = function() {
shell.cp('-f', pluginBuildGradle, subProjectGradle); shell.cp('-f', pluginBuildGradle, subProjectGradle);
} }
}; };
// Some dependencies on Android don't use gradle, or don't have default
// gradle files. This copies a dummy gradle file into them
for (var i = 0; i < subProjects.length; ++i) { for (var i = 0; i < subProjects.length; ++i) {
if (subProjects[i] !== 'CordovaLib') { if (subProjects[i] !== 'CordovaLib' && subProjects[i] !== 'app') {
checkAndCopy(subProjects[i], this.root); checkAndCopy(subProjects[i], this.root);
} }
} }
var name = this.extractRealProjectNameFromManifest(); var name = this.extractRealProjectNameFromManifest();
//Remove the proj.id/name- prefix from projects: https://issues.apache.org/jira/browse/CB-9149 //Remove the proj.id/name- prefix from projects: https://issues.apache.org/jira/browse/CB-9149
var settingsGradlePaths = subProjects.map(function(p){ var settingsGradlePaths = subProjects.map(function(p){
@ -151,6 +163,11 @@ GradleBuilder.prototype.prepBuildFiles = function() {
var buildGradle = fs.readFileSync(path.join(this.root, 'build.gradle'), 'utf8'); var buildGradle = fs.readFileSync(path.join(this.root, 'build.gradle'), 'utf8');
var depsList = ''; var depsList = '';
var root = this.root; var root = this.root;
// Cordova Plugins can be written as library modules that would use Cordova as a
// dependency. Because we need to make sure that Cordova is compiled only once for
// dexing, we make sure to exclude CordovaLib from these modules
var insertExclude = function(p) { var insertExclude = function(p) {
var gradlePath = path.join(root, p, 'build.gradle'); var gradlePath = path.join(root, p, 'build.gradle');
var projectGradleFile = fs.readFileSync(gradlePath, 'utf-8'); var projectGradleFile = fs.readFileSync(gradlePath, 'utf-8');
@ -161,6 +178,7 @@ GradleBuilder.prototype.prepBuildFiles = function() {
depsList +='\n'; depsList +='\n';
} }
}; };
subProjects.forEach(function(p) { subProjects.forEach(function(p) {
console.log('Subproject Path: ' + p); console.log('Subproject Path: ' + p);
var libName=p.replace(/[/\\]/g, ':').replace(name+'-',''); var libName=p.replace(/[/\\]/g, ':').replace(name+'-','');
@ -169,11 +187,14 @@ GradleBuilder.prototype.prepBuildFiles = function() {
depsList += ' releaseCompile(project(path: "' + libName + '", configuration: "release"))'; depsList += ' releaseCompile(project(path: "' + libName + '", configuration: "release"))';
insertExclude(p); insertExclude(p);
}); });
// For why we do this mapping: https://issues.apache.org/jira/browse/CB-8390 // For why we do this mapping: https://issues.apache.org/jira/browse/CB-8390
var SYSTEM_LIBRARY_MAPPINGS = [ var SYSTEM_LIBRARY_MAPPINGS = [
[/^\/?extras\/android\/support\/(.*)$/, 'com.android.support:support-$1:+'], [/^\/?extras\/android\/support\/(.*)$/, 'com.android.support:support-$1:+'],
[/^\/?google\/google_play_services\/libproject\/google-play-services_lib\/?$/, 'com.google.android.gms:play-services:+'] [/^\/?google\/google_play_services\/libproject\/google-play-services_lib\/?$/, 'com.google.android.gms:play-services:+']
]; ];
propertiesObj.systemLibs.forEach(function(p) { propertiesObj.systemLibs.forEach(function(p) {
var mavenRef; var mavenRef;
// It's already in gradle form if it has two ':'s // It's already in gradle form if it has two ':'s
@ -193,6 +214,9 @@ GradleBuilder.prototype.prepBuildFiles = function() {
} }
depsList += ' compile "' + mavenRef + '"\n'; depsList += ' compile "' + mavenRef + '"\n';
}); });
//This code is dangerous and actually writes gradle declarations directly into the build.gradle
//Try not to mess with this if possible
buildGradle = buildGradle.replace(/(SUB-PROJECT DEPENDENCIES START)[\s\S]*(\/\/ SUB-PROJECT DEPENDENCIES END)/, '$1\n' + depsList + ' $2'); buildGradle = buildGradle.replace(/(SUB-PROJECT DEPENDENCIES START)[\s\S]*(\/\/ SUB-PROJECT DEPENDENCIES END)/, '$1\n' + depsList + ' $2');
var includeList = ''; var includeList = '';
propertiesObj.gradleIncludes.forEach(function(includePath) { propertiesObj.gradleIncludes.forEach(function(includePath) {

View File

@ -37,7 +37,7 @@ var TEMPLATE =
function StudioBuilder (projectRoot) { function StudioBuilder (projectRoot) {
GenericBuilder.call(this, projectRoot); GenericBuilder.call(this, projectRoot);
this.binDirs = {gradle: this.binDirs.gradle}; this.binDirs = {gradle: this.binDirs.studio};
} }
util.inherits(StudioBuilder, GenericBuilder); util.inherits(StudioBuilder, GenericBuilder);
@ -81,6 +81,9 @@ StudioBuilder.prototype.runGradleWrapper = function(gradle_cmd) {
StudioBuilder.prototype.readProjectProperties = function () { StudioBuilder.prototype.readProjectProperties = function () {
console.log("Do we even have to do this?");
function findAllUniq(data, r) { function findAllUniq(data, r) {
var s = {}; var s = {};
var m; var m;
@ -145,10 +148,15 @@ StudioBuilder.prototype.prepBuildFiles = function() {
return str; return str;
}); });
// We really shouldn't do this.
// Write the settings.gradle file. // Write the settings.gradle file.
/*
fs.writeFileSync(path.join(this.root, 'settings.gradle'), fs.writeFileSync(path.join(this.root, 'settings.gradle'),
'// GENERATED FILE - DO NOT EDIT\n' + '// GENERATED FILE - DO NOT EDIT\n' +
'include ":"\n' + settingsGradlePaths.join('')); 'include ":"\n' + settingsGradlePaths.join(''));
*/
// Update dependencies within build.gradle. // Update dependencies within build.gradle.
var buildGradle = fs.readFileSync(path.join(this.root, 'build.gradle'), 'utf8'); var buildGradle = fs.readFileSync(path.join(this.root, 'build.gradle'), 'utf8');
var depsList = ''; var depsList = '';
@ -212,21 +220,6 @@ StudioBuilder.prototype.prepEnv = function(opts) {
}).then(function() { }).then(function() {
return self.prepBuildFiles(); return self.prepBuildFiles();
}).then(function() { }).then(function() {
// We now copy the gradle out of the framework
// This is a dirty patch to get the build working
/*
var wrapperDir = path.join(self.root, 'CordovaLib');
if (process.platform == 'win32') {
shell.rm('-f', path.join(self.root, 'gradlew.bat'));
shell.cp(path.join(wrapperDir, 'gradlew.bat'), self.root);
} else {
shell.rm('-f', path.join(self.root, 'gradlew'));
shell.cp(path.join(wrapperDir, 'gradlew'), self.root);
}
shell.rm('-rf', path.join(self.root, 'gradle', 'wrapper'));
shell.mkdir('-p', path.join(self.root, 'gradle'));
shell.cp('-r', path.join(wrapperDir, 'gradle', 'wrapper'), path.join(self.root, 'gradle'));
*/
// If the gradle distribution URL is set, make sure it points to version we want. // If the gradle distribution URL is set, make sure it points to version we want.
// If it's not set, do nothing, assuming that we're using a future version of gradle that we don't want to mess with. // If it's not set, do nothing, assuming that we're using a future version of gradle that we don't want to mess with.
// For some reason, using ^ and $ don't work. This does the job, though. // For some reason, using ^ and $ don't work. This does the job, though.

View File

@ -85,7 +85,7 @@ module.exports.install = function(target, buildResults) {
return module.exports.resolveTarget(target); return module.exports.resolveTarget(target);
}).then(function(resolvedTarget) { }).then(function(resolvedTarget) {
var apk_path = build.findBestApkForArchitecture(buildResults, resolvedTarget.arch); var apk_path = build.findBestApkForArchitecture(buildResults, resolvedTarget.arch);
var manifest = new AndroidManifest(path.join(__dirname, '../../AndroidManifest.xml')); var manifest = new AndroidManifest(path.join(__dirname, '../../app/src/main/AndroidManifest.xml'));
var pkgName = manifest.getPackageId(); var pkgName = manifest.getPackageId();
var launchName = pkgName + '/.' + manifest.getActivity().getName(); var launchName = pkgName + '/.' + manifest.getActivity().getName();
events.emit('log', 'Using apk: ' + apk_path); events.emit('log', 'Using apk: ' + apk_path);

View File

@ -116,7 +116,7 @@ module.exports.list_images_using_avdmanager = function () {
}; };
module.exports.list_images_using_android = function() { module.exports.list_images_using_android = function() {
return superspawn.spawn('android', ['list', 'avd']) return superspawn.spawn('android', ['list', 'avds'])
.then(function(output) { .then(function(output) {
var response = output.split('\n'); var response = output.split('\n');
var emulator_list = []; var emulator_list = [];
@ -171,10 +171,20 @@ module.exports.list_images_using_android = function() {
} }
*/ */
module.exports.list_images = function() { module.exports.list_images = function() {
if (forgivingWhichSync('avdmanager')) { if (forgivingWhichSync('android')) {
return module.exports.list_images_using_android()
.catch(function(err) {
// try to use `avdmanager` in case `android` reports it is no longer available.
// this likely means the target machine is using a newer version of
// the android sdk, and possibly `avdmanager` is available.
if (err.code == 1 && err.stdout.indexOf('android command is no longer available')) {
return module.exports.list_images_using_avdmanager();
} else {
throw err;
}
});
} else if (forgivingWhichSync('avdmanager')) {
return module.exports.list_images_using_avdmanager(); return module.exports.list_images_using_avdmanager();
} else if (forgivingWhichSync('android')) {
return module.exports.list_images_using_android();
} else { } else {
return Q().then(function() { return Q().then(function() {
throw new CordovaError('Could not find either `android` or `avdmanager` on your $PATH! Are you sure the Android SDK is installed and available?'); throw new CordovaError('Could not find either `android` or `avdmanager` on your $PATH! Are you sure the Android SDK is installed and available?');
@ -218,7 +228,6 @@ module.exports.list_started = function() {
}; };
// Returns a promise. // Returns a promise.
// TODO: we should remove this, there's a more robust method under android_sdk.js
module.exports.list_targets = function() { module.exports.list_targets = function() {
return superspawn.spawn('android', ['list', 'targets'], {cwd: os.tmpdir()}) return superspawn.spawn('android', ['list', 'targets'], {cwd: os.tmpdir()})
.then(function(output) { .then(function(output) {
@ -390,7 +399,6 @@ module.exports.create_image = function(name, target) {
}); });
} else { } else {
console.log('WARNING : Project target not found, creating avd with a different target but the project may fail to install.'); console.log('WARNING : Project target not found, creating avd with a different target but the project may fail to install.');
// TODO: there's a more robust method for finding targets in android_sdk.js
return superspawn.spawn('android', ['create', 'avd', '--name', name, '--target', this.list_targets()[0]]) return superspawn.spawn('android', ['create', 'avd', '--name', name, '--target', this.list_targets()[0]])
.then(function() { .then(function() {
// TODO: This seems like another error case, even though it always happens. // TODO: This seems like another error case, even though it always happens.