CB-11259: Improving build output

This closes #305
This commit is contained in:
Richard Knoll 2016-05-03 16:25:48 -07:00
parent e9e27ca47c
commit 9738079c42
11 changed files with 49 additions and 41 deletions

View File

@ -246,7 +246,7 @@ exports.create = function(project_path, config, options, events) {
events.emit('log', '\tActivity: ' + safe_activity_name); events.emit('log', '\tActivity: ' + safe_activity_name);
events.emit('log', '\tAndroid target: ' + target_api); events.emit('log', '\tAndroid target: ' + target_api);
events.emit('verbose', 'Copying template files...'); events.emit('verbose', 'Copying android template project to ' + project_path);
setShellFatal(true, function() { setShellFatal(true, function() {
var project_template_dir = options.customTemplate || path.join(ROOT, 'bin', 'templates', 'project'); var project_template_dir = options.customTemplate || path.join(ROOT, 'bin', 'templates', 'project');

View File

@ -56,7 +56,7 @@ Adb.devices = function (opts) {
}; };
Adb.install = function (target, packagePath, opts) { Adb.install = function (target, packagePath, opts) {
events.emit('verbose', 'Installing apk ' + packagePath + ' on ' + target + '...'); events.emit('verbose', 'Installing apk ' + packagePath + ' on target ' + target + '...');
var args = ['-s', target, 'install']; var args = ['-s', target, 'install'];
if (opts && opts.replace) args.push('-r'); if (opts && opts.replace) args.push('-r');
return spawn('adb', args.concat(packagePath), {cwd: os.tmpdir()}) return spawn('adb', args.concat(packagePath), {cwd: os.tmpdir()})
@ -78,12 +78,12 @@ Adb.install = function (target, packagePath, opts) {
}; };
Adb.uninstall = function (target, packageId) { Adb.uninstall = function (target, packageId) {
events.emit('verbose', 'Uninstalling ' + packageId + ' from ' + target + '...'); events.emit('verbose', 'Uninstalling package ' + packageId + ' from target ' + target + '...');
return spawn('adb', ['-s', target, 'uninstall', packageId], {cwd: os.tmpdir()}); return spawn('adb', ['-s', target, 'uninstall', packageId], {cwd: os.tmpdir()});
}; };
Adb.shell = function (target, shellCommand) { Adb.shell = function (target, shellCommand) {
events.emit('verbose', 'Running command "' + shellCommand + '" on ' + target + '...'); events.emit('verbose', 'Running adb shell command "' + shellCommand + '" on target ' + target + '...');
var args = ['-s', target, 'shell']; var args = ['-s', target, 'shell'];
shellCommand = shellCommand.split(/\s+/); shellCommand = shellCommand.split(/\s+/);
return spawn('adb', args.concat(shellCommand), {cwd: os.tmpdir()}) return spawn('adb', args.concat(shellCommand), {cwd: os.tmpdir()})
@ -94,7 +94,7 @@ Adb.shell = function (target, shellCommand) {
}; };
Adb.start = function (target, activityName) { Adb.start = function (target, activityName) {
events.emit('verbose', 'Starting application "' + activityName + '" on ' + target + '...'); events.emit('verbose', 'Starting application "' + activityName + '" on target ' + target + '...');
return Adb.shell(target, 'am start -W -a android.intent.action.MAIN -n' + activityName) return Adb.shell(target, 'am start -W -a android.intent.action.MAIN -n' + activityName)
.catch(function (output) { .catch(function (output) {
return Q.reject(new CordovaError('Failed to start application "' + return Q.reject(new CordovaError('Failed to start application "' +

View File

@ -28,7 +28,7 @@ function AndroidManifest(path) {
this.path = path; this.path = path;
this.doc = xml.parseElementtreeSync(path); this.doc = xml.parseElementtreeSync(path);
if (this.doc.getroot().tag !== 'manifest') { if (this.doc.getroot().tag !== 'manifest') {
throw new Error(path + ' has incorrect root node name (expected "manifest")'); throw new Error('AndroidManifest at ' + path + ' has incorrect root node name (expected "manifest")');
} }
} }

View File

@ -188,18 +188,18 @@ module.exports.detectArchitecture = function(target) {
// adb kill-server doesn't seem to do the trick. // adb kill-server doesn't seem to do the trick.
// Could probably find a x-platform version of killall, but I'm not actually // Could probably find a x-platform version of killall, but I'm not actually
// sure that this scenario even happens on non-OSX machines. // sure that this scenario even happens on non-OSX machines.
events.emit('verbose', 'adb timed out while detecting device/emulator architecture. Killing adb and trying again.');
return spawn('killall', ['adb']) return spawn('killall', ['adb'])
.then(function() { .then(function() {
events.emit('verbose', 'adb seems hung. retrying.');
return helper() return helper()
.then(null, function() { .then(null, function() {
// The double kill is sadly often necessary, at least on mac. // The double kill is sadly often necessary, at least on mac.
events.emit('warn', 'Now device not found... restarting adb again.'); events.emit('warn', 'adb timed out a second time while detecting device/emulator architecture. Killing adb and trying again.');
return spawn('killall', ['adb']) return spawn('killall', ['adb'])
.then(function() { .then(function() {
return helper() return helper()
.then(null, function() { .then(null, function() {
return Q.reject(new CordovaError('USB is flakey. Try unplugging & replugging the device.')); return Q.reject(new CordovaError('adb timed out a third time while detecting device/emulator architecture. Try unplugging & replugging the device.'));
}); });
}); });
}); });

View File

@ -79,7 +79,7 @@ AntBuilder.prototype.prepEnv = function(opts) {
writeBuildXml(path.join(self.root, subProjects[i])); writeBuildXml(path.join(self.root, subProjects[i]));
} }
if (propertiesObj.systemLibs.length > 0) { if (propertiesObj.systemLibs.length > 0) {
throw new CordovaError('Project contains at least one plugin that requires a system library. This is not supported with ANT. Please build using gradle.'); throw new CordovaError('Project contains at least one plugin that requires a system library. This is not supported with ANT. Use gradle instead.');
} }
var propertiesFile = opts.buildType + SIGNING_PROPERTIES; var propertiesFile = opts.buildType + SIGNING_PROPERTIES;

View File

@ -187,14 +187,14 @@ module.exports.start = function(emulator_ID, boot_timeout) {
.unref(); .unref();
// wait for emulator to start // wait for emulator to start
events.emit('log', 'Waiting for emulator...'); events.emit('log', 'Waiting for emulator to start...');
return self.wait_for_emulator(uuid); return self.wait_for_emulator(uuid);
}).then(function(emulatorId) { }).then(function(emulatorId) {
if (!emulatorId) if (!emulatorId)
return Q.reject(new CordovaError('Failed to start emulator')); return Q.reject(new CordovaError('Failed to start emulator'));
//wait for emulator to boot up //wait for emulator to boot up
process.stdout.write('Booting up emulator (this may take a while)...'); process.stdout.write('Waiting for emulator to boot (this may take a while)...');
return self.wait_for_boot(emulatorId, boot_timeout) return self.wait_for_boot(emulatorId, boot_timeout)
.then(function(success) { .then(function(success) {
if (success) { if (success) {
@ -272,7 +272,7 @@ module.exports.wait_for_boot = function(emulator_id, time_remaining) {
* Returns a promise. * Returns a promise.
*/ */
module.exports.create_image = function(name, target) { module.exports.create_image = function(name, target) {
console.log('Creating avd named ' + name); console.log('Creating new avd named ' + name);
if (target) { if (target) {
return spawn('android', ['create', 'avd', '--name', name, '--target', target]) return spawn('android', ['create', 'avd', '--name', name, '--target', target])
.then(null, function(error) { .then(null, function(error) {
@ -286,7 +286,7 @@ module.exports.create_image = function(name, target) {
.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.
console.error('ERROR : Unable to create an avd emulator, no targets found.'); console.error('ERROR : Unable to create an avd emulator, no targets found.');
console.error('Please insure you have targets available by running the "android" command'); console.error('Ensure you have targets available by running the "android" command');
return Q.reject(); return Q.reject();
}, function(error) { }, function(error) {
console.error('ERROR : Failed to create emulator image : '); console.error('ERROR : Failed to create emulator image : ');
@ -299,7 +299,7 @@ module.exports.resolveTarget = function(target) {
return this.list_started() return this.list_started()
.then(function(emulator_list) { .then(function(emulator_list) {
if (emulator_list.length < 1) { if (emulator_list.length < 1) {
return Q.reject('No started emulators found, please start an emultor before deploying your project.'); return Q.reject('No running Android emulators found, please start an emulator before deploying your project.');
} }
// default emulator // default emulator
@ -392,8 +392,8 @@ module.exports.install = function(givenTarget, buildResults) {
if (!/INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES/.test(error.toString())) if (!/INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES/.test(error.toString()))
throw error; throw error;
events.emit('warn', 'Uninstalling app from device and reinstalling it again because the ' + events.emit('warn', 'Uninstalling app from device and reinstalling it because the ' +
'installed app already signed with different key'); 'currently installed app was signed with different key');
// This promise is always resolved, even if 'adb uninstall' fails to uninstall app // This promise is always resolved, even if 'adb uninstall' fails to uninstall app
// or the app doesn't installed at all, so no error catching needed. // or the app doesn't installed at all, so no error catching needed.

View File

@ -28,8 +28,8 @@ var CordovaError = require('cordova-common').CordovaError;
var handlers = { var handlers = {
'source-file':{ 'source-file':{
install:function(obj, plugin, project, options) { install:function(obj, plugin, project, options) {
if (!obj.src) throw new CordovaError('<source-file> element is missing "src" attribute for plugin: ' + plugin.id); if (!obj.src) throw new CordovaError(generateAttributeError('src', 'source-file', plugin.id));
if (!obj.targetDir) throw new CordovaError('<source-file> element is missing "target-dir" attribute for plugin: ' + plugin.id); if (!obj.targetDir) throw new CordovaError(generateAttributeError('target-dir', 'source-file', plugin.id));
var dest = path.join(obj.targetDir, path.basename(obj.src)); var dest = path.join(obj.targetDir, path.basename(obj.src));
if (options && options.force) { if (options && options.force) {
copyFile(plugin.dir, obj.src, project.projectDir, dest, !!(options && options.link)); copyFile(plugin.dir, obj.src, project.projectDir, dest, !!(options && options.link));
@ -63,7 +63,7 @@ var handlers = {
'framework': { 'framework': {
install:function(obj, plugin, project, options) { install:function(obj, plugin, project, options) {
var src = obj.src; var src = obj.src;
if (!src) throw new CordovaError('src not specified in <framework> for plugin: ' + plugin.id); if (!src) throw new CordovaError(generateAttributeError('src', 'framework', plugin.id));
events.emit('verbose', 'Installing Android library: ' + src); events.emit('verbose', 'Installing Android library: ' + src);
var parentDir = obj.parent ? path.resolve(project.projectDir, obj.parent) : project.projectDir; var parentDir = obj.parent ? path.resolve(project.projectDir, obj.parent) : project.projectDir;
@ -88,7 +88,7 @@ var handlers = {
}, },
uninstall:function(obj, plugin, project, options) { uninstall:function(obj, plugin, project, options) {
var src = obj.src; var src = obj.src;
if (!src) throw new CordovaError('src not specified in <framework> for plugin: ' + plugin.id); if (!src) throw new CordovaError(generateAttributeError('src', 'framework', plugin.id));
events.emit('verbose', 'Uninstalling Android library: ' + src); events.emit('verbose', 'Uninstalling Android library: ' + src);
var parentDir = obj.parent ? path.resolve(project.projectDir, obj.parent) : project.projectDir; var parentDir = obj.parent ? path.resolve(project.projectDir, obj.parent) : project.projectDir;
@ -120,10 +120,10 @@ var handlers = {
asset:{ asset:{
install:function(obj, plugin, project, options) { install:function(obj, plugin, project, options) {
if (!obj.src) { if (!obj.src) {
throw new CordovaError('<asset> tag without required "src" attribute. plugin=' + plugin.dir); throw new CordovaError(generateAttributeError('src', 'asset', plugin.id));
} }
if (!obj.target) { if (!obj.target) {
throw new CordovaError('<asset> tag without required "target" attribute'); throw new CordovaError(generateAttributeError('target', 'asset', plugin.id));
} }
copyFile(plugin.dir, obj.src, project.www, obj.target); copyFile(plugin.dir, obj.src, project.www, obj.target);
@ -135,7 +135,7 @@ var handlers = {
uninstall:function(obj, plugin, project, options) { uninstall:function(obj, plugin, project, options) {
var target = obj.target || obj.src; var target = obj.target || obj.src;
if (!target) throw new CordovaError('<asset> tag without required "target" attribute'); if (!target) throw new CordovaError(generateAttributeError('target', 'asset', plugin.id));
removeFileF(path.resolve(project.www, target)); removeFileF(path.resolve(project.www, target));
removeFileF(path.resolve(project.www, 'plugins', plugin.id)); removeFileF(path.resolve(project.www, 'plugins', plugin.id));
@ -205,13 +205,13 @@ function copyFile (plugin_dir, src, project_dir, dest, link) {
var real_path = fs.realpathSync(src); var real_path = fs.realpathSync(src);
var real_plugin_path = fs.realpathSync(plugin_dir); var real_plugin_path = fs.realpathSync(plugin_dir);
if (real_path.indexOf(real_plugin_path) !== 0) if (real_path.indexOf(real_plugin_path) !== 0)
throw new CordovaError('"' + src + '" not located within plugin!'); throw new CordovaError('File "' + src + '" is located outside the plugin directory "' + plugin_dir + '"');
dest = path.resolve(project_dir, dest); dest = path.resolve(project_dir, dest);
// check that dest path is located in project directory // check that dest path is located in project directory
if (dest.indexOf(project_dir) !== 0) if (dest.indexOf(project_dir) !== 0)
throw new CordovaError('"' + dest + '" not located within project!'); throw new CordovaError('Destination "' + dest + '" for source file "' + src + '" is located outside the project');
shell.mkdir('-p', path.dirname(dest)); shell.mkdir('-p', path.dirname(dest));
@ -270,3 +270,7 @@ function removeFileAndParents (baseDir, destFile, stopper) {
} }
} }
} }
function generateAttributeError(attribute, element, id) {
return 'Required attribute "' + attribute + '" not specified in <' + element + '> element from plugin: ' + id;
}

View File

@ -50,7 +50,7 @@ module.exports.prepare = function (cordovaProject) {
handleSplashes(cordovaProject.projectConfig, self.root); handleSplashes(cordovaProject.projectConfig, self.root);
}) })
.then(function () { .then(function () {
events.emit('verbose', 'updated project successfully'); events.emit('verbose', 'Prepared android project successfully');
}); });
}; };
@ -69,7 +69,7 @@ module.exports.prepare = function (cordovaProject) {
* configuration is already dumped to appropriate config.xml file. * configuration is already dumped to appropriate config.xml file.
*/ */
function updateConfigFilesFrom(sourceConfig, configMunger, locations) { function updateConfigFilesFrom(sourceConfig, configMunger, locations) {
events.emit('verbose', 'Generating config.xml from defaults for platform "android"'); events.emit('verbose', 'Generating platform-specific config.xml from defaults for android at ' + locations.configXml);
// First cleanup current config and merge project's one into own // First cleanup current config and merge project's one into own
// Overwrite platform config.xml with defaults.xml. // Overwrite platform config.xml with defaults.xml.
@ -79,6 +79,7 @@ function updateConfigFilesFrom(sourceConfig, configMunger, locations) {
// in project (including project's config) // in project (including project's config)
configMunger.reapply_global_munge().save_all(); configMunger.reapply_global_munge().save_all();
events.emit('verbose', 'Merging project\'s config.xml into platform-specific android config.xml');
// Merge changes from app's config.xml into platform's one // Merge changes from app's config.xml into platform's one
var config = new ConfigParser(locations.configXml); var config = new ConfigParser(locations.configXml);
xmlHelpers.mergeXml(sourceConfig.doc.getroot(), xmlHelpers.mergeXml(sourceConfig.doc.getroot(),
@ -108,7 +109,7 @@ function updateWwwFrom(cordovaProject, destinations) {
// If project contains 'merges' for our platform, use them as another overrides // If project contains 'merges' for our platform, use them as another overrides
var merges_path = path.join(cordovaProject.root, 'merges', 'android'); var merges_path = path.join(cordovaProject.root, 'merges', 'android');
if (fs.existsSync(merges_path)) { if (fs.existsSync(merges_path)) {
events.emit('verbose', 'Found "merges" for android platform. Copying over existing "www" files.'); events.emit('verbose', 'Found "merges/android" folder. Copying its contents into the android project.');
var overrides = path.join(merges_path, '*'); var overrides = path.join(merges_path, '*');
shell.cp('-rf', overrides, destinations.www); shell.cp('-rf', overrides, destinations.www);
} }
@ -127,7 +128,7 @@ function updateProjectAccordingTo(platformConfig, locations) {
var strings = xmlHelpers.parseElementtreeSync(locations.strings); var strings = xmlHelpers.parseElementtreeSync(locations.strings);
strings.find('string[@name="app_name"]').text = name; strings.find('string[@name="app_name"]').text = name;
fs.writeFileSync(locations.strings, strings.write({indent: 4}), 'utf-8'); fs.writeFileSync(locations.strings, strings.write({indent: 4}), 'utf-8');
events.emit('verbose', 'Wrote out Android application name to "' + name + '"'); events.emit('verbose', 'Wrote out android application name "' + name + '" to ' + locations.strings);
// Java packages cannot support dashes // Java packages cannot support dashes
var pkg = (platformConfig.android_packageName() || platformConfig.packageName()).replace(/-/g, '_'); var pkg = (platformConfig.android_packageName() || platformConfig.packageName()).replace(/-/g, '_');
@ -153,15 +154,15 @@ function updateProjectAccordingTo(platformConfig, locations) {
}); });
if (java_files.length === 0) { if (java_files.length === 0) {
throw new CordovaError('No Java files found which extend CordovaActivity.'); throw new CordovaError('No Java files found that extend CordovaActivity.');
} else if(java_files.length > 1) { } else if(java_files.length > 1) {
events.emit('log', 'Multiple candidate Java files (.java files which extend CordovaActivity) found. Guessing at the first one, ' + java_files[0]); events.emit('log', 'Multiple candidate Java files that extend CordovaActivity found. Guessing at the first one, ' + java_files[0]);
} }
var destFile = path.join(locations.root, 'src', pkg.replace(/\./g, '/'), path.basename(java_files[0])); var destFile = path.join(locations.root, 'src', pkg.replace(/\./g, '/'), path.basename(java_files[0]));
shell.mkdir('-p', path.dirname(destFile)); shell.mkdir('-p', path.dirname(destFile));
shell.sed(/package [\w\.]*;/, 'package ' + pkg + ';', java_files[0]).to(destFile); shell.sed(/package [\w\.]*;/, 'package ' + pkg + ';', java_files[0]).to(destFile);
events.emit('verbose', 'Wrote out Android package name to "' + pkg + '"'); events.emit('verbose', 'Wrote out Android package name "' + pkg + '" to ' + destFile);
if (orig_pkg !== pkg) { if (orig_pkg !== pkg) {
// If package was name changed we need to remove old java with main activity // If package was name changed we need to remove old java with main activity
@ -195,6 +196,8 @@ function default_versionCode(version) {
if (+nums[2]) { if (+nums[2]) {
versionCode += +nums[2]; versionCode += +nums[2];
} }
events.emit('verbose', 'android-versionCode not found in config.xml. Generating a code based on version in config.xml (' + version + '): ' + versionCode);
return versionCode; return versionCode;
} }
@ -209,7 +212,7 @@ function copyImage(src, resourcesDir, density, name) {
} }
var destFilePath = path.join(destFolder, isNinePatch ? ninePatchName : name); var destFilePath = path.join(destFolder, isNinePatch ? ninePatchName : name);
events.emit('verbose', 'copying image from ' + src + ' to ' + destFilePath); events.emit('verbose', 'Copying image from ' + src + ' to ' + destFilePath);
shell.cp('-f', src, destFilePath); shell.cp('-f', src, destFilePath);
} }
@ -289,7 +292,7 @@ function handleIcons(projectConfig, platformRoot) {
} }
if (!size && !icon.density) { if (!size && !icon.density) {
if (default_icon) { if (default_icon) {
events.emit('verbose', 'more than one default icon: ' + JSON.stringify(icon)); events.emit('verbose', 'Found extra default icon: ' + icon.src + ' (ignoring in favor of ' + default_icon.src + ')');
} else { } else {
default_icon = icon; default_icon = icon;
} }

View File

@ -66,7 +66,7 @@ function getInstallTarget(runOptions) {
events.emit('warn', 'No target specified, deploying to device \'' + device_list[0] + '\'.'); events.emit('warn', 'No target specified, deploying to device \'' + device_list[0] + '\'.');
install_target = device_list[0]; install_target = device_list[0];
} else { } else {
events.emit('warn', 'No target specified, deploying to emulator'); events.emit('warn', 'No target specified and no devices found, deploying to emulator');
install_target = '--emulator'; install_target = '--emulator';
} }
}); });

View File

@ -61,7 +61,7 @@ String doFindLatestInstalledBuildTools(String minBuildToolsVersion) {
highestBuildToolsVersion highestBuildToolsVersion
} else { } else {
throw new RuntimeException( throw new RuntimeException(
"No installed build tools found. Please install the Android build tools version " + "No installed build tools found. Install the Android build tools version " +
minBuildToolsVersion + " or higher.") minBuildToolsVersion + " or higher.")
} }
} }

View File

@ -47,8 +47,9 @@ describe('common platform handler', function() {
it('should throw if src not in plugin directory', function(){ it('should throw if src not in plugin directory', function(){
shell.mkdir('-p', project_dir); shell.mkdir('-p', project_dir);
fs.writeFileSync(non_plugin_file, 'contents', 'utf-8'); fs.writeFileSync(non_plugin_file, 'contents', 'utf-8');
expect(function(){copyFile(test_dir, '../non_plugin_file', project_dir, dest);}). var outside_file = '../non_plugin_file';
toThrow(new Error('"' + non_plugin_file + '" not located within plugin!')); expect(function(){copyFile(test_dir, outside_file, project_dir, dest);}).
toThrow(new Error('File "' + path.resolve(test_dir, outside_file) + '" is located outside the plugin directory "' + test_dir + '"'));
shell.rm('-rf', test_dir); shell.rm('-rf', test_dir);
}); });
@ -75,7 +76,7 @@ describe('common platform handler', function() {
} }
expect(function(){copyFile(test_dir, symlink_file, project_dir, dest);}). expect(function(){copyFile(test_dir, symlink_file, project_dir, dest);}).
toThrow(new Error('"' + symlink_file + '" not located within plugin!')); toThrow(new Error('File "' + path.resolve(test_dir, symlink_file) + '" is located outside the plugin directory "' + test_dir + '"'));
shell.rm('-rf', project_dir); shell.rm('-rf', project_dir);
}); });
@ -83,7 +84,7 @@ describe('common platform handler', function() {
shell.mkdir('-p', java_dir); shell.mkdir('-p', java_dir);
fs.writeFileSync(java_file, 'contents', 'utf-8'); fs.writeFileSync(java_file, 'contents', 'utf-8');
expect(function(){copyFile(test_dir, java_file, project_dir, non_plugin_file);}). expect(function(){copyFile(test_dir, java_file, project_dir, non_plugin_file);}).
toThrow(new Error('"' + non_plugin_file + '" not located within project!')); toThrow(new Error('Destination "' + path.resolve(project_dir, non_plugin_file) + '" for source file "' + path.resolve(test_dir, java_file) + '" is located outside the project'));
shell.rm('-rf', project_dir); shell.rm('-rf', project_dir);
}); });