How to skip a specific gradle task - android

I want to skip one of the tasks of Android gradle build, so I tried something like
project.tasks.getByName("transformNativeLibsWithStripDebugSymbolForDebug").onlyIf {
println("Skipping")
return false
}
However, the task is not found, even though I can see it in the tasks that are executed...
Any idea how can I get this task? I guess it should be dependant of one of the tasks in the project.
Context - I have a shared library that I want to user the unstripped version of it, however gradle strips it anyway...
EDIT
After some digging, it seems that those tasks are added as part of the binary plugin (the apply plugin: 'com.android.library' line at the top of the gradle file).
This transform task is added using the transform API, which doesn't seems to have a way to unregister/modify existing transform...

It's dynamically generated task. Try to add next:
android {...}
afterEvaluate { project ->
project.tasks.transformNativeLibsWithStripDebugSymbolForDebug {
onlyIf {
println 'Skipping...'
return false
}
}
}
dependencies {...}
In Gradle Console you should see:
Skipping...
:app:transformNativeLibsWithStripDebugSymbolForDebug SKIPPED
Do not forget that transformNativeLibsWithStripDebugSymbolForDebug task is only executed, when you use assembleDebug task (or Shift+F10 combination in Android Studio).

Related

How to Run Custom Gradle Task In Gradle Plugin During Android Project Configuration Phase?

Okay, So I have been pulling my hair out trying to get this to work properly and I just want to write my tests and push this to maven already so my standup updates are not the same day after day.
Problem Statement: I have multiple android projects that all require the same pre-configuration. This includes declaring maven repositories, setting versionCodes and versionNames, and other misc configuration items. The same blocks are copy and pasted in every project or placed in a local common.gradle file and then applied and called at the project level build.gradle configuration stage.
My Solution: Extract all of this common logic into a standalone gradle plugin and let each projects build.gradle file apply this plugin to receive these configuration events.
I created a standalone plugin, did an override on plugin and it looks something like this:
class CommonManager: Plugin<Project> {
override fun apply(target: Project) {
println("APPLY FUNCTION HAS BEEN ENTERED")
target.tasks.create("myPluginTask", ReleaseManager::class.java)
}
The ReleaseManager class is a class that extends DefaultTask and creates a task to run. This is what is recommended in the gradle docs and it makes sense from a testability / reusability standpoint and that class looks something like this:
abstract class ReleaseManager: DefaultTask() {
#get:Input
abstract val versionHistoryPath: Property<String>
#TaskAction
fun configureVersionsAndReleaseMeta() { // do work... }
According to the docs I have read and other example projects I have thumbed through, this is set up correctly. I built the plugin using java-library instead of java just to have a local .jar file to point to in my android projects for quick testing. An example of one of these build.gradle files looks like the following:
buildscript {
repositories {
google()
mavenCentral()
maven {
url "https://plugins.gradle.org/m2/"
}
}
dependencies {
// misc dependencies ...
classpath files("libs/MyCustomPlugin-1.0-SNAPSHOT.jar")
}
}
apply plugin: "com.myplugin.common"
myPluginTask {
versionHistoryPath.set("sdk/version_history.txt")
}
subprojects {
apply plugin: 'maven-publish'
apply plugin: 'com.jfrog.artifactory'
group = 'org.jfrog.test.gradle.publish'
}
I create a dependency on the .jar and apply the plugin, then configure the task with the property it needs to run. However, the task never runs during the configuration phase when the log outputs > Configure project I see my print statement from the apply function in my plugin log but no output or configuration happens in the task class that I defined. If I click the play arrow next to the myPluginTaks{} configuration definition, it does in fact run fine. At this point, I know I'm pointing to the .jar correctly, I can access the task that I create in my plugin class, and I can set its property respectively. I'm newer to doing this kind of custom work with gradle and want to understand what I am doing wrong. Let me outline my thought process and debugging steps I have tried.
My "Bug" Statement: I want the task "myPluginTask" to execute after I apply my plugin in my build.gradle script and I have configured the task with the proper input. It is currently not executing the task.
Thoughts and notes:
I used create over register in the plugin apply function when creating my task. My understanding is that by using create, I am saying "hey I want to create a task, and I want the build to know about it immediately" rather than "Hey here's a task definition, but lets lazy load it once it is actually invoked on the implementation end"
I moved the implementation of ReleaseManager into the plugin class as individual functions and called these functions inside the task creation closure. That works. To my understanding this is because anything defined in there is run during a builds configuration phase. I can defer this to the execution phase with a doFirst or doLast closure if I wanted to.
My hunch is, I've created a task and sure the build now knows about it immediately, but it has no plans on executing the task until at least the execution phase and needs a trigger. Whether that trigger is a direct invocation of the task or the task depends on a task that is going to run in the execution phase.
Any help would be greatly appreciated. Happy to provide any additional information if needed.
A user below provided an insightful link that helped me arrive at a solution. I also have a much better understanding of Gradle at this point and can better articulate my problem.
TLDR; My hunch was correct. I had successfully created a task, but did not provide a way to trigger such task. You do this by depending on a task provided by the target project.
To reiterate, my plugin class looked like the following:
class CommonManager: Plugin<Project> {
override fun apply(target: Project) {
println("APPLY FUNCTION HAS BEEN ENTERED")
target.tasks.create("myPluginTask", ReleaseManager::class.java)
}
My module level build.gradle file applying the plugin had the following:
apply plugin: "com.myplugin.common"
apply plugin: "com.android.library"
The apply function would trigger in my plugin and the task would be created. It would NOT run however. To accomplish this, I needed to depend on another task in my target build as a trigger. That effectively looked like the following:
class CommonManager: Plugin<Project> {
override fun apply(target: Project) {
println("APPLY FUNCTION HAS BEEN ENTERED")
target.tasks.create("myPluginTask", ReleaseManager::class.java) { task ->
// Have target project create a dependency between its task and mine
target.tasks.findByName("preBuild").dependsOn(task)
}
"preBuild" is a task provided by the android plugin. In my case com.android.library this lead to issue two. When building my project, the plugin would throw an error stating that "preBuild" is not a defined task. At first I thought I just couldn't reference tasks provided by the android library but then it hit me. If you look at my above code block for my modules build.gradle file, you will notice I define my common plugin first. This means at the time of my plugins apply block firing, the plugin was unaware of this task because the android plugin had yet to be applied.
You could define the android plugin first and document that order of definition is important but, that is not a pragmatic solution. What you can do however, is implement the afterEvaluate closure on the target project like so:
class CommonManager: Plugin<Project> {
override fun apply(target: Project) {
println("APPLY FUNCTION HAS BEEN ENTERED")
target.tasks.create("myPluginTask", ReleaseManager::class.java) { task ->
target.afterEvaluate {
// Have target project create a dependency between its task and mine
target.tasks.findByName("preBuild").dependsOn(task)
}
}
This will wait for evaluation of the modules build.gradle file to finish. Therefore, once my plugins apply block is triggered, my plugin will know about the "preBuild" task because the android plugin was applied during project evaluation.
Bonus tip: I am using create to make my task in the plugin. If you are using register, you do not need the afterEvaluate closure. If you define one, the plugin will throw a runtime error if running gradle 7.0 or higher. The gradle community considers that a redundancy.
Am not quite sure that this will help you or not .
did you try to
myPluginTask.configure {
mustRunAfter TASKNAME
}
I was looking around and this might be helpful as well .

Crashlytics NDK symbols and Gradle tasks

I have a question that mostly relates to gradle.
I'm using Crashlytics to report NDK crashes in my Android app.
I have a task in build.gradle that calls ndk-build and compiles the cpp files into an .so file.
At the end of this task I want to call a task that uploads generated symbols mapping to Crashlytics.
After installing the Fabric plugin in Android Studio, I saw there are some new tasks that were added to the Gradle tab. One of them is
crashlyticsUploadSymbols[buildType][flavour] where buildType and flavour indicate which buildtype and flavour is currently selected.
This task does seem to upload a symbols file.
My question is,
Is it possible to call this task from within build.gradle?
Currently I use a manual call in Android Studio's terminal tab in the form of:
./gradlew crashlyticsUploadSymbols[buildType][flavour]
Is it possible to call this task somehow from within build.gradle?
To call this task I use finalizedBy at the end of the buildNdk task, so once buildNdk has finished, the upload task will execute.
Also very important, how can I get the current buildType and flavour so I am able to add it to the crashlyticsUploadSymbols call?
Thank you!
Mike from Crashlytics and Fabric here.
This was also answered on the Twitter Community forum's, but sharing the same answer here.
Option 1:
If you only want or need to upload symbols for your release builds, then you can set crashlticsUploadSymbolsRelease as the finalizedBy task for your ndk-build task.
Option 2:
If you have multiple variant-based tasks, you can do something like:
android.applicationVariants.all { variant ->
def variantName = variant.name.capitalize()
def task = project.task ("ndkBuild${variantName}")
task.finalizedBy project.("crashlyticsUploadSymbols${variantName}")
}
The following did the job for me:
android {
...
afterEvaluate {
assembleDebug.finalizedBy(crashlyticsUploadSymbolsDebug)
assembleRelease.finalizedBy(crashlyticsUploadSymbolsRelease)
}
}

How to get build variant output directory

I need to run some tasks that occur after an Android project's assemble* task finishes. In particular, these tasks need to know what was the output directory for all the compiled classes for a particular build variant. How do I retrieve the output directory for an assembleFlavor1Debug task?
My current workaround is something like this (although this workaround presents problems of its own, like not being able to find the assemble tasks even though it's been placed after the android configuration block):
android.buildTypes.all { theBuildType ->
android.productFlavors.all { theFlavor ->
String capitalizedType = ... //Type name with first letter capitalized
String capitalizedFlavor = ... //Flavor name with first letter capitalized
...
project.tasks["assemble${capitalizedType}${capitalizedFlavor}"].configure {
doLast {
project.ext.variantOutput = "build/intermediates/classes/${theFlavor.name}/${theBuildType.name}"
}
}
}
}
EDIT #1: I was able to fix my workaround. The major issue was that the Android assemble* tasks (assembleProdDebug, assembleProdRelease, etc.) were not yet created on the project, even though configuration was occurring after the Android configuration block. I was able to get the additional configuration on the assemble* tasks done by enclosing the entire code snippet above into a gradle.taskGraph.whenReady {...} block, but this did mean I lose out on the ability to continue configuring the dependency graph. Fortunately, not being to configure dependencies in my particular case was not a major loss; all I needed was the ability to record the last assembled build type and product flavor.
I'd also like to note that this behavior is with version 1.0.0 of the Android Gradle plugin. Although I have not checked, the absence of these Android tasks might not occur on newer versions of this plugin.
EDIT #2: So I've also tried version 1.3.0 of the Android Gradle plugin. I'd also like to note that this is the LIBRARY Android plugin, and not the application plugin (I suspect these missing assemble* tasks are not generated during project configuration for the application plugin as well, however).
You might want to try instead of wrapping the entire thing with gradle.taskGraph.whenReady try using afterEvaluate closure. The tasks should exist after the project is evaluated.
This means your closure would run at the end of the configuration phase and before the execution phase. At this time all tasks would have to be registered.
afterEvaluate { project ->
// do work on `Project` object just like normal
project.android.buildTypes.all { theBuildType ->
...
}
}
ref: https://docs.gradle.org/current/javadoc/org/gradle/api/Project.html#afterEvaluate(groovy.lang.Closure)

How to Build AAR and Sample Application

I'm running into a collection of gradle problems in setting up a multi-module project. I'm trying to produce an AAR that contains an SDK for our customers use. I'm also trying to produce a sample application that uses that AAR both as a development platform internally and as an example for our customers of how to use the platform.
settings.gradle:
include :sdk
include :SampleApplication
build.gradle:
...
// copy the AAR produced by the SDK into the SampleApplication
task import_aar(type: Copy) {
dependsOn ":sdk:build"
from(new File(project(':sdk').getBuildDir(), 'outputs/aar')) {
include '*-release.aar'
rename '(.*)-release.aar', '$1-v1.0.0.aar'
}
into new File(project(':SampleApplication').projectDir, 'aars')
}
...
SampleApplication/build.gradle:
...
repositories {
...
flatDir {
dirs 'aars'
}
}
...
dependencies {
...
// This causes gradle to fail if the AAR hasn't been copied yet
compile 'com.moxiesoft.netagent:moxieMobileSDK:+#aar'
compile project(':moxieMobileSDK')
...
}
So the biggest problem that I'm having right now is getting the import_aar task to run before the compileDebug/ReleaseSources tasks. I've tried adding explicit dependencies to the compile tasks, but I'm apparently not finding the right way to do it.
I've tried putting this in SampleApplication/settings.gradle:
tasks['compileReleaseSources'].dependsOn(':import_aar')
but gradle fails because there's no compileReleaseSources task, even though gradle :SampleApplication:tasks shows one.
I also tried putting similar stuff in settings.gradle, but it also failed with an error that the task compileReleaseSources didn't exist.
I did have limited success by putting this in my SampleApplication/settings.gradle:
tasks['build'].dependsOn(':import_aar')
But that only has the correct affect if I use "gradle build", which doesn't happen if I'm debugging or running from Android Studio.
I was finally able to get this to work by putting the dependsOn on the preBuild task, but I'm still not particularly happy with the solution, because:
It requires me to have the aar in place before gradle runs, which
means I wind up putting the .aar into git, which isn't a
particularly good idea.
I'd rather not have the AAR generation leaking into the
SampleApplication/build.gradle file, since that's intended for
customer usage.
Is there a better way of handling the problem in general?
I also had problem adding a dependency to compileReleaseSources task and described here a solution that worked for me. In short, the dependency need to be added in tasks.whenTaskAdded closure.

Ignore Gradle Build Failure and continue build script?

Managing Android's dependencies with Gradle is done in a weird way. They have to be downloaded differently into a local repo. This is a pain when setting up CI build as there are multiple nodes this could run on. As such I'm using the sdk-manager-plugin to have the Android dependencies downloaded at build time. There seems to be an old bug that I'm experiencing with the sdk-manager-plugin though in that it will download the dependencies at build time, but they won't be available on that command.
The next time the command is run everything works fine (as everything is already downloaded), but I need to find a way to ignore the build failure of the first gradle command so that everything is downloaded and good to go for the second. I realize this is hacky, but I'm done messing with this.
Ideally something like this would work:
./gradlew clean --ignoreBuildFailures
./gradlew distributeCIBuild
The closest thing I could find in the Gradle documentation is --quite but that doesn't look like it'd work.
Any creative solutions welcome.
The flag to use is --continue.
From the documentation:
Continues task execution after a task failure.
add this in the build.gradle file :
tasks.withType(JavaCompile) {
options.failOnError(false)
}
You can use ignoreExitValue
task ktlint(type: JavaExec, group: "verification") {
description = "Check Kotlin code style."
ignoreExitValue = true
}

Categories

Resources