public ExitCode build()

in sources/java-incremental-compilation/jvm-inc-builder/src/com/intellij/tools/build/bazel/jvmIncBuilder/BazelIncBuilder.java [33:282]


  public ExitCode build(BuildContext context) {
    // todo: support cancellation checks
    // todo: additional diagnostics, if necessary

    DiagnosticSink diagnostic = context;
    NodeSourceSnapshotDelta srcSnapshotDelta = null;
    ResourcesSnapshotDelta resourcesDelta = null;
    Iterable<NodeSource> modifiedLibraries = List.of();
    Iterable<NodeSource> deletedLibraries = List.of();

    try (StorageManager storageManager = new StorageManager(context)) {

      try {
        GraphUpdater graphUpdater = new GraphUpdater(context.getTargetName());

        LOG.fine(() -> "Building " + context.getTargetName() + " (rebuild requested: " + context.isRebuild() + ")");

        if (context.isRebuild() || !storageManager.getOutputBuilder().isInputZipExist()) {
          // either rebuild is explicitly requested, or there is no previous data, need to compile the whole target
          srcSnapshotDelta = new SnapshotDeltaImpl(context.getSources());
          srcSnapshotDelta.markRecompileAll(); // force rebuild
        }
        else {
          ConfigurationState pastState = ConfigurationState.loadSavedState(context);
          ConfigurationState presentState = new ConfigurationState(context.getPathMapper(), context.getSources(), context.getResources(), context.getBinaryDependencies(), context.getFlags());

          srcSnapshotDelta = new SnapshotDeltaImpl(pastState.getSources(), presentState.getSources());

          if (shouldRecompileAll(srcSnapshotDelta) || pastState.getFlagsDigest() != presentState.getFlagsDigest() || pastState.getClasspathStructureDigest() != presentState.getClasspathStructureDigest()) {
            int changedPercent = srcSnapshotDelta.getChangedPercent();
            LOG.fine(() -> "Marking whole target for recompilation [" + context.getTargetName() + "]. Changed sources: " + changedPercent + "% (threshold " + RECOMPILE_CHANGED_RATIO_PERCENT + "%) ");
            srcSnapshotDelta.markRecompileAll();
          }
          else {
            Predicate<NodeSource> isLibTracked = ns -> DataPaths.isLibraryTracked(ns.toString());
            ElementSnapshotDeltaImpl<NodeSource> libsSnapshotDelta = new ElementSnapshotDeltaImpl<>(
              ElementSnapshot.derive(pastState.getLibraries(), isLibTracked),
              ElementSnapshot.derive(context.getBinaryDependencies(), isLibTracked)
            );
            modifiedLibraries = libsSnapshotDelta.getModified();
            deletedLibraries = libsSnapshotDelta.getDeleted();

            if (!isEmpty(modifiedLibraries)) {
              // differentiate library deps
              List<Graph> pastLibGraphs = new ArrayList<>();
              List<Graph> presentLibGraphs = new ArrayList<>();
              Set<NodeSource> changedLibNodeSources = new HashSet<>();
              Set<NodeSource> deletedLibNodeSources = new HashSet<>();
              for (NodeSource presentLib : modifiedLibraries) {
                Path presentLibPath = context.getPathMapper().toPath(presentLib);
                Path pastLibPath = DataPaths.getJarBackupStoreFile(context, presentLibPath);
                try {
                  Pair<NodeSourceSnapshot, Graph> presentGraph = LibraryGraphLoader.getLibraryGraph(presentLib, presentState.getLibraries().getDigest(presentLib), presentLibPath);
                  Pair<NodeSourceSnapshot, Graph> pastGraph = LibraryGraphLoader.getLibraryGraph(presentLib, pastState.getLibraries().getDigest(presentLib), pastLibPath);
                  NodeSourceSnapshotDelta delta = new SnapshotDeltaImpl(pastGraph.first, presentGraph.first);
                  if (!isEmpty(delta.getModified()) || !isEmpty(delta.getDeleted())) {
                    collect(delta.getModified(), changedLibNodeSources);
                    collect(delta.getDeleted(), deletedLibNodeSources);
                    pastLibGraphs.add(pastGraph.second); // all nodes of the past state should be available for analysis
                    presentLibGraphs.add(presentGraph.second);
                  }
                }
                catch (Exception e) { // problems loading library graphs
                  LOG.log(Level.WARNING, "Problems loading library graphs, recompiling whole target " + context.getTargetName(), e);
                  srcSnapshotDelta.markRecompileAll();
                  context.report(Message.create(null, Message.Kind.WARNING, e));
                  break;
                }
              }

              if (!changedLibNodeSources.isEmpty() || !deletedLibNodeSources.isEmpty()) {

                // Add to 'pastLibGraphs' all previously available graph parts, even if they are not changed. Reason: need full nodes info for graph node traversals
                for (NodeSource lib : libsSnapshotDelta.getBaseSnapshot().getElements()) {
                  if (!contains(libsSnapshotDelta.getModified(), lib)) {
                    pastLibGraphs.add(
                      LibraryGraphLoader.getLibraryGraph(lib, presentState.getLibraries().getDigest(lib), context.getPathMapper().toPath(lib)).second
                    );
                  }
                }

                try {
                  Delta libDelta = new DeltaView(changedLibNodeSources, deletedLibNodeSources, CompositeGraph.create(presentLibGraphs));
                  srcSnapshotDelta = graphUpdater.updateBeforeCompilation(storageManager.getGraph(), srcSnapshotDelta, libDelta, pastLibGraphs);
                  if (shouldRecompileAll(srcSnapshotDelta)) {
                    srcSnapshotDelta.markRecompileAll();
                  }
                }
                catch (IOException e) {
                  LOG.log(Level.WARNING, "Problems loading dependency graph, recompiling whole target " + context.getTargetName(), e);
                  srcSnapshotDelta.markRecompileAll();
                  context.report(Message.create(null, Message.Kind.WARNING, e));
                }
              }
            }

            // expand compile scope
            if (!srcSnapshotDelta.isRecompileAll()) {
              DependencyGraph graph = storageManager.getGraph();
              Delta sourceOnlyDelta = graph.createDelta(srcSnapshotDelta.getModified(), srcSnapshotDelta.getDeleted(), true);
              srcSnapshotDelta = graphUpdater.updateBeforeCompilation(graph, srcSnapshotDelta, sourceOnlyDelta, List.of());
              if (shouldRecompileAll(srcSnapshotDelta)) {
                srcSnapshotDelta.markRecompileAll();
              }
            }
          }

          if (!srcSnapshotDelta.isRecompileAll()) {
            resourcesDelta = new ResourcesSnapshotDelta(pastState.getResources(), presentState.getResources());
          }
        }

        List<CompilerRunner> roundCompilers = collect(map(RunnerRegistry.getRoundCompilers(), f -> f.create(context, storageManager)), new ArrayList<>());
        boolean isInitialRound = true;

        do { // build rounds loop

          if (srcSnapshotDelta.isRecompileAll()) {
            storageManager.cleanBuildState();
            modifiedLibraries = ElementSnapshot.derive(context.getBinaryDependencies(), ns -> DataPaths.isLibraryTracked(ns.toString())).getElements();
            deletedLibraries = Set.of();
          }
          else {
            if (isInitialRound) {
              storageManager.cleanTrashDir();
            }
          }

          diagnostic = isInitialRound? new PostponedDiagnosticSink() : context; // for initial round postpone error reporting
          OutputSinkImpl outSink = new OutputSinkImpl(storageManager);

          if (isInitialRound) {

            // processing resources
            ZipOutputBuilder out = storageManager.getOutputBuilder();
            if (resourcesDelta == null || srcSnapshotDelta.isRecompileAll()) {
              // copy everything
              for (ResourceGroup group : context.getResources()) {
                copyResources(group, context.getPathMapper(), out);
              }
            }
            else {
              // only copy modified and remove deleted
              deleteResources(resourcesDelta.getPastResources(), flat(resourcesDelta.getDeleted(), resourcesDelta.getChanged()), out);
              copyResources(resourcesDelta.getPresentResources(), resourcesDelta.getModified(), context.getPathMapper(), out);
            }

            // processing deleted sources makes sense on inintial round only
            if (!srcSnapshotDelta.isRecompileAll() && !isEmpty(srcSnapshotDelta.getDeleted())) {
              // clean outputs that correspond to deleted sources, no matter of source type
              Collection<String> cleaned = deleteCompilerOutputs(
                storageManager.getGraph(), srcSnapshotDelta.getDeleted(), storageManager.getCompositeOutputBuilder(), new ArrayList<>()
              );
              logDeletedPaths(context, cleaned);
            }
          }
          else {
            if (srcSnapshotDelta.isRecompileAll()) {
              // After several rounds, the IC logic can decide to recompile everything
              ZipOutputBuilder out = storageManager.getOutputBuilder();
              for (ResourceGroup group : context.getResources()) {
                copyResources(group, context.getPathMapper(), out);
              }
            }
          }

          for (CompilerRunner runner : roundCompilers) {

            Iterable<NodeSource> toCompile = collect(filter(srcSnapshotDelta.getModified(), runner::canCompile), new ArrayList<>());

            if (!srcSnapshotDelta.isRecompileAll() && !isEmpty(toCompile)) {
              // delete outputs corresponding to recompiled sources before running the compiler
              ZipOutputBuilder outBuilder = storageManager.getCompositeOutputBuilder();
              Collection<String> cleaned = deleteCompilerOutputs(
                storageManager.getGraph(), toCompile, outBuilder, new ArrayList<>()
              );
              for (String toDelete : runner.getOutputPathsToDelete()) {
                if (outBuilder.deleteEntry(toDelete)) {
                  cleaned.add(toDelete);
                }
              }
              logDeletedPaths(context, cleaned);
            }

            ExitCode code = runner.compile(toCompile, filter(srcSnapshotDelta.getDeleted(), runner::canCompile), diagnostic, outSink);
            if (code == ExitCode.CANCEL) {
              return code;
            }
            if (code == ExitCode.ERROR && !diagnostic.hasErrors()) {
              // ensure we have some error message
              diagnostic.report(Message.error(runner, runner.getName() + " completed with errors"));
            }
            if (diagnostic.hasErrors()) {
              break;
            }
          }

          NodeSourceSnapshotDelta nextSnapshotDelta = graphUpdater.updateAfterCompilation(
            storageManager.getGraph(), srcSnapshotDelta, createGraphDelta(storageManager.getGraph(), srcSnapshotDelta, outSink), diagnostic.hasErrors()
          );

          if (!diagnostic.hasErrors()) {
            srcSnapshotDelta = nextSnapshotDelta;
          }
          else {
            if (srcSnapshotDelta.isRecompileAll() || !nextSnapshotDelta.hasChanges()) {
              return ExitCode.ERROR;
            }
            // keep previous snapshot delta, just augment it with the newly found sources for recompilation
            if (nextSnapshotDelta.isRecompileAll()) {
              srcSnapshotDelta.markRecompileAll();
            }
            else {
              for (NodeSource source : nextSnapshotDelta.getModified()) {
                srcSnapshotDelta.markRecompile(source);
              }
            }
            if (!isInitialRound) {
              return ExitCode.ERROR;
            }
            // for initial round, partial compilation and when analysis has expanded the scope, attempt automatic error recovery by repeating the compilation with the expanded scope
          }

          isInitialRound = false;
        }
        while (srcSnapshotDelta.hasChanges());
        
      }
      catch (Throwable e) {
        // catch and report all errors before the storage manager is closed
        LOG.log(Level.SEVERE, "Unexpected error compiling " + context.getTargetName(), e);
        diagnostic.report(Message.create(null, e));
        return ExitCode.ERROR;
      }
      finally {
        if (diagnostic instanceof PostponedDiagnosticSink) {
          // report postponed errors, if necessary; ensure all errors are reported before storages are closed
          ((PostponedDiagnosticSink)diagnostic).drainTo(context);
        }
      }

      return ExitCode.OK;
    }
    finally {
      NodeSourceSnapshot sourcesState = srcSnapshotDelta != null? srcSnapshotDelta.asSnapshot() : null;
      saveBuildState(
        context, sourcesState, context.getResources(), modifiedLibraries, deletedLibraries
      );
    }
  }