We use bazel
1 to build Vere, which is packaged as a single binary,
urbit
. We support the following (host, target)
pairs, where the host platform
is where bazel
runs and the target platform is where urbit
will
run:
Host Platform | Target Platform |
---|---|
linux-aarch64 |
linux-aarch64 |
linux-x86_64 |
linux-x86_64 |
macos-aarch64 |
macos-aarch64 |
macos-x86_64 |
macos-x86_64 |
The first step is to install bazel
using the instructions found on the bazel
1 site.
All platforms require the automake
and libtool
suite of tools installed in
order to build Vere. Before going any further, install them using your package
manager. For example, on macOS:
brew install automake libtool
After installing automake
, autoconf-archive
, and libtool
, you need to install the musl libc toolchain. We use musl libc instead of glibc on Linux, which enables us to generate statically linked binaries. As a prerequisite, you need to install the musl libc toolchain appropriate for your target platform.
To install the x86_64-linux-musl-gcc
toolchain at
/usr/local/x86_64-linux-musl-gcc
, run:
bazel run //bazel/toolchain:x86_64-linux-musl-gcc
This will take a few minutes.
To install the aarch64-linux-musl-gcc
toolchain at
/usr/local/aarch64-linux-musl-gcc
, run:
bazel run //bazel/toolchain:aarch64-linux-musl-gcc
This will take a few minutes.
After installing automake
, autoconf-archive
, pkg-config
, and libtool
, you're ready to build Vere.
macOS is curious operating system because the kernel is derived from from two codebases, the Mach kernel and the BSD kernel. It inherits two different hardware exception handling facilities, Mach exceptions and POSIX signals. We use libsigsegv
and utilize the POSIX signals which is usually fine except when it comes to debugging with lldb
.
lldb
hijacks the Mach exception ports for the task when it attaches to the process. Mach exceptions get handled before POSIX signals which means that as soon as vere faults (this happens often) lldb
stop with a EXC_BAD_ACCESS
. It is impossible to continue debugging from this state without the workaround we implemented in urbit#611.
There are more annoying warts with lldb
currently. First, if you attach the debugger when booting the ship with lldb -- your-ship/.run
you have to specify -t
, otherwise the ship is unable to boot for mysterious reasons. The other option is to start the ship and attach afterwards with lldb -p PID
. Afterwards you should do this dance:
p (void)darwin_register_mach_exception_handler()
pro hand -p true -s false -n false SIGBUS
pro hand -p true -s false -n false SIGSEGV
Once you install the prerequisites, you're ready to build:
bazel build :urbit
If you want a debug build, which changes the optimization level from -O3
to
-O0
and includes more debugging information, specify dbg
as the
compilation_mode
:
bazel build --compilation_mode=dbg :urbit
Note that you cannot change the optimization level for third party
dependencies--those targets specified in bazel/third_party
--from the command
line.
You can turn on CPU and memory debugging by specifying the cpu_dbg
and/or the
mem_dbg
build config.
bazel build --config=cpu_dbg --config=mem_dbg :urbit
Note that ships booted from builds with one or both of these configs specified are incompatible with builds of vere without them and vice versa.
If you need to specify arbitrary C compiler or linker options, use
--copt
or --linkopt
, respectively:
bazel build --copt='-Wall' :urbit
Note --copt
can be used to specify arbitrary C compiler
optimizations. They're passed directly to the compiler commands generated by
bazel.
Additionally, --copt
is propagated to all dependencies. This has the
undesirable side effect of considerably slowing down build times when you're
switching between different copts (say the inclusion or exclusion of some macro
define with --copt='-DMACRO'
) since all dependencies will be rebuilt. If you
want only to pass the copt to the compiler commands invoked to build :urbit
,
you can pass them with --per_file_copt
like so:
bazel build --per_file_copt='pkg/.*@-DMACRO'
bazel run //bazel:refresh_compile_commands
Running this command will generate a compile_commands.json
file in the root
of the repository, which clangd
(or other language server processors) will
use automatically to provide modern editor features like syntax highlighting,
go-to definitions, call hierarchies, symbol manipulation, etc.
You can build and run unit tests only on native builds. If you have a native build and want to run all unit tests, run:
bazel test --build_tests_only ...
If you want to run a specific test, say
pkg/noun/hashtable_tests.c
, run:
bazel test //pkg/noun:hashtable_tests
Any options you specify at the command line can instead be specified in
.user.bazelrc
if you find yourself using the same options over and over. This
file is not tracked by git
, so whatever you add to it will not affect anyone
else. As an example, if you want to change the optimization level but don't want
type --copt='-O0'
each time, you can do the following:
echo "build --copt='-O0'" >> .user.bazelrc
bazel build :urbit
For more information on Bazel configuration files, consult the Bazel docs.
If bazel build
or bazel test
generates an undeclared inclusion(s) in rule
error on macOS, the version of clang
expected by the build system likely
doesn't match the version of clang
installed on your system. To address this,
run clang --version
and pass the version number via
--clang_version="<version_string>"
to the failing command.
If build fails on nix/NixOS, you should pass ACLOCAL_PATH
environment
variable to bazel, using --action_env=ACLOCAL_PATH=$ACLOCAL_PATH
, like so:
bazel build :urbit --action_env=ACLOCAL_PATH=$ACLOCAL_PATH
Footnotes
-
If you're interested in digging into the details of the build system, check out
WORKSPACE.bazel
,BUILD.bazel
,bazel/
, and the multipleBUILD.bazel
files inpkg/
. ↩ ↩2