Some parts of this repository are setup to build and test with the
bazel build system. This document describes the
common workflows.
- On macOS make sure you have a relatively recent version of Xcode or the Xcode Command Line Tools installed.
bazelautomatically downloads the current nightly version ofmojo(defined in theMODULE.bazel), meaning it does not pick up the current globally installed version.- For convenience there is a
bazelwscript in the root of the repository that automatically downloads the currently supported version ofbazel.
bazel has 2 primary subcommands you will interact with build and
test. For example to build all the code in the repository you can run:
./bazelw build //...You can also build more specific targets when you're iterating on a
subset of the repository. For example to package only the stdlib, you
can run:
./bazelw build //mojo/stdlib/stdSimilarly to run all the tests in the repository you can run:
./bazelw test //...You can also scope build or test commands to specific directories. For
example to run only the stdlib tests you can run:
./bazelw test //mojo/stdlib/...To see what targets are available to build or test you can run:
./bazelw query //...Some tests in the repository only support specific hardware. In that
case bazel automatically skips building and testing them. bazel also
automatically detects the current GPU hardware, so tests that are
specific to individual GPUs can be run.
If you're working on one off test cases that might not end up being a
normal test file, you can do so in the root of the repository in
repro.mojo. This file is gitignored but setup to be run with all mojo
dependencies so you can easily iterate on mojo changes. Once you edit
that file you can run it with:
./bazelw run //:reproA few linters and formatters can be run easily from bazel.
To just see the output of the linters, you can run:
./bazelw run //:lintTo automatically apply the fixes (where possible), you can run:
./bazelw run //:formatSee the lint and format targets defined in
bazel/lint/BUILD.bazel
to see what the current tools being run are.
The Mojo version in
bazel/mojo.MODULE.bazel
is automatically updated with each nightly, but can be manually replaced
locally to use a different version if desired.
The BUILD.bazel files throughout the repository define how targets are
built and tested. There are a few common rules that are used.
To produce a mojopkg that can be consumed by other Mojo code, you can
use the mojo_library rule. For example:
load("//bazel:api.bzl", "mojo_library")
mojo_library(
name = "some_library",
srcs = glob(["**/*.mojo"]),
visibility = ["//visibility:public"],
deps = [
"@mojo//:std",
],
)To produce a binary that runs Mojo code, you can use the mojo_binary
rule:
load("//bazel:api.bzl", "mojo_binary")
mojo_binary(
name = "example",
srcs = ["src/example.mojo"],
deps = [
"@mojo//:std",
],
)To add new test targets we have a few different rules depending on the use case.
If you're writing a test that needs to compile and execute a Mojo
binary, that then uses the testing module to write assertions, you can
use the mojo_test rule:
mojo_test(
name = "test_hash",
srcs = ["hashlib/test_hash.mojo"],
deps = [
"@mojo//:std",
],
)If you're writing a test that needs to compile and execute a Mojo
binary, that then needs to check its output using FileCheck, you can
use mojo_filecheck_test rule:
mojo_filecheck_test(
name = "test_shared_mem_barrier.mojo.test",
srcs = ["test_shared_mem_barrier.mojo"],
deps = [
"//max:layout",
"//max:linalg",
"@mojo//:std",
],
)When possible, prefer using mojo_test since the assertions should be
easier to debug.
bazel rules have many different knobs for customizing how things are
built and run. There are a few important ones for this repository.
target_compatible_with is used to specify the hardware or other
configuration that a target works with, and therefore will be
automatically skipped on when not matched. For example if a test is only
compatible with H100 GPUs, you can specify:
target_compatible_with = ["//:h100_gpu"],You can see the current options for different GPUs specifically in the
bazel/config.bzl
file.
You can also use target_compatible_with to specify that a target
requires a specific operating system:
target_compatible_with = ["@platforms//os:linux"],If you need to exclude a target from a specific configuration, you can use something like this:
target_compatible_with = select({
"//:asan": ["@platforms//:incompatible"],
"//conditions:default": [],
})In this example, the target is disabled when running with ASAN.
Tags allow filtering targets based on metadata. In this repository we
use the gpu tag to specify that a test should be run on GPU hardware.
Currently this only applies to internal Modular CI but should still be
added to make sure tests are run correctly internally.
tags = ["gpu"],In order to run GPU tests in this repo, Mojo must support the GPU
architecture, and bazel must be configured to detect the GPU. To add a
new GPU to bazel, update the mojo.gpu_toolchains section of the
common.MODULE.bazel
file.
First update gpu_mapping to map from the output of nvidia-smi to a
human readable name of the GPU. For example:
% nvidia-smi --query-gpu=gpu_name --format=csv,noheader
NVIDIA A100-SXM4-80GBIn this case when the output contains A100 we map it to a100. Then
update the supported_gpus dictionary where the key is the human
readable name, and the value is the --target-accelerator value passed
to Mojo compiles. You can fetch this value with gpu-query which ships
as part of pip install modular:
% gpu-query --target-accelerator
nvidia:80