Cross-Compilation & Toolchain
TEKNİK REHBER CROSS-COMPILATION MESON 2026

Meson —
cross file ile ARM/RISC-V derleme.

Cross file anatomisi, exe_wrapper ile QEMU testi, pkg-config entegrasyonu ve musl-libc cross derleme. crosstool-NG ile Meson combo.

00 Meson cross-compilation mimarisi

Meson, cross-compilation için INI formatında "cross file" kullanır. CMake'in toolchain.cmake'ine karşılık gelir, ancak daha okunabilir bir sözdizimi vardır.

Genel akış

  meson setup builddir --cross-file arm-cortex-a53.ini
         │
         ↓
  cross file okunur
  ┌─────────────────────────────────────────────────────────┐
  │  [host_machine]    → hedef sistemin özellikleri         │
  │  [binaries]        → derleyici ve araç yolları          │
  │  [properties]      → CPU ve platform özellikleri        │
  │  [built_in_options]→ varsayılan derleme bayrakları      │
  └─────────────────────────────────────────────────────────┘
         │
         ↓
  Compiler introspection (ARM gcc ile)
  Dependency detection (pkg-config wrapper ile)
         │
         ↓
  Ninja build dosyaları üretilir
    

Meson kurulumu

bash
# pip ile güncel sürüm
pip3 install --user meson ninja

# veya sistem paketi
sudo apt install meson ninja-build

meson --version
# 1.3.0
BUILD vs HOST (MESON TERMİNOLOJİSİ)

Dikkat: Meson'un terminolojisi diğer araçlardan tersdir. Meson'da build_machine derlemenin yapıldığı makine (x86_64 iş istasyonu), host_machine binary'nin çalışacağı hedef (ARM kart). CMake ve autotools'daki "host" ile "target" kavramlarıyla karıştırma.

01 Cross file formatı — machine / binaries / properties

Cross file, INI formatında bölümlerden oluşur. Her bölüm farklı bir yapılandırma kategorisini kapsar.

Tam cross file yapısı

arm-cortex-a53.ini — tüm bölümler
[host_machine]
# Hedef sistemin özellikleri
system     = 'linux'       # işletim sistemi
cpu_family = 'arm'         # CPU ailesi: arm, aarch64, riscv32, riscv64, x86, x86_64
cpu        = 'cortex-a53'  # spesifik CPU (meson.get_compiler().cpu() ile okunur)
endian     = 'little'      # endianness: little veya big

[binaries]
# Cross derleyici ve araç yolları
c        = 'arm-unknown-linux-gnueabihf-gcc'
cpp      = 'arm-unknown-linux-gnueabihf-g++'
ar       = 'arm-unknown-linux-gnueabihf-ar'
as       = 'arm-unknown-linux-gnueabihf-as'
ld       = 'arm-unknown-linux-gnueabihf-ld'
strip    = 'arm-unknown-linux-gnueabihf-strip'
objcopy  = 'arm-unknown-linux-gnueabihf-objcopy'
objdump  = 'arm-unknown-linux-gnueabihf-objdump'
ranlib   = 'arm-unknown-linux-gnueabihf-ranlib'
nm       = 'arm-unknown-linux-gnueabihf-nm'
pkgconfig = '/home/user/bin/arm-linux-gnueabihf-pkg-config'

# QEMU ile cross test (opsiyonel)
exe_wrapper = ['qemu-arm-static', '-L',
    '/home/user/x-tools/arm-unknown-linux-gnueabihf/arm-unknown-linux-gnueabihf/sysroot']

[properties]
# Hedef sistem hakkında ek bilgi
sys_root = '/home/user/x-tools/arm-unknown-linux-gnueabihf/arm-unknown-linux-gnueabihf/sysroot'

# Meson'un test edemediği özellikler (cross ortamda çalıştıramaz)
sizeof_int    = 4
sizeof_long   = 4
sizeof_void*  = 4

# pkg-config için sysroot
pkg_config_libdir = [
    '/home/user/.../sysroot/usr/lib/pkgconfig',
    '/home/user/.../sysroot/usr/share/pkgconfig'
]

[built_in_options]
# Varsayılan derleme seçenekleri
c_args   = ['-march=armv8-a', '-mcpu=cortex-a53', '-mfpu=neon-fp-armv8', '-mfloat-abi=hard']
cpp_args = ['-march=armv8-a', '-mcpu=cortex-a53', '-mfpu=neon-fp-armv8', '-mfloat-abi=hard']
c_link_args   = ['--sysroot=/home/user/.../sysroot']
cpp_link_args = ['--sysroot=/home/user/.../sysroot']

cpu_family değerleri

arm
32-bit ARM (ARMv4T - ARMv7). Cortex-A5/7/8/9/15/17 ve üstü 32-bit modunda.
aarch64
64-bit ARM (ARMv8+). Cortex-A53/55/72/73 64-bit modunda, Raspberry Pi 4 AArch64.
riscv32
RISC-V 32-bit. Küçük MCU-class RISC-V sistemleri.
riscv64
RISC-V 64-bit. SiFive HiFive, BeagleV Ahead, StarFive VisionFive.

02 exe_wrapper — QEMU ile cross test

exe_wrapper, Meson'un cross ortamda ürettiği binary'leri çalıştırmasını sağlar. Derleme testleri ve unit testler için kritiktir.

QEMU user-mode kurulumu

bash
# QEMU user-mode static binary'leri kur
sudo apt install qemu-user-static binfmt-support

# binfmt desteğini etkinleştir (ARM binary'leri otomatik çalıştırır)
sudo update-binfmts --enable qemu-arm
sudo update-binfmts --enable qemu-aarch64

# test
qemu-arm-static --version
# qemu-arm version 8.x.x

# ARM binary'yi doğrudan çalıştır (binfmt aktifse)
./arm-binary
# veya açıkça
qemu-arm-static ./arm-binary

Cross file'da exe_wrapper

arm-cortex-a53.ini — exe_wrapper
[binaries]
# ... diğer araçlar ...

# QEMU ile çalıştır + sysroot belirt
exe_wrapper = [
    'qemu-arm-static',
    '-L', '/home/user/x-tools/arm-unknown-linux-gnueabihf/arm-unknown-linux-gnueabihf/sysroot'
]

Meson test çalıştırma

bash
meson setup builddir --cross-file arm-cortex-a53.ini
cd builddir
ninja

# testleri QEMU ile çalıştır
ninja test
# [1/3] test_basic  PASSED  0.12s (via qemu-arm-static)
# [2/3] test_math   PASSED  0.08s (via qemu-arm-static)
# [3/3] test_io     PASSED  0.15s (via qemu-arm-static)
EXE_WRAPPER OLMADAN

exe_wrapper tanımlanmazsa Meson, cross ortamda derleyici özellik testlerini ve unit testleri atlayabilir. Derleme hâlâ çalışır ama bazı özellik algılamaları properties bölümündeki sabit değerlerle yapılır.

03 --cross-file parametresi ve birden fazla cross file

Meson, birden fazla cross file kabul eder — sonrakiler önceki dosyaları geçersiz kılar. Bu, temel toolchain dosyasını değiştirmeden proje-spesifik ayarlar eklemeye olanak tanır.

bash
# tek cross file
meson setup builddir \
    --cross-file arm-cortex-a53.ini \
    --buildtype release

# birden fazla cross file (ikinci birincinin üzerine yazar)
meson setup builddir \
    --cross-file toolchains/arm-base.ini \
    --cross-file projects/myproject-arm.ini \
    --buildtype release

# native file ile birlikte
meson setup builddir \
    --native-file native-x86_64.ini \
    --cross-file arm-cortex-a53.ini

Proje-spesifik ek cross file

myproject-arm.ini
# Temel arm-cortex-a53.ini üzerine proje-spesifik ayarlar
# Bu dosya arm-cortex-a53.ini'yi geçersiz kılmaz, tamamlar

[built_in_options]
# Proje özel derleme seçenekleri (temel dosyadakilerle birleşir)
c_args   = ['-DMYPROJECT_VERSION="1.2.3"', '-DENABLE_FEATURE_X=1']

Meson introspect ile cross bilgisini sorgula

bash
cd builddir

# cross-compile bilgisini göster
meson introspect --machines
# {
#   "host": {"cpu": "cortex-a53", "cpu_family": "arm", ...},
#   "build": {"cpu": "x86_64", "cpu_family": "x86_64", ...}
# }

# kullanılan derleyicileri göster
meson introspect --compilers
# {"c": {"id": "gcc", "exelist": ["arm-unknown-linux-gnueabihf-gcc"], ...}}

04 pkg-config entegrasyonu cross dosyada

Meson, bağımlılıkları bulmak için pkg-config kullanır. Cross ortamda cross sistemine ait pkg-config wrapper'ı kullanılmalıdır.

Cross file'da pkgconfig ayarı

arm-cortex-a53.ini — pkgconfig bölümü
[binaries]
# ... derleyiciler ...
pkgconfig = '/home/user/bin/arm-linux-gnueabihf-pkg-config'

[properties]
# pkg-config arama dizinleri
pkg_config_libdir = [
    '/home/user/x-tools/arm-unknown-linux-gnueabihf/arm-unknown-linux-gnueabihf/sysroot/usr/lib/pkgconfig',
    '/home/user/x-tools/arm-unknown-linux-gnueabihf/arm-unknown-linux-gnueabihf/sysroot/usr/share/pkgconfig'
]

meson.build'de dependency kullanımı

meson.build
project('myapp', 'c', version: '1.0.0')

# pkg-config üzerinden bağımlılık bul
openssl_dep = dependency('openssl', version: '>=1.1.0')
zlib_dep    = dependency('zlib')
curl_dep    = dependency('libcurl', required: false)

# cross ortamda otomatik olarak arm pkg-config kullanılır
executable('myapp', 'main.c',
    dependencies: [openssl_dep, zlib_dep, curl_dep],
)

# cmake bul
# cmake paketi pkg-config desteklemiyorsa
sdl2_dep = dependency('sdl2', method: 'pkg-config')

Bağımlılık durumunu kontrol et

bash
meson setup builddir --cross-file arm-cortex-a53.ini
# Meson üretir:
# Checking for pkg-config binary... /home/user/bin/arm-linux-gnueabihf-pkg-config (1.8.1)
# Dependency openssl found: YES 3.0.2
# Dependency zlib found: YES 1.2.11
# Dependency libcurl found: NO (disabled)

05 Native file vs cross file farkı

Native file, derleme makinesi (build machine) için araçları tanımlar. Cross file ise hedef makine (host machine) için. İkisi birbirini tamamlar.

cross file [host_machine]
Hedef sistem özellikleri. Binary'nin çalışacağı platform — ARM, RISC-V vb.
native file [binaries]
Build makinesindeki araçlar. Code generation (moc, protoc, flatc gibi araçlar) için kullanılacak binary'ler.
native file [properties]
Build makinesi özellikleri. Genellikle otomatik algılanır, nadiren elle tanımlanır.

Native file örneği

native-x86_64.ini
[binaries]
# Build makinesinde çalışan araçlar (code generation için)
c        = 'gcc'
cpp      = 'g++'
python3  = '/usr/bin/python3'

# protobuf code generator (ARM'da çalışamaz, host'ta çalışır)
protoc   = '/usr/bin/protoc'

# Qt moc (host'ta kurulu Qt araçları)
moc      = '/usr/lib/qt6/libexec/moc'
rcc      = '/usr/lib/qt6/libexec/rcc'
uic      = '/usr/lib/qt6/libexec/uic'

Meson'da native vs cross kullanımı

meson.build — native: true
# protobuf: proto dosyalarından C++ üret (build makinesinde çalışır)
protoc = find_program('protoc', native: true)

proto_gen = custom_target('proto_gen',
    input:   'messages.proto',
    output:  ['messages.pb.cc', 'messages.pb.h'],
    command: [protoc, '--cpp_out=.', '@INPUT@'],
    native:  true   # build makinesinde çalışır
)

# ana kütüphane (cross — ARM için derlenir)
mylib = library('mylib',
    'mylib.c',
    proto_gen,      # üretilen dosyaları dahil et
)
bash
# hem native hem cross file ile setup
meson setup builddir \
    --native-file native-x86_64.ini \
    --cross-file arm-cortex-a53.ini

06 meson configure ile cross değiştirme

Bir kez kurulmuş build dizininde cross file değiştirilemez. Yeni hedef için yeni build dizini gerekir. Ancak bazı build seçenekleri değiştirilebilir.

Build seçeneklerini değiştirme

bash
# Mevcut build seçeneklerini listele
meson configure builddir

# build tipini değiştir (cross file değişmez)
meson configure builddir --buildtype=debug

# proje-spesifik seçenek değiştir
meson configure builddir -Denable_tests=false

# c_args ekle (dikkat: mevcut değerlerin üzerine yazar)
meson configure builddir -Dc_args="-DDEBUG_VERBOSE=1"

# sonra yeniden derle
ninja -C builddir

Farklı hedef için yeni build dizini

bash
# ARM 32-bit için
meson setup build-arm32 \
    --cross-file arm-cortex-a53.ini \
    --buildtype release

# ARM 64-bit için (ayrı dizin)
meson setup build-arm64 \
    --cross-file aarch64-cortex-a55.ini \
    --buildtype release

# RISC-V için (ayrı dizin)
meson setup build-riscv64 \
    --cross-file riscv64.ini \
    --buildtype release

# hepsini derle
for dir in build-arm32 build-arm64 build-riscv64; do
    ninja -C $dir
done
BUILD DİZİNİNİ SILMEDEN YENIDEN SETUP

meson setup --wipe builddir --cross-file yeni-cross.ini komutu build dizinini temizleyip yeni cross file ile yeniden konfigüre eder. Cache ve derleme çıktıları silinir, kaynak değişmez.

07 Subproject ve wrap dependency cross'ta

Meson subproject'leri ve .wrap dosyaları, bağımlılıkları kaynak koddan derler. Cross ortamda otomatik olarak aynı cross toolchain kullanılır.

Wrap dosyası ile bağımlılık

subprojects/zlib.wrap
[wrap-file]
directory = zlib-1.3.1
source_url = https://zlib.net/zlib-1.3.1.tar.gz
source_hash = 9a93b2b7dfdac77ceba5a558a580e74667dd6fede4585b91eefb60f03b72df23

[provide]
zlib = zlib_dep
meson.build — wrap ile bağımlılık
project('myapp', 'c')

# önce sistem pkg-config'te ara, bulamazsa subproject'ten al
zlib_dep = dependency('zlib',
    fallback: ['zlib', 'zlib_dep']
)

# sadece subproject'ten al (sistem kütüphanesi kullanma)
openssl_dep = dependency('openssl',
    fallback: ['openssl', 'openssl_dep'],
    default_options: ['default_library=static']
)

executable('myapp', 'main.c',
    dependencies: [zlib_dep, openssl_dep]
)

WrapDB'den bağımlılık

bash
# WrapDB'den mevcut wrap dosyalarını listele
meson wrap list

# zlib wrap dosyasını indir
meson wrap install zlib

# cross compile — subproject otomatik olarak ARM için derlenir
meson setup build-arm \
    --cross-file arm-cortex-a53.ini \
    --wrap-mode=forcefallback  # sistem kütüphanesini yoksay, hep subproject kullan

ninja -C build-arm
WRAP MODU

--wrap-mode=nofallback: subproject fallback'leri devre dışı bırak (sadece sistem kütüphanesi). --wrap-mode=forcefallback: her zaman subproject kullan. Cross ortamda forcefallback kullanmak güvenlidir — tüm bağımlılıklar hedef mimari için derlenir.

08 Pratik: ARM aarch64 cross file örneği

Raspberry Pi 4 (Cortex-A72, AArch64) için tam cross file — pkg-config, QEMU ve sysroot dahil.

aarch64-rpi4.ini
# ════════════════════════════════════════════════════════════
# Meson Cross File — Raspberry Pi 4 (AArch64 / Cortex-A72)
# Kullanım: meson setup builddir --cross-file aarch64-rpi4.ini
# ════════════════════════════════════════════════════════════

[host_machine]
system     = 'linux'
cpu_family = 'aarch64'
cpu        = 'cortex-a72'
endian     = 'little'

[binaries]
c        = 'aarch64-unknown-linux-gnu-gcc'
cpp      = 'aarch64-unknown-linux-gnu-g++'
ar       = 'aarch64-unknown-linux-gnu-ar'
strip    = 'aarch64-unknown-linux-gnu-strip'
objcopy  = 'aarch64-unknown-linux-gnu-objcopy'
pkgconfig = '/home/user/bin/aarch64-linux-gnu-pkg-config'
exe_wrapper = [
    'qemu-aarch64-static',
    '-L', '/home/user/x-tools/aarch64-unknown-linux-gnu/aarch64-unknown-linux-gnu/sysroot'
]

[properties]
sys_root = '/home/user/x-tools/aarch64-unknown-linux-gnu/aarch64-unknown-linux-gnu/sysroot'
sizeof_int   = 4
sizeof_long  = 8
sizeof_void* = 8
pkg_config_libdir = [
    '/home/user/x-tools/aarch64-unknown-linux-gnu/aarch64-unknown-linux-gnu/sysroot/usr/lib/pkgconfig',
    '/home/user/x-tools/aarch64-unknown-linux-gnu/aarch64-unknown-linux-gnu/sysroot/usr/share/pkgconfig'
]

[built_in_options]
c_args   = ['-mcpu=cortex-a72', '-O2', '-ffunction-sections', '-fdata-sections']
cpp_args = ['-mcpu=cortex-a72', '-O2', '-ffunction-sections', '-fdata-sections']
c_link_args   = ['--sysroot=/home/user/x-tools/aarch64-unknown-linux-gnu/aarch64-unknown-linux-gnu/sysroot', '-Wl,--gc-sections']
cpp_link_args = ['--sysroot=/home/user/x-tools/aarch64-unknown-linux-gnu/aarch64-unknown-linux-gnu/sysroot', '-Wl,--gc-sections']

Kullanım

bash
# setup
meson setup build-rpi4 \
    --cross-file aarch64-rpi4.ini \
    --buildtype release \
    --strip

# derleme
ninja -C build-rpi4 -j$(nproc)

# test (QEMU ile)
ninja -C build-rpi4 test

# RPi'ye kopyala ve çalıştır
scp build-rpi4/myapp pi@raspberrypi.local:/home/pi/
ssh pi@raspberrypi.local "./myapp"

09 musl-libc ile cross ve Meson + crosstool-NG combo

musl-libc, statik linkinge uygun hafif C kütüphanesidir. crosstool-NG ile musl toolchain oluştururken Meson cross file'ı da buna göre ayarlamak gerekir.

musl cross file

arm-musl-cortex-a53.ini
[host_machine]
system     = 'linux'
cpu_family = 'arm'
cpu        = 'cortex-a53'
endian     = 'little'

[binaries]
c        = 'arm-unknown-linux-musleabihf-gcc'
cpp      = 'arm-unknown-linux-musleabihf-g++'
ar       = 'arm-unknown-linux-musleabihf-ar'
strip    = 'arm-unknown-linux-musleabihf-strip'
pkgconfig = '/home/user/bin/arm-linux-musleabihf-pkg-config'
exe_wrapper = ['qemu-arm-static', '-L',
    '/home/user/x-tools/arm-unknown-linux-musleabihf/arm-unknown-linux-musleabihf/sysroot']

[properties]
sys_root = '/home/user/x-tools/arm-unknown-linux-musleabihf/arm-unknown-linux-musleabihf/sysroot'
pkg_config_libdir = [
    '/home/user/x-tools/arm-unknown-linux-musleabihf/arm-unknown-linux-musleabihf/sysroot/usr/lib/pkgconfig'
]

[built_in_options]
# musl için statik link önerilir
c_args        = ['-march=armv8-a', '-mcpu=cortex-a53', '-mfpu=neon-fp-armv8', '-mfloat-abi=hard']
c_link_args   = ['--sysroot=/home/user/x-tools/arm-unknown-linux-musleabihf/arm-unknown-linux-musleabihf/sysroot', '-static']
cpp_args      = ['-march=armv8-a', '-mcpu=cortex-a53', '-mfpu=neon-fp-armv8', '-mfloat-abi=hard']
cpp_link_args = ['--sysroot=/home/user/x-tools/arm-unknown-linux-musleabihf/arm-unknown-linux-musleabihf/sysroot', '-static']

crosstool-NG + Meson tam iş akışı

bash
# ── ADIM 1: crosstool-NG ile musl toolchain derle ─────────────
mkdir ~/toolchains/arm-musl && cd ~/toolchains/arm-musl
ct-ng arm-unknown-linux-musleabihf
ct-ng menuconfig
# C-library → musl seç
# Target → cortex-a53, neon-fp-armv8, hard-float
ct-ng build.$(nproc)

# ── ADIM 2: pkg-config wrapper oluştur ────────────────────────
cat > ~/bin/arm-linux-musleabihf-pkg-config <<'EOF'
#!/bin/bash
SYSROOT="${HOME}/x-tools/arm-unknown-linux-musleabihf/arm-unknown-linux-musleabihf/sysroot"
export PKG_CONFIG_SYSROOT_DIR="${SYSROOT}"
export PKG_CONFIG_LIBDIR="${SYSROOT}/usr/lib/pkgconfig"
export PKG_CONFIG_PATH=""
exec pkg-config "$@"
EOF
chmod +x ~/bin/arm-linux-musleabihf-pkg-config

# ── ADIM 3: Meson ile cross-compile ───────────────────────────
cd ~/projects/myapp
meson setup build-musl-arm \
    --cross-file arm-musl-cortex-a53.ini \
    --buildtype release \
    --default-library static  # musl ile statik tercih et

ninja -C build-musl-arm -j$(nproc)

# ── ADIM 4: Binary boyutunu kontrol et ────────────────────────
arm-unknown-linux-musleabihf-size build-musl-arm/myapp
file build-musl-arm/myapp
# ELF 32-bit LSB executable, ARM, EABI5, statically linked

# musl statik binary — hedef sistemde hiçbir kütüphane gerektirmez
qemu-arm-static build-musl-arm/myapp
MUSL + STATİK'İN AVANTAJLARI

musl ile statik linklenmiş binary, hedef sistemde herhangi bir dinamik kütüphane gerektirmez. Minimal rootfs (BusyBox benzeri) kurulumlarında, güvenlik kritik uygulamalarda ve containerize edilmiş embedded sistemlerde ideal yaklaşımdır. Binary boyutu glibc dinamik'e kıyasla genellikle daha küçük olur.