Set the project properties in subclassed gradle task - groovy

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.

Related

How can custom compilation parameters be synchronized through the Android studio project?

Some parameters in my project need to be passed in at compile time.
So there is such a fragment in my build.gradle file:
assemble {
description 'Check the necessary parameters and terminate compilation if they do not exist.'
if (!project.hasProperty('customArgs')) {
throw new RuntimeException("Parameter customArgs is not defined")
}
}
When I compile, I will use the command gradle build -PcustomArgs=1234
Everything was fine until I imported the project into Android studio.
Gradle project sync is always fails.
That is to say, in the process of project synchronization, I can't find any place where I can set parameters.
In other words, how to set the parameters of sync in Android Studio?
You should move this custom behavior from the configuration phase to the execution phase. Otherwise Android Studio will execute it when configuring you project.
assemble {
doLast {
description 'Check the necessary parameters and terminate compilation if they do not exist.'
if (!project.hasProperty('customArgs')) {
throw new RuntimeException("Parameter customArgs is not defined")
}
}
}
More informations here

How to prevent rawproto file generation or delete them automatically?

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());
""")
}
}
}

How can ConfigSlurper's groovy file can call methods defined in build.gradle?

I am working on project whose build logic is defined in gradle and has a build.gradle file for it. Now, we would like to manage property of the project using groovy's config Sluper. And, I have placed config.groovy file in folder that contains all the Helper class for the project. The content of the conf.groovy file is below:
categories {
includeCategories = defaultIncludeCategories()
excludeCategories = defaultExcludeCategories()
}
String defaultIncludeCategories() {
def include = 'default'
if( isAbcJob() ) {
include = 'tier0'
}
logger.info "defaultIncludeCategories: $include"
include
}
Now, as you can see there is a method name isAbcJob() that I need to use in the configuration file but this method is present in build.gradle, which is the file that call conf.groovy file for property management.
Bottom line is how would conf.groovy script would know where isAbcJob() method is?
Please advice.
I found the following question that has been asked on Gradle forums very helpful. And, it looks like that accessing other gradle script's methods is not possible unless you access them through tasks:
https://discuss.gradle.org/t/multiple-apply-froms-dont-seem-to-import-all-methods/2274/6

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

Resources