Implement whitelisting for implicit outputs produced by CcLibrary rules

RELNOTES:none
PiperOrigin-RevId: 320348483
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/BUILD b/src/main/java/com/google/devtools/build/lib/analysis/BUILD
index 02925d8..c893f6e 100644
--- a/src/main/java/com/google/devtools/build/lib/analysis/BUILD
+++ b/src/main/java/com/google/devtools/build/lib/analysis/BUILD
@@ -399,6 +399,7 @@
         "//src/main/java/com/google/devtools/build/lib/profiler",
         "//src/main/java/com/google/devtools/build/lib/profiler:google-auto-profiler-utils",
         "//src/main/java/com/google/devtools/build/lib/profiler/memory:current_rule_tracker",
+        "//src/main/java/com/google/devtools/build/lib/rules/cpp:denied_implicit_outputs_marker_provider",
         "//src/main/java/com/google/devtools/build/lib/shell",
         "//src/main/java/com/google/devtools/build/lib/skyframe:aspect_creation_exception",
         "//src/main/java/com/google/devtools/build/lib/skyframe:aspect_value_key",
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/ConfiguredTargetFactory.java b/src/main/java/com/google/devtools/build/lib/analysis/ConfiguredTargetFactory.java
index dd0b0ec..609e30e 100644
--- a/src/main/java/com/google/devtools/build/lib/analysis/ConfiguredTargetFactory.java
+++ b/src/main/java/com/google/devtools/build/lib/analysis/ConfiguredTargetFactory.java
@@ -65,6 +65,7 @@
 import com.google.devtools.build.lib.packages.StarlarkProviderIdentifier;
 import com.google.devtools.build.lib.packages.Target;
 import com.google.devtools.build.lib.profiler.memory.CurrentRuleTracker;
+import com.google.devtools.build.lib.rules.cpp.DeniedImplicitOutputMarkerProvider;
 import com.google.devtools.build.lib.skyframe.AspectValueKey;
 import com.google.devtools.build.lib.skyframe.ConfiguredTargetAndData;
 import com.google.devtools.build.lib.skyframe.ConfiguredTargetKey;
@@ -85,6 +86,11 @@
  */
 @ThreadSafe
 public final class ConfiguredTargetFactory {
+
+  public static final String CC_LIB_IMPLICIT_OUTPUTS_ERROR =
+      "Using implicit outputs from cc_library (%s) is forbidden. Use"
+          + " the rule cc_implicit_output as an alternative.";
+
   // This class is not meant to be outside of the analysis phase machinery and is only public
   // in order to be accessible from the .view.skyframe package.
 
@@ -221,6 +227,14 @@
                   Optional.empty());
       Verify.verifyNotNull(rule);
       Artifact artifact = rule.getArtifactByOutputLabel(outputFile.getLabel());
+
+      if (rule.get(DeniedImplicitOutputMarkerProvider.PROVIDER) != null) {
+        analysisEnvironment
+            .getEventHandler()
+            .handle(Event.error(String.format(CC_LIB_IMPLICIT_OUTPUTS_ERROR, rule.getLabel())));
+        return null;
+      }
+
       return new OutputFileConfiguredTarget(targetContext, outputFile, rule, artifact);
     } else if (target instanceof InputFile) {
       InputFile inputFile = (InputFile) target;
diff --git a/src/main/java/com/google/devtools/build/lib/rules/cpp/BUILD b/src/main/java/com/google/devtools/build/lib/rules/cpp/BUILD
index f52f810..557aa12 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/cpp/BUILD
+++ b/src/main/java/com/google/devtools/build/lib/rules/cpp/BUILD
@@ -28,10 +28,13 @@
             "*.java",
             "transitions/*.java",
         ],
-        exclude = INTERFACE_SOURCES,
+        exclude = INTERFACE_SOURCES + [
+            "DeniedImplicitOutputMarkerProvider.java",
+        ],
     ),
     deps = [
         ":cpp_interface",
+        ":denied_implicit_outputs_marker_provider",
         "//src/main/java/com/google/devtools/build/lib:syntax",
         "//src/main/java/com/google/devtools/build/lib/actions",
         "//src/main/java/com/google/devtools/build/lib/actions:execution_requirements",
@@ -140,3 +143,13 @@
         "//third_party:jsr305",
     ],
 )
+
+java_library(
+    name = "denied_implicit_outputs_marker_provider",
+    srcs = ["DeniedImplicitOutputMarkerProvider.java"],
+    deps = [
+        "//src/main/java/com/google/devtools/build/lib/concurrent",
+        "//src/main/java/com/google/devtools/build/lib/packages",
+        "//third_party:jsr305",
+    ],
+)
diff --git a/src/main/java/com/google/devtools/build/lib/rules/cpp/CcLibrary.java b/src/main/java/com/google/devtools/build/lib/rules/cpp/CcLibrary.java
index 33862d2..35ed3f8 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/cpp/CcLibrary.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/cpp/CcLibrary.java
@@ -22,6 +22,7 @@
 import com.google.devtools.build.lib.actions.Artifact;
 import com.google.devtools.build.lib.actions.FailAction;
 import com.google.devtools.build.lib.actions.MutableActionGraph.ActionConflictException;
+import com.google.devtools.build.lib.analysis.Allowlist;
 import com.google.devtools.build.lib.analysis.AnalysisUtils;
 import com.google.devtools.build.lib.analysis.ConfiguredTarget;
 import com.google.devtools.build.lib.analysis.FileProvider;
@@ -77,6 +78,8 @@
   /** A string constant for the name of Windows def file output group. */
   public static final String DEF_FILE_OUTPUT_GROUP_NAME = "def_file";
 
+  public static final String IMPLICIT_OUTPUTS_ALLOWLIST = "allowed_cc_lib_implicit_outputs";
+
   private final CppSemantics semantics;
 
   protected CcLibrary(CppSemantics semantics) {
@@ -487,6 +490,16 @@
                 ruleContext,
                 ruleContext.getFragment(CppConfiguration.class),
                 ccCompilationOutputs));
+
+    maybeAddDeniedImplicitOutputsProvider(targetBuilder, ruleContext);
+  }
+
+  private static void maybeAddDeniedImplicitOutputsProvider(
+      RuleConfiguredTargetBuilder targetBuilder, RuleContext ruleContext) {
+    if (ruleContext.getRule().getImplicitOutputsFunction() != ImplicitOutputsFunction.NONE
+        && !Allowlist.isAvailable(ruleContext, IMPLICIT_OUTPUTS_ALLOWLIST)) {
+      targetBuilder.addNativeDeclaredProvider(new DeniedImplicitOutputMarkerProvider());
+    }
   }
 
   private static NestedSet<Artifact> collectHiddenTopLevelArtifacts(
diff --git a/src/main/java/com/google/devtools/build/lib/rules/cpp/DeniedImplicitOutputMarkerProvider.java b/src/main/java/com/google/devtools/build/lib/rules/cpp/DeniedImplicitOutputMarkerProvider.java
new file mode 100644
index 0000000..f15495b
--- /dev/null
+++ b/src/main/java/com/google/devtools/build/lib/rules/cpp/DeniedImplicitOutputMarkerProvider.java
@@ -0,0 +1,31 @@
+// Copyright 2014 The Bazel Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//    http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.devtools.build.lib.rules.cpp;
+
+import com.google.devtools.build.lib.concurrent.ThreadSafety.Immutable;
+import com.google.devtools.build.lib.packages.NativeInfo;
+import com.google.devtools.build.lib.packages.NativeProvider;
+
+/** TODO(plf): Remove once implicit outputs are removed from cc_library */
+@Immutable
+public class DeniedImplicitOutputMarkerProvider extends NativeInfo {
+  public static final NativeProvider<DeniedImplicitOutputMarkerProvider> PROVIDER =
+      new NativeProvider<DeniedImplicitOutputMarkerProvider>(
+          DeniedImplicitOutputMarkerProvider.class, "DeniedImplicitOutputMarkerProvider") {};
+
+  public DeniedImplicitOutputMarkerProvider() {
+    super(PROVIDER);
+  }
+}
diff --git a/src/test/java/com/google/devtools/build/lib/rules/cpp/CcLibraryConfiguredTargetTest.java b/src/test/java/com/google/devtools/build/lib/rules/cpp/CcLibraryConfiguredTargetTest.java
index db26624..6a1ab64 100644
--- a/src/test/java/com/google/devtools/build/lib/rules/cpp/CcLibraryConfiguredTargetTest.java
+++ b/src/test/java/com/google/devtools/build/lib/rules/cpp/CcLibraryConfiguredTargetTest.java
@@ -1633,4 +1633,56 @@
         ")");
     checkError("//foo", "Trying to link twice");
   }
+
+  @Test
+  public void testImplicitOutputsWhitelistNotOnWhitelist() throws Exception {
+    if (analysisMock.isThisBazel()) {
+      return;
+    }
+    scratch.overwriteFile(
+        "tools/build_defs/cc/whitelists/cc_lib_implicit_outputs/BUILD",
+        "package_group(",
+        "    name = 'allowed_cc_lib_implicit_outputs',",
+        "    packages = [])");
+
+    scratch.file(
+        "foo/BUILD",
+        "filegroup(",
+        "    name = 'denied',",
+        "    srcs = [':libdenied_cc_lib.a'],",
+        ")",
+        "cc_library(",
+        "    name = 'denied_cc_lib',",
+        "    srcs = ['denied_cc_lib.cc'],",
+        ")");
+    checkError(
+        "//foo:denied",
+        "Using implicit outputs from cc_library (//foo:denied_cc_lib) is "
+            + "forbidden. Use the rule cc_implicit_output as an alternative.");
+  }
+
+  @Test
+  public void testImplicitOutputsWhitelistOnWhitelist() throws Exception {
+    if (analysisMock.isThisBazel()) {
+      return;
+    }
+    scratch.overwriteFile(
+        "tools/build_defs/cc/whitelists/cc_lib_implicit_outputs/BUILD",
+        "package_group(",
+        "    name = 'allowed_cc_lib_implicit_outputs',",
+        "    packages = ['//bar'])");
+
+    scratch.file(
+        "bar/BUILD",
+        "filegroup(",
+        "    name = 'allowed',",
+        "    srcs = [':liballowed_cc_lib.a'],",
+        ")",
+        "cc_library(",
+        "    name = 'allowed_cc_lib',",
+        "    srcs = ['allowed_cc_lib.cc'],",
+        ")");
+    getConfiguredTarget("//bar:allowed");
+    assertNoEvents();
+  }
 }