Skip to content

Building

Narta Xaymar Dirks edited this page Nov 1, 2026 · 19 revisions

Building

BlitzLLVM builds with CMake. The compiler is blitzcc, and it carries the runtime library with it. The first configure builds the dependencies (Vulkan headers, SDL3, SDL3_image and SDL3_mixer, linked statically except for the AVIF, JPEG XL, TIFF (except on macOS) and WebP codecs of SDL3_image and the Opus libraries (libopus, opusfile and libogg) of SDL3_mixer, which are shared libraries, and the header-only VulkanMemoryAllocator) from the submodules in third_party/, which takes a few minutes. The physics library blitzphysics (code_physics) builds Jolt Physics from third_party/JoltPhysics into itself as part of the normal build.

Requirements

  • CMake 4.2 or newer.
  • Git, for fetching the dependencies.
  • A C++20 compiler: Visual Studio on Windows, Clang or GCC on Linux, Apple Clang on macOS.
  • Ninja on Linux and macOS.
  • NASM on x86 and x64, optional. It builds the assembly of dav1d (AVIF images). Without it the build still works, with a warning, and AVIF images decode more slowly. Pass -DBLITZLLVM_REQUIRE_NASM=ON to make a missing NASM an error, as the nightly builds do. Install it with winget install nasm on Windows, sudo apt-get install nasm on Debian and Ubuntu or brew install nasm on macOS. It is found on PATH, in %LOCALAPPDATA%\bin\NASM or in C:\Program Files\NASM; set BLITZLLVM_NASM to its path if it is elsewhere.
  • Python 3, optional. It enables the suite-coverage test.
  • The dependencies (SDL3, SDL_image, SDL_mixer and FFmpeg) are built while CMake configures, FFmpeg at the same time as the others, and together they use as many compiler processes as the computer has logical cores. Pass -DBLITZLLVM_DEPENDENCY_JOBS=<n> to use fewer (or more). With the Visual Studio generator every project is built with all of its files at once (/MP).

Windows

Install Visual Studio with the "Desktop development with C++" workload. The nightly builds use Visual Studio 18 2026; the repository also has presets for Visual Studio 17 2022 and for clang (see below).

Linux

SDL3 needs the development packages of the windowing, audio and input libraries. On Debian and Ubuntu:

sudo apt-get install -y ninja-build libx11-dev libxext-dev libxcursor-dev libxi-dev libxrandr-dev \
  libxss-dev libxtst-dev libxfixes-dev libwayland-dev libxkbcommon-dev libdecor-0-dev \
  libasound2-dev libpulse-dev libudev-dev libdrm-dev libgbm-dev libegl-dev

macOS

brew install ninja

Getting the source

git clone https://github.com/Xaymar/BlitzLLVM.git
cd BlitzLLVM
git submodule update --init --depth 1 cmake third_party

The submodules in cmake/ and third_party/ are required to build. The third_party/blitz3d, third_party/blitz3d_soloud and third_party/blitzplus_msvc2017 submodules only hold the original Blitz3D and BlitzPlus sources that the function test suite tools compare against, so they are not needed to build. Fetch them with git submodule update --init third_party/blitz3d third_party/blitz3d_soloud third_party/blitzplus_msvc2017 if you want them.

Build

Linux and macOS:

cmake -S . -B build -G Ninja -DCMAKE_BUILD_TYPE=Release
cmake --build build

Windows:

cmake -S . -B build -G "Visual Studio 18 2026" -A x64
cmake --build build --config Release

Instead of choosing the generator yourself you can use a preset from CMakePresets.json: windows-x64-msvc2022 (Visual Studio 17 2022) or windows-x64-llvm (Ninja Multi-Config with the LLVM toolchain from cmake/toolchains/llvm.cmake).

cmake --preset windows-x64-msvc2022
cmake --build --preset windows-x64-msvc2022 --config Release

The compiler ends up in the build directory as blitzcc (blitzcc.exe), with the runtime in a runtime directory next to it (an install puts it in lib/blitzllvm/runtime).

System libraries (Linux)

On Linux the dependencies can come from the distribution instead of the submodules, which keeps them out of the packages and lets the distribution update them. Each one has a cache variable that is AUTO (use the system's if pkg-config finds a new enough copy, otherwise build the submodule), ON (fail if there is none) or OFF (always build the submodule):

Variable Takes How programs use it
BLITZ_SYSTEM_SDL3 SDL3 (3.4.0 or newer, BLITZ_SDL3_MINIMUM_VERSION) linked as a shared library
BLITZ_SYSTEM_SDL3_IMAGE, BLITZ_SYSTEM_SDL3_MIXER SDL3_image and SDL3_mixer (need the system's SDL3) linked as shared libraries
BLITZ_SYSTEM_IMAGE_CODECS libavif (with dav1d), libjxl, libtiff and libwebp loaded when a program opens such an image
BLITZ_SYSTEM_AUDIO_CODECS opusfile (with libopus and libogg) loaded when a program plays an Opus file, and libopus when a movie has Opus sound
BLITZ_SYSTEM_FFMPEG FFmpeg, but only if it is LGPL and has the decoders listed in dependencies.cmake loaded when a program opens a movie

The libraries that programs link to are written to runtime/system-libraries.txt next to the runtime, which blitzcc adds to the link. On Debian and Ubuntu the .deb depends on (or, for the libraries that are loaded when needed, recommends) the packages that own them, at least in the version that the package was built with. The distributions ship FFmpeg with GPL parts (check with avcodec_license()), so the bundled LGPL build is used there. Windows and macOS always build the submodules.

Install

cmake --install build --config Release --prefix dist

This creates:

dist/bin/blitzcc          the compiler
dist/lib/blitzllvm/runtime/  the runtime libraries and sources, lib has to stay next to bin
dist/userlibs/            user libraries, with a README

Tests

ctest --test-dir build -C Release

This compiles and runs every program in tests/ and the function test suite in tests/suite without a window (BLITZLLVM_HEADLESS=1). The function test suite, the audio test, the close prompt test and the AutoSuspend tests run twice: as the compiler builds by default (Release), and with -d (Debug, which implies --profile). The Debug ones have debug in their names, for example suite-debug-run-math; ctest -R debug runs only those. Tests that look at pixels need a window and a GPU and are skipped unless you configure with -DBLITZ_TEST_GPU=ON. The tests need clang (see Installation).

Continuous integration

The Test workflow (.github/workflows/test.yml) runs the tests on Linux, Windows and macOS (all of them, or only the ones a change can affect, see below), for every pull request to root and for every push to root. It also runs the tests that need a window and a GPU (-DBLITZ_TEST_GPU=ON): the runners have no GPU, so Mesa's llvmpipe renders Vulkan on the CPU inside a virtual X server (xvfb-run). To run the same thing on your own Linux machine:

sudo apt-get install -y mesa-vulkan-drivers xvfb xdotool x11-utils
cmake -S . -B build -G Ninja -DCMAKE_BUILD_TYPE=Release -DBLITZ_TEST_GPU=ON
cmake --build build
cd build/code_compiler && xvfb-run -a ctest -j8 --output-on-failure

Windows and macOS

Test (Windows) (x64) and Test (macOS) run the same selection as Linux, but only when all of the tests run (see Which tests run); a change to one part is tested on Linux alone, which saves the few runners of the other platforms (the repository has 20 for every run, 5 of them macOS, and a run uses at most 3 at a time). The build of a pull request is made by build.yml, which builds the three test platforms (build (ubuntu24.04-x64), build (windows-x64) and build (macos-universal), or only Linux for a partial selection) and uploads the build tree as test-build-<platform>; all ten platforms are built and packaged only when the pull request changes the binaries. The test job downloads and unpacks the tree instead of compiling, and runs ctest. Neither runner has a GPU, so Windows renders Vulkan on the CPU with Mesa's lavapipe (installed by jakoch/install-vulkan-sdk-action), and macOS uses MoltenVK, which needs a Metal device: if the runner has none, the tests that need a window and a GPU (label gpu) are left out and the run says so in a warning. The comment on the pull request has a Debug and a Release column for each platform. Each job lists the tests that skipped themselves in its job summary. CMake does not register these tests on Windows: the close prompt test, the profile-*_pixels tests, the audio output tests (they use SDL's disk audio driver) and the AutoSuspend tests (they need xdotool on X11). Tests run in parallel (ctest -j) except the ones with RUN_SERIAL (the audio and AutoSuspend tests, which depend on timing or on the focus); programs that call MoveMouse take turns (they share the pointer).

Which tests run

A pull request does not run the tests that its changes can not affect. The changes job of the workflow compares the pull request with root, .github/scripts/test-select.py maps every changed file to the parts it can affect (the rules are in tests/test_selection.json), and Test (Linux) runs only the tests of those parts, plus the few that are not in any part (lex-test, suite-coverage, ci-test-select, ci-test-report). The tests that were left out show as skipped (⏭️) in the test comment, with the reason. A skipped test counts as passed, so the required check stays green.

What changed What runs
Documentation, the wiki sources, extra/, the benchmarks, other workflows Nothing. The check succeeds without building.
One part of the runtime, for example sound.c (audio), network.c (network), movie.cpp (movies), gui_*.cpp (BlitzPlus GUI), scene*.cpp (Blitz3D), files.c (files), graphics_*.cpp (everything that draws) The tests of that part, in Release and Debug.
A test program (tests/suite/x.bb) The compile and run tests of that program only.
A test of one tool or the command line (tests/cli, tests/format, tests/tidy, code_format, code_tidy, code_tools) The tests of that tool.
The compiler, the core of the runtime, the CMake files, third_party, the workflow, the test selection itself, or any file that has no rule Everything.

Everything also runs for every push to root, for a manual run, and for a pull request with the label run-all-tests (add the label, then re-run the workflow; the label is read when the run starts). The push to root is the safety net: if a rule skips something it should not have, the run on root finds it.

To change the rules, edit tests/test_selection.json: areas says which tests (by name) belong to a part, rules says which parts a changed file affects (the first rule that matches wins; "all" runs everything, "none" nothing, [] only the tests that are in no part). When in doubt, map a file to more parts, not fewer. python3 tests/ci/test_select.py tests the rules (it also runs as the ctest test ci-test-select), and it fails if a program in tests/suite is in no part. To see what a change would run, put the changed files in a file and run python3 .github/scripts/test-select.py changes --changed files.txt.

Requiring the tests before a pull request can be merged

The check to require is Build and test result: it always runs and fails if the build or any test of any platform failed. (The checks of the single platforms, Build / build (<platform>), only exist when something is built, so a pull request that only changes documentation never reports them and a required one would stay pending.) A workflow on its own never blocks a merge; GitHub only blocks it for checks that the repository marks as required. This is a repository setting, so the maintainer has to make it (it can not be part of a pull request).

  1. Merge the pull request that adds test.yml, and let it run once on root (or on any pull request). GitHub only offers a check in the list below after it has run in the repository during the last 7 days.
  2. Open the repository on GitHub, then Settings → Rules → Rulesets → New ruleset → New branch ruleset. (The older way is Settings → Branches → Add branch protection rule with the branch name pattern root; the steps below are the same.)
  3. Give it a name such as root, set Enforcement status to Active, and under Target branches choose Add target → Include by pattern and enter root.
  4. Under Branch rules tick Require a pull request before merging, and Require status checks to pass.
  5. In Require status checks to pass, tick Require branches to be up to date before merging if a pull request has to be tested on top of the current root (recommended), then Add checks, search for Build and test result and select it. Leave the source on Any source, or pick GitHub Actions.
  6. Create. The merge button of a pull request is now disabled until those checks are green, and pushes straight to root are refused too (unless the ruleset lists a bypass for administrators).

Things to know:

  • The check must always report. test.yml therefore has no paths filter (the selection above is done inside the job); a required check that is skipped never turns green and blocks the pull request forever.
  • If you rename the job (name: in test.yml) you have to change the required check in the ruleset too, or every pull request waits for a check that no longer exists.
  • The name has to be unique among the workflows. A job called Build and test result in two workflows would be ambiguous.
  • With Require branches to be up to date a pull request has to be updated with root after every other merge. Without it, a pull request can be merged green while being broken by a newer commit on root.
  • Pushes to root run the tests too, so a break that slipped in (for example by an administrator bypass) shows up on the commit.

Home · Command Index



Differences
Language Features
BlitzBasic
Blitz3D
BlitzPlus

Clone this wiki locally