I'm trying to deal with annotation processors. I followed tutorials. Here is my code:
#ExampleAnnotation
private void someMethod(){
System.out.println("hi");
}
#Retention(RetentionPolicy.SOURCE)
#Target(ElementType.METHOD)
public #interface ExampleAnnotation {
}
#SupportedAnnotationTypes("org.example.ExampleAnnotation")
public class Processor extends AbstractProcessor {
#Override
public boolean process(Set<? extends TypeElement> anots, RoundEnvironment roundEnvironment) {
anots.forEach(System.out::println);
return true;
}
}
I created META-INF/SERVICES/javax.annotation.processing.Processor
and registered my processor: org.example.Processor. It seems like everything is OK, but block of code in the processor just dont start. I have no idea what is wrong. P.S.: I use Gradle and Java 11.
i fixed my issue and decided to make a little step-by-step tutorial to create simple JAP:
Java-Annotation-Processor-guide
This is guide for annotation processing using gradle
I spent 3 days trying to deal with it, but with this little tutorial you will made JAP in 5 minutes.
sorry for bad english :)
So, first you should do is create gradle subproject for your project:
create project itself with gradle init or intellij
add to your main project's settings.gradle file this line include "*your subproject name, lets say:*annotation-processor"
Congrats, now let go to the processor itself
here is so much tutorials on the web about this part, so you can read something like this https://www.baeldung.com/java-annotation-processing-builde,
or this (Если ты русский): https://habr.com/ru/company/e-legion/blog/206208/
Final step is very easy - you will add your annotation processor to main project.
!!! instead of annotation-processor you should use your subproject name !!!
kotlin (or java + kotlin) -> {
add this plugin to your plugins: id "org.jetbrains.kotlin.kapt" version "1.7.21"
add this to your dependencies:
kapt project('annotation-processor')
compileOnly project('annotation-processor')
}
java -> {
add this to your dependencies:
annotationProcessor project('annotation-processor')
compileOnly project('annotation-processor')
}
(or you can read this on github: https://github.com/Blu3cr0ss/Java-Annotation-Processor-guide)
Related
I'm writing a custom gradle plugin in which I want to have a bunch of common for several of my projects tasks and a sort of a 'main' task to control which of these tasks to turn on.
Regular tasks in the plugin are e.g.:
CopyDockerResourcesTask
CopyContainerFilesTask
PerformAnalysisTask
and the 'main' task is:
BaseProjectTask
so then in the project in build.gradle I'd like to be able to do this:
BaseProjectTask {
copyDockerResources = true
copyContainerFiles = true
performAnalysis = true
}
I want the default behaviour of the plugin to be to not to do anything, only add certain tasks if they are turned on in BaseProjectTask.
I wanted to achieve this with adding task dependency in #TaskAction method of BaseProjectTask:
class BaseProjectTask extends DefaultTask {
private final BaseProjectExtension extension
private final Project project
#Optional
#Input
Boolean copyContainerFiles = false
...
#Inject
BaseProjectTask(Project project, BaseProjectExtension extension) {
this.project = project
this.extension = extension
}
#TaskAction
def execute() {
if (copyContainerFiles) {
project.tasks.assemble.dependsOn(project.tasks.copyContainerFiles)
}
...
}
}
Creating task dependency, this line:
project.tasks.assemble.dependsOn(project.tasks.copyContainerFiles)
doesn't work.
Edit:
My current findings are that defining task dependency in #TaskAction is too late as this is execution phase. I could do it in the constructor (this way it works) but its too early as property copyContainerFiles isn't set yet.
Does anyone know a way of adding code in the task class that would be fired in the configuration phase? I think this is what I'm missing.
You need to configure task dependencies during the build configuration phase, as you surmised.
It's not possible to do it in the #TaskAction method. It's fundamental to the way Gradle works that it needs to know how tasks depend on each other before it starts executing the build. That allows Gradle to do some useful things, such as only executing the tasks that are not up to date, or working out what tasks will execute without actually executing them.
In general, tasks should not be aware of one another1.
When you are trying to do this in a plugin using values in a project extension, you must wait until after the project has evaluated so that the build script code executes first. You can do this with project.afterEvaluate()2.
So you can do the following (using Kotlin DSL3):
project.afterEvaluate {
tasks.register("baseTask") {
if (baseProjectExtension.copyDockerResources)
dependsOn(tasks.getByName("copyDockerResources"))
if (baseProjectExtension.copyContainerFiles)
dependsOn(tasks.getByName("copyContainerFiles"))
if (baseProjectExtension.performAnalysis)
dependsOn(tasks.getByName("performAnalysis"))
}
}
1See How to declare dependencies of a Gradle custom task?
2See https://docs.gradle.org/current/userguide/build_lifecycle.html#sec:project_evaluation
3What I am familiar with. Hopefully not too much trouble to convert to Groovy.
Let's say we have the following test code:
import org.jetbrains.annotations.NotNull;
import org.junit.Test;
public class NullTest {
#Test
public void testNull() {
doNothing(null);
}
private #NotNull String doNothing(#NotNull String value) {
return value;
}
}
The test will pass when running gradle test directly or if IDEA delegates the action to gradle.
But it will fail with IllegalArgumentException: Argument for #NotNull parameter 'value' must not be null exception if it runs using IDEA runner (not delegated to gradle).
The question is: how to fail the test running it with gradle?
The easiest solution I have found is to apply org.jetbrains.intellij plugin.
Because among other things this plugin "patches compile tasks to instrument code with nullability assertions".
apply plugin: 'org.jetbrains.intellij'
intellij {
instrumentCode = true
downloadSources = false
}
Try adding the following to your dependencies. It worked for me.
compile 'org.jetbrains:annotations:13.0'
With this code - no way, because you use annotations from org.jetbrains.annotations.*, that use only in intellij idea tests runner. For gradle, annotation #NotNull (or #Nullable) says nothing. Maven also doesn't see this annotation. I can advise you use Objects.requireNonNull(T obj) for null checking.
We found that Lombok's #NonNull works better. But you need to configure IDEA to prefer this one during nullability-related analysis and generation
Adding the following dependency worked for me:
compile group: 'org.jetbrains', name: 'annotations', version: '15.0'
Run the 'dependencies' task & push the refresh button in Gradle.
I want to develop a Groovy AST transformation to add some methods on certain classes. So I write an annotation class and corresponding transformation class. Then I annotate a java class with my Groovy AST annotation.
When I compile the java annotated class with embedded groovy compiler (for example by this snippet: Class enhancedClass = new GroovyClassLoader().parseClass(new File("..."));), the transformation is performed and methods are added to the compiled class which is called enhancedClass in the snippet.
But I cann't compile the java class with Gradle groovy plugin and Intellij IDEA correctly.
QUESTION: Can everyone help me to working with Groovy AST transformation in Gradle and Intellij IDEA?
NOTE 1: I use Intellij IDEA 14 ultimate edition.
NOTE 2:
My Groovy AST classes and the java annotated class and my "build.gradle" file are somethings like the followings:
Annotation class:
#Retention(RetentionPolicy.SOURCE)
#Target(ElementType.TYPE)
#GroovyASTTransformationClass(classes = {MyASTTransformation.class})
public #interface MyAST {
}
and Transformation class:
#CompileStatic
#GroovyASTTransformation(phase = CompilePhase.SEMANTIC_ANALYSIS)
public class MyASTTransformation implements ASTTransformation {
#Override
public void visit(ASTNode[] nodes, SourceUnit sourceUnit) {
...
}
}
The java annotated class:
#MyAST
public class A {
...
}
The "build.gradle" file:
apply plugin: 'groovy'
sourceSets {
main {
groovy {
srcDirs = ['src/main/groovy', 'src/main/java']
}
java {
srcDirs = []
}
}
}
dependencies {
compile 'org.codehaus.groovy:groovy-all:2.4.4'
}
I created my first custom AST transformation and I also ran into similar issues so sharing my limited experience. I'm using Groovy 2.4.7 and IntelliJ IDEA 2016.3.2. I was only able to use the custom transform annotation if:
It was created as a separate artifact (project) and another project that was using the annotation referenced it.
Ran a Groovy test script in the same project as the custom AST transformation code that ran a GroovyShell with Groovy code that used the custom annotation.
I had first attempted doing it in the same project but that didn't work. I believe it's because the AST transformation occurs at compile time. Here's my example:
AST transformation project
Annotation
#Retention (RetentionPolicy.SOURCE)
#Target ([ElementType.TYPE, ElementType.METHOD])
#GroovyASTTransformationClass (classes = [SqlAssistTransform])
#interface SqlAssist {
}
Transformation
#GroovyASTTransformation(phase=CompilePhase.SEMANTIC_ANALYSIS)
class SqlAssistTransform extends AbstractASTTransformation {
...
}
settings.gradle
rootProject.name = 'groovy-sql-transform'
build.gradle
...
group = 'com.company.groovy.transform'
version = '1.0-groovy-2.4'
description = 'Groovy AST transformation for SQL syntax'
...
Project using AST annotation
Annotated class
#SqlAssist
class Something {
...
}
Dependency example #1 - dependency on AST artifact from repository
build.gradle
...
dependencies {
...
// As long as your local repository (or remote repository) has the AST transformation project installed. i.e. Run '.\gradlew install' in AST project to install to local repository
compile group:'com.company.groovy.transform', name:'groovy-sql-transform', version: '1.0.0-groovy-2-4'
...
}
...
Dependency example #2 - dependency on AST project
build.gradle
...
dependencies {
...
compile project(':groovy-sql-transform')
...
}
...
settings.gradle
rootProject.name = 'project-name'
// Assumption that AST project lives at the same level as this project
include "groovy-sql-transform"
project(":groovy-sql-transform").projectDir = new File("../groovy-sql-transform")
Testing
I didn't create a proper unit test, but I did use the following to test and debugged in IntelliJ so I could look at the AST in different places in my AST transformation code. Example test Groovy script that I had within the AST transformation project:
new GroovyShell(getClass().classLoader).evaluate '''
import com.company.groovy.transform.SqlAssist
#SqlAssist
class Testing {
...
}
'''
Hi I need to write Junit tests for an Android project but it has JNI methods as it uses webkit.Is there any way I can test those android methods(I dont want to test JNI methods).
Its like:
public void androidMethod(){
//some android code
nativeInit(); //how do I mock such methods?
//some code again
}
I have tried powermock,easymock,roboelectric but wasnt successful.Please help me.
I yesterday found I could solve this with Mockito (I didn't try powermock or easymock). Assuming your class is class C, my solution is:
C c=spy(new C);
doNothing().when(c).nativeInit();
c.androidMethod()
verify(c).nativeInit();
This does, of course, require that nativeInit is visible to the test.
Similar Problem
I had the same problem event though I was already using mockito in JUnit tests under src/test. Once I added tests under src/androidTest I started having issues, including this crash:
Mockito cannot mock/spy because :
- final class
And after making the class open, manually, I still got crashes in the JNI layer as it tried to load the *.so library (which wouldn't happen if mocks were working properly).
Working Solution
Instead, what I had to do was open the class for testing purposes using Kotlin's all-open plugin. The process is also explained well in this recent medium post but it boils down to the following four simple changes that are also modeled in one of the architecture components sample apps:
1. Make these additions to build.gradle:
buildscript {
dependencies {
classpath "org.jetbrains.kotlin:kotlin-allopen:${versions.kotlin}"
}
}
apply plugin: "kotlin-allopen"
allOpen {
// marker for classes that we want to be able to extend in debug builds
annotation 'com.your.package.name.OpenClass'
}
2. Add the corresponding annotations in the debug flavor. For example: app/src/debug/java/com/your/package/name/OpenForTesting.kt
package com.your.package.name
#Target(AnnotationTarget.ANNOTATION_CLASS)
annotation class OpenClass
#OpenClass
#Target(AnnotationTarget.CLASS)
annotation class OpenForTesting
3. Add the corresponding annotation in the release flavor. For example: app/src/release/java/com/your/package/name/OpenForTesting.kt
package com.your.package.name
#Target(AnnotationTarget.CLASS)
annotation class OpenForTesting
4. Add the #OpenForTesting annotation to the class that needs to be mocked
package com.your.package.name
#OpenForTesting
class JniClassOfVictory {
...
external fun nativeInit()
...
companion object {
init {
System.loadLibrary("victoryeveryday")
}
}
}
The result is a flexible way to mark classes as open without actually making them open in release builds. Of course, this is because the #OpenForTesting annotation that we created in release is not marked with #OpenClass but the same annotation in debug is marked with #OpenClass. In build.gradle we designated that annotation as the signal to the kotlin-allopen plugin. So any class annotated with #OpenForTesting will be made open at compile-time but only on Debug builds.
I'm reading up on Gradle and am very interested in it, specifically because (it appears) that it allows the introduction of inheritance into the build process. For instance, if I have a Java web app that might be packaged and deployed to Google App Engine instances as well as Amazon EC2 instances, I need a sophisticated build that can take the same Java, XML, PROPERTIES, CSS and image files and package/deploy them into 2 drastically-differently packaged WAR files.
GAE apps are very specific as to how they are packaged; EC2 (pretty much) just require that you conform to servlet specs. GAE apps get "deployed" by running an update command from the appcfg.sh script that comes with your SDK; EC2 has their own way to deploy apps. The point is, they are very different packaging/deployment processes for both PaaS providers:
public abstract class PackageTask {
// ...
}
// Package my Eclipse project for deployment to GAE.
public class AppEnginePackageTask extends PackageTask {
// ...
}
// Package my Eclipse project for deployment to EC2 instances.
public class AmazonPackageTask extends PackageTask {
// ...
}
public abstract class DeployTask {
// ...
}
// Deployment to GAE.
public class AppEngineDeployTask extends DeployTask {
// ...
}
// Deployment to EC2.
public class AmazonDeployTask extends DeployTask {
// ...
}
Then, I might have a myapp.gradle buildfile that templates the build order of tasks:
clean()
compile()
package()
deploy()
...and somehow, I can configure/inject AppEnginePackageTask/AppEngineDeployTask in place of package()/deploy() for a GAE-based build, or I can configure/inject AmazonPackageTask/AmazoneDeployTask into those templated tasks. Again, I'm not sure how to do this (or even if Gradle can do this), but it's what I'm after.
My understanding was that Gradle can do this. Ant can also be forced to have highly-modular, elegant builds that work this way, but being XML-based, it takes some finessing, whereas an OOP-based language like Groovy makes this cleaner and simpler.
However, all the examples I see of Gradle tasks take the following form:
task package(dependsOn: 'compile') {
// ...
}
task deploy(dependsOn: 'package') {
// ...
}
So I ask: these look/feel like non-OOP task definitions. Is my understanding of Gradle (and its OOP nature) fundamentally incorrect? What am I missing here? How can I accomplish these notions of "configurable/injectable build templates" and inheritance-based tasks? Thanks in advance!
Edit I re-tagged this question with "groovy" because Gradle buildscripts are written in a Groovy DSL, and someone who happens to be a Groovy-guru (say that 5 times fast) might also be able to chime in even if they know little about Gradle.
As described here, there are simple tasks and enhanced tasks. The latter are much more flexible and powerful.
The following example isn't exactly what you describe, re:injection, but it illustrates OOP.
Here is the sample build.gradle file. It avoids "package" as that is a keyword in Java/Groovy. The 'build' target depends on 'compile' and some flavour of 'doPack', depending on a property called 'pkgTarget'.
task compile << {
println "compiling..."
}
task build() << {
}
build.dependsOn {
compile
}
build.dependsOn {
if (pkgTarget == "Amazon") {
task doPack(type: AmazonPackageTask)
} else if (pkgTarget == "Google") {
task doPack(type: GooglePackageTask)
} else {
task doPack(type: MyPackageTask)
}
}
where the tasks are defined later in the same file. (Per doc, this code can go into a "build src" directory):
// -----
class MyPackageTask extends DefaultTask {
def init() { println 'common stuff' }
#TaskAction
def doPackage() {
println 'hello from MyPackageTask'
}
}
class AmazonPackageTask extends MyPackageTask {
#TaskAction
def doPackage() {
init()
println 'hello from AmazonPackageTask'
}
}
class GooglePackageTask extends MyPackageTask {
#TaskAction
def doPackage() {
init()
println 'hello from GooglePackageTask'
}
}
and here is the gradle.properties file:
pkgTarget=Amazon