From 8aa813b8625ec771e05977ca366220fb8f2388e2 Mon Sep 17 00:00:00 2001 From: Ian Clelland Date: Mon, 18 Aug 2014 09:40:33 -0400 Subject: [PATCH 1/4] CB-3445: Add an initial set of Gradle build scripts These scripts will build an android project, in debug and release mode. They also support additional library projects, such as Crosswalk, being added to libraries.gradle (and settings.gradle). A flag can be set in libraries.gradle to enable multi-architecture builds. --- bin/lib/create.js | 12 +- bin/templates/project/build.gradle | 81 +++++++++ .../gradle/wrapper/gradle-wrapper.properties | 6 + bin/templates/project/gradlew | 164 ++++++++++++++++++ bin/templates/project/gradlew.bat | 90 ++++++++++ bin/templates/project/libraries.gradle | 7 + bin/templates/project/settings.gradle | 1 + framework/build.gradle | 33 ++++ 8 files changed, 393 insertions(+), 1 deletion(-) create mode 100644 bin/templates/project/build.gradle create mode 100644 bin/templates/project/gradle/wrapper/gradle-wrapper.properties create mode 100755 bin/templates/project/gradlew create mode 100644 bin/templates/project/gradlew.bat create mode 100644 bin/templates/project/libraries.gradle create mode 100644 bin/templates/project/settings.gradle create mode 100644 framework/build.gradle diff --git a/bin/lib/create.js b/bin/lib/create.js index 78345817..ad3c21c0 100755 --- a/bin/lib/create.js +++ b/bin/lib/create.js @@ -71,6 +71,7 @@ function copyJsAndLibrary(projectPath, shared, projectName) { shell.mkdir('-p', nestedCordovaLibPath); shell.cp('-f', path.join(ROOT, 'framework', 'AndroidManifest.xml'), nestedCordovaLibPath); shell.cp('-f', path.join(ROOT, 'framework', 'project.properties'), nestedCordovaLibPath); + shell.cp('-f', path.join(ROOT, 'framework', 'build.gradle'), nestedCordovaLibPath); shell.cp('-r', path.join(ROOT, 'framework', 'src'), nestedCordovaLibPath); // Create an eclipse project file and set the name of it to something unique. // Without this, you can't import multiple CordovaLib projects into the same workspace. @@ -89,7 +90,9 @@ function runAndroidUpdate(projectPath, target_api, shared) { function copyAntRules(projectPath) { var srcDir = path.join(ROOT, 'bin', 'templates', 'project'); - shell.cp('-f', path.join(srcDir, 'custom_rules.xml'), projectPath); + if (fs.existsSync(path.join(srcDir, 'custom_rules.xml'))) { + shell.cp('-f', path.join(srcDir, 'custom_rules.xml'), projectPath); + } } function copyScripts(projectPath) { @@ -213,6 +216,13 @@ exports.createProject = function(project_path, package_name, project_name, proje 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('-f', path.join(project_template_dir, 'build.gradle'), project_path); + shell.cp('-f', path.join(project_template_dir, 'libraries.gradle'), project_path); + shell.cp('-f', path.join(project_template_dir, 'settings.gradle'), project_path); + shell.cp('-f', path.join(project_template_dir, 'gradlew'), project_path); + shell.cp('-f', path.join(project_template_dir, 'gradlew.bat'), project_path); + shell.cp('-r', path.join(project_template_dir, 'gradle'), project_path); + // Manually create directories that would be empty within the template (since git doesn't track directories). shell.mkdir(path.join(project_path, 'libs')); diff --git a/bin/templates/project/build.gradle b/bin/templates/project/build.gradle new file mode 100644 index 00000000..4b34e48f --- /dev/null +++ b/bin/templates/project/build.gradle @@ -0,0 +1,81 @@ +import java.util.regex.Pattern + +apply plugin: 'android' + +buildscript { + repositories { + mavenCentral() + } + + dependencies { + classpath 'com.android.tools.build:gradle:0.10.+' + } +} + +ext.multiarch=false + +dependencies { + compile project(':CordovaLib') +} +apply from: 'libraries.gradle' + +android { + sourceSets { + main { + manifest.srcFile 'AndroidManifest.xml' + java.srcDirs = ['src'] + resources.srcDirs = ['src'] + aidl.srcDirs = ['src'] + renderscript.srcDirs = ['src'] + res.srcDirs = ['res'] + assets.srcDirs = ['assets'] + } + } + + defaultConfig { + versionCode Integer.parseInt("" + getVersionCodeFromManifest() + "0") + } + + compileSdkVersion 19 + buildToolsVersion "19.0.0" + + if (multiarch) { + productFlavors { + armv7 { + versionCode defaultConfig.versionCode + 2 + ndk { + abiFilters "armeabi-v7a", "" + } + } + x86 { + versionCode defaultConfig.versionCode + 4 + ndk { + abiFilters "x86", "" + } + } + all { + ndk { + abiFilters "all", "" + } + } + } + } + + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_7 + targetCompatibility JavaVersion.VERSION_1_7 + } + +} + +task wrapper(type: Wrapper) { + gradleVersion = '1.12' +} + +def getVersionCodeFromManifest() { + def manifestFile = file(android.sourceSets.main.manifest.srcFile) + def pattern = Pattern.compile("versionCode=\"(\\d+)\"") + def matcher = pattern.matcher(manifestFile.getText()) + matcher.find() + return Integer.parseInt(matcher.group(1)) +} diff --git a/bin/templates/project/gradle/wrapper/gradle-wrapper.properties b/bin/templates/project/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 00000000..497dc9bf --- /dev/null +++ b/bin/templates/project/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,6 @@ +#Fri Jun 13 09:52:43 EDT 2014 +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-1.12-bin.zip diff --git a/bin/templates/project/gradlew b/bin/templates/project/gradlew new file mode 100755 index 00000000..91a7e269 --- /dev/null +++ b/bin/templates/project/gradlew @@ -0,0 +1,164 @@ +#!/usr/bin/env bash + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS="" + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn ( ) { + echo "$*" +} + +die ( ) { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; +esac + +# For Cygwin, ensure paths are in UNIX format before anything is touched. +if $cygwin ; then + [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"` +fi + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >&- +APP_HOME="`pwd -P`" +cd "$SAVED" >&- + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin, switch paths to Windows format before running java +if $cygwin ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=$((i+1)) + done + case $i in + (0) set -- ;; + (1) set -- "$args0" ;; + (2) set -- "$args0" "$args1" ;; + (3) set -- "$args0" "$args1" "$args2" ;; + (4) set -- "$args0" "$args1" "$args2" "$args3" ;; + (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules +function splitJvmOpts() { + JVM_OPTS=("$@") +} +eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS +JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" + +exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" diff --git a/bin/templates/project/gradlew.bat b/bin/templates/project/gradlew.bat new file mode 100644 index 00000000..aec99730 --- /dev/null +++ b/bin/templates/project/gradlew.bat @@ -0,0 +1,90 @@ +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS= + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto init + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto init + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:init +@rem Get command-line arguments, handling Windowz variants + +if not "%OS%" == "Windows_NT" goto win9xME_args +if "%@eval[2+2]" == "4" goto 4NT_args + +:win9xME_args +@rem Slurp the command line arguments. +set CMD_LINE_ARGS= +set _SKIP=2 + +:win9xME_args_slurp +if "x%~1" == "x" goto execute + +set CMD_LINE_ARGS=%* +goto execute + +:4NT_args +@rem Get arguments from the 4NT Shell from JP Software +set CMD_LINE_ARGS=%$ + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/bin/templates/project/libraries.gradle b/bin/templates/project/libraries.gradle new file mode 100644 index 00000000..f4975435 --- /dev/null +++ b/bin/templates/project/libraries.gradle @@ -0,0 +1,7 @@ +dependencies { +// This file contains no plugins by default. +// To add a third-party library project, add a line to this file like +// compile project(':library_dir_name') +} +// If multiple ndk architectures are provided, uncomment this line +//ext.multiarch=true diff --git a/bin/templates/project/settings.gradle b/bin/templates/project/settings.gradle new file mode 100644 index 00000000..b938ccc5 --- /dev/null +++ b/bin/templates/project/settings.gradle @@ -0,0 +1 @@ +include ':CordovaLib', ':' diff --git a/framework/build.gradle b/framework/build.gradle new file mode 100644 index 00000000..82c1ac29 --- /dev/null +++ b/framework/build.gradle @@ -0,0 +1,33 @@ +buildscript { + repositories { + mavenCentral() + } + + dependencies { + classpath 'com.android.tools.build:gradle:0.10.+' + } +} + +apply plugin: 'android-library' + +android { + compileSdkVersion 19 + buildToolsVersion "19.0.0" + + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_7 + targetCompatibility JavaVersion.VERSION_1_7 + } + + sourceSets { + main { + manifest.srcFile 'AndroidManifest.xml' + java.srcDirs = ['src'] + resources.srcDirs = ['src'] + aidl.srcDirs = ['src'] + renderscript.srcDirs = ['src'] + res.srcDirs = ['res'] + assets.srcDirs = ['assets'] + } + } +} From 7d6ac8703378cfd58b175de8483a2bf26242f799 Mon Sep 17 00:00:00 2001 From: Ian Clelland Date: Mon, 18 Aug 2014 09:44:00 -0400 Subject: [PATCH 2/4] CB-3445: Add option to build and install with gradle This gives build/cordova two new command-line arguments: --ant and --gradle, and will select the build type from those. As a fallback for the Cordova CLI, the environment variable ANDROID_BUILD can also be used, set to either "ant" or "gradle". The default is currently "ant", but it is intended for this to change in the future. --- bin/templates/cordova/build | 10 +- bin/templates/cordova/lib/build.js | 231 ++++++++++++++++++++++------- 2 files changed, 184 insertions(+), 57 deletions(-) diff --git a/bin/templates/cordova/build b/bin/templates/cordova/build index 2d589018..367bf7d2 100755 --- a/bin/templates/cordova/build +++ b/bin/templates/cordova/build @@ -24,12 +24,16 @@ 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().done(function() { - return build.run(args[2]); + return build.run.call(build.run, args.slice(2)); }, function(err) { console.error(err); process.exit(2); diff --git a/bin/templates/cordova/lib/build.js b/bin/templates/cordova/lib/build.js index 5134f412..3d0ae872 100644 --- a/bin/templates/cordova/lib/build.js +++ b/bin/templates/cordova/lib/build.js @@ -27,39 +27,164 @@ var shell = require('shelljs'), ROOT = path.join(__dirname, '..', '..'); var check_reqs = require('./check_reqs'); +// Globals +var build_type, + build_method; + +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; + }).map(function(p) { return p.p; }); + return candidates; + } else { + 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')); } -module.exports.getAntArgs = 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'); + +module.exports.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'); + } + 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. + */ + build: function(build_type) { + var builder = this; + var args = builder.getArgs(build_type == "debug" ? 'debug' : 'release'); + return Q().then(function() { + return spawn('ant', args); + }).then(function() { + return builder.getOutputFiles(); + }); + }, + + // 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 args = [cmd, '-b', path.join(ROOT, 'build.gradle')]; + return args; + }, + + /* + * 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); + }); + }, + + // 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; + } } - return args; }; /* - * Builds the project with ant. + * Builds the project with the specifed options * 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.'); +module.exports.run = function(options) { + + // Backwards-compatibility: Allow a single string argument + if (typeof options == "string") options = [options]; + + // 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': + if (build_type) { + return Q.reject('Multiple build types (' + build_type + ' and ' + option + ') specified.'); + } + build_type = option; + break; + case 'ant': + case 'gradle': + if (build_method) { + return Q.reject('Multiple build methods (' + build_method + ' and ' + option + ') specified.'); + } + build_method = option; + break; + case 'nobuild' : + console.log('Skipping build...'); + return Q(); + default : + return Q.reject('Build option \'' + options[i] + '\' not recognized.'); + } + } else { + return Q.reject('Build option \'' + options[i] + '\' not recognized.'); + } } + // Defaults + build_type = build_type || "debug"; + build_method = build_method || process.env.ANDROID_BUILD || "ant"; + + // Get the builder + var builder = module.exports.builders[build_method]; + // Without our custom_rules.xml, we need to clean before building. var ret; if (!hasCustomRules()) { @@ -68,50 +193,48 @@ module.exports.run = function(build_type) { } else { ret = check_reqs.check_ant(); } + + // Return a promise for the actual build return ret.then(function() { - return spawn('ant', args); + return builder.build.call(builder, build_type); + }).then(function(apkFiles) { + var outputDir = path.join(ROOT, 'out'); + try { + fs.mkdirSync(outputDir); + } catch (e) { + if (e.code != "EEXIST") { + throw e; + } + } + 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() { - var binDir = ''; - if(!hasCustomRules()) { - binDir = path.join(ROOT, 'bin'); - } else { - binDir = path.join(ROOT, 'ant-build'); - } - 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); - 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; - } else { - console.error('ERROR : unable to find project ' + binDir + ' directory, could not locate .apk'); +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); } -} + 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); -} +}; From fd6a1e5ed00baba902279dbc52cb9639b0e78eb3 Mon Sep 17 00:00:00 2001 From: Ian Clelland Date: Fri, 15 Aug 2014 13:35:50 -0400 Subject: [PATCH 3/4] CB-3445: Add which to checked-in node_modules --- bin/node_modules/which/LICENSE | 23 ++++++ bin/node_modules/which/README.md | 5 ++ bin/node_modules/which/bin/which | 14 ++++ bin/node_modules/which/package.json | 31 +++++++++ bin/node_modules/which/which.js | 104 ++++++++++++++++++++++++++++ 5 files changed, 177 insertions(+) create mode 100644 bin/node_modules/which/LICENSE create mode 100644 bin/node_modules/which/README.md create mode 100755 bin/node_modules/which/bin/which create mode 100644 bin/node_modules/which/package.json create mode 100644 bin/node_modules/which/which.js diff --git a/bin/node_modules/which/LICENSE b/bin/node_modules/which/LICENSE new file mode 100644 index 00000000..05a40109 --- /dev/null +++ b/bin/node_modules/which/LICENSE @@ -0,0 +1,23 @@ +Copyright 2009, 2010, 2011 Isaac Z. Schlueter. +All rights reserved. + +Permission is hereby granted, free of charge, to any person +obtaining a copy of this software and associated documentation +files (the "Software"), to deal in the Software without +restriction, including without limitation the rights to use, +copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. diff --git a/bin/node_modules/which/README.md b/bin/node_modules/which/README.md new file mode 100644 index 00000000..ff1eb531 --- /dev/null +++ b/bin/node_modules/which/README.md @@ -0,0 +1,5 @@ +The "which" util from npm's guts. + +Finds the first instance of a specified executable in the PATH +environment variable. Does not cache the results, so `hash -r` is not +needed when the PATH changes. diff --git a/bin/node_modules/which/bin/which b/bin/node_modules/which/bin/which new file mode 100755 index 00000000..8432ce2f --- /dev/null +++ b/bin/node_modules/which/bin/which @@ -0,0 +1,14 @@ +#!/usr/bin/env node +var which = require("../") +if (process.argv.length < 3) { + console.error("Usage: which ") + process.exit(1) +} + +which(process.argv[2], function (er, thing) { + if (er) { + console.error(er.message) + process.exit(er.errno || 127) + } + console.log(thing) +}) diff --git a/bin/node_modules/which/package.json b/bin/node_modules/which/package.json new file mode 100644 index 00000000..6c5ccb34 --- /dev/null +++ b/bin/node_modules/which/package.json @@ -0,0 +1,31 @@ +{ + "author": { + "name": "Isaac Z. Schlueter", + "email": "i@izs.me", + "url": "http://blog.izs.me" + }, + "name": "which", + "description": "Like which(1) unix command. Find the first instance of an executable in the PATH.", + "version": "1.0.5", + "repository": { + "type": "git", + "url": "git://github.com/isaacs/node-which.git" + }, + "main": "which.js", + "bin": { + "which": "./bin/which" + }, + "engines": { + "node": "*" + }, + "dependencies": {}, + "devDependencies": {}, + "readme": "The \"which\" util from npm's guts.\n\nFinds the first instance of a specified executable in the PATH\nenvironment variable. Does not cache the results, so `hash -r` is not\nneeded when the PATH changes.\n", + "readmeFilename": "README.md", + "bugs": { + "url": "https://github.com/isaacs/node-which/issues" + }, + "homepage": "https://github.com/isaacs/node-which", + "_id": "which@1.0.5", + "_from": "which@" +} diff --git a/bin/node_modules/which/which.js b/bin/node_modules/which/which.js new file mode 100644 index 00000000..db7e8f74 --- /dev/null +++ b/bin/node_modules/which/which.js @@ -0,0 +1,104 @@ +module.exports = which +which.sync = whichSync + +var path = require("path") + , fs + , COLON = process.platform === "win32" ? ";" : ":" + , isExe + +try { + fs = require("graceful-fs") +} catch (ex) { + fs = require("fs") +} + +if (process.platform == "win32") { + // On windows, there is no good way to check that a file is executable + isExe = function isExe () { return true } +} else { + isExe = function isExe (mod, uid, gid) { + //console.error(mod, uid, gid); + //console.error("isExe?", (mod & 0111).toString(8)) + var ret = (mod & 0001) + || (mod & 0010) && process.getgid && gid === process.getgid() + || (mod & 0100) && process.getuid && uid === process.getuid() + //console.error("isExe?", ret) + return ret + } +} + + + +function which (cmd, cb) { + if (isAbsolute(cmd)) return cb(null, cmd) + var pathEnv = (process.env.PATH || "").split(COLON) + , pathExt = [""] + if (process.platform === "win32") { + pathEnv.push(process.cwd()) + pathExt = (process.env.PATHEXT || ".EXE").split(COLON) + if (cmd.indexOf(".") !== -1) pathExt.unshift("") + } + //console.error("pathEnv", pathEnv) + ;(function F (i, l) { + if (i === l) return cb(new Error("not found: "+cmd)) + var p = path.resolve(pathEnv[i], cmd) + ;(function E (ii, ll) { + if (ii === ll) return F(i + 1, l) + var ext = pathExt[ii] + //console.error(p + ext) + fs.stat(p + ext, function (er, stat) { + if (!er && + stat && + stat.isFile() && + isExe(stat.mode, stat.uid, stat.gid)) { + //console.error("yes, exe!", p + ext) + return cb(null, p + ext) + } + return E(ii + 1, ll) + }) + })(0, pathExt.length) + })(0, pathEnv.length) +} + +function whichSync (cmd) { + if (isAbsolute(cmd)) return cmd + var pathEnv = (process.env.PATH || "").split(COLON) + , pathExt = [""] + if (process.platform === "win32") { + pathEnv.push(process.cwd()) + pathExt = (process.env.PATHEXT || ".EXE").split(COLON) + if (cmd.indexOf(".") !== -1) pathExt.unshift("") + } + for (var i = 0, l = pathEnv.length; i < l; i ++) { + var p = path.join(pathEnv[i], cmd) + for (var j = 0, ll = pathExt.length; j < ll; j ++) { + var cur = p + pathExt[j] + var stat + try { stat = fs.statSync(cur) } catch (ex) {} + if (stat && + stat.isFile() && + isExe(stat.mode, stat.uid, stat.gid)) return cur + } + } + throw new Error("not found: "+cmd) +} + +var isAbsolute = process.platform === "win32" ? absWin : absUnix + +function absWin (p) { + if (absUnix(p)) return true + // pull off the device/UNC bit from a windows path. + // from node's lib/path.js + var splitDeviceRe = + /^([a-zA-Z]:|[\\\/]{2}[^\\\/]+[\\\/][^\\\/]+)?([\\\/])?/ + , result = splitDeviceRe.exec(p) + , device = result[1] || '' + , isUnc = device && device.charAt(1) !== ':' + , isAbsolute = !!result[2] || isUnc // UNC paths are always absolute + + return isAbsolute +} + +function absUnix (p) { + return p.charAt(0) === "/" || p === "" +} From a91bd095b0ebe1e4ed85c69248f376df7cbc626a Mon Sep 17 00:00:00 2001 From: Ian Clelland Date: Thu, 19 Jun 2014 16:12:40 -0400 Subject: [PATCH 4/4] CB-3445: android: Copy Gradle wrapper from Android SDK rather than bundling a JAR --- bin/lib/check_reqs.js | 18 ++ bin/lib/create.js | 16 +- .../gradle/wrapper/gradle-wrapper.properties | 6 - bin/templates/project/gradlew | 164 ------------------ bin/templates/project/gradlew.bat | 90 ---------- 5 files changed, 31 insertions(+), 263 deletions(-) delete mode 100644 bin/templates/project/gradle/wrapper/gradle-wrapper.properties delete mode 100755 bin/templates/project/gradlew delete mode 100644 bin/templates/project/gradlew.bat diff --git a/bin/lib/check_reqs.js b/bin/lib/check_reqs.js index db454749..f6160354 100644 --- a/bin/lib/check_reqs.js +++ b/bin/lib/check_reqs.js @@ -24,6 +24,7 @@ var shelljs = require('shelljs'), Q = require('q'), path = require('path'), fs = require('fs'), + which = require('which'), ROOT = path.join(__dirname, '..', '..'); var isWindows = process.platform == 'win32'; @@ -55,6 +56,23 @@ module.exports.get_target = function() { } } +// 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.'); diff --git a/bin/lib/create.js b/bin/lib/create.js index ad3c21c0..312881cd 100755 --- a/bin/lib/create.js +++ b/bin/lib/create.js @@ -109,6 +109,13 @@ function copyScripts(projectPath) { shell.cp(path.join(ROOT, 'bin', 'lib', 'android_sdk_version.js'), path.join(projectPath, 'cordova', 'lib', 'android_sdk_version.js')); } +function copyGradleWrapper(sdkPath, projectPath) { + var wrapperDir = path.join(sdkPath, 'tools', 'templates','gradle','wrapper'); + shell.cp(path.join(wrapperDir, 'gradlew'), projectPath); + shell.cp(path.join(wrapperDir, 'gradlew.bat'), projectPath); + shell.cp('-r', path.join(wrapperDir, 'gradle'), projectPath); +} + /** * Test whether a package name is acceptable for use as an android project. * Returns a promise, fulfilled if the package name is acceptable; rejected @@ -219,9 +226,12 @@ exports.createProject = function(project_path, package_name, project_name, proje shell.cp('-f', path.join(project_template_dir, 'build.gradle'), project_path); shell.cp('-f', path.join(project_template_dir, 'libraries.gradle'), project_path); shell.cp('-f', path.join(project_template_dir, 'settings.gradle'), project_path); - shell.cp('-f', path.join(project_template_dir, 'gradlew'), project_path); - shell.cp('-f', path.join(project_template_dir, 'gradlew.bat'), project_path); - shell.cp('-r', path.join(project_template_dir, 'gradle'), project_path); + check_reqs.sdk_dir().then(function(dir) { + console.log("Copying Gradle wrapper from " + dir); + copyGradleWrapper(dir, project_path); + }).catch(function(err) { + console.log("Cannot find Android SDK. Not installing Gradle wrapper."); + }); // Manually create directories that would be empty within the template (since git doesn't track directories). shell.mkdir(path.join(project_path, 'libs')); diff --git a/bin/templates/project/gradle/wrapper/gradle-wrapper.properties b/bin/templates/project/gradle/wrapper/gradle-wrapper.properties deleted file mode 100644 index 497dc9bf..00000000 --- a/bin/templates/project/gradle/wrapper/gradle-wrapper.properties +++ /dev/null @@ -1,6 +0,0 @@ -#Fri Jun 13 09:52:43 EDT 2014 -distributionBase=GRADLE_USER_HOME -distributionPath=wrapper/dists -zipStoreBase=GRADLE_USER_HOME -zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-1.12-bin.zip diff --git a/bin/templates/project/gradlew b/bin/templates/project/gradlew deleted file mode 100755 index 91a7e269..00000000 --- a/bin/templates/project/gradlew +++ /dev/null @@ -1,164 +0,0 @@ -#!/usr/bin/env bash - -############################################################################## -## -## Gradle start up script for UN*X -## -############################################################################## - -# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -DEFAULT_JVM_OPTS="" - -APP_NAME="Gradle" -APP_BASE_NAME=`basename "$0"` - -# Use the maximum available, or set MAX_FD != -1 to use that value. -MAX_FD="maximum" - -warn ( ) { - echo "$*" -} - -die ( ) { - echo - echo "$*" - echo - exit 1 -} - -# OS specific support (must be 'true' or 'false'). -cygwin=false -msys=false -darwin=false -case "`uname`" in - CYGWIN* ) - cygwin=true - ;; - Darwin* ) - darwin=true - ;; - MINGW* ) - msys=true - ;; -esac - -# For Cygwin, ensure paths are in UNIX format before anything is touched. -if $cygwin ; then - [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"` -fi - -# Attempt to set APP_HOME -# Resolve links: $0 may be a link -PRG="$0" -# Need this for relative symlinks. -while [ -h "$PRG" ] ; do - ls=`ls -ld "$PRG"` - link=`expr "$ls" : '.*-> \(.*\)$'` - if expr "$link" : '/.*' > /dev/null; then - PRG="$link" - else - PRG=`dirname "$PRG"`"/$link" - fi -done -SAVED="`pwd`" -cd "`dirname \"$PRG\"`/" >&- -APP_HOME="`pwd -P`" -cd "$SAVED" >&- - -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar - -# Determine the Java command to use to start the JVM. -if [ -n "$JAVA_HOME" ] ; then - if [ -x "$JAVA_HOME/jre/sh/java" ] ; then - # IBM's JDK on AIX uses strange locations for the executables - JAVACMD="$JAVA_HOME/jre/sh/java" - else - JAVACMD="$JAVA_HOME/bin/java" - fi - if [ ! -x "$JAVACMD" ] ; then - die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME - -Please set the JAVA_HOME variable in your environment to match the -location of your Java installation." - fi -else - JAVACMD="java" - which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. - -Please set the JAVA_HOME variable in your environment to match the -location of your Java installation." -fi - -# Increase the maximum file descriptors if we can. -if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then - MAX_FD_LIMIT=`ulimit -H -n` - if [ $? -eq 0 ] ; then - if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then - MAX_FD="$MAX_FD_LIMIT" - fi - ulimit -n $MAX_FD - if [ $? -ne 0 ] ; then - warn "Could not set maximum file descriptor limit: $MAX_FD" - fi - else - warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" - fi -fi - -# For Darwin, add options to specify how the application appears in the dock -if $darwin; then - GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" -fi - -# For Cygwin, switch paths to Windows format before running java -if $cygwin ; then - APP_HOME=`cygpath --path --mixed "$APP_HOME"` - CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` - - # We build the pattern for arguments to be converted via cygpath - ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` - SEP="" - for dir in $ROOTDIRSRAW ; do - ROOTDIRS="$ROOTDIRS$SEP$dir" - SEP="|" - done - OURCYGPATTERN="(^($ROOTDIRS))" - # Add a user-defined pattern to the cygpath arguments - if [ "$GRADLE_CYGPATTERN" != "" ] ; then - OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" - fi - # Now convert the arguments - kludge to limit ourselves to /bin/sh - i=0 - for arg in "$@" ; do - CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` - CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option - - if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition - eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` - else - eval `echo args$i`="\"$arg\"" - fi - i=$((i+1)) - done - case $i in - (0) set -- ;; - (1) set -- "$args0" ;; - (2) set -- "$args0" "$args1" ;; - (3) set -- "$args0" "$args1" "$args2" ;; - (4) set -- "$args0" "$args1" "$args2" "$args3" ;; - (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; - (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; - (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; - (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; - (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; - esac -fi - -# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules -function splitJvmOpts() { - JVM_OPTS=("$@") -} -eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS -JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" - -exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" diff --git a/bin/templates/project/gradlew.bat b/bin/templates/project/gradlew.bat deleted file mode 100644 index aec99730..00000000 --- a/bin/templates/project/gradlew.bat +++ /dev/null @@ -1,90 +0,0 @@ -@if "%DEBUG%" == "" @echo off -@rem ########################################################################## -@rem -@rem Gradle startup script for Windows -@rem -@rem ########################################################################## - -@rem Set local scope for the variables with windows NT shell -if "%OS%"=="Windows_NT" setlocal - -@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -set DEFAULT_JVM_OPTS= - -set DIRNAME=%~dp0 -if "%DIRNAME%" == "" set DIRNAME=. -set APP_BASE_NAME=%~n0 -set APP_HOME=%DIRNAME% - -@rem Find java.exe -if defined JAVA_HOME goto findJavaFromJavaHome - -set JAVA_EXE=java.exe -%JAVA_EXE% -version >NUL 2>&1 -if "%ERRORLEVEL%" == "0" goto init - -echo. -echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. - -goto fail - -:findJavaFromJavaHome -set JAVA_HOME=%JAVA_HOME:"=% -set JAVA_EXE=%JAVA_HOME%/bin/java.exe - -if exist "%JAVA_EXE%" goto init - -echo. -echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. - -goto fail - -:init -@rem Get command-line arguments, handling Windowz variants - -if not "%OS%" == "Windows_NT" goto win9xME_args -if "%@eval[2+2]" == "4" goto 4NT_args - -:win9xME_args -@rem Slurp the command line arguments. -set CMD_LINE_ARGS= -set _SKIP=2 - -:win9xME_args_slurp -if "x%~1" == "x" goto execute - -set CMD_LINE_ARGS=%* -goto execute - -:4NT_args -@rem Get arguments from the 4NT Shell from JP Software -set CMD_LINE_ARGS=%$ - -:execute -@rem Setup the command line - -set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar - -@rem Execute Gradle -"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% - -:end -@rem End local scope for the variables with windows NT shell -if "%ERRORLEVEL%"=="0" goto mainEnd - -:fail -rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of -rem the _cmd.exe /c_ return code! -if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 -exit /b 1 - -:mainEnd -if "%OS%"=="Windows_NT" endlocal - -:omega