Get a PsiFile inside a Gradle plugin task - android-studio

I am working on a Gradle plugin that will/should inspect some code using Psi (https://www.jetbrains.org/intellij/sdk/docs/basics/architectural_overview/psi.html). I know how to use Psi if it was an IntelliJ plugin, but I don't know where to start in the context of a Gradle task.
The Gradle task API would look like this:
generateRibStateTree {
rootRibBuilderFile = file("$rootDir/app/src/main/java/com/coca/jorge/root/RootBuilder.kt")
reportDirectoryFile = file("$rootDir/reports") // your desired directory here
}
The goal is to receive the rooRibBuilderFile in a java.io.File object, but then I do not know how to start parsing that file with Psi. Any ideas?

Related

Gradle ant build file not not available during configuration phase

I can't figure out how to execute ant target in case that ant build.xml file is not available during configuration phase. Because it's a remote resource (Maven Remote Resources Plugin).
So basically first I need to get this remote resource like this:
configurations {
remoteResources
}
dependencies.remoteResources 'group:my-remote-resource:version'
task getRemoteResources(type: Copy) {
from(zipTree(configurations.remoteResources.first()))
into("$buildDir/remote-resources")
// replace placeholders
filter(org.apache.tools.ant.filters.ReplaceTokens, , tokens: remotePlaceholders)
}
Only then I have build.xml in
"$buildDir/remote-resources"
But I can't use ant.importBuild as that expects build.xml to be available during the configuration, which is not my case.
I was thinking to move the remote resource "download" into initialization phase, but I have a multi-module project and although only some sub-projects are using this remote resource they all has it own placeholders to replace.
Is there any way how to execute ant targets in this special case?
EDIT: I found pretty nice solution utilising Ant's ProjectHelper and Project classes. So I guess that is my answer..
So here is the final solution. (as already mentioned credits go to groovy-almanac)
import org.apache.tools.ant.Project;
import org.apache.tools.ant.ProjectHelper;
task executeAntTarget(dependsOn: getRemoteResources) << {
def antFile = new File("$buildDir/remote-resources/build.xml")
def antProject = new Project()
antProject.init()
ProjectHelper.projectHelper.parse(antProject, antFile)
antProject.executeTarget('<target-to-execute>');
}

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

How do I run a task created by a plugin in gradle

I've written a gradle plugin that adds a custom task called generateTestDocs, which depends on the task groovydoc, which itself is created by the groovy plugin.
//MyPlugin.groovy
#Override
void apply(Project project) {
project.apply(plugin: 'groovy')
project.task(type: GenerateTestDocsTask, dependsOn: ':groovydoc', 'generateTestDocs')
}
project.tasks.groovydoc.doFirst {
println "I should see this message but I don't"
}
I'm trying to test this plugin by running the task generateTestDocs
#Test
void testRunGenerateTestDocs() {
Project project = ProjectBuilder.builder().build()
project.apply(plugin: 'my.gradle.plugin')
project.tasks.generateTestDocs.actions*.execute(project.tasks.generateTestDocs)
}
For the last line in my test, I'd like to instead just say project.task.generateTestDocs.execute() and have it run the task with all of its dependencies, but that doesn't seem to work. The documentation for writing gradle plugins only shows assertions like assertTrue(project.tasks.hello instanceof GreetingTask) which shows the task is added to the project, but doesn't show how to run that task.
ProjectBuilder is only meant for unit tests. To run a build as part of a test, you'll need to use the Gradle tooling API, or a third-party plugin such as nebula-test (which builds upon the tooling API).
I faced the same question, I solved this by add
apply plugin: CustomPluginName
in current gradle.build :
apply plugin: 'groovy'
...
// in this case Plugin name is MyPlugin
apply plugin: MyPlugin

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