CB-12546: switch to superspawn for shelling out where possible. rework android sdk module to work with new sdk.

This commit is contained in:
filmaj 2017-03-14 16:01:32 -07:00
parent 4a354bba86
commit ebd4a02d2c
3 changed files with 109 additions and 82 deletions

View File

@ -19,10 +19,10 @@
under the License. under the License.
*/ */
var android_sdk_version = require('./templates/cordova/lib/android_sdk'); var android_sdk = require('./templates/cordova/lib/android_sdk');
android_sdk_version.run().done(null, function(err) { android_sdk.print_newest_available_sdk_target.done(null, function(err) {
console.log(err); console.error(err);
process.exit(2); process.exit(2);
}); });

View File

@ -19,47 +19,14 @@
under the License. under the License.
*/ */
var child_process = require('child_process'), var Q = require('q'),
Q = require('q'); superspawn = require('cordova-common').superspawn;
var get_highest_sdk = function(results){ module.exports.print_newest_available_sdk_target = function() {
var reg = /\d+/; return module.exports.list_targets()
var apiLevels = []; .then(function(targets) {
for(var i=0;i<results.length;i++){ console.log(targets[0]);
apiLevels[i] = parseInt(results[i].match(reg)[0]);
}
apiLevels.sort(function(a,b){return b-a;});
console.log(apiLevels[0]);
};
var get_sdks = function() {
var d = Q.defer();
child_process.exec('android list targets', function(err, stdout, stderr) {
if (err) d.reject(stderr);
else d.resolve(stdout);
}); });
return d.promise.then(function(output) {
var reg = /android-\d+/gi;
var results = output.match(reg);
if(results.length===0){
return Q.reject(new Error('No android sdks installed.'));
}else{
get_highest_sdk(results);
}
return Q();
}, function(stderr) {
if (stderr.match(/command\snot\sfound/) || stderr.match(/'android' is not recognized/)) {
return Q.reject(new Error('The command \"android\" failed. Make sure you have the latest Android SDK installed, and the \"android\" command (inside the tools/ folder) is added to your path.'));
} else {
return Q.reject(new Error('An error occurred while listing Android targets'));
}
});
};
module.exports.run = function() {
return Q.all([get_sdks()]);
}; };
module.exports.version_string_to_api_level = { module.exports.version_string_to_api_level = {
@ -76,3 +43,52 @@ module.exports.version_string_to_api_level = {
'7.0': 24, '7.0': 24,
'7.1.1': 25 '7.1.1': 25
}; };
module.exports.list_targets = function() {
return superspawn.spawn('android', ['list', 'targets'])
.then(function(stdout) {
var target_out = stdout.split('\n');
var targets = [];
for (var i = target_out.length; i >= 0; i--) {
if(target_out[i].match(/id:/)) {
targets.push(targets[i].split(' ')[1]);
}
}
return targets;
}).catch(function(err) {
// there's a chance `android` no longer works.
// lets see if `sdkmanager` is available and we can figure it out
var avail_regex = /android command is no longer available/;
if (err.code && (err.stdout.match(avail_regex) || err.stderr.match(avail_regex))) {
return superspawn.spawn('sdkmanager', ['--list'], {capture: ['stdout', 'stderr']})
.then(function(result) {
var parsing_installed_packages = false;
var lines = result.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 && line.match(/platforms;android-\d+/)) {
targets.push(line.match(/android-\d+/)[0].split('-')[1]);
}
}
return targets;
});
} else throw err;
}).then(function(targets) {
if (targets.length === 0) {
return Q.reject(new Error('No android targets (SDKs) installed!'));
} else {
// Ensure we are working with integers
targets = targets.map(function(t) { return parseInt(t); });
// Sort them in descending order.
targets.sort(function(a, b) { return b-a; });
return targets;
}
});
};

View File

@ -30,6 +30,7 @@ var shelljs = require('shelljs'),
REPO_ROOT = path.join(__dirname, '..', '..', '..', '..'), REPO_ROOT = path.join(__dirname, '..', '..', '..', '..'),
PROJECT_ROOT = path.join(__dirname, '..', '..'); PROJECT_ROOT = path.join(__dirname, '..', '..');
var CordovaError = require('cordova-common').CordovaError; var CordovaError = require('cordova-common').CordovaError;
var superspawn = require('cordova-common').superspawn;
function forgivingWhichSync(cmd) { function forgivingWhichSync(cmd) {
@ -83,10 +84,12 @@ module.exports.get_target = function() {
// Returns a promise. Called only by build and clean commands. // Returns a promise. Called only by build and clean commands.
module.exports.check_ant = function() { 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.') return superspawn.spawn('ant', ['-version'])
.then(function (output) { .then(function(output) {
// Parse Ant version from command output // Parse Ant version from command output
return /version ((?:\d+\.)+(?:\d+))/i.exec(output)[1]; return /version ((?:\d+\.)+(?:\d+))/i.exec(output)[1];
}).catch(function(err) {
throw new CordovaError("Failed to run `ant -version`. Make sure you have `ant` on your $PATH.");
}); });
}; };
@ -98,15 +101,14 @@ module.exports.get_gradle_wrapper = function() {
androidStudioPath = path.join(process.env['ProgramFiles'],'Android', 'Android Studio', 'gradle'); androidStudioPath = path.join(process.env['ProgramFiles'],'Android', 'Android Studio', 'gradle');
} }
if(androidStudioPath !== null && fs.existsSync(androidStudioPath)) { if (androidStudioPath !== null && fs.existsSync(androidStudioPath)) {
var dirs = fs.readdirSync(androidStudioPath); var dirs = fs.readdirSync(androidStudioPath);
if(dirs[0].split('-')[0] == 'gradle') if(dirs[0].split('-')[0] == 'gradle') {
{ return path.join(androidStudioPath, dirs[0], 'bin', 'gradle');
return path.join(androidStudioPath, dirs[0], 'bin', 'gradle'); }
}
} else { } else {
//OK, let's try to check for Gradle! //OK, let's try to check for Gradle!
return forgivingWhichSync('gradle'); return forgivingWhichSync('gradle');
} }
}; };
@ -119,16 +121,15 @@ module.exports.check_gradle = function() {
'Might need to install Android SDK or set up \'ANDROID_HOME\' env variable.')); 'Might need to install Android SDK or set up \'ANDROID_HOME\' env variable.'));
var gradlePath = module.exports.get_gradle_wrapper(); var gradlePath = module.exports.get_gradle_wrapper();
if(gradlePath.length !== 0) if (gradlePath.length !== 0)
d.resolve(gradlePath); d.resolve(gradlePath);
else else
d.reject(new CordovaError('Could not find an installed version of Gradle either in Android Studio,\n' + d.reject(new CordovaError('Could not find an installed version of Gradle either in Android Studio,\n' +
'or on your system to install the gradle wrapper. Please include gradle \n' + 'or on your system to install the gradle wrapper. Please include gradle \n' +
'in your path, or install Android Studio')); 'in your path, or install Android Studio'));
return d.promise; return d.promise;
}; };
// Returns a promise. // Returns a promise.
module.exports.check_java = function() { module.exports.check_java = function() {
var javacPath = forgivingWhichSync('javac'); var javacPath = forgivingWhichSync('javac');
@ -141,12 +142,15 @@ module.exports.check_java = function() {
} }
} else { } else {
if (javacPath) { if (javacPath) {
var msg = 'Failed to find \'JAVA_HOME\' environment variable. Try setting setting it manually.';
// OS X has a command for finding JAVA_HOME. // OS X has a command for finding JAVA_HOME.
if (fs.existsSync('/usr/libexec/java_home')) { var find_java = '/usr/libexec/java_home';
return tryCommand('/usr/libexec/java_home', msg) var default_java_error_msg = 'Failed to find \'JAVA_HOME\' environment variable. Try setting setting it manually.';
if (fs.existsSync(find_java)) {
return superspawn.spawn(find_java)
.then(function(stdout) { .then(function(stdout) {
process.env['JAVA_HOME'] = stdout.trim(); process.env['JAVA_HOME'] = stdout.trim();
}).catch(function(err) {
throw new CordovaError(default_java_error_msg);
}); });
} else { } else {
// See if we can derive it from javac's location. // See if we can derive it from javac's location.
@ -155,7 +159,7 @@ module.exports.check_java = function() {
if (fs.existsSync(path.join(maybeJavaHome, 'lib', 'tools.jar'))) { if (fs.existsSync(path.join(maybeJavaHome, 'lib', 'tools.jar'))) {
process.env['JAVA_HOME'] = maybeJavaHome; process.env['JAVA_HOME'] = maybeJavaHome;
} else { } else {
throw new CordovaError(msg); throw new CordovaError(default_java_error_msg);
} }
} }
} else if (module.exports.isWindows()) { } else if (module.exports.isWindows()) {
@ -178,21 +182,21 @@ module.exports.check_java = function() {
} }
} }
}).then(function() { }).then(function() {
var msg = var msg =
'Failed to run "javac -version", make sure that you have a JDK installed.\n' + 'Failed to run "javac -version", make sure that you have a JDK installed.\n' +
'You can get it from: http://www.oracle.com/technetwork/java/javase/downloads.\n'; 'You can get it from: http://www.oracle.com/technetwork/java/javase/downloads.\n';
if (process.env['JAVA_HOME']) { if (process.env['JAVA_HOME']) {
msg += 'Your JAVA_HOME is invalid: ' + process.env['JAVA_HOME'] + '\n'; msg += 'Your JAVA_HOME is invalid: ' + process.env['JAVA_HOME'] + '\n';
} }
// We use tryCommand with catchStderr = true, because // We use tryCommand with catchStderr = true, because
// javac writes version info to stderr instead of stdout // javac writes version info to stderr instead of stdout
return tryCommand('javac -version', msg, true) return tryCommand('javac -version', msg, true)
.then(function (output) { .then(function (output) {
//Let's check for at least Java 8, and keep it future proof so we can support Java 10 //Let's check for at least Java 8, and keep it future proof so we can support Java 10
var match = /javac ((?:1\.)(?:[8-9]\.)(?:\d+))|((?:1\.)(?:[1-9]\d+\.)(?:\d+))/i.exec(output); var match = /javac ((?:1\.)(?:[8-9]\.)(?:\d+))|((?:1\.)(?:[1-9]\d+\.)(?:\d+))/i.exec(output);
return match && match[1]; return match && match[1];
});
}); });
});
}; };
// Returns a promise. // Returns a promise.
@ -297,9 +301,11 @@ module.exports.check_android = function() {
module.exports.getAbsoluteAndroidCmd = function () { module.exports.getAbsoluteAndroidCmd = function () {
var cmd = forgivingWhichSync('android'); var cmd = forgivingWhichSync('android');
if(cmd.length === 0) // TODO: this probably needs to be `sdkmanager`, no?
cmd = forgivingWhichSync('avdmanager'); if (cmd.length === 0) {
if (process.platform === 'win32') { cmd = forgivingWhichSync('avdmanager');
}
if (module.exports.isWindows()) {
return '"' + cmd + '"'; return '"' + cmd + '"';
} }
return cmd.replace(/(\s)/g, '\\$1'); return cmd.replace(/(\s)/g, '\\$1');
@ -314,13 +320,13 @@ module.exports.check_android_target = function(originalError) {
var valid_target = module.exports.get_target(); var valid_target = module.exports.get_target();
var msg = 'Android SDK not found. Make sure that it is installed. If it is not at the default location, set the ANDROID_HOME environment variable.'; var msg = 'Android SDK not found. Make sure that it is installed. If it is not at the default location, set the ANDROID_HOME environment variable.';
// Changing "targets" to "target" is stupid and makes more code. Thanks Google! // Changing "targets" to "target" is stupid and makes more code. Thanks Google!
var cmd = 'android list targets --compact'; var cmd = 'android';
// TODO: the following conditional needs fixing, probably should leverage `sdkmanager` // TODO: the following conditional needs fixing, probably should leverage `sdkmanager`
// oh and tests // oh and tests
if(forgivingWhichSync('avdmanager').length > 0) if (forgivingWhichSync('avdmanager').length > 0)
cmd = 'avdmanager list target --compact'; cmd = 'avdmanager';
return tryCommand(cmd, msg) return superspawn.spawn(cmd, ['list', 'targets', '--compact'])
.then(function(output) { .then(function(stdout) {
var targets = output.split('\n'); var targets = output.split('\n');
if (targets.indexOf(valid_target) >= 0) { if (targets.indexOf(valid_target) >= 0) {
return targets; return targets;
@ -337,6 +343,11 @@ module.exports.check_android_target = function(originalError) {
msg = originalError + '\n' + msg; msg = originalError + '\n' + msg;
} }
throw new CordovaError(msg); throw new CordovaError(msg);
}).catch(function(err) {
throw new CordovaError(msg);
});
return tryCommand(cmd, msg)
.then(function(output) {
}); });
}; };