Dagger can not find classes generated by other annotation processor - java

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'
}

Related

How can I access a RetentionPolicy.CLASS java annotation?

Is there a way to configure a ClassLoader or a JVM to load annotations with CLASS retention policy, so I can access them using reflection?
This is useful for compile-time post-processing, as mentioned here.
I annotate some of my classes in order to generate an antlib.xml file automatically. I would prefer if my annotation could have CLASS retention policy, so that it does not create runtime dependencies.
javac can process source and class level annotations, with the -processor option. See javax.annotation.processing.AbstractProcessor. (Since java 1.6).
I started using it while compiling .java files. Apparently it can also be used to process CLASS annotations with .class input files. I haven't tried this because I'm using ant to compile, and ant does not seem to pass .class files to the compiler.
I have to do a full compile when I want to process all the annotations in my project.
I think you might want to have a look at this tutorial.
It explains how to create your own annotation processor and how to use it to generate code. It doesn't handle bytecode manipulation though.
He also gave a presentation available on YouTube. In case you're too lazy to read... ;-)

Gradle - Executing custom annotation processor during compile time

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'])
}

Can annotation processor be used for code generation?

Let's say I define an annotation called #MyAnnotation.
There is a class X which is declared as:
#MyAnnotation
class X { .... }
Now at compile time I want to inspect all classes annotated with #MyAnnotation and do some code generation to more java source files that need to be compiled as well in the same process.
Is this possible using java annotation processor or some other tool?
APT tool has been merged into javac in Java 6. This is a much better tutorial for annotation processing.
You may take a look at the Java apt (Annotation Processing Tool) for such a thing.
You can find the Getting Started page, and a nice article (1, 2, 3) about how to use that to generate code.

How to step-debug annotation processor during compile?

I have an annotation processor for an annotation of retention policy=SOURCE.
I have not idea how to step-debug it.
I have issued print statements, logger info when I run mvn install, compile or package or ant javac, and I see their sysouts in the compile log.
However, I have no idea how to step-debug the processor in Eclipse. I mean, how do you step-debug compile-time?
An option in recent times is to use something like http://github.com/google/compile-testing which lets you invoke the compilation job against arbitrary annotation processors, which you can set break points, step through, etc.
#Test public void testStuff() {
// Create a source file to process, or load one from disk.
JavaFileObject file = JavaFileObjects.fromSourceLines("test.Foo",
"package test;",
"",
"import bar.*;",
"",
"#MyAnnotation(blah=false)",
"interface TestInterface {",
" Bar someBar();",
"}",
// assert conditions following a compilation in the context of MyProcessor.
assert_().about(javaSource()).that(file)
.processedWith(new MyProcessor())
.failsToCompile()
.withErrorContaining("some error message").in(file).onLine(5);
}
This test expects you will get some error message because #MyAnnotation is incorrectly declared in the test data source. If this assertion fails, you can run it in debug mode in your IDE, set breakpoints in MyProcessor, and step through with a full compiler environment active during debugging.
For unit testing specific methods within your processor, you can also use the #Rule called CompilationRule from which you can obtain Elements and Types utility classes in order to test specific logic in your compiler in a more isolated way.
You have to invoke the Java compiler from Eclipse, using a debug configuration (you'll need to create the configuration manually, from the "Debug Configurations..." menu choice.
The "correct" way to invoke the Java compiler under JDK 1.6 or above is to use the JavaCompiler interface in javax.tools, which you get from the ToolProvider (I include all the links because there's a decent amount of class/package documentation that you should read).
The "quick-and-dirty" way (that should work, but I make no guarantees) is to invoke com.sun.tools.javac.Main.main(), passing it your normal command-line arguments. To do this, you'll need tools.jar on your classpath (it's found in $JAVA_HOME/lib).
Annotation processing occurs during compilation, so normal debugging won't work. If you want to debug it in the context of you project, you can use Eclipse remote debugging, while having Gradle or Maven in debug mode. Then you can put breakpoints in the Annotation Processor's files.
See Debugging an Annotation Processor in any project.
Disclaimer: I wrote the post.

Dynamically generating java class from a java program

At the build(compile) time of my project I need to do code-generation for a java class. The generated java class is a java bean class with a set of getters and setters. At the build time I am getting the name of the class and names of variables. So what I need to do is dynamically generate the java bean from the information which I have.
eg. At the compile time I am getting following data.
class-name=Test
variable-name=aaa
So the generate class should look like following.
public class Test {
public String aaa;
public void setVar(String str) {
this.aaa = str;
}
public String getVar(){
return this.aaa;
}
}
When I searched for a tool that I can use, I found Arch4j [1] interesting but the problem with that is it is not compatible with Apache 2.0 license. I am looking for a project/tool that is compatible with Apache 2.0 license.
I would appreciate if someone can give me some insight on how I can do this.
[1] - http://arch4j.sourceforge.net/components/generator/index.html
Why not just generate the .java file during your build, using a custom ant task or Maven plugin? This seems like a rather easy task which doesn't need any complex library. You could even use a template file with placeholders for the class name and the field name, and generate the real .java file using a replace task.
JET from Eclipse can be used:
http://eclipse.org/articles/Article-JET/jet_tutorial1.html
It can be called by ant: http://help.eclipse.org/helios/index.jsp?topic=/org.eclipse.jet.doc/references/ant/antTasks.xhtml
And I think from maven: http://mvnrepository.com/artifact/org.eclipse/jet/0.8.0-v20070605
Take a look at javax.tools package. You can create and load a dynamically generated class only with that package.
Just bear in mind you need the JDK available and that's not re-distributable so your customer would need to downloaded it separately ( just like you do in any IDE today )
http://download.oracle.com/javase/6/docs/api/javax/tools/package-summary.html
For instance, you can invoke the java compiler programatically with:
http://download.oracle.com/javase/6/docs/api/javax/tools/JavaCompiler.html
And you can load it with URLClassLoader
Odd suggestion, but it seems like you want to generate beans. Why not use something like apache common's DynaBean? They allow you to create beans at run time. Here is an example of using DynaBean.
Of course this is at run time and not compile time. For compile time, I would recommend using an ant task to compile your source and add a dependency for compile on generation of your classes. You can handle the classes generation by writing a small java application that uses velocity as the java class template's engine.
So your ant task on compile first calls a small java program that generates the java class files using velocity template (delete the old files in ant if needed). Then compile as normal.

Categories