I'm trying to fire off various android based gradle tasks e.g. assemble, after 'gradle clean test' is run.
Some background...
My company has jenkins and it is managed by a separate team so I don't have access to configure it myself. On any changes to the remote repo (git) a jenkins job will fire, running gradle clean test and using a build.gradle file that we have inside our repo.
I'm told that this is the only command that the build team will provide and if I want any further actions running, I'll have to configure them inside the build.gradle script.
I am imagining that I can possibly do something like afterTest(:assemble) or maybe addTestListener() but I can't seem to find any examples on google.
Can anyone here help me? Is this even possible or should I ask my build team to allow me to run a diff gradle task depending on what I want?
Configuring CI jobs uniformly is a good idea. However, there is no good way to have additional independent tasks executed when gradle clean test is run. They'd have to at least run gradle clean build so that you can add tasks with build.dependsOn(myTask). (However, keep in mind that build already depends on assemble.) Or they run a custom task such as gradle (clean) ciBuild which by default only depends on test, and to which further task dependencies can be added as necessary.
Related
I'm trying to get a better picture of what happens behind the scenes in Android Studio when building an Android application. I've been reading up on Gradle, but one thing I cannot figure out is how to see the respective CLI command and arguments that is being invoked by Gradle. It seems to be abstracted and not logged to the Gradle Console or Event Log.
The closest I've gotten to seeing what's going on inside Gradle is the AOSP code.
2.2.2 Source:
https://android.googlesource.com/platform/tools/base/+/gradle_2.2.2/build-system/gradle-core/src/main/java/com/android/build/gradle/tasks
Goals
I want to be able to see the respective CLI command that is generated by the Gradle tasks inside Android Studio.
Use Case Example
I want to view the Legacy Android Build Process in depth. This includes going through the following:
Source Code / Library Code -> javac -> Java bytecode (.class) -> proguard -> minimized bytecode (.class) -> dex -> DEX bytecode (.dex)
For example I would want to see the respective javac command invoked by AndroidJavaCompile. https://android.googlesource.com/platform/tools/base/+/gradle_2.2.2/build-system/gradle-core/src/main/java/com/android/build/gradle/tasks/factory/AndroidJavaCompile.java
I fear that the only way to do this is to look directly through source code or even build directly from source.
Due Diligence
I've done quite a bit of searching on Google, Android blogs, Google I/O talks, Android books, and much more. I haven't been able to find a straight-forward answer.
That's not possible. Simply, because most of the Gradle tasks do not invoke CLI commands.
Every Gradle build file is a piece of Groovy code that gets executed in a JVM along with the Gradle API (written in Java). Therefor, you can implement any task or configuration functionality directly in any JVM language, from which most plugins make use of instead of executing command line tools. Nevertheless, this is possible by using or extending the Exec task.
The compilation step is handled by a AndroidJavaCompile task, which extends the common JavaCompile Gradle task by some version checks and the Instant Run feature. However, you don't know how Gradle actually compiles the .java files. In the internal source files for the JavaCompile task of the Gradle API, there seem to be various implementations (DaemonJavaCompiler, JdkJavaCompiler and even CommandLineJavaCompiler). Since you can specify CompilerOptions with your task, Gradle seems to choose the real compiler based on these options. Please note, that even if a CommandLineJavaCompiler exists, it is also possible (and highly likely), that Gradle prefers to use the javax.tools package and its JavaCompiler implementation to compile the source files instead of invoking a command line tool.
I also took a look on the ProGuard step in your example build process: ProGuard can be used as command line tool, where you can specify arguments to define how it'll work. But ProGuard also provides a Gradle task (ProGuardTask), that executes without invoking ProGuard from command line. The ProGuard Java code will be executed in the Gradle JVM.
As you can see, even if each Gradle task may be replaced by one (or multiple) CLI command(s), Gradle does not execute these commands. Instead, the functionality is called directly in the Gradle JVM. If you want to get a better insight, you can increase the Gradle log level. Good implementations of Gradle tasks should provide all necessary information in logs.
Is that possible? I have changed a few basic things in my code and want to make a clean build to see how many things needs to be adjusted now. This results in 1000s of cannot find symbol class ... messages in my Messages Gradle Build window.
So I want to run the annotation processor manually now to get rid of those messages and only see the relevant messages (looking through so many messages is cumbersome and I know that all annotated classes can be build by the annotation processor without problems).
Is that somehow possible? How do I run the annotation processor in android studio manually?
You should never have to run Annotation processors manually (unless of course you are developing one).
Make sure you have Annotation Processors enabled in Android Studio (https://www.jetbrains.com/help/idea/2017.1/configuring-annotation-processing.html), Build > Clean project, and then Build > Rebuild project.
If you have everything set up correctly, this should work. However if your build is failing due to an annotation processor (Example: Dagger not meeting dependencies graph) then you will need to figure out the underlying issue.
If you are working with kotlin and using kapt there should be a task named kaptDebugKotlin you can access using gradle CLI by simply executing this command in the terminal ./gradlew app:kaptDebugKotlin
Make sure you replace app with your module name in case you have a multi-module project.
If you are working with java then simply replace app:kaptDebugKotlin with compileDebugJavaWithJavac and that should execute your annotation processor.
to force the build to continue in case of compilation error add --continue to the command in the terminal.
Happy coding.
I want to understand more about ConnectedAndroidTest Gradle task. I see that it is used to install the application and test apks and run the tests.
But what are the individual steps that it does? (gradle tasks if any)
"gradle build" seems to generate the Application apk. What task generates the test apk? And how does it(ConnectedAndroidTest) install the application and test apk? And how does it start the tests?
Thanks very much.
My first SO answer, please be gentle ;)
But what are the individual steps that it does? (gradle tasks if any)
So if you want a high-level overview of what tasks ConnectedAndroidTest depends on, just running ./gradlew connectedAndroidTest or ./gradlew cAT (without the -q option) will output the name of each task that cAT depends on before it itself is executed. The task itself can't have other tasks inside it, but can depend on others coming before it.
From this answer, the gradle build task is actually something java related, and isn't what's responsible for building the test apk. Instead, it's the assembleAndroidTest task that comes right before connectedAndroidTest that does it. You are right about the connectedAndroidTest though, it actually installs and runs the test apk. But I'll come to how in a bit. The rest of my answer is goes into more detail than is necessary to use the task effectively, but is useful if you want to understand how it works.
Some background
Like many other Android gradle plug-in tasks, connectedAndroidTest is actually put together at some point in the execution phase because of the different build variants (debug, release, flavour 1, flavor 2 etc.). So connectedAndroidTest isn't available to you in the configuration phase (when most of your build script logic is executed). Instead, once it's built, it's set as the connectedInstrumentTest property (basically, a field) of the testVariants property in the android object.
As an example for clarification, if you want to access this task to manipulate it somehow (maybe add an Action to the end of it), you can do something like this in your build.gradle file:
android {
testVariants.all { variant ->
variant.connectedInstrumentTest.doLast {
println "This will be executed right after our connectedInstrumentTest!"
println "The name of the test type: $connectedInstrumentTest.name"
println "The type of test $connectedInstrumentTest.class"
}
}
}
And then run ./gradlew -q cAT
So here, I'm adding an action to the end of whatever task has been built and assigned to the connectedInstrumentTest property, which is nested fairly deep in the android object. This task will be likely beconnectedDebugAndroidTest or something similar.
What's the task doing?
Now, from the type property I put in the last println, we can see that the class of the task is actually com.android.build.gradle.internal.tasks.DeviceProviderInstrumentTestTask_Decorated. To be honest, I'm not too sure yet where that _Decorated part comes from, but a google search for the rest of the class name provides us with the source code for the base class of the task.
The main Action of the task is called runTests() and shows you more or less how the task accomplishes what it does. If you follow the source code around a bit, you eventually find that the adb pm install command will be used to install the apk.
Although I couldn't quite find it, I suspect that somewhere else the adb command adb shell am instrument -w com.package.name/android.support.test.runner.AndroidJUnitRunner command is being used to finally drive the tests.
So I hope that wasn't too confusing - I learnt most of this very recently so some things might not be 100%. I would suggest working through the gradle docs, in particular how to create a custom plugin and a custom task, and also check out the Android gradle plug-in tools documentation.
To answer the more general question "what are the list of tasks that task <taskName> executes?", there are two simple ways to find that out for any given task.
The first is:
./gradlew tasks --all | grep <taskName>
where <taskName> should be replaced with whatever task you care about. E.g., ./gradlew tasks --all | grep connectedDebugAndroidTest. Note that I'm piping through grep to save myself the trouble of manually sifting through the list of all tasks.
The second is:
Use the task-tree plugin. Once applied, usage looks like this:
./gradlew <taskName> taskTree
Or, as I usually prefer it:
./gradlew <taskName> taskTree --no-repeat -quiet
The latter option makes the output a bit less cluttered.
You could also execute ./gradlew connectedAndroidTest --dry-run which will list all the tasks it'd run, but won't run any. --dry-run does the same for any gradle task that you want to analyze.
I have an Android Studio project which consists of several modules.
Some of these modules need to run an external pre-processing tool before they're compiled. Plus, I also need to run another tool project-wide every time ANY module in the project is compiled
Unfortunately I'm new to Gradle and I'm being a bit overwhelmed by it. I've succesfully included a new Gradle script in my project by editing settings.gradle and I've written in it some Gradle tasks that run the tools I want to run, and that is fine.
But the problem is I don't understand how to hook them up so they get executed at the right moment.
Basically, I want to know how to edit my build scripts in order to:
A) Always run a certain gradle task before any module in the project is built. (If possible, I'd want this to run only once, in the sense that if I rebuild the entire project, which is composed of 20+ modules, I don't want it to run 20 times, just once. This is secondary tho, the main thing is that it needs to run every time any module in the project is built)
B) Always run a certain gradle task before a certain module is built. In other words: how do I edit the build script of a single, specific module in order to run a certain task before compilation? Note that in this case the invoked task needs to be able to know which gradle project invoked it, ie: which module is being compiled.
Hope the questions are clear, I'll clarify if necessary (as I said, I'm new to Gradle, hope I didn't mess up the terminology too much)
Answer to A is task dependency, I'm assuming when you say modules you mean subprojects, the ones you included in your settings.gradle. If all these sub projects requires this task to run first, I would define the subprojects:
subprojects {
apply plugin: 'java'
task precompiletask() {
println "Executing pre-compile task"
}
compileJava.dependsOn precompiletask
}
The second part of A you might get for free if you setup your task correctly and put inputs/outputs. This is what gradle checks if it needs to rerun the task again or not. If nothing change in inputs/outputs then it won't run the precompiletask and would skip it.
For part B, what I would do with this is find a common attributes between these projects and configure them:
configure(someProjects()) {
// do whatever you want here to those projects
// for example, set up pre compile task like the one above
}
def someProjects() {
ext.someProjects = [] as Set
ext.someProjects.addAll subprojects.findAll { Project aProject ->
// filter here what's common with those projects
// for example, all projects that have yml file
}
logger.debug("Some projects [{}]", ext.someProjects)
ext.someProjects
}
Hope this helps, have fun :)
I'm trying to customize the behavior of my Gradle build to be Android-Wear friendly.
I am bundling manually my wear apk in my handled apk (because i didnt managed to do it automagically).
This means that if I want to build a new version of the handled apk, i have to manually build my wear apk, copy/past the generated wear-apk insinde my res/raw of the handled project then build the new handled apk.
I want all this to be automatized.
So, what I need to do is :
Launch app:assembleRelease from cmd line
Gradle first do a wear:assembleRelease
At the end, Gradle take the apk from wear/output/apk/wear-apk.apk and copy it in app/src/main/res/raw
Then Gradle can procede to do app:assembleRelease
I dont find how to launch a task (wear:assembleRelease) from another task.
Any help is welcome !
I found a solution that may not be optimal but it is working for what I need.
In my handled app, i first have to say that the assembleRelease depends on my wear:assembleRelease:
app/build.gradle
project.afterEvaluate {
preReleaseBuild.dependsOn(':wear:assembleRelease')
}
preReleaseBuildis one of the very first task of the build but this task is created dynamically, that's why you have to wrap it after the project is evaluated.
Then, in my wear build.gradle, I have to specify the copy at the end of the build:
wear/build.gradle
assembleRelease << {
println "Copying the Wear APK"
copy {
from 'build/outputs/apk'
into '../app/src/main/assets'
include '**/wear-release.apk'
}
}
With only theses modifications, i managed to have the workflow explained in the question.
This could be enhanced because it is only working for the release build but it's a good first step.
Feel free to comment this solution.