Build Sistemleri Karşılaştırması
Bir evin inşaatını düşün. Tek bir duvar örerken tuğlaları elle taşıyabilirsin. Ama 10 katlı bir bina yaparken her şeyin koordineli olması gerekir — önce temel atılır, sonra kolon, sonra kiriş, sonra döşeme. Malzeme siparişleri zamanında gelmeli, bir katın betonunu kurutmadan üstüne çıkamamalısın. İşte build sistemi senin yazılım projendeki bu şantiye koordinatörüdür. Hangi dosyanın önce derleneceğini, neyin neye bağlı olduğunu, hangi parçaların yeniden derlenmesi gerektiğini otomatik yönetir.
Küçük projelerde g++ main.cpp -o main yeterli. Ama dosya sayısı 5'i geçtiğinde, kütüphaneler eklendiğinde, farklı platformları desteklemen gerektiğinde elle derleme sürdürülemez hale gelir. Bu derste Makefile'dan başlayarak CMake, Meson, Bazel ve Ninja'yı tanıyacak, hangisini ne zaman seçmen gerektiğini öğreneceksin.
Neden Build Sistemi Gerekli?
Elle Derlemenin Sınırları
Basit bir projede bile sorunlar hızla büyür:
# 3 dosyalık bir proje — elle derleme
g++ -c -std=c++17 src/main.cpp -o build/main.o
g++ -c -std=c++17 src/utils.cpp -o build/utils.o
g++ -c -std=c++17 src/database.cpp -o build/database.o
g++ build/main.o build/utils.o build/database.o -o build/app -lsqlite3Her değişiklikte bu dört komutu çalıştırman gerekiyor. Sadece utils.cpp değiştiysen diğer ikisini yeniden derlemeye gerek yok — ama bunu elle takip etmek imkansız. Dosya sayısı 50'ye çıkınca derleme 5 dakika sürüyorsa ve her seferinde hepsini derliyorsan, verimlilik sıfıra düşer.
Build sistemi şu sorunları çözer:
| Problem | Build Sistemi Çözümü |
|---|---|
| Hangi dosyalar değişti? | Dependency tracking — sadece değişeni derle |
| Derleme sırası ne? | Bağımlılık grafiği — doğru sırayı otomatik belirle |
| Farklı platformlar? | Platform abstraction — aynı config, farklı komutlar |
| Kütüphaneler nerede? | Package finding — sistem kütüphanelerini otomatik bul |
| Paralel derleme? | -j flag'i — birden fazla çekirdeği kullan |
Makefile: Klasik Unix Build Aracı
Temel Kavramlar
Makefile, 1976'dan beri kullanılan en eski build aracıdır. Basit ama güçlü bir yapıya sahiptir:
hedef: bağımlılıklar
tarif (recipe)Üç temel kavram:
Target (hedef): Oluşturulacak dosya veya yapılacak iş
Prerequisite (bağımlılık): Hedefin oluşması için gereken dosyalar
Recipe (tarif): Hedefi oluşturmak için çalıştırılacak komutlar
⚠️ Dikkat: Recipe satırları TAB karakteriyle başlamalıdır — boşluk (space) kullanırsan Makefile:X: *** missing separator. Stop. hatası alırsın. Bu, Makefile'ın en bilinen tuzağıdır.
Basit Makefile Örneği
# Değişkenler
CXX = g++
CXXFLAGS = -std=c++17 -Wall -Wextra -Werror
LDFLAGS = -lsqlite3
SRCDIR = src
BUILDDIR = build
# Kaynak dosyalar ve object dosyaları
SOURCES = $(wildcard $(SRCDIR)/*.cpp)
OBJECTS = $(patsubst $(SRCDIR)/%.cpp, $(BUILDDIR)/%.o, $(SOURCES))
TARGET = $(BUILDDIR)/app
# Varsayılan hedef
all: $(TARGET)
# Linkleme
$(TARGET): $(OBJECTS)
$(CXX) $(OBJECTS) -o $(TARGET) $(LDFLAGS)
# Derleme kuralı (pattern rule)
$(BUILDDIR)/%.o: $(SRCDIR)/%.cpp | $(BUILDDIR)
$(CXX) $(CXXFLAGS) -c $< -o $@
# Build dizinini oluştur
$(BUILDDIR):
mkdir -p $(BUILDDIR)
# Temizlik
clean:
rm -rf $(BUILDDIR)
# Phony hedefler (dosya değil, komut)
.PHONY: all cleanKullanım:
make # Varsayılan hedefi (all) derle
make -j4 # 4 çekirdek ile paralel derle
make clean # Build dosyalarını temizle
make TARGET=build/debug # Değişkeni override etMakefile'ın Gücü ve Sınırları
make'in güçlü yönü sadeliğidir. Küçük projelerde, script'lerde, C projelerinde hâlâ çok kullanılır. Ama ciddi sınırları var:
Platform bağımlılığı: Windows'ta farklı, Linux'ta farklı komutlar gerekir
Kütüphane bulma: Kütüphane yollarını elle belirtmen gerekir
Cross-compilation: Çok zor
IDE entegrasyonu: Yetersiz
Ölçeklenme: 100+ dosyalık projelerde yönetimi zorlaşır
CMake: Endüstri Standardı
Kısa Özet
CMake'i zaten biliyorsun. Burada kısa bir hatırlatma ve diğer araçlarla karşılaştırma için özet:
cmake_minimum_required(VERSION 3.14)
project(MyApp LANGUAGES CXX)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
# Kütüphane
add_library(utils src/utils.cpp)
target_include_directories(utils PUBLIC include/)
# Ana program
add_executable(app src/main.cpp)
target_link_libraries(app PRIVATE utils)
# Harici kütüphane bul
find_package(Threads REQUIRED)
target_link_libraries(app PRIVATE Threads::Threads)
# FetchContent ile dependency indir
include(FetchContent)
FetchContent_Declare(
fmt
GIT_REPOSITORY https://github.com/fmtlib/fmt.git
GIT_TAG 10.2.1
)
FetchContent_MakeAvailable(fmt)
target_link_libraries(app PRIVATE fmt::fmt)Build:
cmake -B build -G Ninja # Ninja generator ile configure
cmake --build build -j$(nproc) # Derle
ctest --test-dir build # Testleri çalıştırCMake'in avantajları:
Endüstri standardı — neredeyse her C++ projesi CMake kullanır
Platform bağımsız — Windows, Linux, macOS
IDE desteği — CLion, VS Code, Visual Studio
Geniş ekosistem — vcpkg, Conan, FetchContent
Generator tabanlı — Makefile, Ninja, VS Solution oluşturabilir
CMake'in dezavantajları:
Syntax — kendine özgü dil, öğrenme eğrisi yüksek
Dokümantasyon — resmi dokümantasyon karmaşık
Hata mesajları — bazen anlaşılmaz
Eski kod — eski CMake tarzı kodlar (
CMAKE_CXX_FLAGSvs.) kafa karıştırır
Meson: Modern ve Basit
Tanıtım
Meson, 2013'te başlayan ve hızla popülerleşen modern bir build sistemidir. GNOME, GStreamer, systemd gibi büyük projeler Meson'a geçti. Felsefesi: basitlik ve hız.
Kurulum:
# Python ile
pip3 install meson
# Ubuntu
sudo apt install meson
# macOS
brew install mesonmeson.build Dosyası
Meson'un konfigürasyon dosyası meson.build adını taşır ve Python benzeri bir syntax kullanır:
# meson.build
project('my-app', 'cpp',
version : '1.0.0',
default_options : ['cpp_std=c++17', 'warning_level=3'])
# Kütüphane
utils_lib = library('utils',
sources : ['src/utils.cpp'],
include_directories : include_directories('include'))
# Ana program
executable('app',
sources : ['src/main.cpp'],
link_with : utils_lib,
include_directories : include_directories('include'))
# Harici dependency
thread_dep = dependency('threads')
sqlite_dep = dependency('sqlite3', required : true)
executable('db_app',
sources : ['src/db_main.cpp'],
dependencies : [thread_dep, sqlite_dep],
include_directories : include_directories('include'))Build:
meson setup builddir # Configure
meson compile -C builddir # Derle (Ninja kullanır)
meson test -C builddir # Testleri çalıştırNeden Meson?
Meson'un CMake'e göre avantajları:
Okunabilir syntax: Python benzeri, anlaşılır
Hızlı: Arka planda Ninja kullanır, configure süresi çok kısa
Cross-compilation: Cross file ile basit yapılandırma
Subprojects: Dependency yönetimi (wrap dosyaları)
Test desteği: Yerleşik test runner
Dezavantajları:
Ekosistem: CMake kadar yaygın değil
IDE desteği: CMake'den daha az
Python bağımlılığı: Meson'un kendisi Python ile yazılmış
Alt Proje (Subproject) Yönetimi
Meson'da dış bağımlılıkları wrap dosyasıyla yönetirsin:
# subprojects/fmt.wrap
[wrap-git]
url = https://github.com/fmtlib/fmt.git
revision = 10.2.1
depth = 1
[provide]
fmt = fmt_dep# meson.build
fmt_dep = dependency('fmt', fallback : ['fmt', 'fmt_dep'])
executable('app',
sources : ['src/main.cpp'],
dependencies : fmt_dep)Sistem kütüphanesi varsa onu kullanır, yoksa subproject'ten derler.
Bazel: Google'ın Build Sistemi
Tanıtım
Bazel, Google'ın iç build sistemi (Blaze) üzerine kuruludur. Binlerce geliştiricinin milyonlarca satır kod üzerinde çalıştığı monorepo ortamları için tasarlanmıştır.
Kurulum:
# Bazelisk (önerilen — otomatik versiyon yönetimi)
npm install -g @bazel/bazelisk
# veya doğrudan
# https://bazel.build/install adresindenWORKSPACE ve BUILD Dosyaları
Bazel'de her proje bir WORKSPACE dosyası ile tanımlanır. Her dizinde BUILD dosyası hangi hedeflerin nasıl derleneceğini belirtir.
my-project/
├── WORKSPACE
├── BUILD
├── src/
│ ├── BUILD
│ ├── main.cpp
│ └── utils.cpp
├── include/
│ └── utils.h
└── tests/
├── BUILD
└── utils_test.cpp# WORKSPACE
workspace(name = "my_project")
load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
# Google Test
http_archive(
name = "com_google_googletest",
urls = ["https://github.com/google/googletest/archive/v1.14.0.tar.gz"],
strip_prefix = "googletest-1.14.0",
)# src/BUILD
cc_library(
name = "utils",
srcs = ["utils.cpp"],
hdrs = ["//include:utils.h"],
visibility = ["//visibility:public"],
)
cc_binary(
name = "app",
srcs = ["main.cpp"],
deps = [":utils"],
)# tests/BUILD
cc_test(
name = "utils_test",
srcs = ["utils_test.cpp"],
deps = [
"//src:utils",
"@com_google_googletest//:gtest_main",
],
)Build ve test:
bazel build //src:app # Tek hedef derle
bazel build //... # Her şeyi derle
bazel test //tests:... # Tüm testleri çalıştır
bazel run //src:app # Derle ve çalıştırHermetic Build Nedir?
Bazel'in en önemli özelliği hermetic (kapalı/izole) build'dir. Bu şu anlama gelir:
Aynı girdiler → aynı çıktı: Hangi makinede, ne zaman derlersen derle, sonuç aynı
Sandboxing: Her build adımı izole bir ortamda çalışır
Remote caching: Build sonuçları uzak sunucuda cache'lenir — 1000 kişilik ekipte biri derlerse diğerleri cache'den alır
Remote execution: Build adımları uzak sunucularda paralel çalıştırılabilir
Bu özellikler küçük projelerde gereksiz, ama Google, Meta, Uber ölçeğinde hayat kurtarır.
Bazel Ne Zaman Mantıklı?
Monorepo: Tüm kod tek repository'de ve birbirine bağımlıysa
Çoklu dil: C++, Java, Python, Go aynı projede
Büyük ekip: 50+ geliştirici, build süresi kritik
Reproducibility: Bit-perfect tekrarlanabilir build gerekiyorsa
Küçük-orta projelerde Bazel'in karmaşıklığı faydadan çok ağır basar. 5-10 kişilik bir ekipte CMake veya Meson çok daha pratik.
Ninja: Düşük Seviye Build Executor
CMake + Ninja
Ninja bir meta-build sistemi değildir — kendi başına konfigürasyon yapmaz. CMake veya Meson gibi araçlar Ninja dosyası üretir, Ninja sadece çok hızlı bir şekilde çalıştırır.
Ninja, Make'in alternatifidir ama tek bir amacı var: mümkün olan en hızlı build. Minimal overhead, paralel derleme, akıllı dependency tracking.
# CMake ile Ninja kullan
cmake -B build -G Ninja
cmake --build build
# Veya doğrudan
cd build && ninja
# Paralel iş sayısını kontrol et
ninja -j8
# Hangi adımlar çalışacak (dry run)
ninja -n
# Dependency grafiğini göster
ninja -t graph | dot -Tpng > graph.pngMake vs Ninja Performans Karşılaştırması
| Özellik | Make | Ninja |
|---|---|---|
| Startup süresi | Yavaş (Makefile parse) | ✅ Çok hızlı |
| Dependency analizi | Dosya bazlı | ✅ Hash bazlı |
| Paralel derleme | -j ile | ✅ Varsayılan paralel |
| İnsan okunabilir | ✅ Evet | ❌ Makine formatı |
| Elle yazılır mı | Evet | Hayır (generator gerekir) |
| Build log | Basit | ✅ Kompakt ve bilgilendirici |
Ninja dosyaları (build.ninja) elle yazılmak üzere tasarlanmamıştır. CMake veya Meson tarafından üretilirler.
💡 İpucu: CMake projelerinde -G Ninja kullanmak, özellikle büyük projelerde build süresini belirgin şekilde azaltır. Tek değişiklik bir flag — dene ve farkı gör.
Ninja Dosyası Neye Benzer?
Merak edenler için küçük bir örnek:
# build.ninja (genellikle elle yazmassın)
cxx = g++
cxxflags = -std=c++17 -Wall -Wextra
rule compile
command = $cxx $cxxflags -c $in -o $out
description = Compiling $in
rule link
command = $cxx $in -o $out
description = Linking $out
build build/main.o: compile src/main.cpp
build build/utils.o: compile src/utils.cpp
build build/app: link build/main.o build/utils.oSyntax minimalist ve makineler için optimize edilmiş. Hız farkının kaynağı bu sadelik — Ninja parse ederken zaman kaybetmez.
Büyük Karşılaştırma Tablosu
| Özellik | Makefile | CMake | Meson | Bazel |
|---|---|---|---|---|
| İlk çıkış | 1976 | 2000 | 2013 | 2015 |
| Dil | Make syntax | CMake dili | Python benzeri | Starlark |
| Öğrenme eğrisi | Düşük | Orta-Yüksek | Düşük | Yüksek |
| Platform desteği | Unix | ✅ Tümü | ✅ Tümü | ✅ Tümü |
| IDE entegrasyonu | Minimal | ✅ Mükemmel | İyi | Orta |
| Dependency yönetimi | Manuel | FetchContent, vcpkg | Wrap, pkg-config | WORKSPACE |
| Cross-compilation | Zor | Toolchain dosyası | Cross file | Platform kuralları |
| Build hızı | Orta | Generator'a bağlı | ✅ Hızlı (Ninja) | ✅ Hızlı (cache) |
| Ölçeklenebilirlik | Düşük | İyi | İyi | ✅ Mükemmel |
| Hermetic build | ❌ | ❌ | ❌ | ✅ Evet |
| Remote cache | ❌ | ❌ | ❌ | ✅ Evet |
| Çoklu dil | Sınırlı | C/C++ odaklı | C/C++ odaklı | ✅ Tümü |
| Topluluk | Çok geniş | ✅ En geniş | Büyüyen | Google ekosistemi |
| Dokümantasyon | İyi | Karmaşık | ✅ Temiz | İyi |
Hangisini Seçmeli?
Proje Büyüklüğüne Göre Karar Ağacı
Tek dosya veya script düzeyinde proje: → Build sistemi gereksiz. g++ main.cpp -o main yeterli.
2-10 dosya, kişisel/küçük proje: → Makefile veya CMake. Makefile hızlı başlamak için, CMake büyüme potansiyeli için.
10-100 dosya, küçük-orta ekip (2-10 kişi): → CMake. Endüstri standardı, IDE desteği mükemmel, ekosistem geniş.
Yeni proje, modern araç istiyorsan: → Meson. Syntax temiz, konfigürasyon hızlı, Ninja ile build hızlı. Ekosistem CMake kadar geniş değil ama yeterli.
100+ dosya, büyük ekip (10+ kişi): → CMake (yerleşik ekosistem) veya Bazel (build süresi kritikse).
Monorepo, çoklu dil, 50+ geliştirici: → Bazel. Remote cache ve execution ile build sürelerini dramatik azaltır.
Gerçek Dünya Tercihleri
| Proje | Build Sistemi |
|---|---|
| Linux Kernel | Makefile (Kbuild) |
| LLVM/Clang | CMake |
| Qt | CMake (eskiden qmake) |
| Chromium | GN + Ninja |
| systemd | Meson |
| GStreamer | Meson |
| TensorFlow | Bazel |
| Abseil (Google) | CMake + Bazel |
| Unreal Engine | Özel (UnrealBuildTool) |
| Boost | B2 (eskiden bjam) |
Çoğu C++ projesi CMake kullanır. Bu bir standart haline gelmiş durumda. Eğer özel bir nedenin yoksa CMake güvenli tercihtir.
Pratik: Aynı Projeyi Farklı Araçlarla
Basit bir projenin her araçla nasıl yapılandırıldığını görelim:
Proje Yapısı
calculator/
├── include/
│ └── calculator.h
├── src/
│ ├── calculator.cpp
│ └── main.cpp
└── tests/
└── calculator_test.cpp// include/calculator.h
#pragma once
class Calculator {
public:
double add(double a, double b);
double divide(double a, double b);
};// src/calculator.cpp
#include "calculator.h"
#include <stdexcept>
double Calculator::add(double a, double b) {
return a + b;
}
double Calculator::divide(double a, double b) {
if (b == 0.0) throw std::invalid_argument("Division by zero");
return a / b;
}Makefile Versiyonu
CXX = g++
CXXFLAGS = -std=c++17 -Wall -Wextra -Iinclude
BUILDDIR = build
SOURCES = src/calculator.cpp src/main.cpp
OBJECTS = $(patsubst src/%.cpp, $(BUILDDIR)/%.o, $(SOURCES))
all: $(BUILDDIR)/app
$(BUILDDIR)/app: $(OBJECTS)
$(CXX) $^ -o $@
$(BUILDDIR)/%.o: src/%.cpp | $(BUILDDIR)
$(CXX) $(CXXFLAGS) -c $< -o $@
$(BUILDDIR):
mkdir -p $(BUILDDIR)
clean:
rm -rf $(BUILDDIR)
.PHONY: all cleanCMake Versiyonu
cmake_minimum_required(VERSION 3.14)
project(Calculator LANGUAGES CXX)
set(CMAKE_CXX_STANDARD 17)
add_library(calculator src/calculator.cpp)
target_include_directories(calculator PUBLIC include/)
add_executable(app src/main.cpp)
target_link_libraries(app PRIVATE calculator)Meson Versiyonu
project('calculator', 'cpp',
default_options : ['cpp_std=c++17', 'warning_level=3'])
calc_lib = library('calculator',
sources : ['src/calculator.cpp'],
include_directories : include_directories('include'))
executable('app',
sources : ['src/main.cpp'],
link_with : calc_lib,
include_directories : include_directories('include'))Bazel Versiyonu
# src/BUILD
cc_library(
name = "calculator",
srcs = ["calculator.cpp"],
hdrs = ["//include:calculator.h"],
includes = ["../include"],
visibility = ["//visibility:public"],
)
cc_binary(
name = "app",
srcs = ["main.cpp"],
deps = [":calculator"],
)Aynı proje — dört farklı syntax. CMake ve Meson en okunabilir olanlar. Makefile en kısa ama en az esnek. Bazel en yapılandırılmış ama küçük proje için overkill.
İleri Seviye: Build Sistemi Özellikleri
Precompiled Headers (PCH)
Büyük projelerde sık kullanılan header'ları önderlemek build süresini azaltır:
# CMake
target_precompile_headers(app PRIVATE
<vector>
<string>
<iostream>
<memory>
)# Meson
executable('app',
sources : ['src/main.cpp'],
cpp_pch : 'src/pch/pch.h')Unity Build
Tüm .cpp dosyalarını tek bir derleme birimine birleştirerek header parsing'i azaltır:
# CMake 3.16+
set(CMAKE_UNITY_BUILD ON)
set(CMAKE_UNITY_BUILD_BATCH_SIZE 8)ccache ile Derleme Cache'leme
ccache, daha önce derlenmiş dosyaları cache'leyerek tekrar derlemeyi önler:
# Kurulum
sudo apt install ccache
# CMake ile kullanım
cmake -B build -DCMAKE_CXX_COMPILER_LAUNCHER=ccache# Meson (otomatik algılar)
# Yalnızca ccache kurulu olmalıBu teknikler build sisteminden bağımsızdır ama entegrasyonu build sistemi sağlar.
⚠️ Dikkat: Build sistemi seçimini projenin başında yap ve ekiple hemfikir ol. Sonradan değiştirmek (özellikle büyük projelerde) çok maliyetlidir. Yanlış tercihi düzeltmektense baştan doğru tercihi yapmak iyidir.
Özet
Build sistemi, çok dosyalı projelerde derleme otomasyonu, dependency tracking ve paralel build sağlayan zorunlu bir araçtır
Makefile basit ve hızlı başlamak için iyidir ama platform bağımlılığı ve ölçeklenme sorunları vardır
CMake endüstri standardıdır — IDE desteği, ekosistem ve topluluk açısından en güçlü seçenek
Meson modern, okunabilir syntax ve Ninja tabanlı hızlı build ile CMake'e güçlü bir alternatiftir
Bazel monorepo, hermetic build ve remote cache ile büyük ölçekli projelerde parlar — küçük projelerde gereksizdir
Çoğu C++ projesi için CMake + Ninja kombinasyonu en pratik ve performanslı çözümdür
AI Asistan
Sorularını yanıtlamaya hazır