← Kursa Dön
📄 Text · 12 min

CMake ile Proje Yönetimi

Şimdiye kadar programları muhtemelen tek bir g++ main.cpp -o main komutuyla derledin. Küçük projeler için bu yeterli. Ama proje büyüyünce — 10 dosya, 5 kütüphane, farklı platformlar — bu yaklaşım çöker. İşte CMake tam bu noktada devreye girer.

CMake'i bir "derleme tarifi" yazma aracı olarak düşün. Bir restoran zinciri düşün: her şubede farklı fırın ve ocak var ama tarif aynı. CMake de aynı şekilde — sen tarifi yazarsın (CMakeLists.txt), CMake hangi platformda olursan ol uygun derleme komutlarını üretir.


CMake Nedir, Neden Makefile Yerine CMake?

CMake bir build system generator'dır. Doğrudan derleme yapmaz — derleme için gerekli dosyaları (Makefile, Ninja build dosyaları, Visual Studio projeleri...) üretir.

Makefile'ın sorunları:

  • Platform bağımlı (Windows'ta farklı, Linux'ta farklı)

  • Karmaşık söz dizimi

  • Kütüphane bulmak elle yapılır

  • Büyük projelerde yönetimi kabus

CMake'in avantajları:

  • Tek bir CMakeLists.txt ile tüm platformlarda çalışır

  • Kütüphaneleri otomatik bulur (find_package)

  • Modern, okunabilir söz dizimi

  • Endüstri standardı (Qt, OpenCV, LLVM, Unreal Engine... hepsi CMake kullanır)

                  CMakeLists.txt
                       │
            ┌──────────┼──────────┐
            ▼          ▼          ▼
        Makefile    Ninja      VS Project
        (Linux)    (Linux)    (Windows)
            │          │          │
            ▼          ▼          ▼
        g++ / clang  ninja     MSVC

CMakeLists.txt — Temel Yapı

Her CMake projesi bir CMakeLists.txt dosyasıyla başlar. Bu dosya projenin "tarif defteri"dir.

Minimal Örnek

Önce basit bir proje yapısı:

projem/
├── CMakeLists.txt
└── main.cpp

main.cpp:

#include <iostream>

int main() {
    std::cout << "Merhaba CMake!\n";
    return 0;
}

CMakeLists.txt:

cmake_minimum_required(VERSION 3.16)
project(MerhabaCMake LANGUAGES CXX)

set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

add_executable(merhaba main.cpp)

Bu beş satır ne yapıyor:

  1. `cmake_minimum_required`: Projenin gerektirdiği minimum CMake sürümü. Eski sürümlerde uyumsuzluk olmasın diye.

  2. `project`: Projenin adı ve dili. LANGUAGES CXX C++ demek.

  3. `set(CMAKE_CXX_STANDARD 17)`: C++17 standardını kullan.

  4. `set(CMAKE_CXX_STANDARD_REQUIRED ON)`: C++17 desteği yoksa hata ver (düşürme).

  5. `add_executable`: merhaba adlı çalıştırılabilir dosyayı main.cpp'den oluştur.

Derleme Adımları

# Proje dizininde
mkdir build
cd build
cmake ..        # CMakeLists.txt'yi isleyip Makefile uret
cmake --build . # Derlemeyi baslat (veya sadece make)
./merhaba       # Calistir

İlk cmake .. komutu yapılandırma yapar (configure), cmake --build . derleme yapar (build).

💡 `cmake --build .` komutu platform bağımsızdır. Linux'ta make, Windows'ta msbuild çağırır. Her zaman bu komutu tercih et.


Out-of-Source Build — Build Dizini

Yukarıda build dizini oluşturduk. Bu "out-of-source build" denir ve çok önemli bir pratiktir. Kaynak dosyalar ile derleme çıktıları ayrı dizinlerde tutulur.

Neden önemli?

  • Kaynak kodun temiz kalır — .o, .a, Makefile gibi dosyalar build dizininde

  • build dizinini silmek temiz bir başlangıç sağlar

  • Farklı konfigürasyonlar için farklı build dizinleri oluşturabilirsin

# Debug build
mkdir build-debug && cd build-debug
cmake -DCMAKE_BUILD_TYPE=Debug ..

# Release build
mkdir build-release && cd build-release
cmake -DCMAKE_BUILD_TYPE=Release ..

Debug build'de optimizasyon kapalıdır ve debug bilgisi eklenir (-g). Release build'de optimizasyon açıktır (-O2 veya -O3).

⚠️ Asla kaynak dizinde `cmake .` çalıştırma. Oluşan dosyalar kaynak kodunla karışır ve temizlemesi zordur. Her zaman ayrı bir build dizini kullan.


Çoklu Dosyalı Proje Yapısı

Gerçek projelerde tek bir .cpp dosyası olmaz. CMake ile birden fazla dosyayı yönetmek kolaydır.

hesap-makinesi/
├── CMakeLists.txt
├── src/
│   ├── main.cpp
│   ├── hesaplayici.cpp
│   └── hesaplayici.h
└── build/

src/hesaplayici.h:

#pragma once

class Hesaplayici {
public:
    double topla(double a, double b);
    double cikar(double a, double b);
    double carp(double a, double b);
    double bol(double a, double b);
};

src/hesaplayici.cpp:

#include "hesaplayici.h"
#include <stdexcept>

double Hesaplayici::topla(double a, double b) { return a + b; }
double Hesaplayici::cikar(double a, double b) { return a - b; }
double Hesaplayici::carp(double a, double b) { return a * b; }
double Hesaplayici::bol(double a, double b) {
    if (b == 0.0) throw std::runtime_error("Sifira bolme!");
    return a / b;
}

src/main.cpp:

#include <iostream>
#include "hesaplayici.h"

int main() {
    Hesaplayici h;
    std::cout << "3 + 5 = " << h.topla(3, 5) << "\n";
    std::cout << "10 / 3 = " << h.bol(10, 3) << "\n";
    return 0;
}

CMakeLists.txt:

cmake_minimum_required(VERSION 3.16)
project(HesapMakinesi LANGUAGES CXX)

set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

add_executable(hesap
    src/main.cpp
    src/hesaplayici.cpp
)

# Header dosyalarinin bulunabilmesi icin
target_include_directories(hesap PRIVATE src)

add_executable içine tüm .cpp dosyalarını listeliyorsun. target_include_directories ise derleyiciye header dosyalarını nerede arayacağını söyler.

Kütüphane Oluşturma

Büyük projelerde kodu kütüphanelere ayırmak yaygındır:

cmake_minimum_required(VERSION 3.16)
project(HesapMakinesi LANGUAGES CXX)

set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

# Statik kutuphane olustur
add_library(hesap_lib STATIC
    src/hesaplayici.cpp
)
target_include_directories(hesap_lib PUBLIC src)

# Calistirilabilir dosya
add_executable(hesap src/main.cpp)

# Kutuphaney baglа
target_link_libraries(hesap PRIVATE hesap_lib)

add_library bir kütüphane oluşturur:

  • STATIC: Statik kütüphane (.a / .lib)

  • SHARED: Dinamik kütüphane (.so / .dll)

target_link_libraries ile çalıştırılabilir dosyaya kütüphaneyi bağlarsın.


target_link_libraries, bir hedefin (target) hangi kütüphanelere bağımlı olduğunu belirtir. Üç erişim düzeyi vardır:

DüzeyAnlamı
PUBLICBu hedef ve onu kullananlar için geçerli
PRIVATESadece bu hedef için geçerli
INTERFACESadece onu kullananlar için geçerli
# hesap_lib PUBLIC include dizinine sahip
# hesap_lib'i kullanan herkes de bu include'u gorur
target_include_directories(hesap_lib PUBLIC src)

# pthread PRIVATE bagimlilik
# hesap sadece kendi icinde pthread kullanir
target_link_libraries(hesap PRIVATE pthread)

Pratikte çoğu durumda PRIVATE yeterlidir. PUBLIC sadece kütüphane yazarken, dışarıya header expose ediyorsan gerekir.


find_package ile Kütüphane Ekleme

Sistem kütüphanelerini veya üçüncü parti kütüphaneleri bulmak için find_package kullanılır. CMake, bilinen kütüphaneler için hazır "find module"leri içerir.

cmake_minimum_required(VERSION 3.16)
project(ThreadOrnek LANGUAGES CXX)

set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

# Threads kutuphanesini bul
find_package(Threads REQUIRED)

add_executable(program main.cpp)
target_link_libraries(program PRIVATE Threads::Threads)

REQUIRED bayrağı, kütüphane bulunamazsa CMake'in hata vermesini sağlar. Threads::Threads bir "imported target"tır — kütüphanenin tüm bilgilerini (include path, linker flags) taşır.

Popüler kütüphaneler ve CMake'teki adları:

find_package(Boost REQUIRED COMPONENTS filesystem system)
find_package(OpenCV REQUIRED)
find_package(fmt REQUIRED)
find_package(GTest REQUIRED)
target_link_libraries(program PRIVATE
    Boost::filesystem
    opencv_core
    fmt::fmt
    GTest::gtest_main
)

💡 Modern CMake "target-based" yaklaşımı kullanır. Eski include_directories() ve link_libraries() gibi global komutlar yerine target_include_directories() ve target_link_libraries() kullan. "Target" başına bağımlılık net ve temizdir.


Derleyici Uyarıları ve Seçenekleri

CMake ile derleyici uyarılarını proje genelinde etkinleştirebilirsin:

add_executable(program main.cpp)

# Derleyici uyarilari
target_compile_options(program PRIVATE
    -Wall -Wextra -Wpedantic
)

# Debug modda sanitizer ekle
if(CMAKE_BUILD_TYPE STREQUAL "Debug")
    target_compile_options(program PRIVATE -fsanitize=address)
    target_link_options(program PRIVATE -fsanitize=address)
endif()

CMake Değişkenleri ile Konfigürasyon

# Secenekler
option(TESTLERI_DERLE "Test dosyalarini derle" OFF)

if(TESTLERI_DERLE)
    add_executable(testler test/test_main.cpp)
    target_link_libraries(testler PRIVATE hesap_lib)
endif()
# Testleri aktif ederek derle
cmake -DTESTLERI_DERLE=ON ..

Tam Bir Proje Örneği

Şimdiye kadar öğrendiklerimizi birleştiren tam bir proje:

proje/
├── CMakeLists.txt
├── src/
│   ├── main.cpp
│   ├── matematik.cpp
│   └── matematik.h
└── build/
cmake_minimum_required(VERSION 3.16)
project(MatematikApp
    VERSION 1.0.0
    LANGUAGES CXX
    DESCRIPTION "Basit matematik kutuphanesi"
)

set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)  # IDE destegi icin

# Kutuphane
add_library(matematik STATIC src/matematik.cpp)
target_include_directories(matematik PUBLIC src)
target_compile_options(matematik PRIVATE -Wall -Wextra)

# Calistirilabilir
add_executable(app src/main.cpp)
target_link_libraries(app PRIVATE matematik)
target_compile_options(app PRIVATE -Wall -Wextra)

# Bilgi mesaji
message(STATUS "Proje: ${PROJECT_NAME} v${PROJECT_VERSION}")
message(STATUS "Build tipi: ${CMAKE_BUILD_TYPE}")
mkdir build && cd build
cmake -DCMAKE_BUILD_TYPE=Release ..
cmake --build .
./app

CMAKE_EXPORT_COMPILE_COMMANDS seçeneği compile_commands.json dosyası üretir. Bu dosya, VS Code, CLion gibi IDE'lerin kodu analiz etmesi ve otomatik tamamlama yapması için kullanılır.


Özet

  • CMake, platform bağımsız bir build system generator'dır. CMakeLists.txt yazarsın, CMake platforma uygun derleme dosyalarını üretir.

  • Temel komutlar: cmake_minimum_required, project, add_executable, add_library, target_link_libraries, target_include_directories.

  • Out-of-source build her zaman kullan: mkdir build && cd build && cmake ... Kaynak kodunu temiz tut.

  • target_link_libraries ile bağımlılıkları hedefe bağla. PRIVATE / PUBLIC / INTERFACE erişim düzeyleriyle kapsamı kontrol et.

  • find_package ile sistem ve üçüncü parti kütüphaneleri bul. REQUIRED bayrağı ile bulunamazsa hata ver.

  • Modern CMake "target-based" yaklaşım kullanır. Global komutlar (include_directories, link_libraries) yerine target_ önekli komutları tercih et.