Compiling Ymir
July 21, 2026 ยท View on GitHub
Ymir requires CMake 3.28+ and a C++20 compiler.
The repository includes several vendored dependencies as Git submodules. When cloning, make sure to include the --recurse-submodules parameter.
You can also initialize submodules with git submodule update --init --recursive after a git clone or when pulling changes.
Ymir has been successfully compiled with the following toolchains:
- Visual Studio 2022's Clang 19.1.5
- Visual Studio 2022's MSVC 19.44.35213.0
- Visual Studio 2026's Clang 20.1.8
- Visual Studio 2026's MSVC 19.50.35724.0
- Clang 15.0.7 on WSL Ubuntu 24.04.5 LTS (
clang-15/clang++-15) - Clang 18.1.3 on WSL Ubuntu 24.04.5 LTS (
clang/clang++) - Clang 19.1.1 on Ubuntu 24.04.2 LTS (
clang-19/clang++-19) - GCC 14.2.0 on Ubuntu 24.04.2 LTS (
gcc-14/g++-14) - Clang 19.1.7 on FreeBSD 14.3-RELEASE (
clang19/clang++19) - Clang 21.1.0 on FreeBSD 14.3-RELEASE (
clang21/clang++21) - Apple Clang 17 on macOS 15 Sequoia
The project has been compiled for x86_64 and ARM64 Windows, Linux, FreeBSD and macOS platforms.
Clang is the preferred compiler for its multiplatform support and excellent code generation. Ymir requires Clang 15 or later. Ninja is the preferred build system for its speed and feature support. Makefiles are also acceptable.
Build configuration
You can tune the build with following CMake options:
Ymir_AVX2(BOOL): Set toONto use AVX2 extensions (on x86_64 platforms only).OFFuses the platform's default instruction set, typically SSE2. ARM64 platforms will always use NEON. Disabled by default.Ymir_ENABLE_TESTS(BOOL): Includes the unit test project in the build. Enabled by default if this is the top level CMake project.Ymir_ENABLE_SANDBOX(BOOL): Includes the sandbox project in the build. Enabled by default if this is the top level CMake project.Ymir_ENABLE_YMDASM(BOOL): Includes the disassembly tool project in the build. Enabled by default if this is the top level CMake project.Ymir_ENABLE_IPO(BOOL): Enables interprocedural optimizations (also called link-time optimizations) on all projects. Enabled by default.Ymir_ENABLE_DEVLOG(BOOL): Enables logs meant to aid development. Enabled by default.Ymir_ENABLE_DEV_ASSERTIONS(BOOL): Enables development assertions, meant to mark code as incomplete or for potential bugs. Disabled by default.Ymir_ENABLE_IMGUI_DEMO(BOOL): Enables the ImGui demo window, useful as a reference when developing new UI elements. Enabled by default.Ymir_ENABLE_UPDATE_CHECKS(BOOL): Enables automatic update checks and onboarding process. Enabled by default.Ymir_EXTRA_INLINING(BOOL): Enables more aggressive inlining, which slows down the build in exchange for better runtime performance. Only applies to Clang, which handles heavy inlining much better than GCC or MSVC. Disabled by default.Ymir_PGO(STRING): PGO mode. Valid values areOFF,GENERATE,USE. Defaults toOFF.Ymir_PGO_DIR(PATH): Directory where PGO profile data is written. Defaults to${CMAKE_BINARY_DIR}/pgo-profdata.Ymir_PGO_PROFDATA(FILEPATH): Merged LLVM PGO profile data path. Defaults to${Ymir_PGO_DIR}/ymir.profdata.Ymir_LIBRARY_ONLY(BOOL): Compilesymir-coreonly, for use as a subproject in another CMake project. Defaults toONif included as subproject,OFFotherwise.
These options are used by the build workflows to tune the build output:
Ymir_DEV_BUILD(BOOL): Create a development build. This affects the versioning scheme (-devsuffix added, "(development build)" added to About window) and availability of development-friendly features (e.g. disabled automatic update checks). Enabled by default, and should probably not be disabled unless you're checking behavior of stable and nightly builds.
Ymir also supports feature flags. These are enabled by default on development and nightly builds:
Ymir_FEATUREFLAG_DEFAULT(BOOL): Enables or disables all non-overridden feature flags. Enabled by default on development builds.Ymir_FF_HOST_CD_DRIVES(BOOL): Enables support for reading discs from host CD drives (physical or virtual). Disabled by default, including development and nightly builds.
Feature flags are made available to code as macros in ymir-core's CMakeLists.txt (find ## Define feature flags macros).
If you add new feature flags, make sure to add the macro to this file too.
Depending on the target build system, you might have to specify CMAKE_BUILD_TYPE for Release builds, otherwise CMake defaults to slow Debug builds.
This option can be set to one of these values (see docs):
Debug: an unoptimized Debug build, with debug assertions and no inlining. Ideal for deep code debugging, but very slow.Release: optimized Release build without debug symbols. Best used for stable and nightly builds. Probably not useful for development.RelWithDebInfo: same asRelease, but includes debug symbols. Great for general development, but the optimizations and inlining might get in the way of debugging.MinSizeRel: a Release build that is optimized for code size. Not very useful since we're not targeting memory-constrained systems.
For a Release build, you might want to disable the devlog and ImGui demo window and enable extra inlining to maximize performance and reduce the binary size.
It is highly recommended to use Ninja as it greatly accelerates the build process, especially on machines with high CPU core counts.
Building on Windows
To build Ymir on Windows, you will need Visual Studio 2022 Community or later and CMake 3.28+. Clang is highly recommended over MSVC as it produces much higher quality code, outperforming MSVC by 50-80%. However, MSVC tends to provide a better debugging experience.
All dependencies are included through vcpkg and in the vendor directory, and are built together with the emulator. No external dependencies are needed.
You can choose to generate a .sln file with CMake or open the directory directly with Visual Studio. Both methods work, but opening the directory allows Visual Studio to use Ninja for significantly faster build times. If you choose to generate the .sln file, you will need to specify the vcpkg toolchain:
cmake -DCMAKE_TOOLCHAIN_FILE=vcpkg/scripts/buildsystems/vcpkg.cmake
Ymir uses custom Windows triplets to ensure all libraries are statically linked when possible. You can find the overridden triplets in the vcpkg-triplets folder.
Building on Linux
To build Ymir on Linux, first you will need to install SDL3's required dependencies. Follow the instructions on this page to install them. You might also have to install additional packages:
autoconf autoconf-archive automake libtoolfor ALSApython3 python3-venvfor dbus
The compiler of choice for this platform is Clang. GCC is also supported, but produces slightly slower code.
Use CMake to generate a Makefile or (preferably) a Ninja build script:
cmake -S . -B build -G Ninja -DCMAKE_TOOLCHAIN_FILE=vcpkg/scripts/buildsystems/vcpkg.cmake
Pass additional -D<option>=<value> parameters to tune the build. See the Build configuration section above for details.
CMAKE_BUILD_TYPE defaults to Debug. Use -DCMAKE_BUILD_TYPE=RelWithDebInfo for a Release build with debug symbols.
You can use CMake to build the project, regardless of generator:
cmake --build build --parallel
Building on FreeBSD
Install required packages:
pkg install cmake evdev-proto git gmake libX11 libXcursor libXext libXfixes libXi \
libXrandr libXrender libXScrnSaver libXtst libglvnd libinotify llvm19 ninja patchelf \
pkgconf python3 vulkan-loader zip
Notes:
- A default FreeBSD installation provides a stripped-down LLVM toolchain which lacks
the required
clang-scan-depsbinary. Therefore it is necessary to install a complete LLVM toolchain package, e.g.llvm19. - The usage of CMake's "Precompile Headers" feature triggers a compiler bug in LLVM
prior to version 21 for ARM64 builds on FreeBSD. Therefore it is necessary to install
and use at least
llvm21for ARM64.
Configure build:
CXX=clang++19 \
CC=clang19 \
cmake -S . -B build -G Ninja -DCMAKE_TOOLCHAIN_FILE=vcpkg/scripts/buildsystems/vcpkg.cmake
Notes:
- By default vcpkg and CMake will use the stripped-down LLVM toolchain instead of
the previously installed complete toolchain. Therefore it is necessary to set
the
CXXandCCenvironment variables to the correct compilers.
Pass additional -D<option>=<value> parameters to tune the build. See the Build configuration section above for details.
Build:
cmake --build build --parallel
Ymir uses SDL3's Dialog API. This requires an installed dialog driver in order for the file dialogs to work in Ymir. Install Zenity:
pkg install zenity
Building on macOS
Use CMake to generate a Makefile or (preferably) a Ninja build script:
cmake -S . -B build -G Ninja -DCMAKE_TOOLCHAIN_FILE=vcpkg/scripts/buildsystems/vcpkg.cmake
Pass additional -D<option>=<value> parameters to tune the build. See the Build configuration section above for details.
CMAKE_BUILD_TYPE defaults to Debug. Use -DCMAKE_BUILD_TYPE=RelWithDebInfo for a Release build with debug symbols.
You can use CMake to build the project, regardless of generator:
cmake --build build --parallel
After building, you will find the .app bundle at:
build/apps/ymir-sdl3/ymir-sdl3.app
Profile Guided Optimization (PGO)
PGO is supported for Clang/AppleClang and GCC (MSVC PGO may be added in future releases). Use a two-phase build: first generate profile data, then rebuild using that data.
Windows - Visual Studio IDE, open folder
With the project opened as a folder:
- Select the Full x86-64 [arch] Clang Release/Dist (PGO Generate) target.
- Build and run a representative workload. This will produce a
.profrawfile inout\pgo-profdata\windows\x86_64-[arch]. - In the Solution Explorer view, switch to the CMake Targets view. You can also find it in the View menu.
- Build the
ymir-pgo-mergeutility target. This will combine the.profrawfiles intoymir.profdata. - Select the Full x86-64 [arch] Clang Release/Dist (PGO Use) target.
- Build and run. This build will use profile-guided optimization with the profiling data you acquired from the previous run.
Windows - Visual Studio command line tools
-
Launch the x64 Native Tools Command Prompt for VS 2022/2026.
-
Generate an instrumented build:
cmake -S . -B build-pgo-gen -G Ninja ^ -DCMAKE_BUILD_TYPE=Release ^ -DCMAKE_TOOLCHAIN_FILE=vcpkg/scripts/buildsystems/vcpkg.cmake ^ -DYmir_PGO=GENERATE cmake --build build-pgo-gen --parallel -
Run a representative workload and emit
.profrawinto the PGO directory:set LLVM_PROFILE_FILE="%CD%\build-pgo-gen\pgo-profdata\ymir_%p.profraw" .\build-pgo-gen\apps\ymir-sdl3\ymir-sdl3 -
Merge the raw profiles using the CMake target:
cmake --build build-pgo-gen --target ymir-pgo-mergeOr manually with
llvm-profdata:llvm-profdata merge ^ -o "%CD%\build-pgo-gen\pgo-profdata\ymir.profdata" ^ "%CD%\build-pgo-gen\pgo-profdata"\*.profraw -
Build using the merged profile:
cmake -S . -B build-pgo-use -G Ninja -DCMAKE_BUILD_TYPE=Release ^ -DYmir_PGO=USE ^ -DYmir_PGO_PROFDATA="%CD%\build-pgo-gen\pgo-profdata\ymir.profdata" cmake --build build-pgo-use --parallel
Linux and macOS - Clang / AppleClang
-
Generate an instrumented build:
Linux
cmake -S . -B build-pgo-gen -G Ninja \ -DCMAKE_BUILD_TYPE=Release \ -DCMAKE_TOOLCHAIN_FILE=vcpkg/scripts/buildsystems/vcpkg.cmake \ -DYmir_PGO=GENERATE cmake --build build-pgo-gen --parallelmacOS
cmake -S . -B build-pgo-gen -G Ninja \ -DCMAKE_BUILD_TYPE=Release \ -DCMAKE_TOOLCHAIN_FILE="$PWD/vcpkg/scripts/buildsystems/vcpkg.cmake" \ -DYmir_PGO=GENERATE cmake --build build-pgo-gen --parallel -
Run a representative workload and emit
.profrawinto the PGO directory:export LLVM_PROFILE_FILE="$PWD/build-pgo-gen/pgo-profdata/ymir_%p.profraw" ./build-pgo-gen/apps/ymir-sdl3/ymir-sdl3 -
Merge the raw profiles using the CMake target:
cmake --build build-pgo-gen --target ymir-pgo-mergeOr manually with
llvm-profdata:llvm-profdata merge \ -o "$PWD/build-pgo-gen/pgo-profdata/ymir.profdata" \ "$PWD/build-pgo-gen/pgo-profdata"/*.profraw -
Build using the merged profile:
Linux
cmake -S . -B build-pgo-use -G Ninja -DCMAKE_BUILD_TYPE=Release \ -DYmir_PGO=USE \ -DYmir_PGO_PROFDATA="$PWD/build-pgo-gen/pgo-profdata/ymir.profdata" cmake --build build-pgo-use --parallelmacOS
cmake -S . -B build-pgo-use -G Ninja -DCMAKE_BUILD_TYPE=Release \ -DCMAKE_TOOLCHAIN_FILE="$PWD/vcpkg/scripts/buildsystems/vcpkg.cmake" \ -DYmir_PGO=USE \ -DYmir_PGO_PROFDATA="$PWD/build-pgo-gen/pgo-profdata/ymir.profdata" cmake --build build-pgo-use --parallel
GCC
-
Generate an instrumented build:
cmake -S . -B build-pgo-gen -DCMAKE_BUILD_TYPE=Release -DYmir_PGO=GENERATE cmake --build build-pgo-gen --parallel -
Run a representative workload to emit
.gcdadata intobuild-pgo-gen/pgo-profdata/gcc:./build-pgo-gen/apps/ymir-sdl3/ymir-sdl3Profiles will appear in the subdirectory tree, not flat.
-
Build using the generated profiles (point the use build at the generate build directory):
cmake -S . -B build-pgo-use -DCMAKE_BUILD_TYPE=Release \ -DYmir_PGO=USE \ -DYmir_PGO_DIR="$PWD/build-pgo-gen/pgo-profdata" cmake --build build-pgo-use --parallel
Consuming Ymir as a library
When Ymir is included as a subproject inside another CMake project, it automatically sets Ymir_LIBRARY_ONLY=ON, which
reconfigures the project to only build the ymir::ymir-core static library target without using vcpkg.
The easiest ways to use Ymir as a library are to include it as a Git submodule or by using CMake's FetchContent.
Whatever method is used, be patient -- Ymir includes several large Git submodules that can take some time to download.
Important
Before v0.3.2, Ymir did not offer the option to build the core library alone and required vcpkg to work. You will not be able to use older versions of the emulator as a library in this manner without a ton of headaches due to the combined use of vcpkg integration and Git submodules as dependencies.
Version 0.3.2 fixes this by offering the Ymir_LIBRARY_ONLY option explained above.
Using a Git submodule
It is recommended to keep your vendored dependencies in a subdirectory of your repository, usually called vendor or
third_party. Inside it, run these commands:
git submodule add https://github.com/StrikerX3/Ymir.git
git submodule update --init --recursive
Once that is done, adding Ymir to your project is as simple as adding these instructions to your CMakeLists.txt:
add_subdirectory(vendor/Ymir)
target_link_libraries(your-target PRIVATE ymir-core)
With this method, you can easily keep your dependency up-to-date by git pulling the latest commit from within the
Ymir subdirectory. This method also lets you tinker with the emulator's source code at any time.
Using FetchContent
With FetchContent, the Git cloning process is automated by CMake. Add these instructions to your CMakeLists.txt:
include(FetchContent)
FetchContent_Declare(
ymir
GIT_REPOSITORY https://github.com/StrikerX3/Ymir
GIT_TAG v0.3.2 # ideally, a specific tag or commit, but `main` also works
)
FetchContent_MakeAvailable(ymir)
To link your target against Ymir:
add_subdirectory(vendor/Ymir)
target_link_libraries(your-target PRIVATE ymir-core)
With this option, you cannot modify Ymir's source code. Additionally, the dependency is recursively cloned for each build configuration you use, which could waste disk space.