Skip to content

Commit

Permalink
Merge pull request #155 from moonbitlang/lijunchen/update-build-syste…
Browse files Browse the repository at this point in the history
…m-tutorial

update build system tutorial
  • Loading branch information
bzy-debug authored Feb 6, 2024
2 parents 4ee5d1d + ec3ddff commit 8ee845e
Show file tree
Hide file tree
Showing 2 changed files with 275 additions and 215 deletions.
249 changes: 140 additions & 109 deletions build-system-tutorial.md
Original file line number Diff line number Diff line change
@@ -1,88 +1,104 @@
# MoonBit's Build System Tutorial

Moon is the build system for the MoonBit language, currently based on the [n2](https://github.com/evmar/n2) project. Moon supports parallel and incremental builds. Additionally, moon also supports managing and building third-party packages on [mooncakes.io](mooncakes.io)

## Prerequisites

Before you begin with this tutorial, make sure you have installed the following:

1. **MoonBit CLI Tools**: Download it from the [https://www.moonbitlang.com/download/](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.

```bash
$ 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
help Print this message or the help of the given subcommand(s)

Options:
-h, --help Print help
```

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
```
2. **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 building a new module in MoonBit.
Once you have these prerequisites fulfilled, let's start by creating a new MoonBit module.
## Creating a New Module
To create a new module, use the `moon new` command in your terminal:
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.
```bash
moon new
$ 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
```

This command will launch a new module wizard. Following all default options will create a new module named `hello` in the `my-project` directory.

## Understanding the Module Directory Structure

After creating the new module, your directory structure should resemble the following:

```bash
my-project
├── README.md
├── lib
│ ├── hello.mbt
│ └── moon.pkg.json
│   ├── hello.mbt
│   ├── hello_test.mbt
│   └── moon.pkg.json
├── main
├── main.mbt
└── moon.pkg.json
   ├── main.mbt
   └── moon.pkg.json
└── moon.mod.json
```

Here's a brief explanation of the directory structure:

- `lib` and `main` directories: These are packages in the module. Each package can contain multiple `.mbt` files, which are the source code files in MoonBit language. However, regardless of the number of `.mbt` files in a package, they all share a common `moon.pkg.json` file.
- `lib` and `main` 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 common `moon.pkg.json` file. `lib/*_test.mbt` are separate test files in the `lib` package, where private members of the `lib` 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`:

```json
{
"is_main": true,
"import": {
"hello8/lib": ""
```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`:

```json
{}
```
```json
{}
```

This file is empty. Its purpose is simply to inform the build system that this folder is a package.

Expand All @@ -94,43 +110,51 @@ Here's a brief explanation of the directory structure:
}
```

## Checking Your Project

You can open your project with Visual Studio Code. After you've installed the MoonBit plugin, you can use the `moon check --watch` command in your terminal to automatically check your project.

![before watch](./imgs/before_watch.png)
## Working with Packages

After executing `moon check --watch`, VS Code should look like this.
Our `username/hello` module contains two packages: `lib` and `main`.

![after watch](./imgs/after_watch.png)
The `lib` package contains `hello.mbt` and `hello_test.mbt` files:

## Working with Packages
`hello.mbt`
```rust
pub fn hello() -> String {
"Hello, world!"
}
```

Our `hello` module contains two packages: `lib` and `main`.
`hello_test.mbt`
```rust
test "hello" {
if hello() != "Hello, world!" {
abort("")
}
}
```

The `lib` package contains a `hello.mbt` file:
The `main` package contains a `main.mbt` file:

```rust
pub fn hello() -> String {
"Hello, world!\n"
}
```
```rust
fn init {
println(@lib.hello())
}
```

The `main` package contains a `main.mbt` file:
To execute the program, specify the path to the `main` package in the `moon run` command:

```rust
fn init {
println(@lib.hello())
}
```bash
$ moon run ./main
Hello, world!
```

To execute your program, specify the path to the `main` package:
You can also omit `./`

```bash
$ moon run ./main
$ moon run main
Hello, world!
```


## Package Importing

In the MoonBit's build system, a module's name is used to reference its internal packages.
Expand All @@ -139,13 +163,15 @@ To import the `lib` package in `main/main.mbt`, you need to specify it in `main/
```json
{
"is_main": true,
"import": {
"hello/lib": ""
}
"import": [
"username/hello/lib"
]
}
```

Here, `"hello/lib": ""` specifies that the `lib` package from the `hello` module is to be imported, and `""` means that `lib` has no name alias.
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

Expand Down Expand Up @@ -194,104 +220,109 @@ pub fn fib2(num : Int) -> Int {
After creating these files, your directory structure should look like this:

```bash
.
my-project
├── README.md
├── lib
│ ├── fib
│ │ ├── a.mbt
│ │ ├── b.mbt
│ │ └── moon.pkg.json
│ ├── hello.mbt
│ ├── hello_test.mbt
│ └── moon.pkg.json
├── main
│ ├── main.mbt
│ └── moon.pkg.json
└── moon.mod.json
```

In the `main/moon.pkg.json` file, import package `hello/lib/fib`:
In the `main/moon.pkg.json` file, import the `username/hello/lib/fib` package and customize its alias to `my_awesome_fibonacci`:

```json
{
"is_main": true,
"import": {
"hello/lib": "",
"hello/lib/fib": ""
}
"import": [
"username/hello/lib",
{
"path": "username/hello/lib/fib",
"alias": "my_awesome_fibonacci"
}
]
}
```

This line imports the `fib` package, which is part of the `lib` package in the `hello` module. After doing this, you can use the `fib` package in `main/main.mbt`. Replace the file content of `main/main.mbt` to:

```rust
fn init {
let a = @fib.fib(10)
let b = @fib.fib2(11)
let a = @my_awesome_fibonacci.fib(10)
let b = @my_awesome_fibonacci.fib2(11)
println("fib(10) = \(a), fib(11) = \(b)")

println(@lib.hello())
}
```

To execute your program, specify the path to the `main` package:

```bash
$ moon run ./main
$ moon run main
fib(10) = 55, fib(11) = 89
Hello, world!
```

## Inline tests
## Adding tests

In the last, let's add some tests to verify our fib implementation.

In the last, let's add some tests to verify our fib implementation. Add the following to `lib/fib/a.mbt`:
First, inside the `lib/fib` directory, create a file named `fib_test.mbt` and paste the following code:

`lib/fib/fib_test.mbt`
```rust
fn assert_eq[T: Show + Eq](lhs: T, rhs: T) {
fn assert_eq[T: Eq](lhs: T, rhs: T) {
if lhs != rhs {
abort("assert_eq failed.\n lhs: \(lhs)\n rhs: \(rhs)")
abort("")
}
}

test {
fn init {
assert_eq(fib(1), 1)
assert_eq(fib(2), 1)
assert_eq(fib2(2), 1)
assert_eq(fib(3), 2)
assert_eq(fib(4), 3)
assert_eq(fib2(4), 3)
assert_eq(fib(5), 5)
}
```

This code tests the first five terms of the Fibonacci sequence. `test { ... }` defines an inline test block. The code inside inline test blocks will be executed in test mode, using `moon test`:
This code tests the first five terms of the Fibonacci sequence.

```bash
$ moon test
test lib/fib ... ok
```

`moon test` will execute all inline tests in current module.

Inline test blocks will be discarded in normal mode (`moon build` and `moon run`), so they will not result in code size bloat.

## Stand-alone test files

Besides inline tests, MoonBit also supports stand-alone test files. Source files whose names end with `_test.mbt` will be considered test files. They will be included in test mode only. You can write inline tests and test utilities in these stand-alone test files.
For example, inside the `lib/fib` directory, create a file named `fib_test.mbt` and paste the following code:
You can also write inline tests directly in lib/fib/a.mbt:

`lib/fib/a.mbt`
```rust
fn assert_eq[T: Show + Eq](lhs: T, rhs: T) {
if lhs != rhs {
abort("assert_eq failed.\n lhs: \(lhs)\n rhs: \(rhs)")
pub fn fib(n : Int) -> Int {
match n {
0 => 0
1 => 1
_ => fib(n - 1) + fib(n - 2)
}
}

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 {
if fib(4) != 3 { abort("fib(4) != 3") }
}
```

`moon test` will include all scanfiles ending with `_test.mbt` during compilation. If everything works, run `moon test`, and you'll see:
Finally, use the command `moon test` which scans the module, identifying and running all files ending with `_test.mbt`. If everything works, you'll see:

```bash
$ moon test
test lib/fib ... ok
test lib ... ok
fib(10) = 55, fib(11) = 89
Hello, world!
test main ... ok
```

Note that `main/main.mbt:init` is also executed here. We will improve the issue with tests and package initialization functions in the future.
Loading

0 comments on commit 8ee845e

Please sign in to comment.