Add feature to produce serialized diagnostics files

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.

Closes #15191.

Closes #15403.

PiperOrigin-RevId: 452024270
Change-Id: I8d28e6ceb729d90c3200ddd2ba96cdb09ac14afc
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 8b87133..1a2416c 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", ".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 17af008..049441d 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
@@ -1552,8 +1552,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,
@@ -1575,11 +1575,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,
@@ -1639,6 +1646,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.
@@ -1714,6 +1725,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 831d631..3a97e6c 100755
--- 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
@@ -320,6 +320,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 174bfc7..7547098 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
@@ -225,6 +225,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)
@@ -253,6 +254,7 @@
       NestedSet<Artifact> additionalPrunableHeaders,
       Artifact outputFile,
       @Nullable Artifact dotdFile,
+      @Nullable Artifact diagnosticsFile,
       @Nullable Artifact gcnoFile,
       @Nullable Artifact dwoFile,
       @Nullable Artifact ltoIndexingFile,
@@ -272,7 +274,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;
@@ -293,7 +302,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;
@@ -317,6 +332,7 @@
   private static ImmutableSet<Artifact> collectOutputs(
       @Nullable Artifact outputFile,
       @Nullable Artifact dotdFile,
+      @Nullable Artifact diagnosticsFile,
       @Nullable Artifact gcnoFile,
       @Nullable Artifact dwoFile,
       @Nullable Artifact ltoIndexingFile,
@@ -333,6 +349,9 @@
     if (dotdFile != null) {
       outputs.add(dotdFile);
     }
+    if (diagnosticsFile != null) {
+      outputs.add(diagnosticsFile);
+    }
     if (dwoFile != null) {
       outputs.add(dwoFile);
     }
@@ -347,6 +366,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 36c81dc..a0ae6dd 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<>();
@@ -306,6 +307,7 @@
             prunableHeaders,
             outputFile,
             dotdFile,
+            diagnosticsFile,
             gcnoFile,
             dwoFile,
             ltoIndexingFile,
@@ -465,9 +467,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;
   }
 
@@ -492,6 +500,15 @@
     } else {
       dotdFile = null;
     }
+    if (serializedDiagnosticsFilesEnabled()) {
+      String diagnosticsFileName =
+          CppHelper.getDiagnosticsFileName(ccToolchain, outputCategory, outputName);
+      diagnosticsFile =
+          CppHelper.getCompileOutputArtifact(
+              actionConstructionContext, label, diagnosticsFileName, configuration);
+    } else {
+      diagnosticsFile = null;
+    }
     return this;
   }
 
@@ -517,6 +534,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 269ea1d..28acc9a 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
@@ -47,6 +47,7 @@
   private final SpecialArtifact sourceTreeArtifact;
   private final SpecialArtifact outputTreeArtifact;
   private final SpecialArtifact dotdTreeArtifact;
+  private final SpecialArtifact diagnosticsTreeArtifact;
   private final CcToolchainProvider toolchain;
   private final ImmutableList<ArtifactCategory> categories;
   private final ActionOwner actionOwner;
@@ -59,6 +60,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.
@@ -71,6 +73,7 @@
       SpecialArtifact sourceTreeArtifact,
       SpecialArtifact outputTreeArtifact,
       SpecialArtifact dotdTreeArtifact,
+      SpecialArtifact diagnosticsTreeArtifact,
       CppCompileActionBuilder cppCompileActionBuilder,
       CcToolchainProvider toolchain,
       ImmutableList<ArtifactCategory> categories,
@@ -79,6 +82,7 @@
     this.sourceTreeArtifact = sourceTreeArtifact;
     this.outputTreeArtifact = outputTreeArtifact;
     this.dotdTreeArtifact = dotdTreeArtifact;
+    this.diagnosticsTreeArtifact = diagnosticsTreeArtifact;
     this.toolchain = toolchain;
     this.categories = categories;
     this.actionOwner = checkNotNull(actionOwner, outputTreeArtifact);
@@ -136,9 +140,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));
     }
 
     return expandedActions.build();
@@ -156,6 +170,7 @@
             cppCompileActionBuilder.getCoptsFilter(),
             CppActionNames.CPP_COMPILE,
             dotdTreeArtifact,
+            diagnosticsTreeArtifact,
             cppCompileActionBuilder.getFeatureConfiguration(),
             cppCompileActionBuilder.getVariables());
     CppCompileAction.computeKey(
@@ -186,13 +201,14 @@
       TreeFileArtifact sourceTreeFileArtifact,
       TreeFileArtifact outputTreeFileArtifact,
       @Nullable Artifact dotdFileArtifact,
+      @Nullable Artifact diagnosticsFileArtifact,
       NestedSet<Artifact> privateHeaders)
       throws ActionExecutionException {
     CppCompileActionBuilder builder =
         new CppCompileActionBuilder(cppCompileActionBuilder)
             .setAdditionalPrunableHeaders(privateHeaders)
             .setSourceFile(sourceTreeFileArtifact)
-            .setOutputs(outputTreeFileArtifact, dotdFileArtifact);
+            .setOutputs(outputTreeFileArtifact, dotdFileArtifact, diagnosticsFileArtifact);
 
     CcToolchainVariables.Builder buildVariables =
         CcToolchainVariables.builder(cppCompileActionBuilder.getVariables());
@@ -207,6 +223,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 58e5ebf..33ccac1 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 =
@@ -402,13 +404,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.
@@ -764,6 +773,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(
       CcToolchainProvider toolchain,
       ArtifactCategory category,
@@ -786,6 +814,19 @@
     return getArtifactNameForCategory(toolchain, ArtifactCategory.INCLUDED_FILE_LIST, baseName);
   }
 
+  static String getDiagnosticsFileName(
+      CcToolchainProvider toolchain, ArtifactCategory outputCategory, String outputName)
+      throws RuleErrorException {
+    String baseName =
+        outputCategory == ArtifactCategory.OBJECT_FILE
+                || outputCategory == ArtifactCategory.PROCESSED_HEADER
+            ? outputName
+            : getArtifactNameForCategory(toolchain, outputCategory, outputName);
+
+    return getArtifactNameForCategory(
+        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 a13b80d..95429ae 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 9d6fa41..b495290 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
@@ -144,6 +144,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/main/starlark/builtins_bzl/common/cc/cc_helper.bzl b/src/main/starlark/builtins_bzl/common/cc/cc_helper.bzl
index 6b0c8ed..0dfe126 100644
--- a/src/main/starlark/builtins_bzl/common/cc/cc_helper.bzl
+++ b/src/main/starlark/builtins_bzl/common/cc/cc_helper.bzl
@@ -30,6 +30,7 @@
     INTERFACE_LIBRARY = "INTERFACE_LIBRARY",
     PIC_FILE = "PIC_FILE",
     INCLUDED_FILE_LIST = "INCLUDED_FILE_LIST",
+    SERIALIZED_DIAGNOSTICS_FILE = "SERIALIZED_DIAGNOSTICS_FILE",
     OBJECT_FILE = "OBJECT_FILE",
     PIC_OBJECT_FILE = "PIC_OBJECT_FILE",
     CPP_MODULE = "CPP_MODULE",
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 5c7d588..c591ad7 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 0cc4106..21b57cb 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
@@ -148,6 +148,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 9e2e744..b90493f 100644
--- a/tools/cpp/unix_cc_toolchain_config.bzl
+++ b/tools/cpp/unix_cc_toolchain_config.bzl
@@ -1029,6 +1029,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_feature = feature(
         name = "dynamic_library_linker_tool",
         flag_sets = [
@@ -1186,6 +1211,7 @@
         artifact_name_patterns = []
         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 975a507..6b27e6d 100644
--- a/tools/osx/crosstool/cc_toolchain_config.bzl
+++ b/tools/osx/crosstool/cc_toolchain_config.bzl
@@ -2291,6 +2291,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,
@@ -2698,6 +2722,7 @@
             include_paths_feature,
             sysroot_feature,
             dependency_file_feature,
+            serialized_diagnostics_file_feature,
             pic_feature,
             per_object_debug_info_feature,
             preprocessor_defines_feature,
@@ -2777,6 +2802,7 @@
             include_paths_feature,
             sysroot_feature,
             dependency_file_feature,
+            serialized_diagnostics_file_feature,
             pic_feature,
             per_object_debug_info_feature,
             preprocessor_defines_feature,