00 Build system landscape
Build sistemi, derleyici komutlarını otomatize eder, dependency'leri takip eder ve sadece değişen dosyaları yeniden derler.
Bir C/C++ projesini derlemek için en basit yol gcc main.c -o myapp komutunu doğrudan çalıştırmaktır. Ama proje büyüdüğünde — onlarca kaynak dosyası, harici kütüphaneler, farklı platformlar, debug/release konfigürasyonları — bu yaklaşım sürdürülemez. Build sistemi bu problemi çözer:
Kaynak dosyaları → Build sistemi → Binary / kütüphane
(*.c, *.cpp) (bağımlılık grafiği) (*.o → *.a / *.so / ELF)
Neden build system?
Tarihsel timeline
1976 make Stuart Feldman — Bell Labs. "Yeterli" ama taşınabilir değil. 1991 autotools GNU: autoconf + automake + libtool. Taşınabilir ama karmaşık. 1999 SCons Python tabanlı. Güçlü ama yavaş — büyük projelerde ölümcül. 2000 CMake Kitware. Generator modeli: platform-native build files üretir. 2009 Ninja Chromium'dan. CMake backend olarak — hız odaklı. 2012 Meson Python tabanlı sözdizimi, Ninja backend. Modern alternatif. 2015 Bazel Google. Hermetic build — monorepo ve büyük ölçek için.
Karşılaştırma tablosu
| Araç | Öğrenme eğrisi | Platform | Ecosystem | Hız |
|---|---|---|---|---|
| make | Düşük–Orta | Unix-centric | Legacy, geniş | Orta |
| autotools | Yüksek | Unix/Linux | GNU projeler | Yavaş |
| CMake | Orta | Cross-platform | C/C++ dominant | Hızlı (Ninja ile) |
| Meson | Düşük–Orta | Cross-platform | Büyüyor | Hızlı |
| Bazel | Çok yüksek | Cross-platform | Google/monorepo | Çok hızlı (cache) |
Embedded Linux'ta gerçek hayat
Embedded Linux dünyasında hangi araçların kullanıldığını bilmek, hangi beceriyi önce öğreneceğini belirler:
Hem Makefile hem CMake öğren. Makefile'ı anlamadan CMake'in ne ürettiğini kavrayamazsın. CMake olmadan modern cross-platform veya Zephyr projelerinde çalışamazsın.
Bu bölümde
- Build sisteminin temel amacı: otomasyon, dependency tracking, incremental build
- make → autotools → CMake → Meson → Bazel tarihsel sırası
- Embedded Linux ekosisteminde CMake dominant; Yocto + Zephyr + PlatformIO
01 Makefile temelleri
Makefile, target-prerequisite-recipe üçlüsü üzerine kurulu bir bağımlılık yönetim dilidir.
Temel sözdizimi
Her Makefile kuralı üç parçadan oluşur: hedef (target), ön koşullar (prerequisites) ve tarif (recipe). Recipe satırı mutlaka bir tab ile başlamalıdır — space kullanılamaz.
# target: prerequisites
# [TAB]recipe
target: prerequisite1 prerequisite2
recipe_komutu arg1 arg2
# Örnek: main.c ve utils.c varsa → myapp oluştur
myapp: main.o utils.o
gcc -o myapp main.o utils.o
main.o: main.c main.h
gcc -c main.c
utils.o: utils.c utils.h
gcc -c utils.c
Recipe satırları tab karakteri ile başlamalıdır. Space ile başlarsa Makefile:N: *** missing separator. Stop. hatası alırsın. Bu, 1976'dan beri gelen tarihsel bir tasarım kararıdır ve hâlâ geçerlidir.
Phony targets
Phony target, karşılığında gerçek bir dosya olmayan sanal hedeftir. .PHONY bildirimi yapılmazsa make, aynı isimde bir dosya olup olmadığını kontrol eder.
.PHONY: all clean help install
all: myapp
@echo "Build tamamlandı."
clean:
rm -f myapp *.o *.d
help:
@echo "Hedefler: all clean install help"
install: myapp
install -m 755 myapp /usr/local/bin/
Basit C projesi — tam Makefile
İki kaynak dosyalı (main.c + utils.c) bir C projesi için gerçek bir Makefile:
CC := gcc
CFLAGS := -Wall -Wextra -std=c11 -O2
TARGET := myapp
SRCS := main.c utils.c
OBJS := $(SRCS:.c=.o)
.PHONY: all clean
all: $(TARGET)
# Link: .o dosyalarından binary oluştur
$(TARGET): $(OBJS)
$(CC) $(CFLAGS) -o $@ $^
# Compile: her .c → .o
%.o: %.c
$(CC) $(CFLAGS) -c $< -o $@
clean:
rm -f $(TARGET) $(OBJS)
Otomatik header dependency tracking
Bir header dosyası değiştiğinde, onu include eden tüm .c dosyalarının yeniden derlenmesi gerekir. Bunu otomatik yapmak için gcc'nin -MMD flag'ini kullan:
CC := gcc
CFLAGS := -Wall -Wextra -std=c11 -O2
TARGET := myapp
SRCS := main.c utils.c
OBJS := $(SRCS:.c=.o)
DEPS := $(SRCS:.c=.d) # her .c için bir .d dependency dosyası
.PHONY: all clean
all: $(TARGET)
$(TARGET): $(OBJS)
$(CC) -o $@ $^
# -MMD: .d dosyası üret; -MP: sahte hedef ekle (eksik header için hata verme)
%.o: %.c
$(CC) $(CFLAGS) -MMD -MP -c $< -o $@
# Üretilen .d dosyalarını dahil et
-include $(DEPS)
clean:
rm -f $(TARGET) $(OBJS) $(DEPS)
-include $(DEPS) satırındaki tire (-) öneki, .d dosyaları henüz yoksa (ilk derleme) make'in hata vermesini engeller. İlk derlemeden sonra her derleme aşamasında .d dosyaları güncellenir.
Bu bölümde
- target: prerequisites → [TAB]recipe sözdizimi
- Tab zorunluluğu — space çalışmaz, tarihsel bir kural
- .PHONY: all, clean, help, install tanımları
- -MMD -MP ile otomatik header dependency tracking
02 Makefile değişkenler ve fonksiyonlar
Makefile değişkenleri ve built-in fonksiyonlar, büyük projeleri bakımı kolay hale getirir.
Atama operatörleri
CFLAGS += -DDEBUG yaygın kullanım.# Recursive: CFLAGS her kullanımda $(EXTRA) ile genişler
CFLAGS = -Wall $(EXTRA)
# Simple: OBJS anında hesaplanır, sonraki SRCS değişikliği etkilemez
OBJS := $(SRCS:.c=.o)
# Append: mevcut değere ekle
CFLAGS += -O2
# Conditional: komut satırından make CC=clang ile override edilebilir
CC ?= gcc
Önemli built-in değişkenler
Automatic variables
Pattern rule içinde kullanılan özel değişkenler — her kural için otomatik doldurulur:
gcc -o $@ ... → binary adını yazar.%.o: %.c → main.c için $* = main.Built-in fonksiyonlar
# wildcard: glob ile dosya listesi
SRCS := $(wildcard src/*.c)
# patsubst: pattern substitution — .c → .o
OBJS := $(patsubst %.c,%.o,$(SRCS))
# notdir: path'den dosya adını çıkar
FILENAMES := $(notdir $(SRCS)) # src/main.c → main.c
# addprefix: her öğeye prefix ekle
OBJS := $(addprefix build/,$(notdir $(SRCS:.c=.o)))
# dir: path kısmı
DIRS := $(dir $(SRCS)) # src/main.c → src/
# shell: shell komutu çalıştır, çıktısını al
GIT_HASH := $(shell git rev-parse --short HEAD)
# subst: literal string ikamesi
REL_SRCS := $(subst src/,,$(SRCS)) # src/ prefix'ini kaldır
# filter / filter-out: listeyi filtrele
C_SRCS := $(filter %.c,$(ALL_SRCS))
NO_TEST := $(filter-out %_test.c,$(SRCS))
Multi-directory proje Makefile
CC := gcc
CFLAGS := -Wall -Wextra -std=c11 -Iinclude
LDFLAGS :=
LDLIBS := -lm
TARGET := build/myapp
# src/ ve src/platform/ altındaki tüm .c dosyaları
SRCS := $(wildcard src/*.c) $(wildcard src/platform/*.c)
# src/foo.c → build/foo.o
OBJS := $(patsubst src/%.c,build/%.o,$(SRCS))
DEPS := $(OBJS:.o=.d)
.PHONY: all clean dirs
all: dirs $(TARGET)
dirs:
@mkdir -p build/platform
$(TARGET): $(OBJS)
$(CC) $(LDFLAGS) -o $@ $^ $(LDLIBS)
build/%.o: src/%.c
$(CC) $(CFLAGS) -MMD -MP -c $< -o $@
-include $(DEPS)
clean:
rm -rf build/
Bu bölümde
- = (recursive) vs := (simple) vs += (append) vs ?= (conditional) farkları
- CC, CXX, CFLAGS, CXXFLAGS, LDFLAGS, LDLIBS built-in değişkenleri
- $@, $<, $^, $* automatic variables
- wildcard, patsubst, notdir, addprefix, shell fonksiyonları
03 CMake temelleri
CMake, platform-native build dosyaları üreten bir meta-build sistemidir — doğrudan derlemez, Makefile veya Ninja dosyaları üretir.
CMake generator modeli
CMakeLists.txt → cmake (configure) → Makefile / Ninja / .sln / Xcode ↓ make / ninja / VS (build) ↓ binary / kütüphane
Bu iki aşamalı model sayesinde aynı CMakeLists.txt ile Linux'ta Makefile, Windows'ta Visual Studio solution, macOS'ta Xcode projesi üretebilirsin.
Minimum CMakeLists.txt
# CMake minimum versiyonu — kullanılan özelliklere göre belirle
cmake_minimum_required(VERSION 3.21)
# Proje adı, versiyonu ve kullanılan diller
project(MyProject
VERSION 1.0.0
LANGUAGES C CXX
)
# Executable tanımla
add_executable(myapp
src/main.c
src/utils.c
)
Out-of-source build
CMake projeleri kaynak ağacı dışında derlenir. Build dosyaları kaynak kodla karışmaz:
# build/ dizini oluştur ve içine gir
mkdir build && cd build
# CMake ile configure: üst dizindeki CMakeLists.txt'i işle
cmake ..
# Derleme — platform-native aracı çağırır (make veya ninja)
cmake --build .
# Temizle
cmake --build . --target clean
# Paralel build (tüm CPU çekirdeklerini kullan)
cmake --build . --parallel $(nproc)
# Alternatif: build/ dizininden çıkmadan configure + build
cmake -S . -B build
cmake --build build
Ninja generator — daha hızlı
# Ninja'yı yükle (Ubuntu/Debian)
sudo apt install ninja-build
# Ninja generator ile configure
cmake -G "Ninja" -S . -B build-ninja
# Build — cmake --build ninja'yı otomatik çağırır
cmake --build build-ninja
# Mevcut generator'ları listele
cmake --help | grep "Generators" -A 30
CMake cache ve değişkenler
CMake ilk configure sırasında CMakeCache.txt oluşturur. Sonraki çalıştırmalarda cache'den okur:
# -D ile cache değişkeni set et
cmake -DCMAKE_BUILD_TYPE=Release ..
cmake -DCMAKE_INSTALL_PREFIX=/opt/myapp ..
cmake -DMYAPP_ENABLE_TESTS=ON ..
# Birden fazla -D bir arada
cmake \
-DCMAKE_BUILD_TYPE=Release \
-DCMAKE_INSTALL_PREFIX=/opt/myapp \
-DBUILD_SHARED_LIBS=OFF \
..
# Cache'i sil ve yeniden configure (clean configure)
rm -rf build/ && cmake -S . -B build
TUI ve GUI araçlar
apt install cmake-curses-guiapt install cmake-qt-guiCMakeCache.txt'i elle düzenleme. Bunun yerine cmake -D veya ccmake kullan. Cache bozulursa build/ dizinini tamamen sil ve yeniden configure et.
Bu bölümde
- CMake iki aşamalıdır: configure (cmake) → build (make/ninja)
- cmake -S . -B build ile out-of-source build
- cmake -G "Ninja" ile Ninja generator — paralel build hızı
- cmake -DVAR=val ile CMakeCache.txt üzerinden değişken set etme
04 Targets ve library türleri
CMake'de her şey target etrafında döner — executable, library veya custom target.
Executable ve library tanımları
# ── Executable ──────────────────────────────────────────────
add_executable(myapp
src/main.c
src/app.c
)
# ── Static library (.a) ─────────────────────────────────────
# Derleme zamanında binary'e gömülür. Runtime'da bağımlılık yok.
add_library(mylib_static STATIC
src/utils.c
src/parser.c
)
# ── Shared library (.so / .dll) ─────────────────────────────
# Runtime'da dinamik yüklenir. ABI uyumluluğu önemli.
add_library(mylib_shared SHARED
src/utils.c
src/parser.c
)
# ── Object library (.o dosya koleksiyonu) ───────────────────
# Static ve shared arasında derlenmeden paylaşılır.
add_library(mylib_obj OBJECT
src/utils.c
src/parser.c
)
# ── Interface library (header-only) ─────────────────────────
# Compile edilecek kaynak yok; sadece usage requirements taşır.
add_library(mylib_iface INTERFACE)
target_include_directories(mylib_iface INTERFACE include/)
target_link_libraries ve visibility
CMake 3.x'in en güçlü özelliği: her target kendi bağımlılıklarını bilir ve bunları downstream'e aktarıp aktarmayacağını belirler.
# mylib: libssl'i kullanır ama dışarı aktarmaz (PRIVATE)
add_library(mylib STATIC src/crypto.c)
target_link_libraries(mylib
PRIVATE OpenSSL::SSL # sadece mylib'in implementation'ı kullanır
PUBLIC mylib_headers # mylib de kullanır, mylib'e bağlananlar da
)
# myapp: mylib'e bağlanır → OpenSSL transitif DEĞİL gelir (PRIVATE)
add_executable(myapp src/main.c)
target_link_libraries(myapp
PRIVATE mylib
)
Library seçim rehberi — embedded context
| Tür | Çıktı | Ne zaman |
|---|---|---|
| STATIC | .a | Embedded target, runtime bağımlılık istemiyorsun |
| SHARED | .so | Plugin, büyük kütüphane, ABI kararlılığı gerekli |
| OBJECT | .o koleksiyonu | Ortak kodu hem static hem shared'e gömeceksin |
| INTERFACE | — | Header-only template kütüphanesi, pure CMake hedef |
Embedded sistemlerde genellikle STATIC tercih edilir — deployment sırasında ayrı .so dosyası taşıma gerekliliği ortadan kalkar. Rootfs boyutu kritikse shared da kullanılabilir.
Bu bölümde
- add_executable, add_library (STATIC, SHARED, OBJECT, INTERFACE)
- PRIVATE / PUBLIC / INTERFACE visibility modeli
- Embedded'de STATIC tercih sebebi
05 find_package ve bağımlılıklar
Dış kütüphaneleri bulmanın ve projeye eklemenin birden fazla yolu vardır — find_package, FetchContent ve ExternalProject.
find_package — sistem kütüphanesi bul
# REQUIRED: bulunamazsa hata ver ve dur
find_package(OpenSSL REQUIRED)
find_package(Threads REQUIRED)
find_package(ZLIB REQUIRED)
# OPTIONAL: bulunamazsa devam et, sonraki if ile kontrol et
find_package(OpenMP)
if(OpenMP_FOUND)
target_link_libraries(myapp PRIVATE OpenMP::OpenMP_C)
endif()
# Versiyon gereksinimi
find_package(Boost 1.75 REQUIRED COMPONENTS filesystem system)
# Imported target ile kullan
target_link_libraries(myapp
PRIVATE
OpenSSL::SSL
OpenSSL::Crypto
Threads::Threads
ZLIB::ZLIB
)
Config-mode vs Module-mode
pkg-config entegrasyonu
find_package(PkgConfig REQUIRED)
# pkg_check_modules: pkg-config ile kütüphane bul
pkg_check_modules(LIBCURL REQUIRED libcurl)
pkg_check_modules(GLIB2 REQUIRED glib-2.0>=2.50)
target_include_directories(myapp PRIVATE ${LIBCURL_INCLUDE_DIRS})
target_link_libraries(myapp PRIVATE ${LIBCURL_LIBRARIES})
target_compile_options(myapp PRIVATE ${LIBCURL_CFLAGS_OTHER})
FetchContent — bağımlılığı kaynak koddan indir
Sistem kütüphanesi olmayan veya belirli versiyon gerektiren bağımlılıklar için FetchContent kullan. Build zamanında indirir ve configure eder:
include(FetchContent)
# nlohmann/json — header-only JSON kütüphanesi
FetchContent_Declare(
json
URL https://github.com/nlohmann/json/releases/download/v3.11.3/json.tar.xz
URL_HASH SHA256=d6c65aca6b1ed68e7a182f4757257b107ae403032760ed6ef121c9d55e81757d
)
# GoogleTest
FetchContent_Declare(
googletest
GIT_REPOSITORY https://github.com/google/googletest.git
GIT_TAG v1.14.0
)
# İndir + configure + kaynak ağacına ekle
FetchContent_MakeAvailable(json googletest)
# Artık imported target olarak kullanılabilir
target_link_libraries(myapp PRIVATE nlohmann_json::nlohmann_json)
target_link_libraries(mytest PRIVATE GTest::gtest_main)
ExternalProject_Add — kısa özet
ExternalProject_Add, FetchContent'ten farklı olarak bağımlılığı build aşamasında (configure değil) indirir ve ayrı bir build dizininde derler. Cross-compilation ve non-CMake projeleri (autotools vb.) için tercih edilir. FetchContent ise kaynak ağacına dahil edildiği için CMake target'ları doğrudan kullanılabilir.
Bu bölümde
- find_package: sistem kütüphanesi, REQUIRED / OPTIONAL, versiyon
- Config-mode (Package-Config.cmake) vs Module-mode (FindPackage.cmake)
- pkg_check_modules ile pkg-config entegrasyonu
- FetchContent_Declare + FetchContent_MakeAvailable — git veya URL'den indirme
06 target_* direktifleri ve properties
Modern CMake'de her şey target property'leri üzerinden yönetilir — global değişkenler yerine target-scoped direktifler kullan.
target_include_directories
# include/ klasörünü bu target için arama yoluna ekle
target_include_directories(myapp
PRIVATE include/ # sadece myapp kullanır
PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include # myapp + bağlananlar
INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/api # sadece bağlananlar
)
# Generator expression ile kaynak vs install ayrımı
target_include_directories(mylib
PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:include>
)
target_compile_options
target_compile_options(myapp
PRIVATE
-Wall
-Wextra
-Wpedantic
-Werror # uyarıları hata yap
-fstack-protector-strong
$<$<CONFIG:Debug>:-g -O0>
$<$<CONFIG:Release>:-O3 -DNDEBUG>
)
# Derleyici bazlı koşullu flag (Clang vs GCC)
target_compile_options(myapp PRIVATE
$<$<C_COMPILER_ID:Clang>:-Weverything>
$<$<C_COMPILER_ID:GNU>:-Wformat=2>
)
target_compile_definitions
target_compile_definitions(myapp
PRIVATE
DEBUG=1
VERSION="${PROJECT_VERSION}"
PLATFORM_LINUX
$<$<CONFIG:Debug>:ENABLE_LOGGING> # sadece Debug'da
)
# Makefile'daki -DFOO=1 eşdeğeri
# target_compile_definitions, -D prefix'ini kendisi ekler
target_sources — kaynak ekleme
add_executable(myapp src/main.c)
# Sonradan kaynak ekle (koşullu platform dosyaları için ideal)
target_sources(myapp PRIVATE
src/utils.c
src/parser.c
)
if(CMAKE_SYSTEM_NAME STREQUAL "Linux")
target_sources(myapp PRIVATE src/platform/linux.c)
elseif(CMAKE_SYSTEM_NAME STREQUAL "Darwin")
target_sources(myapp PRIVATE src/platform/macos.c)
endif()
set_target_properties ve get_target_property
# C/C++ standart versiyonunu zorla
set_target_properties(myapp PROPERTIES
C_STANDARD 11
C_STANDARD_REQUIRED ON
C_EXTENSIONS OFF
CXX_STANDARD 17
CXX_STANDARD_REQUIRED ON
OUTPUT_NAME "myapp-v${PROJECT_VERSION}" # binary adını değiştir
RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin
)
# Property oku
get_target_property(MY_SOURCES myapp SOURCES)
message(STATUS "Sources: ${MY_SOURCES}")
# Shared library versiyonlama
set_target_properties(mylib PROPERTIES
VERSION ${PROJECT_VERSION}
SOVERSION ${PROJECT_VERSION_MAJOR}
)
Eski CMake kılavuzlarında include_directories(), add_compile_options() ve add_definitions() gibi global direktifler görürsün. Bunlar tüm sonraki target'ları etkiler ve büyük projelerde istenmeyen yan etkiler yaratır. Modern CMake'de her zaman target_* versiyonlarını kullan.
Bu bölümde
- target_include_directories — PRIVATE / PUBLIC / INTERFACE visibility
- target_compile_options — -Wall, -O2, debug/release generator expression
- target_compile_definitions — -DFOO=1 eşdeğeri
- target_sources — koşullu platform kaynak ekleme
- set_target_properties — C_STANDARD, OUTPUT_NAME, SOVERSION
07 Cross compilation
Cross compilation, hedef platforma (ARM, MIPS, RISC-V) farklı bir host'ta (x86_64 Linux) binary üretmektir — CMake toolchain file ile sistematik şekilde yönetilir.
Toolchain file nedir?
Toolchain file, CMake'e hangi derleyiciyi, hangi sysroot'u ve hangi arama yollarını kullanacağını söyleyen özel bir CMake script'idir. CMAKE_TOOLCHAIN_FILE değişkeniyle belirtilir ve configure aşamasının en başında okunur.
# Hedef sistem bilgisi
set(CMAKE_SYSTEM_NAME Linux)
set(CMAKE_SYSTEM_PROCESSOR arm)
# Cross compiler — PATH'te olmalı
set(CMAKE_C_COMPILER arm-linux-gnueabihf-gcc)
set(CMAKE_CXX_COMPILER arm-linux-gnueabihf-g++)
set(CMAKE_ASM_COMPILER arm-linux-gnueabihf-as)
# Sysroot: hedef sistemin rootfs kopyası
set(CMAKE_SYSROOT /opt/sysroot/arm-linux-gnueabihf)
# find_* komutlarının arama davranışı
set(CMAKE_FIND_ROOT_PATH /opt/sysroot/arm-linux-gnueabihf)
# HOST araçları (cmake, python) host'ta ara
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
# Kütüphaneleri ve header'ları sadece sysroot'ta ara
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY)
# ARM Cortex-A için optimize flag'ler (opsiyonel)
set(CMAKE_C_FLAGS_INIT "-march=armv7-a -mfpu=neon-vfpv4 -mfloat-abi=hard")
set(CMAKE_CXX_FLAGS_INIT "-march=armv7-a -mfpu=neon-vfpv4 -mfloat-abi=hard")
Toolchain file ile configure ve build
# Toolchain yükle (Debian/Ubuntu)
sudo apt install gcc-arm-linux-gnueabihf g++-arm-linux-gnueabihf
# Ayrı bir build dizini kullan — host build'dan ayrı tut
cmake \
-S . \
-B build-arm \
-DCMAKE_TOOLCHAIN_FILE=cmake/arm-linux-gnueabihf.cmake \
-DCMAKE_BUILD_TYPE=Release
cmake --build build-arm --parallel $(nproc)
# Çıktıyı doğrula
file build-arm/myapp
# → build-arm/myapp: ELF 32-bit LSB executable, ARM, EABI5 version 1
# QEMU ile test (opsiyonel)
qemu-arm -L /opt/sysroot/arm-linux-gnueabihf build-arm/myapp
CMAKE_FIND_ROOT_PATH_MODE değerleri
Raspberry Pi için toolchain
set(CMAKE_SYSTEM_NAME Linux)
set(CMAKE_SYSTEM_PROCESSOR aarch64)
set(CMAKE_C_COMPILER aarch64-linux-gnu-gcc)
set(CMAKE_CXX_COMPILER aarch64-linux-gnu-g++)
# Raspberry Pi OS sysroot (rsync ile RPi'den alınmış)
set(CMAKE_SYSROOT /opt/rpi4-sysroot)
set(CMAKE_FIND_ROOT_PATH /opt/rpi4-sysroot)
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
# Cortex-A72 (RPi4) optimizasyonu
set(CMAKE_C_FLAGS_INIT "-mcpu=cortex-a72 -mtune=cortex-a72")
Yocto SDK entegrasyonu
Yocto, cross-compilation ortamını otomatik kuran bir SDK (Software Development Kit) üretir. Bu SDK bir CMake toolchain file içerir:
# SDK'yı yükle (Yocto'dan üretilen installer)
./poky-glibc-x86_64-core-image-minimal-aarch64-qemuarm64-toolchain-4.3.sh
# SDK ortamını aktif et
source /opt/poky/4.3/environment-setup-aarch64-poky-linux
# SDK'nın sağladığı toolchain file ile CMake çalıştır
cmake \
-DCMAKE_TOOLCHAIN_FILE=$OECORE_NATIVE_SYSROOT/usr/share/cmake/OEToolchainConfig.cmake \
-S . -B build-yocto
cmake --build build-yocto
Bu bölümde
- CMAKE_TOOLCHAIN_FILE ile cross compiler, sysroot ve arama modları
- CMAKE_FIND_ROOT_PATH_MODE_PROGRAM=NEVER, LIBRARY/INCLUDE=ONLY
- Raspberry Pi aarch64 ve Yocto SDK entegrasyon örnekleri
- file build-arm/myapp ile ELF doğrulama
08 Generator expressions
Generator expression'lar, CMake'in configure aşamasında değil build/install aşamasında değerlendirilen koşullu ifadelerdir.
Normal CMake değişkenleri configure zamanında değerlendirilir. Ama bazı şeyler ancak o anda bilinir: build tipi (Debug/Release), derleyici ID'si, hedef platform. Generator expression'lar bu bilgilere erişim sağlar.
Temel sözdizimi
# $<condition> → 1 veya 0
$<BOOL:val> # val boş veya 0 değilse 1
$<CONFIG:Debug> # build tipi Debug ise 1
$<PLATFORM_ID:Linux> # platform Linux ise 1
$<C_COMPILER_ID:GNU> # C derleyicisi GCC ise 1
# $<IF:condition,then,else>
$<IF:$<CONFIG:Debug>,-g,-O3>
# $<condition:value> → condition=1 ise value, 0 ise boş string
$<$<CONFIG:Debug>:-fsanitize=address>
Sık kullanılan generator expression'lar
Debug / Release koşullu flag'ler
target_compile_options(myapp PRIVATE
# Debug: adres sanitizer + debug semboller, optimizasyon yok
$<$<CONFIG:Debug>:
-g
-O0
-fsanitize=address
-fsanitize=undefined
-fno-omit-frame-pointer
>
# Release: maksimum optimizasyon
$<$<CONFIG:Release>:
-O3
-DNDEBUG
-ffunction-sections
-fdata-sections
>
# RelWithDebInfo: release + debug semboller
$<$<CONFIG:RelWithDebInfo>:
-O2
-g
-DNDEBUG
>
)
# Linker flag: ASan için link etmek gerekiyor
target_link_options(myapp PRIVATE
$<$<CONFIG:Debug>:-fsanitize=address -fsanitize=undefined>
)
# Kullanım: cmake -DCMAKE_BUILD_TYPE=Debug -S . -B build-debug
Dile göre flag (C vs C++)
# Karma C/C++ projesinde dile özgü standart flag'ler
target_compile_options(myapp PRIVATE
$<$<COMPILE_LANGUAGE:C>: -std=c11 -Wstrict-prototypes>
$<$<COMPILE_LANGUAGE:CXX>: -std=c++17 -Wno-deprecated>
)
# TARGET_FILE: test komutunda binary path'i dinamik al
add_test(NAME mytest
COMMAND $<TARGET_FILE:myapp> --test
)
Bu bölümde
- Generator expression'lar configure zamanı değil, build zamanı değerlendirilir
- $<CONFIG:Debug>, $<IF:cond,then,else>, $<COMPILE_LANGUAGE:C>
- Debug'da -fsanitize=address, Release'de -O3 — koşullu flag örneği
- $<BUILD_INTERFACE:path> vs $<INSTALL_INTERFACE:path>
09 ccache ve build optimizasyonu
ccache, derleme çıktısını önbelleğe alır — aynı kaynak kodu yeniden derlediğinde önbellekten döner ve derleme süresini dramatik düşürür.
ccache nedir?
ccache (Compiler Cache) şeffaf bir derleme önbelleğidir. Girdi (kaynak dosyası + flag'ler + header'lar) değişmediyse önceki derlemenin .o çıktısını doğrudan döner. Büyük projelerde full rebuild süresini %80-95 düşürebilir.
# Yükle
sudo apt install ccache
# Versiyon ve yol
ccache --version
which ccache # → /usr/bin/ccache
# İstatistikler
ccache -s
# Cache'i temizle
ccache -C
# Cache boyutunu ayarla (default 5GB)
ccache -M 10G
CMake entegrasyonu — CMake 3.4+
# Yöntem 1: CMAKE_*_COMPILER_LAUNCHER (önerilen, CMake 3.4+)
find_program(CCACHE_PROGRAM ccache)
if(CCACHE_PROGRAM)
set(CMAKE_C_COMPILER_LAUNCHER ${CCACHE_PROGRAM})
set(CMAKE_CXX_COMPILER_LAUNCHER ${CCACHE_PROGRAM})
message(STATUS "ccache found: ${CCACHE_PROGRAM}")
endif()
# Yöntem 2: global set (CMakeLists.txt'e sabit yaz)
set(CMAKE_C_COMPILER_LAUNCHER ccache)
set(CMAKE_CXX_COMPILER_LAUNCHER ccache)
# Yöntem 3: komut satırından
# cmake -DCMAKE_C_COMPILER_LAUNCHER=ccache ...
ccache -s — istatistik okuma
ccache -s
# Çıktı örneği:
# Summary:
# Hits: 892 / 1024 (87.1%)
# Direct: 712
# Preprocessed: 180
# Misses: 132
# Direct: 105
# Preprocessed: 27
# Primary storage:
# Cache size (GiB): 2.14 / 10.00
# Files: 4816
Ninja vs Make — paralel build hızı
Ninja, make'e kıyasla daha az overhead'e sahip bir build sistemidir. Özellikle incremental build ve büyük projelerde fark belirginleşir:
Precompiled headers (PCH)
# CMake 3.16+: target_precompile_headers
target_precompile_headers(myapp PRIVATE
include/pch.h # sık değişmeyen, büyük header'lar buraya
<vector>
<string>
<unordered_map>
)
# Birden fazla target arasında PCH paylaş
target_precompile_headers(mylib2 REUSE_FROM mylib)
Unity build
# CMake 3.16+: UNITY_BUILD — tüm .c dosyalarını tek bir compilation unit'e toplar
set_target_properties(myapp PROPERTIES
UNITY_BUILD ON
)
# Batch boyutu: kaç dosyayı tek compilation unit'e koy
set_target_properties(myapp PROPERTIES
UNITY_BUILD_BATCH_SIZE 8
)
Unity build, static global değişkenlerin ve anonim namespace'lerin davranışını değiştirebilir. Önce projenin unity build ile düzgün derlenip derlenmediğini doğrula.
Bu bölümde
- ccache: derleme çıktısını önbellekle, full rebuild süresini %80+ düşür
- CMAKE_C_COMPILER_LAUNCHER=ccache ile CMake entegrasyonu
- ccache -s ile hit/miss istatistikleri
- target_precompile_headers (CMake 3.16+) ve UNITY_BUILD property
10 Pratik: eksiksiz C projesi
Library, executable, testler, install ve paketleme içeren tam bir CMake projesi — gerçek dünya yapısı.
Proje yapısı
myproject/
├── CMakeLists.txt # Ana CMake dosyası
├── cmake/
│ ├── arm-linux-gnueabihf.cmake
│ └── myproject-config.cmake.in
├── include/
│ └── myproject/
│ └── core.h # Public API header'ları
├── src/
│ ├── core.c # Library kaynak kodu
│ └── main.c # Executable entry point
└── tests/
├── CMakeLists.txt
└── test_core.c
Ana CMakeLists.txt
cmake_minimum_required(VERSION 3.21)
project(myproject
VERSION 1.2.0
DESCRIPTION "Embedded Linux utility library"
LANGUAGES C
)
# ── ccache ──────────────────────────────────────────────────
find_program(CCACHE ccache)
if(CCACHE)
set(CMAKE_C_COMPILER_LAUNCHER ${CCACHE})
endif()
# ── Build type varsayılanı ───────────────────────────────────
if(NOT CMAKE_BUILD_TYPE)
set(CMAKE_BUILD_TYPE Release CACHE STRING "Build type" FORCE)
endif()
# ── Options ─────────────────────────────────────────────────
option(MYPROJECT_BUILD_TESTS "Build unit tests" ON)
option(MYPROJECT_BUILD_SHARED "Build shared lib" OFF)
# ── Library ─────────────────────────────────────────────────
set(LIB_TYPE STATIC)
if(MYPROJECT_BUILD_SHARED)
set(LIB_TYPE SHARED)
endif()
add_library(myproject_core ${LIB_TYPE}
src/core.c
)
target_include_directories(myproject_core
PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:include>
)
target_compile_options(myproject_core PRIVATE
-Wall -Wextra -Wpedantic
$<$<CONFIG:Debug>:-g -O0 -fsanitize=address>
$<$<CONFIG:Release>:-O2>
)
set_target_properties(myproject_core PROPERTIES
C_STANDARD 11
C_STANDARD_REQUIRED ON
VERSION ${PROJECT_VERSION}
SOVERSION ${PROJECT_VERSION_MAJOR}
)
# ── Executable ──────────────────────────────────────────────
add_executable(myapp src/main.c)
target_link_libraries(myapp PRIVATE myproject_core)
# ── Tests ───────────────────────────────────────────────────
if(MYPROJECT_BUILD_TESTS)
enable_testing()
add_subdirectory(tests)
endif()
# ── Install ─────────────────────────────────────────────────
include(GNUInstallDirs)
install(TARGETS myproject_core myapp
EXPORT myproject-targets
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
)
install(DIRECTORY include/myproject
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
)
install(EXPORT myproject-targets
FILE myproject-targets.cmake
NAMESPACE myproject::
DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/myproject
)
tests/CMakeLists.txt — CTest entegrasyonu
add_executable(test_core test_core.c)
target_link_libraries(test_core PRIVATE myproject_core)
# CTest'e test ekle
add_test(
NAME core_unit_test
COMMAND $<TARGET_FILE:test_core>
)
# Test ortam değişkenleri
set_tests_properties(core_unit_test PROPERTIES
ENVIRONMENT "MYAPP_LOG_LEVEL=debug"
TIMEOUT 30
)
Build, test ve install
# Configure
cmake \
-S . -B build \
-DCMAKE_BUILD_TYPE=Debug \
-DMYPROJECT_BUILD_TESTS=ON
# Build
cmake --build build --parallel $(nproc)
# Test — tüm testleri çalıştır
ctest --test-dir build --output-on-failure
# Install (custom prefix)
cmake --install build --prefix /opt/myproject
# Sonuç dizin yapısı:
# /opt/myproject/
# ├── bin/myapp
# ├── include/myproject/core.h
# └── lib/libmyproject_core.a
CPack ile paketleme
# Ana CMakeLists.txt'e ekle (install direktiflerinden sonra)
set(CPACK_PACKAGE_NAME "myproject")
set(CPACK_PACKAGE_VERSION ${PROJECT_VERSION})
set(CPACK_PACKAGE_CONTACT "dev@example.com")
set(CPACK_GENERATOR "DEB;TGZ")
set(CPACK_DEBIAN_PACKAGE_DEPENDS "libc6 (>= 2.17)")
include(CPack)
# Paket oluştur
# cmake --build build
# cpack --config build/CPackConfig.cmake
# → myproject-1.2.0-Linux.tar.gz
# → myproject_1.2.0_amd64.deb
GitHub Actions CI snippet
name: CI
on: [push, pull_request]
jobs:
build:
runs-on: ubuntu-22.04
strategy:
matrix:
build_type: [Debug, Release]
steps:
- uses: actions/checkout@v4
- name: Install dependencies
run: sudo apt-get install -y ccache ninja-build
- name: Configure
run: |
cmake \
-S . -B build \
-G Ninja \
-DCMAKE_BUILD_TYPE=${{ matrix.build_type }} \
-DCMAKE_C_COMPILER_LAUNCHER=ccache \
-DMYPROJECT_BUILD_TESTS=ON
- name: Build
run: cmake --build build --parallel
- name: Test
run: ctest --test-dir build --output-on-failure
Bu bölümde
- Gerçek proje yapısı: src/, include/, tests/, cmake/ dizinleri
- Library + executable + tests — tek CMakeLists.txt'te
- enable_testing() + add_test() + ctest ile CTest entegrasyonu
- GNUInstallDirs + install(TARGETS) + cmake --install ile kurulum
- CPack ile .deb ve .tar.gz paket oluşturma
- GitHub Actions CI: ccache + Ninja + Debug/Release matrix