Inside build.gradle for Android project
task runAndroidApplication(type: Exec, dependsOn: ':installDebug') {
//TODO update Activity name below or find a way to get it from AndroidManifest.xml
if (System.properties['os.name'].toLowerCase().contains('windows')) {
// windows
commandLine 'cmd', '/c', 'adb', 'shell', 'am', 'start', '-n', "com.example.androidapptorun.MainActivity"
} else {
// linux
commandLine 'adb', 'shell', 'am', 'start', '-n', "com.example.androidapptorun.MainActivity"
}
}
How to get value for main Activity from AndroidManifest for the default Activity?
(Also if there are several activities, making logic to select one would make it long,
while processing is to be inside Android tools)
Is there better way from android plugin then parsing AndroidManifest.xml ?
UPDATE: https://github.com/novoda/gradle-android-command-plugin can possible suit some needs, but I needed no-argument version to make quick run in Eclipse via http://marketplace.eclipse.org/content/gradle
You can use XmlSlurper class.
example:
AndroidManifest.xml
<manifest package="com.example.your.app">
and retrieve it in gradle
def manifest = new XmlSlurper().parse(file("AndroidManifest.xml"))
// returns "com.exmaple.your.app"
manifest.#package.text()
I just wrote this for ADT 20 (L), Gradle 1.12 and com.android.tools.build:gradle:0.12.2.
It works with flavors, build types (don't forget myBuildType.initWith(existingBuildType)), and applicationIdSuffix.
Put the following at the end of android { ... }:
applicationVariants.all { variant ->
if (variant.install) {
tasks.create(name: "run${variant.name.capitalize()}", type: Exec, dependsOn: variant.install) {
description "Installs the APK for ${variant.description}, and then runs the main launcher activity."
def getMainActivity = { file ->
new XmlSlurper().parse(file).application.activity.find{ it.'intent-filter'.find{ filter ->
return filter.action .find{it.#name.text() == 'android.intent.action.MAIN'} \
&& filter.category.find{it.#name.text() == 'android.intent.category.LAUNCHER'}
}}.#name
}
doFirst {
def activityClass = getMainActivity(variant.processManifest.manifestOutputFile)
commandLine android.adbExe, 'shell', 'am', 'start', '-n', "${variant.packageName}/${activityClass}"
}
}
}
}
For libraries you need to change applicationVariants to libraryVariants: see manual.
Update: ADT 20, Gradle 2.1 and com.android.tools.build:gradle:0.13.1:
applicationVariants.all { variant ->
if (variant.install) {
tasks.create(name: "run${variant.name.capitalize()}", type: Exec, dependsOn: variant.install) {
description "Installs the APK for ${variant.description}, and then runs the main launcher activity."
def getMainActivity = { file ->
new XmlSlurper().parse(file).application.activity.find{ it.'intent-filter'.find{ filter ->
return filter.action .find{it.'#android:name'.text() == 'android.intent.action.MAIN' } \
&& filter.category.find{it.'#android:name'.text() == 'android.intent.category.LAUNCHER'}
}}.'#android:name'
}
doFirst {
def activityClass = getMainActivity(variant.outputs.processManifest.manifestOutputFile)
commandLine android.adbExe, 'shell', 'am', 'start', '-n', "${variant.applicationId}/${activityClass}"
// or without the XML hacking: commandLine android.adbExe, 'shell', 'monkey', '-p', variant.applicationId, '1'
}
}
}
}
Related
The cd command in my Gradle task is not working. Using it to go to another folder.
task assembleTask(overwrite: true, type:Exec) {
commandLine "gradle", "assembleDev"
doLast {
commandLine "cd tests"
commandLine "ls"
}
}
The Exec task only runs once. You are setting the commandLine property 3 times.
once in the configuration phase, before the task runs
twice after the task has run (this will have no effect)
If you want one task to run another, you might do
task assembleTask {
dependsOn assembleDev
doLast {
file('tests').listFiles().each { File f ->
println f.name
}
}
}
Or perhaps you want a GradleBuild task, not sure
If you want to run multiple execs in a single task you might want to use project.exec() instead of Exec task. Eg:
task assembleTask {
doLast {
exec {
commandLine 'foo'
}
exec {
commandLine 'bar'
}
exec {
commandLine 'baz'
}
}
}
I would like to merge Android gradle tasks into one and execute them in a specific order.
task.dependsOn did not work for me for a list of tasks, neither did the shouldRunAfter method.
task buildAll {
shouldRunAfter = ['clean', 'checkstyle', 'build']
}
What is the best way to run Android gradle tasks in specific order?
I ended up using doLast in connection with exec. In this example we define buildAll task which executes clean, checkstyle, build and lint in order - one after the other.
task buildAll {
doLast {
exec {
commandLine "${getRootDir()}/gradlew", "clean", "checkstyle", "build", "lint"
}
}
}
you can also use:
task buildAll {
doLast {
exec {
commandLine "${getRootDir()}/gradlew", "clean"
}
exec {
commandLine "${getRootDir()}/gradlew", "checkstyle"
}
exec {
commandLine "${getRootDir()}/gradlew", "build"
}
exec {
commandLine "${getRootDir()}/gradlew", "lint"
}
}
}
I'm trying to add a custom task to my Android project's build.gradle to copy the final APK and Proguard's mapping.txt into a different directory. My task depends on the assembleDevDebug task:
task publish(dependsOn: 'assembleDevDebug') << {
description 'Copies the final APK to the release directory.'
...
}
I can see how to do a file copy using the standard Copy task type, as per the docs:
task(copy, type: Copy) {
from(file('srcDir'))
into(buildDir)
}
but that assumes you know the name and location of the file you want to copy.
How can I find the exact name and location of the APK file which was built as part of the assembleDevDebug task? Is this available as a property? It feels as if I should be able to declare the files as inputs to my task, and declare them as outputs from the assemble task, but my Gradle-fu isn't strong enough.
I have some custom logic to inject the version number into the APK filename, so my publish task can't just assume the default name and location.
If you can get the variant object associated with devDebug you could query it with getOutputFile().
So if you wanted to publish all variants you'd something like this:
def publish = project.tasks.create("publishAll")
android.applicationVariants.all { variant ->
def task = project.tasks.create("publish${variant.name}Apk", Copy)
task.from(variant.outputFile)
task.into(buildDir)
task.dependsOn variant.assemble
publish.dependsOn task
}
Now you can call gradle publishAll and it'll publish all you variants.
One issue with the mapping file is that the Proguard task doesn't give you a getter to the file location, so you cannot currently query it. I'm hoping to get this fixed.
The following code is what I'm using to archive apk and proguard mapping into a zip file for each variant with 'release' build type:
def releasePath = file("${rootDir}/archive/${project.name}")
def releaseTask = tasks.create(name: 'release') {
group 'Build'
description "Assembles and archives all Release builds"
}
android.applicationVariants.all { variant ->
if (variant.buildType.name == 'release') {
def build = variant.name.capitalize()
def releaseBuildTask = tasks.create(name: "release${build}", type: Zip) {
group 'Build'
description "Assembles and archives apk and its proguard mapping for the $build build"
destinationDir releasePath
baseName variant.packageName
if (!variant.buildType.packageNameSuffix) {
appendix variant.buildType.name
}
if (variant.versionName) {
version "${variant.versionName}_${variant.versionCode}"
} else {
version "$variant.versionCode"
}
def archiveBaseName = archiveName.replaceFirst(/\.${extension}$/, '')
from(variant.outputFile.path) {
rename '.*', "${archiveBaseName}.apk"
}
if (variant.buildType.runProguard) {
from(variant.processResources.proguardOutputFile.parent) {
include 'mapping.txt'
rename '(.*)', "${archiveBaseName}-proguard_\$1"
}
}
}
releaseBuildTask.dependsOn variant.assemble
variant.productFlavors.each { flavor ->
def flavorName = flavor.name.capitalize()
def releaseFlavorTaskName = "release${flavorName}"
def releaseFlavorTask
if (tasks.findByName(releaseFlavorTaskName)) {
releaseFlavorTask = tasks[releaseFlavorTaskName]
} else {
releaseFlavorTask = tasks.create(name: releaseFlavorTaskName) {
group 'Build'
description "Assembles and archives all Release builds for flavor $flavorName"
}
releaseTask.dependsOn releaseFlavorTask
}
releaseFlavorTask.dependsOn releaseBuildTask
}
}
}
It creates tasks like the following:
release - Assembles and archives all Release builds
releaseFree - Assembles and archives all Release builds for flavor Free
releaseFreeRelease - Assembles and archives apk and its proguard mapping for the FreeRelease build
releasePaid - Assembles and archives all Release builds for flavor Paid
releasePaidRelease - Assembles and archives apk and its proguard mapping for the PaidRelease build
Content of archive/projectName/packageName-buildType-versionName_versionCode.zip would be:
packageName-buildType-versionName_versionCode.apk
packageName-buildType-versionName_versionCode-proguard_mapping.txt
I've got some good pointers here but also had a hard time to get done as I wanted. Here's my final version:
def archiveBuildTypes = ["distribute"];
def archiveFlavors = ["googleplay"]
android.applicationVariants.all { variant ->
if (variant.buildType.name in archiveBuildTypes) {
variant.productFlavors.each { flavor ->
if (flavor.name in archiveFlavors) {
def taskSuffix = variant.name.capitalize()
def version = "${android.defaultConfig.versionCode} (${android.defaultConfig.versionName})" // assumes that versionName was especified here instead of AndroidManifest.xml
def destination = "${rootDir}/${project.name}/archive/${version}"
def assembleTaskName = "assemble${taskSuffix}"
if (tasks.findByName(assembleTaskName)) {
def copyAPKTask = tasks.create(name: "archive${taskSuffix}", type:org.gradle.api.tasks.Copy) {
description "Archive/copy APK and mappings.txt to a versioned folder."
from ("${buildDir}") {
include "**/proguard/${flavor.name}/${variant.buildType.name}/mapping.txt"
include "**/apk/${variant.outputFile.name}"
}
into destination
eachFile { file->
file.path = file.name // so we have a "flat" copy
}
includeEmptyDirs = false
}
tasks[assembleTaskName].finalizedBy = [copyAPKTask]
}
}
}
}
}
This is how I copy mappings.txt whenever proguard runs
tasks.whenTaskAdded { task ->
if (task.name.startsWith("proguard")) {//copy proguard mappings
task << {
copy {
from buildDir.getPath() + "/proguard"
into '../proguard'
include '**/mapping.txt'
}
println "PROGUARD FILES COPIED"
}
}
}
def publish = project.tasks.create("publishAll")// publish all task
applicationVariants.all { variant ->
if (variant.buildType.name.equals("release")) {// Only Release
File outDir = file("//192.168.4.11/Android/Release")
File apkFile = variant.outputs[0].outputFile
File mapFile = variant.mappingFile
def task = project.tasks.create("publish${variant.name.capitalize()}Apk", Copy)
task.from apkFile, mapFile
task.into outDir
task.rename "mapping.txt", "${apkFile.name.substring(0, apkFile.name.length() - 3)}mapping.txt"// Rename mapping.txt
task.doLast{
println ">>>publish ${variant.name} success!" +
"\ndir: ${outDir}" +
"\napk: ${apkFile.name}"
}
task.dependsOn variant.assemble
publish.dependsOn task
}
}
I found this gradle script built for android studio to allow the use of the NDK until the team can create an official (and more understandable) way of accomplishing this. I don't really understand how it works or how it ties in to a normal gradle script as I am still new to this IDE. Can anybody explain it to me?
The script is:
//////////////
// NDK Support
//////////////
// If using this, Android studio will fail run the following to set the environment
// variable for android studio:
// launchctl setenv ANDROID_NDK_HOME
// /Users/boos_patrick/Development/Android/android-ndk-r8e
// otherwise remove the dependsOn part and run ./gradlew buildNative from the
// command line
task copyNativeLibs(type: Copy, dependsOn: 'buildNative') {
dependsOn 'buildNative'
from(new File('libs')) { include '**/*.so' }
into new File(buildDir, 'native-libs')
}
tasks.withType(Compile) { compileTask -> compileTask.dependsOn copyNativeLibs }
clean.dependsOn 'cleanCopyNativeLibs'
tasks.withType(com.android.build.gradle.tasks.PackageApplication) { pkgTask ->
pkgTask.jniDir new File(buildDir, 'native-libs')
}
task buildNative(type: Exec) {
if (System.env.ANDROID_NDK_HOME != null) {
def ndkBuild = new File(System.env.ANDROID_NDK_HOME, 'ndk-build')
commandLine ndkBuild
} else {
doLast {
println '##################'
println 'Skipping NDK build'
println 'Reason: ANDROID_NDK_HOME not set.'
println '##################'
}
}
}
I'm wondering how to add dependency to specific productFlavor and buildType in gradle.
For example I have productFlavor free and build type release, how can I add a dependency on the assembleFreeRelease task?
I've tried many variants but neither works.
For example I tried:
task('release', dependsOn: assembleProductionRelease) {
}
// error: Could not find property 'assembleProductionRelease' on root project 'app'.
Or:
task('release', dependsOn: 'assembleProductionRelease') {
}
Here there is no error but the task is executed for every flavor and build type, very confusing.
There is built-in support for flavor and buildType dependencies.
dependencies {
flavor1Compile "..."
freeReleaseCompile "..."
}
http://tools.android.com/tech-docs/new-build-system/user-guide#TOC-Sourcesets-and-Dependencies
These task are generated dynamically based on your Android plugin configuration. At the time of configuration they are not available to you yet. You can defer the creation of your task in two ways:
Wait until the project is evaluated.
afterEvaluate {
task yourTask(dependsOn: assembleFreeRelease) {
println "Your task"
}
}
Lazily declaring the task dependency as String.
task yourTask(dependsOn: 'assembleFreeRelease') {
println "Your task"
}
Looks like now in version 1 of the android gradle plugin and gradle 2.2+, you can do this with a configuration:
configurations {
freeReleaseCompile
}
android {
...
productFlavors {
free {
...
}
}
}
dependencies {
freeReleaseCompile('myLib#aar')
}
Reference: https://groups.google.com/forum/#!msg/adt-dev/E2H_rNyO6-o/h-zFJso-ncoJ
Muschko's answer didn't work for me, so this is my solution, written and posted by me here
Define the task that should only be executed on a specific buildType/variant/flavor
task doSomethingOnWhenBuildProductionRelease << {
//code
}
It's important to use the "<<" syntax or else the task will automatically be called every time.
Dynamically set the dependency when the tasks are added by gradle
tasks.whenTaskAdded { task ->
if (task.name == 'assembleProductionRelease') {
task.dependsOn doSomethingOnWhenBuildProductionRelease
}
}
android {
ext.addDependency = {
task, flavor, dependency ->
def taskName = task.name.toLowerCase(Locale.US)
if (taskName.indexOf(flavor.toLowerCase(Locale.US)) >= 0) {
task.dependsOn dependency
}
}
productFlavors {
production {
}
other
}
task theProductionTask << {
println('only in production')
}
tasks.withType(JavaCompile) {
compileTask -> addDependency compileTask, "production", theProductionTask
}
}
To be frank, I don't which locale is used to generate names for compile taks so toLowerCase(Locale.US) may be counterproductive.
Here is another way that I used:
tasks.withType(JavaCompile) {
compileTask ->
def dependedTaskName = "dependedTask_";
if(compileTask.name.contains('Release') {
dependedTaskName += "Release";
}
createTask(dependedTaskName, Exec) {
........
}
compileTask.dependsOn ndkBuildTaskName
}
Another way:
tasks.whenTaskAdded { task ->
if (task.name == 'generateReleaseBuildTypeBuildConfig') {
task.dependsOn doSomethingForReleaseBuild
}
}
The 1st method is dynamic while the second one is simpler.
Update: This solution no longer works as of 12/8/2022
I now get the error message: No such property: productFlavors for class: org.gradle.api.internal.provider.DefaultProperty
All the above answers helped me, but I ended up needing to run a script before the compilation of each flavor's debug + release build. The first argument of the script takes the flavor name.
In order to have this trigger early on, but after the flavor specific task has been created, the key is binding the dependsOn to generateMyflavorDebugBuildConfig.
Here's the script I ended up with, which can be placed at the bottom of app/build.gradle
// When a task is active, loop through all app flavors, and see if the
// task.name matches the earliest task we can set up the dependsOn
// loop through all flavors, and create a task that does the work we want to do
// before anything else
android.productFlavors.all { flavor ->
task("${flavor.name}RunCommandBefore", type: Exec) {
workingDir "$projectDir/../.."
commandLine 'sh', 'scripts/run_thing.sh', "${flavor.name}"
}
}
// when tasks created, loop through and as early as we can, bind the
// RunCommandBefore task we created above
tasks.whenTaskAdded { task ->
def taskName = task.name
// loop through all flavors
android.productFlavors.all { flavor ->
def flavorName = flavor.name
// loop through release types so that this happens for debug and release
['debug', 'release'].each { releaseType ->
// generateMyflavorDebugBuildConfig is the earliest task we're able
// to set up the dependsOn to make sure that our script runs
if (taskName.toLowerCase() == "generate${flavorName.toLowerCase()}${releaseType}buildconfig") {
// now myflavorRunCommandBefore will run before
// generateMyflavorDebugBuildConfig
tasks."$taskName".dependsOn "${flavorName}RunCommandBefore"
}
}
}
}
Hope this helps someone! Amazing how difficult Android makes running a simple script first thing..