How to prevent rawproto file generation or delete them automatically? - android-studio

Android gradle plugin generates tons of .rawproto files in build/android-profile directory. What are they used for? Is there a way to disable this madness or automatically delete them?

I've been bugged by it for a long time, and now that I noticed there's gigabytes of this hogging my smallish SSD, I've decided to figure out a way to disable it. For me the most annoying thing before occupying too much space was gradlew clean leaving a build folder behind.
Only tested with com.android.tools.build:gradle:3.0.1, so YMMV.
TL;DR
For global application read last section, per-project use this in rootProject's build.gradle:
com.android.build.gradle.internal.profile.ProfilerInitializer.recordingBuildListener =
new com.android.build.gradle.internal.profile.RecordingBuildListener(
com.android.builder.profile.ProcessProfileWriter.get());
// and then `gradlew --stop && gradlew clean` to verify no build folder is left behind
Investigation
Thanks to https://stackoverflow.com/a/43910084/253468 linked by #JeffRichards mentioning ProcessProfileWriterFactory.java, I've put a breakpoint there and checked who's calling it by running gradlew -Dorg.gradle.debug=true --info (not to be confused with --debug) and attaching a remote debugger.
I followed the trail and found that ProcessProfileWriter.finishAndMaybeWrite creates the folder and writes. Backtracing on method calls I found that ProfilerInitializer.recordingBuildListener controls whether it's called ... and that is initialized directly by BasePlugin (apply plugin: 'com.android.*').
So in order to prevent anything from happening I opted to try to disable the guard, by pre-initialized that static field. Thankfully Groovy (and hence Gradle) doesn't give a * about JVM visibility modifiers, so without reflection here's the magic line:
com.android.build.gradle.internal.profile.ProfilerInitializer.recordingBuildListener =
new com.android.build.gradle.internal.profile.RecordingBuildListener(
com.android.builder.profile.ProcessProfileWriter.get());
I know, it's a bit verbose, but it works, and if you import stuff it looks better:
ProfilerInitializer.recordingBuildListener = new RecordingBuildListener(ProcessProfileWriter.get());
Applying the magic
In a single-project build (one build.gradle) you must apply this before
apply plugin: 'com.android.application'
In multi-project builds (most template projects: app folder, settings.gradle, and many build.gradles) I suggest you apply it around the buildscript block:
buildscript {
// ...
dependencies {
classpath 'com.android.tools.build:gradle:3.0.1'
}
}
// magic line here
Make sure it's before any apply plugin:s, and not inside a buildscript block.
Applying the magic globally
Obviously if it bothers us in one project, it will in all cases, so following Gradle's manual, create a file in ~/.gradle/init.gradle or %USERPROFILE%\.gradle\init.gradle (mind you this folder can be relocated with GRADLE_USER_HOME) with the following contents:
// NB: any changes to this script require a new daemon (`gradlew --stop` or `gradlew --no-daemon <tasks>`)
rootProject { rootProject -> // see https://stackoverflow.com/a/48087543/253468
// listen for lifecycle events on the project's plugins
rootProject.plugins.whenPluginAdded { plugin ->
// check if any Android plugin is being applied (not necessarily just 'com.android.application')
// this plugin is actually exactly for this purpose: to get notified
if (plugin.class.name == 'com.android.build.gradle.api.AndroidBasePlugin') {
logger.info 'Turning off `build/android-profile/profile-*.(rawproto|json)` generation.'
// execute the hack in the context of the buildscript, not in this initscript
new GroovyShell(plugin.class.classLoader).evaluate("""
com.android.build.gradle.internal.profile.ProfilerInitializer.recordingBuildListener =
new com.android.build.gradle.internal.profile.RecordingBuildListener(
com.android.builder.profile.ProcessProfileWriter.get());
""")
}
}
}

Related

What the difference in applying gradle plugin

I don't understand gradle plugins block
apply plugin: 'someplugin1'
apply plugin: 'maven'
and other one:
plugins {
id 'org.hidetake.ssh' version '1.1.2'
}
In first block We have some plugin name. in second one package and version. I don't understand where I should use first block and when second one.
The plugins block is the newer method of applying plugins, and they must be available in the Gradle plugin repository. The apply approach is the older, yet more flexible method of adding a plugin to your build.
The new plugins method does not work in multi-project configurations (subprojects, allprojects), but will work on the build configuration for each child project.
I would think that as functionality progresses, the plugins configuration method will overtake the older approach, but at this point both can be and are used concurrently.
As already mentioned by #cjstehno the apply plugin is a legacy method that you should avoid.
With the introduction of the plugins DSL, users should have little reason to use the legacy method of applying plugins. It is documented here in case a build author cannot use the plugins DSL due to restrictions in how it currently works.
With the new plugins block method, you can add a plugin and control when to apply it using an optional parameter apply:
plugins {
id «plugin id» version «plugin version» [apply «false»]
}
You would still use the legacy method in situations where you want to apply an already added but not applied plugin in your plugins block. E.g, in the master project a plugin xyz is added but not applied and it should be applied only in a subproject subPro:
plugins {
id "xyz" version "1.0.0" apply false
}
subprojects { subproject ->
if (subproject.name == "subPro") {
apply plugin: 'xyz'
}
}
Notice that you don't need the version anymore. The version is required in the plugins block unless you are using one of the Core Gradle plugins, such as java, scala, ...
I spent some time understanding the difference while trying to create a Spring Boot application, and that's why I am answering this again after a while. The following example for using Spring Boot plugin helped me a lot:
What should currently be used:
plugins {
id "org.springframework.boot" version "2.0.1.RELEASE"
}
What had been used before Gradle 2.1:
buildscript {
repositories {
maven {
url "https://plugins.gradle.org/m2/"
}
}
dependencies {
classpath "org.springframework.boot:spring-boot-gradle-plugin:2.0.1.RELEASE"
}
}
apply plugin: "org.springframework.boot"
These are two different ways to use Gradle plugin。
The apply plugin way: First resolve plugin you needed from root build.gradle like:
buildscript {
repositories {
// other repositories...
mavenCentral()
}
dependencies {
// other plugins...
classpath 'com.google.dagger:hilt-android-gradle-plugin:2.44'
}
Then in the build.gradle of your Android Gradle modules apply the plugin:
apply plugin: 'com.android.application'
apply plugin: 'com.google.dagger.hilt.android'
The plugins way:combine resovle and apply where in your root build.gradle like:
plugins {
// other plugins...
id 'com.google.dagger.hilt.android' version '2.44' apply false
}
Then in the build.gradle of your Android Gradle modules apply the plugin:
plugins {
// other plugins...
id 'com.android.application'
id 'com.google.dagger.hilt.android'
}
android {
// ...
}
Now ( In Gradle 6) you can give repositories name for plugins without using build script.
In settings.gradle, we can add plugin pluginManagement
pluginManagement {
repositories {
maven {
url '../maven-repo'
}
gradlePluginPortal()
ivy {
url '../ivy-repo'
}
}
}
Reference: https://docs.gradle.org/current/userguide/plugins.html#sec:custom_plugin_repositories
I would like to point out though, that is it not required for a plugin to be published remotely to be able to use it!
It can also be a UNPUBLISHED locally available plugin (be it convention plugins or otherwise) just as well.
In case one wishes to refer to such an unpublished locally-available plugin,
you'll have to include it's so-called "build" within the desired component/build (identified via the settings.gradle(.kts)-file) like so:
pluginManagement {
includeBuild '<path-to-the-plugin-dir-containing-the-settings-file>'
}
Afther that is done, one may use the local plugin within the plugins {}-DSL-block via its pluginId.
If the plugin needs a version then it's safer to put the version number in the pluginManagement block in your settings.gradle file, rather than in plugins block.
By safer I mean that you won't encounter an error like plugin request for plugin already on the classpath must not include a version. That can happen if you includeFlat a project into another project that uses the same plugin and your plugin versions are in the plugins block.
So rather than:
plugins {
id 'pl.allegro.tech.build.axion-release' version '1.10.3'
}
Do:
plugins {
id 'pl.allegro.tech.build.axion-release'
}
and then in your settings.gradle file:
pluginManagement {
plugins {
id 'pl.allegro.tech.build.axion-release' version '1.10.3'
}
}
I'm going to add a little twist to what's been said. Gradle introduced the concept of a plugins block as a technique to speed up and optimize the build process. Here's what Gradle's documentation says:
This way of adding plugins to a project is much more than a more convenient syntax. The plugins DSL is processed in a way which allows Gradle to determine the plugins in use very early and very quickly. This allows Gradle to do smart things such as:
Optimize the loading and reuse of plugin classes.
Provide editors detailed information about the potential properties and values in the buildscript for editing assistance.
This requires that plugins be specified in a way that Gradle can easily and quickly extract, before executing the rest of the build script. It also requires that the definition of plugins to use be somewhat static.
It's not just a newer way of dealing with plugins, it's a way of improving the build process and/or user's editing experience.
In order for it to work, it needs to be specified at the top of the build, but it also needs to be specified after the buildscript block if one is included. Why is that? Because the code in the build scripts is evaluated in the order its written. The buildscript block must be evaluated before the plugins block is evaluated. Remember, the buildscript block is about setting up of the plugin environment. Hence the rule that the plugins block must be specified after the buildscript block.
The new plugins block not only specifies the plugins that the project is using, but it also specifies whether the plugin is applied. By default, all plugins in the plugins block are automatically applied, unless it is specifically declared not to be applied (i.e., adding "apply false" after the plugin declaration in the plugins block).
So why would you declare a plugin and not apply it. There are two main reasons that I can think of:
1.) so you can declare the version of the plugin you want used. After you've declared a plugin, the plugin is now on the "classpath". Once a plugin is on the classpath you no longer need to specify the version of the plugin when you apply it later. In multiproject builds, that makes supporting buildscripts a little easier. (i.e., you only have one place where the plugin version is specified.)
2.) Sometimes, you may have a plugin, that requires certain things defined before they are applied. In that case, you can declare a plugin in the plugins block, and defer the plugin from being applied until after you define the things that the plugin requires as input. For example, I have a custom plugin that looks for a configuration named "mavenResource". In the dependencies block I'll added a dependency like: "mavenResource(maven_coordinate)". That plugin will find all the dependencies contained in the mavenResource configuration and copy the associated maven artifact to the projects "src/main/resources" directory. As you can see, I don't want to apply that plugin until after the mavenResource configuration is added to that project, and the mavenResource dependencies are defined. Hence, I define my custom plugin the plugins block, and I apply it after the project dependencies have been defined. So, the concept that applying a plugin is old style and wrong is a misconception.
Some of you might wonder what it means to apply a plugin. It's pretty straightforward. It means that you call the plugin's apply function passing it the Gradle Project object for the project where the plugin is being applied. What the plugin does from there on is totally at the discretion of the plugin. Most commonly, the apply function usually creates some Gradle tasks and adds them to the Gradle build task dependency graph. When Gradle starts its execution phase, those tasks will get executed at the appropriate time in the build process. The plugin apply function can also do things like deferring some of it work until afterEvaluate. That's a way to allow other things in the build script to be setup even though they are defined later on in the buildscript. So, you might ask why I didn't do that trick in my custom plugin. What I've observed is that the next subproject starts processing after the root project finishes being evaluated. In my case, I needed the resource added before the next subproject began. So, there was a race condition, that I avoided by not doing the afterEvaluate technique and specifically applying the plugin once the things I needed setup was completed.

Set the project properties in subclassed gradle task

I am defining a gradle task "launchIPad2Simulator" that is subclassing another already defined task "launchIPadSimulatorfrom" in robovm gradle plugin. The goal is to set the project properties which are defining which simulator will run.
// Run the IPad2 simulator
task launchIPad2Simulator2(type: org.robovm.gradle.tasks.IPadSimulatorTask) {
project.setProperty("robovm.device.name", "iPad-2")
project.setProperty("robovm.arch", "x86")
}
But the problem is, I must first define the properties in the gradle.properties file. They don't even need to have any value assigned. The whole content of the gradle.properties file:
robovm.device.name
robovm.arch
I would rather have gradle.properties file empty, but if the above task is then run, the error: Error:(112, 0) No such property: robovm.device.name for class: org.gradle.api.internal.project.DefaultProject_Decorated is shown.
Also if properties are only defined in task as following (gradle.properties is empty), they are ignored.
// Run the IPad2 simulator
task launchIPad2Simulator2(type: org.robovm.gradle.tasks.IPadSimulatorTask) {
project.properties.put("robovm.device.name", "iPad-2")
project.properties.put("robovm.arch", "x86")
}
So what is the correct way to dynamically set the project properties in subclassed task?
=== Edit ===
Ok now I see that setting the project properties is also not good, because in multiple tasks it gets overwritten. So maybe this shouldn't be project properties in first place.
The temp solution now is using command line invocation of tasks:
// simulator with properties launched from command line
task launchIPad2Simulator1(type: Exec) {
commandLine 'gradle', '-Probovm.device.name=iPad-2', '-Probovm.arch=x86', 'launchIPadSimulator'
}
try
task launchIPad2Simulator2(type: org.robovm.gradle.tasks.IPadSimulatorTask) {
project.ext."robovm.device.name" = "iPad-2"
project.ext."robovm.arch" = "x86"
}
this is the gradle syntax to add dynamic properites to the project object.

Android Studio -- clear application data for Instrumentation Test

How can I get Android Studio (AndroidJunitRunner) to clear application data preceding an instrumentation test without manually running adb command?
I discovered that android.support.test.runner.AndroidJUnitRunner kind of cheats -- it never actually invokes connectedCheck or connectedAndroidTest.
When run from command line $ gradle connectedCheck
:MyMainApp:assembleDebug UP-TO-DATE
:MyMainApp:assembleDebugTest UP-TO-DATE
:MyMainApp:clearMainAppData
:MyMainApp:connectedCheck
When run from within IDE by clicking the instrumentation test configuration (green Android robot logo with red/green arrows)
**Executing tasks: [:MyMainAppApp:assembleDebug, :MyMainAppApp:assembleDebugTest]**
As you can see, the last gradle target is assembleDebugTest
I had added a hook onto connectedCheck in build.gradle to clear the data of the main app before starting the instrumentation test.
// Run 'adb' shell command to clear application data of main app for 'debug' variant
task clearMainAppData(type: Exec) {
// we have to iterate to find the 'debug' variant to obtain a variant reference
android.applicationVariants.all { variant ->
if (variant.name.equals("debug")) {
def clearDataCommand = ['adb', 'shell', 'pm', 'clear', getPackageName(variant)]
println "Clearing application data of ${variant.name} variant: [${clearDataCommand}]"
commandLine clearDataCommand
}
}
}
// Clear Application Data (once) before running instrumentation test
tasks.whenTaskAdded { task ->
// Both of these targets are equivalent today, although in future connectedCheck
// will also include connectedUiAutomatorTest (not implemented yet)
if(task.name.equals("connectedAndroidTest") || task.name.equals("connectedCheck" )){
task.dependsOn(clearMainAppData)
}
}
I realize that alternatively I could implement a 'clear data' button in the main app and have the instrumentation app click through the UI, but I find that solution undesirable.
I looked at AndroidJUnitRunner API and there are hooks via Runlistener interface but hooks are during the context of the test app, i.e. running on device, and Android forbids one app from modifying another app.
http://junit.sourceforge.net/javadoc/org/junit/runner/notification/RunListener.html
Best answer goes to you if you can help me trigger one of the following automatically from within Android Studio:
execute a command line adb shell pm clear my.main.app.package,
or preferably invoke my gradle task clearMainAppData
I'm also all ears if there is an alternate way. Surely with device testing automation there should be a clear way to clear application data?
Thank you!
I know it's been a while, and hopefully by now you will have this issue sorted.
I ran into that same issue today, and crashed here without any solution.
But I managed to make it work by calling my task from the test configuration.
Step 1 : Go to your test configuration
Step 2 : Simply add the gradle task you created
By the way, the task in my case simply looks like this :
task clearData(type: Exec) {
def clearDataCommand = ['adb', 'shell', 'pm', 'clear', 'com.your.application']
commandLine clearDataCommand
}
Hope this will help someone :)
With Android Test Orchestrator it is easier to provide this option via gradle script.
android {
defaultConfig {
...
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
// The following argument makes the Android Test Orchestrator run its
// "pm clear" command after each test invocation. This command ensures
// that the app's state is completely cleared between tests.
testInstrumentationRunnerArguments clearPackageData: 'true'
}
Below is the link for Android Test Orchestrator
https://developer.android.com/training/testing/junit-runner#using-android-test-orchestrator

Gradle Incremental Tasks: Adding already generated code to the classpath

I have created a custom Gradle task that generates some java code. To optimize execution this plugin uses the #InputDirectory and #OutputDirectory annotations, so that the code does not have to be generated each build.
However, I do want this task to add the generated code to the classpath. I am currently doing this by
class JaxbTask extends DefaultTask {
#OutputDirectory
File destdir = project.file( "${project.buildDir}/generated-sources/mygen" )
#InputDirectory
File schemaRoot = project.file("${project.projectDir}/src/main/resources/myschema/")
#TaskAction
def main() {
..
project.sourceSets.main.java.srcDirs += destdir
..
}
The problem is that the TaskAction is not executed and the sourcedirectory is not added to the compile path when the generated code is up to date. Is there any way to make sure that the modification of the sourcepath is always performed?
A task should never try to configure the build model. Configuration is the responsibility of build scripts and plugins, and needs to happen in the configuration phase (before any task has run).

How to refer to the values to be declared in build.gradle

I'm totally new to this gradle, teamcity and groovy.
I'm tryign to write a plugin,which will get the value from svninfo. If the developer wants to override the value(in build.gradle) they can override something like this.
globalVariables{
virtualRepo = "virtualRepo"
baseName = "baseName"
version = "version"
group = "group"
}
Here i provide the extension called globalvariable.
Now, The jars to be produced shd hav the name according to the values from the build.gradle..
How to get the value from build.gradle in the plugin inorder name the jar???
Not sure I understand the question. It's the plugin that installs the extension object, and it's the plugin that needs to do something with it.
Note that the plugin has to defer reading information from the extension object because the latter might only get populated after the plugin has run. (A plugin runs when apply plugin: is called in a build script. globalVariables {} might come after that.) There are several techniques for deferring configuration. In your particular case, if I wanted to use the information provided by the extension object to configure a Jar task, I might use jar.doFirst { ... } or gradle.projectsEvaluated { jar. ... }.
Before you go about writing plugins, make sure to study the Writing Custom Plugins chapter in the Gradle user guide. A search on Stack Overflow or on http://forums.gradle.org should turn up more information on techniques for deferring configuration. Another valuable source of information is the Gradle codebase (e.g. the plugins in the code-quality subproject).

Resources