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 df90c13953a6ca..e91f37b0d1a2d6 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 @@ public void init(ConfiguredRuleClassProvider.Builder builder) { 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 e3b88d6fdc92dd..1067e484595629 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 @@ java_library( "//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 45c3e070110ef2..cb7a8c56fdada0 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 { public static final ProtoToolchainInfoProvider PROVIDER = new ProtoToolchainInfoProvider(); /** Creates a {@link ProtoToolchainInfo} from a {@link RuleContext}. */ @@ -44,17 +47,36 @@ public static ProtoToolchainInfo fromRuleContext(RuleContext ruleContext) { return null; } - return new AutoValue_ProtoToolchainInfo(PROVIDER, compiler, protoConfiguration.protocOpts()); + return create(compiler, protoConfiguration.protocOpts()); } + private static final ProtoToolchainInfo create( + FilesToRunProvider compiler, ImmutableList compilerOptions) { + Preconditions.checkNotNull(compiler); + Preconditions.checkNotNull(compilerOptions); + + return new AutoValue_ProtoToolchainInfo(PROVIDER, compiler, compilerOptions); + } + + @Override public abstract FilesToRunProvider getCompiler(); + @Override public abstract ImmutableList getCompilerOptions(); /** Provider class for {@link ProtoToolchainInfo} objects. */ - public static class ProtoToolchainInfoProvider extends BuiltinProvider { + public static class ProtoToolchainInfoProvider extends BuiltinProvider + implements ProtoToolchainInfoApi.Provider { public ProtoToolchainInfoProvider() { - super(ProtoBootstrap.PROTO_TOOLCHAIN_INFO_STARLARK_NAME, ProtoToolchainInfo.class); + super(ProtoToolchainInfoApi.NAME, ProtoToolchainInfo.class); + } + + @Override + public ProtoToolchainInfoApi 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 0a2eb50a80f1de..fca7d5aad1b63c 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 @@ public class ProtoBootstrap implements Bootstrap { /** 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> + protoToolchainInfoApi; private final Object protoCommon; private final StarlarkAspectApi protoRegistryAspect; private final ProviderApi protoRegistryProvider; public ProtoBootstrap( ProtoInfoProviderApi protoInfoApiProvider, + ProtoToolchainInfoApi.Provider> + 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 @@ public ProtoBootstrap( @Override public void addBindingsToBuilder(ImmutableMap.Builder 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 00000000000000..8a40346029ed07 --- /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 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 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> + 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 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 b04dae1c34c414..a3adb78806ba3a 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 @@ java_test( "//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 00000000000000..db30a5bc9f4b39 --- /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(); + } +}