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.
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.
I have multiple projects and one trunk...
Some times I need to run via cmd:
gradlew Proj1:assembleRelease
And other times:
gradlew Proj2:assembleRelease
How can I in gradle file know what is building Proj1 or Proj2?
You can access this information via the gradle property on any instance of Project. From the docs:
getGradle()
Returns the Gradle invocation which this project belongs to.
The instance of Gradle provides the startParameter property, which contains the information you want. For example, in the root scope:
print project.gradle.startParameter
For my particular invocation, I could print the name of the first task request via
print project.gradle.startParameter.taskRequests[0].args[0]
However, the above is probably quite fragile and will surely fail in a number of important cases.
There are also many other things you may find useful in the Gradle instance, if the above doesn't do exactly what you want, such as the task execution graph and root project for this build. Note that all the above information is available by following links from the documentation for the Project class, which I linked above. I strongly recommend that you peruse this documentation.
On the other hand, why do you want to do this? Usually you would just add configuration to the individual projects themselves and let the task runner take care of running your specific implementations, rather than saying if task1 is running then x else y
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.
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.
I always prefer ant test command to am instrument to run android tests on my build server, because it's simplier and am instrument doesn't work always.
But so far, I don't know whether if it is possible with ant to run just specific test classes, packages, or suites in order to be more flexible and separate the frequency of run of different test types (like, for example, unit, ui, acceptance, performance etc.).
If there the answer to my question is "no", what else could I do, of course without using am instrument?
Thanks in advance.
You could define a separate Ant target for each type of test, and then make each target conditional on some property.
<target name="performance" if="test.performance">
</target>
You could then execute the required tests by setting the required properties at build time - e.g. by passing on the Ant command line (-Dkey=value) or defining in a properties file which you load in your build file.