| // 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.FileProvider; |
| import com.google.devtools.build.lib.analysis.actions.SpawnAction; |
| import com.google.devtools.build.lib.analysis.util.BuildViewTestCase; |
| import com.google.devtools.build.lib.cmdline.Label; |
| import com.google.devtools.build.lib.packages.StarlarkInfo; |
| import com.google.devtools.build.lib.packages.StarlarkProvider; |
| import com.google.devtools.build.lib.packages.StarlarkProviderIdentifier; |
| 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"); |
| |
| private static final StarlarkProviderIdentifier boolProviderId = |
| StarlarkProviderIdentifier.forKey( |
| new StarlarkProvider.Key( |
| Label.parseCanonicalUnchecked("//foo:should_generate.bzl"), "BoolProvider")); |
| |
| @Before |
| public final void setup() throws Exception { |
| MockProtoSupport.setup(mockToolsConfig); |
| invalidatePackages(); |
| |
| 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'])", |
| "filegroup(name = 'something', srcs = ['something.proto'])", |
| "proto_library(name = 'mixed', srcs = [':descriptors', ':something'])", |
| "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',", |
| " allowlist_different_package =" |
| + " '//tools/allowlists/proto_library_allowlists:lang_proto_library_allowed_in_different_package'", |
| ")", |
| "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',", |
| ")"); |
| |
| mockToolsConfig.overwrite( |
| "tools/allowlists/proto_library_allowlists/BUILD", |
| "package_group(", |
| " name='lang_proto_library_allowed_in_different_package',", |
| " packages=['//...', '-//test/...'],", |
| ")"); |
| |
| 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 == 'single':", |
| " kwargs['plugin_output'] = outfile.path", |
| " elif ctx.attr.plugin_output == 'multiple':", |
| " kwargs['plugin_output'] = ctx.genfiles_dir.path", |
| " elif ctx.attr.plugin_output == 'wrong':", |
| " kwargs['plugin_output'] = ctx.genfiles_dir.path + '///'", |
| " 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", |
| " if ctx.attr.progress_message:", |
| " kwargs['experimental_progress_message'] = ctx.attr.progress_message", |
| " proto_common_do_not_use.compile(", |
| " ctx.actions,", |
| " ctx.attr.proto_dep[ProtoInfo],", |
| " 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(),", |
| " 'progress_message': attr.string(),", |
| " })"); |
| |
| scratch.file( |
| "foo/should_generate.bzl", |
| "BoolProvider = provider()", |
| "def _impl(ctx):", |
| " result = proto_common_do_not_use.experimental_should_generate_code(", |
| " ctx.attr.proto_dep[ProtoInfo],", |
| " ctx.attr.toolchain[proto_common_do_not_use.ProtoLangToolchainInfo],", |
| " 'MyRule',", |
| " ctx.attr.proto_dep.label)", |
| " return [BoolProvider(value = result)]", |
| "should_generate_rule = rule(_impl,", |
| " attrs = {", |
| " 'proto_dep': attr.label(),", |
| " 'toolchain': attr.label(default = '//foo:toolchain'),", |
| " })"); |
| |
| scratch.file( |
| "foo/declare_generated_files.bzl", |
| "def _impl(ctx):", |
| " files = proto_common_do_not_use.declare_generated_files(", |
| " ctx.actions,", |
| " ctx.attr.proto_dep[ProtoInfo],", |
| " ctx.attr.extension,", |
| " (lambda s: s.replace('-','_').replace('.','/')) if ctx.attr.python_names else None)", |
| " for f in files:", |
| " ctx.actions.write(f, '')", |
| " return [DefaultInfo(files = depset(files))]", |
| "declare_generated_files = rule(_impl,", |
| " attrs = {", |
| " 'proto_dep': attr.label(),", |
| " 'extension': attr.string(),", |
| " 'python_names': attr.bool(default = False),", |
| " })"); |
| |
| scratch.file( |
| "foo/check_collocated.bzl", |
| "def _impl(ctx):", |
| " proto_common_do_not_use.check_collocated(", |
| " ctx.label,", |
| " ctx.attr.proto_dep[ProtoInfo],", |
| " ctx.attr.toolchain[proto_common_do_not_use.ProtoLangToolchainInfo])", |
| " return None", |
| "check_collocated = rule(_impl,", |
| " attrs = {", |
| " 'proto_dep': attr.label(),", |
| " 'toolchain': attr.label(default = '//foo:toolchain'),", |
| " })"); |
| } |
| |
| /** 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", "-I.", "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("-I.", "bar/A.proto") |
| .inOrder(); |
| } |
| |
| /** |
| * Verifies usage of <code>proto_common.generate_code</code> with <code>plugin_output</code> |
| * parameter set to file. |
| */ |
| @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 = 'single')"); |
| |
| ConfiguredTarget target = getConfiguredTarget("//bar:simple"); |
| |
| List<String> cmdLine = |
| getGeneratingSpawnAction(getBinArtifact("out", target)).getRemainingArguments(); |
| assertThat(cmdLine) |
| .comparingElementsUsing(MATCHES_REGEX) |
| .containsExactly( |
| "--java_out=param1,param2:bl?azel?-out/k8-fastbuild/bin/bar/out", |
| "--plugin=bl?azel?-out/[^/]*-exec-[^/]*/bin/third_party/x/plugin", |
| "-I.", |
| "bar/A.proto") |
| .inOrder(); |
| } |
| |
| /** |
| * Verifies usage of <code>proto_common.generate_code</code> with <code>plugin_output</code> |
| * parameter set to directory. |
| */ |
| @Test |
| public void generateCode_withDirectoryPluginOutput() 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 = 'multiple')"); |
| |
| ConfiguredTarget target = getConfiguredTarget("//bar:simple"); |
| |
| List<String> cmdLine = |
| getGeneratingSpawnAction(getBinArtifact("out", target)).getRemainingArguments(); |
| assertThat(cmdLine) |
| .comparingElementsUsing(MATCHES_REGEX) |
| .containsExactly( |
| "--java_out=param1,param2:bl?azel?-out/k8-fastbuild/bin", |
| "--plugin=bl?azel?-out/[^/]*-exec-[^/]*/bin/third_party/x/plugin", |
| "-I.", |
| "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", |
| "-I.", |
| "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", |
| "-I.", |
| "--foo", |
| "--bar", |
| "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 { |
| 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", |
| "-Ibl?azel?-out/k8-fastbuild/bin", |
| "-I.", |
| "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 { |
| 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", |
| "-Ibl?azel?-out/k8-fastbuild/bin", |
| "-I.", |
| "bar/A.proto") |
| .inOrder(); |
| } |
| |
| /** |
| * Verifies <code>proto_common.generate_code</code> correctly handles external <code>proto_library |
| * </code>-es. |
| */ |
| @Test |
| @TestParameters({ |
| "{sibling: false, generated: false, expectedFlags:" + " ['-Iexternal/foo']}", |
| "{sibling: false, generated: true, expectedFlags:" |
| + " ['-Ibl?azel?-out/k8-fastbuild/bin/external/foo']}", |
| "{sibling: true, generated: false,expectedFlags:" + " ['-I../foo']}", |
| "{sibling: true, generated: true, expectedFlags:" + " ['-Ibl?azel?-out/foo/k8-fastbuild/bin']}", |
| }) |
| public void generateCode_externalProtoLibrary( |
| boolean sibling, boolean generated, List<String> expectedFlags) throws Exception { |
| 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), |
| "-I.", |
| "bar/A.proto") |
| .inOrder(); |
| } |
| |
| /** Verifies <code>experimental_progress_message</code> parameters. */ |
| @Test |
| public void generateCode_overrideProgressMessage() 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', progress_message = 'My %{label}')"); |
| |
| 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", "-I.", "bar/A.proto") |
| .inOrder(); |
| assertThat(spawnAction.getMnemonic()).isEqualTo("MyMnemonic"); |
| assertThat(spawnAction.getProgressMessage()).isEqualTo("My //bar:simple"); |
| } |
| |
| /** Verifies <code>proto_common.should_generate_code</code> call. */ |
| @Test |
| public void shouldGenerateCode_basic() throws Exception { |
| scratch.file( |
| "bar/BUILD", |
| TestConstants.LOAD_PROTO_LIBRARY, |
| "load('//foo:should_generate.bzl', 'should_generate_rule')", |
| "proto_library(name = 'proto', srcs = ['A.proto'])", |
| "should_generate_rule(name = 'simple', proto_dep = ':proto')"); |
| |
| ConfiguredTarget target = getConfiguredTarget("//bar:simple"); |
| |
| StarlarkInfo boolProvider = (StarlarkInfo) target.get(boolProviderId); |
| assertThat(boolProvider.getValue("value", Boolean.class)).isTrue(); |
| } |
| |
| /** Verifies <code>proto_common.should_generate_code</code> call. */ |
| @Test |
| public void shouldGenerateCode_dontGenerate() throws Exception { |
| scratch.file( |
| "bar/BUILD", |
| TestConstants.LOAD_PROTO_LIBRARY, |
| "load('//foo:should_generate.bzl', 'should_generate_rule')", |
| "should_generate_rule(name = 'simple', proto_dep = '//third_party/x:denied')"); |
| |
| ConfiguredTarget target = getConfiguredTarget("//bar:simple"); |
| |
| StarlarkInfo boolProvider = (StarlarkInfo) target.get(boolProviderId); |
| assertThat(boolProvider.getValue("value", Boolean.class)).isFalse(); |
| } |
| |
| /** Verifies <code>proto_common.should_generate_code</code> call. */ |
| @Test |
| public void shouldGenerateCode_mixed() throws Exception { |
| scratch.file( |
| "bar/BUILD", |
| TestConstants.LOAD_PROTO_LIBRARY, |
| "load('//foo:should_generate.bzl', 'should_generate_rule')", |
| "should_generate_rule(name = 'simple', proto_dep = '//third_party/x:mixed')"); |
| |
| reporter.removeHandler(failFastHandler); |
| getConfiguredTarget("//bar:simple"); |
| |
| assertContainsEvent( |
| "The 'srcs' attribute of '@@//third_party/x:mixed' contains protos for which 'MyRule'" |
| + " shouldn't generate code (third_party/x/metadata.proto," |
| + " third_party/x/descriptor.proto), in addition to protos for which it should" |
| + " (third_party/x/something.proto).\n" |
| + "Separate '@@//third_party/x:mixed' into 2 proto_library rules."); |
| } |
| |
| /** Verifies <code>proto_common.declare_generated_files</code> call. */ |
| @Test |
| public void declareGenerateFiles_basic() throws Exception { |
| scratch.file( |
| "bar/BUILD", |
| TestConstants.LOAD_PROTO_LIBRARY, |
| "load('//foo:declare_generated_files.bzl', 'declare_generated_files')", |
| "proto_library(name = 'proto', srcs = ['A.proto', 'b/B.proto'])", |
| "declare_generated_files(name = 'simple', proto_dep = ':proto', extension = '.cc')"); |
| |
| ConfiguredTarget target = getConfiguredTarget("//bar:simple"); |
| |
| assertThat(prettyArtifactNames(target.getProvider(FileProvider.class).getFilesToBuild())) |
| .containsExactly("bar/A.cc", "bar/b/B.cc"); |
| } |
| |
| /** Verifies <code>proto_common.declare_generated_files</code> call for Python. */ |
| @Test |
| public void declareGenerateFiles_pythonc() throws Exception { |
| scratch.file( |
| "bar/BUILD", |
| TestConstants.LOAD_PROTO_LIBRARY, |
| "load('//foo:declare_generated_files.bzl', 'declare_generated_files')", |
| "proto_library(name = 'proto', srcs = ['my-proto.gen.proto'])", |
| "declare_generated_files(name = 'simple', proto_dep = ':proto', extension = '_pb2.py',", |
| " python_names = True)"); |
| |
| ConfiguredTarget target = getConfiguredTarget("//bar:simple"); |
| |
| assertThat(prettyArtifactNames(target.getProvider(FileProvider.class).getFilesToBuild())) |
| .containsExactly("bar/my_proto/gen_pb2.py"); |
| } |
| |
| @Test |
| public void langProtoLibrary_inDifferentPackage_allowed() throws Exception { |
| scratch.file( |
| "proto/BUILD", |
| TestConstants.LOAD_PROTO_LIBRARY, |
| "proto_library(name = 'proto', srcs = ['A.proto'])"); |
| scratch.file( |
| "bar/BUILD", |
| "load('//foo:check_collocated.bzl', 'check_collocated')", |
| "check_collocated(name = 'simple', proto_dep = '//proto:proto')"); |
| |
| getConfiguredTarget("//bar:simple"); |
| |
| assertNoEvents(); |
| } |
| |
| @Test |
| public void langProtoLibrary_inDifferentPackage_fails() throws Exception { |
| scratch.file( |
| "proto/BUILD", |
| TestConstants.LOAD_PROTO_LIBRARY, |
| "proto_library(name = 'proto', srcs = ['A.proto'])"); |
| scratch.file( |
| "test/BUILD", |
| "load('//foo:check_collocated.bzl', 'check_collocated')", |
| "check_collocated(name = 'simple', proto_dep = '//proto:proto')"); |
| |
| reporter.removeHandler(failFastHandler); |
| getConfiguredTarget("//test:simple"); |
| |
| assertContainsEvent( |
| "lang_proto_library '@@//test:simple' may only be created in the same package as" |
| + " proto_library '@@//proto:proto'"); |
| } |
| |
| @Test |
| public void langProtoLibrary_exportNotAllowed() throws Exception { |
| scratch.file( |
| "x/BUILD", |
| "proto_library(name='foo', srcs=['foo.proto'], allow_exports = ':test')", |
| "package_group(", |
| " name='test',", |
| " packages=['//allowed'],", |
| ")"); |
| scratch.file( |
| "notallowed/BUILD", |
| "load('//foo:check_collocated.bzl', 'check_collocated')", |
| "check_collocated(name = 'simple', proto_dep = '//x:foo')"); |
| |
| reporter.removeHandler(failFastHandler); |
| getConfiguredTarget("//notallowed:simple"); |
| |
| assertContainsEvent( |
| "lang_proto_library '@@//notallowed:simple' may only be created in the same package as" |
| + " proto_library '@@//x:foo'"); |
| } |
| |
| @Test |
| public void langProtoLibrary_exportAllowed() throws Exception { |
| scratch.file( |
| "x/BUILD", |
| "proto_library(name='foo', srcs=['foo.proto'], allow_exports = ':test')", |
| "package_group(", |
| " name='test',", |
| " packages=['//allowed'],", |
| ")"); |
| scratch.file( |
| "allowed/BUILD", |
| "load('//foo:check_collocated.bzl', 'check_collocated')", |
| "check_collocated(name = 'simple', proto_dep = '//x:foo')"); |
| |
| getConfiguredTarget("//allowed:simple"); |
| |
| assertNoEvents(); |
| } |
| } |