@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);
    }
  }
 @Override
 public ExitCode build(
     CompileContext context,
     ModuleChunk chunk,
     DirtyFilesHolder<JavaSourceRootDescriptor, ModuleBuildTarget> dirtyFilesHolder,
     OutputConsumer outputConsumer)
     throws ProjectBuildException, IOException {
   if (dirtyFilesHolder.hasRemovedFiles()) {
     final BackwardReferenceIndexWriter writer = BackwardReferenceIndexWriter.getInstance();
     if (writer != null) {
       for (ModuleBuildTarget target : chunk.getTargets()) {
         final Collection<String> files = dirtyFilesHolder.getRemovedFiles(target);
         writer.processDeletedFiles(files);
       }
     }
   }
   return null;
 }
  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);
    }
  }
  static List<File> collectChangedFiles(
      CompileContext context,
      DirtyFilesHolder<JavaSourceRootDescriptor, ModuleBuildTarget> dirtyFilesHolder,
      final boolean forStubs,
      final boolean forEclipse,
      final Ref<Boolean> hasExcludes)
      throws IOException {

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

    final JpsGroovySettings settings =
        JpsGroovySettings.getSettings(context.getProjectDescriptor().getProject());

    final List<File> toCompile = new ArrayList<File>();
    dirtyFilesHolder.processDirtyFiles(
        new FileProcessor<JavaSourceRootDescriptor, ModuleBuildTarget>() {
          public boolean apply(
              ModuleBuildTarget target, File file, JavaSourceRootDescriptor sourceRoot)
              throws IOException {
            final String path = file.getPath();
            // todo file type check
            if ((isGroovyFile(path) || forEclipse && path.endsWith(".java"))
                && !configuration.isResourceFile(file, sourceRoot.root)) {
              if (forStubs && settings.isExcludedFromStubGeneration(file)) {
                hasExcludes.set(true);
                return true;
              }

              toCompile.add(file);
            }
            return true;
          }
        });
    return toCompile;
  }
 private Map<ModuleBuildTarget, List<File>> collectChangedFiles(
     CompileContext context,
     DirtyFilesHolder<JavaSourceRootDescriptor, ModuleBuildTarget> dirtyFilesHolder)
     throws IOException {
   final Map<ModuleBuildTarget, List<File>> toCompile =
       new HashMap<ModuleBuildTarget, List<File>>();
   dirtyFilesHolder.processDirtyFiles(
       new FileProcessor<JavaSourceRootDescriptor, ModuleBuildTarget>() {
         public boolean apply(
             ModuleBuildTarget target, File file, JavaSourceRootDescriptor sourceRoot)
             throws IOException {
           if (file.getName().endsWith(".ice")) {
             List<File> files = toCompile.get(target);
             if (files == null) {
               files = new ArrayList<File>();
               toCompile.put(target, files);
             }
             files.add(file);
           }
           return true;
         }
       });
   return toCompile;
 }
  @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();
  }
  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;
  }