如何在Bazel构建的二进制文件中使用整个目录
问题:Bazel运行二进制程序时无法访问指定目录
场景复现
我们有一个C++二进制程序,通过Bazel构建,功能是遍历固定目录下的子目录并解析其中的.json文件,核心代码如下:
for (const auto& entry : std::filesystem::directory_iterator("/system/artefacts")) { if (entry.is_directory()) { // 查找.json文件并进行处理 } }
对应的Bazel配置:
cc_binary( name = "dir_iterator", srcs = ["dir_iterator.cpp"], data = [":data"], deps = [ #... ], )
filegroup( name = "data", srcs = glob([ "system/artefacts", # 此配置无效 ]), )
运行bazel run //utils:dir_iterator时出现报错:
terminate called after throwing an instance of 'std::filesystem::__cxx11::filesystem_error' what(): filesystem error: directory iterator cannot open directory: No such file or directory [system/artefacts/]
工作区目录结构:
. ├── system │ └── artefacts │ ├── x │ │ └── a.json │ ├── y │ │ └── a.json │ └── z │ └── a.json └── util ├── BUILD └── dir_iterator.cpp
问题原因
- Filegroup配置错误:原
filegroup仅指定了目录system/artefacts,Bazel不会自动递归包含目录内的文件和子目录,导致数据未被纳入运行环境。 - 路径硬编码问题:代码中使用绝对路径
/system/artefacts,但Bazel运行时的工作目录是其输出目录(如bazel-bin),该绝对路径不存在;实际数据文件会被复制到Bazel的runfiles目录下。
解决方案
步骤1:修正Filegroup配置
修改filegroup,用**/*递归匹配目录下所有文件和子目录:
filegroup( name = "data", srcs = glob([ "system/artefacts/**/*", ]), )
方案A:通过Runfiles目录获取正确路径
利用Bazel提供的Runfiles机制获取数据文件的实际路径,需要依赖Bazel的Runfiles库:
- 更新
cc_binary配置,添加Runfiles依赖:
cc_binary( name = "dir_iterator", srcs = ["dir_iterator.cpp"], data = [":data"], deps = [ "@bazel_tools//tools/cpp/runfiles", ], )
- 修改C++代码,获取Runfiles目录并拼接正确路径(替换
your_workspace_name为你的Bazel工作区根目录名称):
#include <filesystem> #include <iostream> #include "tools/cpp/runfiles/runfiles.h" namespace fs = std::filesystem; using bazel::tools::cpp::runfiles::Runfiles; int main(int argc, char** argv) { std::string error; std::unique_ptr<Runfiles> runfiles(Runfiles::Create(argv[0], &error)); if (!runfiles) { std::cerr << "Failed to init runfiles: " << error << std::endl; return 1; } // 拼接工作区下的system/artefacts路径 std::string artefacts_path = runfiles->Rlocation("your_workspace_name/system/artefacts"); try { for (const auto& entry : fs::directory_iterator(artefacts_path)) { if (entry.is_directory()) { // 处理子目录中的JSON文件 for (const auto& json_entry : fs::directory_iterator(entry.path())) { if (json_entry.path().extension() == ".json") { // 执行解析逻辑 } } } } } catch (const fs::filesystem_error& e) { std::cerr << "Error: " << e.what() << std::endl; return 1; } return 0; }
方案B:通过命令行参数传递路径
通过Bazel的$(location)宏将数据目录路径作为命令行参数传递给程序,无需依赖额外库:
- 更新
cc_binary配置,添加args传递路径:
cc_binary( name = "dir_iterator", srcs = ["dir_iterator.cpp"], data = [":data"], deps = [], args = ["$(location :data)"], )
- 修改C++代码,读取命令行参数作为目标路径:
#include <filesystem> #include <iostream> namespace fs = std::filesystem; int main(int argc, char* argv[]) { if (argc != 2) { std::cerr << "Usage: " << argv[0] << " <artefacts_dir>" << std::endl; return 1; } fs::path artefacts_path = argv[1]; try { for (const auto& entry : fs::directory_iterator(artefacts_path)) { if (entry.is_directory()) { // 处理子目录中的JSON文件 for (const auto& json_entry : fs::directory_iterator(entry.path())) { if (json_entry.path().extension() == ".json") { // 执行解析逻辑 } } } } } catch (const fs::filesystem_error& e) { std::cerr << "Error: " << e.what() << std::endl; return 1; } return 0; }
内容的提问来源于stack exchange,提问作者Curious
相关产品推荐
相关产品推荐

