diff --git a/bin/lib/check_reqs.js b/bin/lib/check_reqs.js
index 8e57a119..40f3ade3 100644
--- a/bin/lib/check_reqs.js
+++ b/bin/lib/check_reqs.js
@@ -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()]);
}
diff --git a/bin/lib/create.js b/bin/lib/create.js
index 485c60eb..f20c6a01 100755
--- a/bin/lib/create.js
+++ b/bin/lib/create.js
@@ -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.');
});
};
diff --git a/bin/node_modules/which/package.json b/bin/node_modules/which/package.json
index c71cabb5..6c5ccb34 100644
--- a/bin/node_modules/which/package.json
+++ b/bin/node_modules/which/package.json
@@ -27,5 +27,5 @@
},
"homepage": "https://github.com/isaacs/node-which",
"_id": "which@1.0.5",
- "_from": "which@^1.0.5"
+ "_from": "which@"
}
diff --git a/bin/templates/cordova/build b/bin/templates/cordova/build
index a38f3b63..3c3aee4e 100755
--- a/bin/templates/cordova/build
+++ b/bin/templates/cordova/build
@@ -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);
});
diff --git a/bin/templates/cordova/clean b/bin/templates/cordova/clean
index 4e0808bf..d9a7d490 100755
--- a/bin/templates/cordova/clean
+++ b/bin/templates/cordova/clean
@@ -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);
});
}
diff --git a/bin/templates/cordova/lib/build.js b/bin/templates/cordova/lib/build.js
index 5f100e21..7d8828db 100644
--- a/bin/templates/cordova/lib/build.js
+++ b/bin/templates/cordova/lib/build.js
@@ -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 = / 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);
};
diff --git a/bin/templates/cordova/lib/clean.js b/bin/templates/cordova/lib/clean.js
deleted file mode 100644
index 0a2e0ce0..00000000
--- a/bin/templates/cordova/lib/clean.js
+++ /dev/null
@@ -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);
-}
diff --git a/bin/templates/project/gitignore b/bin/templates/project/gitignore
new file mode 100644
index 00000000..a1c8ff71
--- /dev/null
+++ b/bin/templates/project/gitignore
@@ -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
diff --git a/bin/templates/project/project.properties b/bin/templates/project/project.properties
new file mode 100644
index 00000000..ddd3a060
--- /dev/null
+++ b/bin/templates/project/project.properties
@@ -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
diff --git a/test/AndroidManifest.xml b/test/AndroidManifest.xml
index 6dec1aec..e058a420 100755
--- a/test/AndroidManifest.xml
+++ b/test/AndroidManifest.xml
@@ -255,5 +255,15 @@
+
+
+
+
+
+
diff --git a/test/src/org/apache/cordova/test/junit/IntentUriOverrideTest.java b/test/src/org/apache/cordova/test/junit/IntentUriOverrideTest.java
index aea06afe..381e0edd 100644
--- a/test/src/org/apache/cordova/test/junit/IntentUriOverrideTest.java
+++ b/test/src/org/apache/cordova/test/junit/IntentUriOverrideTest.java
@@ -25,6 +25,7 @@ public class IntentUriOverrideTest extends ActivityInstrumentationTestCase2