private static void runRebar(
      @Nullable String contentRootPath,
      @NotNull String rebarPath,
      boolean addDebugInfo,
      @NotNull final CompileContext context)
      throws ProjectBuildException {
    GeneralCommandLine commandLine = new GeneralCommandLine();
    commandLine.setWorkDirectory(contentRootPath);
    commandLine.setExePath(rebarPath);
    commandLine.addParameter("compile");

    if (addDebugInfo) {
      commandLine.getEnvironment().put("ERL_FLAGS", "+debug_info");
    }

    Process process;
    try {
      process = commandLine.createProcess();
    } catch (ExecutionException e) {
      throw new ProjectBuildException("Failed to run rebar", e);
    }
    BaseOSProcessHandler handler =
        new BaseOSProcessHandler(
            process, commandLine.getCommandLineString(), Charset.defaultCharset());
    ProcessAdapter adapter =
        new ErlangCompilerProcessAdapter(
            context,
            NAME,
            commandLine.getWorkDirectory().getPath()); // TODO provide rebar messages handling
    handler.addProcessListener(adapter);
    handler.startNotify();
    handler.waitFor();
  }
  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 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()));
    }
  }
  @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();
  }