CMake’te Cache Değişkenlerini Değiştirmek ve Hata Ayıklama

Bir önceki yazıda CMake’teki değişken tiplerine değinmiştim. Bildiğiniz gibi CMake’te set ve option komutları ile çeşitli değişkenleri tanımlamamız mümkündü. Son anlattığım Cache değişkenlerinin diğerlerinden farkı ise, değerlerinin kalıcı olarak tutulabilmesi özelliğiydi. Yani bir "CMakeLists.txt" dosyasında tanımladığımız ve kullandığımız normal değişkenler sadece o çalıştırmayı kapsarken, Cache değişkenleri bir sonraki CMake çalıştırmalarında da saklanabilmekteydi. Bu nedenle Cache değişkenlerinin CMake’teki yeri ve önemi oldukça büyüktür. Bu bölümde de Cache değişkenleri ile ilgili bazı detayları vermeye devam edeceğim.

Bir programlama projesinin derlenmesinde işler her zaman yolunda gitmeyebilir; çıkacak olan hedeflerden bazıları istenmeyebilir, dış paketlere ve hatta derleyici ve bağlayıcı programlarına olan yol (path) bilgileri sistemden sisteme değişiklik gösterebilir, çeşitli ekstra derleyici ve bağlayıcı bayrakları (flags) ayarlanmak istenebilir. İşte Cache değişkenleri derlemeyi yapan kişiye bu özelleştirmeleri yapma imkanı tanır. Cache değişkenlerini (tıpkı bir önceki yazıda GUI ekranında yaptığımız gibi) değiştirmenin farklı yolları bulunmaktadır. Bunlardan biri komut satırı üzerinden yapılırken, diğeri GUI üzerinden yapılır. Şimdi bu konuyu ayrıntılı olarak anlatalım.

Komut Satırından Cache Değişkenlerini Değiştirmek

Öncelikle komut satırı üzerinden Cache değişkenlerini nasıl değiştirebileceğimizi anlatalım. cmake komutuna -D seçeneğini vererek bir Cache değişkeninin değerini komut satırından değiştirmemiz mümkündür. Aslında bununla ilgili bir örneği iki önceki yazımda vermiştim:

cmake -DBUILD_SHARED_LIBS=TRUE ..

Bildiğiniz gibi bu komut ile CMake’e derleme işleminde (eğer herhangi bir şey belirtilmediyse) çıkacak kütüphane dosyalarını birer dinamik kütüphane olarak ele alması gerektiğini anlatmaktayız. Burada gördüğünüz BUILD_SHARED_LIBS gibi değişkenler, aslında CMake’in kendisinin sağladığı bir tür Cache değişkenleridir. Örneğin buradaki BUILD_SHARED_LIBS değişkeni CMake’te add_library komutunun varsayılan davranışını belirlemektedir. Bunun gibi CMake tarafından sağlanan yüzlerce değişken vardır. Bunların CMake’in son versiyonunda yer alanlarını kategorilenmiş bir şekilde şu bağlantıda bulabilirsiniz:

https://cmake.org/cmake/help/latest/manual/cmake-variables.7.html

Yeri geldiğinde bu değişkenlerden bazılarının ne anlama geldiğini açıklayacak ve onunla ilgili örnekler vereceğiz. Ancak şu anki konumuz bir Cache değişkenini komut satırından nasıl değiştirebileceğimiz ile ilgilidir. Yukarıdaki komutta aslında bunu nasıl yapacağımızı net bir şekilde görüyoruz. Bir Cache değişkenini komut satırından değiştirmenin temel formu şu şekildedir:

cmake -D variableName=value

Burada -D ile variableName arasına bir boşluk koyabilirsiniz veya en başta belirttiğim gibi onları birleşik yazabilirsiniz. Ancak "=" işaretinin sağına veya soluna boşluk koyamazsınız. Sağına koyduğunuz boşlukta bir hata almasanız da değişkene boş bir String atanacaktır. Elbette birden fazla Cache değişkenine şu şekilde değer vermeniz mümkündür:

cmake -D test=mustafa -D test2=yemural ..

Bu komutta test Cache değişkenine mustafa değeri, test2 Cache değişkenine ise yemural değeri atanacaktır. Aslında buradaki kullanım aynı isimli bir Cache değişkenini FORCE anahtar kelimesi kullanarak set komutuyla yeniden tanımlamaya benzer. Yani en son verdiğiniz değer o değişkenin yeni değeri olur. Ayrıca komut satırından bu değer verme işini bir kez yapmanız yeterlidir. cmake komutunu bir sonraki çalıştırmanızda bu değişkenlere en son verdiğiniz değerler saklanacaktır. Değişkenlere komut satırından değer atarken, onların tür bilgilerini de belirtebilirsiniz:

cmake -D testBool:BOOL=TRUE ..
cmake -D testString:STRING=NoSpace ..
cmake -D "testString2:STRING= Contains Spaces" ..
cmake -D testString3:STRING="Mustafa Yemural" ..
cmake -D testFilepath:FILEPATH=mustafa/text.txt ..
cmake -D testPath:PATH=/usr/lib ..
cmake -D test=value

Muhtemelen bu örneklerde en çok dikkatinizi çeken kısım testString2 değişkenine değer atarken, değişken ismiyle birlikte her yerin tırnak içine alınmasıdır. Boşluklu değerler verirken böyle bir kullanımın da mümkün olduğunu göstermek için bu örneği verdim. Bunun dışında eğer test isimli, değişkende olduğu gibi tür belirtmeden bir atama yapılırsa, bu durumda değişkenin türü INTERNAL olarak ele alınacaktır.

Bazen bir Cache değişkeninin değerini komut satırından değiştirseniz bile, "CMakeLists.txt" dosyasının ileriki satırlarında bu değişkenin değeri tekrar değiştirilebilir. Bu gibi durumlarda komut satırından tür bilgisi sağlanmayan değişkenleri daha sonra PATH veya FILEPATH türünde tanımlarken istisnai bir durum vardır. En son verilen yol bilgisi bir göreceli yol (relative path) ise, bu yol bilgisi CMake tarafından komutun çağrıldığı yere göre tam yola (absolute path) dönüştürülür. Ancak bu durumun birçok sakıncası vardır, çünkü cmake komutu sistemin herhangi bir yerinden çağrılabilir. Bu nedenle komut satırından yol içeren değerlere sahip değişkenlerin türlerini her zaman belirtmekte fayda vardır. Bu konuda dikkatli olmalısınız.

Bildiğiniz gibi set komutu ile değişken tanımı yaparken, unset komutu ile bu değişkeni silebiliyorduk. Komut satırından bir Cache değişkeni tanımlayabildiğimiz veya onun değerini değiştirebildiğimiz gibi, varolan Cache değişkenini silmemiz de mümkündür. Bunun için -U seçeneğinden yararlanırız. Örneğin projemizde test isimli bir Cache değişkeni varsa, bunu komut satırından şu şekilde silmemiz mümkündür:

cmake -U test ...

Yine birden fazla -U seçeneği kullanmanız da mümkündür. Komut satırından Cache değişkeni silmenin çok fazla kullanım alanı olmadığından bu konuyla ilgili şimdilik bu bilgiler yeterli diye düşünüyorum.

GUI Üzerinden Cache Değişkenlerini Değiştirmek

GUI araçları derleme yapacak geliştiricilere daha iyi bir kullanıcı deneyimi sağlamaktadır. CMake’in resmi olarak cmake-gui ve ccmake isimli iki aracı bulunmaktadır. Ben burada yalnızca cmake-gui aracından bahsedeceğim. ccmake aracı ona oldukça benzer olsa da yazı bazlı bir grafiksel ortam sunmaktadır. Her iki araç da CMake paketleri ile birlikte sisteminize yüklense de bazı GNU/Linux dağırımlarının sağladığı paketlerde bu araçlar her zaman bulunmayabilir.

Aslında cmake-gui aracından daha önceki yazılarımda bahsetsem de onun sağladığı özellikler ile ilgili genel bir tekrar yapmamızda fayda olduğunu düşünüyorum. Yine OpenCV kütüphanesi için olan GUI ekranının Generator seçimi ve Configure işlemini yaptıktan sonraki ekran görüntüsünü aşağıdaki figürde verdim:

Örnek cmake-gui Ekranı
Figür 1: Örnek cmake-gui Ekranı

Burada yukarıdaki kısma Source ve Build dizinlerinin yollarını gireceğimiz bir yer vardır. En alt kısımda zaten daha önceden bahsettiğim "Configure" ve "Generate" butonları yer alırken, ortada bu yazının konusu olan Cache değişkenlerinin bir listesi yer alır. Bu listenin sol tarafında Cache değişkeninin ismi, sağ tarafında ise onun değeri yer alır.

Bir Cache değişkeninin değer kısmının görüntüsü ona verilen tür bilgisine bağlıdır. Bunların ayrıntısını zaten bir önceki yazımda verdim. Eğer tür INTERNAL ise bu Cache değişkeninin ekranda gözükmeyeceğini de belirttim. Değişkenlerin hepsi ilk Configure işleminden sonra kırmızı bir hal almaktadır. Ancak ikinci Configure işleminde değerinde herhangi bir sorun olmayan değişkenler beyaz arkaplanlı, sorunlu değişkenler ise kırmızı arkaplanlı olarak kalmaktadır. Buradaki temel mantık, tek bir kırmızı görmeyinceye kadar gerekli düzenlemeleri yapıp Configure aşamasını tekrarlamaktır. Configure aşaması her çalıştırıldığında Cache değişkenlerinin değerleri de GUI ekranındaki değerlere göre güncellenir.

Yine en altta neler olup bittiğini bize ayrıntılı gösteren bir Log ekranı görmekteyiz. Bu ekranda eğer Configure aşaması düzgün bir şekilde tamamlandıysa "Configuring done" yazısını görürüz. Eğer bir sorun varsa bunun hangi değişkende (veya "CMakeLists.txt" dosyasının neresinde) olduğunu yine buradan görebiliriz. Bununla birlikte yukarıdaki "Search" kısmında Cache değişkenlerini arayabilirsiniz. Bu, çok fazla Cache değişkeni olan projeler için oldukça kullanışlıdır. Yine "Add Entry" ve "Remove Entry" butonlarını görmektesiniz. Bu butonlar ile yeni bir Cache değişkeni ekleyeblir veya varolan Cache değişkenini silebilirsiniz.

Arama kısmının yanında "Grouped" ve "Advanced" etiketli iki Checkbox görmekteyiz. "Advanced" Checkbox’ını işaretlersek, o anda derlediğimiz kaynak ile ilgili daha ileri düzey Cache değişkenlerine ulaşabiliriz. "Advanced" olarak işaretli olan Cache değişkenleri aslında normal kullanıcıların derleme yaparken düzenleyebildikleri değişkenler ile ileri düzeydeki kullanıcıların düzenleyebildiklerini ayırır. Böylelikle farklı kullanıcı grupları için daha temiz bir görünüm kazandırmış olursunuz. Cache değişkenlerinden hangilerinin "Advanced" olarak işaretleneceğini hangilerinin ise işaretlenmeyeceğini belirtmek için mark_as_advanced komutu kullanılır. Bu komutun genel formu şöyledir:

mark_as_advanced([CLEAR|FORCE] <var1> …)

Bu komuta aslında basitçe "Advanced" olarak işaretlenecek Cache değişkenlerinin isimlerini yazmamız yeterlidir. Eğer zaten "Advanced" olarak işaretlenmiş değişkenlerin durumunu "Advanced" olmayan hale çevirmek istiyorsanız komutun başında CLEAR anahtar kelimesini yazarak bunu yapabilirsiniz. Eğer değişkenleri "Advanced" olarak işaretlemek istiyorsanız, komutun başına FORCE anahtar kelimesini yazmalısınız. Eğer komutun başında bu iki anahtar kelimeden herhangi biri kullanılmazsa, bu durumda değişken "Advanced" olarak işaretlenir. Ancak zaten "Advanced" olarak işaretlenmiş veya "Advanced" olarak işaretlenmemiş değişkenlerin durumu bu yazım şeklinde değişmeyecektir. Sadece "Advanced" ile ilgili hiçbir işlem yapılmayan değişkenler bundan etkilenecektir. Şimdi bir örnek verelim:

set(cache1 val1 CACHE STRING "Test")
set(cache2 val2 CACHE STRING "Test")
set(cache3 val3 CACHE STRING "Test")
set(cache4 val4 CACHE STRING "Test")

mark_as_advanced(cache1 cache2) # 1
mark_as_advanced(CLEAR cache1) # 2
mark_as_advanced(cache1 cache3) # 3
mark_as_advanced(FORCE cache1) # 4

Burada öncelikle 4 değişkenin de ilk başta herhangi bir "Advanced" olma veya olmama durumu yoktur. 1. komuttan sonra cache1 ve cache2 değişkenleri "Advanced" olarak işaretlenir. 2. komuttan sonra cache1 değişkeni "Advanced" olmayan olarak işaretlenir. 3. komuttan sonra ise cache1 değişkeni "Advanced" olmayan olarak işaretlendiğinden, onun durumu değişmez. Ancak cache3 ile ilgili herhangi bir işlem yapılmadığından o "Advanced" olarak işaretlenir. 4. ve son komuttan sonra ise cache1 değişkeni zorla "Advanced" olarak işaretlenir.

Bu projeyi GUI aracıyla açıp ilk Configure aşamasını gerçekleştirdiğinizde (CMake tarafından sağlanan değişkenler dışında) sadece cache4 değişkenini görürsünüz. "Advanced" Checkbox’ını işaretlediğinizde ise diğer tüm değişkenleri de görürsünüz.

Bu arada bu olaydan INTERNAL olarak tanımlanmış değişkenler etkilenmez. Çünkü onlar zaten GUI ekranında gözükmez. "Grouped" Checkbox’ı ile Cache değişkenlerini ilk kelimelerine göre gruplandırabilirsiniz. Eğer o ilk kelimeden başka yoksa bu değişken "Ungrouped" grubu altına alınacaktır.

Aslında Property (Özellik) kavramını daha sonraki yazılarda anlatacağım. Ancak şimdilik GUI ekranı ile ilgili bir konudan burada kısaca bahsetmek isterim. Bildiğiniz gibi STRING türündeki değişkenler için GUI ekranında normal bir metin girebileceğiniz bir yer bulunur. Ancak burada kullanıcıya sizin belirlediğiniz değerlerden oluşan bir Combobox kontrolü de göstermeniz mümkündür. Bunu yapmak için tanımladığınız STRING türünden Cache değişkeninin STRINGS Property’sini değiştirmeniz gerekmektedir. Bunun için set_property komutu kullanılmaktadır. Bu komutun formunu size burada vermeyeceğim. Şimdilik aşağıdaki örnek üzerinden gidelim:

set(color red CACHE STRING "Color value")
set_property(CACHE color PROPERTY STRINGS red green blue grey)

Burada önce color isimli ve değeri red olan bir Cache değişkeni tanımladık. Daha sonra set_property komutu ile bu değişkenin alabileceği değerleri red, green, blue ve grey olarak ayarladık. Bunu "CmakeLists.txt" dosyasına yazıp cmake-gui ile Build etmeye çalıştığınızda color değişkeninin karşısındaki değer kısmına tıkladığınızda, bu değerlerden meydana gelen bir Combobox açıldığını görebilirsiniz. Bir Cache değişkeninin STRINGS Property’sini değiştirmek ona atanacak değerler ile ilgili herhangi bir engel koymaz. Sadece GUI ekranında derlemeyi yapacak kişiye yardımcı olmanızı sağlar.

Hata Yakalama

CMake projeleri oldukça basit olabilecekleri gibi çok karmaşık yapıda da olabilirler. Elbette bu tip projelerde ilk kontrol edeceğiniz şey değişkenlerin herhangi bir andaki değerleri olacaktır. CMake’te Configure aşamasında belli mesajları ve değişkenlerin değerlerini ekrana yazdırmak için message komutu kullanılmaktadır. Bu komutun genel formu oldukça basittir ve şu şekildedir:

message([<mode>] “message text” …)

Burada "[<mode>]" kısmına yazabileceğiniz çeşitli anahtar kelimeler bulunmaktadır. Bu anahtar kelimeler ekrana yazacağınız mesajın niteliğini ve CMake’in bir sonraki davranışını belirler. Bu anahtar kelimelerden bazıları ve anlamları şöyledir:

  • FATAL_ERROR: Kodda kritik bir hata olduğunu belirtir ve bu satırda Configure aşaması durdurulur. Ekrana mesaj ve mesajın verildiği satırın numarası basılır.
  • SEND_ERROR: Bu anahtar kelime de aslında kodda kritik bir hata olduğunu belirtse de mesaj ekrana basılıp Configure aşaması çalıştırılmaya devam edilir. Fakat Generation aşamasının çalıştırılmasına izin verilmez. Yani Configure aşamasının her türlü başarısız olduğunu vurgular.
  • WARNING: CMake’te gösterilen uyarı mesajlarını belirtir. Mesaj ekrana basılır ve işlemler normal bir şekilde yapılmaya devam edilir. Generation aşaması için de bir sorun teşkil etmez.
  • AUTHOR_WARNING: WARNING gibidir, ancak sadece geliştirici uyarıları aktif olduğunda ekrana basılır. CMake’te geliştirici uyarılarını açmak için cmake komutuna -Wdev seçeneğinin verilmesi gerekir.
  • DEPRECATION: CMake’te eskimişliği belirten özel bir mesaj türüdür. Eğer CMAKE_ERROR_DEPRECATED değişkeninin değeri true olarak tanımlandıya bu mesaj bir hata olarak ele alınır. Eğer CMAKE_WARN_DEPRECATED değişkeni true olarak tanımlandıysa bu mesaj bir uyarı olarak ele alınır. Eğer bu değişkenlerin hiç biri tanımlanmadıysa mesaj ekranda gösterilmez.
  • STATUS: Küçük bilgilendirme mesajlarını belirtir. Mesajlar genellikle iki tire (-) ile başlayarak ekrana basılır.
  • NOTICE: Mesajın Standart Hata (Standard Error) akımına yazılmasını ve kullanıcının farkına varması gereken önemli bir mesaj olduğunu belirtir. "[<mode>]" kısmına hiçbir anahtar kelime yazılmaması ile NOTICE yazılması aynı anlama gelir.
  • VERBOSE: Projenin kullanıcıları için detaylı bilgiye sahip mesajlardır. Bu ek detaylı mesajlar çoğu durumda derleme yapan kişileri ilgilendirmez. Ancak proje hakkında daha detaylı bilgiler isteyen kişilere özel olur.
  • DEBUG: Projenin kendisinde çalışan geliştiricileri ilgilendiren mesajlardır. Genellikle projeyi derlemek isteyenleri ilgilendirmez ve içsel implementasyonla ilgili mesajları içerir.
  • TRACE: Çok düşük seviye implementasyon bilgileri içeren mesajları belirtir. Genellikle projeyi yayınlarken (veya paketlerken) bu mesajlar silinir.

NOTICE, VERBOSE, DEBUG ve TRACE anahtar kelimeleri CMake 3.15 ile eklenmiştir. Şimdi message komutunun kullanımı ile ilgili birkaç örnek verelim:

set(name "Mustafa Yemural")
set(error "Bir hata var!")

message("This is a notice!")
message(WARNING "This is a warning!")

set(CMAKE_WARN_DEPRECATED true)
message(DEPRECATION "Deprecation warning!")

message(STATUS "Status message from ${name}")
message(SEND_ERROR "${error}")

Bu komutları içeren bir CMake projesini derlemeye kalktığınızda şuna benzer bir çıktı ile karşılaşırsınız:

Örnek Komutların Çıktısı
Figür 2: Örnek Komutların Çıktısı

Burada DEPRECATION türünde bir mesaj çıktısı vermeden önce CMAKE_WARN_DEPRECATED değişkeninin değerini true yaptığımıza dikkat edin. Bu şekilde eskime ile ilgili mesajlar ekrana bir hata iletisi olarak değil, bir uyarı iletisi olarak yazılmış olur. Ayrıca uyarı ve hata iletilerinde ekrana mesajla birlikte satır numarasının da yazıldığına dikkat edin. STATUS mesajlarının başına otomatik olarak "—" eklendiğini de çıktıdan farkedebilirsiniz. Eğer kodda SEND_ERROR veya FATAL_ERROR türünden bir mesaj ile karşılaşılırsa, çıktıda yer alan en alttaki mesajı göreceksiniz.

Değişkenlerin Değerlerini İzlemek

message komutunu ekrana mesaj yazmak için kullanabileceğiniz gibi, değişkenlerin o anki değerlerini öğrenmek için de kullanabilirsiniz. Ancak bu iş için CMake’te başka bir komut daha bulunmaktadır. variable_watch isimli bu komut ile, değeri sürekli değişen ve en son değerinin ne olacağına karar vermekte zorlandığınız değişkenleri izleyebilirsiniz. Bir değişkeni izlemeye aldığınızda, o değişken ile ilgili yapılan bütün okuma ve yazma eylemleri Log olarak kayıt edilir. variable_watch komutunun genel formu şöyledir:

variable_watch(<variable> [<command>])

"<variable>" kısmına izlenecek değişkenin adı yazılırken "[<command>]" kısmına ise bu değişkene her yazma veya okuma yapıldığında çalıştırılacak komut yazılır. Komut kısmının ayrıntılarını burada vermeyeceğim. Genellikle bir değişkeni komut ile izleme kullanılmaz. Basitçe variable_watch komutuna izlenecek değişken ismi yazılarak kullanılır. Hemen basit bir örnek vererek yazıyı sonlandıralım:

set(test mustafa)
variable_watch(test)

set(test yemural)
message("${test}")

set(test merhaba)

test isimli değişkeni ilk olarak tanımladıktan sonra onu izliyoruz. Sonra da onun değerini değiştirip yeni değeri ekrana basıyoruz. En sonunda onun değerini tekrar değiştiriyoruz. Bu değişkene seri bir şekilde yaptığımız okuma ve yazmaları izlemek için cmake komutunu aşağıdaki gibi --trace seçeneği ile çağırmamız gerekir:

cmake --trace ..

Bu seçenek ile cmake komutu çağrıldığında ekrana muhtemelen uzun bir çıktının yazıldığını göreceksiniz. Bu çıtının sonlarında ise şuna benzer ifadeler görmeniz mümkündür:

Değişken İzlemede Örnek Ekran Çıktısı
Figür 3: Değişken İzlemede Örnek Ekran Çıktısı

Bu çıktıda gördüğünüz MODIFIED_ACCESS değişkene yapılan yazmayı, READ_ACCESS ise değişkene yapılan okumayı belirtmektedir. Bu çıktıya göre 8. satırda değişkenin değeri yemural olarak değiştirilmiş ve 9. satırda bu değişkenin değeri message komutu ile okunmuştur. Daha sonra 11. satırda bu değişkenin değeri merhaba olarak tekrar değiştirilmiştir. İşte variable_watch komutu ile değişkenleri bu şekilde ayrıntılı olarak izlemeniz mümkündür. Ancak çoğu durumda message komutunu kullanmak da iş görecektir.

Bu yazıda Cache değişkenlerini hem komut satırından hem de GUI uygulamasından nasıl değiştirebileceğimizi ve Cache değişkenlerini GUI ekranında kategorileştirmek için yapmamız gerekenleri gösterdim. Ayrıca derleme aşamasında ekrana çeşitli türlerde mesajlar basmayı ve bir değişkene yapılan okuma ve yazma erişimlerini izlemeyi anlatmaya çalıştım. Böylelikle genel anlamda değişkenler konusunu bitirmiş olduk. Bir sonraki yazıda String’ler ve listeler üzerinde özel işlemler yapan çeşitli komutlardan bahsedeceğim.

5 3 votes
Article Rating
Subscribe
Bildir
guest

0 Yorum
Eskiler
En Yeniler Beğenilenler