All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.takari.maven.plugins.compile.javac.CompilerJavac Maven / Gradle / Ivy

There is a newer version: 2.2.0
Show newest version
package io.takari.maven.plugins.compile.javac;

import io.takari.incrementalbuild.BuildContext;
import io.takari.incrementalbuild.BuildContext.Input;
import io.takari.incrementalbuild.BuildContext.Output;
import io.takari.incrementalbuild.BuildContext.Resource;
import io.takari.incrementalbuild.BuildContext.Severity;
import io.takari.incrementalbuild.spi.DefaultBuildContext;
import io.takari.maven.plugins.compile.AbstractCompileMojo.Proc;

import java.io.File;
import java.io.PrintWriter;
import java.io.Writer;
import java.nio.charset.Charset;
import java.util.ArrayDeque;
import java.util.Deque;
import java.util.HashMap;
import java.util.Map;

import javax.inject.Inject;
import javax.inject.Named;
import javax.tools.Diagnostic;
import javax.tools.Diagnostic.Kind;
import javax.tools.DiagnosticCollector;
import javax.tools.JavaCompiler;
import javax.tools.JavaFileObject;
import javax.tools.StandardJavaFileManager;
import javax.tools.ToolProvider;

import org.apache.maven.plugin.MojoExecutionException;

@Named(CompilerJavac.ID)
public class CompilerJavac extends AbstractCompilerJavac {

  public static final String ID = "javac";

  private static final boolean isJava7;

  static {
    boolean isJava7x = true;
    try {
      Class.forName("java.nio.file.Files");
    } catch (Exception e) {
      isJava7x = false;
    }
    isJava7 = isJava7x;
  }

  static JavaCompiler getSystemJavaCompiler() throws MojoExecutionException {
    JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
    if (compiler == null) {
      throw new MojoExecutionException("No compiler is provided in this environment. "
          + "Perhaps you are running on a JRE rather than a JDK?");
    }
    return compiler;
  }

  private static interface JavaCompilerFactory {
    public JavaCompiler acquire() throws MojoExecutionException;

    public void release(JavaCompiler compiler);
  }

  private static final JavaCompilerFactory REUSECREATED = new JavaCompilerFactory() {

    // TODO broken if this plugin is loaded by multiple classloaders
    // https://cwiki.apache.org/confluence/display/MAVEN/Maven+3.x+Class+Loading
    private final Deque compilers = new ArrayDeque();

    @Override
    public JavaCompiler acquire() throws MojoExecutionException {
      synchronized (compilers) {
        if (!compilers.isEmpty()) {
          return compilers.removeFirst();
        }
      }
      return getSystemJavaCompiler();
    }

    @Override
    public void release(JavaCompiler compiler) {
      synchronized (compilers) {
        compilers.addFirst(compiler);
      }
    }
  };

  private static final JavaCompilerFactory SINGLETON = new JavaCompilerFactory() {

    private JavaCompiler compiler;

    @Override
    public void release(JavaCompiler compiler) {}

    @Override
    public synchronized JavaCompiler acquire() throws MojoExecutionException {
      if (compiler == null) {
        compiler = getSystemJavaCompiler();
      }
      return compiler;
    }
  };

  @Inject
  public CompilerJavac(DefaultBuildContext context, ProjectClasspathDigester digester) {
    super(context, digester);
  }

  @Override
  public void compile() throws MojoExecutionException {
    // java 6 limitations
    // - there is severe performance penalty using new JavaCompiler instance
    // - the same JavaCompiler cannot be used concurrently
    // - even different JavaCompiler instances can't do annotation processing concurrently

    // java 7 (and I assume newer) do not have these limitations

    // The workaround is two-fold
    // - reuse JavaCompiler instances, but not on multiple threads
    // - do not allow in-process annotation processing

    if (!isJava7 && getProc() != Proc.none) {
      // TODO maybe allow in single-threaded mode
      throw new MojoExecutionException("Annotation processing requires forked JVM on Java 6");
    }

    final JavaCompilerFactory factory = isJava7 ? SINGLETON : REUSECREATED;

    final JavaCompiler compiler = factory.acquire();
    try {
      compile(compiler);
    } finally {
      factory.release(compiler);
    }
  }

  private void compile(JavaCompiler compiler) {
    final Charset sourceEncoding = getSourceEncoding();
    final DiagnosticCollector diagnosticCollector =
        new DiagnosticCollector();
    final StandardJavaFileManager standardFileManager =
        compiler.getStandardFileManager(diagnosticCollector, null, sourceEncoding);
    final Iterable javaSources =
        standardFileManager.getJavaFileObjectsFromFiles(sources);

    final Map> outputs = new HashMap>();
    final Map> inputs = new HashMap>();

    final Iterable options = getCompilerOptions();
    final RecordingJavaFileManager recordingFileManager =
        new RecordingJavaFileManager(standardFileManager) {
          @Override
          protected void record(File outputFile) {
            outputs.put(outputFile, context.processOutput(outputFile));
          }
        };

    Writer stdout = new PrintWriter(System.out, true);
    final JavaCompiler.CompilationTask task = compiler.getTask(stdout, // Writer out
        recordingFileManager, // file manager
        diagnosticCollector, // diagnostic listener
        options, //
        null, // Iterable classes to process by annotation processor(s)
        javaSources);

    boolean success = task.call();

    for (JavaFileObject source : javaSources) {
      File sourceFile = FileObjects.toFile(source);
      inputs.put(sourceFile, context.registerInput(sourceFile).process());
    }

    for (Diagnostic diagnostic : diagnosticCollector.getDiagnostics()) {
      JavaFileObject source = diagnostic.getSource();

      // javac appears to report errors even when compilation was success.
      // I was only able to reproduce this with annotation processing on java 6
      // for consistency with forked mode, downgrade errors to warning here too
      Severity severity =
          success ? BuildContext.Severity.WARNING : toSeverity(diagnostic.getKind());

      if (source != null) {
        File file = FileObjects.toFile(source);
        Resource resource = inputs.get(file);
        if (resource == null) {
          resource = outputs.get(file);
        }
        if (resource != null) {
          resource.addMessage((int) diagnostic.getLineNumber(), (int) diagnostic.getColumnNumber(),
              diagnostic.getMessage(null), severity, null);
        } else {
          log.warn("Unexpected java {} resource {}", source.getKind(), file);
        }
      } else {
        Input input = context.registerInput(getPom()).process();
        // TODO execution line/column
        input.addMessage(0, 0, diagnostic.getMessage(null), severity, null);
      }
    }
  }

  private BuildContext.Severity toSeverity(Kind kind) {
    return kind == Kind.ERROR ? BuildContext.Severity.ERROR : BuildContext.Severity.WARNING;
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy