Expose ProtoToolchainInfo to Starlark

Progress on https://docs.google.com/document/d/1go3UMwm2nM8JHhI3MZrtyoFEy3BYg5omGqQmOwcjmNE/edit

Closes #13732.

PiperOrigin-RevId: 387087817
diff --git a/src/main/java/com/google/devtools/build/lib/bazel/rules/BazelRuleClassProvider.java b/src/main/java/com/google/devtools/build/lib/bazel/rules/BazelRuleClassProvider.java
index df90c13..e91f37b 100644
--- a/src/main/java/com/google/devtools/build/lib/bazel/rules/BazelRuleClassProvider.java
+++ b/src/main/java/com/google/devtools/build/lib/bazel/rules/BazelRuleClassProvider.java
@@ -108,6 +108,7 @@
 import com.google.devtools.build.lib.rules.proto.ProtoConfiguration;
 import com.google.devtools.build.lib.rules.proto.ProtoInfo;
 import com.google.devtools.build.lib.rules.proto.ProtoLangToolchainRule;
+import com.google.devtools.build.lib.rules.proto.ProtoToolchainInfo;
 import com.google.devtools.build.lib.rules.python.PyInfo;
 import com.google.devtools.build.lib.rules.python.PyRuleClasses.PySymlink;
 import com.google.devtools.build.lib.rules.python.PyRuntimeInfo;
@@ -295,6 +296,7 @@
           ProtoBootstrap bootstrap =
               new ProtoBootstrap(
                   ProtoInfo.PROVIDER,
+                  ProtoToolchainInfo.PROVIDER,
                   BazelProtoCommon.INSTANCE,
                   new StarlarkAspectStub(),
                   new ProviderStub());
diff --git a/src/main/java/com/google/devtools/build/lib/rules/proto/BUILD b/src/main/java/com/google/devtools/build/lib/rules/proto/BUILD
index e3b88d6..1067e48 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/proto/BUILD
+++ b/src/main/java/com/google/devtools/build/lib/rules/proto/BUILD
@@ -51,6 +51,7 @@
         "//src/main/java/com/google/devtools/build/lib/vfs",
         "//src/main/java/com/google/devtools/build/lib/vfs:pathfragment",
         "//src/main/java/com/google/devtools/common/options",
+        "//src/main/java/net/starlark/java/eval",
         "//third_party:auto_value",
         "//third_party:guava",
         "//third_party:jsr305",
diff --git a/src/main/java/com/google/devtools/build/lib/rules/proto/ProtoToolchainInfo.java b/src/main/java/com/google/devtools/build/lib/rules/proto/ProtoToolchainInfo.java
index 45c3e07..cb7a8c5 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/proto/ProtoToolchainInfo.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/proto/ProtoToolchainInfo.java
@@ -21,12 +21,15 @@
 import com.google.devtools.build.lib.analysis.RuleContext;
 import com.google.devtools.build.lib.packages.BuiltinProvider;
 import com.google.devtools.build.lib.packages.NativeInfo;
-import com.google.devtools.build.lib.starlarkbuildapi.proto.ProtoBootstrap;
+import com.google.devtools.build.lib.starlarkbuildapi.proto.ProtoToolchainInfoApi;
 import javax.annotation.Nullable;
+import net.starlark.java.eval.EvalException;
+import net.starlark.java.eval.Sequence;
 
 /** Toolchain for {@code proto_*} rules. */
 @AutoValue
-public abstract class ProtoToolchainInfo extends NativeInfo {
+public abstract class ProtoToolchainInfo extends NativeInfo
+    implements ProtoToolchainInfoApi<FilesToRunProvider> {
   public static final ProtoToolchainInfoProvider PROVIDER = new ProtoToolchainInfoProvider();
 
   /** Creates a {@link ProtoToolchainInfo} from a {@link RuleContext}. */
@@ -44,17 +47,36 @@
       return null;
     }
 
-    return new AutoValue_ProtoToolchainInfo(PROVIDER, compiler, protoConfiguration.protocOpts());
+    return create(compiler, protoConfiguration.protocOpts());
   }
 
+  private static final ProtoToolchainInfo create(
+      FilesToRunProvider compiler, ImmutableList<String> compilerOptions) {
+    Preconditions.checkNotNull(compiler);
+    Preconditions.checkNotNull(compilerOptions);
+
+    return new AutoValue_ProtoToolchainInfo(PROVIDER, compiler, compilerOptions);
+  }
+
+  @Override
   public abstract FilesToRunProvider getCompiler();
 
+  @Override
   public abstract ImmutableList<String> getCompilerOptions();
 
   /** Provider class for {@link ProtoToolchainInfo} objects. */
-  public static class ProtoToolchainInfoProvider extends BuiltinProvider<ProtoToolchainInfo> {
+  public static class ProtoToolchainInfoProvider extends BuiltinProvider<ProtoToolchainInfo>
+      implements ProtoToolchainInfoApi.Provider<FilesToRunProvider> {
     public ProtoToolchainInfoProvider() {
-      super(ProtoBootstrap.PROTO_TOOLCHAIN_INFO_STARLARK_NAME, ProtoToolchainInfo.class);
+      super(ProtoToolchainInfoApi.NAME, ProtoToolchainInfo.class);
+    }
+
+    @Override
+    public ProtoToolchainInfoApi<FilesToRunProvider> create(
+        FilesToRunProvider protoc, Sequence<?> protocOptions) throws EvalException {
+      return ProtoToolchainInfo.create(
+          protoc,
+          Sequence.cast(protocOptions, String.class, "compiler_options").getImmutableList());
     }
   }
 }
diff --git a/src/main/java/com/google/devtools/build/lib/starlarkbuildapi/proto/ProtoBootstrap.java b/src/main/java/com/google/devtools/build/lib/starlarkbuildapi/proto/ProtoBootstrap.java
index 0a2eb50..fca7d5a 100644
--- a/src/main/java/com/google/devtools/build/lib/starlarkbuildapi/proto/ProtoBootstrap.java
+++ b/src/main/java/com/google/devtools/build/lib/starlarkbuildapi/proto/ProtoBootstrap.java
@@ -16,6 +16,8 @@
 
 import com.google.common.collect.ImmutableMap;
 import com.google.devtools.build.lib.packages.semantics.BuildLanguageOptions;
+import com.google.devtools.build.lib.starlarkbuildapi.FileApi;
+import com.google.devtools.build.lib.starlarkbuildapi.FilesToRunProviderApi;
 import com.google.devtools.build.lib.starlarkbuildapi.ProtoInfoApi.ProtoInfoProviderApi;
 import com.google.devtools.build.lib.starlarkbuildapi.StarlarkAspectApi;
 import com.google.devtools.build.lib.starlarkbuildapi.core.Bootstrap;
@@ -28,23 +30,25 @@
   /** The name of the proto info provider in Starlark. */
   public static final String PROTO_INFO_STARLARK_NAME = "ProtoInfo";
 
-  /** The name of the proto toolchain info provider in Starlark. */
-  public static final String PROTO_TOOLCHAIN_INFO_STARLARK_NAME = "ProtoToolchainInfo";
-
   /** The name of the proto namespace in Starlark. */
   public static final String PROTO_COMMON_NAME = "proto_common";
 
   private final ProtoInfoProviderApi protoInfoApiProvider;
+  private final ProtoToolchainInfoApi.Provider<? extends FilesToRunProviderApi<? extends FileApi>>
+      protoToolchainInfoApi;
   private final Object protoCommon;
   private final StarlarkAspectApi protoRegistryAspect;
   private final ProviderApi protoRegistryProvider;
 
   public ProtoBootstrap(
       ProtoInfoProviderApi protoInfoApiProvider,
+      ProtoToolchainInfoApi.Provider<? extends FilesToRunProviderApi<? extends FileApi>>
+          protoToolchainInfoApi,
       Object protoCommon,
       StarlarkAspectApi protoRegistryAspect,
       ProviderApi protoRegistryProvider) {
     this.protoInfoApiProvider = protoInfoApiProvider;
+    this.protoToolchainInfoApi = protoToolchainInfoApi;
     this.protoCommon = protoCommon;
     this.protoRegistryAspect = protoRegistryAspect;
     this.protoRegistryProvider = protoRegistryProvider;
@@ -53,6 +57,7 @@
   @Override
   public void addBindingsToBuilder(ImmutableMap.Builder<String, Object> builder) {
     builder.put(PROTO_INFO_STARLARK_NAME, protoInfoApiProvider);
+    builder.put(ProtoToolchainInfoApi.NAME, protoToolchainInfoApi);
     builder.put(PROTO_COMMON_NAME, protoCommon);
     builder.put(
         "ProtoRegistryAspect",
diff --git a/src/main/java/com/google/devtools/build/lib/starlarkbuildapi/proto/ProtoToolchainInfoApi.java b/src/main/java/com/google/devtools/build/lib/starlarkbuildapi/proto/ProtoToolchainInfoApi.java
new file mode 100644
index 0000000..8a40346
--- /dev/null
+++ b/src/main/java/com/google/devtools/build/lib/starlarkbuildapi/proto/ProtoToolchainInfoApi.java
@@ -0,0 +1,84 @@
+// Copyright 2021 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.starlarkbuildapi.proto;
+
+import com.google.common.collect.ImmutableList;
+import com.google.devtools.build.docgen.annot.DocCategory;
+import com.google.devtools.build.docgen.annot.StarlarkConstructor;
+import com.google.devtools.build.lib.starlarkbuildapi.FileApi;
+import com.google.devtools.build.lib.starlarkbuildapi.FilesToRunProviderApi;
+import com.google.devtools.build.lib.starlarkbuildapi.core.ProviderApi;
+import com.google.devtools.build.lib.starlarkbuildapi.core.StructApi;
+import net.starlark.java.annot.Param;
+import net.starlark.java.annot.ParamType;
+import net.starlark.java.annot.StarlarkBuiltin;
+import net.starlark.java.annot.StarlarkMethod;
+import net.starlark.java.eval.EvalException;
+import net.starlark.java.eval.Sequence;
+
+/** Information about the {@code proto} toolchain. */
+@StarlarkBuiltin(
+    name = ProtoToolchainInfoApi.NAME,
+    category = DocCategory.PROVIDER,
+    doc = "Information about the `proto` toolchain.")
+public interface ProtoToolchainInfoApi<
+        FilesToRunProviderApiT extends FilesToRunProviderApi<? extends FileApi>>
+    extends StructApi {
+  /** The name of the provider in Starlark. */
+  String NAME = "ProtoToolchainInfo";
+
+  @StarlarkMethod(name = "compiler", doc = "The proto compiler to use.", structField = true)
+  FilesToRunProviderApiT getCompiler();
+
+  @StarlarkMethod(
+      name = "compiler_options",
+      doc = "Additional options to pass to `protoc`.",
+      structField = true)
+  ImmutableList<String> getCompilerOptions();
+
+  /** The provider implementing this can construct {@code ProtoToolchainInfo} objects. */
+  @StarlarkBuiltin(
+      name = "Provider",
+      doc = "",
+      // This object is documented via the ProtoToolchainInfo documentation and the docuemntation of
+      // its
+      // callable function.
+      documented = false)
+  interface Provider<FilesToRunProviderApiT extends FilesToRunProviderApi<? extends FileApi>>
+      extends ProviderApi {
+    @StarlarkMethod(
+        name = NAME,
+        doc = "The `ProtoToolchainInfo` constructor.",
+        parameters = {
+          @Param(
+              name = "compiler",
+              doc = "The proto compiler.",
+              positional = false,
+              named = true,
+              allowedTypes = {@ParamType(type = FilesToRunProviderApi.class)}),
+          @Param(
+              name = "compiler_options",
+              doc = "The proto compiler.",
+              positional = false,
+              named = true,
+              defaultValue = "[]",
+              allowedTypes = {@ParamType(type = Sequence.class, generic1 = String.class)})
+        },
+        selfCall = true)
+    @StarlarkConstructor
+    ProtoToolchainInfoApi<FilesToRunProviderApiT> create(
+        FilesToRunProviderApiT protoc, Sequence<?> protocOptions) throws EvalException;
+  }
+}
diff --git a/src/test/java/com/google/devtools/build/lib/rules/proto/BUILD b/src/test/java/com/google/devtools/build/lib/rules/proto/BUILD
index b04dae1..a3adb78 100644
--- a/src/test/java/com/google/devtools/build/lib/rules/proto/BUILD
+++ b/src/test/java/com/google/devtools/build/lib/rules/proto/BUILD
@@ -94,3 +94,18 @@
         "//third_party:truth",
     ],
 )
+
+java_test(
+    name = "ProtoToolchainInfoTest",
+    srcs = [
+        "ProtoToolchainInfoTest.java",
+    ],
+    deps = [
+        "//src/main/java/com/google/devtools/build/lib/analysis:analysis_cluster",
+        "//src/main/java/com/google/devtools/build/lib/analysis:configured_target",
+        "//src/main/java/com/google/devtools/build/lib/rules/proto",
+        "//src/test/java/com/google/devtools/build/lib/analysis/util",
+        "//third_party:junit4",
+        "//third_party:truth",
+    ],
+)
diff --git a/src/test/java/com/google/devtools/build/lib/rules/proto/ProtoToolchainInfoTest.java b/src/test/java/com/google/devtools/build/lib/rules/proto/ProtoToolchainInfoTest.java
new file mode 100644
index 0000000..db30a5b
--- /dev/null
+++ b/src/test/java/com/google/devtools/build/lib/rules/proto/ProtoToolchainInfoTest.java
@@ -0,0 +1,88 @@
+// Copyright 2021 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.proto;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import com.google.devtools.build.lib.analysis.ConfiguredTarget;
+import com.google.devtools.build.lib.analysis.FilesToRunProvider;
+import com.google.devtools.build.lib.analysis.util.BuildViewTestCase;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/** Unit tests for {@code ProtoToolchainInfo}. */
+@RunWith(JUnit4.class)
+public class ProtoToolchainInfoTest extends BuildViewTestCase {
+  @Before
+  public void setUp() throws Exception {
+    scratch.file(
+        "proto/toolchain.bzl",
+        "def _impl(ctx):",
+        "  return ProtoToolchainInfo(",
+        "      compiler = ctx.attr.compiler.files_to_run,",
+        "      compiler_options = ctx.attr.compiler_options,",
+        "  )",
+        "proto_toolchain = rule(",
+        "  implementation = _impl,",
+        "  attrs = {",
+        "    'compiler': attr.label(executable=True, cfg='exec'),",
+        "    'compiler_options': attr.string_list(),",
+        "  },",
+        ")");
+    scratch.file(
+        "proto/BUILD",
+        "load(':toolchain.bzl', 'proto_toolchain')",
+        "cc_binary(",
+        "  name = 'compiler',",
+        ")");
+  }
+
+  @Test
+  public void testStarlarkApi() throws Exception {
+    scratch.file(
+        "foo/BUILD",
+        "load('//proto:toolchain.bzl', 'proto_toolchain')",
+        "proto_toolchain(",
+        "  name = 'toolchain',",
+        "  compiler = '//proto:compiler',",
+        ")");
+
+    ConfiguredTarget target = getConfiguredTarget("//foo:toolchain");
+    ProtoToolchainInfo protoToolchain = target.get(ProtoToolchainInfo.PROVIDER);
+    FilesToRunProvider compiler = protoToolchain.getCompiler();
+    assertThat(compiler.getExecutable().getOwner().toString()).isEqualTo("//proto:compiler");
+    assertThat(protoToolchain.getCompilerOptions()).isEmpty();
+  }
+
+  @Test
+  public void testStarlarkApi_withCompilerOptions() throws Exception {
+    scratch.file(
+        "foo/BUILD",
+        "load('//proto:toolchain.bzl', 'proto_toolchain')",
+        "proto_toolchain(",
+        "  name = 'toolchain',",
+        "  compiler = '//proto:compiler',",
+        "  compiler_options = ['--foo', '--bar'],",
+        ")");
+
+    ConfiguredTarget target = getConfiguredTarget("//foo:toolchain");
+    ProtoToolchainInfo protoToolchain = target.get(ProtoToolchainInfo.PROVIDER);
+    FilesToRunProvider compiler = protoToolchain.getCompiler();
+    assertThat(compiler.getExecutable().getOwner().toString()).isEqualTo("//proto:compiler");
+    assertThat(protoToolchain.getCompilerOptions()).containsExactly("--foo", "--bar").inOrder();
+  }
+}