Gradle Android - Override standard tasks - android

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.

Related

Signing and aligning APK file(s) **/*.apk in Azure Devops

I have been trying to make devops build of android apk and my pipeline fails in Signing and aligning Apk Stage.
I am getting the following error
##[error]Error: No matching files were found with search pattern: D:\a\1\s***.apk
PIPELINE Definition Snapshot_2
[error]Error: No matching files were found with search pattern: D:\a\1\s***.apk
This error indicates that you don't have a xx.apk file generated in default working directory. You can follow steps below to locate the root cause of the issue and resolve that:
Leave gradlew build task's working directory blank and make sure this task succeeds to generate the xx.apk file.
In Android signing task, use the **\*.apk pattern.
Make sure the gradlew build and Signing and aligning APK tasks are in same Job. If you're using hosted agent to run those tasks, they must be in same job within same pipeline/stage so that the Signing and aligning APK task can directly access the outputs of gradlew build task.
Please make sure these two tasks are in same job. If you have specific reason that you have to make them in separate jobs/stages/pipelines, consider using Artifacts-related tasks to share the files between jobs/stages/pipelines.
Since a job is a series of steps that run sequentially as a unit, different jobs will run in different machines though those agents are called hosted windows/linux. So it's recommended to put the two tasks in same job.
If the tasks are in same job while the issue persists, set the system.debug variable to true and run the pipeline again, then you can check the details about the gradlew build task.
Then you can find the output path where the xx.apk files are generated. After that copy those files to Default working directory ($(System.DefaultWorkingDirectory)) and the signing task can find the file.
It looks like .apk file is not found under working directory of the build agent. Please check code checkout step of the build pipeline and make sure the .apk file is being checked out as part of the build step. You can view the log as well to confirm this.

Android build instant app from terminal

I have a an app that has 2 modules base and restaurant. I have a module output that is used for normal app release and another one for instant app. When I run the build using android studio it is working fine but when I run
./gradlew assembleRelease only the output module seems to be built. Is it possible to create the instant app apk from terminal? My instant app gradle file is only like:
apply plugin: 'com.android.instantapp'
dependencies {
implementation project(':base')
implementation project(':restaurant')
}
without any signing configs inside.
Gradle build only one module gives you a clue.
You can point gradle to the com.android.instantapp module. The instant app module basically instructs which modules to assemble, those defined by implementation project(), and zip them up for the instant app.
This base cmd worked for me:
./gradlew instantapp:assemble
Then the ia zip files will be contained in the instantapp /build/output/apk/ directory.
But even if you run ./gradlew assemble, you should still find your instant app apks.zip in that same directory.
You should open the Gradle window.
View -> Tool Windows -> Gradle.
You can synchronise and run any task you want to execute (independently).
if you need absolutely to do in the terminal or in a script.
I m sure that after seen the name of the task. You ll be able to write your command line.
It doesn't look like this question was ever answered, so in case anyone else is searching for it, here you go:
The new recommended way to produce an Instant App is via Instant Enabled App Bundles. (See Google Developer Documents for details)
Basically, you need to first build an app "bundle". To do so, there are a number of steps, but basically you need to get your application within the recommended size limitations (10 MB) and ensure that you are not using any unsupported permissions. If you are working with a larger application, the best way to do this is to modularize your features and only include those features that you want to be "instant". This "instant" version of your application will be a separate Flavor, and from there, you can add in the following to the Manifest that corresponds to the Instant app version of your application.
<manifest ... xmlns:dist="http://schemas.android.com/apk/distribution">
<dist:module dist:instant="true" />
...
</manifest>
When you've done that, if you want to build using CLI/Terminal, you will need to build using the new bundle version of your application. You can run ./gradlew tasks to see what options you have available, or just run ./gradlew bundle which will run them all.
Next, to test to ensure that your app bundle was done correctly, you will need a couple more tools. The bundletool (provided by Google here), and the ia tool, located in your $ANDROID_HOME/extras/google/instantapps/ directory. (Just slap that on your path or have an alias point to it).
With both of these tools in hand, you will need access to a signing key for the instant app. I haven't tried it, but you could probably use the debug signing key for testing purposes. Just to quickly demonstrate how to build a key using CLI, here you go:
apksigner sign --ks my-instant-app-key.jks --out my-app-release.apk my-app-unsigned-aligned.apk
Note: Don't use this for production, this is just demonstration.
Now, we can build the APK's for our app bundle and sign them.
$ java -jar bundletool-all-*.jar build-apks \
//1
--bundle=app.aab \
//2
--output=app.apks \
//3
--connected-device \
//4
--ks=your-keystore-path \
//5
--ks-pass=pass:your-keystore-password \
//6
--ks-key-alias=your-key-alias \
//7
--key-pass=pass:your-key-password
Note: The above was taken from the following tutorial.
Next, check to see if your instant app was done correctly by running the following:
ia check instant-app.apks
The s at the end of apks is not a typo. If all checks out, you can run the app using:
ia run instant-app.apks
Hope this helps!

How can I view the CLI command executed by a Gradle task in Android Studio?

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.

Android Studio / Gradle: run external tool before compilation

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 :)

android gradle run assemble after tests pass

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.

Categories

Resources