Implement and expose proto_common.compile call.
Design doc: https://docs.google.com/document/d/1dY_jfRvnH8SjRXGIfg8av-vquyWsvIZydXJOywvaR1A/edit
PiperOrigin-RevId: 440098122
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 8aa2ed8..1fbf128 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
@@ -112,6 +112,7 @@
import com.google.devtools.build.lib.rules.proto.BazelProtoLibraryRule;
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.ProtoLangToolchainProvider;
import com.google.devtools.build.lib.rules.proto.ProtoLangToolchainRule;
import com.google.devtools.build.lib.rules.python.PyInfo;
import com.google.devtools.build.lib.rules.python.PyRuleClasses.PySymlink;
@@ -291,6 +292,8 @@
new StarlarkAspectStub(),
new ProviderStub());
builder.addStarlarkBootstrap(bootstrap);
+ builder.addStarlarkBuiltinsInternal(
+ "ProtoLangToolchainInfo", ProtoLangToolchainProvider.PROVIDER);
}
@Override
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..3cc888d 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
@@ -34,6 +34,8 @@
/** The name of the proto namespace in Starlark. */
public static final String PROTO_COMMON_NAME = "proto_common";
+ public static final String PROTO_COMMON_SECOND_NAME = "proto_common_do_not_use";
+
private final ProtoInfoProviderApi protoInfoApiProvider;
private final Object protoCommon;
private final StarlarkAspectApi protoRegistryAspect;
@@ -54,6 +56,7 @@
public void addBindingsToBuilder(ImmutableMap.Builder<String, Object> builder) {
builder.put(PROTO_INFO_STARLARK_NAME, protoInfoApiProvider);
builder.put(PROTO_COMMON_NAME, protoCommon);
+ builder.put(PROTO_COMMON_SECOND_NAME, protoCommon);
builder.put(
"ProtoRegistryAspect",
FlagGuardedValue.onlyWhenExperimentalFlagIsTrue(
diff --git a/src/main/starlark/builtins_bzl/common/exports.bzl b/src/main/starlark/builtins_bzl/common/exports.bzl
index e949e90..0ce3ac4 100755
--- a/src/main/starlark/builtins_bzl/common/exports.bzl
+++ b/src/main/starlark/builtins_bzl/common/exports.bzl
@@ -23,7 +23,7 @@
load("@_builtins//:common/objc/apple_static_library.bzl", "apple_static_library")
load("@_builtins//:common/objc/compilation_support.bzl", "compilation_support")
load("@_builtins//:common/objc/linking_support.bzl", "linking_support")
-load("@_builtins//:common/proto/proto_common.bzl", "proto_common")
+load("@_builtins//:common/proto/proto_common.bzl", "proto_common", "proto_common_do_not_use")
load("@_builtins//:common/proto/proto_library.bzl", "proto_library")
load("@_builtins//:common/java/proto/java_lite_proto_library.bzl", "java_lite_proto_library")
load("@_builtins//:common/cc/cc_library.bzl", "cc_library")
@@ -33,6 +33,7 @@
# that builtins injection is working properly. Its built-in value is
# "original value".
"_builtins_dummy": "overridden value",
+ "proto_common_do_not_use": proto_common_do_not_use,
}
# A list of Starlarkified native rules.
diff --git a/src/main/starlark/builtins_bzl/common/proto/proto_common.bzl b/src/main/starlark/builtins_bzl/common/proto/proto_common.bzl
index 49bfb91..f34905e 100644
--- a/src/main/starlark/builtins_bzl/common/proto/proto_common.bzl
+++ b/src/main/starlark/builtins_bzl/common/proto/proto_common.bzl
@@ -88,6 +88,87 @@
def _Iimport_path_equals_fullpath(proto_source):
return "-I%s=%s" % (proto_source.import_path(), proto_source.source_file().path)
+def _compile(
+ actions,
+ proto_library_target,
+ proto_lang_toolchain_info,
+ generated_files,
+ plugin_output = None,
+ additional_args = None,
+ additional_tools = [],
+ additional_inputs = depset(),
+ resource_set = None):
+ """Creates proto compile action for compiling *.proto files to language specific sources.
+
+ Args:
+ actions: (ActionFactory) Obtained by ctx.actions, used to register the actions.
+ proto_library_target: (Target) The proto_library to generate the sources for.
+ Obtained as the `target` parameter from an aspect's implementation.
+ proto_lang_toolchain_info: (ProtoLangToolchainInfo) The proto lang toolchain info.
+ Obtained from a `proto_lang_toolchain` target or constructed ad-hoc..
+ generated_files: (list[File]) The output files generated by the proto compiler.
+ Callee needs to declare files using `ctx.actions.declare_file`.
+ See also: `proto_common.declare_generated_files`.
+ plugin_output: (File|str) The file or directory passed to the plugin.
+ Formatted with `proto_lang_toolchain.out_replacement_format_flag`
+ additional_args: (Args) Additional arguments to add to the action.
+ Accepts an ctx.actions.args() object that is added at the beginning
+ of the command line.
+ additional_tools: (list[File]) Additional tools to add to the action.
+ additional_inputs: (Depset[File]) Additional input files to add to the action.
+ resource_set:
+ (func) A callback function that is passed to the created action.
+ See `ctx.actions.run`, `resource_set` parameter for full definition of
+ the callback.
+ """
+ proto_info = proto_library_target[_builtins.toplevel.ProtoInfo]
+
+ args = actions.args()
+ args.use_param_file(param_file_arg = "@%s")
+ args.set_param_file_format("multiline")
+ tools = list(additional_tools)
+
+ if plugin_output:
+ args.add(plugin_output, format = proto_lang_toolchain_info.out_replacement_format_flag)
+ if proto_lang_toolchain_info.plugin:
+ tools.append(proto_lang_toolchain_info.plugin)
+ args.add(proto_lang_toolchain_info.plugin.executable, format = proto_lang_toolchain_info.plugin_format_flag)
+
+ args.add_all(proto_info.transitive_proto_path, map_each = _proto_path_flag)
+ # Example: `--proto_path=--proto_path=bazel-bin/target/third_party/pkg/_virtual_imports/subpkg`
+
+ args.add_all(proto_lang_toolchain_info.protoc_opts)
+
+ # Include maps
+ # For each import, include both the import as well as the import relativized against its
+ # protoSourceRoot. This ensures that protos can reference either the full path or the short
+ # path when including other protos.
+ args.add_all(proto_info.transitive_proto_sources(), map_each = _Iimport_path_equals_fullpath)
+ # Example: `-Ia.proto=bazel-bin/target/third_party/pkg/_virtual_imports/subpkg/a.proto`
+
+ args.add_all(proto_info.direct_sources)
+
+ if additional_args:
+ additional_args.use_param_file(param_file_arg = "@%s")
+ additional_args.set_param_file_format("multiline")
+
+ actions.run(
+ mnemonic = proto_lang_toolchain_info.mnemonic,
+ progress_message = proto_lang_toolchain_info.progress_message,
+ executable = proto_lang_toolchain_info.proto_compiler,
+ arguments = [additional_args, args] if additional_args else [args],
+ inputs = depset(transitive = [proto_info.transitive_sources, additional_inputs]),
+ outputs = generated_files,
+ tools = tools,
+ use_default_shell_env = True,
+ resource_set = resource_set,
+ )
+
proto_common = struct(
create_proto_compile_action = _create_proto_compile_action,
)
+
+proto_common_do_not_use = struct(
+ compile = _compile,
+ ProtoLangToolchainInfo = _builtins.internal.ProtoLangToolchainInfo,
+)
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 40cd6df..0a34bf6 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
@@ -13,6 +13,24 @@
)
java_test(
+ name = "BazelProtoCommonTest",
+ srcs = ["BazelProtoCommonTest.java"],
+ deps = [
+ "//src/main/java/com/google/devtools/build/lib/actions:localhost_capacity",
+ "//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/util:os",
+ "//src/test/java/com/google/devtools/build/lib/actions/util",
+ "//src/test/java/com/google/devtools/build/lib/analysis/util",
+ "//src/test/java/com/google/devtools/build/lib/packages:testutil",
+ "//src/test/java/com/google/devtools/build/lib/testutil:TestConstants",
+ "//third_party:junit4",
+ "//third_party:truth",
+ "@com_google_testparameterinjector//:testparameterinjector",
+ ],
+)
+
+java_test(
name = "ProtoCompileActionBuilderTest",
srcs = ["ProtoCompileActionBuilderTest.java"],
deps = [
diff --git a/src/test/java/com/google/devtools/build/lib/rules/proto/BazelProtoCommonTest.java b/src/test/java/com/google/devtools/build/lib/rules/proto/BazelProtoCommonTest.java
new file mode 100644
index 0000000..c1312f6
--- /dev/null
+++ b/src/test/java/com/google/devtools/build/lib/rules/proto/BazelProtoCommonTest.java
@@ -0,0 +1,464 @@
+// Copyright 2022 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 static com.google.devtools.build.lib.actions.util.ActionsTestUtil.prettyArtifactNames;
+
+import com.google.common.truth.Correspondence;
+import com.google.devtools.build.lib.actions.ResourceSet;
+import com.google.devtools.build.lib.analysis.ConfiguredTarget;
+import com.google.devtools.build.lib.analysis.actions.SpawnAction;
+import com.google.devtools.build.lib.analysis.util.BuildViewTestCase;
+import com.google.devtools.build.lib.packages.util.MockProtoSupport;
+import com.google.devtools.build.lib.testutil.TestConstants;
+import com.google.devtools.build.lib.util.OS;
+import com.google.testing.junit.testparameterinjector.TestParameterInjector;
+import com.google.testing.junit.testparameterinjector.TestParameters;
+import java.util.List;
+import java.util.regex.Pattern;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/** Unit test for proto_common module. */
+@RunWith(TestParameterInjector.class)
+public class BazelProtoCommonTest extends BuildViewTestCase {
+ private static final Correspondence<String, String> MATCHES_REGEX =
+ Correspondence.from((a, b) -> Pattern.matches(b, a), "matches");
+
+ @Before
+ public final void setup() throws Exception {
+ MockProtoSupport.setupWorkspace(scratch);
+ invalidatePackages();
+
+ MockProtoSupport.setup(mockToolsConfig);
+
+ scratch.file(
+ "third_party/x/BUILD",
+ "licenses(['unencumbered'])",
+ "cc_binary(name = 'plugin', srcs = ['plugin.cc'])",
+ "cc_library(name = 'runtime', srcs = ['runtime.cc'])",
+ "filegroup(name = 'descriptors', srcs = ['metadata.proto', 'descriptor.proto'])",
+ "filegroup(name = 'any', srcs = ['any.proto'])",
+ "proto_library(name = 'denied', srcs = [':descriptors', ':any'])");
+ scratch.file(
+ "foo/BUILD",
+ TestConstants.LOAD_PROTO_LANG_TOOLCHAIN,
+ "proto_lang_toolchain(",
+ " name = 'toolchain',",
+ " command_line = '--java_out=param1,param2:$(OUT)',",
+ " plugin_format_flag = '--plugin=%s',",
+ " plugin = '//third_party/x:plugin',",
+ " runtime = '//third_party/x:runtime',",
+ " blacklisted_protos = ['//third_party/x:denied'],",
+ " progress_message = 'Progress Message %{label}',",
+ " mnemonic = 'MyMnemonic',",
+ ")",
+ "proto_lang_toolchain(",
+ " name = 'toolchain_noplugin',",
+ " command_line = '--java_out=param1,param2:$(OUT)',",
+ " runtime = '//third_party/x:runtime',",
+ " blacklisted_protos = ['//third_party/x:denied'],",
+ " progress_message = 'Progress Message %{label}',",
+ " mnemonic = 'MyMnemonic',",
+ ")");
+
+ scratch.file(
+ "foo/generate.bzl",
+ "def _resource_set_callback(os, inputs_size):",
+ " return {'memory': 25 + 0.15 * inputs_size, 'cpu': 1}",
+ "def _impl(ctx):",
+ " outfile = ctx.actions.declare_file('out')",
+ " kwargs = {}",
+ " if ctx.attr.plugin_output:",
+ " kwargs['plugin_output'] = ctx.attr.plugin_output",
+ " if ctx.attr.additional_args:",
+ " additional_args = ctx.actions.args()",
+ " additional_args.add_all(ctx.attr.additional_args)",
+ " kwargs['additional_args'] = additional_args",
+ " if ctx.files.additional_tools:",
+ " kwargs['additional_tools'] = ctx.files.additional_tools",
+ " if ctx.files.additional_inputs:",
+ " kwargs['additional_inputs'] = depset(ctx.files.additional_inputs)",
+ " if ctx.attr.use_resource_set:",
+ " kwargs['resource_set'] = _resource_set_callback",
+ " proto_common_do_not_use.compile(",
+ " ctx.actions,",
+ " ctx.attr.proto_dep,",
+ " ctx.attr.toolchain[proto_common_do_not_use.ProtoLangToolchainInfo],",
+ " [outfile],",
+ " **kwargs)",
+ " return [DefaultInfo(files = depset([outfile]))]",
+ "generate_rule = rule(_impl,",
+ " attrs = {",
+ " 'proto_dep': attr.label(),",
+ " 'plugin_output': attr.string(),",
+ " 'toolchain': attr.label(default = '//foo:toolchain'),",
+ " 'additional_args': attr.string_list(),",
+ " 'additional_tools': attr.label_list(cfg = 'exec'),",
+ " 'additional_inputs': attr.label_list(allow_files = True),",
+ " 'use_resource_set': attr.bool(),",
+ " })");
+ }
+
+ /** Verifies basic usage of <code>proto_common.generate_code</code>. */
+ @Test
+ public void generateCode_basic() throws Exception {
+ scratch.file(
+ "bar/BUILD",
+ TestConstants.LOAD_PROTO_LIBRARY,
+ "load('//foo:generate.bzl', 'generate_rule')",
+ "proto_library(name = 'proto', srcs = ['A.proto'])",
+ "generate_rule(name = 'simple', proto_dep = ':proto')");
+
+ ConfiguredTarget target = getConfiguredTarget("//bar:simple");
+
+ SpawnAction spawnAction = getGeneratingSpawnAction(getBinArtifact("out", target));
+ List<String> cmdLine = spawnAction.getRemainingArguments();
+ assertThat(cmdLine)
+ .comparingElementsUsing(MATCHES_REGEX)
+ .containsExactly(
+ "--plugin=bl?azel?-out/[^/]*-exec-[^/]*/bin/third_party/x/plugin",
+ "-Ibar/A.proto=bar/A.proto",
+ "bar/A.proto")
+ .inOrder();
+ assertThat(spawnAction.getMnemonic()).isEqualTo("MyMnemonic");
+ assertThat(spawnAction.getProgressMessage()).isEqualTo("Progress Message //bar:simple");
+ }
+
+ /** Verifies usage of proto_common.generate_code with no plugin specified by toolchain. */
+ @Test
+ public void generateCode_noPlugin() throws Exception {
+ scratch.file(
+ "bar/BUILD",
+ TestConstants.LOAD_PROTO_LIBRARY,
+ "load('//foo:generate.bzl', 'generate_rule')",
+ "proto_library(name = 'proto', srcs = ['A.proto'])",
+ "generate_rule(name = 'simple', proto_dep = ':proto',",
+ " toolchain = '//foo:toolchain_noplugin')");
+
+ ConfiguredTarget target = getConfiguredTarget("//bar:simple");
+
+ List<String> cmdLine =
+ getGeneratingSpawnAction(getBinArtifact("out", target)).getRemainingArguments();
+ assertThat(cmdLine)
+ .comparingElementsUsing(MATCHES_REGEX)
+ .containsExactly("-Ibar/A.proto=bar/A.proto", "bar/A.proto")
+ .inOrder();
+ }
+
+ /**
+ * Verifies usage of <code>proto_common.generate_code</code> with <code>plugin_output</code>
+ * parameter.
+ */
+ @Test
+ public void generateCode_withPluginOutput() throws Exception {
+ scratch.file(
+ "bar/BUILD",
+ TestConstants.LOAD_PROTO_LIBRARY,
+ "load('//foo:generate.bzl', 'generate_rule')",
+ "proto_library(name = 'proto', srcs = ['A.proto'])",
+ "generate_rule(name = 'simple', proto_dep = ':proto', plugin_output = 'foo.srcjar')");
+
+ ConfiguredTarget target = getConfiguredTarget("//bar:simple");
+
+ List<String> cmdLine =
+ getGeneratingSpawnAction(getBinArtifact("out", target)).getRemainingArguments();
+ assertThat(cmdLine)
+ .comparingElementsUsing(MATCHES_REGEX)
+ .containsExactly(
+ "--java_out=param1,param2:foo.srcjar",
+ "--plugin=bl?azel?-out/[^/]*-exec-[^/]*/bin/third_party/x/plugin",
+ "-Ibar/A.proto=bar/A.proto",
+ "bar/A.proto")
+ .inOrder();
+ }
+
+ /**
+ * Verifies usage of <code>proto_common.generate_code</code> with <code>additional_args</code>
+ * parameter.
+ */
+ @Test
+ public void generateCode_additionalArgs() throws Exception {
+ scratch.file(
+ "bar/BUILD",
+ TestConstants.LOAD_PROTO_LIBRARY,
+ "load('//foo:generate.bzl', 'generate_rule')",
+ "proto_library(name = 'proto', srcs = ['A.proto'])",
+ "generate_rule(name = 'simple', proto_dep = ':proto', additional_args = ['--a', '--b'])");
+
+ ConfiguredTarget target = getConfiguredTarget("//bar:simple");
+
+ List<String> cmdLine =
+ getGeneratingSpawnAction(getBinArtifact("out", target)).getRemainingArguments();
+ assertThat(cmdLine)
+ .comparingElementsUsing(MATCHES_REGEX)
+ .containsExactly(
+ "--a",
+ "--b",
+ "--plugin=bl?azel?-out/[^/]*-exec-[^/]*/bin/third_party/x/plugin",
+ "-Ibar/A.proto=bar/A.proto",
+ "bar/A.proto")
+ .inOrder();
+ }
+
+ /**
+ * Verifies usage of <code>proto_common.generate_code</code> with <code>additional_tools</code>
+ * parameter.
+ */
+ @Test
+ public void generateCode_additionalTools() throws Exception {
+ scratch.file(
+ "bar/BUILD",
+ TestConstants.LOAD_PROTO_LIBRARY,
+ "load('//foo:generate.bzl', 'generate_rule')",
+ "proto_library(name = 'proto', srcs = ['A.proto'])",
+ "cc_binary(name = 'tool1', srcs = ['tool1.cc'])",
+ "cc_binary(name = 'tool2', srcs = ['tool2.cc'])",
+ "generate_rule(name = 'simple', proto_dep = ':proto',",
+ " additional_tools = [':tool1', ':tool2'])");
+
+ ConfiguredTarget target = getConfiguredTarget("//bar:simple");
+
+ SpawnAction spawnAction = getGeneratingSpawnAction(getBinArtifact("out", target));
+ assertThat(prettyArtifactNames(spawnAction.getTools()))
+ .containsAtLeast("bar/tool1", "bar/tool2", "third_party/x/plugin");
+ }
+
+ /**
+ * Verifies usage of <code>proto_common.generate_code</code> with <code>additional_tools</code>
+ * parameter and no plugin on the toolchain.
+ */
+ @Test
+ public void generateCode_additionalToolsNoPlugin() throws Exception {
+ scratch.file(
+ "bar/BUILD",
+ TestConstants.LOAD_PROTO_LIBRARY,
+ "load('//foo:generate.bzl', 'generate_rule')",
+ "proto_library(name = 'proto', srcs = ['A.proto'])",
+ "cc_binary(name = 'tool1', srcs = ['tool1.cc'])",
+ "cc_binary(name = 'tool2', srcs = ['tool2.cc'])",
+ "generate_rule(name = 'simple',",
+ " proto_dep = ':proto',",
+ " additional_tools = [':tool1', ':tool2'],",
+ " toolchain = '//foo:toolchain_noplugin',",
+ ")");
+
+ ConfiguredTarget target = getConfiguredTarget("//bar:simple");
+
+ SpawnAction spawnAction = getGeneratingSpawnAction(getBinArtifact("out", target));
+ assertThat(prettyArtifactNames(spawnAction.getTools()))
+ .containsAtLeast("bar/tool1", "bar/tool2");
+ }
+
+ /**
+ * Verifies usage of <code>proto_common.generate_code</code> with <code>additional_inputs</code>
+ * parameter.
+ */
+ @Test
+ public void generateCode_additionalInputs() throws Exception {
+ scratch.file(
+ "bar/BUILD",
+ TestConstants.LOAD_PROTO_LIBRARY,
+ "load('//foo:generate.bzl', 'generate_rule')",
+ "proto_library(name = 'proto', srcs = ['A.proto'])",
+ "generate_rule(name = 'simple', proto_dep = ':proto',",
+ " additional_inputs = [':input1.txt', ':input2.txt'])");
+
+ ConfiguredTarget target = getConfiguredTarget("//bar:simple");
+
+ SpawnAction spawnAction = getGeneratingSpawnAction(getBinArtifact("out", target));
+ assertThat(prettyArtifactNames(spawnAction.getInputs()))
+ .containsAtLeast("bar/input1.txt", "bar/input2.txt");
+ }
+
+ /**
+ * Verifies usage of <code>proto_common.generate_code</code> with <code>resource_set</code>
+ * parameter.
+ */
+ @Test
+ public void generateCode_resourceSet() throws Exception {
+ scratch.file(
+ "bar/BUILD",
+ TestConstants.LOAD_PROTO_LIBRARY,
+ "load('//foo:generate.bzl', 'generate_rule')",
+ "proto_library(name = 'proto', srcs = ['A.proto'])",
+ "generate_rule(name = 'simple', proto_dep = ':proto', use_resource_set = True)");
+
+ ConfiguredTarget target = getConfiguredTarget("//bar:simple");
+
+ SpawnAction spawnAction = getGeneratingSpawnAction(getBinArtifact("out", target));
+ assertThat(spawnAction.getResourceSetOrBuilder().buildResourceSet(OS.DARWIN, 0))
+ .isEqualTo(ResourceSet.createWithRamCpu(25, 1));
+ assertThat(spawnAction.getResourceSetOrBuilder().buildResourceSet(OS.LINUX, 2))
+ .isEqualTo(ResourceSet.createWithRamCpu(25.3, 1));
+ }
+
+ /** Verifies <code>--protocopts</code> are passed to command line. */
+ @Test
+ public void generateCode_protocOpts() throws Exception {
+ useConfiguration("--protocopt=--foo", "--protocopt=--bar");
+ scratch.file(
+ "bar/BUILD",
+ TestConstants.LOAD_PROTO_LIBRARY,
+ "load('//foo:generate.bzl', 'generate_rule')",
+ "proto_library(name = 'proto', srcs = ['A.proto'])",
+ "generate_rule(name = 'simple', proto_dep = ':proto')");
+
+ ConfiguredTarget target = getConfiguredTarget("//bar:simple");
+
+ SpawnAction spawnAction = getGeneratingSpawnAction(getBinArtifact("out", target));
+ List<String> cmdLine = spawnAction.getRemainingArguments();
+ assertThat(cmdLine)
+ .comparingElementsUsing(MATCHES_REGEX)
+ .containsExactly(
+ "--plugin=bl?azel?-out/[^/]*-exec-[^/]*/bin/third_party/x/plugin",
+ "--foo",
+ "--bar",
+ "-Ibar/A.proto=bar/A.proto",
+ "bar/A.proto")
+ .inOrder();
+ }
+
+ /**
+ * Verifies <code>proto_common.generate_code</code> correctly handles direct generated <code>
+ * .proto</code> files.
+ */
+ @Test
+ public void generateCode_directGeneratedProtos() throws Exception {
+ useConfiguration("--noincompatible_generated_protos_in_virtual_imports");
+ scratch.file(
+ "bar/BUILD",
+ TestConstants.LOAD_PROTO_LIBRARY,
+ "load('//foo:generate.bzl', 'generate_rule')",
+ "genrule(name = 'generate', srcs = ['A.txt'], cmd = '', outs = ['G.proto'])",
+ "proto_library(name = 'proto', srcs = ['A.proto', 'G.proto'])",
+ "generate_rule(name = 'simple', proto_dep = ':proto')");
+
+ ConfiguredTarget target = getConfiguredTarget("//bar:simple");
+
+ SpawnAction spawnAction = getGeneratingSpawnAction(getBinArtifact("out", target));
+ List<String> cmdLine = spawnAction.getRemainingArguments();
+ assertThat(cmdLine)
+ .comparingElementsUsing(MATCHES_REGEX)
+ .containsExactly(
+ "--plugin=bl?azel?-out/[^/]*-exec-[^/]*/bin/third_party/x/plugin",
+ "-Ibar/A.proto=bar/A.proto",
+ "-Ibar/G.proto=bl?azel?-out/k8-fastbuild/bin/bar/G.proto",
+ "bar/A.proto",
+ "bl?azel?-out/k8-fastbuild/bin/bar/G.proto")
+ .inOrder();
+ }
+
+ /**
+ * Verifies <code>proto_common.generate_code</code> correctly handles in-direct generated <code>
+ * .proto</code> files.
+ */
+ @Test
+ public void generateCode_inDirectGeneratedProtos() throws Exception {
+ useConfiguration("--noincompatible_generated_protos_in_virtual_imports");
+ scratch.file(
+ "bar/BUILD",
+ TestConstants.LOAD_PROTO_LIBRARY,
+ "load('//foo:generate.bzl', 'generate_rule')",
+ "genrule(name = 'generate', srcs = ['A.txt'], cmd = '', outs = ['G.proto'])",
+ "proto_library(name = 'generated', srcs = ['G.proto'])",
+ "proto_library(name = 'proto', srcs = ['A.proto'], deps = [':generated'])",
+ "generate_rule(name = 'simple', proto_dep = ':proto')");
+
+ ConfiguredTarget target = getConfiguredTarget("//bar:simple");
+
+ SpawnAction spawnAction = getGeneratingSpawnAction(getBinArtifact("out", target));
+ List<String> cmdLine = spawnAction.getRemainingArguments();
+ assertThat(cmdLine)
+ .comparingElementsUsing(MATCHES_REGEX)
+ .containsExactly(
+ "--plugin=bl?azel?-out/[^/]*-exec-[^/]*/bin/third_party/x/plugin",
+ "-Ibar/A.proto=bar/A.proto",
+ "-Ibar/G.proto=bl?azel?-out/k8-fastbuild/bin/bar/G.proto",
+ "bar/A.proto")
+ .inOrder();
+ }
+
+ /**
+ * Verifies <code>proto_common.generate_code</code> correctly handles external <code>proto_library
+ * </code>-es.
+ */
+ @Test
+ @TestParameters({
+ "{virtual: false, sibling: false, generated: false, expectedFlags:"
+ + " ['--proto_path=external/foo','-Ie/E.proto=external/foo/e/E.proto']}",
+ "{virtual: false, sibling: false, generated: true, expectedFlags:"
+ + " ['--proto_path=external/foo',"
+ + " '-Ie/E.proto=bl?azel?-out/k8-fastbuild/bin/external/foo/e/E.proto']}",
+ "{virtual: true, sibling: false, generated: false,expectedFlags:"
+ + " ['--proto_path=external/foo','-Ie/E.proto=external/foo/e/E.proto']}",
+ "{virtual: true, sibling: false, generated: true, expectedFlags:"
+ + " ['--proto_path=bl?azel?-out/k8-fastbuild/bin/external/foo/e/_virtual_imports/e',"
+ + " '-Ie/E.proto=bl?azel?-out/k8-fastbuild/bin/external/foo/e/_virtual_imports/e/e/E.proto']}",
+ "{virtual: true, sibling: true, generated: false,expectedFlags:"
+ + " ['--proto_path=../foo','-I../foo/e/E.proto=../foo/e/E.proto']}",
+ "{virtual: true, sibling: true, generated: true, expectedFlags:"
+ + " ['--proto_path=bl?azel?-out/foo/k8-fastbuild/bin/e/_virtual_imports/e',"
+ + " '-Ie/E.proto=bl?azel?-out/foo/k8-fastbuild/bin/e/_virtual_imports/e/e/E.proto']}",
+ "{virtual: false, sibling: true, generated: false,expectedFlags:"
+ + " ['--proto_path=../foo','-I../foo/e/E.proto=../foo/e/E.proto']}",
+ "{virtual: false, sibling: true, generated: true, expectedFlags:"
+ + " ['--proto_path=../foo','-Ie/E.proto=bl?azel?-out/foo/k8-fastbuild/bin/e/E.proto']}",
+ })
+ public void generateCode_externalProtoLibrary(
+ boolean virtual, boolean sibling, boolean generated, List<String> expectedFlags)
+ throws Exception {
+ if (virtual) {
+ useConfiguration("--incompatible_generated_protos_in_virtual_imports");
+ } else {
+ useConfiguration("--noincompatible_generated_protos_in_virtual_imports");
+ }
+ if (sibling) {
+ setBuildLanguageOptions("--experimental_sibling_repository_layout");
+ }
+ scratch.appendFile("WORKSPACE", "local_repository(name = 'foo', path = '/foo')");
+ invalidatePackages();
+ scratch.file("/foo/WORKSPACE");
+ scratch.file(
+ "/foo/e/BUILD",
+ TestConstants.LOAD_PROTO_LIBRARY,
+ "proto_library(name='e', srcs=['E.proto'])",
+ generated
+ ? "genrule(name = 'generate', srcs = ['A.txt'], cmd = '', outs = ['E.proto'])"
+ : "");
+ scratch.file(
+ "bar/BUILD",
+ TestConstants.LOAD_PROTO_LIBRARY,
+ "load('//foo:generate.bzl', 'generate_rule')",
+ "proto_library(name = 'proto', srcs = ['A.proto'], deps = ['@foo//e:e'])",
+ "generate_rule(name = 'simple', proto_dep = ':proto')");
+
+ ConfiguredTarget target = getConfiguredTarget("//bar:simple");
+
+ SpawnAction spawnAction = getGeneratingSpawnAction(getBinArtifact("out", target));
+ List<String> cmdLine = spawnAction.getRemainingArguments();
+ assertThat(cmdLine)
+ .comparingElementsUsing(MATCHES_REGEX)
+ .containsExactly(
+ "--plugin=bl?azel?-out/[^/]*-exec-[^/]*/bin/third_party/x/plugin",
+ expectedFlags.get(0),
+ "-Ibar/A.proto=bar/A.proto",
+ expectedFlags.get(1),
+ "bar/A.proto")
+ .inOrder();
+ }
+}