private static boolean isInTestSourceRoot(final JpsModule module, final String path) {
   for (JpsModuleSourceRoot testSrcRoot : module.getSourceRoots(JavaSourceRootType.TEST_SOURCE)) {
     final String testSrcRootPath = JpsPathUtil.urlToPath(testSrcRoot.getUrl());
     if (path.startsWith(testSrcRootPath + "/")) return true;
   }
   return false;
 }
 private void doTest(boolean rebuildBeforeMake) {
   initProject();
   rebuildAll();
   for (JpsModule module : myProject.getModules()) {
     for (JpsModuleSourceRoot sourceRoot : module.getSourceRoots()) {
       processFile(sourceRoot.getFile(), rebuildBeforeMake);
     }
   }
 }
  private void addLibClasses(final Element rootElement) throws IOException {
    final JpsCompilerExcludes excludes =
        JpsJavaExtensionService.getInstance()
            .getOrCreateCompilerConfiguration(myModule.getProject())
            .getCompilerExcludes();
    final Ref<Boolean> noClasses = new Ref<Boolean>(true);

    for (JpsTypedModuleSourceRoot srcRoot : myModule.getSourceRoots(JavaSourceRootType.SOURCE)) {
      final File srcFolder = JpsPathUtil.urlToFile(srcRoot.getUrl());
      if (srcFolder.isDirectory()) {
        processFilesRecursively(
            srcFolder,
            new Processor<File>() {
              public boolean process(final File file) {
                if (myProjectDescriptor.getIgnoredFileIndex().isIgnored(file.getName()))
                  return false;
                if (file.isDirectory()) return true;
                if (!FlexCommonUtils.isSourceFile(file.getName())) return true;
                if (excludes.isExcluded(file)) return true;

                String packageRelativePath =
                    FileUtil.getRelativePath(srcFolder, file.getParentFile());
                assert packageRelativePath != null : srcFolder.getPath() + ": " + file.getPath();
                if (packageRelativePath.equals(".")) packageRelativePath = "";

                final String packageName = packageRelativePath.replace(File.separatorChar, '.');
                final String qName =
                    StringUtil.getQualifiedName(
                        packageName, FileUtil.getNameWithoutExtension(file));

                if (isSourceFileWithPublicDeclaration(file)) {
                  addOption(rootElement, CompilerOptionInfo.INCLUDE_CLASSES_INFO, qName);
                  noClasses.set(false);
                }

                return true;
              }
            });
      }
    }

    if (noClasses.get()
        && myBC.getCompilerOptions().getFilesToIncludeInSWC().isEmpty()
        && !Utils.IS_TEST_MODE) {
      throw new IOException(
          FlexCommonBundle.message(
              "nothing.to.compile.in.library", myModule.getName(), myBC.getName()));
    }
  }
  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()));
    }
  }
  private void addSourcePaths(final Element rootElement) {
    final String localeValue =
        getValueAndSource(CompilerOptionInfo.getOptionInfo("compiler.locale")).first;
    final List<String> locales =
        StringUtil.split(localeValue, CompilerOptionInfo.LIST_ENTRIES_SEPARATOR);
    // when adding source paths we respect locales set both in UI and in Additional compiler options
    locales.addAll(
        FlexCommonUtils.getOptionValues(
            myProjectLevelCompilerOptions.getAdditionalOptions(), "locale", "compiler.locale"));
    locales.addAll(
        FlexCommonUtils.getOptionValues(
            myModuleLevelCompilerOptions.getAdditionalOptions(), "locale", "compiler.locale"));
    locales.addAll(
        FlexCommonUtils.getOptionValues(
            myBC.getCompilerOptions().getAdditionalOptions(), "locale", "compiler.locale"));

    final Set<String> sourcePathsWithLocaleToken =
        new THashSet<String>(); // Set - to avoid duplication of paths like "locale/{locale}"
    final List<String> sourcePathsWithoutLocaleToken = new LinkedList<String>();

    for (JpsModuleSourceRoot srcRoot : myModule.getSourceRoots(JavaSourceRootType.SOURCE)) {
      final String srcRootPath = JpsPathUtil.urlToPath(srcRoot.getUrl());
      if (locales.contains(PathUtilRt.getFileName(srcRootPath))) {
        sourcePathsWithLocaleToken.add(
            PathUtilRt.getParentPath(srcRootPath) + "/" + FlexCommonUtils.LOCALE_TOKEN);
      } else {
        sourcePathsWithoutLocaleToken.add(srcRootPath);
      }
    }

    if (includeTestRoots()) {
      for (JpsModuleSourceRoot srcRoot : myModule.getSourceRoots(JavaSourceRootType.TEST_SOURCE)) {
        final String srcRootPath = JpsPathUtil.urlToPath(srcRoot.getUrl());
        if (locales.contains(PathUtilRt.getFileName(srcRootPath))) {
          sourcePathsWithLocaleToken.add(
              PathUtilRt.getParentPath(srcRootPath) + "/" + FlexCommonUtils.LOCALE_TOKEN);
        } else {
          sourcePathsWithoutLocaleToken.add(srcRootPath);
        }
      }
    }

    final StringBuilder sourcePathBuilder = new StringBuilder();

    if (myCSS) {
      final String cssFolderPath = PathUtilRt.getParentPath(myBC.getMainClass());
      if (!sourcePathsWithoutLocaleToken.contains(cssFolderPath)) {
        sourcePathBuilder.append(cssFolderPath);
      }
    }

    for (final String sourcePath : sourcePathsWithLocaleToken) {
      if (sourcePathBuilder.length() > 0) {
        sourcePathBuilder.append(CompilerOptionInfo.LIST_ENTRIES_SEPARATOR);
      }
      sourcePathBuilder.append(sourcePath);
    }

    for (final String sourcePath : sourcePathsWithoutLocaleToken) {
      if (sourcePathBuilder.length() > 0) {
        sourcePathBuilder.append(CompilerOptionInfo.LIST_ENTRIES_SEPARATOR);
      }
      sourcePathBuilder.append(sourcePath);
    }

    addOption(rootElement, CompilerOptionInfo.SOURCE_PATH_INFO, sourcePathBuilder.toString());
  }
  @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();
  }