Fix handling of generated resource outputs
which should be included in the source or class output locations, respectively.
RELNOTES: N/A
-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=272496172
diff --git a/java/com/google/turbine/binder/Binder.java b/java/com/google/turbine/binder/Binder.java
index 47efe98..2468a3f 100644
--- a/java/com/google/turbine/binder/Binder.java
+++ b/java/com/google/turbine/binder/Binder.java
@@ -66,6 +66,7 @@
import java.time.Duration;
import java.util.List;
import java.util.Optional;
+import javax.annotation.processing.Processor;
/** The entry point for analysis. */
public class Binder {
@@ -530,6 +531,11 @@
units, modules, classPathEnv, tli, generatedSources, generatedClasses, statistics);
}
+ public BindingResult withGeneratedSources(ImmutableList<SourceFile> generatedSources) {
+ return new BindingResult(
+ units, modules, classPathEnv, tli, generatedSources, generatedClasses, statistics);
+ }
+
public BindingResult withStatistics(Statistics statistics) {
return new BindingResult(
units, modules, classPathEnv, tli, generatedSources, generatedClasses, statistics);
diff --git a/java/com/google/turbine/binder/Processing.java b/java/com/google/turbine/binder/Processing.java
index 1391336..809276e 100644
--- a/java/com/google/turbine/binder/Processing.java
+++ b/java/com/google/turbine/binder/Processing.java
@@ -255,6 +255,9 @@
// TODO(cushon): consider handling generated classes after each round
result = result.withGeneratedClasses(filer.generatedClasses());
}
+ if (!filer.generatedSources().isEmpty()) {
+ result = result.withGeneratedSources(filer.generatedSources());
+ }
result =
result.withStatistics(Statistics.create(timers.build(), ImmutableMap.copyOf(statistics)));
diff --git a/java/com/google/turbine/processing/TurbineFiler.java b/java/com/google/turbine/processing/TurbineFiler.java
index 86a5897..6fe2de9 100644
--- a/java/com/google/turbine/processing/TurbineFiler.java
+++ b/java/com/google/turbine/processing/TurbineFiler.java
@@ -79,7 +79,6 @@
private final Map<String, SourceFile> generatedSources = new LinkedHashMap<>();
private final Map<String, byte[]> generatedClasses = new LinkedHashMap<>();
- private final Map<String, byte[]> generatedResources = new LinkedHashMap<>();
/** Generated source file objects from all rounds. */
public ImmutableList<SourceFile> generatedSources() {
@@ -91,11 +90,6 @@
return ImmutableMap.copyOf(generatedClasses);
}
- /** Generated resource file objects from all rounds. */
- public ImmutableMap<String, byte[]> generatedResources() {
- return ImmutableMap.copyOf(generatedResources);
- }
-
public TurbineFiler(
Set<String> seen, Function<String, Supplier<byte[]>> classPath, ClassLoader loader) {
this.seen = seen;
@@ -119,7 +113,16 @@
generatedClasses.put(path, e.bytes());
break;
case OTHER:
- generatedResources.put(path, e.bytes());
+ switch (e.location()) {
+ case CLASS_OUTPUT:
+ generatedClasses.put(path, e.bytes());
+ break;
+ case SOURCE_OUTPUT:
+ this.generatedSources.put(path, new SourceFile(path, e.contents()));
+ break;
+ default:
+ throw new AssertionError(e.location());
+ }
break;
case HTML:
throw new UnsupportedOperationException(String.valueOf(e.getKind()));
@@ -135,7 +138,7 @@
throws IOException {
String name = n.toString();
checkArgument(!name.contains("/"), "invalid type name: %s", name);
- return create(Kind.SOURCE, name.replace('.', '/') + ".java");
+ return create(StandardLocation.SOURCE_OUTPUT, Kind.SOURCE, name.replace('.', '/') + ".java");
}
@Override
@@ -143,25 +146,28 @@
throws IOException {
String name = n.toString();
checkArgument(!name.contains("/"), "invalid type name: %s", name);
- return create(Kind.CLASS, name.replace('.', '/') + ".class");
+ return create(StandardLocation.CLASS_OUTPUT, Kind.CLASS, name.replace('.', '/') + ".class");
}
@Override
public FileObject createResource(
Location location, CharSequence p, CharSequence r, Element... originatingElements)
throws IOException {
+ checkArgument(location instanceof StandardLocation, "%s", location);
String pkg = p.toString();
String relativeName = r.toString();
checkArgument(!pkg.contains("/"), "invalid package: %s", pkg);
String path = packageRelativePath(pkg, relativeName);
- return create(Kind.OTHER, path);
+ return create((StandardLocation) location, Kind.OTHER, path);
}
- private JavaFileObject create(Kind kind, String path) throws FilerException {
+ private JavaFileObject create(StandardLocation location, Kind kind, String path)
+ throws FilerException {
+ checkArgument(location.isOutputLocation());
if (!seen.add(path)) {
throw new FilerException("already created " + path);
}
- TurbineJavaFileObject result = new TurbineJavaFileObject(kind, path);
+ TurbineJavaFileObject result = new TurbineJavaFileObject(location, kind, path);
files.add(result);
return result;
}
@@ -270,11 +276,13 @@
private static class TurbineJavaFileObject extends WriteOnlyFileObject implements JavaFileObject {
+ private final StandardLocation location;
private final Kind kind;
private final CharSequence name;
private final ByteArrayOutputStream baos = new ByteArrayOutputStream();
- public TurbineJavaFileObject(Kind kind, CharSequence name) {
+ public TurbineJavaFileObject(StandardLocation location, Kind kind, CharSequence name) {
+ this.location = location;
this.kind = kind;
this.name = name;
}
@@ -336,6 +344,10 @@
public String contents() {
return new String(baos.toByteArray(), UTF_8);
}
+
+ public StandardLocation location() {
+ return location;
+ }
}
private static class ResourceFileObject extends ReadOnlyFileObject {
diff --git a/javatests/com/google/turbine/processing/ProcessingIntegrationTest.java b/javatests/com/google/turbine/processing/ProcessingIntegrationTest.java
index 95a4b42..57c3e0f 100644
--- a/javatests/com/google/turbine/processing/ProcessingIntegrationTest.java
+++ b/javatests/com/google/turbine/processing/ProcessingIntegrationTest.java
@@ -18,14 +18,18 @@
import static com.google.common.collect.ImmutableList.toImmutableList;
import static com.google.common.truth.Truth.assertThat;
+import static java.nio.charset.StandardCharsets.UTF_8;
import static org.junit.Assert.fail;
import com.google.common.base.Joiner;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.MoreCollectors;
import com.google.turbine.binder.Binder;
+import com.google.turbine.binder.Binder.BindingResult;
import com.google.turbine.binder.ClassPathBinder;
import com.google.turbine.binder.Processing;
+import com.google.turbine.binder.Processing.ProcessorInfo;
import com.google.turbine.diag.SourceFile;
import com.google.turbine.diag.TurbineError;
import com.google.turbine.lower.IntegrationTestSupport;
@@ -44,6 +48,7 @@
import javax.lang.model.element.TypeElement;
import javax.tools.Diagnostic;
import javax.tools.JavaFileObject;
+import javax.tools.StandardLocation;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
@@ -167,4 +172,86 @@
assertThat(diags.get(1)).contains("proc error");
}
}
+
+ @SupportedAnnotationTypes("*")
+ public static class ResourceProcessor extends AbstractProcessor {
+
+ @Override
+ public SourceVersion getSupportedSourceVersion() {
+ return SourceVersion.latestSupported();
+ }
+
+ private boolean first = true;
+
+ @Override
+ public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
+ if (first) {
+ try {
+ try (Writer writer = processingEnv.getFiler().createSourceFile("Gen").openWriter()) {
+ writer.write("class Gen {}");
+ }
+ try (Writer writer =
+ processingEnv
+ .getFiler()
+ .createResource(StandardLocation.SOURCE_OUTPUT, "", "source.txt")
+ .openWriter()) {
+ writer.write("hello source output");
+ }
+ try (Writer writer =
+ processingEnv
+ .getFiler()
+ .createResource(StandardLocation.CLASS_OUTPUT, "", "class.txt")
+ .openWriter()) {
+ writer.write("hello class output");
+ }
+ } catch (IOException e) {
+ throw new UncheckedIOException(e);
+ }
+ first = false;
+ }
+ return false;
+ }
+ }
+
+ @Test
+ public void resources() throws IOException {
+ ImmutableList<Tree.CompUnit> units =
+ IntegrationTestSupport.TestInput.parse(
+ Joiner.on('\n')
+ .join(
+ "=== Test.java ===", //
+ "@Deprecated",
+ "class Test {",
+ "}"))
+ .sources
+ .entrySet()
+ .stream()
+ .map(e -> new SourceFile(e.getKey(), e.getValue()))
+ .map(Parser::parse)
+ .collect(toImmutableList());
+ BindingResult bound =
+ Binder.bind(
+ units,
+ ClassPathBinder.bindClasspath(ImmutableList.of()),
+ ProcessorInfo.create(
+ ImmutableList.of(new ResourceProcessor()),
+ getClass().getClassLoader(),
+ ImmutableMap.of(),
+ SourceVersion.latestSupported()),
+ TestClassPaths.TURBINE_BOOTCLASSPATH,
+ Optional.empty());
+
+ assertThat(bound.generatedSources().stream().map(s -> s.path()).collect(toImmutableList()))
+ .containsExactly("Gen.java", "source.txt");
+ assertThat(bound.generatedClasses().keySet()).containsExactly("class.txt");
+
+ assertThat(
+ bound.generatedSources().stream()
+ .filter(s -> s.path().equals("source.txt"))
+ .collect(MoreCollectors.onlyElement())
+ .source())
+ .isEqualTo("hello source output");
+ assertThat(new String(bound.generatedClasses().get("class.txt"), UTF_8))
+ .isEqualTo("hello class output");
+ }
}