Skip to content

Commit

Permalink
update build system tutorial (#284)
Browse files Browse the repository at this point in the history
* update build system tutorial

* use mdlint to fix unusable code
  • Loading branch information
notch1p authored Aug 30, 2024
1 parent 5fd5953 commit 2e619c4
Show file tree
Hide file tree
Showing 2 changed files with 227 additions and 175 deletions.
207 changes: 128 additions & 79 deletions moonbit-docs/docs/build-system-tutorial.md
Original file line number Diff line number Diff line change
@@ -1,55 +1,75 @@
# 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](https://mooncakes.io/)
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](https://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.
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
```plaintext
$ moon help
Moonbit's build system
The build system and package manager for MoonBit.

Usage: moon <COMMAND>
Usage: moon [OPTIONS] <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)
new Create a new moonbit package
build Build the current package
check Check the current package, but don't build object files
run Run WebAssembly module
test Test the current package
clean Clean the target directory
fmt Format moonbit source code
doc Generate documentation
info Generate public interface (`.mbti`) files for all packages in the module
add Add a dependency
remove Remove a dependency
install Install dependencies
tree Display the dependency tree
login Log in to your account
register Register an account at mooncakes.io
publish Publish the current package
update Update the package registry index
coverage Code coverage utilities
generate-build-matrix Generate build matrix for benchmarking (legacy feature)
upgrade Upgrade toolchains
shell-completion Generate shell completion for bash/elvish/fish/pwsh/zsh to stdout
version Print version info and exit
help Print this message or the help of the given subcommand(s)

Options:
-h, --help Print help
-C, --directory <SOURCE_DIR> The source code directory. Defaults to the current directory
--target-dir <TARGET_DIR> The target directory. Defaults to `source_dir/target`
-q, --quiet Suppress output
-v, --verbose Increase verbosity
--trace Trace the execution of the program
--dry-run Do not actually run the command
-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.
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 must-have functionalities like syntax highlighting,
intellisense, interactive debugging, testing, and more.

Once you have these prerequisites fulfilled, let's start by creating a new MoonBit module.
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` comes with a handy module creation wizard `moon new`. The default
settings are shown below:

```bash
```plaintext
$ moon new
Enter the path to create the project (. for current directory): my-project
Select the create mode: exec
Expand All @@ -59,11 +79,14 @@ Enter your license: Apache-2.0
Created my-project
```

## Understanding the Module Directory Structure
This creates a new module named `hello` in `my-project`. The word _project_ is
used interchangeably with _module_.

After creating the new module, your directory structure should resemble the following:
## Understanding MoonBit's Module Structure

```bash
A typical module/project structure resembles the following:

```plaintext
my-project
├── README.md
├── lib
Expand All @@ -76,34 +99,46 @@ my-project
└── moon.mod.json
```

Here's a brief explanation of the directory structure:
Here's a brief explanation of the structure:

- `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.
- `lib` and `main` directories: These are the packages within the module. Each
package may contain multiple moonbit source files (.mbt). A common
`moon.pkg.json` is shared around every source files within that package.

- `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.
- `lib/*_test.mbt` are tests for the `lib` package. However members of `lib`
aren't directly visible to the tests, you'll need to explicitly use the
`@my-project.*` to access it. Tests are only included in
test compilation, allowing inline tests and utility functions for
testing purposes to be written within these separate test files.

- `moon.pkg.json` is the 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,
"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.

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
{}
```

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:
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 declare a directory as a MoonBit module. It
contains the module's name:

```json
{
Expand All @@ -117,6 +152,9 @@ Here's a brief explanation of the directory structure:
}
```

Refer to the [json schema](./build-system-configuration.md) of module/package
descriptor for a complete specification.

## Working with Packages

Our `username/hello` module contains two packages: `lib` and `main`.
Expand All @@ -125,39 +163,32 @@ The `lib` package contains `hello.mbt` and `hello_test.mbt` files:

`hello.mbt`

```moonbit
```moonbit -f=hello.mbt
pub fn hello() -> String {
"Hello, world!"
}
```

`hello_test.mbt`

```moonbit
```moonbit -f=hello.mbt
test "hello" {
if hello() != "Hello, world!" {
return Err("hello() != \"Hello, world!\"")
fail!("hello() != \"Hello, world!\"")
}
}
```

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

```moonbit
```moonbit no-check
fn main {
println(@lib.hello())
}
```

To execute the program, specify the path to the `main` package in the `moon run` command:

```bash
$ moon run ./main
Hello, world!
```
You can also omit `./`
```bash
$ moon run main
Hello, world!
Expand All @@ -172,8 +203,9 @@ 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`:
MoonBit's build system uses the name of a module to reference its internal
packages. To use `lib` within `main/main.mbt`, you need to specify it in
`main/moon.pkg.json`:

```json
{
Expand All @@ -184,21 +216,23 @@ To import the `lib` package in `main/main.mbt`, you need to specify it in `main/
}
```

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`.
(Although the intellisense will do that for you: just type `@lib` then enter.)

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
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`.

First, create a new directory named `fib` under `lib`:
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`.

```bash
mkdir lib/fib
```
## Creating and Using a New Package

Now, you can create new files under `lib/fib`:
Suppose we have a new package named `fib` under `lib` and two moonbit source
`lib/fib/{a,b}.mbt`.

`a.mbt`:
In `a.mbt`:

```moonbit
pub fn fib(n : Int) -> Int {
Expand All @@ -210,7 +244,7 @@ pub fn fib(n : Int) -> Int {
}
```

`b.mbt`:
in `b.mbt`:

```moonbit
pub fn fib2(num : Int) -> Int {
Expand All @@ -226,13 +260,13 @@ pub fn fib2(num : Int) -> Int {
}
```

`moon.pkg.json`:
and `moon.pkg.json`:

```json
{}
```

After creating these files, your directory structure should look like this:
Our project structure should look like this now:

```bash
my-project
Expand All @@ -251,7 +285,8 @@ my-project
└── moon.mod.json
```

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

```json
{
Expand All @@ -266,19 +301,19 @@ In the `main/moon.pkg.json` file, import the `username/hello/lib/fib` package an
}
```

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:
In `main/main.mbt`:

```moonbit
```moonbit no-check
fn main {
let a = @my_awesome_fibonacci.fib(10)
let b = @my_awesome_fibonacci.fib2(11)
println("fib(10) = \(a), fib(11) = \(b)")
println("fib(10) = \{a}, fib(11) = \{b}")
println(@lib.hello())
}
```

To execute your program, specify the path to the `main` package:
Running `moon run main` again gives us the expected:

```bash
$ moon run main
Expand All @@ -288,14 +323,20 @@ Hello, world!

## Adding Tests

Let's add some tests to verify our fib implementation. Add the following content in `lib/fib/a.mbt`:
MoonBit differentiate between white-box and black-box tests. A white-box test
usually refers to an inline test block or a stand-alone `*_wbtest.mbt` source, intended for package developers to test their code,
whereas a black-box test refers to a `*_test.mbt` source, emulating package
users using current package. They may have different imports: a white-box test
automatically imports everything from `import` and `test-import` in
`moon.pkg.json`; a black-box test imports just the same as white-box but
with the addition of current package.

`lib/fib/a.mbt`
Let's add some inline tests to verify our fib implementation. In `lib/fib/a.mbt`:

```moonbit
fn assert_eq[T: Show + Eq](lhs: T, rhs: T) -> Unit {
if lhs != rhs {
abort("assert_eq failed.\n lhs: \(lhs)\n rhs: \(rhs)")
abort("assert_eq failed.\n\tlhs: \{lhs}\n\trhs: \{rhs}")
}
}
Expand All @@ -308,13 +349,19 @@ test {
}
```

This code tests the first five terms of the Fibonacci sequence. `test { ... }` defines an inline test block. The code inside an inline test block is executed in test mode.
This code tests the first five terms of the Fibonacci sequence. `test { ... }`
defines an inline test block. The code inside an inline test block is executed
in test mode.

Inline test blocks are discarded in non-test compilation modes (`moon build` and `moon run`), so they won't cause the generated code size to bloat.
Inline test blocks are discarded in non-test compilation modes (`moon build` and
`moon run`), so they won't cause the generated code size to bloat.

## Stand-alone test files

Besides inline tests, MoonBit also supports stand-alone test files. Source files ending in `_test.mbt` are considered stand-alone 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:
Stand-alone tests are included in test mode only as well. 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:

`lib/fib/fib_test.mbt`

Expand All @@ -328,7 +375,9 @@ test {
}
```

Finally, use the `moon test` command, which scans the entire project, identifies, and runs all inline tests as well as files ending with `_test.mbt`. If everything is normal, you will see:
Now we use the `moon test` command, which scans the entire project,
identifies, and runs all inline tests as well as files ending with `_test.mbt`
or `_wbtest.mbt`. If everything is normal, you will see:

```bash
$ moon test
Expand Down
Loading

0 comments on commit 2e619c4

Please sign in to comment.