CMake – II (devam)

Evet arkadaşlar CMake ile ilgili ikinci yazımıza hoş geldiniz. İlk yazı için aşağıdaki linki takip edebilirsiniz:

Merhaba CMake

Bu yazımda CMake ile ilgili diğer kabiliyetlere değinip daha fazla örnekleri inceleyeceğiz. Şunu belirtmem gerekiyor ki her konuda olduğu gibi CMake’i de en iyi öğrenme şekillerinden biri kendi kişisel veya diğer projelerinizde bunu kullanmak ve diğer örnek CMake kodlarını incelemek. Açıkçası bu zamana kadar ben ikinci yönteme daha fazla ağırlık verdim ama bundan sonra ben de projelerimde CMake kullanmaya başlayacağım. Bu yazıda bahsedeceğim hususları başlık başlık vermeye çalıştım. Örnek kodları aşağıdaki adresten edinebilirsiniz:

Değişken kullanımı:

* Değişkenleri aşağıdaki gibi tanımlayabiliyoruz (bunu ilk yazımdan hatırlıyorsunuzdur)

sonrasında ise

şeklinde kullanabiliyoruz. Ayrıca komut satırından da bu değişkenlere değer atayabilirsiniz. Onun için de aşağıdaki komutu kullanabilirsiniz:

“Header” dosyaların belirtilmesi:

Oluşturma işlemlerinde en çok ihtiyaç duyduğumuz konulardan birisi de “header” dosyalarının (C++ da *. h, *. hpp) bulunduğu dizinlere erişim. Bunun için “include_directories()” komutunu kullanıyoruz. Bunun normal kullanımı “include_directories( ${MY_SOURCE_DIR}/include)” gibidir. Eğer buna bir öncelik tanımlamak isterseniz de ayrıca bir parametre geçirerek bunu sağlayabilirsiniz. Aşağıda buna ilişkin de bir kullanım görebilirsiniz:

Bu komut ile ilgili olarak;

  • Göreceli dizinler mevcut kaynak dizine göredir,
  • BEFORE/AFTER parametreleri ile ilgili dizinin, dizin arama listesinde nereye ekleneceği belirtebilirsiniz. Varsayılan olarak bunlar sona ekleniyor.

Daha detaylı bilgi için aşağıdaki adrese bakabilirsiniz.

https://cmake.org/cmake/help/latest/command/include_directories.html

Birden fazla dizinden oluşan projeler

Öncelikli olarak herhangi bir kütüphane kullanmayan ama birden fazla dizinden oluşan bir örnek inceleyelim. Bu örnekteki dizin yapısı aşağıdaki gibi olacak (bu arada bu gösterimi komut satırı üzerinden “tree” komutunu kullanarak oluşturabilirsiniz):

Bu örnekte sadece tek bir CMakeLists.txt kullanıyoruz. Bu yapıdaki bir projeyi oluşturmak için aşağıdaki gibi bir kod işinizi görecektir.

Alt projelerin eklenmesi

VS gibi bir çok IDE projeleri mantıksal “solution” konteynerleri içerisinde barındırmakta ve geliştiriciye bunları sunmakta. Fakat CMake de direk bunun karşılığı bir kavram yok. Bunun yerine bir çok proje arasında bağımlılık tanımlanabiliyor. Bunu CMake ile başarmak için öncelikle olarak VS’de “solution” dediğimiz tepe proje için en üst seviyede bir CMakeLists.txt oluşturulur ve bu kök dizinde tutulur. Daha sonra eklenecek her ek proje için ayrı bir dizin açılıp bunların içerisinin her birine ilgili projeleri adresleyecek olan CMakeLists.txt dosyaları eklenir. Son olarak bu projeler tepe projeye “add_subdirectory(dizinIsmi)” ile eklenir. Büyük projeleri bu şekilde gruplandırmak ve organize etmek her zaman idameyi kolaylaştıracaktır.

C++ ilişkin bazı ayarlar

Daha önceki yazılarımı takip edenler bir süredir Modern C++ ile ilgili yazılarımı görmüşlerdir. Her ne kadar artık çoğu C++ derleyicisi modern C++ standartlarını desteklese de, bazı kullanıcılar için bu geçerli olmayabilir ve bunu kontrol etmekte her zaman fayda vardır. Diyelim ki C++ 14 uyumlu bir derleyici ihtiyacınız var ve bunun ile ilgili ayarlamaları derleyiciden bağımsız bir şekilde yapmak istiyorsunuz. İşte bunun için CMake’in sunduğu ön tanımlı değişkenlerini kullanabilirsiniz. Bu durum için bizim kullanacağımız değişken ise CMAKE_CXX_STANDARD değişkeni. Bu değişken ile aslında komut satırından “-std=c++14” geçirmiş olduğunuzu düşünebilirsiniz. Bunun için bu değişkene ilgili değeri atamanız yeterli.

Bunun ile birlikte derleyiciden derleyiciye değişen bir diğer ayar da CMAKE_CXX_FLAGS ön tanımlı değişkenidir. Bunun ile de genel bir şekilde uyarı seviyelerini ayarlayabilirsiniz. Örneğin bütün uyarıları görmek için aşağıdaki gibi bir komut kullanabilirsiniz.

Bu arada birden fazla parametre ayarlamak için de aşağıdaki bir kullanımı takip etmeniz önceki ayarları da korumanız için faydalı olacaktır:

Burada bu ayarı yapsanız bile CMake eğer C++ sürümü ile uyumlu bir derleyici bulamaz ise sadece uyarı verip, en son desteklenen sürümü kullanır. Bunu da önlemek için aşağıdaki komutu eklemeniz gerekiyor.

Çoğu oluşturma işleminiz için yukarıdaki mekanizma yeterli olacaktır. Bu yöntemin yanında CMake’in 3.1 sürümünden bu yana sunduğu bir yaklaşım daha var. O da sizin derleyiciden beklediğiniz özellikleri belirtmeniz ve bunun ile ilgili kontrolleri CMake bırakmanız. Bunu “target_compile_features()” komutu ile gerçekleştirebiliyorsunuz. Aşağıda bu komutun genel yapısını görebilirsiniz:

PRIVATE|PUBLIC|INTERFACE” parametrelerinin anlamı şu. Eğer “PRIVATE” seçili ise; burada belirtilecek olan dil özellikleri sadece verilen “target” için kontrol edilir. Eğer “PUBLIC” ise bunu kullanan bütün kod parçaları için kontrol edilir ve eğer “INTERFACE” ise sadece buna bağlanan kod parçaları için kontrol edilir kullanılacak olan. (Dışarıdan, daha önce var olan hedefler için sadece bu seçenek kullanılabilir). Tahmin edeceğiniz üzere “…” kısımlarına ilgili C++ dil özelliklerini ekleyebilirsiniz. Kullanılan CMake tarafından bilinen dil özellikleri için ise CMAKE_CXX_KNOWN_FEATURES e bakabilirsiniz.

Aşağıda örnek bazı kullanımları görebilirsiniz:

Kütüphane oluşturulması

Şimdi CMake ile ilgili örneklerimizi bir adım daha öteye taşıyalım ve kütüphaneleri nasıl oluşturabileceğimize bir göz atalım. Bu amaçla çok basit bir matematik kütüphanesi tanımlayacağız. Bu örnek için kullanacağımız dizin yapısı aşağıdaki gibi:

İlk olarak kütüphane kodlarını direk ekleyerek çalıştırabilir kodu üretebilirsiniz. Bunun için birden fazla dizinin kullanımına ilişkin yöntemi takip edebilir ve aşağıdaki CMakeLists.txt dosyasını kullanabilirsiniz:

Ama bizim amacımız matematik ile ilgili olan kodları ayrı bir kütüphane olarak oluşturmak ve onları kullanmak. Bu noktada genel olarak takip edilen iki yöntem var bunlardan ilki: Kütüphanelere ilişkin oluşturma kodlarının da tepe CMakeLists.txt içerisine yerleştirilmesi ve bunları çalıştırılabilir kod ile birlikte oluşturulması.
Ya da bunların ayrı olarak her bir kütüphane içerisine yerleştirilerek ana CMakeLists.txt içerisinden bunun kullanılması. Genel olarak basit uygulamalarda her ne kadar birinci yöntem da fazla kabul görse de, ikinci yöntem ile kütüphanenin oluşturma işlemi ile çalıştırılabilir kod oluşturma işini birbirinden ayırabilir, sadece kütüphaneleri oluşturabilirsiniz. Ben alışkanlık olması açısından ikinci yöntemi önereceğim keza projelerin büyüklükleri arttıkça ilk yöntemi idame ettirmek oldukça zorlaşacaktır.

Şimdi ilk yönteme ilişkin hemen bir örnek inceleyelim. Öncelikli olarak oluşturulacak olan kütüphaneleri farklı bir dizine koymayı hedefleyelim. Bunun için ön tanımlı EXECUTABLE_OUTPUT_PATH/LIBRARY_OUTPUT_PATH değişkenlerini kullanabiliriz. Ayrıca oluşturacağımız bu kütüphaneleri nihai çalıştırılabilir uygulamaya da eklememiz gerekiyor. Bunun için de “target_link_libraries()” komutunu kullanacağız. Bu yöntem için aşağıdaki CMakeLists.txt’yi kullanabiliriz.

Bu arada CMake’in kütüphane oluşturmak için kullandığı komut “add_library()” komutudur. İlk parametre kütüphane ismi ikincisi ise tipi ve diğerleri de dosyaları belirtir.

CMake komutu ile oluşturma işlemini yaptığınızda build/lib dizini içerisine ilgili kütüphane dosyasının oluşturulduğunu göreceksiniz.

Şimdi gelelim ikinci yönteme. Bunun için ilgili kütüphane dizini içerisine de CMakeLists.txt ekleyeceğiz ve mevcut olanı da biraz değiştireceğiz.
Öncelikle lib/math içerisine aşağıdaki gibi bir dosya oluşturalım:

Tepe dizindeki CMakeLists.txt dosyasını da aşağıdaki gibi güncelleyelim:

Gördüğünüz üzere aslında çıktı anlamında bir değişiklik olmadı ama daha düzenli ve idame edilebilir bir oluşturma yaklaşımı sağlamış olduk.

Burada dikkat edilmesi gereken nokta ise özellikle paylaşılabilir dinamik kütüphanelerin kullanımları (“Shared Libraries”) her platform için farklılık göstermekte. Örneğin yukarıdaki örneklerde STATIC yerine SHARED seçeneğini direk seçip Windows üzerinde oluşturmayı deneseydiniz ne yazık ki hata ile karşılaşırdınız. Çünkü VS derleyicisi DLL’i direk bağlayamıyor (MinGW’de de sıkıntı yok). Bunu kullanabilmek için bazı noktalara daha değinmemiz gerekecek. Öncelikli olarak MinGW ile oluşturmuş olduğunuz DLL kütüphaneleri bütün sembolleri dışarıya sunuyorlar. Eğer metotlardan herhangi biri __declspec(dllexport) ile işaretlenmiş ise, bu durumda sadece bunlar dışarı sunuluyor. VS derleyicisinde ise varsayılan olarak hiç bir sembol dışarı sunulmamakta. Bu sebeple VS derleyicisi ile DLL oluşturmak istediğinizde, __declspec(dllexport) ifadesini dışarı sunulmak istenen metotlar önüne eklemeniz gerekiyor.

Açık kaynaklı ve windows platformunu destekleyen yazılımları kullananlarınız aşağıdaki tanımlamalar ile öyle ya da böyle bir noktada karşılaşmışlardır.

Bunu yaptığımız zaman VS derleyicisi bizim için bir .lib dosyası daha oluşturuyorlar, fakat bu normal statik kütüphanelerden farklı olarak sembol tanımlamaları içermiyor fakat derleme için yeterli tanımlamaları sunuyor. Bu durumda SHARED_EXPORT tanımlamasını ilgili sınıfların önüne eklememiz gerekiyor. Hatta VS içerisinde .def dosyaları hazırlayabilirsiniz ve bu sayede dışarıya bu sembolleri sunabilirsiniz. Peki bunu bizim yerimize CMake yapmaz mı 🙂 Elbette yapar.

Öncelikli olarak lib/math dizini içerisindeki CMakeLists.txt yi aşağıdaki satırları ekleyelim ve STATIC tanımlamasını DYNAMIC ile değiştirelim.

Tepe dizindeki CMakeLists.txt ye de sadece ilgili tanımlama dosyasına erişmek için bir “include()” komutu ekleyeceğiz

Son olarak matematik sınıfımızı aşağıdaki gibi güncelleyelim.

evet şimdi tekrar build dizini içerisine girip “CMake ..” komutunu çağırdığımızda. Dinamik kütüphanemizi kullanan bir VS oluşturma dosya kümesi elde etmiş olacağız.

Bu arada DLL ve diğer paylaşımlı kütüphanelerin oluşturulmasına ilişkin

http://gernotklingler.com/blog/creating-using-shared-libraries-different-compilers-different-operating-systems/

de çok güzel bir anlatım var, ona da bakabilirsiniz.

Opsiyonlar

Şimdi doğal olarak aklınıza “yav her seferinde statik veya paylaşımlı kütüphane seçimini CMakeLists.txt üzerinden yapmasak olmaz mı?” sorularının geldiğini duyar oluyorum 🙂 Ya da bu CMake içerisine kendimize ilişkin opsiyonlar ve benzeri mekanizmalar ekleyemez miyiz diye soruyor olabilirsiniz. Elbette ekleyebilirsiniz. Bir önceki örneğimizden devam edecek olursak. Statik veya dinamik kütüphane oluşturma işini bir opsiyona bağlayıp bunu da ya CMake-GUI veya komut satırından vermek istediğimiz düşünelim. Bunun için “option()“komutunu kullanabilirsiniz. Kısaca bu komut:

şeklinde. Yani ilk parametre bizim opsiyona verdiğimiz isim, ikincisi onun açıklaması ve üçüncüsü de ilk değeri ki bu ya ON ya da OFF oluyor. Varsayılan değer OFF.

Bu bağlamda ilgili CMake dosyalarımız aşağıdaki gibi değişiyor:

Tepe dosyamız:

Kütüphane dosyamız da:

şekline dönüşüyor. Opsiyonlara ilişkin detaylar için

https://cmake.org/cmake/help/latest/command/option.html?highlight=i

sayfasına bir göz atabilirsiniz.

Son olarak burada örnek verdiğim ve örnekler için aşağıdaki ambara bir göz atabilirsiniz:

https://github.com/yazilimperver/CMake-Files

Ayrıca aşağıdaki sayfada da oldukça çeşitli örnek kod ve kullanım var:

https://github.com/onqtam/awesome-cmake

Kaynaklar

https://medium.com/@onur.dundar1/cmake-tutorial-585dd180109b

https://www.wikiwand.com/en/CMake#/Applications_that_use_CMake

https://crascit.com/2015/03/28/enabling-cxx11-in-cmake/

https://yazilimcorbasi.blogspot.com/2015/04/cmake.html

http://gernotklingler.com/blog/creating-using-shared-libraries-different-compilers-different-operating-systems/

https://cmake.org/cmake/help/v3.0/module/GenerateExportHeader.html

https://cmake.org/cmake/help/latest/command/option.html?highlight=i

https://www.jetbrains.com/help/clion/quick-cmake-tutorial.html

http://preshing.com/20170522/learn-cmakes-scripting-language-in-15-minutes/

Bir cevap yazın

E-posta hesabınız yayımlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir

Bu site, istenmeyenleri azaltmak için Akismet kullanıyor. Yorum verilerinizin nasıl işlendiği hakkında daha fazla bilgi edinin.