mirror of
https://github.com/apache/cordova-android.git
synced 2025-02-20 23:56:20 +08:00
Merge branch 'master' into 4.0.x (build & create script updates)
Conflicts: bin/lib/check_reqs.js bin/lib/create.js bin/node_modules/which/package.json bin/templates/cordova/lib/build.js
This commit is contained in:
commit
4c1942e3fe
@ -19,7 +19,7 @@
|
||||
under the License.
|
||||
*/
|
||||
|
||||
var shell = require('shelljs'),
|
||||
var shelljs = require('shelljs'),
|
||||
child_process = require('child_process'),
|
||||
Q = require('q'),
|
||||
path = require('path'),
|
||||
@ -27,92 +27,157 @@ var shell = require('shelljs'),
|
||||
which = require('which'),
|
||||
ROOT = path.join(__dirname, '..', '..');
|
||||
|
||||
var isWindows = process.platform == 'win32';
|
||||
|
||||
function forgivingWhichSync(cmd) {
|
||||
try {
|
||||
return which.sync(cmd);
|
||||
} catch (e) {
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
function tryCommand(cmd, errMsg) {
|
||||
var d = Q.defer();
|
||||
child_process.exec(cmd, function(err, stdout, stderr) {
|
||||
if (err) d.reject(new Error(errMsg));
|
||||
else d.resolve(stdout);
|
||||
});
|
||||
return d.promise;
|
||||
}
|
||||
|
||||
// Get valid target from framework/project.properties
|
||||
module.exports.get_target = function() {
|
||||
if(fs.existsSync(path.join(ROOT, 'framework', 'project.properties'))) {
|
||||
var target = shell.grep(/target=android-[\d+]/, path.join(ROOT, 'framework', 'project.properties'));
|
||||
var target = shelljs.grep(/target=android-[\d+]/, path.join(ROOT, 'framework', 'project.properties'));
|
||||
return target.split('=')[1].replace('\n', '').replace('\r', '').replace(' ', '');
|
||||
} else if (fs.existsSync(path.join(ROOT, 'project.properties'))) {
|
||||
// if no target found, we're probably in a project and project.properties is in ROOT.
|
||||
// this is called on the project itself, and can support Google APIs AND Vanilla Android
|
||||
var target = shell.grep(/target=android-[\d+]/, path.join(ROOT, 'project.properties')) ||
|
||||
shell.grep(/target=Google Inc.:Google APIs:[\d+]/, path.join(ROOT, 'project.properties'));
|
||||
var target = shelljs.grep(/target=android-[\d+]/, path.join(ROOT, 'project.properties')) ||
|
||||
shelljs.grep(/target=Google Inc.:Google APIs:[\d+]/, path.join(ROOT, 'project.properties'));
|
||||
if(target == "" || !target) {
|
||||
// Try Google Glass APIs
|
||||
target = shell.grep(/target=Google Inc.:Glass Development Kit Preview:[\d+]/, path.join(ROOT, 'project.properties'));
|
||||
target = shelljs.grep(/target=Google Inc.:Glass Development Kit Preview:[\d+]/, path.join(ROOT, 'project.properties'));
|
||||
}
|
||||
return target.split('=')[1].replace('\n', '').replace('\r', '');
|
||||
}
|
||||
}
|
||||
|
||||
// Returns a promise.
|
||||
module.exports.sdk_dir = function() {
|
||||
var d = Q.defer();
|
||||
which('android', function(err, path) {
|
||||
if (err) {
|
||||
d.reject(new Error('ERROR: Cannot find Android SDK. android command not found.'));
|
||||
} else {
|
||||
var toolsDir = path.substring(0, path.lastIndexOf('/'));
|
||||
if (toolsDir.substring(toolsDir.length-6) != "/tools") {
|
||||
d.reject(new Error('ERROR: Cannot find Android SDK. android command not found in tools dir.'));
|
||||
}
|
||||
d.resolve(toolsDir.substring(0, toolsDir.length-6));
|
||||
}
|
||||
});
|
||||
return d.promise;
|
||||
// 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.');
|
||||
};
|
||||
|
||||
// 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.
|
||||
module.exports.check_ant = function() {
|
||||
var d = Q.defer();
|
||||
child_process.exec('ant -version', function(err, stdout, stderr) {
|
||||
if (err) d.reject(new Error('ERROR : executing command \'ant\', make sure you have ant installed and added to your path.'));
|
||||
else d.resolve();
|
||||
});
|
||||
return d.promise;
|
||||
}
|
||||
|
||||
// Returns a promise.
|
||||
module.exports.check_java = function() {
|
||||
var d = Q.defer();
|
||||
child_process.exec('java -version', function(err, stdout, stderr) {
|
||||
if(err) {
|
||||
var msg =
|
||||
'Failed to run \'java -version\', make sure your java environment is set up\n' +
|
||||
'including JDK and JRE.\n' +
|
||||
'Your JAVA_HOME variable is ' + process.env.JAVA_HOME + '\n';
|
||||
d.reject(new Error(msg + err));
|
||||
var javacPath = forgivingWhichSync('javac');
|
||||
var hasJavaHome = !!process.env['JAVA_HOME'];
|
||||
return Q().then(function() {
|
||||
if (hasJavaHome) {
|
||||
// Windows java installer doesn't add javac to PATH, nor set JAVA_HOME (ugh).
|
||||
if (!javacPath) {
|
||||
process.env['PATH'] += path.delimiter + path.join(process.env['JAVA_HOME'], 'bin');
|
||||
}
|
||||
} else {
|
||||
if (javacPath) {
|
||||
// OS X has a command for finding JAVA_HOME.
|
||||
if (fs.existsSync('/usr/libexec/java_home')) {
|
||||
return tryCommand('/usr/libexec/java_home', 'Failed to run: /usr/libexec/java_home')
|
||||
.then(function(stdout) {
|
||||
process.env['JAVA_HOME'] = stdout.trim();
|
||||
});
|
||||
} else {
|
||||
// See if we can derive it from javac's location.
|
||||
var maybeJavaHome = path.dirname(path.dirname(javacPath));
|
||||
if (fs.existsSync(path.join(maybeJavaHome, 'lib', 'tools.jar'))) {
|
||||
process.env['JAVA_HOME'] = maybeJavaHome;
|
||||
} else {
|
||||
throw new Error('Could not find JAVA_HOME. Try setting the environment variable manually');
|
||||
}
|
||||
}
|
||||
} else if (isWindows) {
|
||||
// Try to auto-detect java in the default install paths.
|
||||
var firstJdkDir =
|
||||
shelljs.ls(process.env['ProgramFiles'] + '\\java\\jdk*')[0] ||
|
||||
shelljs.ls('C:\\Program Files\\java\\jdk*')[0] ||
|
||||
shelljs.ls('C:\\Program Files (x86)\\java\\jdk*')[0];
|
||||
if (firstJdkDir) {
|
||||
// shelljs always uses / in paths.
|
||||
firstJdkDir = firstJdkDir.replace(/\//g, path.sep);
|
||||
if (!javacPath) {
|
||||
process.env['PATH'] += path.delimiter + path.join(firstJdkDir, 'bin');
|
||||
}
|
||||
process.env['JAVA_HOME'] = firstJdkDir;
|
||||
}
|
||||
}
|
||||
}
|
||||
else d.resolve();
|
||||
}).then(function() {
|
||||
var msg =
|
||||
'Failed to run "java -version", make sure your java environment is set up\n' +
|
||||
'including JDK and JRE.\n' +
|
||||
'Your JAVA_HOME variable is: ' + process.env['JAVA_HOME'];
|
||||
return tryCommand('java -version', msg)
|
||||
}).then(function() {
|
||||
msg = 'Failed to run "javac -version", make sure you have a Java JDK (not just a JRE) installed.';
|
||||
return tryCommand('javac -version', msg)
|
||||
});
|
||||
return d.promise;
|
||||
}
|
||||
|
||||
// Returns a promise.
|
||||
module.exports.check_android = function() {
|
||||
var valid_target = this.get_target();
|
||||
var d = Q.defer();
|
||||
child_process.exec('android list targets', function(err, stdout, stderr) {
|
||||
if (err) d.reject(stderr);
|
||||
else d.resolve(stdout);
|
||||
return Q().then(function() {
|
||||
var androidCmdPath = forgivingWhichSync('android');
|
||||
var adbInPath = !!forgivingWhichSync('adb');
|
||||
var hasAndroidHome = !!process.env['ANDROID_HOME'] && fs.existsSync(process.env['ANDROID_HOME']);
|
||||
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 (hasAndroidHome && !adbInPath) {
|
||||
process.env['PATH'] += path.delimiter + path.join(process.env['ANDROID_HOME'], 'platform-tools');
|
||||
}
|
||||
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.');
|
||||
}
|
||||
if (!fs.existsSync(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());
|
||||
});
|
||||
};
|
||||
|
||||
return d.promise.then(function(output) {
|
||||
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)) {
|
||||
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.'));
|
||||
}
|
||||
return Q();
|
||||
}, function(stderr) {
|
||||
if (stderr.match(/command\snot\sfound/) || stderr.match(/is not recognized as an internal or external command/)) {
|
||||
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. Error: ' + stderr ));
|
||||
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.
|
||||
module.exports.run = function() {
|
||||
return Q.all([this.check_ant(), this.check_java(), this.check_android()]);
|
||||
return Q.all([this.check_java(), this.check_android()]);
|
||||
}
|
||||
|
||||
|
@ -83,16 +83,47 @@ function copyJsAndLibrary(projectPath, shared, projectName) {
|
||||
}
|
||||
}
|
||||
|
||||
function runAndroidUpdate(projectPath, target_api, shared) {
|
||||
var targetFrameworkDir = getFrameworkDir(projectPath, shared);
|
||||
return exec('android update project --subprojects --path "' + projectPath + '" --target ' + target_api + ' --library "' + path.relative(projectPath, targetFrameworkDir) + '"');
|
||||
function extractSubProjectPaths(data) {
|
||||
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);
|
||||
}
|
||||
|
||||
function copyAntRules(projectPath) {
|
||||
var srcDir = path.join(ROOT, 'bin', 'templates', 'project');
|
||||
if (fs.existsSync(path.join(srcDir, 'custom_rules.xml'))) {
|
||||
shell.cp('-f', path.join(srcDir, 'custom_rules.xml'), projectPath);
|
||||
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) {
|
||||
var srcDir = path.join(ROOT, 'bin', 'templates', 'project');
|
||||
shell.cp('-f', path.join(srcDir, 'custom_rules.xml'), projectPath);
|
||||
|
||||
shell.cp('-f', path.join(srcDir, 'build.gradle'), projectPath);
|
||||
shell.cp('-f', path.join(srcDir, 'libraries.gradle'), projectPath);
|
||||
shell.cp('-f', path.join(srcDir, 'settings.gradle'), projectPath);
|
||||
}
|
||||
|
||||
function copyScripts(projectPath) {
|
||||
@ -206,7 +237,7 @@ exports.createProject = function(project_path, package_name, project_name, proje
|
||||
})
|
||||
// Check that requirements are met and proper targets are installed
|
||||
.then(function() {
|
||||
check_reqs.run();
|
||||
return check_reqs.run();
|
||||
}).then(function() {
|
||||
// Log the given values for the project
|
||||
console.log('Creating Cordova project for the Android platform:');
|
||||
@ -222,6 +253,7 @@ exports.createProject = function(project_path, package_name, project_name, proje
|
||||
shell.cp('-r', path.join(project_template_dir, 'assets'), project_path);
|
||||
shell.cp('-r', path.join(project_template_dir, 'res'), project_path);
|
||||
shell.cp('-r', path.join(ROOT, 'framework', 'res', 'xml'), path.join(project_path, 'res'));
|
||||
shell.cp(path.join(project_template_dir, 'gitignore'), path.join(project_path, '.gitignore'));
|
||||
|
||||
shell.cp('-f', path.join(project_template_dir, 'build.gradle'), project_path);
|
||||
shell.cp('-f', path.join(project_template_dir, 'libraries.gradle'), project_path);
|
||||
@ -263,10 +295,10 @@ exports.createProject = function(project_path, package_name, project_name, proje
|
||||
shell.sed('-i', /__PACKAGE__/, package_name, manifest_path);
|
||||
shell.sed('-i', /__APILEVEL__/, target_api.split('-')[1], manifest_path);
|
||||
copyScripts(project_path);
|
||||
copyAntRules(project_path);
|
||||
copyBuildRules(project_path);
|
||||
});
|
||||
// Link it to local android install.
|
||||
return runAndroidUpdate(project_path, target_api, use_shared_project);
|
||||
writeProjectProperties(project_path, target_api);
|
||||
}).then(function() {
|
||||
console.log('Project successfully created.');
|
||||
});
|
||||
@ -289,22 +321,20 @@ function extractProjectNameFromManifest(projectPath) {
|
||||
}
|
||||
|
||||
// Returns a promise.
|
||||
exports.updateProject = function(projectPath) {
|
||||
var version = fs.readFileSync(path.join(ROOT, 'VERSION'), 'utf-8').trim();
|
||||
exports.updateProject = function(projectPath, shared) {
|
||||
var newVersion = fs.readFileSync(path.join(ROOT, 'VERSION'), 'utf-8').trim();
|
||||
// Check that requirements are met and proper targets are installed
|
||||
return check_reqs.run()
|
||||
.then(function() {
|
||||
var projectName = extractProjectNameFromManifest(projectPath);
|
||||
var target_api = check_reqs.get_target();
|
||||
copyJsAndLibrary(projectPath, false, projectName);
|
||||
copyJsAndLibrary(projectPath, shared, projectName);
|
||||
copyScripts(projectPath);
|
||||
copyAntRules(projectPath);
|
||||
copyBuildRules(projectPath);
|
||||
removeDebuggableFromManifest(projectPath);
|
||||
return runAndroidUpdate(projectPath, target_api, false)
|
||||
.then(function() {
|
||||
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.');
|
||||
});
|
||||
writeProjectProperties(projectPath, target_api, shared);
|
||||
console.log('Android project is now at version ' + newVersion);
|
||||
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.');
|
||||
});
|
||||
};
|
||||
|
||||
|
2
bin/node_modules/which/package.json
generated
vendored
2
bin/node_modules/which/package.json
generated
vendored
@ -27,5 +27,5 @@
|
||||
},
|
||||
"homepage": "https://github.com/isaacs/node-which",
|
||||
"_id": "which@1.0.5",
|
||||
"_from": "which@^1.0.5"
|
||||
"_from": "which@"
|
||||
}
|
||||
|
@ -24,13 +24,17 @@ var build = require('./lib/build'),
|
||||
args = process.argv;
|
||||
|
||||
// Support basic help commands
|
||||
if(args[2] == '--help' || args[2] == '/?' || args[2] == '-h' ||
|
||||
args[2] == 'help' || args[2] == '-help' || args[2] == '/help') {
|
||||
if(args[2] == '--help' ||
|
||||
args[2] == '/?' ||
|
||||
args[2] == '-h' ||
|
||||
args[2] == 'help' ||
|
||||
args[2] == '-help' ||
|
||||
args[2] == '/help') {
|
||||
build.help();
|
||||
} else {
|
||||
reqs.run().then(function() {
|
||||
return build.run(args[2]);
|
||||
}).done(null, function(err) {
|
||||
reqs.run().done(function() {
|
||||
return build.run(args.slice(2));
|
||||
}, function(err) {
|
||||
console.error(err);
|
||||
process.exit(2);
|
||||
});
|
||||
|
@ -19,18 +19,26 @@
|
||||
under the License.
|
||||
*/
|
||||
|
||||
var clean = require('./lib/clean'),
|
||||
var build = require('./lib/build'),
|
||||
reqs = require('./lib/check_reqs'),
|
||||
args = process.argv;
|
||||
var path = require('path');
|
||||
|
||||
// Usage support for when args are given
|
||||
if(args.length > 2) {
|
||||
clean.help();
|
||||
// Support basic help commands
|
||||
if(args[2] == '--help' ||
|
||||
args[2] == '/?' ||
|
||||
args[2] == '-h' ||
|
||||
args[2] == 'help' ||
|
||||
args[2] == '-help' ||
|
||||
args[2] == '/help') {
|
||||
console.log('Usage: ' + path.relative(process.cwd(), process.argv[1]));
|
||||
console.log('Cleans the project directory.');
|
||||
process.exit(0);
|
||||
} else {
|
||||
reqs.run().done(function() {
|
||||
return clean.run();
|
||||
return build.runClean(args.slice(2));
|
||||
}, function(err) {
|
||||
console.error('ERROR: ' + err);
|
||||
console.error(err);
|
||||
process.exit(2);
|
||||
});
|
||||
}
|
||||
|
369
bin/templates/cordova/lib/build.js
vendored
369
bin/templates/cordova/lib/build.js
vendored
@ -26,79 +26,330 @@ var shell = require('shelljs'),
|
||||
fs = require('fs'),
|
||||
which = require('which'),
|
||||
ROOT = path.join(__dirname, '..', '..');
|
||||
var check_reqs = require('./check_reqs');
|
||||
|
||||
var LOCAL_PROPERTIES_TEMPLATE =
|
||||
'# This file is automatically generated.\n' +
|
||||
'# Do not modify this file -- YOUR CHANGES WILL BE ERASED!\n';
|
||||
|
||||
module.exports.getAntArgs = function(cmd) {
|
||||
var args = [cmd, '-f', path.join(ROOT, 'build.xml')];
|
||||
try {
|
||||
// Specify sdk dir in case local properties are missing
|
||||
args.push('-Dsdk.dir='+path.join(which.sync('android'), '../..'));
|
||||
} catch(e) {
|
||||
// Can't find android; don't push arg: assume all is okay
|
||||
}
|
||||
return args;
|
||||
};
|
||||
|
||||
/*
|
||||
* Builds the project with ant.
|
||||
* Returns a promise.
|
||||
*/
|
||||
module.exports.run = function(build_type) {
|
||||
//default build type
|
||||
build_type = typeof build_type !== 'undefined' ? build_type : "--debug";
|
||||
var args = module.exports.getAntArgs('debug');
|
||||
switch(build_type) {
|
||||
case '--debug' :
|
||||
break;
|
||||
case '--release' :
|
||||
args[0] = 'release';
|
||||
break;
|
||||
case '--nobuild' :
|
||||
console.log('Skipping build...');
|
||||
return Q();
|
||||
default :
|
||||
return Q.reject('Build option \'' + build_type + '\' not recognized.');
|
||||
}
|
||||
var ret = Q();
|
||||
return ret.then(function() {
|
||||
return spawn('ant', args);
|
||||
});
|
||||
}
|
||||
|
||||
/*
|
||||
* Gets the path to the apk file, if not such file exists then
|
||||
* the script will error out. (should we error or just return undefined?)
|
||||
*/
|
||||
module.exports.get_apk = function() {
|
||||
var binDir = path.join(ROOT, 'bin');
|
||||
if (fs.existsSync(binDir)) {
|
||||
var candidates = fs.readdirSync(binDir).filter(function(p) {
|
||||
// Need to choose between release and debug .apk.
|
||||
return path.extname(p) == '.apk';
|
||||
}).map(function(p) {
|
||||
p = path.join(binDir, p);
|
||||
function find_files(directory, predicate) {
|
||||
if (fs.existsSync(directory)) {
|
||||
var candidates = fs.readdirSync(directory).filter(predicate).map(function(p) {
|
||||
p = path.join(directory, p);
|
||||
return { p: p, t: fs.statSync(p).mtime };
|
||||
}).sort(function(a,b) {
|
||||
return a.t > b.t ? -1 :
|
||||
a.t < b.t ? 1 : 0;
|
||||
});
|
||||
if (candidates.length === 0) {
|
||||
console.error('ERROR : No .apk found in ' + binDir + ' directory');
|
||||
process.exit(2);
|
||||
}
|
||||
console.log('Using apk: ' + candidates[0].p);
|
||||
return candidates[0].p;
|
||||
}).map(function(p) { return p.p; });
|
||||
return candidates;
|
||||
} else {
|
||||
console.error('ERROR : unable to find project ' + binDir + ' directory, could not locate .apk');
|
||||
console.error('ERROR : unable to find project ' + directory + ' directory, could not locate .apk');
|
||||
process.exit(2);
|
||||
}
|
||||
}
|
||||
|
||||
function hasCustomRules() {
|
||||
return fs.existsSync(path.join(ROOT, 'custom_rules.xml'));
|
||||
}
|
||||
|
||||
function extractProjectNameFromManifest(projectPath) {
|
||||
var manifestPath = path.join(projectPath, 'AndroidManifest.xml');
|
||||
var manifestData = fs.readFileSync(manifestPath, 'utf8');
|
||||
var m = /<activity[\s\S]*?android:name\s*=\s*"(.*?)"/i.exec(manifestData);
|
||||
if (!m) {
|
||||
throw new Error('Could not find activity name in ' + manifestPath);
|
||||
}
|
||||
return m[1];
|
||||
}
|
||||
|
||||
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 = {
|
||||
ant: {
|
||||
getArgs: function(cmd) {
|
||||
var args = [cmd, '-f', path.join(ROOT, 'build.xml')];
|
||||
// custom_rules.xml is required for incremental builds.
|
||||
if (hasCustomRules()) {
|
||||
args.push('-Dout.dir=ant-build', '-Dgen.absolute.dir=ant-gen');
|
||||
}
|
||||
return args;
|
||||
},
|
||||
|
||||
prepEnv: function() {
|
||||
return check_reqs.check_ant()
|
||||
.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]));
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
/*
|
||||
* Builds the project with ant.
|
||||
* Returns a promise.
|
||||
*/
|
||||
build: function(build_type) {
|
||||
// Without our custom_rules.xml, we need to clean before building.
|
||||
var ret = Q();
|
||||
if (!hasCustomRules()) {
|
||||
// clean will call check_ant() for us.
|
||||
ret = this.clean();
|
||||
}
|
||||
|
||||
var builder = this;
|
||||
var args = this.getArgs(build_type == 'debug' ? 'debug' : 'release');
|
||||
return check_reqs.check_ant()
|
||||
.then(function() {
|
||||
return spawn('ant', args);
|
||||
}).then(function() {
|
||||
return builder.getOutputFiles();
|
||||
});
|
||||
},
|
||||
|
||||
clean: function() {
|
||||
var args = this.getArgs('clean');
|
||||
return check_reqs.check_ant()
|
||||
.then(function() {
|
||||
return spawn('ant', args);
|
||||
});
|
||||
},
|
||||
|
||||
// Find the recently-generated output APK files
|
||||
// Ant only generates one output file; return it.
|
||||
getOutputFiles: function() {
|
||||
var binDir;
|
||||
if(hasCustomRules()) {
|
||||
binDir = path.join(ROOT, 'ant-build');
|
||||
} else {
|
||||
binDir = path.join(ROOT, 'bin');
|
||||
}
|
||||
var candidates = find_files(binDir, function(candidate) { return path.extname(candidate) == '.apk'; });
|
||||
if (candidates.length === 0) {
|
||||
console.error('ERROR : No .apk found in ' + binDir + ' directory');
|
||||
process.exit(2);
|
||||
}
|
||||
console.log('Using apk: ' + candidates[0]);
|
||||
return [candidates[0]];
|
||||
}
|
||||
},
|
||||
gradle: {
|
||||
getArgs: function(cmd) {
|
||||
var lintSteps = [
|
||||
'lint',
|
||||
'lintVitalRelease',
|
||||
'compileLint',
|
||||
'copyReleaseLint',
|
||||
'copyDebugLint'
|
||||
];
|
||||
var args = [cmd, '-b', path.join(ROOT, 'build.gradle')];
|
||||
// 10 seconds -> 6 seconds
|
||||
args.push('-Dorg.gradle.daemon=true');
|
||||
// Excluding lint: 6s-> 1.6s
|
||||
for (var i = 0; i < lintSteps.length; ++i) {
|
||||
args.push('-x', lintSteps[i]);
|
||||
}
|
||||
// Shaves another 100ms, but produces a "try at own risk" warning. Not worth it (yet):
|
||||
// args.push('-Dorg.gradle.parallel=true');
|
||||
return args;
|
||||
},
|
||||
|
||||
prepEnv: function() {
|
||||
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'));
|
||||
});
|
||||
},
|
||||
|
||||
/*
|
||||
* Builds the project with gradle.
|
||||
* Returns a promise.
|
||||
*/
|
||||
build: function(build_type) {
|
||||
var builder = this;
|
||||
var wrapper = path.join(ROOT, 'gradlew');
|
||||
var args = builder.getArgs('build');
|
||||
return Q().then(function() {
|
||||
return spawn(wrapper, args);
|
||||
}).then(function() {
|
||||
return builder.getOutputFiles(build_type);
|
||||
});
|
||||
},
|
||||
|
||||
clean: function() {
|
||||
var builder = this;
|
||||
var wrapper = path.join(ROOT, 'gradlew');
|
||||
var args = builder.getArgs('clean');
|
||||
return Q().then(function() {
|
||||
return spawn(wrapper, args);
|
||||
});
|
||||
},
|
||||
|
||||
// Find the recently-generated output APK files
|
||||
// Gradle can generate multiple output files; return all of them.
|
||||
getOutputFiles: function(build_type) {
|
||||
var binDir = path.join(ROOT, 'build', 'apk');
|
||||
var candidates = find_files(binDir, function(candidate) {
|
||||
// Need to choose between release and debug .apk.
|
||||
if (build_type === 'debug') {
|
||||
return (path.extname(candidate) == '.apk' && candidate.indexOf('-debug-') >= 0);
|
||||
}
|
||||
if (build_type === 'release') {
|
||||
return (path.extname(candidate) == '.apk' && candidate.indexOf('-release-') >= 0);
|
||||
}
|
||||
return path.extname(candidate) == '.apk';
|
||||
});
|
||||
return candidates;
|
||||
}
|
||||
},
|
||||
|
||||
none: {
|
||||
prepEnv: function() {
|
||||
return Q();
|
||||
},
|
||||
build: function() {
|
||||
console.log('Skipping build...');
|
||||
return Q();
|
||||
},
|
||||
clean: function() {
|
||||
return Q();
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
function parseOpts(options) {
|
||||
// Backwards-compatibility: Allow a single string argument
|
||||
if (typeof options == "string") options = [options];
|
||||
|
||||
var ret = {
|
||||
buildType: 'debug',
|
||||
buildMethod: process.env['ANDROID_BUILD'] || 'ant'
|
||||
};
|
||||
|
||||
// Iterate through command line options
|
||||
for (var i=0; options && (i < options.length); ++i) {
|
||||
if (options[i].substring && options[i].substring(0,2) == "--") {
|
||||
var option = options[i].substring(2);
|
||||
switch(option) {
|
||||
case 'debug':
|
||||
case 'release':
|
||||
ret.buildType = option;
|
||||
break;
|
||||
case 'ant':
|
||||
case 'gradle':
|
||||
ret.buildMethod = option;
|
||||
break;
|
||||
case 'nobuild' :
|
||||
ret.buildMethod = 'none';
|
||||
break;
|
||||
default :
|
||||
return Q.reject('Build option \'' + options[i] + '\' not recognized.');
|
||||
}
|
||||
} else {
|
||||
return Q.reject('Build option \'' + options[i] + '\' not recognized.');
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Builds the project with the specifed options
|
||||
* Returns a promise.
|
||||
*/
|
||||
module.exports.runClean = function(options) {
|
||||
var opts = parseOpts(options);
|
||||
var builder = builders[opts.buildMethod];
|
||||
return builder.prepEnv()
|
||||
.then(function() {
|
||||
return builder.clean();
|
||||
}).then(function() {
|
||||
shell.rm('-rf', path.join(ROOT, 'out'));
|
||||
});
|
||||
};
|
||||
|
||||
/*
|
||||
* Builds the project with the specifed options
|
||||
* Returns a promise.
|
||||
*/
|
||||
module.exports.run = function(options) {
|
||||
var opts = parseOpts(options);
|
||||
|
||||
var builder = builders[opts.buildMethod];
|
||||
return builder.prepEnv()
|
||||
.then(function() {
|
||||
return builder.build(opts.buildType);
|
||||
}).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');
|
||||
shell.mkdir('-p', outputDir);
|
||||
for (var i=0; i < apkFiles.length; ++i) {
|
||||
shell.cp('-f', apkFiles[i], path.join(outputDir, path.basename(apkFiles[i])));
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/*
|
||||
* Gets the path to the apk file, if not such file exists then
|
||||
* the script will error out. (should we error or just return undefined?)
|
||||
* This is called by the run script to install the apk to the device
|
||||
*/
|
||||
module.exports.get_apk = function(build_type) {
|
||||
var outputDir = path.join(ROOT, 'out');
|
||||
var candidates = find_files(outputDir, function() { return true; });
|
||||
if (candidates.length === 0) {
|
||||
console.error('ERROR : No .apk found in ' + outputDir + ' directory');
|
||||
process.exit(2);
|
||||
}
|
||||
// TODO: Use build_type here.
|
||||
console.log('Using apk: ' + candidates[0]);
|
||||
return candidates[0];
|
||||
};
|
||||
|
||||
module.exports.help = function() {
|
||||
console.log('Usage: ' + path.relative(process.cwd(), path.join(ROOT, 'cordova', 'build')) + ' [build_type]');
|
||||
console.log('Build Types : ');
|
||||
console.log(' \'--debug\': Default build, will build project in using ant debug');
|
||||
console.log(' \'--release\': will build project using ant release');
|
||||
console.log(' \'--debug\': Default build, will build project in debug mode');
|
||||
console.log(' \'--release\': will build project for release');
|
||||
console.log(' \'--ant\': Default build, will build project with ant');
|
||||
console.log(' \'--gradle\': will build project with gradle');
|
||||
console.log(' \'--nobuild\': will skip build process (can be used with run command)');
|
||||
process.exit(0);
|
||||
};
|
||||
|
39
bin/templates/cordova/lib/clean.js
vendored
39
bin/templates/cordova/lib/clean.js
vendored
@ -1,39 +0,0 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
/*
|
||||
Licensed to the Apache Software Foundation (ASF) under one
|
||||
or more contributor license agreements. See the NOTICE file
|
||||
distributed with this work for additional information
|
||||
regarding copyright ownership. The ASF licenses this file
|
||||
to you under the Apache License, Version 2.0 (the
|
||||
"License"); you may not use this file except in compliance
|
||||
with the License. You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing,
|
||||
software distributed under the License is distributed on an
|
||||
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
KIND, either express or implied. See the License for the
|
||||
specific language governing permissions and limitations
|
||||
under the License.
|
||||
*/
|
||||
|
||||
var build = require('./build'),
|
||||
spawn = require('./spawn'),
|
||||
path = require('path');
|
||||
|
||||
/*
|
||||
* Cleans the project using ant
|
||||
* Returns a promise.
|
||||
*/
|
||||
module.exports.run = function() {
|
||||
var args = build.getAntArgs('clean');
|
||||
return spawn('ant', args);
|
||||
}
|
||||
|
||||
module.exports.help = function() {
|
||||
console.log('Usage: ' + path.relative(process.cwd(), process.argv[1]));
|
||||
console.log('Cleans the project directory.');
|
||||
process.exit(0);
|
||||
}
|
14
bin/templates/project/gitignore
Normal file
14
bin/templates/project/gitignore
Normal file
@ -0,0 +1,14 @@
|
||||
# Non-project-specific build files:
|
||||
build.xml
|
||||
local.properties
|
||||
/gradlew
|
||||
/gradlew.bat
|
||||
/gradle
|
||||
# Ant builds
|
||||
ant-built
|
||||
ant-gen
|
||||
# Eclipse builds
|
||||
gen
|
||||
out
|
||||
# Gradle builds
|
||||
/build
|
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
|
@ -255,5 +255,15 @@
|
||||
<category android:name="android.intent.category.SAMPLE_CODE" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
<activity
|
||||
android:windowSoftInputMode="adjustPan"
|
||||
android:label="@string/app_name"
|
||||
android:configChanges="orientation|keyboardHidden"
|
||||
android:name="org.apache.cordova.test.SabotagedActivity" >
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
<category android:name="android.intent.category.SAMPLE_CODE" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
</application>
|
||||
</manifest>
|
||||
|
@ -25,6 +25,7 @@ public class IntentUriOverrideTest extends ActivityInstrumentationTestCase2<Sabo
|
||||
private String BAD_URL = "file:///sdcard/download/wl-exploit.htm";
|
||||
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
public IntentUriOverrideTest()
|
||||
{
|
||||
super("org.apache.cordova.test",SabotagedActivity.class);
|
||||
|
Loading…
Reference in New Issue
Block a user