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