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.
*/
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) {
console.log(err);
android_sdk.print_newest_available_sdk_target.done(null, function(err) {
console.error(err);
process.exit(2);
});

View File

@ -19,47 +19,14 @@
under the License.
*/
var child_process = require('child_process'),
Q = require('q');
var Q = require('q'),
superspawn = require('cordova-common').superspawn;
var get_highest_sdk = function(results){
var reg = /\d+/;
var apiLevels = [];
for(var i=0;i<results.length;i++){
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);
module.exports.print_newest_available_sdk_target = function() {
return module.exports.list_targets()
.then(function(targets) {
console.log(targets[0]);
});
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 = {
@ -76,3 +43,52 @@ module.exports.version_string_to_api_level = {
'7.0': 24,
'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, '..', '..', '..', '..'),
PROJECT_ROOT = path.join(__dirname, '..', '..');
var CordovaError = require('cordova-common').CordovaError;
var superspawn = require('cordova-common').superspawn;
function forgivingWhichSync(cmd) {
@ -83,10 +84,12 @@ module.exports.get_target = function() {
// Returns a promise. Called only by build and clean commands.
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.')
.then(function (output) {
return superspawn.spawn('ant', ['-version'])
.then(function(output) {
// Parse Ant version from command output
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');
}
if(androidStudioPath !== null && fs.existsSync(androidStudioPath)) {
var dirs = fs.readdirSync(androidStudioPath);
if(dirs[0].split('-')[0] == 'gradle')
{
return path.join(androidStudioPath, dirs[0], 'bin', 'gradle');
}
if (androidStudioPath !== null && fs.existsSync(androidStudioPath)) {
var dirs = fs.readdirSync(androidStudioPath);
if(dirs[0].split('-')[0] == 'gradle') {
return path.join(androidStudioPath, dirs[0], 'bin', 'gradle');
}
} else {
//OK, let's try to check for Gradle!
return forgivingWhichSync('gradle');
//OK, let's try to check for 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.'));
var gradlePath = module.exports.get_gradle_wrapper();
if(gradlePath.length !== 0)
d.resolve(gradlePath);
if (gradlePath.length !== 0)
d.resolve(gradlePath);
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' +
'in your path, or install Android Studio'));
return d.promise;
};
// Returns a promise.
module.exports.check_java = function() {
var javacPath = forgivingWhichSync('javac');
@ -141,12 +142,15 @@ module.exports.check_java = function() {
}
} else {
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.
if (fs.existsSync('/usr/libexec/java_home')) {
return tryCommand('/usr/libexec/java_home', msg)
var find_java = '/usr/libexec/java_home';
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) {
process.env['JAVA_HOME'] = stdout.trim();
}).catch(function(err) {
throw new CordovaError(default_java_error_msg);
});
} else {
// 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'))) {
process.env['JAVA_HOME'] = maybeJavaHome;
} else {
throw new CordovaError(msg);
throw new CordovaError(default_java_error_msg);
}
}
} else if (module.exports.isWindows()) {
@ -178,21 +182,21 @@ module.exports.check_java = function() {
}
}
}).then(function() {
var msg =
'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';
if (process.env['JAVA_HOME']) {
msg += 'Your JAVA_HOME is invalid: ' + process.env['JAVA_HOME'] + '\n';
}
// We use tryCommand with catchStderr = true, because
// javac writes version info to stderr instead of stdout
return tryCommand('javac -version', msg, true)
.then(function (output) {
//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);
return match && match[1];
});
var msg =
'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';
if (process.env['JAVA_HOME']) {
msg += 'Your JAVA_HOME is invalid: ' + process.env['JAVA_HOME'] + '\n';
}
// We use tryCommand with catchStderr = true, because
// javac writes version info to stderr instead of stdout
return tryCommand('javac -version', msg, true)
.then(function (output) {
//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);
return match && match[1];
});
});
};
// Returns a promise.
@ -297,9 +301,11 @@ module.exports.check_android = function() {
module.exports.getAbsoluteAndroidCmd = function () {
var cmd = forgivingWhichSync('android');
if(cmd.length === 0)
cmd = forgivingWhichSync('avdmanager');
if (process.platform === 'win32') {
// TODO: this probably needs to be `sdkmanager`, no?
if (cmd.length === 0) {
cmd = forgivingWhichSync('avdmanager');
}
if (module.exports.isWindows()) {
return '"' + cmd + '"';
}
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 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!
var cmd = 'android list targets --compact';
var cmd = 'android';
// TODO: the following conditional needs fixing, probably should leverage `sdkmanager`
// oh and tests
if(forgivingWhichSync('avdmanager').length > 0)
cmd = 'avdmanager list target --compact';
return tryCommand(cmd, msg)
.then(function(output) {
if (forgivingWhichSync('avdmanager').length > 0)
cmd = 'avdmanager';
return superspawn.spawn(cmd, ['list', 'targets', '--compact'])
.then(function(stdout) {
var targets = output.split('\n');
if (targets.indexOf(valid_target) >= 0) {
return targets;
@ -337,6 +343,11 @@ module.exports.check_android_target = function(originalError) {
msg = originalError + '\n' + msg;
}
throw new CordovaError(msg);
}).catch(function(err) {
throw new CordovaError(msg);
});
return tryCommand(cmd, msg)
.then(function(output) {
});
};