如何在Bazel中让一个genrule的部分源文件(如头文件)供另一个genrule使用?
我来帮你梳理下这个问题的解决方案——你遇到的核心矛盾是Bazel不允许同一个文件既是genrule的输入又是输出,而且手动列所有头文件确实太繁琐。这里有几个从简单到更专业的解决思路:
方案1:用filegroup收集安装产物(最小改动现有genrule)
你可以调整openssl的genrule,把所有安装产物放到一个独立的子目录里,然后用filegroup统一收集这些产物,避免逐个列出文件。
修改你的openssl BUILD文件:
# 首先,修改genrule,将产物安装到子目录,用一个标记文件作为输出 genrule( name = "_build_openssl", visibility = ["//visibility:private"], srcs = glob(["**/*"], exclude=["bazel-*", "install/**/*"]), # 用一个标记文件来标识构建完成,避免逐个列产物 outs = ["install_complete"], cmd = """ # 创建独立的安装目录 INSTALL_DIR="$$(realpath $(RULEDIR)/install)" mkdir -p "$$INSTALL_DIR" # 进入源码目录执行构建 pushd "$$(dirname $(location config))" # 注意指定prefix为/,这样install时会把内容放到DESTDIR下的对应路径 ./config --prefix=/ --openssldir=ssl make -j6 # 安装到我们指定的目录 make DESTDIR="$$INSTALL_DIR" install_sw install_ssldirs # 标记构建完成 touch $(OUTS) """ ) # 用filegroup收集所有安装后的产物,对外暴露 filegroup( name = "openssl", visibility = ["//visibility:public"], srcs = [":_build_openssl"], # 匹配安装目录下的所有文件 output_files = glob(["install/**/*"]), )
然后在xmlsec1的genrule中,你可以通过$(location @openssl//:install/include/openssl)获取头文件路径,或者直接拿到整个安装目录:
genrule( name = "build_xmlsec1", srcs = glob(["**/*"], exclude=["bazel-*"]) + ["@openssl//:openssl"], outs = [...], # 替换为你的xmlsec1产物列表 cmd = """ OPENSSL_INSTALL="$$(realpath $(dirname $(location @openssl//:install_complete))/install)" pushd "$$(dirname $(location configure))" ./configure --with-openssl="$$OPENSSL_INSTALL" make -j6 # 后续安装逻辑... """ )
方案2:用cc_library封装openssl(更贴合Bazel生态)
既然openssl是C库,用Bazel原生的cc_library封装它的产物会更方便,依赖它的目标可以自动获取头文件和库路径:
在openssl的BUILD文件中,在genrule之后添加:
cc_library( name = "openssl_cc", visibility = ["//visibility:public"], # 指定静态库文件 srcs = [ "install/lib/libssl.a", "install/lib/libcrypto.a", ], # 收集所有头文件 hdrs = glob(["install/include/openssl/**/*.h"]), # 告诉Bazel头文件的根目录,这样依赖方可以直接#include <openssl/xxx.h> includes = ["install/include"], # 静态链接库 linkstatic = True, )
然后在xmlsec1的genrule中,你可以手动提取openssl的路径:
genrule( name = "build_xmlsec1", srcs = glob(["**/*"], exclude=["bazel-*"]) + ["@openssl//:openssl_cc"], outs = [...], # 替换为你的xmlsec1产物列表 cmd = """ # 获取openssl的头文件根目录 OPENSSL_INCLUDE="$$(realpath $(dirname $(location @openssl//:install/include/openssl/opensslconf.h))/../..)" OPENSSL_LIB="$$(realpath $(dirname $(location @openssl//:install/lib/libssl.a)))" pushd "$$(dirname $(location configure))" ./configure --with-openssl="$$OPENSSL_INCLUDE/.." \ CPPFLAGS="-I$$OPENSSL_INCLUDE" \ LDFLAGS="-L$$OPENSSL_LIB" make -j6 # 后续安装逻辑... """ )
方案3:使用rules_foreign_cc(推荐,更专业的外部项目构建)
如果你的项目经常需要封装外部Makefile项目,Google官方维护的rules_foreign_cc是最优选择——它专门处理这类场景,自动处理依赖、产物收集和增量构建,比手写genrule更可靠。
首先,在WORKSPACE中引入rules_foreign_cc(记得替换为最新版本的sha256):
load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") http_archive( name = "rules_foreign_cc", sha256 = "a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2", url = "https://github.com/bazelbuild/rules_foreign_cc/releases/download/0.9.0/rules_foreign_cc-0.9.0.tar.gz", ) load("@rules_foreign_cc//foreign_cc:repositories.bzl", "rules_foreign_cc_dependencies") rules_foreign_cc_dependencies()
然后在openssl的BUILD文件中,用make规则替代genrule:
load("@rules_foreign_cc//foreign_cc:defs.bzl", "make") make( name = "openssl", visibility = ["//visibility:public"], # 源码文件 srcs = glob(["**/*"], exclude=["bazel-*"]), # configure命令,指定prefix为/方便安装到指定目录 configure_command = "./config --prefix=/ --openssldir=ssl", # 安装前缀,配合configure的prefix,产物会放到out/include和out/lib install_prefix = "/", # 指定输出的头文件目录和库目录 out_include_dir = "include", out_lib_dir = "lib", # 要收集的静态库 static_libraries = ["libssl.a", "libcrypto.a"], )
然后xmlsec1的构建同样用make规则,并依赖openssl:
load("@rules_foreign_cc//foreign_cc:defs.bzl", "make") make( name = "xmlsec1", visibility = ["//visibility:public"], srcs = glob(["**/*"], exclude=["bazel-*"]), # 依赖openssl deps = ["@openssl//:openssl"], # configure时指定openssl的路径,rules_foreign_cc会自动传递依赖路径 configure_command = "./configure --with-openssl=$(location @openssl//:include)/..", install_prefix = "/", # 根据你的需求指定输出产物 static_libraries = ["libxmlsec1.a"], )
这个方案的优势在于,rules_foreign_cc会自动处理增量构建、依赖传递,不需要手动写复杂的cmd逻辑,也避免了手动处理文件路径的麻烦。
内容的提问来源于stack exchange,提问作者frans

