Add module_files output group, used to build PCMs from cc_library etc directly

Today, `bazel build :target` on a cc_library will not build its PCM.
To debug problems with module compiles we resort to building a target
that depends on the module. This gets in the way, particularly when a *minimal*
such target doesn't exist and others need to reproduce the failure.

With this change, `bazel build :target --output_groups=module_files` will produce the PCM.

PiperOrigin-RevId: 570629210
Change-Id: Ic07509da42637321a0c65f41b0bba8864e3b6a83
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 b5be413..23388bf 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
@@ -1626,6 +1626,9 @@
       if (gcnoFile != null) {
         result.addPicGcnoFile(gcnoFile);
       }
+      if (outputCategory == ArtifactCategory.CPP_MODULE) {
+        result.addModuleFile(picAction.getPrimaryOutput());
+      }
     }
 
     if (generateNoPicAction) {
@@ -1699,6 +1702,9 @@
       if (gcnoFile != null) {
         result.addGcnoFile(gcnoFile);
       }
+      if (outputCategory == ArtifactCategory.CPP_MODULE) {
+        result.addModuleFile(compileAction.getPrimaryOutput());
+      }
     }
     return directOutputs.build();
   }
diff --git a/src/main/java/com/google/devtools/build/lib/rules/cpp/CcCompilationOutputs.java b/src/main/java/com/google/devtools/build/lib/rules/cpp/CcCompilationOutputs.java
index 5df5b73..b65048c 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/cpp/CcCompilationOutputs.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/cpp/CcCompilationOutputs.java
@@ -76,6 +76,9 @@
    */
   private final ImmutableList<Artifact> headerTokenFiles;
 
+  /** All .pcm files built by the target. */
+  private final ImmutableList<Artifact> moduleFiles;
+
   private CcCompilationOutputs(
       ImmutableList<Artifact> objectFiles,
       ImmutableList<Artifact> picObjectFiles,
@@ -85,7 +88,8 @@
       ImmutableList<Artifact> gcnoFiles,
       ImmutableList<Artifact> picGcnoFiles,
       NestedSet<Artifact> temps,
-      ImmutableList<Artifact> headerTokenFiles) {
+      ImmutableList<Artifact> headerTokenFiles,
+      ImmutableList<Artifact> moduleFiles) {
     this.objectFiles = objectFiles;
     this.picObjectFiles = picObjectFiles;
     this.ltoCompilationContext = ltoCompilationContext;
@@ -95,6 +99,7 @@
     this.picGcnoFiles = picGcnoFiles;
     this.temps = temps;
     this.headerTokenFiles = headerTokenFiles;
+    this.moduleFiles = moduleFiles;
   }
 
   /**
@@ -135,6 +140,12 @@
     return StarlarkList.immutableCopyOf(getHeaderTokenFiles());
   }
 
+  @Override
+  public Sequence<Artifact> getStarlarkModuleFiles(StarlarkThread thread) throws EvalException {
+    CcModule.checkPrivateStarlarkificationAllowlist(thread);
+    return StarlarkList.immutableCopyOf(getModuleFiles());
+  }
+
   /** Returns information about bitcode object files resulting from compilation. */
   public LtoCompilationContext getLtoCompilationContext() {
     return ltoCompilationContext;
@@ -209,6 +220,11 @@
     return headerTokenFiles;
   }
 
+  /** Returns an unmodifiable view of the .pcm files. */
+  public Iterable<Artifact> getModuleFiles() {
+    return moduleFiles;
+  }
+
   /** Returns the output files that are considered "compiled" by this C++ compile action. */
   NestedSet<Artifact> getFilesToCompile(boolean parseHeaders, boolean usePic) {
     NestedSetBuilder<Artifact> files = NestedSetBuilder.stableOrder();
@@ -236,6 +252,7 @@
     private final Set<Artifact> picGcnoFiles = new LinkedHashSet<>();
     private final NestedSetBuilder<Artifact> temps = NestedSetBuilder.stableOrder();
     private final Set<Artifact> headerTokenFiles = new LinkedHashSet<>();
+    private final Set<Artifact> moduleFiles = new LinkedHashSet<>();
 
     private Builder() {
       // private to avoid class initialization deadlock between this class and its outer class
@@ -251,7 +268,8 @@
           ImmutableList.copyOf(gcnoFiles),
           ImmutableList.copyOf(picGcnoFiles),
           temps.build(),
-          ImmutableList.copyOf(headerTokenFiles));
+          ImmutableList.copyOf(headerTokenFiles),
+          ImmutableList.copyOf(moduleFiles));
     }
 
     @CanIgnoreReturnValue
@@ -264,6 +282,7 @@
       this.picGcnoFiles.addAll(outputs.picGcnoFiles);
       this.temps.addTransitive(outputs.temps);
       this.headerTokenFiles.addAll(outputs.headerTokenFiles);
+      this.moduleFiles.addAll(outputs.moduleFiles);
       this.ltoCompilationContext.addAll(outputs.ltoCompilationContext);
       return this;
     }
@@ -356,5 +375,11 @@
       headerTokenFiles.add(artifact);
       return this;
     }
+
+    @CanIgnoreReturnValue
+    public Builder addModuleFile(Artifact artifact) {
+      moduleFiles.add(artifact);
+      return this;
+    }
   }
 }
diff --git a/src/main/java/com/google/devtools/build/lib/starlarkbuildapi/cpp/CcCompilationOutputsApi.java b/src/main/java/com/google/devtools/build/lib/starlarkbuildapi/cpp/CcCompilationOutputsApi.java
index 7528680..4e7e935 100644
--- a/src/main/java/com/google/devtools/build/lib/starlarkbuildapi/cpp/CcCompilationOutputsApi.java
+++ b/src/main/java/com/google/devtools/build/lib/starlarkbuildapi/cpp/CcCompilationOutputsApi.java
@@ -74,6 +74,9 @@
   @StarlarkMethod(name = "header_tokens", documented = false, useStarlarkThread = true)
   Sequence<FileT> getStarlarkHeaderTokens(StarlarkThread thread) throws EvalException;
 
+  @StarlarkMethod(name = "module_files", documented = false, useStarlarkThread = true)
+  Sequence<FileT> getStarlarkModuleFiles(StarlarkThread thread) throws EvalException;
+
   @StarlarkMethod(name = "lto_compilation_context", documented = false, useStarlarkThread = true)
   Object getLtoCompilationContextForStarlark(StarlarkThread thread) throws EvalException;
 
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 93dbc23..405c33b 100644
--- a/src/main/starlark/builtins_bzl/common/cc/cc_helper.bzl
+++ b/src/main/starlark/builtins_bzl/common/cc/cc_helper.bzl
@@ -14,11 +14,11 @@
 
 """Utility functions for C++ rules."""
 
+load(":common/cc/cc_common.bzl", "cc_common")
+load(":common/cc/cc_info.bzl", "CcInfo")
+load(":common/objc/objc_common.bzl", "objc_common")
 load(":common/objc/semantics.bzl", objc_semantics = "semantics")
 load(":common/paths.bzl", "paths")
-load(":common/cc/cc_info.bzl", "CcInfo")
-load(":common/cc/cc_common.bzl", "cc_common")
-load(":common/objc/objc_common.bzl", "objc_common")
 
 cc_internal = _builtins.internal.cc_internal
 CcNativeLibraryInfo = _builtins.internal.CcNativeLibraryInfo
@@ -285,6 +285,7 @@
     )
     output_groups_builder["compilation_outputs"] = files_to_compile
     output_groups_builder["compilation_prerequisites_INTERNAL_"] = _collect_compilation_prerequisites(ctx = ctx, compilation_context = compilation_context)
+    output_groups_builder["module_files"] = depset(compilation_outputs.module_files())
 
     if generate_hidden_top_level_group:
         output_groups_builder["_hidden_top_level_INTERNAL_"] = _collect_library_hidden_top_level_artifacts(
diff --git a/src/test/java/com/google/devtools/build/lib/rules/cpp/CppOutputGroupsTest.java b/src/test/java/com/google/devtools/build/lib/rules/cpp/CppOutputGroupsTest.java
index 7350e04..a7363f0 100644
--- a/src/test/java/com/google/devtools/build/lib/rules/cpp/CppOutputGroupsTest.java
+++ b/src/test/java/com/google/devtools/build/lib/rules/cpp/CppOutputGroupsTest.java
@@ -121,4 +121,23 @@
     assertThat(ActionsTestUtil.prettyArtifactNames(getFilesToBuild(groupDynamic)))
         .contains("a/liblib.so");
   }
+
+  @Test
+  public void testModuleOutputGroups() throws Exception {
+    getAnalysisMock()
+        .ccSupport()
+        .setupCcToolchainConfig(
+            mockToolsConfig,
+            CcToolchainConfig.builder().withFeatures("header_modules_feature_configuration"));
+    scratch.file("header.h");
+    scratch.file(
+        "a/BUILD",
+        "cc_library(name='lib', hdrs=['src.h'], features=['header_modules'])",
+        "filegroup(name='group_modules', srcs=[':lib'], output_group = 'module_files')");
+
+    ConfiguredTarget groupArchive = getConfiguredTarget("//a:group_modules");
+
+    assertThat(ActionsTestUtil.prettyArtifactNames(getFilesToBuild(groupArchive)))
+        .containsExactly("a/_objs/lib/lib.pcm");
+  }
 }
diff --git a/src/test/java/com/google/devtools/build/lib/rules/cpp/StarlarkCcCommonTest.java b/src/test/java/com/google/devtools/build/lib/rules/cpp/StarlarkCcCommonTest.java
index 5145649..b1469e3 100755
--- a/src/test/java/com/google/devtools/build/lib/rules/cpp/StarlarkCcCommonTest.java
+++ b/src/test/java/com/google/devtools/build/lib/rules/cpp/StarlarkCcCommonTest.java
@@ -7207,7 +7207,8 @@
         ImmutableList.of(
             "comp_outputs.temps()",
             "comp_outputs.files_to_compile(parse_headers=False, use_pic=True)",
-            "comp_outputs.header_tokens()");
+            "comp_outputs.header_tokens()",
+            "comp_outputs.module_files()");
     for (String call : calls) {
       scratch.overwriteFile(
           "b/rule.bzl",
diff --git a/src/test/java/com/google/devtools/build/lib/starlark/StarlarkIntegrationTest.java b/src/test/java/com/google/devtools/build/lib/starlark/StarlarkIntegrationTest.java
index 97b514f..0fcca78 100644
--- a/src/test/java/com/google/devtools/build/lib/starlark/StarlarkIntegrationTest.java
+++ b/src/test/java/com/google/devtools/build/lib/starlark/StarlarkIntegrationTest.java
@@ -359,7 +359,8 @@
             OutputGroupInfo.COMPILATION_PREREQUISITES,
             OutputGroupInfo.FILES_TO_COMPILE,
             OutputGroupInfo.TEMP_FILES,
-            OutputGroupInfo.VALIDATION);
+            OutputGroupInfo.VALIDATION,
+            "module_files");
   }
 
   @Test