Skip to content

Commit

Permalink
Support alternate JavaCompilers (such as ECJ) in CompilationRule
Browse files Browse the repository at this point in the history
ECJ expects all JavaFileObjects to be Files, so the rule no longer
uses Compilation.compile() but instead directly uses the JavaCompiler,
and processes a class rather than compiling a dummy (in-memory) source.
  • Loading branch information
tbroyer committed Jul 11, 2015
1 parent db256e0 commit 88dfce7
Show file tree
Hide file tree
Showing 3 changed files with 92 additions and 35 deletions.
6 changes: 6 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,12 @@
<version>1.3.9</version>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.eclipse.jdt.core.compiler</groupId>
<artifactId>ecj</artifactId>
<version>4.4.2</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
Expand Down
100 changes: 65 additions & 35 deletions src/main/java/com/google/testing/compile/CompilationRule.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,10 @@

import static com.google.common.base.Preconditions.checkState;

import com.google.common.base.Charsets;
import com.google.common.base.Supplier;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.testing.compile.Compilation.Result;

import org.junit.Rule;
import org.junit.rules.TestRule;
Expand All @@ -37,61 +38,90 @@
import javax.lang.model.element.TypeElement;
import javax.lang.model.util.Elements;
import javax.lang.model.util.Types;
import javax.tools.DiagnosticCollector;
import javax.tools.JavaCompiler;
import javax.tools.JavaCompiler.CompilationTask;
import javax.tools.JavaFileManager;
import javax.tools.JavaFileObject;
import javax.tools.ToolProvider;

/**
* A {@link JUnit4} {@link Rule} that executes tests such that a instances of {@link Elements} and
* A {@link JUnit4} {@link Rule} that executes tests such that instances of {@link Elements} and
* {@link Types} are available during execution.
*
* <p>To use this rule in a test, just add the following field: <pre> {@code
* <p>To use this rule in a test, just add the following field:
* <pre>{@code
* @Rule public CompilationRule compilationRule = new CompilationRule();}
* </pre>
*
* <p>This rule uses {@link ToolProvider#getSystemJavaCompiler() javac} by default, but you can
* also pass a supplier of {@link JavaCompiler} to use other compilers, such as Eclipse ECJ.
*
* @author Gregory Kick
*/
public final class CompilationRule implements TestRule {
private final Supplier<JavaCompiler> compilerSupplier;

private Elements elements;
private Types types;

public CompilationRule() {
this(new Supplier<JavaCompiler>() {
@Override
public JavaCompiler get() {
return ToolProvider.getSystemJavaCompiler();
}
});
}

public CompilationRule(Supplier<JavaCompiler> compilerSupplier) {
this.compilerSupplier = compilerSupplier;
}

@Override
public Statement apply(final Statement base, Description description) {
return new Statement() {
@Override public void evaluate() throws Throwable {
final AtomicReference<Throwable> thrown = new AtomicReference<Throwable>();
Result result = Compilation.compile(ImmutableList.of(new AbstractProcessor() {
@Override
public SourceVersion getSupportedSourceVersion() {
return SourceVersion.latest();
}
JavaCompiler compiler = compilerSupplier.get();
DiagnosticCollector<JavaFileObject> diagnosticCollector = new DiagnosticCollector<JavaFileObject>();
JavaFileManager fileManager = compiler.getStandardFileManager(diagnosticCollector, null, Charsets.UTF_8);
CompilationTask task = compiler.getTask(null, fileManager, diagnosticCollector, null,
ImmutableSet.of(CompilationRule.class.getCanonicalName()), null);
task.setProcessors(ImmutableList.of(new AbstractProcessor() {
@Override
public SourceVersion getSupportedSourceVersion() {
return SourceVersion.latest();
}

@Override
public Set<String> getSupportedAnnotationTypes() {
return ImmutableSet.of("*");
}
@Override
public Set<String> getSupportedAnnotationTypes() {
return ImmutableSet.of("*");
}

@Override
public synchronized void init(ProcessingEnvironment processingEnv) {
super.init(processingEnv);
elements = processingEnv.getElementUtils();
types = processingEnv.getTypeUtils();
}
@Override
public synchronized void init(ProcessingEnvironment processingEnv) {
super.init(processingEnv);
elements = processingEnv.getElementUtils();
types = processingEnv.getTypeUtils();
}

@Override
public boolean process(Set<? extends TypeElement> annotations,
RoundEnvironment roundEnv) {
// just run the test on the last round after compilation is over
if (roundEnv.processingOver()) {
try {
base.evaluate();
} catch (Throwable e) {
thrown.set(e);
@Override
public boolean process(Set<? extends TypeElement> annotations,
RoundEnvironment roundEnv) {
// just run the test on the last round after compilation is over
if (roundEnv.processingOver()) {
try {
base.evaluate();
} catch (Throwable e) {
thrown.set(e);
}
}
return false;
}
}
return false;
}
}),
ImmutableSet.<String>of(),
// just compile _something_
ImmutableList.of(JavaFileObjects.forSourceLines("Dummy", "final class Dummy {}")));
checkState(result.successful(), result);
}));
boolean successful = task.call();
checkState(successful);
Throwable t = thrown.get();
if (t != null) {
throw t;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package com.google.testing.compile;

import javax.tools.JavaCompiler;

import org.eclipse.jdt.internal.compiler.tool.EclipseCompiler;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;

import com.google.common.base.Supplier;

@RunWith(JUnit4.class)
public class EclipseCompilationRuleTest extends CompilationRuleTest {
{
compilationRule = new CompilationRule(new Supplier<JavaCompiler>() {
@Override
public JavaCompiler get() {
return new EclipseCompiler();
}
});
}
}

0 comments on commit 88dfce7

Please sign in to comment.