Meson Build Template

The meson-build.yml template provides a standardized workflow for building projects using the Meson build system across multiple platforms and toolchains in a GitLab CI environment.

Overview

This template enables you to:

  • Build your project with Meson for various target platforms and architectures using the multiplatform OCI Images.

  • Support both native and cross-compilation via Meson cross files.

  • Generate build artifacts for use in Meson Test Template.

  • Optionally enable coverage reporting with Gcovr for supported environments, with an aggregate summary provided by Gcovr Summary Template.

Pre-built images such as llvm-meson from this repository are suitable for most Meson-based builds (see Pre-built Images). These images include all necessary tools for Meson, LLVM, and GNU toolchain builds. If your project needs additional dependencies, you can extend these images by adding custom layers or modifications in OCI Image Build Template.

Usage in CI

Typically, this template is included in CI pipelines (see Examples) to define jobs for each supported platform. Each job uses the template to set up the build environment, configure Meson, and compile the project. Coverage can be enabled for native builds.

Parallel matrix limitations

Note

Currently, full CI parallel matrix support is limited due to the following GitLab CI issues: #396845, #423553, #255311. At present, the build matrix is only used to build for different toolchains. Only GNU and LLVM toolchains are supported at this point.

Inputs

The template accepts the following inputs:

Target

  • target: Build target in the form OS-TYPE-ARCH (e.g., linux-native-amd64). This is similar to an OCI platform string but uses a hyphen instead of a slash, and includes a TYPE (either native or cross). See Platform Naming for details.

  • toolchain: Array of toolchains to test. Each toolchain should have a corresponding Meson cross file. Used to create a job matrix. Default: [gnu, llvm].

  • qemu_cpu: QEMU_CPU environment variable used by the OCI runner (which uses QEMU). Not used for targets running natively on the runners. Default: "".

    Set this to a specific value if needed to properly discover a platform during the build.

Meson

  • meson_source_directory: Directory containing the Meson project. Default: "."".

  • meson_cross_directory: Directory containing Meson cross files. Cross files should follow the format ${TARGET}-${TOOLCHAIN}.meson. If the cross-file is not found in the directory, it uses the default from /opt/meson-cross from the OCI image. See Meson Cross Files. Default: .gitlab-ci.d/meson-cross.

  • meson_c_args: Extra CARGS (C compiler arguments) to pass to Meson builds. Default: "".

  • meson_setup_args: Extra arguments passed to meson setup. Default: "".

  • meson_compile_args: Extra arguments passed to meson compile. Default: "".

  • enable_gnu_coverage: Enable coverage build flags. It can be later used to collect a coverage report for all the jobs (with Gcovr Summary Template). It should be enabled only for native build environments as they have all the optional dependencies, and are the most reliable and uniform, which is required by Gcovr to properly parse the coverage reports. Disable for cross environments. Default: true.

CI Job

  • allow_failure: Set the allow_failure flag for jobs expected to fail. Set retry input to 0 to prevent unnecessary retries. Default: false.

  • artifact_expiry_in: Set how long GitLab keeps the artifacts. Build artifacts are not strictly needed afterwards besides the test job, so it can be set to short period of time to decrease the usage of artifact storage. Default: 1 day.

  • extends: Array of jobs to extend this job template. Must include an OCI image template (see Top-level Requirements) and can include target activation pattern. Default: [.ci-multiplatform-base].

  • job_name_prefix: Prefix for the job name. Useful for disabling or customizing jobs. Default: "".

  • job_name_suffix: Suffix for the job name. Can be used to prevent job duplication for jobs for the same target. Default: "".

  • oci_job: OCI image build job name. Can remain unchanged if no image is built in the pipeline. Default: oci.

  • runner_tag: GitLab runner tag for this job. Default: "".

  • stage: CI stage name for the job. Default: build.

Meson Cross Files

Meson cross files are configuration files used by Meson to enable cross-compilation, specify custom toolchains for native builds, or set project options. They describe the properties of the target platform, compilers, and tools, allowing Meson to generate correct build rules for different environments.

This template requires cross files even for native builds, making the template more uniform and predictable, but if no modifications are required, the template uses the default cross-files. In this codebase, Meson cross files are named ${TARGET}-${TOOLCHAIN}.meson (e.g., linux-native-amd64-gnu.meson) and stored in the directory specified by meson_cross_directory input (default: .gitlab-ci.d/meson-cross). These files are referenced during CI builds to select the appropriate toolchain and platform configuration.

The default cross-files are located in the Meson OCI image in /opt/meson-cross directory, and in this repo under oci/debian/meson-cross. You can use them as a base for customization.

Besides use in CI, using cross files in development provides a standard target description, allowing you to specify project options and CFLAGS. You can use the cross file during setup, for example:

$ meson setup build \
    --cross-file .gitlab-ci.d/meson-cross/linux-native-amd64-gnu.meson

You can also create a single native-gnu.meson/native-llvm.meson and create symbolic links for all targets using the native toolchain, as done in the Pixman project in .gitlab-ci.d/meson-cross. The following examples are from Pixman.

Refer to the Meson documentation for more details on cross file syntax: https://mesonbuild.com/Cross-compilation.html

Native GNU (Linux)

native-gnu.meson
[binaries]
c = 'gcc'
ar = 'ar'
strip = 'strip'
pkg-config = 'pkg-config'

Uses GCC and standard GNU tools for native builds. No platform section is needed, as host matches target.

Native LLVM (Linux)

native-llvm.meson
[binaries]
c = 'clang'
cpp = 'clang++'
ar = 'llvm-ar'
strip = 'llvm-strip'

Uses Clang and LLVM tools for native builds. Similar to Native GNU (Linux), no platform section is needed.

Cross-Compilation GNU (Windows)

windows-cross-amd64-gnu.meson
[binaries]
c = 'x86_64-w64-mingw32-gcc'
ar = 'x86_64-w64-mingw32-ar'
strip = 'x86_64-w64-mingw32-strip'
windres = 'x86_64-w64-mingw32-windres'
exe_wrapper = 'wine'

[project options]
openmp = 'disabled'

[host_machine]
system = 'windows'
cpu_family = 'x86_64'
cpu = 'x86_64'
endian = 'little'

Specifies the cross-compiler for Windows AMD64. Uses Wine as the exe_wrapper to run Windows binaries on Linux. Defines project options and target platform details. It is recommended to disable openmp for Windows targets, as it can intermittently fail for large and concurrent test sets.

Cross-Compilation GNU (Linux)

linux-cross-mips-gnu.meson
[binaries]
c = 'mips-linux-gnu-gcc'
ar = 'mips-linux-gnu-ar'
strip = 'mips-linux-gnu-strip'
exe_wrapper = ['qemu-mips', '-L', '/usr/mips-linux-gnu/']

[host_machine]
system = 'linux'
cpu_family = 'mips32'
cpu = 'mips32'
endian = 'big'

Uses the cross-compiler for MIPS32 (big-endian) targets. Explicitly specifies QEMU as the exe_wrapper.

Here we need to define the host_machine to indicate the target specification to Meson.

Cross-Compilation LLVM (Linux)

linux-cross-mips-llvm.meson
[binaries]
c = ['clang', '-target', 'mips-linux-gnu', '-fPIC', '-DCI_HAS_ALL_MIPS_CPU_FEATURES']
ar = 'llvm-ar'
strip = 'llvm-strip'
exe_wrapper = ['qemu-mips', '-L', '/usr/mips-linux-gnu/']

[built-in options]
c_link_args = ['-target', 'mips-linux-gnu', '-fuse-ld=lld']

[host_machine]
system = 'linux'
cpu_family = 'mips32'
cpu = 'mips32'
endian = 'big'

Similar to the above, but for LLVM. Clang uses -target argument to specify the cross-compilation target.

Explicit QEMU execution wrapper

To use QEMU with specific flags (e.g., CPU specification) as the exe_wrapper, set it as follows (under [binaries]):

  • exe_wrapper = ['qemu-arm', '-cpu', 'arm1136'] uses qemu-arm and sets the CPU used by Meson.

  • exe_wrapper = ['qemu-mips', '-L', '/usr/mips-linux-gnu/'] uses an explicit ELF interpreter prefix.

Managing project options

You can explicitly set some Meson project options by specifying them as follows:

[project options]
mips-dspr2 = 'disabled'

In this example, mips-dspr2 is disabled to build with another MIPS SIMD backend.

Adding CFLAGS

Add custom C compiler flags (CFLAGS) by modifying the c binary, e.g.:

[binaries]
c = ['gcc', '-DCI_HAS_ALL_MIPS_CPU_FEATURES']

Here, CI_HAS_ALL_MIPS_CPU_FEATURES is added as a compile define.

Job Structure

The build and test stages (see Meson Test Template) are split to two stages in the CI pipeline. This allows you to build artifacts once and test them with multiple configurations, such as different SIMD backends or QEMU flags to emulate various processor features (e.g., RVV VLEN for RISC-V). This approach improves efficiency and flexibility, enabling comprehensive testing without redundant builds.

Each job created by the template:

  • Sets up the build directory and enables coverage if requested.

  • Runs meson setup with the appropriate cross file and arguments.

  • Compiles the project with meson compile.

  • Stores build artifacts for the testing stage in build-${TOOLCHAIN} (e.g., build-gnu for the GNU toolchain).

Examples

A typical job definition in build.yml:

spec:
  inputs:
    ci_path:
      description:
        Path to the ci-multiplatform meson-build component with branch name.
      type: string
      default: gitlab.com/riseproject/ci/ci-multiplatform/meson-build@main
    runner_tag_qemu:
      description: Regular x86 runner with QEMU support.
      type: string
      default: "saas-linux-medium-amd64"
---

include:
  # Native x86_64 target.
  - component: $[[ inputs.ci_path ]]
    inputs:
      target: linux-native-amd64
      runner_tag: $[[ inputs.runner_tag_qemu ]]

  # QEMU target with specific `QEMU_CPU`.
  - component: $[[ inputs.ci_path ]]
    inputs:
      target: linux-native-mips64le
      runner_tag: $[[ inputs.runner_tag_qemu ]]
      qemu_cpu: Loongson-3A4000

  # Target with only GNU toolchain and no coverage support.
  - component: $[[ inputs.ci_path ]]
    inputs:
      target: linux-native-mipsel
      runner_tag: $[[ inputs.runner_tag_qemu ]]
      toolchain: [gnu]
      qemu_cpu: 74Kf
      enable_gnu_coverage: false

Adding ci_path and runner_tag_qemu as inputs makes the declaration shorter and easier to update if any inputs change.

For a full example, see Meson Project Example.

Another example is in the Pixman project in .gitlab-ci.d/02-build.yml file.