如何在Bazel中封装configure_make规则自动注入工具链配置参数
Bazel configure_make 包装扩展实现方案
你不需要维护configure_make的副本,以下两种无侵入方案都可以实现动态从工具链提取编译器路径注入configure参数,BUILD文件无硬编码:
方案一:复用原生规则实现(推荐,无冗余代码)
configure_make 是rules_foreign_cc提供的规则,其内部实现函数是可导入调用的,你不需要复制全量实现,只需要在自定义规则的实现中先提取工具链参数,再调用原生实现即可。
示例代码:
load("@rules_foreign_cc//foreign_cc:defs.bzl", "configure_make") load("@rules_foreign_cc//foreign_cc/private:configure_make.bzl", _configure_make_impl = "_configure_make_impl") load("@rules_cc//cc:defs.bzl", "find_cpp_toolchain", "use_cpp_toolchain") def _custom_configure_make_impl(ctx): cc_toolchain = find_cpp_toolchain(ctx) # 从工具链提取所需工具路径 extra_env = {} if cc_toolchain.cc: extra_env["CC"] = cc_toolchain.cc.executable.path if cc_toolchain.cxx: extra_env["CXX"] = cc_toolchain.cxx.executable.path if cc_toolchain.ld_executable: extra_env["LD"] = cc_toolchain.ld_executable.path if cc_toolchain.ar_executable: extra_env["AR"] = cc_toolchain.ar_executable.path # 合并原有配置的环境变量 merged_env = dict(ctx.attr.configure_env) merged_env.update(extra_env) # 构造代理ctx,传入修改后的属性,调用原生实现 proxy_ctx = struct( **{k: getattr(ctx, k) for k in dir(ctx) if k != "attr"} ) proxy_ctx.attr = struct( **{k: getattr(ctx.attr, k) for k in dir(ctx.attr) if k != "configure_env"} ) proxy_ctx.attr.configure_env = merged_env return _configure_make_impl(proxy_ctx) custom_configure_make = rule( implementation = _custom_configure_make_impl, # 直接复用原生configure_make的所有attrs,不需要手动写全 attrs = dict(configure_make.attrs, **{ "_cc_toolchain": attr.label(default = Label("@bazel_tools//tools/cpp:current_cc_toolchain")), }), toolchains = configure_make.toolchains + use_cpp_toolchain(), # 其余规则参数直接从原生规则复制,保持行为一致 fragments = configure_make.fragments, exec_groups = configure_make.exec_groups, )
注意:该方案依赖rules_foreign_cc的私有实现入口,大版本升级时需要确认导入路径是否变化。
方案二:宏+参数生成文件(兼容性最佳,不依赖私有API)
如果不想依赖私有API,可以用一个极小的辅助规则生成工具链参数文件,再通过宏包装原生configure_make,把参数文件传入即可,完全不需要修改原生规则逻辑。
- 首先写辅助规则生成参数文件:
def _gen_toolchain_config_opts_impl(ctx): cc_toolchain = find_cpp_toolchain(ctx) out_file = ctx.actions.declare_file(ctx.label.name + ".args") args = [] # 注入工具路径,根据你的configure脚本要求调整参数格式 if cc_toolchain.cc: args.append(f"--CC={cc_toolchain.cc.executable.path}") if cc_toolchain.cxx: args.append(f"--CXX={cc_toolchain.cxx.executable.path}") if cc_toolchain.ld_executable: args.append(f"--LD={cc_toolchain.ld_executable.path}") if cc_toolchain.ar_executable: args.append(f"--AR={cc_toolchain.ar_executable.path}") # 可按需追加sysroot、全局编译flag等参数,全部从cc_toolchain动态获取 ctx.actions.write(out_file, "\n".join(args)) return [DefaultInfo(files = depset([out_file]))] gen_toolchain_config_opts = rule( implementation = _gen_toolchain_config_opts_impl, attrs = { "_cc_toolchain": attr.label(default = Label("@bazel_tools//tools/cpp:current_cc_toolchain")), }, toolchains = use_cpp_toolchain(), )
- 写包装宏串联逻辑:
def my_configure_make(name, configure_options = [], **kwargs): opts_target = f"{name}_tc_opts" gen_toolchain_config_opts(name = opts_target) # rules_foreign_cc的configure_options支持传入文件label,会自动读取文件内容拆分参数 configure_make( name = name, configure_options = configure_options + [f":{opts_target}"], **kwargs )
后续你在BUILD文件中直接使用my_configure_make替代原生configure_make即可,所有原生参数都可以正常透传,不需要写任何工具链硬编码路径。
补充说明
如果你使用的是rules_foreign_cc 0.9.0及以上版本,原生configure_make已经默认会从注册的C++工具链提取CC、CXX、AR、LD等变量传递给configure脚本,出现默认使用/usr/bin/gcc的问题,通常是交叉编译工具链注册不正确,或者构建时未通过--platforms指定目标平台导致的,可以先排查这部分配置,不需要额外做包装。
内容的提问来源于stack exchange,提问作者user19185854
相关产品推荐
相关产品推荐

