Пример #1
0
  void test(String[] opts, String className) throws Exception {
    count++;
    System.err.println("Test " + count + " " + Arrays.asList(opts) + " " + className);
    Path testSrcDir = Paths.get(System.getProperty("test.src"));
    Path testClassesDir = Paths.get(System.getProperty("test.classes"));
    Path classes = Paths.get("classes." + count);
    classes.createDirectory();

    Context ctx = new Context();
    PathFileManager fm = new JavacPathFileManager(ctx, true, null);
    JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
    List<String> options = new ArrayList<String>();
    options.addAll(Arrays.asList(opts));
    options.addAll(Arrays.asList("-verbose", "-XDverboseCompilePolicy", "-d", classes.toString()));
    Iterable<? extends JavaFileObject> compilationUnits =
        fm.getJavaFileObjects(testSrcDir.resolve(className + ".java"));
    StringWriter sw = new StringWriter();
    PrintWriter out = new PrintWriter(sw);
    JavaCompiler.CompilationTask t =
        compiler.getTask(out, fm, null, options, null, compilationUnits);
    boolean ok = t.call();
    System.err.println(sw.toString());
    if (!ok) {
      throw new Exception("compilation failed");
    }

    File expect = new File("classes." + count + "/" + className + ".class");
    if (!expect.exists()) throw new Exception("expected file not found: " + expect);
    long expectedSize = new File(testClassesDir.toString(), className + ".class").length();
    long actualSize = expect.length();
    if (expectedSize != actualSize)
      throw new Exception("wrong size found: " + actualSize + "; expected: " + expectedSize);
  }
Пример #2
0
  public static void main(String... args) throws Throwable {
    String testSrcDir = System.getProperty("test.src");
    String testClassDir = System.getProperty("test.classes");
    String self = T6361619.class.getName();

    JavacTool tool = JavacTool.create();

    final PrintWriter out = new PrintWriter(System.err, true);

    Iterable<String> flags =
        Arrays.asList(
            "-processorpath", testClassDir,
            "-processor", self,
            "-d", ".");
    DiagnosticListener<JavaFileObject> dl =
        new DiagnosticListener<JavaFileObject>() {
          public void report(Diagnostic<? extends JavaFileObject> m) {
            out.println(m);
          }
        };

    StandardJavaFileManager fm = tool.getStandardFileManager(dl, null, null);
    Iterable<? extends JavaFileObject> f =
        fm.getJavaFileObjectsFromFiles(Arrays.asList(new File(testSrcDir, self + ".java")));

    JavacTask task = tool.getTask(out, fm, dl, flags, null, f);
    MyTaskListener tl = new MyTaskListener(task);
    task.setTaskListener(tl);

    // should complete, without exceptions
    task.call();
  }
Пример #3
0
  public TestInput(
      Iterable<? extends JavaFileObject> files, Iterable<String> processors, String[] options) {

    this.compiler = ToolProvider.getSystemJavaCompiler();
    this.fileManager = compiler.getStandardFileManager(null, null, null);

    this.files = files;
    this.processors = processors;
    this.options = new LinkedList<String>();

    String classpath = System.getProperty("tests.classpath", "tests" + File.separator + "build");
    String globalclasspath = System.getProperty("java.class.path", "");

    this.options.add("-Xmaxerrs");
    this.options.add("9999");
    this.options.add("-g");
    this.options.add("-d");
    this.options.add(OUTDIR);
    this.options.add("-classpath");
    this.options.add(
        "build"
            + File.pathSeparator
            + "junit.jar"
            + File.pathSeparator
            + classpath
            + File.pathSeparator
            + globalclasspath);
    this.options.addAll(Arrays.asList(options));
  }
Пример #4
0
 protected void checkGrammarSemanticsWarning(
     ErrorQueue equeue, GrammarSemanticsMessage expectedMessage) throws Exception {
   ANTLRMessage foundMsg = null;
   for (int i = 0; i < equeue.warnings.size(); i++) {
     ANTLRMessage m = equeue.warnings.get(i);
     if (m.errorType == expectedMessage.errorType) {
       foundMsg = m;
     }
   }
   assertNotNull("no error; " + expectedMessage.errorType + " expected", foundMsg);
   assertTrue(
       "error is not a GrammarSemanticsMessage", foundMsg instanceof GrammarSemanticsMessage);
   assertEquals(Arrays.toString(expectedMessage.args), Arrays.toString(foundMsg.args));
   if (equeue.size() != 1) {
     System.err.println(equeue);
   }
 }
Пример #5
0
 @NotNull
 public static CompilerConfiguration compilerConfigurationForTests(
     @NotNull ConfigurationKind configurationKind,
     @NotNull TestJdkKind jdkKind,
     @NotNull File... extraClasspath) {
   return compilerConfigurationForTests(
       configurationKind, jdkKind, Arrays.asList(extraClasspath), Collections.<File>emptyList());
 }
Пример #6
0
  private File compileOne(Type type) {
    if (this.flags.contains(Flags.USECACHE)) {
      File dir = cache.get(type.getName());
      if (dir != null) {
        return dir;
      }
    }
    List<JavaFileObject> files = new ArrayList<>();
    SourceProcessor accum = (name, src) -> files.add(new SourceFile(name, src));

    for (Type dep : type.typeDependencies()) {
      dep.generateAsDependency(accum, type.methodDependencies());
    }

    type.generate(accum);

    JavacTask ct =
        (JavacTask) this.systemJavaCompiler.getTask(null, this.fm, null, null, null, files);
    File destDir = null;
    do {
      int value = counter.incrementAndGet();
      destDir = new File(root, Integer.toString(value));
    } while (destDir.exists());

    if (this.flags.contains(Flags.VERBOSE)) {
      System.out.println("Compilation unit for " + type.getName() + " : compiled into " + destDir);
      for (JavaFileObject jfo : files) {
        System.out.println(jfo.toString());
      }
    }

    try {
      destDir.mkdirs();
      this.fm.setLocation(StandardLocation.CLASS_OUTPUT, Arrays.asList(destDir));
    } catch (IOException e) {
      throw new RuntimeException(
          "IOException encountered during compilation: " + e.getMessage(), e);
    }
    Boolean result = ct.call();
    if (result == Boolean.FALSE) {
      throw new RuntimeException("Compilation failure in " + type.getName() + " unit");
    }
    if (this.flags.contains(Flags.USECACHE)) {
      File existing = cache.putIfAbsent(type.getName(), destDir);
      if (existing != null) {
        deleteDir(destDir);
        return existing;
      }
    } else {
      this.tempDirs.add(destDir);
    }
    return destDir;
  }
Пример #7
0
 protected void checkError(ErrorQueue equeue, ANTLRMessage expectedMessage) throws Exception {
   // System.out.println("errors="+equeue);
   ANTLRMessage foundMsg = null;
   for (int i = 0; i < equeue.errors.size(); i++) {
     ANTLRMessage m = (ANTLRMessage) equeue.errors.get(i);
     if (m.errorType == expectedMessage.errorType) {
       foundMsg = m;
     }
   }
   assertTrue("no error; " + expectedMessage.errorType + " expected", equeue.errors.size() > 0);
   assertTrue("too many errors; " + equeue.errors, equeue.errors.size() <= 1);
   assertNotNull("couldn't find expected error: " + expectedMessage.errorType, foundMsg);
   /*
   assertTrue("error is not a GrammarSemanticsMessage",
   		   foundMsg instanceof GrammarSemanticsMessage);
    */
   assertTrue(Arrays.equals(expectedMessage.args, foundMsg.args));
 }
Пример #8
0
 private static ClassLoaderReference getClassLoader(
     EvaluationContext context, DebugProcess process)
     throws EvaluateException, InvocationException, InvalidTypeException, ClassNotLoadedException,
         IncompatibleThreadStateException {
   // TODO: cache
   ClassType loaderClass =
       (ClassType) process.findClass(context, "java.net.URLClassLoader", context.getClassLoader());
   Method ctorMethod =
       loaderClass.concreteMethodByName("<init>", "([Ljava/net/URL;Ljava/lang/ClassLoader;)V");
   ClassLoaderReference reference =
       (ClassLoaderReference)
           process.newInstance(
               context,
               loaderClass,
               ctorMethod,
               Arrays.asList(createURLArray(context), context.getClassLoader()));
   keep(reference, context);
   return reference;
 }
Пример #9
0
 public static void compileKotlinWithJava(
     @NotNull List<File> javaFiles,
     @NotNull List<File> ktFiles,
     @NotNull File outDir,
     @NotNull Disposable disposable,
     @Nullable File javaErrorFile)
     throws IOException {
   if (!ktFiles.isEmpty()) {
     compileKotlinToDirAndGetAnalysisResult(ktFiles, outDir, disposable, ALL, false);
   } else {
     boolean mkdirs = outDir.mkdirs();
     assert mkdirs : "Not created: " + outDir;
   }
   if (!javaFiles.isEmpty()) {
     compileJavaFiles(
         javaFiles,
         Arrays.asList(
             "-classpath",
             outDir.getPath() + File.pathSeparator + ForTestCompileRuntime.runtimeJarForTests(),
             "-d",
             outDir.getPath()),
         javaErrorFile);
   }
 }
Пример #10
0
  /**
   * Wow! much faster than compiling outside of VM. Finicky though. Had rules called r and modulo.
   * Wouldn't compile til I changed to 'a'.
   */
  protected boolean compile(String fileName) {
    String classpathOption = "-classpath";

    String[] args =
        new String[] {
          "javac",
          "-d",
          tmpdir,
          classpathOption,
          tmpdir + pathSep + CLASSPATH,
          tmpdir + "/" + fileName
        };
    String cmdLine =
        "javac"
            + " -d "
            + tmpdir
            + " "
            + classpathOption
            + " "
            + tmpdir
            + pathSep
            + CLASSPATH
            + " "
            + fileName;
    // System.out.println("compile: "+cmdLine);

    File f = new File(tmpdir, fileName);
    JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
    //		DiagnosticCollector<JavaFileObject> diagnostics =
    //			new DiagnosticCollector<JavaFileObject>();

    StandardJavaFileManager fileManager = compiler.getStandardFileManager(null, null, null);

    Iterable<? extends JavaFileObject> compilationUnits =
        fileManager.getJavaFileObjectsFromFiles(Arrays.asList(f));

    Iterable<String> compileOptions =
        Arrays.asList(new String[] {"-d", tmpdir, "-cp", tmpdir + pathSep + CLASSPATH});

    JavaCompiler.CompilationTask task =
        compiler.getTask(null, fileManager, null, compileOptions, null, compilationUnits);
    boolean ok = task.call();

    try {
      fileManager.close();
    } catch (IOException ioe) {
      ioe.printStackTrace(System.err);
    }

    //		List<String> errors = new ArrayList<String>();
    //		for (Diagnostic diagnostic : diagnostics.getDiagnostics()) {
    //			errors.add(
    //				String.valueOf(diagnostic.getLineNumber())+
    //				": " + diagnostic.getMessage(null));
    //		}
    //		if ( errors.size()>0 ) {
    //			System.err.println("compile stderr from: "+cmdLine);
    //			System.err.println(errors);
    //			return false;
    //		}
    return ok;

    /*
    File outputDir = new File(tmpdir);
    try {
    	Process process =
    		Runtime.getRuntime().exec(args, null, outputDir);
    	StreamVacuum stdout = new StreamVacuum(process.getInputStream());
    	StreamVacuum stderr = new StreamVacuum(process.getErrorStream());
    	stdout.start();
    	stderr.start();
    	process.waitFor();
              stdout.join();
              stderr.join();
    	if ( stdout.toString().length()>0 ) {
    		System.err.println("compile stdout from: "+cmdLine);
    		System.err.println(stdout);
    	}
    	if ( stderr.toString().length()>0 ) {
    		System.err.println("compile stderr from: "+cmdLine);
    		System.err.println(stderr);
    	}
    	int ret = process.exitValue();
    	return ret==0;
    }
    catch (Exception e) {
    	System.err.println("can't exec compilation");
    	e.printStackTrace(System.err);
    	return false;
    }
    */
  }
Пример #11
0
 public void setFlags(Flags... flags) {
   this.flags = new HashSet<Flags>(Arrays.asList(flags));
 }
/** @author Eugene Zhuravlev Date: 9/21/11 */
public class JavaBuilder extends ModuleLevelBuilder {
  private static final Logger LOG =
      Logger.getInstance("#org.jetbrains.jps.incremental.java.JavaBuilder");
  public static final String BUILDER_NAME = "java";
  private static final String JAVA_EXTENSION = ".java";
  public static final boolean USE_EMBEDDED_JAVAC =
      System.getProperty(GlobalOptions.USE_EXTERNAL_JAVAC_OPTION) == null;
  private static final Key<Integer> JAVA_COMPILER_VERSION_KEY =
      Key.create("_java_compiler_version_");
  private static final Key<Boolean> IS_ENABLED = Key.create("_java_compiler_enabled_");
  private static final Key<AtomicReference<String>> COMPILER_VERSION_INFO =
      Key.create("_java_compiler_version_info_");

  private static final Set<String> FILTERED_OPTIONS =
      new HashSet<String>(Arrays.<String>asList("-target"));
  private static final Set<String> FILTERED_SINGLE_OPTIONS =
      new HashSet<String>(
          Arrays.<String>asList(
              "-g",
              "-deprecation",
              "-nowarn",
              "-verbose",
              "-proc:none",
              "-proc:only",
              "-proceedOnError"));

  private static final FileFilter JAVA_SOURCES_FILTER =
      SystemInfo.isFileSystemCaseSensitive
          ? new FileFilter() {
            public boolean accept(File file) {
              return file.getPath().endsWith(JAVA_EXTENSION);
            }
          }
          : new FileFilter() {
            public boolean accept(File file) {
              return StringUtil.endsWithIgnoreCase(file.getPath(), JAVA_EXTENSION);
            }
          };

  private final Executor myTaskRunner;
  private static final List<ClassPostProcessor> ourClassProcessors =
      new ArrayList<ClassPostProcessor>();

  public static void registerClassPostProcessor(ClassPostProcessor processor) {
    ourClassProcessors.add(processor);
  }

  public JavaBuilder(Executor tasksExecutor) {
    super(BuilderCategory.TRANSLATOR);
    myTaskRunner = new SequentialTaskExecutor(tasksExecutor);
    // add here class processors in the sequence they should be executed
  }

  @NotNull
  public String getPresentableName() {
    return BUILDER_NAME;
  }

  @Override
  public void buildStarted(CompileContext context) {
    final JpsProject project = context.getProjectDescriptor().getProject();
    final JpsJavaCompilerConfiguration config =
        JpsJavaExtensionService.getInstance().getCompilerConfiguration(project);
    final String compilerId = config == null ? JavaCompilers.JAVAC_ID : config.getJavaCompilerId();
    if (LOG.isDebugEnabled()) {
      LOG.debug("Java compiler ID: " + compilerId);
    }
    final boolean isJavac =
        JavaCompilers.JAVAC_ID.equalsIgnoreCase(compilerId)
            || JavaCompilers.JAVAC_API_ID.equalsIgnoreCase(compilerId);
    final boolean isEclipse =
        JavaCompilers.ECLIPSE_ID.equalsIgnoreCase(compilerId)
            || JavaCompilers.ECLIPSE_EMBEDDED_ID.equalsIgnoreCase(compilerId);
    IS_ENABLED.set(context, isJavac || isEclipse);
    String messageText = null;
    if (isJavac) {
      messageText =
          "Using javac " + System.getProperty("java.version") + " to compile java sources";
    } else if (isEclipse) {
      messageText = "Using eclipse compiler to compile java sources";
    }
    COMPILER_VERSION_INFO.set(context, new AtomicReference<String>(messageText));
  }

  public ExitCode build(
      final CompileContext context,
      final ModuleChunk chunk,
      DirtyFilesHolder<JavaSourceRootDescriptor, ModuleBuildTarget> dirtyFilesHolder,
      OutputConsumer outputConsumer)
      throws ProjectBuildException {
    if (!IS_ENABLED.get(context, Boolean.TRUE)) {
      return ExitCode.NOTHING_DONE;
    }
    try {
      final Map<File, ModuleBuildTarget> filesToCompile =
          new THashMap<File, ModuleBuildTarget>(FileUtil.FILE_HASHING_STRATEGY);

      dirtyFilesHolder.processDirtyFiles(
          new FileProcessor<JavaSourceRootDescriptor, ModuleBuildTarget>() {
            public boolean apply(
                ModuleBuildTarget target, File file, JavaSourceRootDescriptor descriptor)
                throws IOException {
              if (JAVA_SOURCES_FILTER.accept(file)) {
                filesToCompile.put(file, target);
              }
              return true;
            }
          });

      if (context.isMake()) {
        final ProjectBuilderLogger logger = context.getLoggingManager().getProjectBuilderLogger();
        if (logger.isEnabled()) {
          if (filesToCompile.size() > 0) {
            logger.logCompiledFiles(filesToCompile.keySet(), BUILDER_NAME, "Compiling files:");
          }
        }
      }

      return compile(context, chunk, dirtyFilesHolder, filesToCompile.keySet(), outputConsumer);
    } catch (ProjectBuildException e) {
      throw e;
    } catch (Exception e) {
      String message = e.getMessage();
      if (message == null) {
        final ByteArrayOutputStream out = new ByteArrayOutputStream();
        final PrintStream stream = new PrintStream(out);
        try {
          e.printStackTrace(stream);
        } finally {
          stream.close();
        }
        message = "Internal error: \n" + out.toString();
      }
      context.processMessage(new CompilerMessage(BUILDER_NAME, BuildMessage.Kind.ERROR, message));
      throw new ProjectBuildException(message, e);
    }
  }

  @Override
  public boolean shouldHonorFileEncodingForCompilation(File file) {
    return JAVA_SOURCES_FILTER.accept(file);
  }

  private ExitCode compile(
      final CompileContext context,
      ModuleChunk chunk,
      DirtyFilesHolder<JavaSourceRootDescriptor, ModuleBuildTarget> dirtyFilesHolder,
      Collection<File> files,
      OutputConsumer outputConsumer)
      throws Exception {
    ExitCode exitCode = ExitCode.NOTHING_DONE;

    final boolean hasSourcesToCompile = !files.isEmpty();

    if (!hasSourcesToCompile && !dirtyFilesHolder.hasRemovedFiles()) {
      return exitCode;
    }

    final ProjectDescriptor pd = context.getProjectDescriptor();

    JavaBuilderUtil.ensureModuleHasJdk(
        chunk.representativeTarget().getModule(), context, BUILDER_NAME);
    final Collection<File> classpath =
        ProjectPaths.getCompilationClasspath(chunk, false /*context.isProjectRebuild()*/);
    final Collection<File> platformCp =
        ProjectPaths.getPlatformCompilationClasspath(chunk, false /*context.isProjectRebuild()*/);

    // begin compilation round
    final DiagnosticSink diagnosticSink = new DiagnosticSink(context);
    final Mappings delta = pd.dataManager.getMappings().createDelta();
    final Callbacks.Backend mappingsCallback = delta.getCallback();
    final OutputFilesSink outputSink =
        new OutputFilesSink(context, outputConsumer, mappingsCallback, chunk.getName());
    try {
      if (hasSourcesToCompile) {
        final AtomicReference<String> ref = COMPILER_VERSION_INFO.get(context);
        final String versionInfo =
            ref.getAndSet(null); // display compiler version info only once per compile session
        if (versionInfo != null) {
          LOG.info(versionInfo);
          context.processMessage(new CompilerMessage("", BuildMessage.Kind.INFO, versionInfo));
        }
        exitCode = ExitCode.OK;

        final Set<File> srcPath = new HashSet<File>();
        final BuildRootIndex index = pd.getBuildRootIndex();
        for (ModuleBuildTarget target : chunk.getTargets()) {
          for (JavaSourceRootDescriptor rd : index.getTempTargetRoots(target, context)) {
            srcPath.add(rd.root);
          }
        }

        final String chunkName = chunk.getName();
        context.processMessage(new ProgressMessage("Parsing java... [" + chunkName + "]"));

        final int filesCount = files.size();
        boolean compiledOk = true;
        if (filesCount > 0) {
          LOG.info(
              "Compiling "
                  + filesCount
                  + " java files; module: "
                  + chunkName
                  + (chunk.containsTests() ? " (tests)" : ""));
          if (LOG.isDebugEnabled()) {
            for (File file : files) {
              LOG.debug("Compiling " + file.getPath());
            }
            LOG.debug(" classpath for " + chunkName + ":");
            for (File file : classpath) {
              LOG.debug("  " + file.getAbsolutePath());
            }
            LOG.debug(" platform classpath for " + chunkName + ":");
            for (File file : platformCp) {
              LOG.debug("  " + file.getAbsolutePath());
            }
          }
          compiledOk =
              compileJava(
                  context,
                  chunk,
                  files,
                  classpath,
                  platformCp,
                  srcPath,
                  diagnosticSink,
                  outputSink);
        }

        context.checkCanceled();

        if (!compiledOk && diagnosticSink.getErrorCount() == 0) {
          diagnosticSink.report(
              new PlainMessageDiagnostic(
                  Diagnostic.Kind.ERROR, "Compilation failed: internal java compiler error"));
        }
        if (!Utils.PROCEED_ON_ERROR_KEY.get(context, Boolean.FALSE)
            && diagnosticSink.getErrorCount() > 0) {
          if (!compiledOk) {
            diagnosticSink.report(
                new PlainMessageDiagnostic(
                    Diagnostic.Kind.OTHER,
                    "Errors occurred while compiling module '" + chunkName + "'"));
          }
          throw new ProjectBuildException(
              "Compilation failed: errors: "
                  + diagnosticSink.getErrorCount()
                  + "; warnings: "
                  + diagnosticSink.getWarningCount());
        }
      }
    } finally {
      if (JavaBuilderUtil.updateMappings(
          context, delta, dirtyFilesHolder, chunk, files, outputSink.getSuccessfullyCompiled())) {
        exitCode = ExitCode.ADDITIONAL_PASS_REQUIRED;
      }
    }

    return exitCode;
  }

  private boolean compileJava(
      final CompileContext context,
      ModuleChunk chunk,
      Collection<File> files,
      Collection<File> classpath,
      Collection<File> platformCp,
      Collection<File> sourcePath,
      DiagnosticOutputConsumer diagnosticSink,
      final OutputFileConsumer outputSink)
      throws Exception {

    final TasksCounter counter = new TasksCounter();
    COUNTER_KEY.set(context, counter);

    final JpsJavaExtensionService javaExt = JpsJavaExtensionService.getInstance();
    final JpsJavaCompilerConfiguration compilerConfig =
        javaExt.getCompilerConfiguration(context.getProjectDescriptor().getProject());
    assert compilerConfig != null;

    final Set<JpsModule> modules = chunk.getModules();
    ProcessorConfigProfile profile = null;
    if (modules.size() == 1) {
      final JpsModule module = modules.iterator().next();
      profile = compilerConfig.getAnnotationProcessingProfile(module);
    } else {
      // perform cycle-related validations
      Pair<String, LanguageLevel> pair = null;
      for (JpsModule module : modules) {
        final LanguageLevel moduleLevel = javaExt.getLanguageLevel(module);
        if (pair == null) {
          pair = Pair.create(module.getName(), moduleLevel); // first value
        } else {
          if (!Comparing.equal(pair.getSecond(), moduleLevel)) {
            final String message =
                "Modules "
                    + pair.getFirst()
                    + " and "
                    + module.getName()
                    + " must have the same language level because of cyclic dependencies between them";
            diagnosticSink.report(new PlainMessageDiagnostic(Diagnostic.Kind.ERROR, message));
            return true;
          }
        }
      }

      // check that all chunk modules are excluded from annotation processing
      for (JpsModule module : modules) {
        final ProcessorConfigProfile prof = compilerConfig.getAnnotationProcessingProfile(module);
        if (prof.isEnabled()) {
          final String message =
              "Annotation processing is not supported for module cycles. Please ensure that all modules from cycle ["
                  + chunk.getName()
                  + "] are excluded from annotation processing";
          diagnosticSink.report(new PlainMessageDiagnostic(Diagnostic.Kind.ERROR, message));
          return true;
        }
      }
    }

    final Map<File, Set<File>> outs = buildOutputDirectoriesMap(context, chunk);
    final List<String> options = getCompilationOptions(context, chunk, profile);
    final ClassProcessingConsumer classesConsumer =
        new ClassProcessingConsumer(context, outputSink);
    if (LOG.isDebugEnabled()) {
      LOG.debug(
          "Compiling chunk ["
              + chunk.getName()
              + "] with options: \""
              + StringUtil.join(options, " ")
              + "\"");
    }
    try {
      final boolean rc;
      if (USE_EMBEDDED_JAVAC) {
        final boolean useEclipse = useEclipseCompiler(context);
        rc =
            JavacMain.compile(
                options,
                files,
                classpath,
                platformCp,
                sourcePath,
                outs,
                diagnosticSink,
                classesConsumer,
                context.getCancelStatus(),
                useEclipse);
      } else {
        final JavacServerClient client = ensureJavacServerLaunched(context);
        final RequestFuture<JavacServerResponseHandler> future =
            client.sendCompileRequest(
                options,
                files,
                classpath,
                platformCp,
                sourcePath,
                outs,
                diagnosticSink,
                classesConsumer);
        while (!future.waitFor(100L, TimeUnit.MILLISECONDS)) {
          if (context.getCancelStatus().isCanceled()) {
            future.cancel(false);
          }
        }
        rc = future.getMessageHandler().isTerminatedSuccessfully();
      }
      return rc;
    } finally {
      counter.await();
    }
  }

  private static boolean useEclipseCompiler(CompileContext context) {
    if (!USE_EMBEDDED_JAVAC) {
      return false;
    }
    JpsProject project = context.getProjectDescriptor().getProject();
    final JpsJavaCompilerConfiguration configuration =
        JpsJavaExtensionService.getInstance().getCompilerConfiguration(project);
    final String compilerId = configuration != null ? configuration.getJavaCompilerId() : null;
    return JavaCompilers.ECLIPSE_ID.equalsIgnoreCase(compilerId)
        || JavaCompilers.ECLIPSE_EMBEDDED_ID.equalsIgnoreCase(compilerId);
  }

  private void submitAsyncTask(CompileContext context, final Runnable taskRunnable) {
    final TasksCounter counter = COUNTER_KEY.get(context);

    assert counter != null;

    counter.incTaskCount();
    myTaskRunner.execute(
        new Runnable() {
          public void run() {
            try {
              taskRunnable.run();
            } finally {
              counter.decTaskCounter();
            }
          }
        });
  }

  private static synchronized JavacServerClient ensureJavacServerLaunched(CompileContext context)
      throws Exception {
    final ExternalJavacDescriptor descriptor = ExternalJavacDescriptor.KEY.get(context);
    if (descriptor != null) {
      return descriptor.client;
    }
    // start server here
    final int port = findFreePort();
    final int heapSize = getJavacServerHeapSize(context);

    // defaulting to the same jdk that used to run the build process
    String javaHome = SystemProperties.getJavaHome();
    int javaVersion = convertToNumber(SystemProperties.getJavaVersion());

    for (JpsSdk<?> sdk : context.getProjectDescriptor().getProjectJavaSdks()) {
      final String version = sdk.getVersionString();
      final int ver = convertToNumber(version);
      if (ver > javaVersion) {
        javaVersion = ver;
        javaHome = sdk.getHomePath();
      }
    }

    final BaseOSProcessHandler processHandler =
        JavacServerBootstrap.launchJavacServer(
            javaHome, heapSize, port, Utils.getSystemRoot(), getCompilationVMOptions(context));
    final JavacServerClient client = new JavacServerClient();
    try {
      client.connect("127.0.0.1", port);
    } catch (Throwable ex) {
      processHandler.destroyProcess();
      throw new Exception("Failed to connect to external javac process: ", ex);
    }
    ExternalJavacDescriptor.KEY.set(context, new ExternalJavacDescriptor(processHandler, client));
    return client;
  }

  private static int convertToNumber(String ver) {
    if (ver == null) {
      return 0;
    }
    final int quoteBegin = ver.indexOf("\"");
    if (quoteBegin >= 0) {
      final int quoteEnd = ver.indexOf("\"", quoteBegin + 1);
      if (quoteEnd > quoteBegin) {
        ver = ver.substring(quoteBegin + 1, quoteEnd);
      }
    }
    if (ver.isEmpty()) {
      return 0;
    }

    final String prefix = "1.";
    final int parseBegin = ver.startsWith(prefix) ? prefix.length() : 0;

    final int parseEnd = ver.indexOf(".", parseBegin);
    if (parseEnd > 0) {
      ver = ver.substring(parseBegin, parseEnd);
    } else {
      ver = ver.substring(parseBegin);
    }

    try {
      return Integer.parseInt(ver);
    } catch (NumberFormatException ignored) {
    }
    return 0;
  }

  private static int findFreePort() {
    try {
      final ServerSocket serverSocket = new ServerSocket(0);
      try {
        return serverSocket.getLocalPort();
      } finally {
        // workaround for linux : calling close() immediately after opening socket
        // may result that socket is not closed
        synchronized (serverSocket) {
          try {
            serverSocket.wait(1);
          } catch (Throwable ignored) {
          }
        }
        serverSocket.close();
      }
    } catch (IOException e) {
      e.printStackTrace(System.err);
      return JavacServer.DEFAULT_SERVER_PORT;
    }
  }

  private static int getJavacServerHeapSize(CompileContext context) {
    final JpsProject project = context.getProjectDescriptor().getProject();
    final JpsJavaCompilerConfiguration config =
        JpsJavaExtensionService.getInstance().getOrCreateCompilerConfiguration(project);
    final JpsJavaCompilerOptions options = config.getCurrentCompilerOptions();
    return options.MAXIMUM_HEAP_SIZE;
  }

  private static final Key<List<String>> JAVAC_OPTIONS = Key.create("_javac_options_");
  private static final Key<List<String>> JAVAC_VM_OPTIONS = Key.create("_javac_vm_options_");

  private static List<String> getCompilationVMOptions(CompileContext context) {
    List<String> cached = JAVAC_VM_OPTIONS.get(context);
    if (cached == null) {
      loadCommonJavacOptions(context);
      cached = JAVAC_VM_OPTIONS.get(context);
    }
    return cached;
  }

  private static List<String> getCompilationOptions(
      CompileContext context, ModuleChunk chunk, @Nullable ProcessorConfigProfile profile) {
    List<String> cached = JAVAC_OPTIONS.get(context);
    if (cached == null) {
      loadCommonJavacOptions(context);
      cached = JAVAC_OPTIONS.get(context);
      assert cached != null : context;
    }

    List<String> options = new ArrayList<String>(cached);
    addCompilationOptions(options, context, chunk, profile);
    return options;
  }

  public static void addCompilationOptions(
      List<String> options,
      CompileContext context,
      ModuleChunk chunk,
      @Nullable ProcessorConfigProfile profile) {
    if (!isEncodingSet(options)) {
      final CompilerEncodingConfiguration config =
          context.getProjectDescriptor().getEncodingConfiguration();
      final String encoding = config.getPreferredModuleChunkEncoding(chunk);
      if (config.getAllModuleChunkEncodings(chunk).size() > 1) {
        final StringBuilder msgBuilder = new StringBuilder();
        msgBuilder.append("Multiple encodings set for module chunk ").append(chunk.getName());
        if (encoding != null) {
          msgBuilder.append("\n\"").append(encoding).append("\" will be used by compiler");
        }
        context.processMessage(
            new CompilerMessage(BUILDER_NAME, BuildMessage.Kind.INFO, msgBuilder.toString()));
      }
      if (!StringUtil.isEmpty(encoding)) {
        options.add("-encoding");
        options.add(encoding);
      }
    }

    final String langLevel = getLanguageLevel(chunk.getModules().iterator().next());
    if (!StringUtil.isEmpty(langLevel)) {
      options.add("-source");
      options.add(langLevel);
    }

    JpsJavaCompilerConfiguration compilerConfiguration =
        JpsJavaExtensionService.getInstance()
            .getOrCreateCompilerConfiguration(context.getProjectDescriptor().getProject());
    String bytecodeTarget = null;
    int chunkSdkVersion = -1;
    for (JpsModule module : chunk.getModules()) {
      final JpsSdk<JpsDummyElement> sdk = module.getSdk(JpsJavaSdkType.INSTANCE);
      if (sdk != null) {
        final int moduleSdkVersion = convertToNumber(sdk.getVersionString());
        if (moduleSdkVersion != 0 /*could determine the version*/
            && (chunkSdkVersion < 0 || chunkSdkVersion > moduleSdkVersion)) {
          chunkSdkVersion = moduleSdkVersion;
        }
      }

      final String moduleTarget = compilerConfiguration.getByteCodeTargetLevel(module.getName());
      if (moduleTarget == null) {
        continue;
      }
      if (bytecodeTarget == null) {
        bytecodeTarget = moduleTarget;
      } else {
        if (moduleTarget.compareTo(bytecodeTarget) < 0) {
          bytecodeTarget =
              moduleTarget; // use the lower possible target among modules that form the chunk
        }
      }
    }
    if (bytecodeTarget != null) {
      options.add("-target");
      options.add(bytecodeTarget);
    } else {
      if (chunkSdkVersion > 0 && getCompilerSdkVersion(context) > chunkSdkVersion) {
        // force lower bytecode target level to match the version of sdk assigned to this chunk
        options.add("-target");
        options.add("1." + chunkSdkVersion);
      }
    }

    if (profile != null && profile.isEnabled()) {
      // configuring annotation processing
      if (!profile.isObtainProcessorsFromClasspath()) {
        final String processorsPath = profile.getProcessorPath();
        options.add("-processorpath");
        options.add(
            processorsPath == null ? "" : FileUtil.toSystemDependentName(processorsPath.trim()));
      }

      final Set<String> processors = profile.getProcessors();
      if (!processors.isEmpty()) {
        options.add("-processor");
        options.add(StringUtil.join(processors, ","));
      }

      for (Map.Entry<String, String> optionEntry : profile.getProcessorOptions().entrySet()) {
        options.add("-A" + optionEntry.getKey() + "=" + optionEntry.getValue());
      }

      final File srcOutput =
          ProjectPaths.getAnnotationProcessorGeneratedSourcesOutputDir(
              chunk.getModules().iterator().next(), chunk.containsTests(), profile);
      if (srcOutput != null) {
        srcOutput.mkdirs();
        options.add("-s");
        options.add(srcOutput.getPath());
      }
    } else {
      options.add("-proc:none");
    }
  }

  private static String getLanguageLevel(JpsModule module) {
    LanguageLevel level = JpsJavaExtensionService.getInstance().getLanguageLevel(module);
    if (level == null) return null;
    switch (level) {
      case JDK_1_3:
        return "1.3";
      case JDK_1_4:
        return "1.4";
      case JDK_1_5:
        return "1.5";
      case JDK_1_6:
        return "1.6";
      case JDK_1_7:
        return "1.7";
      case JDK_1_8:
        return "8";
      default:
        return null;
    }
  }

  private static boolean isEncodingSet(List<String> options) {
    for (String option : options) {
      if ("-encoding".equals(option)) {
        return true;
      }
    }
    return false;
  }

  private static int getCompilerSdkVersion(CompileContext context) {
    final Integer cached = JAVA_COMPILER_VERSION_KEY.get(context);
    if (cached != null) {
      return cached;
    }
    int javaVersion = convertToNumber(SystemProperties.getJavaVersion());
    if (!USE_EMBEDDED_JAVAC) {
      // in case of external javac, run compiler from the newest jdk that is used in the project
      for (JpsSdk<?> sdk : context.getProjectDescriptor().getProjectJavaSdks()) {
        final String version = sdk.getVersionString();
        final int ver = convertToNumber(version);
        if (ver > javaVersion) {
          javaVersion = ver;
        }
      }
    }
    JAVA_COMPILER_VERSION_KEY.set(context, javaVersion);
    return javaVersion;
  }

  private static void loadCommonJavacOptions(CompileContext context) {
    final List<String> options = new ArrayList<String>();
    final List<String> vmOptions = new ArrayList<String>();

    final JpsProject project = context.getProjectDescriptor().getProject();
    final JpsJavaCompilerConfiguration compilerConfig =
        JpsJavaExtensionService.getInstance().getOrCreateCompilerConfiguration(project);
    final JpsJavaCompilerOptions compilerOptions = compilerConfig.getCurrentCompilerOptions();
    if (compilerOptions.DEBUGGING_INFO) {
      options.add("-g");
    }
    if (compilerOptions.DEPRECATION) {
      options.add("-deprecation");
    }
    if (compilerOptions.GENERATE_NO_WARNINGS) {
      options.add("-nowarn");
    }
    if (compilerOptions instanceof EclipseCompilerOptions) {
      final EclipseCompilerOptions eclipseOptions = (EclipseCompilerOptions) compilerOptions;
      if (eclipseOptions.PROCEED_ON_ERROR) {
        options.add("-proceedOnError");
      }
    }
    final String customArgs = compilerOptions.ADDITIONAL_OPTIONS_STRING;
    if (customArgs != null) {
      final StringTokenizer customOptsTokenizer = new StringTokenizer(customArgs, " \t\r\n");
      boolean skip = false;
      while (customOptsTokenizer.hasMoreTokens()) {
        final String userOption = customOptsTokenizer.nextToken();
        if (FILTERED_OPTIONS.contains(userOption)) {
          skip = true;
          continue;
        }
        if (!skip) {
          if (!FILTERED_SINGLE_OPTIONS.contains(userOption)) {
            if (userOption.startsWith("-J-")) {
              vmOptions.add(userOption.substring("-J".length()));
            } else {
              options.add(userOption);
            }
          }
        }
      }
    }

    if (useEclipseCompiler(context)) {
      for (String option : options) {
        if (option.startsWith("-proceedOnError")) {
          Utils.PROCEED_ON_ERROR_KEY.set(context, Boolean.TRUE);
          break;
        }
      }
    }

    JAVAC_OPTIONS.set(context, options);
    JAVAC_VM_OPTIONS.set(context, vmOptions);
  }

  @Override
  public void chunkBuildFinished(CompileContext context, ModuleChunk chunk) {
    JavaBuilderUtil.cleanupChunkResources(context);
  }

  private static Map<File, Set<File>> buildOutputDirectoriesMap(
      CompileContext context, ModuleChunk chunk) {
    final Map<File, Set<File>> map = new THashMap<File, Set<File>>(FileUtil.FILE_HASHING_STRATEGY);
    for (ModuleBuildTarget target : chunk.getTargets()) {
      final File outputDir = target.getOutputDir();
      if (outputDir == null) {
        continue;
      }
      final Set<File> roots = new THashSet<File>(FileUtil.FILE_HASHING_STRATEGY);
      for (JavaSourceRootDescriptor descriptor :
          context.getProjectDescriptor().getBuildRootIndex().getTargetRoots(target, context)) {
        roots.add(descriptor.root);
      }
      map.put(outputDir, roots);
    }
    return map;
  }

  private class DiagnosticSink implements DiagnosticOutputConsumer {
    private final CompileContext myContext;
    private volatile int myErrorCount = 0;
    private volatile int myWarningCount = 0;

    public DiagnosticSink(CompileContext context) {
      myContext = context;
    }

    public void registerImports(
        final String className,
        final Collection<String> imports,
        final Collection<String> staticImports) {
      // submitAsyncTask(myContext, new Runnable() {
      //  public void run() {
      //    final Callbacks.Backend callback = DELTA_MAPPINGS_CALLBACK_KEY.get(myContext);
      //    if (callback != null) {
      //      callback.registerImports(className, imports, staticImports);
      //    }
      //  }
      // });
    }

    public void outputLineAvailable(String line) {
      if (!StringUtil.isEmpty(line)) {
        if (line.contains("java.lang.OutOfMemoryError")) {
          myContext.processMessage(
              new CompilerMessage(
                  BUILDER_NAME, BuildMessage.Kind.ERROR, "OutOfMemoryError: insufficient memory"));
          myErrorCount++;
        } else {
          final BuildMessage.Kind kind = getKindByMessageText(line);
          if (kind == BuildMessage.Kind.ERROR) {
            myErrorCount++;
          } else if (kind == BuildMessage.Kind.WARNING) {
            myWarningCount++;
          }
          myContext.processMessage(new CompilerMessage(BUILDER_NAME, kind, line));
        }
      }
    }

    private BuildMessage.Kind getKindByMessageText(String line) {
      final String lowercasedLine = line.toLowerCase(Locale.US);
      if (lowercasedLine.contains("error") || lowercasedLine.contains("requires target release")) {
        return BuildMessage.Kind.ERROR;
      }
      return BuildMessage.Kind.INFO;
    }

    public void report(Diagnostic<? extends JavaFileObject> diagnostic) {
      final CompilerMessage.Kind kind;
      switch (diagnostic.getKind()) {
        case ERROR:
          kind = BuildMessage.Kind.ERROR;
          myErrorCount++;
          break;
        case MANDATORY_WARNING:
        case WARNING:
        case NOTE:
          kind = BuildMessage.Kind.WARNING;
          myWarningCount++;
          break;
        default:
          kind = BuildMessage.Kind.INFO;
      }
      File sourceFile = null;
      try {
        // for eclipse compiler just an attempt to call getSource() may lead to an NPE,
        // so calling this method under try/catch to avoid induced compiler errors
        final JavaFileObject source = diagnostic.getSource();
        sourceFile = source != null ? Utils.convertToFile(source.toUri()) : null;
      } catch (Exception e) {
        LOG.info(e);
      }
      final String srcPath =
          sourceFile != null ? FileUtil.toSystemIndependentName(sourceFile.getPath()) : null;
      String message = diagnostic.getMessage(Locale.US);
      if (Utils.IS_TEST_MODE) {
        LOG.info(message);
      }
      myContext.processMessage(
          new CompilerMessage(
              BUILDER_NAME,
              kind,
              message,
              srcPath,
              diagnostic.getStartPosition(),
              diagnostic.getEndPosition(),
              diagnostic.getPosition(),
              diagnostic.getLineNumber(),
              diagnostic.getColumnNumber()));
    }

    public int getErrorCount() {
      return myErrorCount;
    }

    public int getWarningCount() {
      return myWarningCount;
    }
  }

  private class ClassProcessingConsumer implements OutputFileConsumer {
    private final CompileContext myContext;
    private final OutputFileConsumer myDelegateOutputFileSink;

    public ClassProcessingConsumer(CompileContext context, OutputFileConsumer sink) {
      myContext = context;
      myDelegateOutputFileSink =
          sink != null
              ? sink
              : new OutputFileConsumer() {
                public void save(@NotNull OutputFileObject fileObject) {
                  throw new RuntimeException("Output sink for compiler was not specified");
                }
              };
    }

    public void save(@NotNull final OutputFileObject fileObject) {
      if (JavaFileObject.Kind.CLASS != fileObject.getKind()) {
        // generated sources or resources must be saved synchronously, because some compilers (e.g.
        // eclipse)
        // may want to read generated text for further compilation
        try {
          final BinaryContent content = fileObject.getContent();
          if (content != null) {
            content.saveToFile(fileObject.getFile());
          }
        } catch (IOException e) {
          myContext.processMessage(
              new CompilerMessage(BUILDER_NAME, BuildMessage.Kind.ERROR, e.getMessage()));
        }
      }

      submitAsyncTask(
          myContext,
          new Runnable() {
            public void run() {
              try {
                for (ClassPostProcessor processor : ourClassProcessors) {
                  processor.process(myContext, fileObject);
                }
              } finally {
                myDelegateOutputFileSink.save(fileObject);
              }
            }
          });
    }
  }

  private static final Key<TasksCounter> COUNTER_KEY = Key.create("_async_task_counter_");

  private static final class TasksCounter {
    private int myCounter = 0;

    public synchronized void incTaskCount() {
      myCounter++;
    }

    public synchronized void decTaskCounter() {
      myCounter = Math.max(0, myCounter - 1);
      if (myCounter == 0) {
        notifyAll();
      }
    }

    public synchronized void await() {
      while (myCounter > 0) {
        try {
          wait();
        } catch (InterruptedException e) {
        }
      }
    }
  }
}
Пример #13
0
/** @author Eugene Zhuravlev Date: 1/21/12 */
public class JavacMain {
  private static final boolean IS_VM_6_VERSION =
      System.getProperty("java.version", "1.6").contains("1.6");
  // private static final boolean ECLIPSE_COMPILER_SINGLE_THREADED_MODE =
  // Boolean.parseBoolean(System.getProperty("jdt.compiler.useSingleThread", "false"));
  private static final Set<String> FILTERED_OPTIONS =
      new HashSet<String>(Arrays.<String>asList("-d", "-classpath", "-cp", "-bootclasspath"));
  private static final Set<String> FILTERED_SINGLE_OPTIONS =
      new HashSet<String>(
          Arrays.<String>asList(
              /*javac options*/ "-verbose",
              "-proc:only",
              "-implicit:class",
              "-implicit:none",
              /*eclipse options*/ "-noExit"));

  public static boolean compile(
      Collection<String> options,
      final Collection<File> sources,
      Collection<File> classpath,
      Collection<File> platformClasspath,
      Collection<File> sourcePath,
      Map<File, Set<File>> outputDirToRoots,
      final DiagnosticOutputConsumer outConsumer,
      final OutputFileConsumer outputSink,
      CanceledStatus canceledStatus,
      boolean useEclipseCompiler) {
    JavaCompiler compiler = null;
    if (useEclipseCompiler) {
      for (JavaCompiler javaCompiler : ServiceLoader.load(JavaCompiler.class)) {
        compiler = javaCompiler;
        break;
      }
      if (compiler == null) {
        outConsumer.report(
            new PlainMessageDiagnostic(
                Diagnostic.Kind.ERROR, "Eclipse Batch Compiler was not found in classpath"));
        return false;
      }
    }

    final boolean nowUsingJavac;
    if (compiler == null) {
      compiler = ToolProvider.getSystemJavaCompiler();
      if (compiler == null) {
        outConsumer.report(
            new PlainMessageDiagnostic(
                Diagnostic.Kind.ERROR, "System Java Compiler was not found in classpath"));
        return false;
      }
      nowUsingJavac = true;
    } else {
      nowUsingJavac = false;
    }

    for (File outputDir : outputDirToRoots.keySet()) {
      outputDir.mkdirs();
    }

    final List<JavaSourceTransformer> transformers = getSourceTransformers();

    final JavacFileManager fileManager =
        new JavacFileManager(
            new ContextImpl(compiler, outConsumer, outputSink, canceledStatus, nowUsingJavac),
            transformers);

    fileManager.handleOption(
        "-bootclasspath", Collections.singleton("").iterator()); // this will clear cached stuff
    fileManager.handleOption(
        "-extdirs", Collections.singleton("").iterator()); // this will clear cached stuff

    try {
      fileManager.setOutputDirectories(outputDirToRoots);
    } catch (IOException e) {
      fileManager.getContext().reportMessage(Diagnostic.Kind.ERROR, e.getMessage());
      return false;
    }

    if (!classpath.isEmpty()) {
      try {
        fileManager.setLocation(StandardLocation.CLASS_PATH, classpath);
        if (!nowUsingJavac && !isOptionSet(options, "-processorpath")) {
          // for non-javac file manager ensure annotation processor path defaults to classpath
          fileManager.setLocation(StandardLocation.ANNOTATION_PROCESSOR_PATH, classpath);
        }
      } catch (IOException e) {
        fileManager.getContext().reportMessage(Diagnostic.Kind.ERROR, e.getMessage());
        return false;
      }
    }
    if (!platformClasspath.isEmpty()) {
      try {
        fileManager.setLocation(StandardLocation.PLATFORM_CLASS_PATH, platformClasspath);
      } catch (IOException e) {
        fileManager.getContext().reportMessage(Diagnostic.Kind.ERROR, e.getMessage());
        return false;
      }
    }
    try {
      // ensure the source path is set;
      // otherwise, if not set, javac attempts to search both classes and sources in classpath;
      // so if some classpath jars contain sources, it will attempt to compile them
      fileManager.setLocation(StandardLocation.SOURCE_PATH, sourcePath);
    } catch (IOException e) {
      fileManager.getContext().reportMessage(Diagnostic.Kind.ERROR, e.getMessage());
      return false;
    }

    //noinspection IOResourceOpenedButNotSafelyClosed
    final LineOutputWriter out =
        new LineOutputWriter() {
          protected void lineAvailable(String line) {
            if (nowUsingJavac) {
              outConsumer.outputLineAvailable(line);
            } else {
              // todo: filter too verbose eclipse output?
            }
          }
        };

    try {
      final Collection<String> _options = prepareOptions(options, nowUsingJavac);

      // to be on the safe side, we'll have to apply all options _before_ calling any of manager's
      // methods
      // i.e. getJavaFileObjectsFromFiles()
      // This way the manager will be properly initialized. Namely, the encoding will be set
      // correctly
      for (Iterator<String> iterator = _options.iterator(); iterator.hasNext(); ) {
        fileManager.handleOption(iterator.next(), iterator);
      }

      final JavaCompiler.CompilationTask task =
          compiler.getTask(
              out,
              fileManager,
              outConsumer,
              _options,
              null,
              fileManager.getJavaFileObjectsFromFiles(sources));

      // if (!IS_VM_6_VERSION) { //todo!
      //  // Do not add the processor for JDK 1.6 because of the bugs in javac
      //  // The processor's presence may lead to NPE and resolve bugs in compiler
      //  final JavacASTAnalyser analyzer = new JavacASTAnalyser(outConsumer,
      // !annotationProcessingEnabled);
      //  task.setProcessors(Collections.singleton(analyzer));
      // }
      return task.call();
    } catch (IllegalArgumentException e) {
      outConsumer.report(new PlainMessageDiagnostic(Diagnostic.Kind.ERROR, e.getMessage()));
    } catch (CompilationCanceledException ignored) {
      outConsumer.report(
          new PlainMessageDiagnostic(Diagnostic.Kind.OTHER, "Compilation was canceled"));
    } finally {
      fileManager.close();
      if (nowUsingJavac) {
        cleanupJavacNameTable();
      }
    }
    return false;
  }

  private static List<JavaSourceTransformer> getSourceTransformers() {
    final Class<JavaSourceTransformer> transformerClass = JavaSourceTransformer.class;
    final ServiceLoader<JavaSourceTransformer> loader =
        ServiceLoader.load(transformerClass, transformerClass.getClassLoader());
    final List<JavaSourceTransformer> transformers = new ArrayList<JavaSourceTransformer>();
    for (JavaSourceTransformer t : loader) {
      transformers.add(t);
    }
    return transformers;
  }

  private static boolean isAnnotationProcessingEnabled(final Collection<String> options) {
    for (String option : options) {
      if ("-proc:none".equals(option)) {
        return false;
      }
    }
    return true;
  }

  private static boolean isOptionSet(final Collection<String> options, String option) {
    for (String opt : options) {
      if (option.equals(opt)) {
        return true;
      }
    }
    return false;
  }

  private static Collection<String> prepareOptions(
      final Collection<String> options, boolean usingJavac) {
    final List<String> result = new ArrayList<String>();
    if (usingJavac) {
      result.add("-implicit:class"); // the option supported by javac only
    } else { // is Eclipse
      result.add("-noExit");
    }
    boolean skip = false;
    for (String option : options) {
      if (FILTERED_OPTIONS.contains(option)) {
        skip = true;
        continue;
      }
      if (!skip) {
        if (!FILTERED_SINGLE_OPTIONS.contains(option)) {
          result.add(option);
        }
      }
      skip = false;
    }
    return result;
  }

  private static class ContextImpl implements JavacFileManager.Context {
    private final StandardJavaFileManager myStdManager;
    private final DiagnosticOutputConsumer myOutConsumer;
    private final OutputFileConsumer myOutputFileSink;
    private final CanceledStatus myCanceledStatus;

    public ContextImpl(
        @NotNull JavaCompiler compiler,
        @NotNull DiagnosticOutputConsumer outConsumer,
        @NotNull OutputFileConsumer sink,
        CanceledStatus canceledStatus,
        boolean canUseOptimizedmanager) {
      myOutConsumer = outConsumer;
      myOutputFileSink = sink;
      myCanceledStatus = canceledStatus;
      StandardJavaFileManager stdManager = null;
      if (canUseOptimizedmanager) {
        final Class<StandardJavaFileManager> optimizedManagerClass =
            ClasspathBootstrap.getOptimizedFileManagerClass();
        if (optimizedManagerClass != null) {
          try {
            stdManager = optimizedManagerClass.newInstance();
          } catch (Throwable e) {
            if (SystemInfo.isWindows) {
              System.err.println(
                  "Failed to load JPS optimized file manager for javac: " + e.getMessage());
            }
          }
        }
      }
      if (stdManager != null) {
        myStdManager = stdManager;
      } else {
        myStdManager = compiler.getStandardFileManager(outConsumer, Locale.US, null);
      }
    }

    public boolean isCanceled() {
      return myCanceledStatus.isCanceled();
    }

    public StandardJavaFileManager getStandardFileManager() {
      return myStdManager;
    }

    public void reportMessage(final Diagnostic.Kind kind, String message) {
      myOutConsumer.report(new PlainMessageDiagnostic(kind, message));
    }

    public void consumeOutputFile(@NotNull final OutputFileObject cls) {
      myOutputFileSink.save(cls);
    }
  }

  private static boolean ourCleanupFailed = false;

  private static final class NameTableCleanupDataHolder {
    static final com.sun.tools.javac.util.List emptyList = com.sun.tools.javac.util.List.nil();
    static final Field freelistField;

    static {
      try {
        Field freelistRef;
        try {
          // trying jdk 6
          freelistRef =
              Class.forName("com.sun.tools.javac.util.Name$Table").getDeclaredField("freelist");
        } catch (Exception e) {
          // trying jdk 7
          freelistRef =
              Class.forName("com.sun.tools.javac.util.SharedNameTable")
                  .getDeclaredField("freelist");
        }
        freelistRef.setAccessible(true);
        freelistField = freelistRef;
      } catch (Exception e) {
        throw new RuntimeException(e);
      }
    }
  }

  private static void cleanupJavacNameTable() {
    try {
      if (!ourCleanupFailed) {
        NameTableCleanupDataHolder.freelistField.set(null, NameTableCleanupDataHolder.emptyList);
      }
    } catch (Throwable e) {
      ourCleanupFailed = true;
    }
  }
}