I have a custom annotation processor (that extends AbstractProcessor) which adds a properties file to the project based on the annotations. I want this to be run everytime when a compilation is happening. The project is a java project using gradle.
How do I get the annotation processor run during compile time? Should I use some compiler plugin? or should I write a simple gradle task that can invoke this annotation processor and make that task part of the compilation task? (I'm a beginner with gradle)
In the META-INF/services, added the entry for javax.annotation.processing.Processor specifying the custom annotation processor class.
I know that this question is quite old but since it even got a favor and nobody answered it I want to give at least a little answer for future readers.
For this are multiple ways possible depending on the way your environment is set up.
For example you can use something like this in build.gradle or some other .gradle file that is used by all wanted projects:
compileJava{
options.fork = false
options.forkOptions.executable = 'javac'
options.compilerArgs.addAll(['-classpath','path/to/your/compiled/processor.jar'])
}
when you use the dependency system you could use this:
dependencies {
compileClasspath group: 'com.company', name: 'AnnotationProcessor', version: 'your revision'
//or this
compileClasspath 'com.company.AnnotationProcessor:revision'
}
but be sure to have the .Processor file in src/main/resources/META-INF/services for this method. Else you would have to add the compilerArg '-processor','full.package.name.with.class.name' <-- this could be wrong since I never tried that way.
The only way I could get annotations to work was via setting the -processorpath directly.
compileJava{
options.compilerArgs.addAll(['-processorpath',"$rootDir/yoursubproject/annotation.jar"])
options.compilerArgs.addAll(['-Acom.crd.whomever.processors.pass=Production'])
options.compilerArgs.addAll(['-XprintRounds'])
options.compilerArgs.addAll(['-XprintProcessorInfo'])
}
Related
I have written a simple Annotation Processor (just for fun) that will generate some boilerplate code that I have been writing in my previous project. It actually generates a module like following by collecting annotations on Activity classes
#Module
abstract class ActivityInjectorModule {
#ContributesAndroidInjector
abstract fun providesMain2Activity(): Main2Activity
#ContributesAndroidInjector
abstract fun providesMainActivity(): MainActivity
}
However, when I run it with dagger, dagger can't seem to find classes generated by my annotation processor. Although, class is generated and present in generated directory, I can use it in my source code but on compilation, dagger produces the following exception. Any expert suggestion?
error: cannot find symbol
#dagger.Component(modules = {dagger.android.AndroidInjectionModule.class, com.mallaudin.daggietest.di.AppModule.class, ActivityInjectorModule.class})
^
symbol: class ActivityInjectorModule
This is the main app component.
#Singleton
#Component(
modules = [
AndroidInjectionModule::class,
AppModule::class,
ActivityInjectorModule::class
]
)
interface AppComponent : AndroidInjector<App> {
#Component.Builder
interface Builder {
fun addContext(#BindsInstance ctx: Context): Builder
fun build(): AppComponent
}
}
ActivityInjectorModule class is generated by annotation processor and exists in the generated directory.
Application class
class App : DaggerApplication() {
override fun applicationInjector(): AndroidInjector<out DaggerApplication> {
return DaggerAppComponent.builder().addContext(this).build()
}
}
Everything works perfectly, if I create the generated class myself.
Somehow on compile time, dagger is unable to find the class when generated by my annotation processor.
After Yuriy Kulikov's answer,
You can see generated file is in the same package but also referenced with fully qualified name. Still dagger reports errors.
Here is the link to github repository if someone wants to experiment
Solution:
Generate java code. Kapt does not support multiple rounds.
Write generated files on earliest possible round.
Explanation:
Javac annotation processor uses rounds instead of defining processors order. So normally the simplified algorithm is like that:
Gather all java sources
Run all annotation processors. Any annotation processor can generate new files using Filer.
Gather all generated files and if there are any, run step 2 again.
If there are no files generated, run one more round where RoundEnvironment.processingOver() returns true, signaling this is the last round.
Here is a pretty good explanation of the process
Now a bit about kapt. Kapt uses javac to run annotation processors. To make it possible, it runs kotlin compliler first to generate java stub files and runs javac on them. Currently kapt does not support multiple rounds, meaning it does not generate java stubs for kotlin classes, generated by annotation processors.
Note: javac still uses multiple rounds, it just can't pick up generated kotlin sources.
So, back to your question. One possible option is to move your generated classes into a separate module like it's described here.
But the easiest option is to generate java code directly and your generated java classes will be picked up by javac automatically, launching second round of annotation processing, where dagger will process them.
Just a few more notes:
Do not generate your code when RoundEnvironment.processingOver() == true, it will not trigger another round. Generate it during the same round you see the annotation.
To make the generated code visible to annotation processor, write it using Filer.
New answer
I have somehow missed that you are using kapt. Kapt can process your classes, even without full qualified name (which is remarkable) if you add this to your build.gradle:
kapt {
arguments {
arg("argumentIncremental", 'true')
}
correctErrorTypes = true
}
More info about this: https://kotlinlang.org/docs/reference/kapt.html#non-existent-type-correction
Previous answer can be useful is someone has the same issue with annotationProcessor (apt) in gradle.
Short answer: use fully qualified name for ActivityInjectorModule:
#dagger.Component(modules = {dagger.android.AndroidInjectionModule.class, com.mallaudin.daggietest.di.AppModule.class, com.mallaudin.daggietest.di.ActivityInjectorModule.class})
Alternatively put both files in the same package.
Long answer: Dagger is an annotation processor, it runs before your code is compiled and (potentially) before your other annotation processor runs. The sequence in which processors run is not defined.
Dagger annotation processor will process the TypeElement annotated with #dagger.Component and it will try to find all modules including the "ActivityInjectorModule.class". The thing is, ActivityInjectorModule might not have been generated yet. Therefore "ActivityInjectorModule" will not have a package at this point. Dagger will assume that ActivityInjectorModule resides in the same package as the Component class and will not add an import. The usual workaround for this is to use full-qualified names for generated classes, if they are used by other annotation processors. Sometimes it makes sense to move annotation processing to a difference gradle module, but I don't this that this is what you want.
There may be a more elegant way to solve this, but the simplest and most reliable solution is to do two passes with javac—once to run just your annotation processor, and the second to do everything it normally does.
The javac documentation specifies two options which should help you out.
-proc: {none,only}
Controls whether annotation processing and/or compilation is done. -proc:none means that compilation takes place without annotation processing. -proc:only means that only annotation processing is done, without any subsequent compilation.
-processor class1[,class2,class3...]
Names of the annotation processors to run. This bypasses the default discovery process.
The first pass (to run only your own annotation processor) is
javac -proc:only -processor com.foo.bar.MyProcessor MyProject/src/*
and the second pass (a regular build) is
javac MyProject/src/*
If you’re using something like Ant or Maven, you should be able to update the build instructions to have two compiler passes with only a minimal amount of effort.
Edit: here’s my attempt at Gradle instructions
I have no experience with Gradle, but it seems like you need to do something like this.
In your Gradle build script, you need to define the preprocessing task and add a dependency on your task to the javaCompile task.
javaCompile.dependsOn myAnnotationTask
task myAnnotationTask(type: JavaCompile) {
options.compilerArgs << '-proc:only' << '-processors com.foo.bar.MyAnnotationProcessor'
}
I know there are a lot of questions that seem similar. I have also spent a few hours getting to grips with Gradle multiprojects. But I still don't understand what the best course of action is here. Incidentally I am using Groovy as my coding language, but explanations referencing Java would be just as good.
I have developed an Eclipse Gradle project, "ProjectA", which in particular has a class, IndexManager, which is responsible for creating and opening and querying Lucene indices.
Now I am developing a new Eclipse Gradle project, "ProjectB", which would like to use the IndexManager class from ProjectA.
This doesn't really mean that I would like both projects to be part of a multiproject. I don't want to compile the latest version of ProjectA each time I compile ProjectB - instead I would like ProjectB to be dependent on a specific version of ProjectA's IndexManager. With the option of upgrading to a new version at some future point. I.e. much as with the sorts of dependencies you get from Maven or JCenter...
Both projects have the application plugin, so ProjectA produces an executable .jar file whose name incorporates the version. But currently this contains only the .class files, the resource files, and a file called MANIFEST.MF containing the line "Manifest-Version: 1.0". Obviously it doesn't contain any of the dependencies (e.g. Lucene jar files) needed by the .class files.
The application plugin also lets you produce a runnable distribution: this consists of an executable file (2 in fact, one for *nix/Cygwin, one for Windows), but also all the .jar dependencies needed to run it.
Could someone explain how I might accomplish the task of packaging up this class, IndexManager (or alternatively all the classes in ProjectA possibly), and then including it in my dependencies clause of ProjectB's build.gradle... and then using it in a given file (Groovy or Java) of ProjectB?
Or point to some tutorial about the best course of action?
One possible answer to this which I seem to have found, but find a bit unsatisfactory, appears to be to take the class which is to be used by multiple projects, here IndexManager, and put it in a Gradle project which is specifically designed to be a Groovy library. To this end, you can kick it off by creating the project directory and then:
$ gradle init --type groovy-library
... possible to do from the Cygwin prompt, but not from within Eclipse as far as I know. So you then have to import it into Eclipse. build.gradle in this library project then has to include the dependencies needed by IndexManager, in this case:
compile 'org.apache.lucene:lucene-analyzers-common:6.+'
compile 'org.apache.lucene:lucene-queryparser:6.+'
compile 'org.apache.lucene:lucene-highlighter:6.+'
compile 'commons-io:commons-io:2.6'
compile 'org.apache.poi:poi-ooxml:4.0.0'
compile 'ch.qos.logback:logback-classic:1.2.1'
After this, I ran gradle jar to create the .jar which contains this IndexManager class, initially without any fancy stuff in the manifest (e.g. name, version). And I put this .jar file in a dedicated local directory.
Then I created another Gradle project to use this .jar file, the critical dependency here being
compile files('D:/My Documents/software projects/misc/localJars/XGradleLibExp.jar' )
The file to use this class looks like this:
package core
import XGradleLibExp.IndexManager
class Test {
public static void main( args ) {
println "hello xxx"
Printer printer = new Printer()
IndexManager im = new IndexManager( printer )
def result = im.makeIndexFromDbaseTable()
println "call result $result"
}
}
class Printer {
def outPS = new PrintStream(System.out, true, 'UTF-8' )
}
... I had designed IndexManager to use an auxiliary class, which had a property outPS. Groovy duck-typing means you just have to supply anything with such a property and hopefully things work.
The above arrangement didn't run: although you can do build and installdist without errors, the attempt to execute the distributed executable fails because the above 6 compile dependency lines are not present in build.gradle of the "consumer" project. When you put them in this "consumer" Gradle project's build.gradle, it works.
No doubt you can add the version to the generated .jar file, and thus keep older versions for use with "consumer" projects. What I don't understand is how you might harness the mechanism which makes the downloading and use of the dependencies needed by the .jar as automatic as we are used to for things obtained from "real repositories".
PS in the course of my struggles today I seem to have found that Gradle's "maven-publish" plugin is not compatible with Gradle 5.+ (which I'm using). This may or may not be relevant: some people have talked of using a "local Maven repository". I have no idea whether this is the answer to my problem... Await input from an über-Gradle-geek... :)
You should be able to update the Eclipse model to reflect this project-to-project dependency. It looks something like this (in ProjectB's build.gradle):
apply plugin: 'eclipse'
eclipse {
classpath.file.whenMerged {
entries << new org.gradle.plugins.ide.eclipse.model.ProjectDependency('/ProjectA')
}
project.file.whenMerged {
// add a project reference, which should show up in /ProjectB/.project's <projects> element
}
}
These changes may be to the running data model, so they may not actually alter the .classpath and .project files. More info can be found here: https://docs.gradle.org/current/dsl/org.gradle.plugins.ide.eclipse.model.EclipseModel.html
This issue is discussed here: http://gradle.1045684.n5.nabble.com/Gradle-s-Eclipse-DSL-and-resolving-dependencies-to-workspace-projects-td4856525.html and a bug was opened but never resolved here: https://issues.gradle.org/browse/GRADLE-1014
I use OperatingSystem.current() for my daily work with gradle. Now, I want to assemble my java project for different platforms, so I change manually my build.gradle files to build for a specific OS.
My question: Is there a way to specify the OS to use (returned by OperatingSystem.current()) directly in the gradle command line? If no, what is the best strategy to do cross building ?
Note: I depend on some libraries that use themselves the OperatingSystem.current().
I'm cringing while writing this, it's wrong on so many levels - I suggest you'll avoid forcing Gradle to think it's in a different OS.
But assuming you can't avoid it ->
I all depends on the version of Gradle you're using, I'd assume you're using the latest version (in older version that might be simpler)
OperatingSystem.current() works with the "os.name" environment variable which you can override very simply with a -D flag on the Gradle command.
BUT, and this is a big but, Gradle is not the problem here. The underlying JRE being used to execute the build contains OS specific code - see UNIXProcess on UNIX systems.
The current implementation of UNIXProcess blocks overriding the "os.name" value as it performs validations on it.
It's possible you'de be able to bypass that by creating a class in org.gradle.internal.os package that exposes the package-private OperatingSystem.resetCurrent() method and then force OperatingSystem.current() to re-evaluate, bypassing any real JRE checks.
Something like so:
print OperatingSystem.current()
System.setProperty("os.name", <some other OS>)
OperatingSystemWrapper.resetCurrent()
print OperatingSystem.current()
After some additional conversation over the comments, now I understand that the real requirement here is to take conditional dependency on native libs in a simple way that will allow the OP to remove dependency on OperatingSyste.current()
Taking a sample
apply plugin: "java"
dependencies {
compile "org.lwjgl:lwjgl:3.2.0"
compile "org.lwjgl:lwjgl-platform:3.2.0:natives-windows"
compile "org.lwjgl:lwjgl-platform:3.2.0:natives-linux"
compile "org.lwjgl:lwjgl-platform:3.2.0:natives-osx"
}
One can add conditional dependency via a "-P" flag (see https://docs.gradle.org/current/userguide/build_environment.html#sec:gradle_properties_and_system_properties)
apply plugin: "java"
dependencies {
compile "org.lwjgl:lwjgl:3.2.0"
if (buildos == "windows") {
compile "org.lwjgl:lwjgl-platform:3.2.0:natives-windows"
} else if (buildos == "linux") {
compile "org.lwjgl:lwjgl-platform:3.2.0:natives-linux"
} else if (buildos == "osx") {
compile "org.lwjgl:lwjgl-platform:3.2.0:natives-osx"
}
}
gradle build -Pbuildos=windows
A similar thing can be done with a "-D" flag, but then you need to access it with System.getProperty
One can take it even further by building custom tasks and configurations (inheriting from compile/implementation) for each flavor instead of relying on -P flags
I am using JOOQ code generation Tool for generating source code for my schema(MYSQL). I would like to generate source code every time I compile my Project. But I am not able to do it because when I run Code generation gradle Task, Compiler starts complaining about references of deleted source code.
Here is what I did:-
Created an Empty Spring boot Project.
Generated Source code using config xml(jooq.xml below) like this
Triggered Code Generation using a Gradle Task.
Build.gradle
task generateJooqDatabaseSource(type: JavaExec) {
classpath = sourceSets.main.runtimeClasspath
main = 'org.jooq.util.GenerationTool'
args = ['/jooq.xml']
standardOutput = System.out
errorOutput = System.err
}
Used the generated source code and wrote SQLs using JOOQ.
Everything is fine till here. But now I don't want to Push the generated Java Classes to my Project. I would like it to create every time when I compile my Project.
so lets delete the generated source code and re-generate it again(say for my Test environment)
But as soon as I run the Gradle Task generateJooqDatabaseSource
it starts complaining about the generated code references.
error: package autogenered.jooq.code.db.tables does not exist
import autogenered.jooq.code.db.tables.Author;
Tried googling the problem and found suggestions to use plugins like flyway, suggested here
But I really don't want to add another plugin if it can be achieved easily without it.
PS:- Just started to use Gradle, JOOQ from couple of days, apologies if answer is obvious.
Adding Following Lines in build.gradle have done the tweak for me:
compileJava.dependsOn(generateJooqDatabaseSource)
generateJooqDatabaseSource.dependsOn = [processResources, processTestResources]
Intellij Specific configuration:-
Added gradle build task to be triggered every time I do
make Project (Ctrl-F9)
or
Re-build Project:
I can't tell if this is a bug with Gradle 1.0m7, or if we are just doing this wrong.
We have some classes that get compiled as apart of a project, that we want to individually jar into it's own artifact. These are for example standalone domain model objects, that we want to share with another project.
I'd prefer not to go the multi-project build route, so how do we tell Gradle to create another jar for these?
Currently we are doing this:
task modelJar(type: Jar) {
classifier = 'model'
from fileTree(dir: sourceSets.main.classesDir).matching { include 'com/foo/bar/model/**' }
}
artifacts {
archives modeljar
}
The issue here, is the modeljar task runs before the classes are compiled. At first we didn't realise this and thought this was working. Turns out, the artifact was picking up classes from the previous run, not the current run. Doing clean before the build results in a jar with no classes in it, and reveals the problem.
I was looking at custom configuration, but it seems pretty complex and I didn't want to overly complicate the build file.
Appreciate any advice.
Thanks.
the most convenient way to do this is
task modelJar(type: Jar) {
classifier = 'model'
from sourceSets.main.output
include 'com/foo/bar/model/**'
}
Some background:
sourceSets.main.output is a buildable filecollection. This means that if a task works with this file collection, gradle knows that this file collection must be created before another task can use it. in this particular case, sourcesets.main.classes is wired to the classes task of the java plugin. Therefore you your modelJar task does not need to depend on the classes task explicitly.
How about making modelJar task depend on classes (built-in) task? This should make sure compilation is done before modelJar task.
task modelJar(dependsOn: classes, type: Jar){
...