forked from github/cordova-android
CB-7330 Don't run "android update" during creation
Instead, have the build script copy do the equivalent logic on each build. Advantages: - Scripts run much faster - No more duplicate CordovaLib entries in project.properties - Building is more independent from create/update script (more robust)
This commit is contained in:
parent
d56ea25816
commit
dfa66b9dd4
@ -67,7 +67,18 @@ 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 tryCommand('ant -version', 'Failed to run "ant -version", make sure you have ant installed and added to your PATH.');
|
||||||
}
|
};
|
||||||
|
|
||||||
|
// Returns a promise. Called only by build and clean commands.
|
||||||
|
module.exports.check_gradle = function() {
|
||||||
|
var sdkDir = process.env['ANDROID_HOME'];
|
||||||
|
var wrapperDir = path.join(sdkDir, 'tools', 'templates', 'gradle', 'wrapper');
|
||||||
|
if (!fs.existsSync(wrapperDir)) {
|
||||||
|
return Q.reject(new Error('Could not find gradle wrapper within android sdk. Might need to update your Android SDK.\n' +
|
||||||
|
'Looked here: ' + wrapperDir));
|
||||||
|
}
|
||||||
|
return Q.when();
|
||||||
|
};
|
||||||
|
|
||||||
// Returns a promise.
|
// Returns a promise.
|
||||||
module.exports.check_java = function() {
|
module.exports.check_java = function() {
|
||||||
@ -126,41 +137,44 @@ module.exports.check_java = function() {
|
|||||||
|
|
||||||
// Returns a promise.
|
// Returns a promise.
|
||||||
module.exports.check_android = function() {
|
module.exports.check_android = function() {
|
||||||
var androidCmdPath = forgivingWhichSync('android');
|
return Q().then(function() {
|
||||||
var adbInPath = !!forgivingWhichSync('adb');
|
var androidCmdPath = forgivingWhichSync('android');
|
||||||
var hasAndroidHome = !!process.env['ANDROID_HOME'] && fs.existsSync(process.env['ANDROID_HOME']);
|
var adbInPath = !!forgivingWhichSync('adb');
|
||||||
if (hasAndroidHome && !androidCmdPath) {
|
var hasAndroidHome = !!process.env['ANDROID_HOME'] && fs.existsSync(process.env['ANDROID_HOME']);
|
||||||
process.env['PATH'] += path.delimiter + path.join(process.env['ANDROID_HOME'], 'tools');
|
if (hasAndroidHome && !androidCmdPath) {
|
||||||
}
|
process.env['PATH'] += path.delimiter + path.join(process.env['ANDROID_HOME'], 'tools');
|
||||||
if (androidCmdPath && !hasAndroidHome) {
|
|
||||||
var parentDir = path.dirname(androidCmdPath);
|
|
||||||
if (path.basename(parentDir) == 'tools') {
|
|
||||||
process.env['ANDROID_HOME'] = path.dirname(parentDir);
|
|
||||||
hasAndroidHome = true;
|
|
||||||
}
|
}
|
||||||
}
|
if (androidCmdPath && !hasAndroidHome) {
|
||||||
if (hasAndroidHome && !adbInPath) {
|
var parentDir = path.dirname(androidCmdPath);
|
||||||
process.env['PATH'] += path.delimiter + path.join(process.env['ANDROID_HOME'], 'platform-tools');
|
if (path.basename(parentDir) == 'tools') {
|
||||||
}
|
process.env['ANDROID_HOME'] = path.dirname(parentDir);
|
||||||
|
hasAndroidHome = true;
|
||||||
var valid_target = this.get_target();
|
}
|
||||||
var msg = 'Failed to run "android". Make sure you have the latest Android SDK installed, and that the "android" command (inside the tools/ folder) is added to your PATH.';
|
}
|
||||||
return tryCommand('android list targets', msg)
|
if (hasAndroidHome && !adbInPath) {
|
||||||
.then(function(output) {
|
process.env['PATH'] += path.delimiter + path.join(process.env['ANDROID_HOME'], 'platform-tools');
|
||||||
if (!output.match(valid_target)) {
|
|
||||||
return Q.reject(new Error('Please install Android target ' + valid_target.split('-')[1] +
|
|
||||||
' (the Android newest SDK). Make sure you have the latest Android tools installed as well.' +
|
|
||||||
' Run "android" from your command-line to install/update any missing SDKs or tools.'));
|
|
||||||
}
|
}
|
||||||
}).then(function() {
|
|
||||||
if (!process.env['ANDROID_HOME']) {
|
if (!process.env['ANDROID_HOME']) {
|
||||||
throw new Error('ANDROID_HOME is not set and "android" command not in your PATH. You must fulfill at least one of these conditions.');
|
throw new Error('ANDROID_HOME is not set and "android" command not in your PATH. You must fulfill at least one of these conditions.');
|
||||||
}
|
}
|
||||||
if (!fs.existsSync(process.env['ANDROID_HOME'])) {
|
if (!fs.existsSync(process.env['ANDROID_HOME'])) {
|
||||||
throw new Error('ANDROID_HOME is set to a non-existant path: ' + process.env['ANDROID_HOME']);
|
throw new Error('ANDROID_HOME is set to a non-existant path: ' + process.env['ANDROID_HOME']);
|
||||||
}
|
}
|
||||||
|
// Check that the target sdk level is installed.
|
||||||
|
return module.exports.check_android_target(module.exports.get_target());
|
||||||
});
|
});
|
||||||
}
|
};
|
||||||
|
|
||||||
|
module.exports.check_android_target = function(valid_target) {
|
||||||
|
var msg = 'Failed to run "android". Make sure you have the latest Android SDK installed, and that the "android" command (inside the tools/ folder) is added to your PATH.';
|
||||||
|
return tryCommand('android list targets', msg)
|
||||||
|
.then(function(output) {
|
||||||
|
if (!output.match(valid_target)) {
|
||||||
|
throw new Error('Please install Android target "' + valid_target + '".\n' +
|
||||||
|
'Hint: Run "android" from your command-line to open the SDK manager.');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
// Returns a promise.
|
// Returns a promise.
|
||||||
module.exports.run = function() {
|
module.exports.run = function() {
|
||||||
|
@ -83,9 +83,38 @@ function copyJsAndLibrary(projectPath, shared, projectName) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function runAndroidUpdate(projectPath, target_api, shared) {
|
function extractSubProjectPaths(data) {
|
||||||
var targetFrameworkDir = getFrameworkDir(projectPath, shared);
|
var ret = {};
|
||||||
return exec('android update project --subprojects --path "' + projectPath + '" --target ' + target_api + ' --library "' + path.relative(projectPath, targetFrameworkDir) + '"');
|
var r = /^\s*android\.library\.reference\.\d+=(.*)(?:\s|$)/mg
|
||||||
|
var m;
|
||||||
|
while (m = r.exec(data)) {
|
||||||
|
ret[m[1]] = 1;
|
||||||
|
}
|
||||||
|
return Object.keys(ret);
|
||||||
|
}
|
||||||
|
|
||||||
|
function writeProjectProperties(projectPath, target_api, shared) {
|
||||||
|
var dstPath = path.join(projectPath, 'project.properties');
|
||||||
|
var templatePath = path.join(ROOT, 'bin', 'templates', 'project', 'project.properties');
|
||||||
|
var srcPath = fs.existsSync(dstPath) ? dstPath : templatePath;
|
||||||
|
var data = fs.readFileSync(srcPath, 'utf8');
|
||||||
|
data = data.replace(/^target=.*/m, 'target=' + target_api);
|
||||||
|
var subProjects = extractSubProjectPaths(data);
|
||||||
|
subProjects = subProjects.filter(function(p) {
|
||||||
|
return !(/^CordovaLib$/m.exec(p) ||
|
||||||
|
/[\\\/]cordova-android[\\\/]framework$/m.exec(p) ||
|
||||||
|
/^(\.\.[\\\/])+framework$/m.exec(p)
|
||||||
|
);
|
||||||
|
});
|
||||||
|
subProjects.unshift(shared ? path.relative(projectPath, path.join(ROOT, 'framework')) : 'CordovaLib');
|
||||||
|
data = data.replace(/^\s*android\.library\.reference\.\d+=.*\n/mg, '');
|
||||||
|
if (!/\n$/.exec(data)) {
|
||||||
|
data += '\n';
|
||||||
|
}
|
||||||
|
for (var i = 0; i < subProjects.length; ++i) {
|
||||||
|
data += 'android.library.reference.' + (i+1) + '=' + subProjects[i] + '\n';
|
||||||
|
}
|
||||||
|
fs.writeFileSync(dstPath, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
function copyBuildRules(projectPath) {
|
function copyBuildRules(projectPath) {
|
||||||
@ -251,7 +280,7 @@ exports.createProject = function(project_path, package_name, project_name, proje
|
|||||||
copyBuildRules(project_path);
|
copyBuildRules(project_path);
|
||||||
});
|
});
|
||||||
// Link it to local android install.
|
// Link it to local android install.
|
||||||
return runAndroidUpdate(project_path, target_api, use_shared_project);
|
writeProjectProperties(project_path, target_api);
|
||||||
}).then(function() {
|
}).then(function() {
|
||||||
console.log('Project successfully created.');
|
console.log('Project successfully created.');
|
||||||
});
|
});
|
||||||
@ -274,22 +303,20 @@ function extractProjectNameFromManifest(projectPath) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Returns a promise.
|
// Returns a promise.
|
||||||
exports.updateProject = function(projectPath) {
|
exports.updateProject = function(projectPath, shared) {
|
||||||
var version = fs.readFileSync(path.join(ROOT, 'VERSION'), 'utf-8').trim();
|
var newVersion = fs.readFileSync(path.join(ROOT, 'VERSION'), 'utf-8').trim();
|
||||||
// Check that requirements are met and proper targets are installed
|
// Check that requirements are met and proper targets are installed
|
||||||
return check_reqs.run()
|
return check_reqs.run()
|
||||||
.then(function() {
|
.then(function() {
|
||||||
var projectName = extractProjectNameFromManifest(projectPath);
|
var projectName = extractProjectNameFromManifest(projectPath);
|
||||||
var target_api = check_reqs.get_target();
|
var target_api = check_reqs.get_target();
|
||||||
copyJsAndLibrary(projectPath, false, projectName);
|
copyJsAndLibrary(projectPath, shared, projectName);
|
||||||
copyScripts(projectPath);
|
copyScripts(projectPath);
|
||||||
copyBuildRules(projectPath);
|
copyBuildRules(projectPath);
|
||||||
removeDebuggableFromManifest(projectPath);
|
removeDebuggableFromManifest(projectPath);
|
||||||
return runAndroidUpdate(projectPath, target_api, false)
|
writeProjectProperties(projectPath, target_api, shared);
|
||||||
.then(function() {
|
console.log('Android project is now at version ' + newVersion);
|
||||||
console.log('Android project is now at version ' + version);
|
console.log('If you updated from a pre-3.2.0 version and use an IDE, we now require that you import the "CordovaLib" library project.');
|
||||||
console.log('If you updated from a pre-3.2.0 version and use an IDE, we now require that you import the "CordovaLib" library project.');
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
87
bin/templates/cordova/lib/build.js
vendored
87
bin/templates/cordova/lib/build.js
vendored
@ -27,9 +27,9 @@ var shell = require('shelljs'),
|
|||||||
ROOT = path.join(__dirname, '..', '..');
|
ROOT = path.join(__dirname, '..', '..');
|
||||||
var check_reqs = require('./check_reqs');
|
var check_reqs = require('./check_reqs');
|
||||||
|
|
||||||
// Globals
|
var LOCAL_PROPERTIES_TEMPLATE =
|
||||||
var build_type,
|
'# This file is automatically generated.\n' +
|
||||||
build_method;
|
'# Do not modify this file -- YOUR CHANGES WILL BE ERASED!\n';
|
||||||
|
|
||||||
function find_files(directory, predicate) {
|
function find_files(directory, predicate) {
|
||||||
if (fs.existsSync(directory)) {
|
if (fs.existsSync(directory)) {
|
||||||
@ -51,22 +51,25 @@ function hasCustomRules() {
|
|||||||
return fs.existsSync(path.join(ROOT, 'custom_rules.xml'));
|
return fs.existsSync(path.join(ROOT, 'custom_rules.xml'));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Copy the gradle wrapper files on each build so that:
|
function extractProjectNameFromManifest(projectPath) {
|
||||||
// A) We don't require the Android SDK at project creation time, and
|
var manifestPath = path.join(projectPath, 'AndroidManifest.xml');
|
||||||
// B) So that they are always up-to-date.
|
var manifestData = fs.readFileSync(manifestPath, 'utf8');
|
||||||
function copyGradleWrapper() {
|
var m = /<activity[\s\S]*?android:name\s*=\s*"(.*?)"/i.exec(manifestData);
|
||||||
var projectPath = ROOT;
|
if (!m) {
|
||||||
// check_reqs ensures that this is set.
|
throw new Error('Could not find activity name in ' + manifestPath);
|
||||||
var sdkDir = process.env['ANDROID_HOME'];
|
|
||||||
var wrapperDir = path.join(sdkDir, 'tools', 'templates', 'gradle', 'wrapper');
|
|
||||||
if (process.platform == 'win32') {
|
|
||||||
shell.cp('-f', path.join(wrapperDir, 'gradlew.bat'), projectPath);
|
|
||||||
} else {
|
|
||||||
shell.cp('-f', path.join(wrapperDir, 'gradlew'), projectPath);
|
|
||||||
}
|
}
|
||||||
shell.rm('-rf', path.join(projectPath, 'gradle', 'wrapper'));
|
return m[1];
|
||||||
shell.mkdir('-p', path.join(projectPath, 'gradle'));
|
}
|
||||||
shell.cp('-r', path.join(wrapperDir, 'gradle', 'wrapper'), path.join(projectPath, 'gradle'));
|
|
||||||
|
function extractSubProjectPaths() {
|
||||||
|
var data = fs.readFileSync(path.join(ROOT, 'project.properties'), 'utf8');
|
||||||
|
var ret = {};
|
||||||
|
var r = /^\s*android\.library\.reference\.\d+=(.*)(?:\s|$)/mg
|
||||||
|
var m;
|
||||||
|
while (m = r.exec(data)) {
|
||||||
|
ret[m[1]] = 1;
|
||||||
|
}
|
||||||
|
return Object.keys(ret);
|
||||||
}
|
}
|
||||||
|
|
||||||
var builders = {
|
var builders = {
|
||||||
@ -83,6 +86,23 @@ var builders = {
|
|||||||
prepEnv: function() {
|
prepEnv: function() {
|
||||||
return check_reqs.check_ant()
|
return check_reqs.check_ant()
|
||||||
.then(function() {
|
.then(function() {
|
||||||
|
// Copy in build.xml on each build so that:
|
||||||
|
// A) we don't require the Android SDK at project creation time, and
|
||||||
|
// B) we always use the SDK's latest version of it.
|
||||||
|
var sdkDir = process.env['ANDROID_HOME'];
|
||||||
|
var buildTemplate = fs.readFileSync(path.join(sdkDir, 'tools', 'lib', 'build.template'), 'utf8');
|
||||||
|
function writeBuildXml(projectPath) {
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var subProjects = extractSubProjectPaths();
|
||||||
|
writeBuildXml(ROOT);
|
||||||
|
for (var i = 0; i < subProjects.length; ++i) {
|
||||||
|
writeBuildXml(path.join(ROOT, subProjects[i]));
|
||||||
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -156,7 +176,24 @@ var builders = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
prepEnv: function() {
|
prepEnv: function() {
|
||||||
return Q();
|
return check_reqs.check_gradle()
|
||||||
|
.then(function() {
|
||||||
|
// Copy the gradle wrapper on each build so that:
|
||||||
|
// A) we don't require the Android SDK at project creation time, and
|
||||||
|
// B) we always use the SDK's latest version of it.
|
||||||
|
var projectPath = ROOT;
|
||||||
|
// check_reqs ensures that this is set.
|
||||||
|
var sdkDir = process.env['ANDROID_HOME'];
|
||||||
|
var wrapperDir = path.join(sdkDir, 'tools', 'templates', 'gradle', 'wrapper');
|
||||||
|
if (process.platform == 'win32') {
|
||||||
|
shell.cp('-f', path.join(wrapperDir, 'gradlew.bat'), projectPath);
|
||||||
|
} else {
|
||||||
|
shell.cp('-f', path.join(wrapperDir, 'gradlew'), projectPath);
|
||||||
|
}
|
||||||
|
shell.rm('-rf', path.join(projectPath, 'gradle', 'wrapper'));
|
||||||
|
shell.mkdir('-p', path.join(projectPath, 'gradle'));
|
||||||
|
shell.cp('-r', path.join(wrapperDir, 'gradle', 'wrapper'), path.join(projectPath, 'gradle'));
|
||||||
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -182,8 +219,6 @@ var builders = {
|
|||||||
copyGradleWrapper();
|
copyGradleWrapper();
|
||||||
return Q().then(function() {
|
return Q().then(function() {
|
||||||
return spawn(wrapper, args);
|
return spawn(wrapper, args);
|
||||||
}).then(function() {
|
|
||||||
return builder.getOutputFiles(build_type);
|
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -261,9 +296,11 @@ function parseOpts(options) {
|
|||||||
module.exports.runClean = function(options) {
|
module.exports.runClean = function(options) {
|
||||||
var opts = parseOpts(options);
|
var opts = parseOpts(options);
|
||||||
var builder = builders[opts.buildMethod];
|
var builder = builders[opts.buildMethod];
|
||||||
return builder.prepEnv(opts.buildType)
|
return builder.prepEnv()
|
||||||
.then(function() {
|
.then(function() {
|
||||||
return builder.clean();
|
return builder.clean();
|
||||||
|
}).then(function() {
|
||||||
|
shell.rm('-rf', path.join(ROOT, 'out'));
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -275,10 +312,13 @@ module.exports.run = function(options) {
|
|||||||
var opts = parseOpts(options);
|
var opts = parseOpts(options);
|
||||||
|
|
||||||
var builder = builders[opts.buildMethod];
|
var builder = builders[opts.buildMethod];
|
||||||
return builder.prepEnv(opts.buildType)
|
return builder.prepEnv()
|
||||||
.then(function() {
|
.then(function() {
|
||||||
return builder.build(opts.buildType);
|
return builder.build(opts.buildType);
|
||||||
}).then(function(apkFiles) {
|
}).then(function(apkFiles) {
|
||||||
|
// TODO: Rather than copy apks to out, it might be better to
|
||||||
|
// just write out what the last .apk build was. These files
|
||||||
|
// are used by get_apk().
|
||||||
var outputDir = path.join(ROOT, 'out');
|
var outputDir = path.join(ROOT, 'out');
|
||||||
shell.mkdir('-p', outputDir);
|
shell.mkdir('-p', outputDir);
|
||||||
for (var i=0; i < apkFiles.length; ++i) {
|
for (var i=0; i < apkFiles.length; ++i) {
|
||||||
@ -299,6 +339,7 @@ module.exports.get_apk = function(build_type) {
|
|||||||
console.error('ERROR : No .apk found in ' + outputDir + ' directory');
|
console.error('ERROR : No .apk found in ' + outputDir + ' directory');
|
||||||
process.exit(2);
|
process.exit(2);
|
||||||
}
|
}
|
||||||
|
// TODO: Use build_type here.
|
||||||
console.log('Using apk: ' + candidates[0]);
|
console.log('Using apk: ' + candidates[0]);
|
||||||
return candidates[0];
|
return candidates[0];
|
||||||
};
|
};
|
||||||
|
15
bin/templates/project/project.properties
Normal file
15
bin/templates/project/project.properties
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
# This file is automatically generated by Android Tools.
|
||||||
|
# Do not modify this file -- YOUR CHANGES WILL BE ERASED!
|
||||||
|
#
|
||||||
|
# This file must be checked in Version Control Systems.
|
||||||
|
#
|
||||||
|
# To customize properties used by the Ant build system edit
|
||||||
|
# "ant.properties", and override values to adapt the script to your
|
||||||
|
# project structure.
|
||||||
|
#
|
||||||
|
# To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home):
|
||||||
|
#proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt
|
||||||
|
|
||||||
|
android.library.reference.1=CordovaLib
|
||||||
|
# Project target.
|
||||||
|
target=This_gets_replaced
|
Loading…
Reference in New Issue
Block a user