Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
508 views
in Technique[技术] by (71.8m points)

bazel - Propagating copts/defines to all of a target's dependencies

I have a project that involves multiple BUILD files in a single WORKSPACE, within a fairly complex build system. My goal in short: for some specific target, I want all of its recursive dependencies to be built with an extra set of attributes (copts/defines) compared to when those dependency targets are built in any other way. I have not yet found a way to do this cleanly.

For example, target G is normally built with copts = []. If target P depends on target G, and I run bazel build :P, I want both targets to be built with copts = ["-DMY_DEFINE"], along with all dependencies of target G, etc.

The cc_binary.defines argument propagates in the opposite direction: all targets that depend on some target A will receive all of target A's defines.

Limitations:

  • prefer to avoid custom command line flags, I don't control how people call bazel {build,test}
  • duplicating the entire tree of dependency targets is not practical

It doesn't appear possible to set the value of a config_setting from within a BUILD file or a target, so it seems a select-based solution couldn't work.

Previous work:

question from:https://stackoverflow.com/questions/65837131/propagating-copts-defines-to-all-of-a-targets-dependencies

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

Creating a user-defined build setting doesn't require command-line flags. If you set flag = False, then it actually can't be set on the command line. You can use a user-defined transition to set it instead.

I think something like this will do what you're looking for (save it in extra_copts.bzl):

def _extra_copts_impl(ctx):
    context = cc_common.create_compilation_context(
        defines = depset(ctx.build_setting_value)
    )
    return [CcInfo(compilation_context = context)]

extra_copts = rule(
    implementation = _extra_copts_impl,
    build_setting = config.string_list(flag = False),
)

def _use_extra_copts_implementation(ctx):
    return [ctx.attr._copts[CcInfo]]

use_extra_copts = rule(
    implementation = _use_extra_copts_implementation,
    attrs = "_copts": attr.label(default = "//:extra_copts")},
)

def _add_copts_impl(settings, attr):
    return {"//:extra_copts": ["MY_DEFINE"]}

_add_copts = transition(
    implementation = _add_copts_impl,
    inputs = [],
    outputs = ["//:extra_copts"],
)

def _with_extra_copts_implementation(ctx):
    infos = [d[CcInfo] for d in ctx.attr.deps]
    return [cc_common.merge_cc_infos(cc_infos = infos)]

with_extra_copts = rule(
    implementation = _with_extra_copts_implementation,
    attrs = {
        "deps": attr.label_list(cfg = _add_copts),
        "_allowlist_function_transition": attr.label(
            default = "@bazel_tools//tools/allowlists/function_transition_allowlist"
        )
    },
)

and then in the BUILD file:

load("//:extra_copts.bzl", "extra_copts", "use_extra_copts", "with_extra_copts")

extra_copts(name = "extra_copts", build_setting_default = [])

use_extra_copts(name = "use_extra_copts")

cc_library(
    name = "G",
    deps = [":use_extra_copts"],
)

with_extra_copts(
    name = "P_deps",
    deps = [":G"],
)

cc_library(
    name = "P",
    deps = [":P_deps"],
)

extra_copts is the build setting. It returns a CcInfo directly, which means it's straightforward to do any other C++ library swapping with the same approach. Its default is effectively an "empty" CcInfo which won't do anything to libraries that depend on it.

with_extra_copts wraps a set of dependencies, configured to use a different CcInfo. This is the rule that actually changes the value, to create the second version of G with different flags.

_add_copts is the transition which with_extra_copts uses to change the value of the extra_copts build setting. It could examine attr to do something more sophisticated than adding a hard-coded list.

use_extra_copts pulls the CcInfo out of extra_copts so a cc_library can use them.

To avoid rewriting the builtin C++ rules, this uses wrapper rules to pull the copts out and do the transition. You might want to create macros to bundle the wrapper rules along with the corresponding cc_library. Alternatively, you could use rules_cc's my_c_archive as a starting point to create custom rules that reuse the core implementation of the builtin C++ rules while integrating the transition and use of the build setting into a single rule.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...