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();
  }