Önceki yazımda Build otomasyonundan ve onun araçlarından bahsetmiştim. Bildiğiniz gibi Build işlemi derleme ve bağlama gibi pek çok adımı içerisinde barındıran büyük bir işlemdi. Bu işlemin otomasyonunu yapan bazı araçlar ve bu otomasyon araçları ile ilgili Script’leri oluşturan üst araçlar da mevcuttu. İşte CMake bu üst araçlardan biridir. Bu yazıda CMake’in kurulumu, CMake projelerinin genel dizin yapısı, CMake ile Build Script’lerinin oluşturulması ve derleme işleminin başlatılması gibi konulara değineceğim. Ayrıca CMake’in GUI aracından da kısaca bahsedeceğim.
CMake’in Kurulumu
CMake aracı Windows, macOS ve pek çok farklı GNU/Linux dağıtımına kurulabilmektedir. Onun son versiyonunu çeşitli sistemlere çeşitli yükleme araçları ve Script dosyaları ile kurmak için şu linki ziyaret edebilir ve ilgili dosyayı indirebilirsiniz:
Bunun yanında çeşitli GNU/Linux dağıtımlarında yer alan paket depolarında da CMake aracını bulabilirsiniz. Örneğin Ubuntu bazlı dağıtımlarda şu şekilde onu kurmanız mümkündür:
apt install cmake
Arch Linux bazlı sistemlerde ise şu şekilde kurabilirsiniz:
pacman -Syu cmake
Snap ile yüklemek için ise şöyle bir komut kullanabilirsiniz:
snap install cmake --classic
Diğer sistemlerde ve paket yöneticilerinde CMake’in nasıl kurulabileceğini internette kısa bir arama yaparak bulabilirsiniz. Ayrıca CMake’i kaynaktan da yüklemeniz mümkündür. Ancak ben burada bu konun ayrıntısına girmeyeceğim. Zaten özel bir nedeniniz yoksa bu tip bir yüklemeyi tavsiye etmem. CMake’in sisteminizde yüklü olup olmadığını (ve yolunun ortam değişkenlerine eklenip eklenmediğini) kontrol etmek için şu komutu kullanabilirsiniz:
cmake --version
Eğer bu komut sizde bir hata verdiyse, CMake kurulumu sisteminizde düzgün bir şekilde yapılamamış veya aracın yolu ortam değişkenlerine düzgün bir şekilde eklenmemiş demektir. Bendeki gibi şuna benzer bir çıktı gördüyseniz, her şey yolundadır:
CMake suite maintained and supported by Kitware (kitware.com/cmake).
CMake Projelerinde Dizin Yapıları
Günümüzde belki de çoğu projede karşımıza çıkan araç olan CMake’in Build araçlarından biri olduğunu söylemiştim. Bugün GitHub repolarına baktığınızda, çoğu C ve C++ projesinin ana dizininde "CMakeLists.txt" dosyası ile karşılaşırsınız. Bu dosyanın orada bulunması, o projenin CMake ile derlenmeye uygun bir şekilde oluşturulduğunu göstermektedir. "CMakeLists.txt" dosyası, CMake’in o projeyi nasıl derleyeceğini belirten birtakım bildirimler barındıran dosyadır. Bu dosyanın karmaşıklığı proje yapısına göre değişmektedir.
"CMakeLists.txt" dosyasını herhangi bir metin düzenleme editörü ile açıp okuyabilir ve isterseniz onu düzenleyebilirsiniz. CMake eğitiminin büyük bir kısmı bu dosyada yapabileceklerimiz ile ilgilidir. Ancak bu yazıda bunun ayrıntılarına girmeyeceğim. Bu yazıda bir CMake projesinin nasıl derlenip çıktılarının oluşturulabileceğinden bahsedeceğim. Ancak buna da girmeden önce çeşitli dosya ve dizin yapılarından bahsetmemde fayda var. Öncelikle CMake’te adı geçen 2 dizin türü mevcuttur:
- Kaynak Dizini (Source Directory): Projenin kaynak dosyaları ile birlikte "CMakeLists.txt" dosyasının da bulunduğu dizindir. Yani bir projeyi derlemek için gerekli olan tüm dosyalar bu dizinde yer alır. Genellikle
gitgibi versiyon kontrol sistemlerine verilen dizin budur. - Build Dizini (Binary/Build Directory): Build işlemi sonucunda oluşan bütün dosyaların yer aldığı dizindir. CMake’te seçilen derleme araçları ve CTest, CPack gibi programlar bu dizin ve onun alt dizinlerine dosyaları oluştururlar. Bu dizinde ayrıca "CMakeCache.txt" isimli özel bir dosya bulunur ve bu dosyada Build esnasında çalıştırılan bazı komutların kaydı yer alır.
Bir projeyi derlemeden önce Build dizininin nerede yer alacağını belirlemek önemli bir meseledir. Bu, proje yapısı ve CMake ile ilgili pek çok şeyi değiştirecektir. Build dizininin kaynak dizine göre nerede olduğuna bakılarak, bununla ilgili 2 çeşit Build yaklaşımı ortaya çıkmıştır: In-source Build (Kaynak İçi Derleme) ve Out-of-source Build (Kaynak Dışı Derleme). Bu kavramların da yine Türkçe karşılıklarını değil, orijinal hallerini kullanmayı tercih ediyorum.
In-source Build yaklaşımında kaynak ve Build dizinleri aynı dizinde yer almaktadır. Genellikle yazılım işine yeni başlayan geliştiriciler kolaylık açısından bu yapıyı tercih ederler. Ancak bu yapıyı kullanmayla ilgili bazı dezavantajlar vardır. Örneğin kaynak dosyalar Build çıktıları ile karışacağından, Build dosyalarının bir sebepten ötürü kaynak dosyaların üzerine yazılmasını kolaylaştırır. Bu durum projenizin bozulmasına ve versiyon kontrol sistemlerinde bazı sıkıntılara yol açabilir. Aşağıda bu dizin yapısı ile alakalı örnek bir figür verilmiştir:
Out-of-source Build yaklaşımında ise Build ve kaynak dizinleri birbirinden ayrı yerlerde tutulmaktadır. Bu, geliştiriciler arasında daha tercih edilen bir yaklaşımdır. Çünkü kaynak kodların Build çıktıları ile karışması (çok anormal bir şeyler yapmazsak) mümkün değildir. Ayrıca bu yaklaşım, birden çok Build yapılandırması için aynı kaynak kodları kullanırken de avantaj sağlar. Örneğin bir programın hem Debug hem de Release versiyonunu aynı kaynak kodları kullanarak derlemek istiyorsanız, bu klasör düzenini kullanmanız daha iyi olur. Ben de CMake ile örnek yaparken bu dizin yaklaşımını kullanacağım. Bu yaklaşıma bir örneği de şu figür ile verelim:
Hedef Proje Dosyalarının Üretilmesi
CMake’in esas görevi, bulunduğunuz sistemde yer alan Build otomasyon araçları için kod üretimini kolaylaştırmak ve bunun için ortak bir söz dizimi sağlamaktır. Bu araçların ne olduğundan zaten bir önceki yazıda bahsetmiştim. GNU Makefile, Ninja ve Visual Studio Build araçları gibi birçok araç bu gruba dahildir. Elbette CMake’in bu dosyaları "CMakeLists.txt" dosyasından oluşturabilmesi için, bu dosyalar hakkında daha derin bilgilere sahip ara programlara ihtiyaç vardır. Bu programlara CMake’te Generator (Oluşturucu) adı verilmektedir. CMake çok çeşitli Build araçlarına ve IDE projelerine Generator desteği vermektedir. Bu yazıyı yazdığım sırada en yüksek sürüm olan 3.30.3 sürümünün desteklediği Generator programlarından bazıları şöyledir:
| Generator Kategorisi | Generator |
|---|---|
| Makefile Generators | Borland Makefiles |
| MSYS Makefiles | |
| MinGW Makefiles | |
| NMake Makefiles | |
| NMake Makefiles JOM | |
| Unix Makefiles | |
| Watcom WMake | |
| Ninja Generators | Ninja |
| Ninja Multi-Config | |
| Visual Studio Generators | Visual Studio 10 2010 |
| Visual Studio 11 2012 | |
| Visual Studio 12 2013 | |
| Visual Studio 14 2015 | |
| Visual Studio 15 2017 | |
| Visual Studio 16 2019 | |
| Visual Studio 17 2022 | |
| Diğer | Green Hills MULTI |
| Xcode |
Bir önceki kısımda bahsettiğim proje yapısı belirlendikten sonra, CMake’e "CMakeLists.txt" dosyasının bulunduğu kaynak dizini ve dosyaların oluşturulacağı Build dizinini vererek, ilgili proje dosyaları oluşturulabilir. Eğer CMake’e hangi Generator programının kullanılacağı bilgisini açık bir şekilde vermezseniz, o anda sisteminiz için varsayılan Generator kullanılacaktır. Elbette her sistem her Generator programını desteklememektedir. Sisteminizin desteklediği Generator programlarını listelemek için şu komuttan faydalanabilirsiniz:
cmake --help
Çıktının en altında yer alan "Generators" satırından sonraki kısımda desteklenen Generator programlarının listesini ve varsayılan Generator programının hangisi olduğunu (başında yıldız işareti olan) görebilirsiniz. Örneğin benim kullandığım Artix Linux dağıtımında bu komutun çıktısı şu şekilde geldi:
The following generators are available on this platform (* marks default):
Green Hills MULTI = Generates Green Hills MULTI files (experimental, work-in-progress).
* Unix Makefiles = Generates standard UNIX makefiles.
Ninja = Generates build.ninja files.
Ninja Multi-Config = Generates build-<Config>.ninja files.
Watcom WMake = Generates Watcom WMake makefiles.
CodeBlocks – Ninja = Generates CodeBlocks project files (deprecated).
CodeBlocks – Unix Makefiles = Generates CodeBlocks project files (deprecated).
CodeLite – Ninja = Generates CodeLite project files (deprecated).
CodeLite – Unix Makefiles = Generates CodeLite project files (deprecated).
Eclipse CDT4 – Ninja = Generates Eclipse CDT 4.0 project files (deprecated).
Eclipse CDT4 – Unix Makefiles= Generates Eclipse CDT 4.0 project files (deprecated).
Kate – Ninja = Generates Kate project files (deprecated).
Kate – Ninja Multi-Config = Generates Kate project files (deprecated).
Kate – Unix Makefiles = Generates Kate project files (deprecated).
Sublime Text 2 – Ninja = Generates Sublime Text 2 project files (deprecated).
Sublime Text 2 – Unix Makefiles = Generates Sublime Text 2 project files (deprecated).
Burada bazı Generator programlarının sonunda "deprecated" yazdığını görebilirsiniz. CMake’te Extra Generator adı verilen ve bazı IDE programları için özelleştirilmiş proje dosyaları varyasyonları oluşturmaya yarayan bir Generator grubu bulunmaktadır. Ancak CMake 3.27 versiyonu ile beraber bunlar artık eskimiş (deprecated) olarak işaretlenmiştir. Bu nedenle bu Generator tipini burada anlatmayacağım. Alternatif olarak şu komutu kullanarak da desteklenen Generator listesini JSON formatındaki çıktıda yer alan "generators" dizisinden de görebilirsiniz:
cmake -E capabilities
Peki varsayılan Generator programını değil de sistemde desteklenen bir başka Generator programını kullanmamız gerektiğini CMake’e nasıl söyleriz? Bunun için -G komut satırı argümanından faydalanılır. Sisteminizde bulunan Generator programlarını listelediğinizde size gelen isim bilgisini kullanarak, bu argüman ile CMake’e hangi Generator programını kullanacağını söyleyebilirsiniz:
cmake -G "Ninja"
Elbette bu komut düzgün bir yol bilgisi verilmeden doğru bir işlem yapamayacaktır. Birazdan bu komutu nasıl düzgün bir şekilde kullanabileceğimizi göstereceğim. Bunu yapmadan önce çoklu yapılandırma (multiple configuration, multi-config) kavramından biraz bahsedeyim. Çoklu yapılandırma destekleyen Generator’lar, birden fazla Build yapılandırmasına (Debug, Release vb.) tek bir çalıştırma ile derleme yapmaya izin verir. Yani bunların çıktılarını ayrı ayrı almak için, CMake’i ayrı ayrı çalıştırmaya gerek yoktur. Bunu desteklemeyen Generator’lar ise her bir yapılandırma ayarı için CMake’i yeniden çalıştırmalıdır.
Şimdi CMake’te ilgili proje dosyalarını veya Build Script’lerini nasıl oluşturacağımıza bakalım. Bunun için elbette cmake komutunu kullanacağız. Öncelikle herhangi bir GNU/Linux dağıtımı üzerinde olduğunuzu ve projenizi Out-of-source Build dizin yapısında derleyeceğinizi düşünelim. Yani projeniz Figür 2’deki gibi, ama daha "Build" dizini oluşmamış halde olsun. İlk yapmanız gereken şey bu dizini oluşturmak ve içine girmek olacaktır:
mkdir Build
cd Build
Artık "Build" isimli dizini oluşturduk ve içine girdik. Diyelim ki bu projede kullanacağımız Generator programı "Unix Makefiles" olsun. "CMakeLists.txt" dosyası zaten ana dizindeki "Source" isimli dizinde yer aldığından, bulunduğumuz dizinden bu dosyanın bulunduğu dizini de belirtmemiz gerekir. Yine Figür 2’deki gibi bir dizin yapısında "Build" isimli dizinin içinde olduğunuzu düşünürseniz şöyle bir komut çalıştırmanız gerekir:
cmake -G "Unix Makefiles" ../Source
Bu komutu çalıştırdıktan sonra CMake, ilgili UNIX Makefiles Script’lerini "Build" dizini içerisinde oluşturacaktır. Eğer -G seçeneği atlanırsa, CMake üzerinde bulunduğunuz platforma göre otomatik bir Generator atayacaktır. Bu arada Generator isminde boşluklar yer aldığından onu çift tırnak (") arasına aldığımıza dikkat edin. Eğer bu komut çalıştırıldığında her şey doğru giderse, ekranda şu mesajları görürsünüz:
— Generating done
— Build files have been written to: /path/Build
Burada "path" kısmına "Build" dizininin sizin sistemde sahip olduğu tam yol (absolute path) bilgisi gelecektir. Bu mesajdan anlaşıldığı gibi, proje dosyalarını oluşturmak CMake’te iki aşamadan meydana gelmektedir: Configuring (Yapılandırma) ve Generating (Oluşturma). Configuring aşamasında CMake, "CMakeLists.txt" dosyasının içeriğini okur ve projeyi ona göre yapılandırır. Generating aşamasında ise bu yapılandırma bilgisi ile proje dosyalarını oluşturur. Bu gibi komut satırından yapılan Build işlemlerinde bu ikisi arasında bir fark görünmese de GUI programında ve bazı ileri CMake konularında bu ikisi arasındaki fark net bir şekilde görünecektir.
Grafiksel Araç ile Proje Dosyalarının Üretilmesi
GUI programı demişken, cmake-gui üzerinden de nasıl derleme yapacağımızı da anlatmamız gerekir. Öncelikle bu programı komut satırından cmake-gui yazarak veya grafiksel arayüzünüzden herhangi bir şekilde çalıştırmalısınız. İlk çalıştırmada karşınıza şöyle bir pencere gelecektir:
Burada "Where is the source code:" karşısındaki kutucuğa "CMakeLists.txt" dosyasının yer aldığı kaynak dizinin tam yolunu (absolute path) yazmalısınız. Bunu "Browse Source…" butonuna basıp açılan pencereden seçerek de yapabilirsiniz. "Where to build the binaries:" karşısındaki kutucuğa ise oluşturacağınız dosyaları içerecek olan Build dizininin tam yolunu yazmalısınız. Bunu da "Browse Build…" butonundan seçebilirsiniz. Bu kısımları doldurduktan sonra altta "Configure" ve "Generate" butonlarını görmektesiniz. "Configure" ile Configuration aşamasını başlatabilir, "Generate" ile de Generation aşamasını başlatabilirsiniz.
Örnek olarak OpenCV’nin kaynak kodlarını indirdiğinizi ve bir klasöre çıkarttığınızı düşünelim. Daha sonra çıkarttığınız yere gidip bu dosyaları "Source" isimli bir dizin oluşturup onun içine atalım. Daha sonra da bu dizinin yanında bir de "Build" dizini oluşturalım. Buraya kadar bütün ön hazılıklarımızı yapmış olduk. Şimdi CMake’in GUI aracını çalıştırıp ilgili dizinlerin tam yollarını ilgili yelere yazalım. Bunları yaptıktan sonra "Configure" butonuna bastığınızda, karşınıza şunun gibi bir Generator seçme ekranı gelecektir:
"Specifiy the generator for this project" kısmından istediğiniz Generator programını seçin. Ben burada "Unix Makefiles" seçimi yaptım. Alttaki kısımdan ise "Use default native compilers" seçeneğini seçin. Bu size sistemde varsayılan olarak tanımlı olan derleyiciyi kullanma imkanı tanıyacaktır. "Finish" butonuna bastığınızda biraz yapılandırmaların tamamlanmasını bekledikten sonra şuna benzer bir manzara ile karşılaşırsınız:
Bütün bu kırmızı alanlar sizin gözünüzü korkutmasın. Aslında tek bakmanız gereken yer alltaki "Configuring done" yazısıdır. Herhangi bir CMake projesini CMake’in GUI aracı ile ilk yapılandırdığınızda bu görüntüyü alırsınız. Bu aslında değişkenlerde herhangi bir değişiklik yapılmadığını, her şeyin varsayılan değerde olduğunu gösteren bir işarettir. Bir kez daha "Configure" butonuna bastığınızda (eğer zorunlu olarak belirtilmesi gereken bir alan yoksa) bu kırmızılıklar kaybolacaktır. Daha sonra "Generate" butonuna basıp "Generation done" yazısını gördüğünüzde, dosyalarınız ilgili dizinde oluşturulmuş demektir.
CMake’in GUI ekranının ortasında yer alan değişkenler Cache (Önbellek) Değişkenleri olarak bilinirler. Bunlar ya CMake içinde öntanımlı olarak ya da projeyi oluşturan geliştiricilerin tanımladığı değişkenlerdir ve değerleri kalıcı olarak saklanabilir. Bu değişkenler ile projeyi derleyecek olan kullanıcılar, bu derleme işlemini kendilerine göre kişiselleştirebilirler. Bu değişkenler hakkında daha fazla bilgi vermeyeceğim. CMake’de değişkenler ile ilgili bir yazı yazarken bu değişkenleri ve CMake’in GUI aracı üzerindeki etkilerini daha ayrıntılı bir biçimde anlatacağım.
Hedef Build Aracının CMake ile Çalıştırılması
Generating aşamasından sonra (komut satırı veya GUI aracı kullanmanız farketmeden), belirttiğiniz Build dizininde ilgili dosyalar oluşacaktır. Bu dizine girdiğinizde (UNIX Makefile Generator’ını kullandığınız için) "Makefile" dosyası ve çeşitli Build dosyaları ile karşılaşacaksınız. Bunun yanında bir de "CMakeCache.txt" dosyasını görürsünüz. Bu dosyadan kısaca bahsedecek olursak, CMake gerçekleştirdiği Configuring ve Generating aşamaları ile ilgili bilgileri ve daha demin bahsettiğim Cache değişkenlerinin değerlerini bu dosyaya kaydeder. Daha sonraki Build işlemlerinde bu değerlerin tekrar hesaplanmasına gerek kalmadan buradan okunarak ilerlenir. Bu dosyayla genellikle geliştiriciler değil (şimdilik) CMake’in kendisi ilgilenir.
Şimdi bu dosyaları make ve make install komutlarını kullanarak derleyebilir ve sisteme yükleyebiliriz. Aynı şekilde Visual Studio veya Xcode ile alakalı proje dosyaları oluşturduğunuzda da bu projeleri ilgili IDE programlarıyla açıp derleyebilirsiniz. Ancak bunu CMake’in komut satırı programından yapmanın bir yolu da vardır. Örneğin aşağıdaki komut Xcode veya Visual Studio ile ilgili proje dosyaları oluşturulmuş olsa bile çalışacaktır:
cmake --build /path/Build --config Debug --target MyApp
Burada -build seçeneği ile Build dizininin yolunu belirtiyoruz. -config seçeneği ile çoklu yapılandırma (multi-config) destekleyen araçlar için hangi yapılandırmanın kullanılacağını belirtiyoruz. Eğer kullandığınız araç çoklu yapılandırmayı desteklemiyorsa, buraya yazdıklarınız CMake tarafından görmezden gelinir. –target seçeneği derlenecek hedefin (target) seçimini sağlar. Derleme hedefleri konusunu daha sonra göreceğiz. Her Build aracının veya projenin kendi derleme mantığını kullanmak yerine yukarıdaki şekilde Build işlemini gerçekleştirmek, otomasyona oldukça fayda sağlamaktadır. CMake’in bütün bu sürecini şu görsel çok iyi bir şekilde özetlemektedir:
Sonuç olarak, CMake ile ilgili proje dosyalarını veya Script’leri nasıl oluşturabileceğimizi (hem komut satırından hem de GUI aracı ile) görmüş olduk. Ayrıca yine bu araçları kullanmadan CMake ile nasıl Build işlemini gerçekleştirebileceğimizi de görmüş olduk. Böylece CMake projeleri oluştururken veya varolan CMake projelerini derlemek istediğinizde bunu nasıl yapacağınızı artık biliyorsunuz. Bir sonraki yazımda CMake ile basit bir projeyi nasıl oluşturabileceğinizi anlatmayı ve CMake söz dizimi ile ilgili temel bilgileri vermeyi planlıyorum.







