在 Mojo 开源仓库中使用 Bazel 构建编译器、运行代码与调试工具链
【免费下载链接】mojoThe Modular Platform (includes MAX & Mojo)项目地址: https://gitcode.com/GitHub_Trending/mo/mojo
本文是基于 Modular 平台开源仓库(Mojo 编程语言与 MAX 运行时)的开发者指南,重点讲解如何在开源仓库中借助 Bazel 完成 Mojo 编译器的本地构建、单文件运行、标准库测试以及kgen/kgen-translate等底层工具的使用。读完本文,你将掌握build-mojo与prebuilt-mojo两种构建模式的取舍、bazelw包装脚本与常用别名、从源码构建编译器到运行任意.mojo文件的完整命令链,并能准确区分哪些 monorepo 工作流在开源仓库中不可用。
背景:开源仓库与 Modular monorepo 的差异
Mojo/docs/compiler/目录下的多数文档描述的是 Modular 内部 monorepo 的工具与工作流,它们与开源仓库的用法并不完全一致。WorkingInOSRepo.md 就是专门为在开源仓库中工作的人写的“差异说明”:哪些命令可以直接使用、哪些需要额外加参数、哪些目标完全不存在。本文以该文档为骨架,结合仓库中的 Bazel 配置与源码,给出可直接复制的实操命令。
在开始之前,请确保本机已具备 Bazel 工作所需的基础环境:macOS 上需要较新版本的 Xcode 或 Command Line Tools,Linux 上需要对应的 C/C++ 工具链(详见 bazel/docs/usage.md)。仓库根目录的 bazelw 脚本会自动下载受支持的 Bazel 版本(当前为 Bazelisk 1.27.0),并在首次运行时校验 SHA 校验和,因此你不需要自行安装 Bazel。
使用 Bazel:两种构建模式
Modular 的构建系统基于 Bazel,整个仓库的构建操作都通过根目录的bazelw脚本转发给 Bazel。构建 Mojo 相关目标时,bazel以--config=<mode>区分两种模式:
| 模式 | 含义 | 适用场景 |
|---|---|---|
build-mojo | 需要时从源码构建 Mojo 编译器 | 修改 Mojo 编译器本身(Parser、LIT/KGEN Dialect、Pass 等) |
prebuilt-mojo | 使用预构建的mojo包 | 只修改 Mojo 标准库或 MAX 加速器库 |
这两种模式的实际开关定义在仓库根目录的 .bazelrc 中:
build:build-mojo --//:use_prebuilt_mojo_toolchain=false build:prebuilt-mojo --//:use_prebuilt_mojo_toolchain=true也就是说,build-mojo让 Bazel 使用仓库源码编译出的工具链,prebuilt-mojo则切换到预构建工具链。需要特别注意的是:prebuilt-mojo模式下 Bazel 会自动下载当前 nightly 版本的 mojo,而不是你机器上全局安装的版本,因此测试结果可能与本地环境略有差异。
从源码构建 Mojo 编译器耗时较长,如果只是修改标准库代码,使用prebuilt-mojo会快得多(bazel/docs/usage.md 对此有明确建议)。从源码结构看,//Mojo:mojo只是一个指向 Mojo/tools/mojo/BUILD.bazel 中mojo-full目标的别名(见 Mojo/BUILD.bazel),该二进制打包了mojo驱动及其全部子命令(build、run、debug、doc、format、precompile、repl、demangle等,由driver_option_tablegen为每个子命令生成选项表),并链接了@mojo//:std标准库依赖。
固化构建配置:local.bazelrc
每次都敲--config=build-mojo很啰嗦。可以在仓库根目录创建一个local.bazelrc(该文件已被.gitignore忽略,不会污染仓库):
build --config=build-mojo这样后续所有bazel命令都会自动带上该配置。注意 .bazelrc 在文件末尾通过try-import %workspace%/local.bazelrc引入本地配置,因此本地配置可以覆盖默认值。
Bazel 别名速查
Mojo 编译器文档中大量使用以下别名指代常见 Bazel 命令,其中<REPO_PATH>是本地仓库根目录的绝对路径:
| 别名 | 等价命令 |
|---|---|
bb | <REPO_PATH>/bazelw build |
br | <REPO_PATH>/bazelw run |
bt | <REPO_PATH>/bazelw test |
即使你不定义这些别名,记住它们也有助于阅读后续的编译器开发文档(如 Passes and IR 相关章节)。
构建与运行的三个核心命令
1. 构建 Mojo 编译器与标准库
./bazelw build --config=build-mojo //Mojo:mojo该命令会从源码编译 Mojo 编译器及标准库,产出物位于bazel-bin目录。//Mojo:mojo是仓库顶层对外暴露的编译器入口目标。
2. 用本地构建的编译器运行单个 Mojo 文件
./bazelw run --config=build-mojo //Mojo:mojo -- run main.mojo--之后的内容会透传给mojo可执行程序本身:run main.mojo等价于直接执行mojo run main.mojo。这是验证编译器改动最直接的方式——你修改的每一行编译器代码都会反映在本次运行中。
3. 构建并运行标准库测试
./bazelw test --config=build-mojo //Mojo/stdlib/...//Mojo/stdlib/...是一个递归通配目标,会展开为Mojo/stdlib目录下所有可测试的 Bazel 目标。标准库的测试源码位于 Mojo/stdlib/test/,其 BUILD 文件中的测试规则(mojo_test、mojo_filecheck_test)定义了如何编译执行这些测试;mojo_test使用testing模块做断言(更易调试),mojo_filecheck_test则用 FileCheck 校验输出(详见 bazel/docs/usage.md)。
配置别名后的简化形式
若已在local.bazelrc中固化build-mojo并定义了bb/br/bt别名,上述三条命令可简化为:
bb //Mojo:mojo br //Mojo:mojo -- run main.mojo bt //Mojo/stdlib/...⚠️ 重要限制:本地编译器不能构建 MAX 目标
[!NOTE] 你不能用本地构建的编译器去构建任何 MAX 目标,请改用预构建编译器(
--config=prebuilt-mojo)。另外,在提交 PR 之前,务必用预构建编译器再测试一遍你的标准库改动。
原因在于:从源码构建的编译器对应的是仓库当前 checkout 的版本,而 MAX(Modular Accelerated Xecution 运行时与模型服务)目标依赖的是 nightly 预构建工具链的稳定行为。因此,涉及 MAX 的构建一律使用--config=prebuilt-mojo,这也是 bazel/docs/usage.md 中推荐的标准库开发模式。
使用 kgen / kgen-translate 等底层工具
Mojo 还提供若干底层工具用于查看编译器生成的中间表示(IR),其中最有代表性的是kgen和kgen-translate。这些工具对应Mojo/tools/下的独立 Bazel 目标,例如 Mojo/tools/kgen-translate/BUILD.bazel 将kgen-translate构建为一个链接了 MojoParser、LITDialect、KGENDialect、Transforms 等编译器组件的二进制。
为什么需要 -I Mojo/stdlib
在 monorepo 内部,这些工具可以直接引用已安装的标准库;而在开源仓库中,标准库位于仓库内,因此运行这些工具时需要显式加上-I Mojo/stdlib标志,把标准库源码目录加入搜索路径。
以 Passes and Intermediate Representations 中生成 LIT Dialect 的指令为例,monorepo 的写法是:
br //Mojo/tools/kgen-translate -- -import-mojo main.mojo开源仓库的等价写法是:
./bazelw run //Mojo/tools/kgen-translate -- -import-mojo -I Mojo/stdlib main.mojo该命令不会为你构建标准库——-I Mojo/stdlib只是让工具能找到标准库源码进行解析。因此请先按上文“构建 Mojo 编译器与标准库”一节构建好标准库,再运行本命令。
命令成功后,main.mojo会被解析为lit方言的 MLIR 输出。以def foo(arg: Int): pass加def main(): foo(5)为例,你会看到lit.fn @"main()"、kgen.param.constant等高层 IR 结构(详见 Mojo/docs/compiler/manual/PassesAndIR.md),这是理解 Mojo 编译管线(Parse → LIT → KGEN → LLVM)的入口。
开源仓库中不支持的工作流
monorepo 中存在一些在开源仓库里没有对应实现的目标与流程,阅读文档时如果看到以下引用,请知悉其状态:
| 名称 | monorepo 中的作用 | 开源仓库中的状态 |
|---|---|---|
start-modular.sh | monorepo 环境初始化脚本 | 无等价物,不要试图寻找或运行 |
//:install | 构建并安装目标,创建有状态的开发环境,把构建产物安装进PATH以便直接运行工具 | 不支持。可以从构建输出目录(bazel-bin)手动拷贝产物到PATH中的某个目录,但每次重新构建编译器后都必须手动更新这些拷贝 |
换句话说,开源仓库的推荐做法是始终通过./bazelw run //Mojo:mojo -- ...来使用你构建的编译器,而不是把bazel-bin里的二进制“安装”成系统命令。
下一步:深入编译器内部
本文覆盖了在开源仓库中构建、运行与测试 Mojo 的完整工作流。接下来可以继续阅读:
- Mojo Compiler Walkthrough:编译器整体架构总览,从 Parsing/Type Checking 到 Elaboration、Lowering to LLVM 的六个阶段;
- Mojo Compiler Dev Manual:面向编译器修改者的开发手册,其中 Passes and Intermediate Representations 详细讲解 LIT、KGEN、LLVM 各层 IR 与 Pass 的转换关系;
- bazel/docs/usage.md:仓库级 Bazel 使用指南,涵盖
//:repro一次性脚本、linter 运行(//:lint、//:format)以及mojo_library/mojo_binary/mojo_test等 BUILD 规则的定义方式。
掌握本文的命令后,你就可以在开源仓库中自由迭代 Mojo 编译器与标准库代码,并通过kgen-translate观察每一次改动对中间表示的影响了。
【免费下载链接】mojoThe Modular Platform (includes MAX & Mojo)项目地址: https://gitcode.com/GitHub_Trending/mo/mojo
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考