☰
使用 Bazel(Bzlmod)集成 Flecs ECS:从 MODULE.bazel 依赖声明到示例构建
2026/9/25 13:57:29 网站建设 项目流程
  • 游戏开发

【免费下载链接】flecs

A fast entity component system (ECS) for C & C++

项目地址:https://gitcode.com/gh_mirrors/fl/flecs
点击查看免费下载

Flecs 是一个面向 C 与 C++ 的快速实体组件系统(ECS),在 Bazel 生态中可通过 Bzlmod 以模块化方式直接引入。本文基于仓库中 examples/build/bazel/README.md 这一官方指南,完整讲解如何在你的 Bazel 工程中声明 flecs 依赖、通过git_override锁定源码版本、用bazel run //example跑通一个可用的 ECS 演示程序,并给出结合 CI(如 Renovate)保持依赖自动更新的实践方案。读完本文,你将能独立完成一个基于 Bazel + Flecs 的 C++ 项目搭建,并理解依赖解析与构建目标的底层结构。

一、为什么选择 Bazel 模块方式引入 Flecs

Bazel 自 6.x 起默认启用 Bzlmod 依赖解析机制,第三方库不再依赖传统的WORKSPACE全量拉取,而是通过MODULE.bazel中的bazel_dep声明模块依赖。Flecs 在仓库根目录提供了完整的 Bazel 支持文件(MODULE.bazel、BUILD),这意味着:

  • 你不需要手动把 flecs 的源码复制进自己的工程;
  • 依赖版本通过模块声明集中管理,可复现、可升级;
  • 仓库根目录的 BUILD 文件已经把整个 flecs 编译为一个公开的cc_library目标(name = "flecs"),你的工程直接依赖它即可,无需自己编写构建规则。

从根目录 BUILD 的源码结构看,该cc_library目标将src/**/*.c、include/**/*.h等全部源码与头文件打包,并把include目录加入编译搜索路径,同时按平台选择 C++17 标准(Windows 下为/std:c++17,其他平台为-std=c++17),这正是 Flecs C++ API 所需的编译环境。

二、在 MODULE.bazel 中声明 Flecs 依赖

在你的项目根目录(即包含MODULE.bazel文件的目录)中,加入以下依赖声明:

bazel_dep(name = "flecs") git_override( module_name = "flecs", remote = "https://github.com/SanderMertens/flecs.git", commit = "02c8c2666b22ccce5706c8f16efaf813704fe31e", )

这里有两层含义,值得逐一说明:

  • bazel_dep(name = "flecs"):向 Bazel 声明本工程依赖名为flecs的模块;
  • git_override:由于 flecs 尚未以 registry 形式发布,需要指定 Git 仓库地址(remote)并锁定精确提交(commit),Bazel 会基于该提交构建模块。锁定 commit 是 Bazel 可复现构建的关键——换一台机器或 CI 上,构建结果不会因为上游更新而漂移。

参照仓库中官方示例 examples/build/bazel/MODULE.bazel,一个完整可用的模块文件如下:

"Example for using the flecs as a dependency using bazel" module(name = "flecs_bazel_example", version = "1.0.0") bazel_dep(name = "rules_cc", version = "0.0.9") bazel_dep(name = "flecs") git_override( module_name = "flecs", remote = "https://github.com/SanderMertens/flecs.git", commit = "02c8c2666b22ccce5706c8f16efaf813704fe31e", )

注意示例中同时声明了rules_cc模块:Bzlmod 模式下 C++ 规则通过rules_cc提供,bazel_dep(name = "rules_cc", version = "0.0.9")显式固定了其版本,避免 Bazel 自动选择兼容版本时产生不确定性。

三、在 BUILD 文件中使用 @flecs

声明模块依赖后,就可以在自己的BUILD文件中引用 flecs 提供的目标。仓库示例 examples/build/bazel/example/BUILD 展示了最直接的方式:

cc_binary( name = "example", srcs = ["main.cpp"], deps = ["@flecs"], copts = select({ "@bazel_tools//src/conditions:windows": ["/std:c++17"], "//conditions:default": ["-std=c++17"], }), )

要点解析:

  • deps = ["@flecs"]:@flecs即通过bazel_dep引入的外部仓库中的默认目标。由于根目录 BUILD 将cc_library命名为flecs并设置了visibility = ["//visibility:public"],它对外部工程完全可见,可直接出现在deps中;
  • copts使用select()按平台指定 C++17:这与 flecs 自身 BUILD 的编译选项保持一致,确保你的二进制与 flecs 使用相同的语言标准;
  • cc_binary会把你编写的main.cpp与 flecs 静态库链接为可执行文件。

四、运行示例:bazel run //example

仓库在 examples/build/bazel/example/main.cpp 提供了一个完整的 C++ ECS 演示程序,验证了依赖链路可用。在你的 Bazel 工程目录(包含上面BUILD文件的目录)执行:

bazel run //example

Bazel 会自动完成依赖解析、从 Git 拉取 flecs 源码、编译并运行。该示例程序本身演示了 Flecs 最核心的用法,可作为你上手 ECS 的模板:

#include "flecs.h" #include <iostream> /* Component types */ struct Position { double x, y; }; struct Velocity { double x, y; }; int main(int argc, char *argv[]) { flecs::world world(argc, argv); world.system<Position, const Velocity>() .each([](flecs::entity e, Position& p, const Velocity& v) { p.x += v.x; p.y += v.y; std::cout << "Moved " << e.name() << " to {" << p.x << ", " << p.y << "}" << std::endl; }); world.entity("MyEntity") .set<Position>({0, 0}) .set<Velocity>({1, 1}); world.set_target_fps(1); std::cout << "Application move_system is running, press CTRL-C to exit..." << std::endl; /* Run systems */ while (world.progress()) { } }

运行后可观察到:系统每一帧把MyEntity的Position按Velocity累加并打印坐标。这里值得注意的 C++ API 细节包括:

  • world.system<Position, const Velocity>()声明了一个订阅Position与只读Velocity的系统,const限定表达了组件只读语义;
  • .each(...)以 lambda 形式对每个匹配实体执行逻辑;
  • world.entity("MyEntity").set<Position>({0, 0}).set<Velocity>({1, 1})创建带命名实体并初始化两个组件;
  • world.set_target_fps(1)将帧率限制为 1 FPS,方便观察逐步移动。

五、通过 Renovate + CI 保持依赖自动更新

使用git_override锁定 commit 后,flecs 版本更新需要手动修改 commit。指南推荐结合 Renovate 与 CI 实现自动化:Renovate 检测到 flecs 上游新提交后自动发起更新 PR,CI 验证构建,必要时自动合并。

第一步:为 bazel_dep 显式声明版本属性。

bazel_dep(name = "flecs") git_override( module_name = "flecs", remote = "https://github.com/SanderMertens/flecs.git", commit = "02c8c2666b22ccce5706c8f16efaf813704fe31e", )

虽然 README 中的示例没有为bazel_dep写version,但指南明确指出:为了让 Renovate 能追踪更新,必须在bazel_dep上声明版本属性(例如bazel_dep(name = "flecs", version = "4.0.0"))。Renovate 据此判断当前使用版本并比对上游最新版本,进而自动生成升级 PR。

第二步:配置 Renovate。

在仓库的.github/renovate.json5中加入如下配置:

{ "extends": ["config:recommended"], "dependencyDashboard": true, "automerge": true, }

参数含义:

  • "extends": ["config:recommended"]:采用 Renovate 官方推荐的预设规则;
  • "dependencyDashboard": true:启用 Dependency Dashboard,在仓库 Issue 中集中展示所有待更新依赖及其状态,便于人工介入;
  • "automerge": true:当更新 PR 通过 CI 检查后自动合并。若你的 CI 使用 merge queue 管理合并,Renovate 会配合 merge queue 自动完成 CI 触发与合并流程,实现依赖升级的全自动化。

六、源码级验证:flecs 的 Bazel 目标结构

为了让你对“@flecs 到底构建了什么”有清晰认知,这里把仓库中与 Bazel 相关的核心文件串联起来:

文件作用
MODULE.bazelflecs 自身的 Bazel 模块声明(module(name = "flecs")),同时用git_repository引入构建测试所需的bake依赖
BUILD定义对外公开的flecscc_library 目标:源码 glob 覆盖src/**/*.c、src/**/*.h、src/**/*.inl,头文件覆盖include/**/*.h、include/**/*.hpp、include/**/*.inl,includes = ["include"]提供头文件搜索路径,并按平台启用 C++17
examples/build/bazel/MODULE.bazel官方示例模块:声明flecs与rules_cc依赖,并通过git_override锁定具体 commit
examples/build/bazel/example/BUILD官方示例可执行目标:cc_binary,deps = ["@flecs"]
examples/build/bazel/example/main.cpp示例 ECS 程序:Position/Velocity 组件 + 移动系统 + 实体创建 + 主循环
WORKSPACE.bzlmod兼容 Bzlmod 模式的 WORKSPACE 存根

从 BUILD 的srcs = glob(["src/**/*.c", ...])可以看出,flecs 的 Bazel 目标采用全量源码编译方式:它与 CMake 构建(见 CMakeLists.txt)使用同一份src/与include/源码树,保证了不同构建系统下行为一致。Flecs 是单一头文件 API(include/flecs.h)与多源文件实现的组合结构,因此 Bazel 目标的hdrs会同时包含include/**/*.hpp与*.inl,确保 C++ API 的内联实现也能被正确打包。

七、常见问题与注意事项

  1. 必须使用 Bzlmod 模式:本方案基于MODULE.bazel的 Bzlmod 依赖解析,请确认 Bazel 版本支持 Bzlmod(Bazel 6+ 默认启用,Bazel 7 完全默认)。老式WORKSPACE工程可参考 WORKSPACE.bzlmod 迁移;
  2. git_override的 commit 必须存在:commit 值02c8c2666b22ccce5706c8f16efaf813704fe31e是示例锁定的版本,实际使用时应替换为你验证过的最新提交,以保证可复现构建;
  3. C++17 一致性:示例BUILD与 flecs 自身BUILD都通过select()设置 C++17,若你的目标使用更低标准,需自行调整copts,但应知晓 Flecs 的 C++ API 依赖 C++17 特性;
  4. 版本属性与 Renovate:若计划接入 Renovate 自动升级,务必在bazel_dep上声明version属性,否则 Renovate 无法识别当前版本、无法生成升级 PR;
  5. include 路径:示例源码中直接#include "flecs.h",因为 flecs 目标的includes = ["include"]已把 flecs 的 include 目录加入搜索路径,flecs.h实际位于 include/flecs.h。

结语

通过 Bzlmod 的bazel_dep+git_override组合,Flecs ECS 可以被干净、可复现地接入任何 Bazel 工程:依赖声明只需数行配置,@flecs目标直接提供完整 C/C++ API,配合bazel run //example即可快速验证。在此基础上叠加 Renovate 与 CI,还能实现依赖的持续自动升级,让 ECS 基础设施的维护成本降到最低。仓库中 examples/build/bazel 目录提供了可直接参考的完整示例,是上手 Bazel + Flecs 的最佳起点。

  • 游戏开发

【免费下载链接】flecs

A fast entity component system (ECS) for C & C++

项目地址:https://gitcode.com/gh_mirrors/fl/flecs
点击查看免费下载
上一篇:Sunshine游戏串流终极指南:5步打造你的家庭游戏中心
下一篇:Wasp 应用部署指南:理解 Client / Server / Database 三部分结构并选择部署方案

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询