/** Constructs the RuleConfiguredTarget instance based on the values set for this Builder. */
  public ConfiguredTarget build() {
    if (ruleContext.getConfiguration().enforceConstraints()) {
      checkConstraints();
    }
    if (ruleContext.hasErrors()) {
      return null;
    }

    FilesToRunProvider filesToRunProvider =
        new FilesToRunProvider(
            ruleContext.getLabel(),
            RuleContext.getFilesToRun(runfilesSupport, filesToBuild),
            runfilesSupport,
            executable);
    add(FileProvider.class, new FileProvider(ruleContext.getLabel(), filesToBuild));
    add(FilesToRunProvider.class, filesToRunProvider);

    if (runfilesSupport != null) {
      // If a binary is built, build its runfiles, too
      addOutputGroup(OutputGroupProvider.HIDDEN_TOP_LEVEL, runfilesSupport.getRunfilesMiddleman());
    } else if (providers.get(RunfilesProvider.class) != null) {
      // If we don't have a RunfilesSupport (probably because this is not a binary rule), we still
      // want to build the files this rule contributes to runfiles of dependent rules so that we
      // report an error if one of these is broken.
      //
      // Note that this is a best-effort thing: there is .getDataRunfiles() and all the language-
      // specific *RunfilesProvider classes, which we don't add here for reasons that are lost in
      // the mists of time.
      addOutputGroup(
          OutputGroupProvider.HIDDEN_TOP_LEVEL,
          ((RunfilesProvider) providers.get(RunfilesProvider.class))
              .getDefaultRunfiles()
              .getAllArtifacts());
    }

    // Create test action and artifacts if target was successfully initialized
    // and is a test.
    if (TargetUtils.isTestRule(ruleContext.getTarget())) {
      Preconditions.checkState(runfilesSupport != null);
      add(TestProvider.class, initializeTestProvider(filesToRunProvider));
    }
    add(ExtraActionArtifactsProvider.class, initializeExtraActions());
    if (!outputGroupBuilders.isEmpty()) {
      ImmutableMap.Builder<String, NestedSet<Artifact>> outputGroups = ImmutableMap.builder();
      for (Map.Entry<String, NestedSetBuilder<Artifact>> entry : outputGroupBuilders.entrySet()) {
        outputGroups.put(entry.getKey(), entry.getValue().build());
      }

      add(OutputGroupProvider.class, new OutputGroupProvider(outputGroups.build()));
    }

    return new RuleConfiguredTarget(
        ruleContext, mandatoryStampFiles, skylarkProviders.build(), providers);
  }
  private static void addOutputGroups(
      Object value, Location loc, RuleConfiguredTargetBuilder builder) throws EvalException {
    Map<String, SkylarkValue> outputGroups =
        SkylarkType.castMap(value, String.class, SkylarkValue.class, "output_groups");

    for (String outputGroup : outputGroups.keySet()) {
      SkylarkValue objects = outputGroups.get(outputGroup);
      NestedSet<Artifact> artifacts;

      String typeErrorMessage =
          "Output group '%s' is of unexpected type. "
              + "Should be list or set of Files, but got '%s' instead.";

      if (objects instanceof SkylarkList) {
        NestedSetBuilder<Artifact> nestedSetBuilder = NestedSetBuilder.stableOrder();
        for (Object o : (SkylarkList) objects) {
          if (o instanceof Artifact) {
            nestedSetBuilder.add((Artifact) o);
          } else {
            throw new EvalException(
                loc,
                String.format(
                    typeErrorMessage,
                    outputGroup,
                    "list with an element of " + EvalUtils.getDataTypeNameFromClass(o.getClass())));
          }
        }
        artifacts = nestedSetBuilder.build();
      } else {
        artifacts =
            SkylarkType.cast(
                    objects,
                    SkylarkNestedSet.class,
                    Artifact.class,
                    loc,
                    typeErrorMessage,
                    outputGroup,
                    EvalUtils.getDataTypeName(objects, true))
                .getSet(Artifact.class);
      }
      builder.addOutputGroup(outputGroup, artifacts);
    }
  }