Add incompatible flag to forbid loading the native Protobuf rules

RELNOTES: --incompatible_load_proto_rules_from_bzl was added to forbid loading the native proto rules directly. See more on tracking issue #8922

Closes #8923.

PiperOrigin-RevId: 259294885
diff --git a/src/main/java/com/google/devtools/build/lib/rules/proto/BazelProtoLibrary.java b/src/main/java/com/google/devtools/build/lib/rules/proto/BazelProtoLibrary.java
index 158e075..4c1d981 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/proto/BazelProtoLibrary.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/proto/BazelProtoLibrary.java
@@ -30,7 +30,9 @@
 public class BazelProtoLibrary implements RuleConfiguredTargetFactory {
 
   @Override
-  public ConfiguredTarget create(RuleContext ruleContext) throws ActionConflictException {
+  public ConfiguredTarget create(RuleContext ruleContext)
+      throws ActionConflictException, RuleErrorException {
+    ProtoCommon.checkRuleHasValidMigrationTag(ruleContext);
     ProtoInfo protoInfo =
         ProtoCommon.createProtoInfo(
             ruleContext,
diff --git a/src/main/java/com/google/devtools/build/lib/rules/proto/BazelProtoLibraryRule.java b/src/main/java/com/google/devtools/build/lib/rules/proto/BazelProtoLibraryRule.java
index 837056f..2adf628 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/proto/BazelProtoLibraryRule.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/proto/BazelProtoLibraryRule.java
@@ -122,6 +122,10 @@
 
 /*<!-- #BLAZE_RULE (NAME = proto_library, TYPE = LIBRARY, FAMILY = Protocol Buffer) -->
 
+<p>Deprecated. Please use <a href="https://github.com/bazelbuild/rules_proto">
+   https://github.com/bazelbuild/rules_proto</a> instead.
+</p>
+
 <p>Use <code>proto_library</code> to define libraries of protocol buffers
    which may be used from multiple languages. A <code>proto_library</code> may be listed
    in the <code>deps</code> clause of supported rules, such as <code>java_proto_library</code>.
diff --git a/src/main/java/com/google/devtools/build/lib/rules/proto/ProtoCommon.java b/src/main/java/com/google/devtools/build/lib/rules/proto/ProtoCommon.java
index 33d4fc7..d495e8b 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/proto/ProtoCommon.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/proto/ProtoCommon.java
@@ -33,6 +33,8 @@
 import com.google.devtools.build.lib.collect.nestedset.NestedSetBuilder;
 import com.google.devtools.build.lib.events.Location;
 import com.google.devtools.build.lib.packages.BuildType;
+import com.google.devtools.build.lib.packages.RuleClass.ConfiguredTargetFactory.RuleErrorException;
+import com.google.devtools.build.lib.syntax.Type;
 import com.google.devtools.build.lib.util.Pair;
 import com.google.devtools.build.lib.vfs.FileSystemUtils;
 import com.google.devtools.build.lib.vfs.PathFragment;
@@ -46,6 +48,11 @@
     throw new UnsupportedOperationException();
   }
 
+  // Keep in sync with the migration label in
+  // https://github.com/bazelbuild/rules_proto/blob/master/proto/defs.bzl.
+  private static final String PROTO_RULES_MIGRATION_LABEL =
+      "__PROTO_RULES_MIGRATION_DO_NOT_USE_WILL_BREAK__";
+
   /**
    * Gets the direct sources of a proto library. If protoSources is not empty, the value is just
    * protoSources. Otherwise, it's the combined sources of all direct dependencies of the given
@@ -460,6 +467,28 @@
     return ruleContext.getLabel().getPackageIdentifier().equals(source.getPackageIdentifier());
   }
 
+  public static void checkRuleHasValidMigrationTag(RuleContext ruleContext)
+      throws RuleErrorException {
+    if (!ruleContext.getFragment(ProtoConfiguration.class).loadProtoRulesFromBzl()) {
+      return;
+    }
+
+    if (!hasValidMigrationTag(ruleContext)) {
+      ruleContext.ruleError(
+          "The native Protobuf rules are deprecated. Please load "
+              + ruleContext.getRule().getRuleClass()
+              + " from the rules_proto repository."
+              + " See http://github.com/bazelbuild/rules_proto.");
+    }
+  }
+
+  private static boolean hasValidMigrationTag(RuleContext ruleContext) {
+    return ruleContext
+        .attributes()
+        .get("tags", Type.STRING_LIST)
+        .contains(PROTO_RULES_MIGRATION_LABEL);
+  }
+
   /**
    * Creates the {@link ProtoInfo} for the {@code proto_library} rule associated with {@code
    * ruleContext}.
diff --git a/src/main/java/com/google/devtools/build/lib/rules/proto/ProtoConfiguration.java b/src/main/java/com/google/devtools/build/lib/rules/proto/ProtoConfiguration.java
index e443051..8eac3fe 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/proto/ProtoConfiguration.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/proto/ProtoConfiguration.java
@@ -199,11 +199,26 @@
                 + "been superseded by the strip_import_prefix= and import_prefix= attributes")
     public boolean disableProtoSourceRoot;
 
+    @Option(
+        name = "incompatible_load_proto_rules_from_bzl",
+        defaultValue = "false",
+        documentationCategory = OptionDocumentationCategory.UNDOCUMENTED,
+        effectTags = {OptionEffectTag.LOADING_AND_ANALYSIS},
+        metadataTags = {
+          OptionMetadataTag.INCOMPATIBLE_CHANGE,
+          OptionMetadataTag.TRIGGERED_BY_ALL_INCOMPATIBLE_CHANGES
+        },
+        help =
+            "If enabled, direct usage of the native Protobuf rules is disabled. Please use "
+                + "the Starlark rules instead at https://github.com/bazelbuild/rules_proto")
+    public boolean loadProtoRulesFromBzl;
+
     @Override
     public FragmentOptions getHost() {
       Options host = (Options) super.getHost();
       host.disableLegacyProvider = disableLegacyProvider;
       host.disableProtoSourceRoot = disableProtoSourceRoot;
+      host.loadProtoRulesFromBzl = loadProtoRulesFromBzl;
       host.protoCompiler = protoCompiler;
       host.protocOpts = protocOpts;
       host.experimentalProtoExtraActions = experimentalProtoExtraActions;
@@ -320,4 +335,8 @@
   public boolean generatedProtosInVirtualImports() {
     return options.generatedProtosInVirtualImports;
   }
+
+  public boolean loadProtoRulesFromBzl() {
+    return options.loadProtoRulesFromBzl;
+  }
 }
diff --git a/src/main/java/com/google/devtools/build/lib/rules/proto/ProtoLangToolchain.java b/src/main/java/com/google/devtools/build/lib/rules/proto/ProtoLangToolchain.java
index fb610e4..f0803fb 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/proto/ProtoLangToolchain.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/proto/ProtoLangToolchain.java
@@ -37,6 +37,7 @@
   @Override
   public ConfiguredTarget create(RuleContext ruleContext)
       throws InterruptedException, RuleErrorException, ActionConflictException {
+    ProtoCommon.checkRuleHasValidMigrationTag(ruleContext);
     NestedSetBuilder<Artifact> blacklistedProtos = NestedSetBuilder.stableOrder();
     for (TransitiveInfoCollection protos :
         ruleContext.getPrerequisites("blacklisted_protos", TARGET)) {
diff --git a/src/main/java/com/google/devtools/build/lib/rules/proto/ProtoLangToolchainRule.java b/src/main/java/com/google/devtools/build/lib/rules/proto/ProtoLangToolchainRule.java
index 471d4cb..6827483 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/proto/ProtoLangToolchainRule.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/proto/ProtoLangToolchainRule.java
@@ -72,6 +72,7 @@
                 .allowedFileTypes()
                 .mandatoryNativeProviders(
                     ImmutableList.<Class<? extends TransitiveInfoProvider>>of(FileProvider.class)))
+        .requiresConfigurationFragments(ProtoConfiguration.class)
         .advertiseProvider(ProtoLangToolchainProvider.class)
         .removeAttribute("data")
         .removeAttribute("deps")
@@ -90,6 +91,10 @@
 
 /*<!-- #BLAZE_RULE (NAME = proto_lang_toolchain, TYPE = LIBRARY, FAMILY = Protocol Buffer) -->
 
+<p>Deprecated. Please <a href="https://github.com/bazelbuild/rules_proto">
+   https://github.com/bazelbuild/rules_proto</a> instead.
+</p>
+
 <p>
 Specifies how a LANG_proto_library rule (e.g., <code>java_proto_library</code>) should invoke the
 proto-compiler.
diff --git a/src/main/java/com/google/devtools/build/lib/skylarkbuildapi/ProtoInfoApi.java b/src/main/java/com/google/devtools/build/lib/skylarkbuildapi/ProtoInfoApi.java
index 8f57d4c..98a9aea 100644
--- a/src/main/java/com/google/devtools/build/lib/skylarkbuildapi/ProtoInfoApi.java
+++ b/src/main/java/com/google/devtools/build/lib/skylarkbuildapi/ProtoInfoApi.java
@@ -20,7 +20,16 @@
 import com.google.devtools.build.lib.skylarkinterface.SkylarkModule;
 
 /** Info object propagating information about protocol buffer sources. */
-@SkylarkModule(name = "ProtoInfo", doc = "")
+@SkylarkModule(
+    name = "ProtoInfo",
+    doc =
+        "Encapsulates information provided by <a href=\""
+            + "../../be/protocol-buffer.html#proto_library\">proto_library.</a>"
+            + "<p>"
+            + "Please consider using `load(\"@rules_proto//proto:defs.bzl\", \"ProtoInfo\")` "
+            + "to load this symbol from <a href=\"https://github.com/bazelbuild/rules_proto.\">"
+            + "rules_proto</a>"
+            + "</p>")
 public interface ProtoInfoApi<FileT extends FileApi> extends StructApi {
   /** Provider class for {@link ProtoInfoApi} objects. */
   @SkylarkModule(name = "Provider", documented = false, doc = "")
@@ -62,9 +71,8 @@
       name = "direct_descriptor_set",
       doc =
           "The <a href=\""
-              + "https://github.com/google/protobuf/search?q=%22message+FileDescriptorSet%22+path%3A%2Fsrc"
-              + "\">FileDescriptorSet</a> of the direct sources. "
-              + "If no srcs, contains an empty file.",
+              + "https://github.com/google/protobuf/search?q=%22message+FileDescriptorSet%22+path%3A%2Fsrc\">FileDescriptorSet</a>"
+              + " of the direct sources. If no srcs, contains an empty file.",
       structField = true)
   public FileT getDirectDescriptorSet();
 
@@ -72,11 +80,10 @@
       name = "transitive_descriptor_sets",
       doc =
           "A set of <a href=\""
-              + "https://github.com/google/protobuf/search?q=%22message+FileDescriptorSet%22+path%3A%2Fsrc"
-              + "\">FileDescriptorSet</a> files of all dependent proto_library rules, "
-              + "and this one's. "
-              + "This is not the same as passing --include_imports to proto-compiler. "
-              + "Will be empty if no dependencies.",
+              + "https://github.com/google/protobuf/search?q=%22message+FileDescriptorSet%22+path%3A%2Fsrc\">FileDescriptorSet</a>"
+              + " files of all dependent proto_library rules, and this one's. This is not the same"
+              + " as passing --include_imports to proto-compiler. Will be empty if no"
+              + " dependencies.",
       structField = true)
   public NestedSet<FileT> getTransitiveDescriptorSets();
 
diff --git a/src/main/java/com/google/devtools/build/lib/skylarkbuildapi/proto/ProtoModuleApi.java b/src/main/java/com/google/devtools/build/lib/skylarkbuildapi/proto/ProtoModuleApi.java
index 9f72bcf..fdfe4ae 100644
--- a/src/main/java/com/google/devtools/build/lib/skylarkbuildapi/proto/ProtoModuleApi.java
+++ b/src/main/java/com/google/devtools/build/lib/skylarkbuildapi/proto/ProtoModuleApi.java
@@ -21,5 +21,13 @@
  *
  * <p>Currently just a placeholder so that we have a {@code proto_common} symbol.
  */
-@SkylarkModule(name = "proto_common", doc = "Utilities for protocol buffers")
+@SkylarkModule(
+    name = "proto_common",
+    doc =
+        "Utilities for protocol buffers. "
+            + "<p>"
+            + "Please consider using `load(\"@rules_proto//proto:defs.bzl\", \"proto_common\")` "
+            + "to load this symbol from <a href=\"https://github.com/bazelbuild/rules_proto.\">"
+            + "rules_proto</a>"
+            + "</p>")
 public interface ProtoModuleApi {}
diff --git a/src/test/java/com/google/devtools/build/lib/rules/proto/BazelProtoLibraryTest.java b/src/test/java/com/google/devtools/build/lib/rules/proto/BazelProtoLibraryTest.java
index 803b514..550b077 100644
--- a/src/test/java/com/google/devtools/build/lib/rules/proto/BazelProtoLibraryTest.java
+++ b/src/test/java/com/google/devtools/build/lib/rules/proto/BazelProtoLibraryTest.java
@@ -947,4 +947,50 @@
   private Action getDescriptorWriteAction(String label) throws Exception {
     return getGeneratingAction(getDescriptorOutput(label));
   }
+
+  @Test
+  public void testMigrationLabel() throws Exception {
+    if (!isThisBazel()) {
+      return;
+    }
+
+    useConfiguration("--incompatible_load_proto_rules_from_bzl");
+    scratch.file(
+        "a/BUILD",
+        "proto_library(",
+        "    name = 'a',",
+        "    srcs = ['a.proto'],",
+        // Don't use |ProtoCommon.PROTO_RULES_MIGRATION_LABEL| here
+        // so we don't accidentally change it without breaking a local test.
+        "    tags = ['__PROTO_RULES_MIGRATION_DO_NOT_USE_WILL_BREAK__'],",
+        ")");
+
+    getConfiguredTarget("//a");
+  }
+
+  @Test
+  public void testMissingMigrationLabel() throws Exception {
+    if (!isThisBazel()) {
+      return;
+    }
+
+    useConfiguration("--incompatible_load_proto_rules_from_bzl");
+    scratch.file("a/BUILD", "proto_library(", "    name = 'a',", "    srcs = ['a.proto'],", ")");
+
+    reporter.removeHandler(failFastHandler);
+    getConfiguredTarget("//a");
+    assertContainsEvent("The native Protobuf rules are deprecated.");
+  }
+
+  @Test
+  public void testMigrationLabelNotRequiredWhenDisabled() throws Exception {
+    if (!isThisBazel()) {
+      return;
+    }
+
+    useConfiguration("--noincompatible_load_proto_rules_from_bzl");
+    scratch.file("a/BUILD", "proto_library(", "    name = 'a',", "    srcs = ['a.proto'],", ")");
+
+    getConfiguredTarget("//a");
+  }
 }
diff --git a/src/test/java/com/google/devtools/build/lib/rules/proto/ProtoLangToolchainTest.java b/src/test/java/com/google/devtools/build/lib/rules/proto/ProtoLangToolchainTest.java
index e11d2d6..c95972a 100644
--- a/src/test/java/com/google/devtools/build/lib/rules/proto/ProtoLangToolchainTest.java
+++ b/src/test/java/com/google/devtools/build/lib/rules/proto/ProtoLangToolchainTest.java
@@ -83,4 +83,48 @@
     assertThat(toolchain.runtime()).isNull();
     assertThat(toolchain.blacklistedProtos()).isEmpty();
   }
+
+  @Test
+  public void testMigrationLabel() throws Exception {
+    useConfiguration("--incompatible_load_proto_rules_from_bzl");
+    scratch.file(
+        "a/BUILD",
+        "proto_lang_toolchain(",
+        "    name = 'toolchain',",
+        "    command_line = 'cmd-line',",
+        // Don't use |ProtoCommon.PROTO_RULES_MIGRATION_LABEL| here
+        // so we don't accidentally change it without breaking a local test.
+        "    tags = ['__PROTO_RULES_MIGRATION_DO_NOT_USE_WILL_BREAK__'],",
+        ")");
+
+    getConfiguredTarget("//a:toolchain");
+  }
+
+  @Test
+  public void testMissingMigrationLabel() throws Exception {
+    useConfiguration("--incompatible_load_proto_rules_from_bzl");
+    scratch.file(
+        "a/BUILD",
+        "proto_lang_toolchain(",
+        "    name = 'toolchain',",
+        "    command_line = 'cmd-line',",
+        ")");
+
+    reporter.removeHandler(failFastHandler);
+    getConfiguredTarget("//a:toolchain");
+    assertContainsEvent("The native Protobuf rules are deprecated.");
+  }
+
+  @Test
+  public void testMigrationLabelNotRequiredWhenDisabled() throws Exception {
+    useConfiguration("--noincompatible_load_proto_rules_from_bzl");
+    scratch.file(
+        "a/BUILD",
+        "proto_lang_toolchain(",
+        "    name = 'toolchain',",
+        "    command_line = 'cmd-line',",
+        ")");
+
+    getConfiguredTarget("//a:toolchain");
+  }
 }