New features (in development)

New b_freestanding base option

The new b_freestanding base option tells supported compilers that targets do not assume a hosted standard library. GCC-compatible C-family compilers receive -ffreestanding, while clang-cl receives /clang:-ffreestanding.

For Rust targets, the option declares that the crate uses #![no_std]. Meson uses this information when determining the native libraries required by Rust static libraries. Meson does not add the crate attribute itself.

Added support for buildcache

Meson now recognizes BuildCache as a compiler wrapper, so setting e.g. CC="buildcache cc" works like it already does for Ccache and sccache. BuildCache is also auto-detected alongside Ccache and sccache, and is used as a fallback after both of those when no compiler is specified explicitly.

Support for workspace.exclude in Cargo manifests

Directories that are listed in the workspace.exclude field of Cargo.toml are not treated as workspace members anymore. Such packages can still be used through a path dependency, as long as they are placed directly in the subprojects directory. This allows using nested workspaces if needed.

CMake command wrappers accept arguments with backslashes

CMake subprojects can now configure add_custom_command(), add_custom_target(), and set_property() calls whose arguments contain backslashes. Meson's preload wrappers previously expanded those arguments a second time, which could reject valid values such as regular expression backreferences during configuration.

BuildTarget now has an embed_directories keyword argument

This provides an include_directories like path for the c23 and c++26 #embed preprocessor directive.

New search order for find_program()

find_program() searches for programs in machine files and in the system; starting with Meson 1.13.0, the search process is applied separately for each name passed to the function:

  • If the first name is defined in a machine file, Meson uses that entry.

  • Otherwise, Meson searches for the first name in the project's source tree, the directories listed in dirs:, and PATH.

  • If the program is not found or does not satisfy the requested version, Meson tries the next name.

Previous versions of Meson looked for all names in the machine files first. Meson never searched for the program in the system if the machine file pointed to one of the programs, using the program in the machine file even if it did not satisfy the version requirement in meson.build. Likewise, if no name was in a machine file, the search stopped at the first name found in the system, even if a later name would have satisfied the version requirement.

Target introspection reports all direct target dependencies

The depends entry in intro-targets.json now reports direct target dependencies from target inputs, generated sources, extracted objects, internal linking, and explicit depends arguments. Previously it only reported the dependencies attribute used by alias and run targets, so most build target and custom target dependencies were omitted.

Per-subproject options are included in buildoptions introspection

meson introspect --buildoptions and meson-info/intro-buildoptions.json now contain rows for per-subproject options in configured build directories: non-yielding builtin options get one row per subproject (e.g. sub:warning_level), and options with a per-subproject value set (e.g. via -Dsub:c_args=...) are reported with the value in effect for that subproject.

Added support for kache

Meson now recognizes kache as a compiler wrapper, so setting e.g. CC="kache cc" works like it already does for Ccache and sccache. Unlike those, kache is not auto-detected, because it must be invoked with a compiler subcommand (kache cc, kache c++, ...) rather than in front of an arbitrary compiler.

meson format suppression directives

meson format now leaves untouched the lines between # meson-format off and # meson-format on comments. When # meson-format off is not followed by # meson-format on, formatting is disabled until the end of the file. muon fmt's # fmt: off and # fmt: on directives are also recognized.

# meson-format off
sources = files(
  'a.c',      'b.c',
  'c.c',      'd.c',
)
# meson-format on

cpp_rtti is honored for Objective-C++

The cpp_rtti option is now respected when compiling Objective-C++ sources. Setting cpp_rtti=false passes -fno-rtti for .mm files, just like it already did for C++ sources.

Allow using generator with any build_tgt object

Passing any build target to generator.process() is now allowed.

Added support for the QNX qcc/q++ compiler driver

Meson can now detect and use qcc and q++, the C and C++ compiler drivers shipped with the QNX Software Development Platform (SDP), and recognizes QNX as a target platform.

Cross-compiling for QNX

Point a cross file's [binaries] section at qcc/q++ including the required -V target selector, and declare system = 'qnx' in [host_machine], for example:

[binaries]
c = ['qcc', '-Vgcc_ntoaarch64le']
cpp = ['q++', '-Vgcc_ntoaarch64le']

[host_machine]
system = 'qnx'
cpu_family = 'aarch64'
cpu = 'aarch64'
endian = 'little'

Known Issues:

  1. ThinLTO incremental cache doesn't work — -Db_thinlto_cache=true raises a clear error at configure time; plain -Db_lto=true is unaffected. -flto-incremental= is rejected outright by cc1.
  2. No alternate linker selection — c_ld/cpp_ld in a cross/native file (e.g. 'lld', 'gold') raises MesonException. qcc's driver never invokes a separate linker binary itself, and QNX documents no equivalent mechanism.
  3. Stale dependency-file entries after deleting a header — -MP's phony-header rules aren't emitted by this cc1, unlike stock gcc. Narrow, unfixed; only shows up as a missed rebuild until a clean build.
  4. No static sanitizer runtime — -static-libasan is rejected as unknown; only the shared sanitizer runtime can ever be linked.
  5. Sanitizer runtime availability is SDP-version-dependent — SDP 7.1 ships only libubsan (no asan/lsan/tsan at all), so -Db_sanitize=address/=leak fail to link there; SDP 8.0 adds asan/lsan (still no tsan on either SDP). =undefined works on both.
  6. C++20 named modules are broken, differently per SDP — SDP 8.0: -fmodules-ts compiles but silently no-ops on .cppm/.ixx files (no code-level fix possible — it's driver-level source-suffix dispatch before cc1plus ever runs). SDP 7.1: worse — -fmodules-ts itself is hard-rejected as unrecognized on any C++ source. Plain -fmodules is hard-rejected on both SDPs. Only bites a project that explicitly opts into modules via cpp_args.
  7. Symbol-extraction relink-avoidance only applies natively — the gnu_syms() optimization (skip relinking when a shared library's exported symbols haven't changed) only kicks in for a native/self-hosted QNX build; cross-compiling to QNX from another host (the SDP's primary use case) always falls back to unconditional relinking, same as before this work.

Jar files are now installed reproducibly

Installing a jar target used to invoke the jar tool to strip the Class-Path attribute from its manifest, even when there was none. This replaced the manifest's timestamp inside the jar with the time of installation, so two builds of the same sources produced different installed jars.

Manifests are now edited with Python's zipfile module instead. Jars without a Class-Path attribute are installed unmodified, and when the attribute has to be stripped, all entry metadata such as timestamps is preserved. As a side effect, the jar binary is no longer needed at install time.

meson.can_run_host_binaries() now accounts for Rosetta 2 on Apple Silicon

Previously, when cross-compiling for x86_64 on an aarch64 Mac, meson.can_run_host_binaries() (and the underlying needs_exe_wrapper logic) always returned false, even though Rosetta 2 lets these Macs execute x86_64 binaries directly. Meson now detects whether Rosetta 2 is installed and, if so, reports that x86_64 host binaries can run natively without requiring an exe_wrapper.

Rust now supports the has_define method

This method returns true if the given cfg (either a valueless symbol, or name="value") is defined, else false is returned. Rust does not support get_define.

New Rust compiler options matching Cargo profile keys

Several new per-machine Rust compiler options have been added so that the settings found in a Cargo [profile] section can be expressed natively:

  • rust_panic (none, unwind, abort): selects the panic strategy (-C panic=). It is ignored for dylib and proc-macro crates, which must always unwind.
  • rust_overflow_checks (true, false): toggles runtime integer overflow checks (-C overflow-checks=).
  • rust_codegen_units (integer, 0 to use the compiler default): number of code generation units to split a crate into (-C codegen-units=).
  • rust_incremental (true, false): enables incremental compilation (-C incremental=).
  • rust_incremental_cache_dir (path): directory used to store the incremental compilation data. When left empty a per-target directory under the build directory is used.

The remaining Cargo profile keys map onto existing built-in options: opt-level to optimization, debug to debug, debug-assertions to b_ndebug and lto to b_lto.

The Cargo workspace object will also add these automatically based on the value of another new option, rust_cargo_profile.

c_std=clatest support for MSVC

The option enables all currently implemented compiler and standard library features proposed in the next draft C standard, as well as some in-progress and experimental features.

Per-subproject language arguments are now applied

Compiler and linker arguments set for a specific subproject, for example -Dsub:c_args=-DFOO on the command line or c_args in the default_options of a subproject() call, are now added to the compile and link commands of that subproject's targets. Previously such values were accepted and stored but silently ignored.

Similarly, <lang>_args and <lang>_link_args entries in a target's override_options now take effect.

In all cases the per-subproject or per-target value replaces the global value (including flags coming from environment variables such as CFLAGS), it is not appended to it. This matches what get_option('c_args') already returned inside a subproject.

TAP tests report how many subtests were skipped

The result line of a TAP test now reports how many of its subtests were skipped, so environment-gated subtests no longer disappear silently from a passing test:

1/1 project:gated OK              0.02s   21 subtests passed, 5 skipped

Failing TAP tests list their subtests in the summary of failures

The summary of failures, printed with --print-errorlogs or --verbose, now lists the failing and skipped subtests of each failing TAP test, both on the console and in testlog.txt. On the console, --verbose lists all subtests, including the passing ones:

Summary of Failures:

1/1 project:mixed FAIL            0.02s   2/3 subtests passed, 1 skipped
  ▶ sub-fail      FAIL
  ▶ sub-skip      SKIP

Use werror for gnome.generate_gir(fatal_warnings:)

If fatal_warnings is not passed to gnome.generate_gir(), the value of the werror option is now read to determine whether to turn it on. Explicitly passing fatal_warnings: true or fatal_warnings: false still takes precedence over werror.

Wild linker is now supported on Linux

When CC_LD environment variables is set to wild, Meson will configure GCC 16+ or Clang to use Wild as the linker on Linux. You should expect to see line like this in the output: C linker for the host machine: cc ld.wild 0.10.0 confirming that Wild was picked up.

The results of the search are