MoonBit's Build System Tutorial
Moon is the build system for the MoonBit language, currently based on the n2 project. Moon supports parallel and incremental builds. Additionally, moon also supports managing and building third-party packages on mooncakes.io
Prerequisites
Before you begin with this tutorial, make sure you have installed the following:
-
MoonBit CLI Tools: Download it from the https://www.moonbitlang.com/download/. This command line tool is needed for creating and managing MoonBit projects.
Use
moon help
to view the usage instructions.$ moon help
Moonbit's build system
Usage: moon <COMMAND>
Commands:
build Build the current package
check Check the current package, but don't build object files
run Run WebAssembly module
clean Remove the target directory
new Create a new moonbit package
bench Generate build matrix for benchmarking
fmt Format moonbit
version Print version info and exit
test Run the tests
login Log in to your account
register Register an account on mooncakes.io
publish Publish the current package
add Add a new dependency
remove Remove a dependency
tree Display the dependency tree
update Update index
doc Generate documentation
install Install dependencies
help Print this message or the help of the given subcommand(s)
Options:
-h, --help Print help -
Moonbit Language plugin in Visual Studio Code: You can install it from the VS Code marketplace. This plugin provides a rich development environment for MoonBit, including functionalities like syntax highlighting, code completion, and more.
Once you have these prerequisites fulfilled, let's start by creating a new MoonBit module.
Creating a New Module
To create a new module, enter the moon new
command in the terminal, and you will see the module creation wizard. By using all the default values, you can create a new module named hello
in the my-project
directory.
$ 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
Understanding the Module Directory Structure
After creating the new module, your directory structure should resemble the following:
my-project
├── README.md
├── lib
│ ├── hello.mbt
│ ├── hello_test.mbt
│ └── moon.pkg.json
├── main
│ ├── main.mbt
│ └── moon.pkg.json
└── moon.mod.json
Here's a brief explanation of the directory structure:
-
lib
andmain
directories: These are the packages within the module. Each package can contain multiple.mbt
files, which are the source code files for the MoonBit language. However, regardless of how many.mbt
files a package has, they all share a commonmoon.pkg.json
file.lib/*_test.mbt
are separate test files in thelib
package, where private members of thelib
package can be accessed directly. These files are only included in the compilation in test mode, allowing for inline tests and utility functions for testing purposes to be written within these separate test files. -
moon.pkg.json
is package descriptor. It defines the properties of the package, such as whether it is the main package and the packages it imports.-
main/moon.pkg.json
:{
"is_main": true,
"import": [
"username/hello/lib"
]
}
Here, "is_main: true" declares that the package needs to be linked by the build system into a wasm file.
-
lib/moon.pkg.json
:{}
This file is empty. Its purpose is simply to inform the build system that this folder is a package.
-
-
moon.mod.json
is used to identify a directory as a MoonBit module. It contains the module's name:{
"name": "username/hello",
"version": "0.1.0",
"readme": "README.md",
"repository": "",
"license": "Apache-2.0",
"keywords": [],
"description": ""
}
Working with Packages
Our username/hello
module contains two packages: lib
and main
.
The lib
package contains hello.mbt
and hello_test.mbt
files:
hello.mbt
pub fn hello() -> String {
"Hello, world!"
}
hello_test.mbt
test "hello" {
if hello() != "Hello, world!" {
return Err("hello() != \"Hello, world!\"")
}
}
The main
package contains a main.mbt
file:
fn main {
println(@lib.hello())
}
To execute the program, specify the path to the main
package in the moon run
command:
$ moon run ./main
Hello, world!
You can also omit ./
$ moon run main
Hello, world!
You can test using the moon test
command:
$ moon test
Total tests: 1, passed: 1, failed: 0.
Package Importing
In the MoonBit's build system, a module's name is used to reference its internal packages.
To import the lib
package in main/main.mbt
, you need to specify it in main/moon.pkg.json
:
{
"is_main": true,
"import": [
"username/hello/lib"
]
}
Here, username/hello/lib
specifies importing the username/hello/lib
package from the username/hello
module, so you can use @lib.hello()
in main/main.mbt
.
Note that the package name imported in main/moon.pkg.json
is username/hello/lib
, and @lib
is used to refer to this package in main/main.mbt
. The import here actually generates a default alias for the package name username/hello/lib
. In the following sections, you will learn how to customize the alias for a package.
Creating and Using a New Package
First, create a new directory named fib
under lib
:
mkdir lib/fib
Now, you can create new files under 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
:
{}
After creating these files, your directory structure should look like this:
my-project
├── README.md
├── lib
│ ├── fib
│ │ ├── a.mbt
│ │ ├── b.mbt
│ │ └── moon.pkg.json
│ ├── hello.mbt
│ ├── hello_test.mbt