private static boolean packageResources(
      @NotNull AndroidFacet facet, @NotNull CompileContext context) {
    final Module module = facet.getModule();

    try {
      context.processMessage(
          new ProgressMessage(
              AndroidJpsBundle.message(
                  "android.jps.progress.packaging.resources", module.getName())));

      final File manifestFile = AndroidJpsUtil.getManifestFileForCompilationPath(facet);
      if (manifestFile == null) {
        context.processMessage(
            new CompilerMessage(
                BUILDER_NAME,
                BuildMessage.Kind.ERROR,
                AndroidJpsBundle.message(
                    "android.jps.errors.manifest.not.found", module.getName())));
        return false;
      }
      final ArrayList<String> assetsDirPaths = new ArrayList<String>();
      collectAssetDirs(facet, assetsDirPaths);

      final File outputDir =
          AndroidJpsUtil.getOutputDirectoryForPackagedFiles(context.getProjectPaths(), module);
      if (outputDir == null) {
        context.processMessage(
            new CompilerMessage(
                BUILDER_NAME,
                BuildMessage.Kind.ERROR,
                AndroidJpsBundle.message(
                    "android.jps.errors.output.dir.not.specified", module.getName())));
        return false;
      }

      final Pair<AndroidSdk, IAndroidTarget> pair =
          AndroidJpsUtil.getAndroidPlatform(module, context, BUILDER_NAME);
      if (pair == null) {
        return false;
      }
      final IAndroidTarget target = pair.getSecond();

      final String outputFilePath = getPackagedResourcesFile(module, outputDir).getPath();
      final String[] resourceDirPaths =
          AndroidJpsUtil.collectResourceDirsForCompilation(facet, true, context);

      return doPackageResources(
          context,
          manifestFile,
          target,
          resourceDirPaths,
          ArrayUtil.toStringArray(assetsDirPaths),
          outputFilePath,
          AndroidJpsUtil.isReleaseBuild(context));
    } catch (IOException e) {
      AndroidJpsUtil.reportExceptionError(context, null, e, BUILDER_NAME);
      return false;
    }
  }
  private static boolean runPngCaching(
      @NotNull CompileContext context,
      @NotNull Module module,
      @NotNull AndroidFileSetStorage storage,
      @Nullable AndroidFileSetState state)
      throws IOException {
    final AndroidFileSetState savedState = storage.getState(module.getName());
    if (context.isMake() && savedState != null && savedState.equalsTo(state)) {
      return true;
    }

    final AndroidFacet facet = AndroidJpsUtil.getFacet(module);
    if (facet == null) {
      return true;
    }

    context.processMessage(
        new CompilerMessage(
            BUILDER_NAME,
            BuildMessage.Kind.INFO,
            AndroidJpsBundle.message("android.jps.progress.res.caching", module.getName())));

    final File resourceDir = AndroidJpsUtil.getResourceDirForCompilationPath(facet);
    if (resourceDir == null) {
      return true;
    }

    final Pair<AndroidSdk, IAndroidTarget> pair =
        AndroidJpsUtil.getAndroidPlatform(module, context, BUILDER_NAME);
    if (pair == null) {
      return false;
    }

    final File resCacheDir = AndroidJpsUtil.getResourcesCacheDir(context, module);

    if (!resCacheDir.exists()) {
      if (!resCacheDir.mkdirs()) {
        context.processMessage(
            new CompilerMessage(
                BUILDER_NAME,
                BuildMessage.Kind.ERROR,
                "Cannot create directory " + resCacheDir.getPath()));
        return false;
      }
    }

    final IAndroidTarget target = pair.second;

    final Map<AndroidCompilerMessageKind, List<String>> messages =
        AndroidApt.crunch(
            target, Collections.singletonList(resourceDir.getPath()), resCacheDir.getPath());

    AndroidJpsUtil.addMessages(context, messages, BUILDER_NAME);

    final boolean success = messages.get(AndroidCompilerMessageKind.ERROR).isEmpty();
    storage.update(module.getName(), success ? state : null);
    return success;
  }
  private boolean performCompilation(
      List<String> args,
      StringWriter out,
      StringWriter err,
      Map<String, List<String>> outputs,
      CompileContext context,
      ModuleChunk chunk) {
    try {
      Class<?> mainClass = Class.forName(GreclipseMain.class.getName(), true, myGreclipseLoader);
      Constructor<?> constructor =
          mainClass.getConstructor(PrintWriter.class, PrintWriter.class, Map.class, Map.class);
      Method compileMethod = mainClass.getMethod("compile", String[].class);

      HashMap<String, Object> customDefaultOptions = ContainerUtil.newHashMap();
      // without this greclipse won't load AST transformations
      customDefaultOptions.put(
          "org.eclipse.jdt.core.compiler.groovy.groovyClassLoaderPath", getClasspathString(chunk));

      // used by greclipse to cache transform loaders
      // names should be different for production & tests
      customDefaultOptions.put(
          "org.eclipse.jdt.core.compiler.groovy.groovyProjectName",
          chunk.getPresentableShortName());

      Object main =
          constructor.newInstance(
              new PrintWriter(out), new PrintWriter(err), customDefaultOptions, outputs);
      return (Boolean) compileMethod.invoke(main, new Object[] {ArrayUtil.toStringArray(args)});
    } catch (Exception e) {
      context.processMessage(new CompilerMessage(getPresentableName(), e));
      return false;
    }
  }
  @Override
  public void build(
      @NotNull ErlangTarget target,
      @NotNull DirtyFilesHolder<ErlangSourceRootDescriptor, ErlangTarget> holder,
      @NotNull BuildOutputConsumer outputConsumer,
      @NotNull CompileContext context)
      throws ProjectBuildException, IOException {
    if (!holder.hasDirtyFiles() && !holder.hasRemovedFiles()) return;

    JpsModule module = target.getModule();
    JpsProject project = module.getProject();
    ErlangCompilerOptions compilerOptions =
        JpsErlangCompilerOptionsExtension.getOrCreateExtension(project).getOptions();
    if (!compilerOptions.myUseRebarCompiler) return;

    String rebarExecutablePath = getRebarExecutablePath(project);
    if (rebarExecutablePath == null) {
      String errorMessage = "Rebar path is not set";
      context.processMessage(new CompilerMessage(NAME, BuildMessage.Kind.ERROR, errorMessage));
      throw new ProjectBuildException(errorMessage);
    }

    for (String contentRootUrl : module.getContentRootsList().getUrls()) {
      String contentRootPath = new URL(contentRootUrl).getPath();
      File contentRootDir = new File(contentRootPath);
      File rebarConfigFile = new File(contentRootDir, REBAR_CONFIG_FILE_NAME);
      if (!rebarConfigFile.exists()) continue;
      runRebar(
          contentRootPath, rebarExecutablePath, compilerOptions.myAddDebugInfoEnabled, context);
    }
  }
    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);
              }
            }
          });
    }
 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));
     }
   }
 }
  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 final ExitCode build(
      CompileContext context,
      ModuleChunk chunk,
      DirtyFilesHolder<JavaSourceRootDescriptor, ModuleBuildTarget> dirtyFilesHolder,
      OutputConsumer outputConsumer)
      throws ProjectBuildException, IOException {
    if (outputConsumer.getCompiledClasses().isEmpty() || !isEnabled(context, chunk)) {
      return ExitCode.NOTHING_DONE;
    }

    final String progress = getProgressMessage();
    final boolean shouldShowProgress = !StringUtil.isEmptyOrSpaces(progress);
    if (shouldShowProgress) {
      context.processMessage(new ProgressMessage(progress + " [" + chunk.getName() + "]"));
    }

    ExitCode exitCode = ExitCode.NOTHING_DONE;
    try {
      InstrumentationClassFinder finder = CLASS_FINDER.get(context); // try using shared finder
      if (finder == null) {
        final Collection<File> platformCp =
            ProjectPaths.getPlatformCompilationClasspath(chunk, false);
        final Collection<File> classpath = new ArrayList<File>();
        classpath.addAll(ProjectPaths.getCompilationClasspath(chunk, false));
        classpath.addAll(ProjectPaths.getSourceRootsWithDependents(chunk).keySet());

        finder = createInstrumentationClassFinder(platformCp, classpath, outputConsumer);
        CLASS_FINDER.set(context, finder);
      }

      exitCode = performBuild(context, chunk, finder, outputConsumer);
    } finally {
      if (shouldShowProgress) {
        context.processMessage(new ProgressMessage("")); // cleanup progress
      }
    }
    return exitCode;
  }
 @Override
 public void buildStarted(CompileContext context) {
   if (myForStubs) {
     File stubRoot = getStubRoot(context);
     if (stubRoot.exists() && !FileUtil.deleteWithRenaming(stubRoot)) {
       context.processMessage(
           new CompilerMessage(
               myBuilderName,
               BuildMessage.Kind.ERROR,
               "External make cannot clean " + stubRoot.getPath()));
     }
   }
 }
  public static void updateDependencies(
      CompileContext context,
      List<File> toCompile,
      Map<ModuleBuildTarget, Collection<GroovycOutputParser.OutputItem>> successfullyCompiled,
      OutputConsumer outputConsumer,
      Builder builder)
      throws IOException {
    JavaBuilderUtil.registerFilesToCompile(context, toCompile);
    if (!successfullyCompiled.isEmpty()) {

      final Callbacks.Backend callback = JavaBuilderUtil.getDependenciesRegistrar(context);

      for (Map.Entry<ModuleBuildTarget, Collection<GroovycOutputParser.OutputItem>> entry :
          successfullyCompiled.entrySet()) {
        final ModuleBuildTarget target = entry.getKey();
        final Collection<GroovycOutputParser.OutputItem> compiled = entry.getValue();
        for (GroovycOutputParser.OutputItem item : compiled) {
          final String sourcePath = FileUtil.toSystemIndependentName(item.sourcePath);
          final String outputPath = FileUtil.toSystemIndependentName(item.outputPath);
          final File outputFile = new File(outputPath);
          final File srcFile = new File(sourcePath);
          try {
            final byte[] bytes = FileUtil.loadFileBytes(outputFile);
            if (Utils.IS_TEST_MODE || LOG.isDebugEnabled()) {
              LOG.info("registerCompiledClass " + outputFile + " from " + srcFile);
            }
            outputConsumer.registerCompiledClass(
                target,
                new CompiledClass(
                    outputFile, srcFile, readClassName(bytes), new BinaryContent(bytes)));
            callback.associate(outputPath, sourcePath, new FailSafeClassReader(bytes));
          } catch (Throwable e) {
            // need this to make sure that unexpected errors in, for example, ASM will not ruin the
            // compilation
            final String message =
                "Class dependency information may be incomplete! Error parsing generated class "
                    + item.outputPath;
            LOG.info(message, e);
            context.processMessage(
                new CompilerMessage(
                    builder.getPresentableName(),
                    BuildMessage.Kind.WARNING,
                    message + "\n" + CompilerMessage.getTextFromThrowable(e),
                    sourcePath));
          }
          JavaBuilderUtil.registerSuccessfullyCompiled(context, srcFile);
        }
      }
    }
  }
 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()));
 }
  private boolean updateDependencies(
      CompileContext context,
      ModuleChunk chunk,
      List<File> toCompile,
      String moduleOutputPath,
      List<GroovycOSProcessHandler.OutputItem> successfullyCompiled)
      throws IOException {
    final Mappings delta = context.createDelta();
    final List<File> successfullyCompiledFiles = new ArrayList<File>();
    if (!successfullyCompiled.isEmpty()) {

      final Callbacks.Backend callback = delta.getCallback();
      final FileGeneratedEvent generatedEvent = new FileGeneratedEvent();

      for (GroovycOSProcessHandler.OutputItem item : successfullyCompiled) {
        final String sourcePath = FileUtil.toSystemIndependentName(item.sourcePath);
        final String outputPath = FileUtil.toSystemIndependentName(item.outputPath);
        final RootDescriptor moduleAndRoot = context.getModuleAndRoot(new File(sourcePath));
        if (moduleAndRoot != null) {
          final String moduleName = moduleAndRoot.module.getName().toLowerCase(Locale.US);
          context
              .getDataManager()
              .getSourceToOutputMap(moduleName, moduleAndRoot.isTestRoot)
              .appendData(sourcePath, outputPath);
        }
        callback.associate(
            outputPath,
            Callbacks.getDefaultLookup(sourcePath),
            new ClassReader(FileUtil.loadFileBytes(new File(outputPath))));
        successfullyCompiledFiles.add(new File(sourcePath));

        generatedEvent.add(
            moduleOutputPath, FileUtil.getRelativePath(moduleOutputPath, outputPath, '/'));
      }

      context.processMessage(generatedEvent);
    }

    return updateMappings(context, delta, chunk, toCompile, successfullyCompiledFiles);
  }
 @Nullable
 public static Map<ModuleBuildTarget, String> getCanonicalModuleOutputs(
     CompileContext context, ModuleChunk chunk, Builder builder) {
   Map<ModuleBuildTarget, String> finalOutputs = new LinkedHashMap<ModuleBuildTarget, String>();
   for (ModuleBuildTarget target : chunk.getTargets()) {
     File moduleOutputDir = target.getOutputDir();
     if (moduleOutputDir == null) {
       context.processMessage(
           new CompilerMessage(
               builder.getPresentableName(),
               BuildMessage.Kind.ERROR,
               "Output directory not specified for module " + target.getModule().getName()));
       return null;
     }
     //noinspection ResultOfMethodCallIgnored
     moduleOutputDir.mkdirs();
     String moduleOutputPath = FileUtil.toCanonicalPath(moduleOutputDir.getPath());
     assert moduleOutputPath != null;
     finalOutputs.put(
         target, moduleOutputPath.endsWith("/") ? moduleOutputPath : moduleOutputPath + "/");
   }
   return finalOutputs;
 }
  public ModuleLevelBuilder.ExitCode build(
      final CompileContext context,
      final ModuleChunk chunk,
      DirtyFilesHolder<JavaSourceRootDescriptor, ModuleBuildTarget> dirtyFilesHolder,
      OutputConsumer outputConsumer)
      throws ProjectBuildException {
    if (GreclipseBuilder.useGreclipse(context)) return ExitCode.NOTHING_DONE;

    long start = 0;
    try {
      JpsGroovySettings settings =
          JpsGroovySettings.getSettings(context.getProjectDescriptor().getProject());

      Ref<Boolean> hasStubExcludes = Ref.create(false);
      final List<File> toCompile =
          collectChangedFiles(context, dirtyFilesHolder, myForStubs, false, hasStubExcludes);
      if (toCompile.isEmpty()) {
        return hasFilesToCompileForNextRound(context)
            ? ExitCode.ADDITIONAL_PASS_REQUIRED
            : ExitCode.NOTHING_DONE;
      }
      if (Utils.IS_TEST_MODE || LOG.isDebugEnabled()) {
        LOG.info("forStubs=" + myForStubs);
      }

      Map<ModuleBuildTarget, String> finalOutputs = getCanonicalModuleOutputs(context, chunk, this);
      if (finalOutputs == null) {
        return ExitCode.ABORT;
      }

      start = System.currentTimeMillis();

      Map<ModuleBuildTarget, String> generationOutputs =
          myForStubs ? getStubGenerationOutputs(chunk, context) : finalOutputs;
      String compilerOutput = generationOutputs.get(chunk.representativeTarget());

      GroovycOutputParser parser =
          runGroovycOrContinuation(
              context,
              chunk,
              settings,
              finalOutputs,
              compilerOutput,
              toCompile,
              hasStubExcludes.get());

      Map<ModuleBuildTarget, Collection<GroovycOutputParser.OutputItem>> compiled =
          processCompiledFiles(
              context, chunk, generationOutputs, compilerOutput, parser.getSuccessfullyCompiled());

      if (checkChunkRebuildNeeded(context, parser)) {
        clearContinuation(context, chunk);
        return ExitCode.CHUNK_REBUILD_REQUIRED;
      }

      if (myForStubs) {
        addStubRootsToJavacSourcePath(context, generationOutputs);
        rememberStubSources(context, compiled);
      }

      for (CompilerMessage message : parser.getCompilerMessages()) {
        context.processMessage(message);
      }

      if (!myForStubs) {
        updateDependencies(context, toCompile, compiled, outputConsumer, this);
      }
      return hasFilesToCompileForNextRound(context)
          ? ExitCode.ADDITIONAL_PASS_REQUIRED
          : ExitCode.OK;
    } catch (Exception e) {
      throw new ProjectBuildException(e);
    } finally {
      if (start > 0 && LOG.isDebugEnabled()) {
        LOG.debug(
            myBuilderName
                + " took "
                + (System.currentTimeMillis() - start)
                + " on "
                + chunk.getName());
      }
      if (!myForStubs) {
        FILES_MARKED_DIRTY_FOR_NEXT_ROUND.set(context, null);
      }
    }
  }
  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 void compileFiles(
      final CompileContext context,
      File frameworkHome,
      List<String> includes,
      ModuleBuildTarget buildTarget,
      List<File> sourceFiles,
      IceComponent target,
      File outputDir)
      throws StopBuildException {
    final JpsModule module = buildTarget.getModule();

    final String translatorName = target.getTranslatorName();
    if (outputDir == null) {
      context.processMessage(
          new CompilerMessage(
              getPresentableName(),
              BuildMessage.Kind.WARNING,
              "Output folder is not specified for "
                  + translatorName
                  + " in module "
                  + module.getName()
                  + ". Check facet configuration."));

      return;
    }

    List<String> command = new ArrayList<String>();
    command.add(target.getTranslatorPath(frameworkHome).getAbsolutePath());
    command.add("--list-generated");
    command.add("--output-dir");
    final String outputDirPath = outputDir.getAbsolutePath();
    command.add(outputDirPath);
    for (String include : includes) {
      command.add("-I" + include);
    }
    for (JpsModuleSourceRoot contentRoot : module.getSourceRoots()) {
      command.add("-I" + contentRoot.getFile().getAbsolutePath());
    }
    for (File source : sourceFiles) {
      command.add(source.getAbsolutePath());
    }

    try {
      Process process = new ProcessBuilder().command(command).start();

      BaseOSProcessHandler handler =
          new BaseOSProcessHandler(
              process, StringUtil.join(command, " "), CharsetToolkit.UTF8_CHARSET);
      final AtomicBoolean hasErrors = new AtomicBoolean();
      handler.addProcessListener(
          new ProcessAdapter() {
            final StringBuilder errorOutput = new StringBuilder();
            final StringBuilder stdOutput = new StringBuilder();

            @Override
            public void onTextAvailable(ProcessEvent event, Key outputType) {
              if (outputType == ProcessOutputTypes.STDERR) {
                errorOutput.append(event.getText());
              } else if (outputType == ProcessOutputTypes.STDOUT) {
                stdOutput.append(event.getText());
              }
            }

            @Override
            public void processTerminated(ProcessEvent event) {
              Document res;
              final String stdout = stdOutput.toString();
              try {
                res = JDOMUtil.loadDocument(stdout);
              } catch (Exception e) {
                context.processMessage(
                    new CompilerMessage(
                        BUILDER_NAME,
                        BuildMessage.Kind.ERROR,
                        "Can't process compiler output: " + stdout));
                hasErrors.set(true);
                return;
              }

              int exitCode = event.getExitCode();
              if (exitCode != 0) {
                for (Element source : res.getRootElement().getChildren("source")) {
                  final Element output = source.getChild("output");
                  if (output != null) {
                    String message = output.getTextTrim();

                    for (String line : message.split("\n")) {
                      int separatorIndex = line.indexOf(": ");
                      final String path;
                      final long lineNumber;
                      if (separatorIndex <= 0) {
                        path = null;
                        lineNumber = -1L;
                      } else {
                        int lineSep = line.lastIndexOf(':', separatorIndex - 1);
                        if (lineSep == -1) {
                          path = null;
                          lineNumber = -1L;
                        } else {
                          path = line.substring(0, lineSep);
                          long l;
                          try {
                            l = Long.parseLong(line.substring(lineSep + 1, separatorIndex));
                          } catch (NumberFormatException e) {
                            l = -1L;
                          }
                          lineNumber = l;
                        }
                      }

                      context.processMessage(
                          new CompilerMessage(
                              BUILDER_NAME,
                              BuildMessage.Kind.ERROR,
                              line,
                              path,
                              -1L,
                              -1L,
                              -1L,
                              lineNumber,
                              -1L));
                    }
                  }
                }

                final String stdErr = errorOutput.toString();
                if (stdErr.length() > 0) {
                  context.processMessage(
                      new CompilerMessage(BUILDER_NAME, BuildMessage.Kind.ERROR, stdErr));
                }
                context.processMessage(
                    new CompilerMessage(
                        BUILDER_NAME,
                        BuildMessage.Kind.ERROR,
                        "translator '"
                            + translatorName
                            + "' for '"
                            + module.getName()
                            + "' finished with exit code "
                            + exitCode));
                hasErrors.set(true);
              } else {
                final FileGeneratedEvent msg = new FileGeneratedEvent();

                for (Element source : res.getRootElement().getChildren("source")) {
                  for (Element file : source.getChildren("file")) {
                    final String fileName = file.getAttributeValue("name");

                    if (fileName.startsWith(outputDirPath)) {
                      msg.add(outputDirPath, fileName.substring(outputDirPath.length() + 1));
                    }
                  }
                }

                context.processMessage(msg);
              }
            }
          });
      handler.startNotify();
      handler.waitFor();
      if (hasErrors.get()) {
        throw new StopBuildException();
      }

    } catch (IOException e) {
      context.processMessage(
          new CompilerMessage(
              getPresentableName(),
              BuildMessage.Kind.ERROR,
              "Failed to translate files with " + translatorName + ". Error: " + e.getMessage()));
    }
  }
  public Builder.ExitCode build(final CompileContext context, ModuleChunk chunk)
      throws ProjectBuildException {
    ExitCode exitCode = ExitCode.OK;
    final Map<File, Module> toCompile = new HashMap<File, Module>();
    try {
      context.processFilesToRecompile(
          chunk,
          new FileProcessor() {
            @Override
            public boolean apply(Module module, File file, String sourceRoot) throws Exception {
              final String path = file.getPath();
              if (isGroovyFile(path)) { // todo file type check
                toCompile.put(file, module);
              }
              return true;
            }
          });

      if (toCompile.isEmpty()) {
        return exitCode;
      }

      final Set<String> cp = new LinkedHashSet<String>();
      // groovy_rt.jar
      // IMPORTANT! must be the first in classpath
      cp.add(ClasspathBootstrap.getResourcePath(GroovyCompilerWrapper.class).getPath());

      for (File file :
          context
              .getProjectPaths()
              .getClasspathFiles(chunk, ClasspathKind.compile(context.isCompilingTests()), false)) {
        cp.add(FileUtil.toCanonicalPath(file.getPath()));
      }
      for (File file :
          context
              .getProjectPaths()
              .getClasspathFiles(chunk, ClasspathKind.runtime(context.isCompilingTests()), false)) {
        cp.add(FileUtil.toCanonicalPath(file.getPath()));
      }

      final File tempFile = FileUtil.createTempFile("ideaGroovyToCompile", ".txt", true);
      final Module representativeModule = chunk.getModules().iterator().next();
      File moduleOutputDir =
          context
              .getProjectPaths()
              .getModuleOutputDir(representativeModule, context.isCompilingTests());
      final File dir =
          myForStubs
              ? FileUtil.createTempDirectory(/*new File("/tmp/stubs/"), */ "groovyStubs", null)
              : moduleOutputDir;
      assert dir != null;

      final Set<String> toCompilePaths = new LinkedHashSet<String>();
      for (File file : toCompile.keySet()) {
        toCompilePaths.add(FileUtil.toSystemIndependentName(file.getPath()));
      }

      String moduleOutputPath = FileUtil.toCanonicalPath(moduleOutputDir.getPath());
      if (!moduleOutputPath.endsWith("/")) {
        moduleOutputPath += "/";
      }
      Map<String, String> class2Src =
          buildClassToSourceMap(chunk, context, toCompilePaths, moduleOutputPath);

      String encoding = "UTF-8"; // todo encoding
      List<String> patchers = Collections.emptyList(); // todo patchers
      GroovycOSProcessHandler.fillFileWithGroovycParameters(
          tempFile,
          FileUtil.toCanonicalPath(dir.getPath()),
          toCompilePaths,
          FileUtil.toSystemDependentName(moduleOutputPath),
          class2Src,
          encoding,
          patchers);

      if (myForStubs) {
        JavaBuilder.addTempSourcePathRoot(context, dir);
      }

      // todo CompilerUtil.addLocaleOptions()
      // todo different outputs in a chunk
      // todo module jdk path
      final List<String> cmd =
          ExternalProcessUtil.buildJavaCommandLine(
              SystemProperties.getJavaHome() + "/bin/java",
              "org.jetbrains.groovy.compiler.rt.GroovycRunner",
              Collections.<String>emptyList(),
              new ArrayList<String>(cp),
              Arrays.asList(
                  "-Xmx384m" /*, "-Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=5858"*/),
              Arrays.<String>asList(myForStubs ? "stubs" : "groovyc", tempFile.getPath()));

      deleteCorrespondingOutputFiles(context, toCompile);

      List<GroovycOSProcessHandler.OutputItem> successfullyCompiled = Collections.emptyList();
      try {
        final Process process = Runtime.getRuntime().exec(cmd.toArray(new String[cmd.size()]));
        GroovycOSProcessHandler handler =
            new GroovycOSProcessHandler(process, null) {
              @Override
              protected void updateStatus(@Nullable String status) {
                context.processMessage(
                    new ProgressMessage(status == null ? GROOVY_COMPILER_IN_OPERATION : status));
              }
            };
        handler.startNotify();
        handler.waitFor();

        successfullyCompiled = handler.getSuccessfullyCompiled();

        final List<CompilerMessage> messages = handler.getCompilerMessages();
        for (CompilerMessage message : messages) {
          context.processMessage(message);
        }

        boolean hasMessages = !messages.isEmpty();

        final StringBuffer unparsedBuffer = handler.getStdErr();
        if (unparsedBuffer.length() != 0) {
          context.processMessage(
              new CompilerMessage(BUILDER_NAME, BuildMessage.Kind.INFO, unparsedBuffer.toString()));
        }

        final int exitValue = handler.getProcess().exitValue();
        if (!hasMessages && exitValue != 0) {
          context.processMessage(
              new CompilerMessage(
                  BUILDER_NAME,
                  BuildMessage.Kind.ERROR,
                  "Internal groovyc error: code " + exitValue));
        }
      } finally {
        if (!myForStubs) {
          final Mappings delta = context.createDelta();
          final List<File> successfullyCompiledFiles = new ArrayList<File>();
          if (!successfullyCompiled.isEmpty()) {
            final Callbacks.Backend callback = delta.getCallback();

            for (GroovycOSProcessHandler.OutputItem item : successfullyCompiled) {
              final String sourcePath = FileUtil.toSystemIndependentName(item.sourcePath);
              final String outputPath = FileUtil.toSystemIndependentName(item.outputPath);
              final RootDescriptor moduleAndRoot = context.getModuleAndRoot(new File(sourcePath));
              if (moduleAndRoot != null) {
                final String moduleName = moduleAndRoot.module.getName().toLowerCase(Locale.US);
                context
                    .getDataManager()
                    .getSourceToOutputMap(moduleName, moduleAndRoot.isTestRoot)
                    .appendData(sourcePath, outputPath);
              }
              callback.associate(
                  outputPath,
                  Callbacks.getDefaultLookup(sourcePath),
                  new ClassReader(FileUtil.loadFileBytes(new File(outputPath))));
              successfullyCompiledFiles.add(new File(sourcePath));
            }
          }

          final boolean needSecondPass =
              updateMappings(context, delta, chunk, toCompile.keySet(), successfullyCompiledFiles);
          if (needSecondPass) {
            exitCode = ExitCode.ADDITIONAL_PASS_REQUIRED;
          }
        }
      }

      return exitCode;
    } catch (Exception e) {
      throw new ProjectBuildException(e);
    }
  }
  @Override
  public void build(
      @NotNull ErlangTarget target,
      @NotNull DirtyFilesHolder<ErlangSourceRootDescriptor, ErlangTarget> holder,
      @NotNull BuildOutputConsumer outputConsumer,
      @NotNull final CompileContext context)
      throws ProjectBuildException, IOException {
    LOG.debug(target.getPresentableName());
    final Ref<Boolean> hasDirtyFiles = Ref.create(false);
    holder.processDirtyFiles(
        new FileProcessor<ErlangSourceRootDescriptor, ErlangTarget>() {
          @Override
          public boolean apply(ErlangTarget target, File file, ErlangSourceRootDescriptor root)
              throws IOException {
            hasDirtyFiles.set(true);
            return true;
          }
        });
    if (!hasDirtyFiles.get() && !holder.hasRemovedFiles()) {
      return;
    }

    JpsModule module = target.getModule();
    JpsJavaExtensionService instance = JpsJavaExtensionService.getInstance();

    File outputDirectory = instance.getOutputDirectory(module, target.isTests());
    if (outputDirectory == null) {
      context.processMessage(
          new CompilerMessage(
              NAME, BuildMessage.Kind.ERROR, "No output dir for module " + module.getName()));
      throw new ProjectBuildException();
    }

    if (!outputDirectory.exists()) FileUtil.createDirectory(outputDirectory);

    JpsSdk<JpsDummyElement> sdk = module.getSdk(JpsErlangSdkType.INSTANCE);
    if (sdk == null) {
      context.processMessage(
          new CompilerMessage(
              NAME, BuildMessage.Kind.ERROR, "No SDK for module " + module.getName()));
      throw new ProjectBuildException();
    }

    File executable = JpsErlangSdkType.getByteCodeCompilerExecutable(sdk.getHomePath());

    List<String> commandList = new ArrayList<String>();
    commandList.add(executable.getAbsolutePath());

    CommonProcessors.CollectProcessor<File> processor =
        new CommonProcessors.CollectProcessor<File>() {
          @Override
          protected boolean accept(File file) {
            return !file.isDirectory() && FileUtilRt.extensionEquals(file.getName(), "erl");
          }
        };
    for (JpsModuleSourceRoot root : module.getSourceRoots()) {
      commandList.add("-I");
      commandList.add(root.getFile().getAbsolutePath());
      FileUtil.processFilesRecursively(root.getFile(), processor);
    }

    for (File f : processor.getResults()) {
      commandList.add(f.getAbsolutePath());
    }

    LOG.debug(StringUtil.join(commandList, " "));
    Process process = new ProcessBuilder(commandList).directory(outputDirectory).start();
    BaseOSProcessHandler handler =
        new BaseOSProcessHandler(process, null, Charset.defaultCharset());
    ProcessAdapter adapter =
        new ProcessAdapter() {
          @Override
          public void onTextAvailable(ProcessEvent event, Key outputType) {
            ErlangCompilerError error = ErlangCompilerError.create("", event.getText());
            if (error != null) {

              boolean isError = error.getCategory() == CompilerMessageCategory.ERROR;
              BuildMessage.Kind kind =
                  isError ? BuildMessage.Kind.ERROR : BuildMessage.Kind.WARNING;
              CompilerMessage msg =
                  new CompilerMessage(
                      NAME,
                      kind,
                      error.getErrorMessage(),
                      VirtualFileManager.extractPath(error.getUrl()),
                      -1,
                      -1,
                      -1,
                      error.getLine(),
                      -1);
              context.processMessage(msg);
            }
          }
        };
    handler.addProcessListener(adapter);
    handler.startNotify();
    handler.waitFor();
  }
  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");
    }
  }
  public ModuleLevelBuilder.ExitCode build(final CompileContext context, ModuleChunk chunk)
      throws ProjectBuildException {
    try {
      final List<File> toCompile = collectChangedFiles(context, chunk);
      if (toCompile.isEmpty()) {
        return ExitCode.NOTHING_DONE;
      }

      String moduleOutput = getModuleOutput(context, chunk);
      String compilerOutput = getCompilerOutput(moduleOutput);

      final Set<String> toCompilePaths = new LinkedHashSet<String>();
      for (File file : toCompile) {
        toCompilePaths.add(FileUtil.toSystemIndependentName(file.getPath()));
      }

      Map<String, String> class2Src =
          buildClassToSourceMap(chunk, context, toCompilePaths, moduleOutput);

      String ideCharset = chunk.getProject().getProjectCharset();
      String encoding =
          !Comparing.equal(CharsetToolkit.getDefaultSystemCharset().name(), ideCharset)
              ? ideCharset
              : null;
      List<String> patchers = Collections.emptyList(); // todo patchers
      final File tempFile =
          GroovycOSProcessHandler.fillFileWithGroovycParameters(
              compilerOutput,
              toCompilePaths,
              FileUtil.toSystemDependentName(moduleOutput),
              class2Src,
              encoding,
              patchers);

      // todo different outputs in a chunk
      // todo xmx
      final List<String> cmd =
          ExternalProcessUtil.buildJavaCommandLine(
              getJavaExecutable(chunk),
              "org.jetbrains.groovy.compiler.rt.GroovycRunner",
              Collections.<String>emptyList(),
              new ArrayList<String>(generateClasspath(context, chunk)),
              Arrays.asList(
                  "-Xmx384m",
                  "-Dfile.encoding=" + CharsetToolkit.getDefaultSystemCharset().name() /*,
                      "-Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=5239"*/),
              Arrays.<String>asList(myForStubs ? "stubs" : "groovyc", tempFile.getPath()));

      final Process process = Runtime.getRuntime().exec(ArrayUtil.toStringArray(cmd));
      GroovycOSProcessHandler handler =
          GroovycOSProcessHandler.runGroovyc(
              process,
              new Consumer<String>() {
                public void consume(String s) {
                  context.processMessage(new ProgressMessage(s));
                }
              });

      if (handler.shouldRetry()) {
        if (CHUNK_REBUILD_ORDERED.get(context) != null) {
          CHUNK_REBUILD_ORDERED.set(context, null);
        } else {
          CHUNK_REBUILD_ORDERED.set(context, Boolean.TRUE);
          return ExitCode.CHUNK_REBUILD_REQUIRED;
        }
      }

      if (myForStubs) {
        JavaBuilder.addTempSourcePathRoot(context, new File(compilerOutput));
      }

      for (CompilerMessage message : handler.getCompilerMessages()) {
        context.processMessage(message);
      }
      if (!myForStubs
          && updateDependencies(
              context, chunk, toCompile, moduleOutput, handler.getSuccessfullyCompiled())) {
        return ExitCode.ADDITIONAL_PASS_REQUIRED;
      }
      return ExitCode.OK;
    } catch (Exception e) {
      throw new ProjectBuildException(e);
    }
  }
  private static boolean doPackagingForModule(
      @NotNull CompileContext context,
      @NotNull Module module,
      @NotNull AndroidFileSetStorage apkFileSetStorage,
      @NotNull AndroidApkBuilderConfigStateStorage apkBuilderConfigStateStorage,
      boolean release)
      throws IOException {
    final AndroidFacet facet = AndroidJpsUtil.getFacet(module);
    if (facet == null || facet.isLibrary()) {
      return true;
    }

    final String[] sourceRoots =
        AndroidJpsUtil.toPaths(AndroidJpsUtil.getSourceRootsForModuleAndDependencies(module));
    final ProjectPaths paths = context.getProjectPaths();

    final File outputDir = AndroidJpsUtil.getOutputDirectoryForPackagedFiles(paths, module);
    if (outputDir == null) {
      context.processMessage(
          new CompilerMessage(
              BUILDER_NAME,
              BuildMessage.Kind.ERROR,
              AndroidJpsBundle.message(
                  "android.jps.errors.output.dir.not.specified", module.getName())));
      return false;
    }

    final Pair<AndroidSdk, IAndroidTarget> pair =
        AndroidJpsUtil.getAndroidPlatform(module, context, BUILDER_NAME);
    if (pair == null) {
      return false;
    }

    final Set<String> externalJarsSet = AndroidJpsUtil.getExternalLibraries(paths, module);
    final File resPackage = getPackagedResourcesFile(module, outputDir);

    final File classesDexFile = new File(outputDir.getPath(), AndroidCommonUtils.CLASSES_FILE_NAME);

    final String sdkPath = pair.getFirst().getSdkPath();
    final String outputPath = AndroidJpsUtil.getApkPath(facet, outputDir);
    if (outputPath == null) {
      context.processMessage(
          new CompilerMessage(
              BUILDER_NAME,
              BuildMessage.Kind.ERROR,
              "Cannot compute output path for file " + AndroidJpsUtil.getApkName(module)));
      return false;
    }
    final String customKeyStorePath =
        FileUtil.toSystemDependentName(facet.getCustomDebugKeyStorePath());
    final String[] nativeLibDirs = collectNativeLibsFolders(facet);

    final String resPackagePath =
        release ? resPackage.getPath() + RELEASE_SUFFIX : resPackage.getPath();
    final String outputApkPath = release ? outputPath + UNSIGNED_SUFFIX : outputPath;
    final String classesDexFilePath = classesDexFile.getPath();
    final String[] externalJars = ArrayUtil.toStringArray(externalJarsSet);

    final AndroidFileSetState currentFileSetState =
        buildCurrentApkBuilderState(
            context.getProject(),
            resPackagePath,
            classesDexFilePath,
            nativeLibDirs,
            sourceRoots,
            externalJars,
            release);

    final AndroidApkBuilderConfigState currentApkBuilderConfigState =
        new AndroidApkBuilderConfigState(outputApkPath, customKeyStorePath);

    final AndroidFileSetState savedApkFileSetState = apkFileSetStorage.getState(module.getName());
    final AndroidApkBuilderConfigState savedApkBuilderConfigState =
        apkBuilderConfigStateStorage.getState(module.getName());

    if (context.isMake()
        && currentFileSetState.equalsTo(savedApkFileSetState)
        && currentApkBuilderConfigState.equalsTo(savedApkBuilderConfigState)) {
      return true;
    }
    context.processMessage(
        new ProgressMessage(
            AndroidJpsBundle.message(
                "android.jps.progress.packaging", AndroidJpsUtil.getApkName(module))));

    final Map<AndroidCompilerMessageKind, List<String>> messages =
        AndroidApkBuilder.execute(
            resPackagePath,
            classesDexFilePath,
            sourceRoots,
            externalJars,
            nativeLibDirs,
            outputApkPath,
            release,
            sdkPath,
            customKeyStorePath,
            new MyExcludedSourcesFilter(context.getProject()));

    AndroidJpsUtil.addMessages(context, messages, BUILDER_NAME);
    final boolean success = messages.get(AndroidCompilerMessageKind.ERROR).isEmpty();

    apkFileSetStorage.update(module.getName(), success ? currentFileSetState : null);
    apkBuilderConfigStateStorage.update(
        module.getName(), success ? currentApkBuilderConfigState : null);
    return success;
  }
  @Override
  public ExitCode build(
      CompileContext context,
      ModuleChunk chunk,
      DirtyFilesHolder<JavaSourceRootDescriptor, ModuleBuildTarget> dirtyFilesHolder,
      OutputConsumer outputConsumer)
      throws ProjectBuildException, IOException {
    final IceConfig config =
        context.getProjectDescriptor().getProject().getContainer().getChild(IceConfig.ROLE);
    IceConfig iceConfig = config == null ? new IceConfig("") : config;

    File frameworkHome = JpsPathUtil.urlToFile(iceConfig.getFrameworkHomeUrl());
    List<String> includes = new ArrayList<String>();
    for (String include : iceConfig.getIncludeUrls()) {
      includes.add(JpsPathUtil.urlToFile(include).getAbsolutePath());
    }

    final Map<ModuleBuildTarget, List<File>> toCompile =
        collectChangedFiles(context, dirtyFilesHolder);
    if (toCompile.isEmpty()) {
      return ExitCode.NOTHING_DONE;
    }

    for (Map.Entry<ModuleBuildTarget, List<File>> e : toCompile.entrySet()) {
      final ModuleBuildTarget target = e.getKey();
      final JpsModule module = target.getModule();
      SliceCompilerSettings settings = module.getContainer().getChild(SliceCompilerSettings.ROLE);
      final SliceCompilerSettings facetConfig =
          settings == null ? new SliceCompilerSettings() : settings;
      final List<File> sourceFiles = e.getValue();

      List<Target> translators = facetConfig.getComponents();

      if (translators.isEmpty()) {
        context.processMessage(
            new CompilerMessage(
                getPresentableName(),
                BuildMessage.Kind.WARNING,
                "No valid translators found for module "
                    + module.getName()
                    + ". Check facet configuration."));

        continue;
      }

      // Translate files
      for (Target c : translators) {
        final File outputDir = c.getOutputFile();

        if (facetConfig.isCleanOutput()) {
          try {
            FileUtils.cleanDirectory(outputDir);
          } catch (IOException ex) {
            context.processMessage(
                new CompilerMessage(
                    getPresentableName(),
                    BuildMessage.Kind.ERROR,
                    "Failed to empty target directory: "
                        + outputDir.getPath()
                        + " . Error: "
                        + ex.getMessage()));
            return ExitCode.ABORT;
          }
        }

        compileFiles(
            context, frameworkHome, includes, target, sourceFiles, c.getComponent(), outputDir);
      }
    }

    return ExitCode.OK;
  }
  @Override
  public ExitCode build(
      final CompileContext context,
      ModuleChunk chunk,
      DirtyFilesHolder<JavaSourceRootDescriptor, ModuleBuildTarget> dirtyFilesHolder,
      OutputConsumer outputConsumer)
      throws ProjectBuildException, IOException {
    if (!useGreclipse(context)) return ModuleLevelBuilder.ExitCode.NOTHING_DONE;

    try {
      final List<File> toCompile =
          GroovyBuilder.collectChangedFiles(
              context, dirtyFilesHolder, false, true, Ref.create(false));
      if (toCompile.isEmpty()) {
        return ExitCode.NOTHING_DONE;
      }

      Map<ModuleBuildTarget, String> outputDirs =
          GroovyBuilder.getCanonicalModuleOutputs(context, chunk, this);
      if (outputDirs == null) {
        return ExitCode.ABORT;
      }

      JpsProject project = context.getProjectDescriptor().getProject();
      GreclipseSettings greclipseSettings = GreclipseJpsCompilerSettings.getSettings(project);
      if (greclipseSettings == null) {
        String message = "Compiler settings component not initialized for " + project;
        LOG.error(message);
        context.processMessage(
            new CompilerMessage(getPresentableName(), BuildMessage.Kind.ERROR, message));
        return ExitCode.ABORT;
      }

      ClassLoader loader = createGreclipseLoader(greclipseSettings.greclipsePath);
      if (loader == null) {
        context.processMessage(
            new CompilerMessage(
                getPresentableName(),
                BuildMessage.Kind.ERROR,
                "Invalid jar path in the compiler settings: '"
                    + greclipseSettings.greclipsePath
                    + "'"));
        return ExitCode.ABORT;
      }

      final JpsJavaExtensionService javaExt = JpsJavaExtensionService.getInstance();
      final JpsJavaCompilerConfiguration compilerConfig = javaExt.getCompilerConfiguration(project);
      assert compilerConfig != null;

      final Set<JpsModule> modules = chunk.getModules();
      ProcessorConfigProfile profile = null;
      if (modules.size() == 1) {
        profile = compilerConfig.getAnnotationProcessingProfile(modules.iterator().next());
      } else {
        String message = JavaBuilder.validateCycle(chunk, javaExt, compilerConfig, modules);
        if (message != null) {
          context.processMessage(
              new CompilerMessage(getPresentableName(), BuildMessage.Kind.ERROR, message));
          return ExitCode.ABORT;
        }
      }

      String mainOutputDir = outputDirs.get(chunk.representativeTarget());
      final List<String> args =
          createCommandLine(context, chunk, toCompile, mainOutputDir, profile, greclipseSettings);

      if (Utils.IS_TEST_MODE || LOG.isDebugEnabled()) {
        LOG.debug("Compiling with args: " + args);
      }

      Boolean notified = COMPILER_VERSION_INFO.get(context);
      if (notified != Boolean.TRUE) {
        context.processMessage(
            new CompilerMessage(
                "",
                BuildMessage.Kind.INFO,
                "Using Groovy-Eclipse to compile Java & Groovy sources"));
        COMPILER_VERSION_INFO.set(context, Boolean.TRUE);
      }

      context.processMessage(
          new ProgressMessage("Compiling java & groovy [" + chunk.getPresentableShortName() + "]"));

      StringWriter out = new StringWriter();
      StringWriter err = new StringWriter();
      HashMap<String, List<String>> outputMap = ContainerUtil.newHashMap();

      boolean success = performCompilation(args, out, err, outputMap, context, chunk);

      List<GroovycOutputParser.OutputItem> items = ContainerUtil.newArrayList();
      for (String src : outputMap.keySet()) {
        //noinspection ConstantConditions
        for (String classFile : outputMap.get(src)) {
          items.add(
              new GroovycOutputParser.OutputItem(
                  FileUtil.toSystemIndependentName(mainOutputDir + classFile),
                  FileUtil.toSystemIndependentName(src)));
        }
      }
      Map<ModuleBuildTarget, Collection<GroovycOutputParser.OutputItem>> successfullyCompiled =
          GroovyBuilder.processCompiledFiles(context, chunk, outputDirs, mainOutputDir, items);

      EclipseOutputParser parser = new EclipseOutputParser(getPresentableName(), chunk);
      List<CompilerMessage> messages =
          ContainerUtil.concat(
              parser.parseMessages(out.toString()), parser.parseMessages(err.toString()));
      boolean hasError = false;
      for (CompilerMessage message : messages) {
        if (message.getKind() == BuildMessage.Kind.ERROR) {
          hasError = true;
        }
        context.processMessage(message);
      }

      if (!success && !hasError) {
        context.processMessage(
            new CompilerMessage(
                getPresentableName(), BuildMessage.Kind.ERROR, "Compilation failed"));
      }

      GroovyBuilder.updateDependencies(
          context, toCompile, successfullyCompiled, outputConsumer, this);
      return ExitCode.OK;

    } catch (Exception e) {
      throw new ProjectBuildException(e);
    }
  }