Mojo 项目清单(Project Manifest)与构建工具:提案深度解读与仓库现状佐证
【免费下载链接】mojoThe Modular Platform (includes MAX & Mojo)项目地址: https://gitcode.com/GitHub_Trending/mo/mojo
导读
本文以仓库中的设计提案 Mojo/proposals/project-manifest-and-build-tool.md 为骨架,系统解读 Mojo 社区早期对"项目清单 + 构建工具"的构想:为什么要为 Mojo 定义一套标准化的项目描述文件,以及如何让一条命令构建任意 Mojo 项目。文章同时结合本仓库中mojo build/mojo precompile的实现、Pixi 的pixi.toml、rattler-build 打包实践与 BazelBUILD文件,梳理"提案设想"与"仓库现状"之间的差距,帮助读者理解 Mojo 项目构建与分发生态的演进脉络。
说明:该提案在文档开头标注为Status: Abandoned(已废弃),是历史上用于征集社区反馈的设计草案。本文以"提案解读"而非"现行规范"的角度展开,所有命令与配置均以仓库当前内容为准。
两个核心概念:Project Manifest 与 Build Tool
提案首先界定了两个容易被混淆的概念:
- 项目清单(Project Manifest):一个描述"构成库或可执行文件的源码文件有哪些、这些文件应如何构建、分发以及被语言工具(如语言服务器、调试器)交互"的文件。提案给出的既有生态例子包括 Rust 的
Cargo.toml、Python 的setup.py,以及语言无关的 Bazel 及其BUILD文件。 - 构建工具(Build Tool):一个能根据项目清单产出库或可执行文件的程序。例如
cargo build会依据Cargo.toml编译并链接 Rust 可执行文件与库;Python 生态中则有多种工具可以处理setup.py与pyproject.toml以产出 wheel 包。
在本仓库中,两种"清单"形态都能找到真实样本:
- Bazel 的
BUILD文件遍布全仓库,例如 Mojo/examples/BUILD.bazel、Mojo/stdlib/std/BUILD.bazel,它们以声明式规则描述 Mojo 目标的构建方式,正是"项目清单"的典型代表; - Pixi 的
pixi.toml是当前仓库推荐的依赖与环境清单,例如 Mojo/examples/life/pixi.toml,它记录了通道(channels)与依赖版本。
提案动机:Mojo SDK v0.7.0 时代的构建现状
提案撰写时(Mojo SDK v0.7.0 时期),Mojo 尚不存在任何项目清单格式。彼时构建一个 Mojo 项目只有两种命令行方式:
mojo precompile:把源码目录编译为预编译的.mojoc文件;mojo build:把源码编译为可执行文件。
这一现状在提案中归纳出两个主要缺陷:
缺陷一:缺乏"从源码构建 Mojo 项目"的标准。提案观察当时 GitHub 上流行的 Mojo 项目,构建方式五花八门:有的用 Docker,Dockerfile 里执行mojo run;有的用 CMake,维护者通过add_custom_command调用mojo build与mojo precompile;有的把mojo precompile命令只写进 README;还有的构建步骤只有维护者本人知道。这种碎片化直接抑制了 Mojo 社区的协作——下载一个仓库却不知道如何构建它。
缺陷二:缺乏清单导致语言工具无法正常工作。很多 Mojo 项目使用编译期定义,例如-D ENABLE_TILING。语言服务器不知道用户实际构建时用了哪些定义,就无法复现用户在真正编译时看到的诊断信息。
仓库中的佐证:.mojoc预编译文件
本仓库对.mojoc文件的约束印证了提案中"现状需要规范化"的判断。在 Mojo/docs/site/manual/packages.md 中明确记载:
.mojoc文件包含的是未展开(non-elaborated)代码,可以跨系统共享,只有被导入到某个 Mojo 程序并经由mojo build编译后才成为架构相关的可执行文件;- 但它不是通用可分发的格式,因为它与产生它的编译器版本强绑定,用不同版本的编译器加载会直接报错;
- 包名被编码进
.mojoc文件内部,想改名必须重新运行mojo precompile,不能只改文件名。
底层实现可见 Mojo/lib/Support/MojoPrecompiledFile.cpp:加载预编译文件时会先检查MLIR 校验和是否匹配,不匹配则报告版本不兼容错误;在双方都带版本信息时,会输出 "Mojo precompiled file is incompatible with the current version" 之类的明确错误。这正是"同一份源码在不同环境构建结果不一致"的一个侧面——没有项目清单,就没有人替你记录"用哪个编译器版本、带哪些编译选项构建的"。
提案目标:一条命令构建任意 Mojo 项目
提案希望解决上述问题,目标非常明确:
- 实现单一命令,能从源码构建任何 Mojo 项目——类比
cargo build以默认设置构建任意 Rust 项目的默认目标,或zig build之于 Zig 项目; - 让语言工具读取清单中的编译器选项——因为清单会指明该使用哪些 Mojo 编译器选项,语言服务器与调试器就能据此给出与真实构建一致的诊断与补全。
第二点尤其值得展开:提案中-D ENABLE_TILING这类编译期定义是语言服务器的"盲区"。一份标准清单一旦落地,IDE 体验(诊断、跳转、补全)就能与命令行构建完全对齐,这也是许多现代语言(Rust、Zig、Swift)已经做到的事情。
指导原则:Mojo 专属、兼容生态、开放开发
提案给出了四条指导原则,至今仍有参考价值:
- 单一命令构建任意 Mojo 项目(见上文);
- 依赖下载(包管理器功能)可以后置:提案援引
zig build的历史——最初并不包含依赖下载,六年后才补上实现。Mojo 构建工具短期内实现依赖的下载与构建是可能的,但将作为单独的提案另行讨论; - 追求与既有构建系统的最佳集成:尽管清单与工具是 Mojo 专属的,但要尽量兼容 Python setuptools、Bazel、Buck2、CMake 等,能配合就配合;
- 开源开发,主要用 Mojo 编写:设计需要社区输入与贡献,因此将以主要使用 Mojo 编写的开源工具形态发展,同时这也能反哺 Mojo 标准库本身的演进。
仓库现状佐证:对既有生态的集成已经发生
"与既有构建系统集成"这一原则在仓库中已呈现为实际形态:
- Pixi / conda 通道:Mojo/docs/site/pixi.mdx 推荐用 Pixi 管理依赖与虚拟环境,所有官方示例都携带
pixi.toml;Mojo/docs/site/tools/packaging.mdx 则展示了用rattler-build基于recipe.yaml把 Mojo 项目打包成 conda 包的完整流程; - Bazel 原生支持:仓库内存在完整的 Mojo Bazel 规则集(如 Mojo/tools/mojo/BUILD.bazel、Mojo/stdlib/BUILD.bazel),
mojo_binary、mojo_library等规则直接描述 Mojo 目标的构建,正是提案所说"与语言无关清单格式"的落地样例。
征求反馈:四个关键设计决策点
提案的主体是向社区征集反馈,核心议题包括:
1. 是否采用 Build Server Protocol(BSP)
提案询问是否采纳build server protocol(构建服务器协议)。采纳它的理由与"与既有工具生态良好集成"的原则一致——BSP 允许 IDE 与构建工具解耦,语言服务器可以通过标准协议向构建工具询问编译命令与目标信息,从而拿到-D ENABLE_TILING这类选项。这与提案动机二中"语言工具需要知晓编译选项"的需求直接呼应。
2. 清单是"可执行程序"还是"纯声明式"?
这是提案中最具深度的设计权衡:类比build.zig(Zig)与Package.swift(Swift)都是"定义项目的程序",是否应定义project.mojo之类的可执行清单?
- 支持可执行清单的理由:表达能力极强,可以条件分支、动态计算依赖、按平台生成构建步骤,适合复杂项目;
- 支持纯声明式清单的理由:可预测、可静态分析、便于工具(语言服务器、包索引器)解析,也更容易被第三方构建系统消费。
提案对此持开放态度,明确表示"两边都有很多论据,也存在权衡",希望社区给出意见。这一抉择至今仍是构建系统设计的经典命题。
3. 其他开放性讨论
- 对提案动机与指导原则是否认同;
- 喜欢哪些项目清单格式与构建工具、为什么(提案表示灵感来自 Rust、Zig、Swift,尤其是 Python 生态);
- 其他任何想法——提案作者自称为"构建系统与语言工具控",邀请社区到 Discord 频道讨论。
提案落地的"影子现状":仓库今天实际怎么构建 Mojo 项目
该提案虽已废弃,但仓库现状恰好构成了一幅"替代方案全景图"。理解它们,比单纯阅读提案更能把握 Mojo 构建生态的实际情况:
mojo build的真实实现
mojo build子命令的实现位于 Mojo/tools/mojo/Build/mojo-build.cpp,其用法提示为:
Usage: mojo build --target-accelerator <arch> file.mojo从源码结构看,该命令支持--target-triple、--print-supported-targets、--print-supported-cpus、--target-accelerator等选项,并通过TargetBackendRegistry过滤"已注册 LLVM 后端且具备对应 TargetBackend 的目标",确保宣传可用的目标不会在真正构建时失败。它还包含--ignore-incompatible-precompiled-file-errors这样的容错选项,与前面提到的.mojoc版本校验逻辑相互呼应。
事实上的"项目清单":pixi.toml
当前仓库推荐每个 Mojo 项目用pixi.toml声明依赖与环境(见 Mojo/docs/site/pixi.mdx):
# 仓库示例 Mojo/examples/life/pixi.toml 采用的通道配置形态 [workspace] channels = [ "https://conda.modular.com/max-nightly", "https://repo.prefix.dev/modular-community", "conda-forge", ]常用命令序列(与提案中"单一命令构建"形成对照,目前仍需要多步操作):
pixi init example-project # 创建项目与虚拟环境 cd example-project pixi add mojo # 添加 Mojo 编译器依赖 pixi run mojo --version # 在环境中执行 mojo依赖版本可以精确指定,例如pixi add "mojo~=1.0.0" "numpy<2.0",或用通配符pixi add "mojo=*"始终取最新版。
事实上的"构建与分发":recipe.yaml+ rattler-build
Mojo/docs/site/tools/packaging.mdx 给出了 Mojo 项目的 conda 打包流程,其中的recipe.yaml实质承担了"项目清单"的职责——声明源码位置、构建命令、依赖与测试:
context: version: "0.1.0" package: name: my-mojo-lib version: ${{ version }} source: - git: https://github.com/yourname/my-mojo-lib.git rev: a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2 build: number: 0 script: - mojo precompile src/my_mojo_lib -o ${{ PREFIX }}/lib/mojo/my_mojo_lib.mojoc requirements: build: - mojo-compiler =25.5.0 host: - mojo-compiler =25.5.0 run: - ${{ pin_compatible('mojo-compiler') }} tests: - script: - if: unix then: - mojo run test.mojo files: recipe: - test.mojo about: homepage: https://github.com/yourname/my-mojo-lib license: MIT license_file: LICENSE summary: A short one-line description of what your library does.该文档给出的实践要点,恰好回扣提案中的两个动机:
- 用完整 40 位 commit SHA 而非分支/标签作为
source.rev,保证任何人从同一 recipe 构建都得到完全相同的源码——回应"下载后能复现构建"的诉求; - 在
requirements.run中用pin_compatible('mojo-compiler')约束运行期编译器版本——正是对.mojoc与编译器版本强绑定的工程化应对(参见 MojoPrecompiledFile.cpp 中的版本/校验和检查逻辑); mojo precompile的输出必须落入$PREFIX/lib/mojo/,该路径是编译器自动发现包的约定位置——某种意义上的"约定即规范"。
面向未来:提案议题与现状的对照
| 提案议题(Abandoned) | 仓库现状(可作为替代路径) |
|---|---|
| 单一命令构建任意 Mojo 项目 | mojo build/mojo precompile仍需手动组合,rattler-build build可一键完成"取源码→构建→测试→打包" |
| 标准项目清单格式 | pixi.toml(依赖/环境)、recipe.yaml(构建/分发)、BUILD.bazel(Bazel 规则)各司其职 |
| 语言服务器获取编译选项 | 尚未由统一清单驱动;BSP 议题未在仓库中落地 |
| 包管理器功能后置 | conda 通道(https://conda.modular.com/max、modular-community)已承担依赖分发 |
| 主要用 Mojo 编写构建工具 | 仓库 bazel/lint/check_licenses.mojo 等工具已展示 Mojo 编写工具的能力 |
结语
project-manifest-and-build-tool.md是一份"宣告意图 + 征集意见"的早期提案,虽然最终标记为 Abandoned,但它准确刻画了 Mojo 构建生态的核心痛点:无标准清单 → 无法从源码复现构建 → 语言工具失准。阅读它时,最值得吸收的并非某个具体方案,而是其分析方法——对比cargo、zig、swift的清单设计,审视可执行清单与声明式清单的取舍,以及"包管理器能力可以后置、工具链对齐必须前置"的优先级判断。
今天的仓库通过 Pixi、rattler-build 与 Bazel 的组合,实际上已经为"如何构建与分发 Mojo 项目"给出了社区化的答案;而提案中"单命令构建 + 工具链对齐"的理想,仍可作为理解这些生态组件设计动机的统一框架。对构建系统与语言工具链感兴趣的读者,不妨顺着 mojo-build.cpp、MojoPrecompiledFile.cpp、packaging.mdx 三条线索继续深入。
【免费下载链接】mojoThe Modular Platform (includes MAX & Mojo)项目地址: https://gitcode.com/GitHub_Trending/mo/mojo
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考