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
命令,你将看到模块创建向导。通过使用所有默认值,你可以在 my-project
目录中创建一个名为 username/hello
的新模块。
$ moon new
Enter the path to create the project (. for current directory): my-project
Select the create mode: exec
Enter your username: username
Enter your project name: hello
Enter your license: Apache-2.0
Created my-project
如果你想使用所有默认值,你可以使用
moon new my-project
在my-project
目录中创建一个名为username/hello
的新模块。
理解模块目录结构#
创建新模块后,你的目录结构应该如下:
my-project
├── LICENSE
├── README.md
├── moon.mod.json
└── src
├── lib
│ ├── hello.mbt
│ ├── hello_test.mbt
│ └── moon.pkg.json
└── main
├── main.mbt
└── moon.pkg.json
以下是目录结构的简要说明:
moon.mod.json
用于标识目录为 MoonBit 模块。它包含模块的元数据,如模块名称、版本等。source
指定模块的源目录。默认值是src
。{ "name": "username/hello", "version": "0.1.0", "readme": "README.md", "repository": "", "license": "Apache-2.0", "keywords": [], "description": "", "source": "src" }
lib
和main
目录:这些是模块中的包。每个包可以包含多个.mbt
文件,这些文件是 MoonBit 语言的源代码文件。但是,无论一个包有多少.mbt
文件,它们都共享一个公共的moon.pkg.json
文件。lib/*_test.mbt
是lib
包中的单独测试文件,这些文件用于黑盒测试,因此lib
包的私有成员不能直接访问。moon.pkg.json
是包描述符。它定义了包的属性,例如它是否是 main 包以及它导入的包。main/moon.pkg.json
:{ "is_main": true, "import": [ "username/hello/lib" ] }
这里,
"is_main: true"
声明该包需要被构建系统链接为一个 Wasm 文件。lib/moon.pkg.json
:{}
此文件为空。它的目的只是告诉构建系统这个文件夹是一个包。
使用包#
我们的 username/hello
模块包含两个包:username/hello/lib
和 username/hello/main
。
username/hello/lib
包包含 hello.mbt
和 hello_test.mbt
文件:
hello.mbt
pub fn hello() -> String {
"Hello, world!"
}
hello_test.mbt
test "hello" {
if @lib.hello() != "Hello, world!" {
fail!("@lib.hello() != \"Hello, world!\"")
}
}
username/hello/main
包包含一个 main.mbt
文件:
fn main {
println(@lib.hello())
}
要执行程序,在 moon run
命令中指定文件系统路径到 username/hello/main
包:
$ moon run ./src/main
Hello, world!
你也可以省略 ./
$ moon run src/main
Hello, world!
你可以使用 moon test
命令进行测试:
$ moon test
Total tests: 1, passed: 1, failed: 0.
包导入#
在 MoonBit 的构建系统中,模块的名称用于引用其内部包。要在 src/main/main.mbt
中导入 username/hello/lib
包,你需要在 src/main/moon.pkg.json
中指定:
{
"is_main": true,
"import": [
"username/hello/lib"
]
}
这里,username/hello/lib
指定从 username/hello
模块导入 username/hello/lib
包,所以你可以在 main/main.mbt
中使用 @lib.hello()
。
请注意,src/main/moon.pkg.json
中导入的包名是 username/hello/lib
,在 src/main/main.mbt
中使用 @lib
来引用这个包。这里的导入实际上为包名 username/hello/lib
生成了一个默认别名。在接下来的章节中,你将学习如何为包定制别名。
创建和使用新包#
首先,在 lib
下创建一个名为 fib
的新目录:
mkdir src/lib/fib
现在,你可以在 src/lib/fib
下创建新文件:
a.mbt
:
pub fn fib(n : Int) -> Int {
match n {
0 => 0
1 => 1
_ => fib(n - 1) + fib(n - 2)
}
}
b.mbt
:
pub fn fib2(num : Int) -> Int {
fn aux(n, acc1, acc2) {
match n {
0 => acc1
1 => acc2
_ => aux(n - 1, acc2, acc1 + acc2)
}
}
aux(num, 0, 1)
}
moon.pkg.json
:
{}
在创建这些文件后,你的目录结构应该如下:
my-project
├── LICENSE
├── README.md
├── moon.mod.json
└── src
├── lib
│ ├── fib
│ │ ├── a.mbt
│ │ ├── b.mbt
│ │ └── moon.pkg.json
│ ├── hello.mbt
│ ├── hello_test.mbt
│ └── moon.pkg.json
└── main
├── main.mbt
└── moon.pkg.json
在 src/main/moon.pkg.json
文件中,导入 username/hello/lib/fib
包并将其别名定制为 my_awesome_fibonacci
:
{
"is_main": true,
"import": [
"username/hello/lib",
{
"path": "username/hello/lib/fib",
"alias": "my_awesome_fibonacci"
}
]
}
这一行导入了 fib
包,它是 hello
模块中 lib
包的一部分。在这样做之后,你可以在 main/main.mbt
中使用 fib
包。
fn main {
let a = @my_awesome_fibonacci.fib(10)
let b = @my_awesome_fibonacci.fib2(11)
println("fib(10) = \{a}, fib(11) = \{b}")
println(@lib.hello())
}
为了执行你的程序,指定 main 包的路径:
$ moon run ./src/main
fib(10) = 55, fib(11) = 89
Hello, world!
添加测试#
让我们添加一些测试来验证我们的 fib 实现。在 src/lib/fib/a.mbt
中添加以下内容:
src/lib/fib/a.mbt
test {
assert_eq!(fib(1), 1)
assert_eq!(fib(2), 1)
assert_eq!(fib(3), 2)
assert_eq!(fib(4), 3)
assert_eq!(fib(5), 5)
}
这段代码测试斐波那契数列的前五项。test { ... }
定义了一个内联测试块。内联测试块中的代码在测试模式下执行。
内联测试块在非测试编译模式(moon build
和 moon run
)中被丢弃,因此它们不会导致生成的代码大小膨胀。
黑盒测试的独立测试文件#
除了内联测试,MoonBit 还支持独立测试文件。以 _test.mbt
结尾的源文件被视为黑盒测试的测试文件。例如,在 src/lib/fib
目录中,创建一个名为 fib_test.mbt
的文件,并粘贴以下代码:
src/lib/fib/fib_test.mbt
test {
assert_eq!(@fib.fib(1), 1)
assert_eq!(@fib.fib2(2), 1)
assert_eq!(@fib.fib(3), 2)
assert_eq!(@fib.fib2(4), 3)
assert_eq!(@fib.fib(5), 5)
}
注意,测试代码使用 @fib
来引用 username/hello/lib/fib
包。构建系统通过使用以 _test.mbt
结尾的文件自动为黑盒测试创建一个新包。这个新包将自动导入当前包,允许你在测试代码中使用 @lib
。
最后,使用 moon test
命令,它会扫描整个项目,识别并运行所有内联测试以及以 _test.mbt
结尾的文件。如果一切正常,你将看到:
$ moon test
Total tests: 3, passed: 3, failed: 0.
$ moon test -v
test username/hello/lib/hello_test.mbt::hello ok
test username/hello/lib/fib/a.mbt::0 ok
test username/hello/lib/fib/fib_test.mbt::0 ok
Total tests: 3, passed: 3, failed: 0.