MoonBit 构建系统教程#
Moon 是 MoonBit 语言的构建系统,目前基于 n2 项目。Moon 支持并行和增量构建。此外,moon 还在 mooncakes.io 上管理和构建第三方包。
先决条件#
在开始本教程之前,请确保你已安装以下内容:
MoonBit CLI 工具:从 https://www.moonbitlang.cn/download/ 下载。这个命令行工具用于创建和管理 MoonBit 项目。
使用
moon help查看使用说明。$ moon help ...
Visual Studio Code 中的 MoonBit 语言 插件:你可以从 VS Code 市场安装。此插件为 MoonBit 提供了丰富的开发环境,包括语法高亮、代码补全等功能。
一旦你满足了这些先决条件,我们就可以开始创建一个新的 MoonBit 模块。
创建一个新模块#
要创建一个新模块,在终端中输入 moon new <path> 命令,其中 path 是你想要创建项目的文件夹目录,之后项目会被创建。使用默认值,你会在 my_project 目录中创建一个名为 username/my_project 的新模块。
$ moon new my_project
Initialized empty Git repository in my_project/.git/
Created username/my_project at my_project
你也可以通过使用 --user 选项和 --name 选项分别指定用户名和模块名称。如果你已经登录,用户名将默认为你的用户名。
理解模块目录结构#
创建新模块后,你的目录结构应该如下:
my_project
├── Agents.md
├── cmd
│ └── main
│ ├── main.mbt
│ └── moon.pkg
├── LICENSE
├── moon.mod.json
├── moon.pkg
├── my_project_test.mbt
├── my_project.mbt
├── README.mbt.md
└── README.md -> README.mbt.md
备注
在 Windows 系统上,你需要管理员权限或启用开发者模式才能创建符号链接。
以下是目录结构的简要说明:
moon.mod.json用于标识目录为 MoonBit 模块。它包含模块的元数据,如模块名称、版本等。{ "name": "username/my_project", "version": "0.1.0", "readme": "README.md", "repository": "", "license": "Apache-2.0", "keywords": [], "description": "" }
.和cmd/main目录:这些是模块中的包。每个包可以包含多个.mbt文件,这些文件是 MoonBit 语言的源代码文件。但是,无论一个包有多少.mbt文件,它们都共享同一个moon.pkg文件。较旧的项目仍然可能使用遗留的moon.pkg.json格式。*_test.mbt是包中的独立测试文件,这些文件用于黑盒测试,因此同一个包的私有成员不能直接访问。moon.pkg是包描述符。它定义了包的属性,例如它是否是 main 包以及它导入的包。cmd/main/moon.pkg:import { "username/my_project" @lib, } options( "is-main": true, )
这里,
"is-main": true表示该包包含moon run命令的入口。moon.pkg:这个文件可以是空的。它的作用只是告诉构建系统这个文件夹是一个包。
README.mbt.md是 README 文件。在这个文件里,mbt check代码块会由moon check和moon test进行检查和运行。
使用包#
我们的 username/my_project 模块包含两个包:username/my_project 和 username/my_project/cmd/main。
username/my_project 包包含 my_project.mbt 和 my_project_test.mbt 文件:
///|
pub fn fib(n : Int) -> Int64 {
for i = 0, a = 0L, b = 1L; i < n; i = i + 1, a = b, b = a + b {
} else {
b
}
}
///|
test "fib" {
let array = [1, 2, 3, 4, 5].map(fib(_))
// `inspect` is used to check the output of the function
// Just write `inspect(value)` and execute `moon test --update`
// to update the expected output, and verify them afterwards
inspect(array, content="[1, 2, 3, 5, 8]")
}
备注
生成的文件名取决于包名。
username/my_project/cmd/main 包包含一个 main.mbt 文件:
///|
fn main {
println(@lib.fib(10))
}
要执行程序,在 moon run 命令中指定文件系统路径到 username/my_project/cmd/main 包:
$ moon run cmd/main
89
你可以使用 moon test 命令进行测试:
$ moon test
Total tests: 1, passed: 1, failed: 0.
选择目标后端#
Moon 有三个与目标后端相关的配置项,它们分别负责不同的事情:
命令行上的
--target用于选择当前命令使用哪个后端moon.mod.json中的preferred-target用于为moon和语言服务器选择默认后端supported-targets用于声明一个模块或包打算支持哪些后端
例如,一个以 native 为主的 CLI 项目可以这样设置:
{
"preferred-target": "native",
"supported-targets": "native"
}
supported-targets 使用目标集合语法,例如 js、+js+wasm-gc 或 +all-js。
如果只有部分文件与后端相关,就让模块或包的元数据保持宽泛,并使用 moon.pkg 或遗留的 moon.pkg.json 中的 targets 按后端选择文件。
包导入#
在 MoonBit 构建系统中,依赖是在包级别声明的。要在 username/my_project/cmd/main 中导入 username/my_project 包,你需要在 cmd/main/moon.pkg 中指定:
import {
"username/my_project" @lib,
}
options(
"is-main": true,
)
这里,"username/my_project" 表示导入根包,并为它指定别名 lib,因此你可以在 cmd/main/main.mbt 中使用 @lib.fib(10)。
创建和使用新包#
首先,在模块根目录下创建一个名为 fib 的新目录:
mkdir fib
现在,你可以在 fib 下创建新文件:
pub fn fib_slow(n : Int) -> Int {
match n {
0 => 0
1 => 1
_ => fib_slow(n - 1) + fib_slow(n - 2)
}
}
pub fn fib_fast(num : Int) -> Int {
fn aux(n, acc1, acc2) {
match n {
0 => acc1
1 => acc2
_ => aux(n - 1, acc2, acc1 + acc2)
}
}
aux(num, 0, 1)
}
// This package does not need extra options yet.
在创建这些文件后,你的目录结构应该如下:
.
├── Agents.md
├── cmd
│ └── main
│ ├── main.mbt
│ └── moon.pkg
├── fib
│ ├── fast.mbt
│ ├── moon.pkg
│ └── slow.mbt
├── LICENSE
├── moon.mod.json
├── moon.pkg
├── my_project_test.mbt
├── my_project.mbt
├── README.mbt.md
└── README.md -> README.mbt.md
在 cmd/main/moon.pkg 文件中,导入 username/my_project/fib 包并将其别名定制为 my_awesome_fibonacci:
import {
"username/my_project/fib" @my_awesome_fibonacci,
}
options(
"is-main": true,
)
这会导入 fib 包。完成之后,你就可以在 cmd/main/main.mbt 中使用 fib 包了。将 cmd/main/main.mbt 的内容替换为:
fn main {
let a = @my_awesome_fibonacci.fib_slow(10)
let b = @my_awesome_fibonacci.fib_fast(11)
println("fib(10) = \{a}, fib(11) = \{b}")
}
为了执行你的程序,指定 main 包的路径:
$ moon run cmd/main
fib(10) = 55, fib(11) = 89
添加测试#
让我们添加一些测试来验证我们的 fib 实现。在 fib/fib_test.mbt 中添加以下内容:
test {
inspect(@fib.fib_slow(0))
inspect(@fib.fib_slow(1))
inspect(@fib.fib_slow(2))
}
这段代码测试斐波那契数列的前三项。test { ... } 定义了一个内联测试块。内联测试块中的代码在测试模式下执行。
内联测试块在非测试编译模式(moon build 和 moon run)中被丢弃,因此它们不会导致生成的代码大小膨胀。
这里我们使用了快照测试。执行 moon test --update,文件应该被更改为:
test {
inspect(@fib.fib_slow(0), content="0")
inspect(@fib.fib_slow(1), content="1")
inspect(@fib.fib_slow(2), content="1")
}
注意,测试代码使用 @fib 来引用 fib 包。构建系统通过使用以 _test.mbt 结尾的文件自动为黑盒测试创建一个新包。
最后,使用 moon test 命令,它会扫描整个项目,识别并运行所有测试。如果一切正常,你将看到:
$ moon test
Total tests: 2, passed: 2, failed: 0.