@Override
  public ExitCode build(CompileContext context, ModuleChunk chunk, DirtyFilesHolder<JavaSourceRootDescriptor, ModuleBuildTarget> dirtyFilesHolder, OutputConsumer outputConsumer) throws ProjectBuildException, IOException {
    ExitCode exitCode = ExitCode.NOTHING_DONE;
    final JpsProject project = context.getProjectDescriptor().getProject();
    final JpsUiDesignerConfiguration config = JpsUiDesignerExtensionService.getInstance().getOrCreateUiDesignerConfiguration(project);

    if (!config.isInstrumentClasses() && !config.isCopyFormsRuntimeToOutput()) {
      return exitCode;
    }

    final Map<File, ModuleBuildTarget> filesToCompile = FileCollectionFactory.createCanonicalFileMap();
    final Map<File, ModuleBuildTarget> formsToCompile = FileCollectionFactory.createCanonicalFileMap();
    final Map<File, Collection<File>> srcToForms = FileCollectionFactory.createCanonicalFileMap();

    if (!JavaBuilderUtil.isForcedRecompilationAllJavaModules(context) && config.isInstrumentClasses() && FORCE_FORMS_REBUILD_FLAG.get(context, Boolean.FALSE)) {
      // force compilation of all forms, but only once per chunk
      if (!FORMS_REBUILD_FORCED.get(context, Boolean.FALSE)) {
        FORMS_REBUILD_FORCED.set(context, Boolean.TRUE);
        FSOperations.markDirty(context, CompilationRound.CURRENT, chunk, FORM_SOURCES_FILTER);
      }
    }

    dirtyFilesHolder.processDirtyFiles(new FileProcessor<JavaSourceRootDescriptor, ModuleBuildTarget>() {
      @Override
      public boolean apply(@NotNull ModuleBuildTarget target, @NotNull File file, @NotNull JavaSourceRootDescriptor descriptor) throws IOException {
        if (JAVA_SOURCES_FILTER.accept(file)) {
          filesToCompile.put(file, target);
        }
        else if (FORM_SOURCES_FILTER.accept(file)) {
          formsToCompile.put(file, target);
        }
        return true;
      }
    });

    if (config.isInstrumentClasses()) {
      final JpsJavaCompilerConfiguration configuration = JpsJavaExtensionService.getInstance().getCompilerConfiguration(project);
      final JpsCompilerExcludes excludes = configuration.getCompilerExcludes();

      final FSOperations.DirtyFilesHolderBuilder<JavaSourceRootDescriptor, ModuleBuildTarget> holderBuilder = FSOperations.createDirtyFilesHolderBuilder(context, CompilationRound.CURRENT);

      // force compilation of bound source file if the form is dirty
      final Set<File> alienForms = FileCollectionFactory.createCanonicalFileSet();
      for (final Map.Entry<File, ModuleBuildTarget> entry : formsToCompile.entrySet()) {
        final File form = entry.getKey();
        final ModuleBuildTarget target = entry.getValue();
        try {
          final Collection<File> sources = findBoundSourceCandidates(context, target, form);
          boolean isFormBound = false;
          for (File boundSource : sources) {
            if (!excludes.isExcluded(boundSource)) {
              isFormBound = true;
              addBinding(boundSource, form, srcToForms);
              holderBuilder.markDirtyFile(target, boundSource);
              context.getScope().markIndirectlyAffected(target, boundSource);
              filesToCompile.put(boundSource, target);
              exitCode = ExitCode.OK;
            }
          }
          if (!isFormBound) {
            context.processMessage(new CompilerMessage(
              getPresentableName(), BuildMessage.Kind.ERROR, FormBundle.message("class.to.bind.not.found"), form.getAbsolutePath()
            ));
          }
        }
        catch (AlienFormFileException e) {
          alienForms.add(form);
        }
      }

      formsToCompile.keySet().removeAll(alienForms);

      // form should be considered dirty if the class it is bound to is dirty
      final OneToManyPathsMapping sourceToFormMap = context.getProjectDescriptor().dataManager.getSourceToFormMap();
      for (Map.Entry<File, ModuleBuildTarget> entry : filesToCompile.entrySet()) {
        final File srcFile = entry.getKey();
        final ModuleBuildTarget target = entry.getValue();
        final Collection<String> boundForms = sourceToFormMap.getState(srcFile.getPath());
        if (boundForms != null) {
          for (String formPath : boundForms) {
            final File formFile = new File(formPath);
            if (!excludes.isExcluded(formFile) && formFile.exists()) {
              addBinding(srcFile, formFile, srcToForms);
              holderBuilder.markDirtyFile(target, formFile);

              context.getScope().markIndirectlyAffected(target, formFile);
              formsToCompile.put(formFile, target);
              exitCode = ExitCode.OK;
            }
          }
        }
      }

      BuildOperations.cleanOutputsCorrespondingToChangedFiles(context, holderBuilder.create());
    }

    FORMS_TO_COMPILE.set(context, srcToForms.isEmpty()? null : srcToForms);

    if (config.isCopyFormsRuntimeToOutput() && containsValidForm(formsToCompile.keySet())) {
      for (ModuleBuildTarget target : chunk.getTargets()) {
        if (!target.isTests()) {
          final File outputDir = target.getOutputDir();
          if (outputDir != null) {
            final String outputRoot = FileUtil.toSystemIndependentName(outputDir.getPath());
            final List<File> generatedFiles = CopyResourcesUtil.copyFormsRuntime(outputRoot, false);
            if (!generatedFiles.isEmpty()) {
              exitCode = ExitCode.OK;
              // now inform others about files just copied
              for (File file : generatedFiles) {
                outputConsumer.registerOutputFile(target, file, Collections.emptyList());
              }
            }
          }
        }
      }
    }

    return exitCode;
  }
@Override
  public ExitCode build(CompileContext context, ModuleChunk chunk, DirtyFilesHolder<JavaSourceRootDescriptor, ModuleBuildTarget> dirtyFilesHolder, OutputConsumer outputConsumer)