From b9e6b2922d8b6177d0747f30b738ea467161fc33 Mon Sep 17 00:00:00 2001 From: Red Daly Date: Sat, 19 Nov 2022 13:41:24 -0800 Subject: [PATCH 1/2] Allow greater customization of environment variables by plugins. * Add an "env" property to ProtoPluginInfo that contains an environment variable dictionary. * Add a corresponding "env" attribute to the the proto_plugin rule. * For cases where environment variables must specified based on ctx, as described in https://github.com/bazelbuild/bazel/issues/15470 and https://github.com/aspect-build/rules_js/issues/397, modify proto_compile_impl and proto_compile to accept a base_env dictionary argument. Example usage is below: ```starlark def _ts_proto_compile_impl(ctx): """ Implementation function for ts_proto_compile. Args: ctx: The Bazel rule execution context object. Returns: Providers: - ProtoCompileInfo - DefaultInfo """ base_env = { # Make up for https://github.com/bazelbuild/bazel/issues/15470. "BAZEL_BINDIR": ctx.bin_dir.path, } return proto_compile_impl(ctx, base_env = base_env) ts_proto_compile = rule( implementation = _ts_proto_compile_impl, attrs = dict( proto_compile_attrs, _plugins = attr.label_list( providers = [ProtoPluginInfo], default = [ Label("//bazel/web/targets:ts_proto_compile"), ], doc = "List of protoc plugins to apply", ), ), toolchains = [ str(Label("@rules_proto_grpc//protobuf:toolchain_type")), ], ) ``` ```starlark load("@npm//:ts-proto/package_json.bzl", _ts_proto_bin_factories = "bin") load("@rules_proto_grpc//:defs.bzl", "proto_plugin") _ts_proto_bin_factories.protoc_gen_ts_proto_binary( name = "protoc-gen-ts-proto", ) proto_plugin( name = "ts_proto_compile", outputs = ["{protopath}.ts"], protoc_plugin_name = "ts_proto", tool = "//bazel/web/targets:protoc-gen-ts-proto", use_built_in_shell_environment = False, visibility = ["//visibility:public"], ) ``` --- internal/compile.bzl | 23 ++++++++++++++++++----- internal/plugin.bzl | 5 +++++ internal/providers.bzl | 1 + 3 files changed, 24 insertions(+), 5 deletions(-) diff --git a/internal/compile.bzl b/internal/compile.bzl index 52e99b2f5..7032e8910 100644 --- a/internal/compile.bzl +++ b/internal/compile.bzl @@ -41,12 +41,14 @@ proto_compile_attrs = { ), } -def proto_compile_impl(ctx): +def proto_compile_impl(ctx, base_env = {}): """ Common implementation function for lang_*_compile rules. Args: ctx: The Bazel rule execution context object. + base_env: Default environment to use for protoc if + "use_built_in_shell_environment" = False for a given plugin. Returns: Providers: @@ -63,9 +65,9 @@ def proto_compile_impl(ctx): extra_protoc_files = ctx.files.extra_protoc_files # Execute with extracted attrs - return proto_compile(ctx, options, extra_protoc_args, extra_protoc_files) + return proto_compile(ctx, options, extra_protoc_args, extra_protoc_files, base_env) -def proto_compile(ctx, options, extra_protoc_args, extra_protoc_files): +def proto_compile(ctx, options, extra_protoc_args, extra_protoc_files, base_env): """ Common implementation function for lang_*_compile rules. @@ -74,6 +76,8 @@ def proto_compile(ctx, options, extra_protoc_args, extra_protoc_files): options: The mutable options dict. extra_protoc_args: The mutable extra_protoc_args list. extra_protoc_files: The mutable extra_protoc_files list. + base_env: Default environment to use for protoc if + "use_built_in_shell_environment" = False for a given plugin. Returns: Providers: @@ -327,7 +331,7 @@ def proto_compile(ctx, options, extra_protoc_args, extra_protoc_files): command = "echo '\n##### SANDBOX BEFORE RUNNING PROTOC' && find . -type l && " + command if verbose > 3: - command = "env && " + command + command = "echo Printing environment used to invoke protoc.... && env && " + command for f in cmd_inputs: print("INPUT:", f.path) # buildifier: disable=print for f in protos: @@ -337,7 +341,15 @@ def proto_compile(ctx, options, extra_protoc_args, extra_protoc_files): for f in plugin_outputs: print("EXPECTED OUTPUT:", f.path) # buildifier: disable=print - # Run protoc + env = {} + if (len(env) > 0) and plugin.use_built_in_shell_environment: + fail("env and use_built_in_shell_environment are mutually exclusive; " + + " both set for plugin {}".format(plugin.name)) + + if not plugin.use_built_in_shell_environment: + env = dict(base_env, **env) + + # Run protoc (https://bazel.build/rules/lib/actions#run_shell) ctx.actions.run_shell( mnemonic = mnemonic, command = command, @@ -345,6 +357,7 @@ def proto_compile(ctx, options, extra_protoc_args, extra_protoc_files): inputs = cmd_inputs, tools = tools, outputs = plugin_protoc_outputs, + env = env, use_default_shell_env = plugin.use_built_in_shell_environment, input_manifests = cmd_input_manifests, progress_message = "Compiling protoc outputs for {} plugin on target {}".format( diff --git a/internal/plugin.bzl b/internal/plugin.bzl index 0480edae6..6ff17e6ce 100644 --- a/internal/plugin.bzl +++ b/internal/plugin.bzl @@ -15,6 +15,7 @@ def _proto_plugin_impl(ctx): tool = ctx.attr.tool, tool_executable = ctx.executable.tool, use_built_in_shell_environment = ctx.attr.use_built_in_shell_environment, + env = ctx.attr.env, protoc_plugin_name = ctx.attr.protoc_plugin_name, exclusions = ctx.attr.exclusions, data = ctx.files.data, @@ -53,6 +54,10 @@ proto_plugin = rule( doc = "Whether the tool should use the built in shell environment or not", default = True, ), + "env": attr.string_dict( + doc = "Sets the dictionary of environment variables to use when invoking protoc. Must be None if use_built_in_shell_environment is true.", + default = {}, + ), "protoc_plugin_name": attr.string( doc = "The name used for the plugin binary on the protoc command line. Useful for targeting built-in plugins. Uses plugin name when not set", ), diff --git a/internal/providers.bzl b/internal/providers.bzl index 18fd3c838..0b7bb5c27 100644 --- a/internal/providers.bzl +++ b/internal/providers.bzl @@ -13,6 +13,7 @@ ProtoPluginInfo = provider( "tool": "The plugin binary. If absent, it is assumed the plugin is built-in to protoc itself and plugin_name will be used if available, otherwise the plugin name", "tool_executable": "The plugin binary executable", "use_built_in_shell_environment": "Whether the tool should use the built in shell environment or not", + "env": "Sets the dictionary of environment variables to use when invoking protoc. Must be None if use_built_in_shell_environment is true.", "protoc_plugin_name": "The name used for the plugin binary on the protoc command line. Useful for targeting built-in plugins. Uses plugin name when not set", "exclusions": "Exclusion filters to apply when generating outputs with this plugin. Used to prevent generating files that are included in the protobuf library, for example. Can exclude either by proto name prefix or by proto folder prefix", "data": "Additional files required for running the plugin", From ecdc9944629650d9f87d748bf57cb39655436014 Mon Sep 17 00:00:00 2001 From: Adam Liddell Date: Sun, 4 Dec 2022 22:08:18 +0000 Subject: [PATCH 2/2] Fix my bad merge --- internal/plugin.bzl | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/internal/plugin.bzl b/internal/plugin.bzl index 4013af998..554db5999 100644 --- a/internal/plugin.bzl +++ b/internal/plugin.bzl @@ -15,6 +15,7 @@ def _proto_plugin_impl(ctx): out = ctx.attr.out, output_directory = ctx.attr.output_directory, env = ctx.attr.env, + extra_protoc_args = ctx.attr.extra_protoc_args, exclusions = ctx.attr.exclusions, data = ctx.files.data, use_built_in_shell_environment = ctx.attr.use_built_in_shell_environment, @@ -53,6 +54,9 @@ proto_plugin = rule( doc = "Sets the dictionary of environment variables to use when invoking protoc. Must be None if use_built_in_shell_environment is true.", default = {}, ), + "extra_protoc_args": attr.string_list( + doc = "A list of extra command line arguments to pass directly to protoc, not as plugin options", + ), "exclusions": attr.string_list( doc = "Exclusion filters to apply when generating outputs with this plugin. Used to prevent generating files that are included in the protobuf library, for example. Can exclude either by proto name prefix or by proto folder prefix", ),