如何解决Bazel宏中的‘select不可迭代’报错?
问题:Bazel宏接收select参数时触发"select is not iterable"错误
我定义了ispc_cc_library Bazel宏,在BUILD.bazel中调用时传入包含select的COMMON_DEFINES参数:
COMMON_DEFINES = select({ "@platforms//os:osx": [ "OIDN_BNNS", "OIDN_STATIC_LIB", "OIDN_FILTER_RT", "OIDN_FILTER_RTLIGHTMAP", ], "//conditions:default": [ "OIDN_DNNL", "OIDN_STATIC_LIB", "OIDN_FILTER_RT", "OIDN_FILTER_RTLIGHTMAP", ], }) ispc_cc_library( name = "input_reorder_ispc", srcs = [ "core/color.isph", "core/image.isph", "core/input_reorder.ispc", "core/math.isph", "core/reorder.isph", "core/tensor.isph", "core/vec.isph", ], out = "input_reorder_ispc.h", defines = COMMON_DEFINES, ispc_main_source_file = "core/input_reorder.ispc", )
执行bazel build //...时触发报错:
ERROR: Traceback (most recent call last): File "/private/var/tmp/_bazel_vertexwahn/998288db447b21c21cab4093cf36fa19/external/oidn/BUILD.bazel", line 48, column 16, in <toplevel> ispc_cc_library( File "/private/var/tmp/_bazel_vertexwahn/998288db447b21c21cab4093cf36fa19/external/rules_ispc/ispc.bzl", line 5, column 11, in ispc_cc_library if len(defines) > 0: Error in len: select is not iterable
该宏实现中会对defines执行len(defines)判断并拼接编译参数,直接传固定列表时构建正常。想知道:
- 正确解决方式是什么?
- 是否需要将宏转为规则,或是有简便的临时方案?
- 报错是否因为宏在加载阶段执行,而select在分析阶段解析导致?
重现步骤:
git clone https://github.com/Vertexwahn/rules_ispc.git cd rules_ispc/tests/defines bazel build //defines:main # 可正常构建 # 取消rules_ispc/tests/defines/BUILD.bazel第16行的注释:#defines = COMMON_DEFINES, # this is currenlty not working # 再次构建 bazel build //defines:main # 会触发报错
解答
报错原因
没错,问题根源就是Bazel的执行阶段差异:
- 宏是在加载阶段运行的,此时
select还只是一个未解析的特殊对象,不是实际的列表,所以调用len()会直接报错。 select要到分析阶段才会根据当前平台解析成具体的列表值,而宏无法等到这个阶段再执行代码。
解决方法
临时简便方案(Workaround)
如果不想大改宏的结构,可以修改宏的实现逻辑:不要直接对defines做len()判断或迭代处理,而是把defines直接传递给底层支持select的原生Bazel规则(比如genrule、cc_library的defines参数,这些原生规则本身就支持接收select)。
比如原来宏里可能是手动拼接-D参数的逻辑:
# 错误写法:在宏里处理select对象 if len(defines) > 0: compile_flags = ["-D" + d for d in defines]
改成直接把defines传给原生规则:
# 正确写法:交给原生规则处理select genrule( name = "...", srcs = ctx.attr.srcs, outs = [ctx.attr.out], defines = defines, # 原生genrule支持select cmd = "...", )
这样原生规则会自动在分析阶段解析select,宏不需要在加载阶段处理这个对象。
长期正确方案:将宏转为Bazel规则
宏的本质是加载阶段的代码生成工具,天生无法处理分析阶段才解析的结构(比如select、配置依赖的属性)。而Bazel规则是在分析阶段执行的,能直接拿到解析后的select值,所以把宏改成规则是彻底解决问题的方法。
大致实现步骤:
- 把
macro定义改成rule定义,在attrs中声明支持的参数,其中defines设为string_list类型(默认支持select)。 - 在
implementation函数中处理defines,此时它已经是当前平台对应的具体列表,可以安全调用len()或做其他处理。
示例代码:
# 定义规则 ispc_cc_library = rule( implementation = _ispc_cc_library_impl, attrs = { "srcs": attr.label_list(allow_files = [".ispc", ".isph"]), "out": attr.output(mandatory = True), "defines": attr.string_list(default = []), # 自动支持select "ispc_main_source_file": attr.label(mandatory = True, allow_single_file = [".ispc"]), # 其他必要属性,比如工具链、编译选项等 }, # 如果生成的是C++库,需要声明提供CcInfo provides = [CcInfo], ) # 实现函数 def _ispc_cc_library_impl(ctx): # 这里的defines已经是解析后的列表,可安全处理 defines = ctx.attr.defines compile_flags = [] if len(defines) > 0: compile_flags = ["-D" + d for d in defines] # 后续生成编译动作、输出文件等逻辑 # ...
修改后,调用时传入带select的COMMON_DEFINES就能正常工作了。
内容的提问来源于stack exchange,提问作者Vertexwahn
相关产品推荐
相关产品推荐

