| """ |
| This file contains a way to set flags from BUILD.bazel instead of requiring users to set them from |
| the CLI. |
| |
| It is based off of https://github.com/bazelbuild/examples/tree/main/rules/starlark_configurations/cc_binary_selectable_copts |
| |
| """ |
| |
| _string_flags = [ |
| "//bazel/common_config_settings:fontmgr_factory", |
| "//bazel/common_config_settings:with_gl_standard", |
| ] |
| |
| _string_list_flags = [ |
| "//bazel/common_config_settings:gpu_backend", |
| "//bazel/common_config_settings:include_decoder", |
| "//bazel/common_config_settings:include_encoder", |
| "//bazel/common_config_settings:include_fontmgr", |
| ] |
| |
| # These are the flags that we support setting via set_flags |
| _flags = _string_flags + _string_list_flags |
| |
| def _flag_transition_impl(settings, attr): |
| rv = {} |
| for key in settings: |
| # Get the short form of the name. This the short form used as the keys in the |
| # set_flags dictionary. |
| flag_name = key.split(":")[1] |
| |
| # If there is an entry in set_flags for the short-version of a flag, use that |
| # value or values. If not, use whatever value is set via flags. |
| flag_setting = attr.set_flags.get(flag_name, settings[key]) |
| if key in _string_list_flags: |
| if type(flag_setting) == "list": |
| rv[key] = flag_setting |
| else: |
| rv[key] = [flag_setting] # This usually happens when the default value is used. |
| elif key in _string_flags: |
| if type(flag_setting) == "list": |
| rv[key] = flag_setting[0] |
| else: |
| rv[key] = flag_setting # we know flag_setting is a string (e.g. the default). |
| return rv |
| |
| # This defines a Starlark transition and which flags it reads and writes. |
| _flag_transition = transition( |
| implementation = _flag_transition_impl, |
| inputs = _flags, |
| outputs = _flags, |
| ) |
| |
| # The implementation of transition_rule: all this does is copy the cc_binary's output to |
| # its own output and propagate its runfiles and executable to use for "$ bazel run". |
| # |
| # This makes transition_rule as close to a pure wrapper of cc_binary as possible. |
| def _transition_rule_impl(ctx): |
| actual_binary = ctx.attr.actual_binary[0] |
| outfile = ctx.actions.declare_file(ctx.label.name) |
| cc_binary_outfile = actual_binary[DefaultInfo].files.to_list()[0] |
| |
| ctx.actions.run_shell( |
| inputs = [cc_binary_outfile], |
| outputs = [outfile], |
| command = "cp %s %s" % (cc_binary_outfile.path, outfile.path), |
| ) |
| return [ |
| DefaultInfo( |
| executable = outfile, |
| data_runfiles = actual_binary[DefaultInfo].data_runfiles, |
| ), |
| ] |
| |
| # The purpose of this rule is to take a "set_flags" attribute, invoke a transition that sets |
| # any of _flags to the specified values, then depend on a cc_binary whose deps will be able |
| # to select() on those flags as if the user had set them via the CLI. |
| transition_rule = rule( |
| implementation = _transition_rule_impl, |
| attrs = { |
| # set_flags is a dictionary with the keys being the short-form of a flag name |
| # (e.g. the part that comes after the colon) and the value being a list of values |
| # that the flag should be set to, regardless of the relevant CLI flags. |
| # https://docs.bazel.build/versions/main/skylark/lib/attr.html#string_list_dict |
| "set_flags": attr.string_list_dict(), |
| # This is the cc_binary whose deps will select() on that feature. |
| # Note specifically how it is modified with _flag_transition, which |
| # ensures that the flags propagates down the graph. |
| # https://docs.bazel.build/versions/main/skylark/lib/attr.html#label |
| "actual_binary": attr.label(cfg = _flag_transition), |
| # This is a stock Bazel requirement for any rule that uses Starlark |
| # transitions. It's okay to copy the below verbatim for all such rules. |
| # |
| # The purpose of this requirement is to give the ability to restrict |
| # which packages can invoke these rules, since Starlark transitions |
| # make much larger graphs possible that can have memory and performance |
| # consequences for your build. The allowlist defaults to "everything". |
| # But you can redefine it more strictly if you feel that's prudent. |
| "_allowlist_function_transition": attr.label( |
| default = "@bazel_tools//tools/allowlists/function_transition_allowlist", |
| ), |
| }, |
| # Making this executable means it works with "$ bazel run". |
| executable = True, |
| ) |
| |
| def cc_binary_with_flags(name, cc_binary_name = "", set_flags = {}, **kwargs): |
| """Builds a cc_binary as if set_flags were set on the CLI. |
| |
| Args: |
| name: string, the name for the rule that is the binary, but with the flags changed via |
| a transition. Any dependents should use this name. |
| cc_binary_name: string, the name of the binary created (not the name of the transition |
| rule). The default is the name with "_native_binary" as a suffix. |
| set_flags: dictionary of string to list of strings. The keys should be the name of the |
| flag, and the values should be the desired valid settings for that flag. |
| **kwargs: Any flags that a cc_binary normally takes. |
| """ |
| if not cc_binary_name: |
| cc_binary_name = name + "_native_binary" |
| transition_rule( |
| name = name, |
| actual_binary = ":%s" % cc_binary_name, |
| set_flags = set_flags, |
| testonly = kwargs.get("testonly", False), |
| ) |
| native.cc_binary( |
| name = cc_binary_name, |
| **kwargs |
| ) |