如何在Bazel规则运行时获取指定目录下的自动生成文件
在Bazel中获取生成文件并转换为ProtoInfo的解决方案
核心问题说明
你尝试基于openapi-generator-bazel的规则生成文件后,将DefaultInfo转换为ProtoInfo,但直接用ctx.glob()扫描输出目录不可行——因为ctx.glob()运行在Bazel的分析阶段,此时生成文件还未在执行阶段产生,无法直接获取。
可行解决方案
1. 利用TreeArtifact扫描生成文件
openapi-generator-bazel返回的declared_dir是一个TreeArtifact(目录类型输出),必须通过执行阶段的动作来遍历其中的.proto文件:
def _openapi_proto_wrapper_impl(ctx): # 从依赖中获取DefaultInfo和输出目录 openapi_default = ctx.attr.dep[DefaultInfo] output_dir = openapi_default.files.to_list()[0] # 声明一个临时文件,用于存储扫描到的.proto文件路径 proto_list_file = ctx.actions.declare_file(f"{ctx.label.name}_protos.txt") # 执行shell命令扫描目录下的.proto文件并写入临时文件 ctx.actions.run_shell( inputs = [output_dir], outputs = [proto_list_file], command = f"find {output_dir.path} -type f -name '*.proto' > {proto_list_file.path}", ) # 读取临时文件中的路径,转换为Bazel File对象 proto_files = [] for line in proto_list_file.readlines(): proto_path = line.strip() # 通过output_dir的路径关联找到对应的File对象 proto_file = ctx.actions.declare_file(proto_path[len(output_dir.path)+1:], sibling=output_dir) proto_files.append(proto_file)
2. 构建ProtoInfo结构
使用Bazel的proto_common模块创建ProtoInfo,确保传入正确的源码和根目录:
# 导入proto_common模块(需在BUILD文件中依赖相关规则) proto_info = proto_common.create_proto_info( ctx, direct_sources = proto_files, proto_source_root = output_dir.path, transitive_sources = depset(proto_files), ) # 返回组合后的信息 return [ openapi_default, proto_info, ]
3. 更优方案:直接声明生成文件
如果openapi-generator-bazel支持配置生成的.proto文件名,建议直接在规则中声明这些文件,避免执行阶段扫描:
# 在自定义规则中添加属性让用户指定生成的proto文件名 attrs = { "dep": attr.label(mandatory=True, providers=[DefaultInfo]), "proto_file_names": attr.string_list(mandatory=True), } # 在实现中直接声明文件 proto_files = [ctx.actions.declare_file(f, sibling=output_dir) for f in ctx.attr.proto_file_names]
关键注意事项
- 阶段分离:严格区分Bazel的分析阶段和执行阶段,分析阶段无法获取执行阶段生成的文件,必须通过动作中转。
- TreeArtifact处理:TreeArtifact仅代表目录本身,需通过执行动作遍历子文件,或通过声明的关联文件获取具体生成的
.proto。 - ProtoInfo参数:
proto_source_root必须设置为生成文件所在的根目录,确保Protobuf工具能正确解析导入路径。
内容的提问来源于stack exchange,提问作者montjet
相关产品推荐
相关产品推荐

