-
Enhancement
-
Resolution: Won't Fix
-
P4
-
None
-
8, 9
-
generic
-
generic
A DESCRIPTION OF THE REQUEST :
In order to implement incremental build support for the Java Compiler with using annotation processing, one would require a location, where arbitrary data can be stored between compilations. To solve this, a new StandardLocation enumeration value can be introduced, which represents a location where annotation processors can store data.
JUSTIFICATION :
Some annotation processors generate, and derive data from multiple source files. When compiling the sources in an incremental way, not all of the information may be available to the processor. To solve this, processors can store previously derived data in a standardized location, and use that and the modified sources to derive the up to date information.
The location should be standardized in order to have multiple compiler support over time.
The example provides an use-case, where the annotation provides records the top level classes during compilation, and derives them in a comma separated format. As this is a minimal example, there are more use-cases to this enhancement.
During the file generation, the originating elements could be supplied, however when deriving information in a big project/from lots of sources, compiling them again could increase the build times significantly.
The value addition does not break any compatibility, as it has no effect on the JavaFileManagers which does not support a new location.
The annotation processors should continue working without this location, but that can require full build every time.
EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
The introduced value in javax.tools.StandardLocation can be named as 'INCREMENTAL_OUTPUT' and isOutputLocation() should return true.
ACTUAL -
No standardized value is specified.
---------- BEGIN SOURCE ----------
package bence.sipka.compiler.java;
import java.io.IOException;
import java.io.OutputStream;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
import javax.annotation.processing.Completion;
import javax.annotation.processing.Filer;
import javax.annotation.processing.ProcessingEnvironment;
import javax.annotation.processing.Processor;
import javax.annotation.processing.RoundEnvironment;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.AnnotationMirror;
import javax.lang.model.element.Element;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.TypeElement;
import javax.tools.FileObject;
import javax.tools.StandardLocation;
public class TestProcessor implements Processor {
@Override
public Set<String> getSupportedOptions() {
return Collections.emptySet();
}
@Override
public Set<String> getSupportedAnnotationTypes() {
HashSet<String> result = new HashSet<>();
result.add("*");
return result;
}
@Override
public SourceVersion getSupportedSourceVersion() {
return SourceVersion.latest();
}
private Filer filer;
private ProcessingEnvironment processingEnv;
private Collection<String> classes = new HashSet<>();
@Override
public void init(ProcessingEnvironment processingEnv) {
this.processingEnv = processingEnv;
this.filer = processingEnv.getFiler();
try {
FileObject res = filer.getResource(StandardLocation.INCREMENTAL_OUTPUT, "test.pkg", "file");
classes.addAll(Arrays.asList(res.getCharContent(true).toString().split(",")));
for (Iterator<String> it = classes.iterator(); it.hasNext();) {
String clazz = it.next();
TypeElement type = processingEnv.getElementUtils().getTypeElement(clazz);
if (type == null) {
// class no longer exists, remove
it.remove();
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
for (Element rootelem : roundEnv.getRootElements()) {
if (rootelem instanceof TypeElement) {
classes.add(processingEnv.getElementUtils().getBinaryName((TypeElement) rootelem).toString());
}
}
if (roundEnv.processingOver()) {
try {
FileObject res = filer.createResource(StandardLocation.INCREMENTAL_OUTPUT, "test.pkg", "file");
try (OutputStream os = res.openOutputStream()) {
os.write(String.join(",", classes).getBytes());
}
} catch (IOException e) {
e.printStackTrace();
}
}
return false;
}
@Override
public Iterable<? extends Completion> getCompletions(Element element, AnnotationMirror annotation, ExecutableElement member, String userText) {
return Collections.emptyList();
}
}
---------- END SOURCE ----------
CUSTOMER SUBMITTED WORKAROUND :
Using StandardLocation.locationFor(String) method can bypass this issue, however a single value addition can encourage widespread support.
In order to implement incremental build support for the Java Compiler with using annotation processing, one would require a location, where arbitrary data can be stored between compilations. To solve this, a new StandardLocation enumeration value can be introduced, which represents a location where annotation processors can store data.
JUSTIFICATION :
Some annotation processors generate, and derive data from multiple source files. When compiling the sources in an incremental way, not all of the information may be available to the processor. To solve this, processors can store previously derived data in a standardized location, and use that and the modified sources to derive the up to date information.
The location should be standardized in order to have multiple compiler support over time.
The example provides an use-case, where the annotation provides records the top level classes during compilation, and derives them in a comma separated format. As this is a minimal example, there are more use-cases to this enhancement.
During the file generation, the originating elements could be supplied, however when deriving information in a big project/from lots of sources, compiling them again could increase the build times significantly.
The value addition does not break any compatibility, as it has no effect on the JavaFileManagers which does not support a new location.
The annotation processors should continue working without this location, but that can require full build every time.
EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
The introduced value in javax.tools.StandardLocation can be named as 'INCREMENTAL_OUTPUT' and isOutputLocation() should return true.
ACTUAL -
No standardized value is specified.
---------- BEGIN SOURCE ----------
package bence.sipka.compiler.java;
import java.io.IOException;
import java.io.OutputStream;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
import javax.annotation.processing.Completion;
import javax.annotation.processing.Filer;
import javax.annotation.processing.ProcessingEnvironment;
import javax.annotation.processing.Processor;
import javax.annotation.processing.RoundEnvironment;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.AnnotationMirror;
import javax.lang.model.element.Element;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.TypeElement;
import javax.tools.FileObject;
import javax.tools.StandardLocation;
public class TestProcessor implements Processor {
@Override
public Set<String> getSupportedOptions() {
return Collections.emptySet();
}
@Override
public Set<String> getSupportedAnnotationTypes() {
HashSet<String> result = new HashSet<>();
result.add("*");
return result;
}
@Override
public SourceVersion getSupportedSourceVersion() {
return SourceVersion.latest();
}
private Filer filer;
private ProcessingEnvironment processingEnv;
private Collection<String> classes = new HashSet<>();
@Override
public void init(ProcessingEnvironment processingEnv) {
this.processingEnv = processingEnv;
this.filer = processingEnv.getFiler();
try {
FileObject res = filer.getResource(StandardLocation.INCREMENTAL_OUTPUT, "test.pkg", "file");
classes.addAll(Arrays.asList(res.getCharContent(true).toString().split(",")));
for (Iterator<String> it = classes.iterator(); it.hasNext();) {
String clazz = it.next();
TypeElement type = processingEnv.getElementUtils().getTypeElement(clazz);
if (type == null) {
// class no longer exists, remove
it.remove();
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
for (Element rootelem : roundEnv.getRootElements()) {
if (rootelem instanceof TypeElement) {
classes.add(processingEnv.getElementUtils().getBinaryName((TypeElement) rootelem).toString());
}
}
if (roundEnv.processingOver()) {
try {
FileObject res = filer.createResource(StandardLocation.INCREMENTAL_OUTPUT, "test.pkg", "file");
try (OutputStream os = res.openOutputStream()) {
os.write(String.join(",", classes).getBytes());
}
} catch (IOException e) {
e.printStackTrace();
}
}
return false;
}
@Override
public Iterable<? extends Completion> getCompletions(Element element, AnnotationMirror annotation, ExecutableElement member, String userText) {
return Collections.emptyList();
}
}
---------- END SOURCE ----------
CUSTOMER SUBMITTED WORKAROUND :
Using StandardLocation.locationFor(String) method can bypass this issue, however a single value addition can encourage widespread support.