Tools/Yokai

From Unvanquished
Jump to: navigation, search

Yokai is a CMake framework providing advanced cross-compilation support and features like embedded file C++ generation.

Yokai is meant to be used in CMake projects for C/C++ applications.

📚️

Trivia

In japanese folklore, Yōkai (妖怪) are a class of supernatural entities. The “Yokai” name was used for player classes from the “Dæmon” team in the early “Third race mod” project for Tremulous named “Unvanquished” that was also meant to use a new engine based on XreaL. That third race mod went nowhere, but some names survived. The Unvanquished project became a continuation of the Tremulous gameplay on that engine based on XreaL, the Dæmon name isn't the third race name anymore but the engine name itself, and now the Yokai is not anymore a defunct player model but that CMake framework used in building the Dæmon engine… You can read more about that very old third race project in this blog article by Illwieckz.

Yokai is not exactly a tool in the fact it is not meant to be used as an application, but as a framework included in CMake scripts.

Some command line tools may be provided but are not meant to be used, they are debug tools for developing Yokai.

Status

Yokai is used for building the Unvanquished game, the Dæmon engine, Saigo, and may be used in other Unvanquished-related projects in the future.

Features

Yokai provides:

  • Host system and architecture detection
  • Target system and architecture detection
  • Cross-compilation detection
  • C/C++ compiler detection
  • C++ file embedding

Integration

The best way to integrate Yokai in a CMake project is to store the Yokai directory in your project.

For example if you have a cmake/ subdirectory already configured as a module path:

set(CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake ${CMAKE_MODULE_PATH})

You just have to store the Yokai directory as cmake/Yokai in your project.

Enablement

Everything can be enabled by doing:

include(Yokai/All)

Every detection but no C++ file embedding can be enabled by doing:

include(Yokai/Detection)

System detection can be enabled by doing (this detects both host and target system):

include(Yokai/System)

Architecture detection can be enabled by doing (this detects both host and target architecture):

include(Yokai/Architecture)

C and C++ compiler detection can be enabled by doing (this detects both C and C++ compilers):

include(Yokai/Compiler)

C++ file embedder can be enabled by doing:

include(Yokai/FileEmbedder)

For the target and architecture detection to also generate embedded C++ strings, the file embedder must be enabled first.

Host system and architecture detection

This relies on CMake variables which ultimately relies on things like uname -m or some environment variables according to the system.

Unlike CMake variables, the Yokai variables have the same names and values whatever the host system.

Examples of CMake variables:

  • YOKAI_HOST_SYSTEM_LINUX=ON
  • YOKAI_HOST_SYSTEM_NAME=Linux
  • YOKAI_HOST_ARCH_AMD64=ON
  • YOKAI_HOST_ARCH_NAME=amd64

Target system and architecture detection

The target system and architecture are detected by actually running the preprocessor or the compilers and parsing their output. This means the accuracy of the detection is as good as using ifdef in the code, while not running any native binary. This makes the detection strongly reliable and very portable, making it perfect for cross-compilation.

Unlike CMake variables, the Yokai variables have the same names and values whatever the host system.

Examples of CMake variables:

  • YOKAI_TARGET_SYSTEM_LINUX=ON
  • YOKAI_TARGET_SYSTEM_NAME=Linux
  • YOKAI_TARGET_ARCH_AMD64=ON
  • YOKAI_TARGET_ARCH_NAME=amd64

This also creates compiler definitions in the form of YOKAI_ARCH_AMD64.

When C++ file embedding is enabled, it also creates a C string in the form of YOKAI_ARCH_STRING="amd64".

When host and target system differ, the YOKAY_SYSTEM_CROSS=ON variable is set.

When host and architecture system differ, the YOKAY_ARCH_CROSS=ON variable is set.

C and C++ compiler detection

Except for MSVC for which CMake does a good job, compilers are detected by actually running the preprocessor or the compilers and parsing their output.

Examples of CMake variables:

  • YOKAI_C_COMPILER_GCC=ON
  • YOKAI_CXX_COMPILER_GCC=ON
  • YOKAI_C_COMPILER_CLANG=ON
  • YOKAI_CXX_COMPILER_CLANG=ON
  • YOKAI_C_COMPILER_MSVC=ON
  • YOKAI_CXX_COMPILER_MSVC=ON

This also generate CMake variables for detecting compiler compatibility.

Example with MINGW:

  • YOKAI_CXX_COMPILER_MINGW=ON
  • YOKAI_CXX_COMPILER_GCC_COMPATIBILITY=ON

Example with AppleClang:

  • YOKAI_CXX_COMPILER_APPLECLANG=ON
  • YOKAI_CXX_COMPILER_CLANG_COMPATIBILITY=ON
  • YOKAI_CXX_COMPILER_GCC_COMPATIBILITY=ON

This also creates compiler definitions in the form of YOKAI_C_COMPILER_GCC and YOKAI_CXX_COMPILER_GCC.

When C++ file embedding is enabled, it also creates some C strings in the form of YOKAI_C_COMPILER_STRING=GCC_13.3.0:gcc and YOKAI_CXX_COMPILER_STRING=GCC_13.3.0:g++.

The values are formed this way: <NAME>_<VERSION>:<EXECUTABLE>.

When a compiler is based on another, the C strings are created in the form YOKAI_C_COMPILER_STRING=Saigo_21.0.0/Clang_21.0.0:x86_64-nacl-clang and YOKAI_CXX_COMPILER_STRING=Saigo_21.0.0/Clang_21.0.0:x86_64-nacl-clang++.

The values are then formed this way: <NAME>_<VERSION>/<PARENT_NAME>_<PARENT_VERSION>:<EXECUTABLE>.

The double version string is useful when compilers based on another compiler may use a different version, and it is good to disclose the underlying one, for example AOCC_5.2.0/Clang_17.0.6:clang++ and ICX_2026.0.0/Clang_22.1.0:icpx.

This string can be printed in the application logs, so when a bug report is submitted, application developers can know exactly which compiler was used to reproduce the bug, including the underlying technology.

File embedding

🚧️ TODO: documentation