Add feature to produce serialized diagnostics files (#15600)

Using the `serialized_diagnostics_file` feature will add the `--serialized-diagnostics` flag to C/C++/Objective-C/Objective-C++ compiles, causing a declared`.dia` file output to be produced.

(cherry picked from commit c8ea3684514c0b6b46bde04d14aa745c1120d5b9)
diff --git a/src/main/java/com/google/devtools/build/lib/rules/cpp/ArtifactCategory.java b/src/main/java/com/google/devtools/build/lib/rules/cpp/ArtifactCategory.java
index 0e3f1a8..70fea8f 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/cpp/ArtifactCategory.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/cpp/ArtifactCategory.java
@@ -27,6 +27,7 @@
   INTERFACE_LIBRARY("lib", ".ifso", ".tbd", ".if.lib"),
   PIC_FILE("", ".pic"),
   INCLUDED_FILE_LIST("", ".d"),
+  SERIALIZED_DIAGNOSTICS_FILE("", ".dia"),
   OBJECT_FILE("", ".o", ".obj"),
   PIC_OBJECT_FILE("", ".pic.o"),
   CPP_MODULE("", ".pcm"),
diff --git a/src/main/java/com/google/devtools/build/lib/rules/cpp/CcCompilationHelper.java b/src/main/java/com/google/devtools/build/lib/rules/cpp/CcCompilationHelper.java
index 5f547b8..bed28f6 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/cpp/CcCompilationHelper.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/cpp/CcCompilationHelper.java
@@ -1540,8 +1540,8 @@
     SpecialArtifact outputFiles =
         CppHelper.getCompileOutputTreeArtifact(
             actionConstructionContext, label, sourceArtifact, outputName, usePic);
-    // Dotd file output is specified in the execution phase.
-    builder.setOutputs(outputFiles, /* dotdFile= */ null);
+    // Dotd and dia file outputs are specified in the execution phase.
+    builder.setOutputs(outputFiles, /* dotdFile= */ null, /* diagnosticsFile= */ null);
     builder.setVariables(
         setupCompileBuildVariables(
             builder,
@@ -1563,11 +1563,18 @@
           CppHelper.getDotdOutputTreeArtifact(
               actionConstructionContext, label, sourceArtifact, outputName, usePic);
     }
+    SpecialArtifact diagnosticsTreeArtifact = null;
+    if (builder.serializedDiagnosticsFilesEnabled()) {
+      diagnosticsTreeArtifact =
+          CppHelper.getDiagnosticsOutputTreeArtifact(
+              actionConstructionContext, label, sourceArtifact, outputName, usePic);
+    }
     CppCompileActionTemplate actionTemplate =
         new CppCompileActionTemplate(
             sourceArtifact,
             outputFiles,
             dotdTreeArtifact,
+            diagnosticsTreeArtifact,
             builder,
             ccToolchain,
             outputCategories,
@@ -1627,6 +1634,10 @@
     if (builder.getDotdFile() != null) {
       dotdFileExecPath = builder.getDotdFile().getExecPathString();
     }
+    String diagnosticsFileExecPath = null;
+    if (builder.getDiagnosticsFile() != null) {
+      diagnosticsFileExecPath = builder.getDiagnosticsFile().getExecPathString();
+    }
     if (needsFdoBuildVariables && fdoContext.hasArtifacts(cppConfiguration)) {
       // This modifies the passed-in builder, which is a surprising side-effect, and makes it unsafe
       // to call this method multiple times for the same builder.
@@ -1705,6 +1716,7 @@
         /* thinLtoOutputObjectFile= */ null,
         getCopts(builder.getSourceFile(), sourceLabel),
         dotdFileExecPath,
+        diagnosticsFileExecPath,
         usePic,
         ccCompilationContext.getExternalIncludeDirs(),
         additionalBuildVariables);
diff --git a/src/main/java/com/google/devtools/build/lib/rules/cpp/CcModule.java b/src/main/java/com/google/devtools/build/lib/rules/cpp/CcModule.java
index 6d546f7..143b57d 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/cpp/CcModule.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/cpp/CcModule.java
@@ -305,6 +305,7 @@
         usePic,
         /* fdoStamp= */ null,
         /* dotdFileExecPath= */ null,
+        /* diagnosticsFileExecPath= */ null,
         variablesExtensions,
         /* additionalBuildVariables= */ ImmutableMap.of(),
         /* directModuleMaps= */ ImmutableList.of(),
diff --git a/src/main/java/com/google/devtools/build/lib/rules/cpp/CompileBuildVariables.java b/src/main/java/com/google/devtools/build/lib/rules/cpp/CompileBuildVariables.java
index 866706a..5ebe801 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/cpp/CompileBuildVariables.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/cpp/CompileBuildVariables.java
@@ -44,6 +44,8 @@
   OUTPUT_FILE("output_file"),
   /** Variable for the dependency file path */
   DEPENDENCY_FILE("dependency_file"),
+  /** Variable for the serialized diagnostics file path */
+  SERIALIZED_DIAGNOSTICS_FILE("serialized_diagnostics_file"),
   /** Variable for the module file name. */
   MODULE_NAME("module_name"),
   /**
@@ -151,6 +153,7 @@
       boolean usePic,
       String fdoStamp,
       String dotdFileExecPath,
+      String diagnosticsFileExecPath,
       ImmutableList<VariablesExtension> variablesExtensions,
       ImmutableMap<String, String> additionalBuildVariables,
       Iterable<Artifact> directModuleMaps,
@@ -184,6 +187,7 @@
           usePic,
           fdoStamp,
           dotdFileExecPath,
+          diagnosticsFileExecPath,
           variablesExtensions,
           additionalBuildVariables,
           directModuleMaps,
@@ -219,6 +223,7 @@
       boolean usePic,
       String fdoStamp,
       String dotdFileExecPath,
+      String diagnosticsFileExecPath,
       ImmutableList<VariablesExtension> variablesExtensions,
       ImmutableMap<String, String> additionalBuildVariables,
       Iterable<Artifact> directModuleMaps,
@@ -252,6 +257,7 @@
         usePic,
         fdoStamp,
         dotdFileExecPath,
+        diagnosticsFileExecPath,
         variablesExtensions,
         additionalBuildVariables,
         directModuleMaps,
@@ -281,6 +287,7 @@
       boolean usePic,
       String fdoStamp,
       String dotdFileExecPath,
+      String diagnosticsFileExecPath,
       ImmutableList<VariablesExtension> variablesExtensions,
       ImmutableMap<String, String> additionalBuildVariables,
       Iterable<Artifact> directModuleMaps,
@@ -319,6 +326,7 @@
         thinLtoOutputObjectFile,
         userCompileFlags,
         dotdFileExecPath,
+        diagnosticsFileExecPath,
         usePic,
         ImmutableList.of(),
         ImmutableMap.of());
@@ -338,6 +346,7 @@
       String thinLtoOutputObjectFile,
       Iterable<String> userCompileFlags,
       String dotdFileExecPath,
+      String diagnosticsFileExecPath,
       boolean usePic,
       ImmutableList<PathFragment> externalIncludeDirs,
       Map<String, String> additionalBuildVariables) {
@@ -357,6 +366,12 @@
       buildVariables.addStringVariable(DEPENDENCY_FILE.getVariableName(), dotdFileExecPath);
     }
 
+    // Set diagnostics_file to enable <object>.dia file generation.
+    if (diagnosticsFileExecPath != null) {
+      buildVariables.addStringVariable(
+          SERIALIZED_DIAGNOSTICS_FILE.getVariableName(), diagnosticsFileExecPath);
+    }
+
     if (gcnoFile != null) {
       buildVariables.addStringVariable(GCOV_GCNO_FILE.getVariableName(), gcnoFile);
     }
diff --git a/src/main/java/com/google/devtools/build/lib/rules/cpp/CppCompileAction.java b/src/main/java/com/google/devtools/build/lib/rules/cpp/CppCompileAction.java
index eececf7..ed39beb 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/cpp/CppCompileAction.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/cpp/CppCompileAction.java
@@ -222,6 +222,7 @@
    *     not needed during actual execution.
    * @param outputFile the object file that is written as result of the compilation
    * @param dotdFile the .d file that is generated as a side-effect of compilation
+   * @param diagnosticsFile the .dia file that is generated as a side-effect of compilation
    * @param gcnoFile the coverage notes that are written in coverage mode, can be null
    * @param dwoFile the .dwo output file where debug information is stored for Fission builds (null
    *     if Fission mode is disabled)
@@ -250,6 +251,7 @@
       NestedSet<Artifact> additionalPrunableHeaders,
       Artifact outputFile,
       @Nullable Artifact dotdFile,
+      @Nullable Artifact diagnosticsFile,
       @Nullable Artifact gcnoFile,
       @Nullable Artifact dwoFile,
       @Nullable Artifact ltoIndexingFile,
@@ -269,7 +271,14 @@
         NestedSetBuilder.fromNestedSet(mandatoryInputs)
             .addTransitive(inputsForInvalidation)
             .build(),
-        collectOutputs(outputFile, dotdFile, gcnoFile, dwoFile, ltoIndexingFile, additionalOutputs),
+        collectOutputs(
+            outputFile,
+            dotdFile,
+            diagnosticsFile,
+            gcnoFile,
+            dwoFile,
+            ltoIndexingFile,
+            additionalOutputs),
         env);
     Preconditions.checkNotNull(outputFile);
     this.outputFile = outputFile;
@@ -290,7 +299,13 @@
         Preconditions.checkNotNull(additionalIncludeScanningRoots);
     this.compileCommandLine =
         buildCommandLine(
-            sourceFile, coptsFilter, actionName, dotdFile, featureConfiguration, variables);
+            sourceFile,
+            coptsFilter,
+            actionName,
+            dotdFile,
+            diagnosticsFile,
+            featureConfiguration,
+            variables);
     this.executionInfo = executionInfo;
     this.actionName = actionName;
     this.featureConfiguration = featureConfiguration;
@@ -314,6 +329,7 @@
   private static ImmutableSet<Artifact> collectOutputs(
       @Nullable Artifact outputFile,
       @Nullable Artifact dotdFile,
+      @Nullable Artifact diagnosticsFile,
       @Nullable Artifact gcnoFile,
       @Nullable Artifact dwoFile,
       @Nullable Artifact ltoIndexingFile,
@@ -330,6 +346,9 @@
     if (dotdFile != null) {
       outputs.add(dotdFile);
     }
+    if (diagnosticsFile != null) {
+      outputs.add(diagnosticsFile);
+    }
     if (dwoFile != null) {
       outputs.add(dwoFile);
     }
@@ -344,6 +363,7 @@
       CoptsFilter coptsFilter,
       String actionName,
       Artifact dotdFile,
+      Artifact diagnosticsFile,
       FeatureConfiguration featureConfiguration,
       CcToolchainVariables variables) {
     return CompileCommandLine.builder(sourceFile, coptsFilter, actionName, dotdFile)
diff --git a/src/main/java/com/google/devtools/build/lib/rules/cpp/CppCompileActionBuilder.java b/src/main/java/com/google/devtools/build/lib/rules/cpp/CppCompileActionBuilder.java
index 207a8d5..690e8ac 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/cpp/CppCompileActionBuilder.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/cpp/CppCompileActionBuilder.java
@@ -58,6 +58,7 @@
   private Artifact dwoFile;
   private Artifact ltoIndexingFile;
   private Artifact dotdFile;
+  private Artifact diagnosticsFile;
   private Artifact gcnoFile;
   private CcCompilationContext ccCompilationContext = CcCompilationContext.EMPTY;
   private final List<String> pluginOpts = new ArrayList<>();
@@ -292,6 +293,7 @@
             prunableHeaders,
             outputFile,
             dotdFile,
+            diagnosticsFile,
             gcnoFile,
             dwoFile,
             ltoIndexingFile,
@@ -446,9 +448,15 @@
         && !featureConfiguration.isEnabled(CppRuleClasses.PARSE_SHOWINCLUDES);
   }
 
-  public CppCompileActionBuilder setOutputs(Artifact outputFile, Artifact dotdFile) {
+  public boolean serializedDiagnosticsFilesEnabled() {
+    return featureConfiguration.isEnabled(CppRuleClasses.SERIALIZED_DIAGNOSTICS_FILE);
+  }
+
+  public CppCompileActionBuilder setOutputs(
+      Artifact outputFile, Artifact dotdFile, Artifact diagnosticsFile) {
     this.outputFile = outputFile;
     this.dotdFile = dotdFile;
+    this.diagnosticsFile = diagnosticsFile;
     return this;
   }
 
@@ -475,6 +483,16 @@
     } else {
       dotdFile = null;
     }
+    if (serializedDiagnosticsFilesEnabled()) {
+      String diagnosticsFileName =
+          CppHelper.getDiagnosticsFileName(
+              ruleErrorConsumer, ccToolchain, outputCategory, outputName);
+      diagnosticsFile =
+          CppHelper.getCompileOutputArtifact(
+              actionConstructionContext, label, diagnosticsFileName, configuration);
+    } else {
+      diagnosticsFile = null;
+    }
     return this;
   }
 
@@ -500,6 +518,10 @@
     return this.dotdFile;
   }
 
+  public Artifact getDiagnosticsFile() {
+    return this.diagnosticsFile;
+  }
+
   public CppCompileActionBuilder setGcnoFile(Artifact gcnoFile) {
     this.gcnoFile = gcnoFile;
     return this;
diff --git a/src/main/java/com/google/devtools/build/lib/rules/cpp/CppCompileActionTemplate.java b/src/main/java/com/google/devtools/build/lib/rules/cpp/CppCompileActionTemplate.java
index 24120d5..a5ebdef 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/cpp/CppCompileActionTemplate.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/cpp/CppCompileActionTemplate.java
@@ -48,6 +48,7 @@
   private final SpecialArtifact sourceTreeArtifact;
   private final SpecialArtifact outputTreeArtifact;
   private final SpecialArtifact dotdTreeArtifact;
+  private final SpecialArtifact diagnosticsTreeArtifact;
   private final CcToolchainProvider toolchain;
   private final Iterable<ArtifactCategory> categories;
   private final ActionOwner actionOwner;
@@ -60,6 +61,7 @@
    * @param sourceTreeArtifact the TreeArtifact that contains source files to compile.
    * @param outputTreeArtifact the TreeArtifact that contains compilation outputs.
    * @param dotdTreeArtifact the TreeArtifact that contains dotd files.
+   * @param diagnosticsTreeArtifact the TreeArtifact that contains serialized diagnostics files.
    * @param cppCompileActionBuilder An almost completely configured {@link CppCompileActionBuilder}
    *     without the input and output files set. It is used as a template to instantiate expanded
    *     {CppCompileAction}s.
@@ -72,6 +74,7 @@
       SpecialArtifact sourceTreeArtifact,
       SpecialArtifact outputTreeArtifact,
       SpecialArtifact dotdTreeArtifact,
+      SpecialArtifact diagnosticsTreeArtifact,
       CppCompileActionBuilder cppCompileActionBuilder,
       CcToolchainProvider toolchain,
       Iterable<ArtifactCategory> categories,
@@ -80,6 +83,7 @@
     this.sourceTreeArtifact = sourceTreeArtifact;
     this.outputTreeArtifact = outputTreeArtifact;
     this.dotdTreeArtifact = dotdTreeArtifact;
+    this.diagnosticsTreeArtifact = diagnosticsTreeArtifact;
     this.toolchain = toolchain;
     this.categories = categories;
     this.actionOwner = checkNotNull(actionOwner, outputTreeArtifact);
@@ -137,9 +141,19 @@
               TreeFileArtifact.createTemplateExpansionOutput(
                   dotdTreeArtifact, outputName + ".d", artifactOwner);
         }
+        TreeFileArtifact diagnosticsFileArtifact = null;
+        if (diagnosticsTreeArtifact != null) {
+          diagnosticsFileArtifact =
+              TreeFileArtifact.createTemplateExpansionOutput(
+                  diagnosticsTreeArtifact, outputName + ".dia", artifactOwner);
+        }
         expandedActions.add(
             createAction(
-                inputTreeFileArtifact, outputTreeFileArtifact, dotdFileArtifact, privateHeaders));
+                inputTreeFileArtifact,
+                outputTreeFileArtifact,
+                dotdFileArtifact,
+                diagnosticsFileArtifact,
+                privateHeaders));
       } catch (EvalException e) {
         throw new ActionTemplateExpansionException(e);
       }
@@ -160,6 +174,7 @@
             cppCompileActionBuilder.getCoptsFilter(),
             CppActionNames.CPP_COMPILE,
             dotdTreeArtifact,
+            diagnosticsTreeArtifact,
             cppCompileActionBuilder.getFeatureConfiguration(),
             cppCompileActionBuilder.getVariables());
     CppCompileAction.computeKey(
@@ -190,12 +205,13 @@
       Artifact sourceTreeFileArtifact,
       Artifact outputTreeFileArtifact,
       @Nullable Artifact dotdFileArtifact,
+      @Nullable Artifact diagnosticsFileArtifact,
       NestedSet<Artifact> privateHeaders)
       throws ActionTemplateExpansionException {
     CppCompileActionBuilder builder = new CppCompileActionBuilder(cppCompileActionBuilder);
     builder.setAdditionalPrunableHeaders(privateHeaders);
     builder.setSourceFile(sourceTreeFileArtifact);
-    builder.setOutputs(outputTreeFileArtifact, dotdFileArtifact);
+    builder.setOutputs(outputTreeFileArtifact, dotdFileArtifact, diagnosticsFileArtifact);
 
     CcToolchainVariables.Builder buildVariables =
         CcToolchainVariables.builder(cppCompileActionBuilder.getVariables());
@@ -210,6 +226,11 @@
           CompileBuildVariables.DEPENDENCY_FILE.getVariableName(),
           dotdFileArtifact.getExecPathString());
     }
+    if (diagnosticsFileArtifact != null) {
+      buildVariables.overrideStringVariable(
+          CompileBuildVariables.SERIALIZED_DIAGNOSTICS_FILE.getVariableName(),
+          diagnosticsFileArtifact.getExecPathString());
+    }
 
     builder.setVariables(buildVariables.build());
 
diff --git a/src/main/java/com/google/devtools/build/lib/rules/cpp/CppHelper.java b/src/main/java/com/google/devtools/build/lib/rules/cpp/CppHelper.java
index c47e60f..73067b5 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/cpp/CppHelper.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/cpp/CppHelper.java
@@ -85,6 +85,8 @@
   static final PathFragment PIC_OBJS = PathFragment.create("_pic_objs");
   static final PathFragment DOTD_FILES = PathFragment.create("_dotd");
   static final PathFragment PIC_DOTD_FILES = PathFragment.create("_pic_dotd");
+  static final PathFragment DIA_FILES = PathFragment.create("_dia");
+  static final PathFragment PIC_DIA_FILES = PathFragment.create("_pic_dia");
 
   // TODO(bazel-team): should this use Link.SHARED_LIBRARY_FILETYPES?
   public static final FileTypeSet SHARED_LIBRARY_FILETYPES =
@@ -397,13 +399,20 @@
     }
   }
 
-  /** Returns the directory where object files are created. */
+  /** Returns the directory where dotd files are created. */
   private static PathFragment getDotdDirectory(
       Label ruleLabel, boolean usePic, boolean siblingRepositoryLayout) {
     return AnalysisUtils.getUniqueDirectory(
         ruleLabel, usePic ? PIC_DOTD_FILES : DOTD_FILES, siblingRepositoryLayout);
   }
 
+  /** Returns the directory where serialized diagnostics files are created. */
+  private static PathFragment getDiagnosticsDirectory(
+      Label ruleLabel, boolean usePic, boolean siblingRepositoryLayout) {
+    return AnalysisUtils.getUniqueDirectory(
+        ruleLabel, usePic ? PIC_DIA_FILES : DIA_FILES, siblingRepositoryLayout);
+  }
+
   /**
    * Returns a function that gets the C++ runfiles from a {@link TransitiveInfoCollection} or the
    * empty runfiles instance if it does not contain that provider.
@@ -760,6 +769,25 @@
         sourceTreeArtifact.getRoot());
   }
 
+  /**
+   * Returns the corresponding serialized diagnostics files TreeArtifact given the source
+   * TreeArtifact.
+   */
+  public static SpecialArtifact getDiagnosticsOutputTreeArtifact(
+      ActionConstructionContext actionConstructionContext,
+      Label label,
+      Artifact sourceTreeArtifact,
+      String outputName,
+      boolean usePic) {
+    return actionConstructionContext.getTreeArtifact(
+        getDiagnosticsDirectory(
+                label,
+                usePic,
+                actionConstructionContext.getConfiguration().isSiblingRepositoryLayout())
+            .getRelative(outputName),
+        sourceTreeArtifact.getRoot());
+  }
+
   public static String getArtifactNameForCategory(
       RuleErrorConsumer ruleErrorConsumer,
       CcToolchainProvider toolchain,
@@ -790,6 +818,22 @@
         ruleErrorConsumer, toolchain, ArtifactCategory.INCLUDED_FILE_LIST, baseName);
   }
 
+  static String getDiagnosticsFileName(
+      RuleErrorConsumer ruleErrorConsumer,
+      CcToolchainProvider toolchain,
+      ArtifactCategory outputCategory,
+      String outputName)
+      throws RuleErrorException {
+    String baseName =
+        outputCategory == ArtifactCategory.OBJECT_FILE
+                || outputCategory == ArtifactCategory.PROCESSED_HEADER
+            ? outputName
+            : getArtifactNameForCategory(ruleErrorConsumer, toolchain, outputCategory, outputName);
+
+    return getArtifactNameForCategory(
+        ruleErrorConsumer,toolchain, ArtifactCategory.SERIALIZED_DIAGNOSTICS_FILE, baseName);
+  }
+
   /**
    * Returns true when {@link CppRuleClasses#WINDOWS_EXPORT_ALL_SYMBOLS} feature is enabled and
    * {@link CppRuleClasses#NO_WINDOWS_EXPORT_ALL_SYMBOLS} feature is not enabled and no custom DEF
diff --git a/src/main/java/com/google/devtools/build/lib/rules/cpp/CppLinkstampCompileHelper.java b/src/main/java/com/google/devtools/build/lib/rules/cpp/CppLinkstampCompileHelper.java
index 18e5761..0d40b59 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/cpp/CppLinkstampCompileHelper.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/cpp/CppLinkstampCompileHelper.java
@@ -83,7 +83,7 @@
             .setFeatureConfiguration(featureConfiguration)
             .setSourceFile(sourceFile)
             .setSemantics(semantics)
-            .setOutputs(outputFile, null)
+            .setOutputs(outputFile, /* dotdFile= */ null, /* diagnosticsFile= */ null)
             .setInputsForInvalidation(inputsForInvalidation)
             .setBuiltinIncludeFiles(buildInfoHeaderArtifacts)
             .addMandatoryInputs(nonCodeInputs)
@@ -173,6 +173,7 @@
         needsPic,
         fdoBuildStamp,
         /* dotdFileExecPath= */ null,
+        /* diagnosticsFileExecPath= */ null,
         /* variablesExtensions= */ ImmutableList.of(),
         /* additionalBuildVariables= */ ImmutableMap.of(),
         /* directModuleMaps= */ ImmutableList.of(),
diff --git a/src/main/java/com/google/devtools/build/lib/rules/cpp/CppRuleClasses.java b/src/main/java/com/google/devtools/build/lib/rules/cpp/CppRuleClasses.java
index c4a8224..5657559 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/cpp/CppRuleClasses.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/cpp/CppRuleClasses.java
@@ -141,6 +141,12 @@
   /** A string constant for the dependency_file feature. This feature generates the .d file. */
   public static final String DEPENDENCY_FILE = "dependency_file";
 
+  /**
+   * A string constant for the serialized_diagnostics_file feature. This feature generates the .dia
+   * file.
+   */
+  public static final String SERIALIZED_DIAGNOSTICS_FILE = "serialized_diagnostics_file";
+
   /** A string constant for the module_map_home_cwd feature. */
   public static final String MODULE_MAP_HOME_CWD = "module_map_home_cwd";
 
diff --git a/src/test/java/com/google/devtools/build/lib/packages/util/mock/osx_cc_toolchain_config.bzl b/src/test/java/com/google/devtools/build/lib/packages/util/mock/osx_cc_toolchain_config.bzl
index a453098..9a69d67 100644
--- a/src/test/java/com/google/devtools/build/lib/packages/util/mock/osx_cc_toolchain_config.bzl
+++ b/src/test/java/com/google/devtools/build/lib/packages/util/mock/osx_cc_toolchain_config.bzl
@@ -5575,6 +5575,30 @@
         ],
     )
 
+    serialized_diagnostics_file_feature = feature(
+        name = "serialized_diagnostics_file",
+        flag_sets = [
+            flag_set(
+                actions = [
+                    ACTION_NAMES.assemble,
+                    ACTION_NAMES.preprocess_assemble,
+                    ACTION_NAMES.c_compile,
+                    ACTION_NAMES.cpp_compile,
+                    ACTION_NAMES.cpp_module_compile,
+                    ACTION_NAMES.objc_compile,
+                    ACTION_NAMES.objcpp_compile,
+                    ACTION_NAMES.cpp_header_parsing,
+                ],
+                flag_groups = [
+                    flag_group(
+                        flags = ["--serialize-diagnostics", "%{serialized_diagnostics_file}"],
+                        expand_if_available = "serialized_diagnostics_file",
+                    ),
+                ],
+            ),
+        ],
+    )
+
     opt_only_flag_feature = feature(
         name = "opt_only_flag",
         flag_sets = [
@@ -7823,6 +7847,7 @@
         version_min_feature,
         dead_strip_feature,
         dependency_file_feature,
+        serialized_diagnostics_file_feature,
         random_seed_feature,
         pic_feature,
         per_object_debug_info_feature,
diff --git a/src/test/java/com/google/devtools/build/lib/rules/objc/ObjcLibraryTest.java b/src/test/java/com/google/devtools/build/lib/rules/objc/ObjcLibraryTest.java
index 5bc81d8..dd2fb1f 100644
--- a/src/test/java/com/google/devtools/build/lib/rules/objc/ObjcLibraryTest.java
+++ b/src/test/java/com/google/devtools/build/lib/rules/objc/ObjcLibraryTest.java
@@ -146,6 +146,33 @@
   }
 
   @Test
+  public void testSerializedDiagnosticsFileFeature() throws Exception {
+    useConfiguration("--features=serialized_diagnostics_file");
+
+    createLibraryTargetWriter("//objc/lib1")
+        .setAndCreateFiles("srcs", "a.m")
+        .setAndCreateFiles("hdrs", "hdr.h")
+        .write();
+
+    createLibraryTargetWriter("//objc/lib2")
+        .setAndCreateFiles("srcs", "a.m")
+        .setAndCreateFiles("hdrs", "hdr.h")
+        .setList("deps", "//objc/lib1")
+        .write();
+
+    createLibraryTargetWriter("//objc:x")
+        .setAndCreateFiles("srcs", "a.m", "private.h")
+        .setAndCreateFiles("hdrs", "hdr.h")
+        .setList("deps", "//objc/lib2:lib2")
+        .write();
+
+    CppCompileAction compileA = (CppCompileAction) compileAction("//objc:x", "a.o");
+
+    assertThat(Artifact.toRootRelativePaths(compileA.getOutputs()))
+        .containsExactly("objc/_objs/x/arc/a.o", "objc/_objs/x/arc/a.d", "objc/_objs/x/arc/a.dia");
+  }
+
+  @Test
   public void testCompilesSourcesWithSameBaseName() throws Exception {
     createLibraryTargetWriter("//foo:lib")
         .setAndCreateFiles("srcs", "a.m", "pkg1/a.m", "b.m")
diff --git a/tools/cpp/unix_cc_toolchain_config.bzl b/tools/cpp/unix_cc_toolchain_config.bzl
index 89457b1..0b77afc 100644
--- a/tools/cpp/unix_cc_toolchain_config.bzl
+++ b/tools/cpp/unix_cc_toolchain_config.bzl
@@ -1064,6 +1064,31 @@
         ],
     )
 
+    serialized_diagnostics_file_feature = feature(
+        name = "serialized_diagnostics_file",
+        flag_sets = [
+            flag_set(
+                actions = [
+                    ACTION_NAMES.assemble,
+                    ACTION_NAMES.preprocess_assemble,
+                    ACTION_NAMES.c_compile,
+                    ACTION_NAMES.cpp_compile,
+                    ACTION_NAMES.cpp_module_compile,
+                    ACTION_NAMES.objc_compile,
+                    ACTION_NAMES.objcpp_compile,
+                    ACTION_NAMES.cpp_header_parsing,
+                    ACTION_NAMES.clif_match,
+                ],
+                flag_groups = [
+                    flag_group(
+                        flags = ["--serialize-diagnostics", "%{serialized_diagnostics_file}"],
+                        expand_if_available = "serialized_diagnostics_file",
+                    ),
+                ],
+            ),
+        ],
+    )
+
     dynamic_library_linker_tool_path = tool_paths
     dynamic_library_linker_tool_feature = feature(
         name = "dynamic_library_linker_tool",
@@ -1202,6 +1227,7 @@
     if is_linux:
         features = [
             dependency_file_feature,
+            serialized_diagnostics_file_feature,
             random_seed_feature,
             pic_feature,
             per_object_debug_info_feature,
diff --git a/tools/osx/crosstool/cc_toolchain_config.bzl b/tools/osx/crosstool/cc_toolchain_config.bzl
index 0c3dfc3..434248d 100644
--- a/tools/osx/crosstool/cc_toolchain_config.bzl
+++ b/tools/osx/crosstool/cc_toolchain_config.bzl
@@ -2290,6 +2290,30 @@
         ],
     )
 
+    serialized_diagnostics_file_feature = feature(
+        name = "serialized_diagnostics_file",
+        flag_sets = [
+            flag_set(
+                actions = [
+                    ACTION_NAMES.assemble,
+                    ACTION_NAMES.preprocess_assemble,
+                    ACTION_NAMES.c_compile,
+                    ACTION_NAMES.cpp_compile,
+                    ACTION_NAMES.cpp_module_compile,
+                    ACTION_NAMES.objc_compile,
+                    ACTION_NAMES.objcpp_compile,
+                    ACTION_NAMES.cpp_header_parsing,
+                ],
+                flag_groups = [
+                    flag_group(
+                        flags = ["--serialize-diagnostics", "%{serialized_diagnostics_file}"],
+                        expand_if_available = "serialized_diagnostics_file",
+                    ),
+                ],
+            ),
+        ],
+    )
+
     preprocessor_defines_feature = feature(
         name = "preprocessor_defines",
         enabled = True,
@@ -2697,6 +2721,7 @@
             include_paths_feature,
             sysroot_feature,
             dependency_file_feature,
+            serialized_diagnostics_file_feature,
             pic_feature,
             per_object_debug_info_feature,
             preprocessor_defines_feature,
@@ -2776,6 +2801,7 @@
             include_paths_feature,
             sysroot_feature,
             dependency_file_feature,
+            serialized_diagnostics_file_feature,
             pic_feature,
             per_object_debug_info_feature,
             preprocessor_defines_feature,