CMake’te Akış Kontrolü ve Döngüler

Programlama dillerinde kullanılan if-else gibi koşul yapıları veya while gibi döngü yapıları, programın belli kararlara göre akışını kontrol ederler. Örneğin programda bir sıcaklık değerinin aralığına göre ekrana mesaj yazdırmak istediğinizde if-else yapısını kullanabilirsiniz. Yine belli değerler üzerinde dolaşıp belli işlemleri yapmak istediğinizde ise while ve for gibi döngü yapılarını kullanabilirsiniz. İşte CMake’te de derleme işlemini belli koşullara göre dallandırmak ve yine belli değerler üzerinde dolaşım sağlamak için bu gibi kontrol ve döngü yapıları bulunmaktadır.

İlk olarak if komutundan başlayalım. Bu komut bildiğiniz if kontrolünü gerçekleştirmektedir. Elbette elseif ve else gibi kısımları da bulunmaktadır. CMake’in çok eski versiyonlarında bu komutun kullanımı şu şekildeydi:

if(<koşul1>)
<komutlar>
elseif(<koşul2>)
<komutlar>
else(<koşul1>)
<komutlar>
endif(<koşul1>)

Bunun anlamı, if içerisine yazdığınız koşulun aynısını (eğer varsa) else kısmına ve endif kısmına yazmanız gerekmekteydi. Ancak artık bu hantal kullanıma ihtiyaç duyulmamaktadır (CMake 2.8 ve sonrasında). Onun yerine else ve endif parantezlerinin içinin boş bırakılması yeterlidir:

if(<koşul1>)
<komutlar>
elseif(<koşul2>)
<komutlar>
else()
<komutlar>
endif()

Buradan anlayacağınız gibi belli koşullara göre “<komutlar>” kısmı çalıştırılmaktadır. Peki koşul kısımlarına neler yazılabilir? Öncelikle basit bir örnek üzerinden gidelim:

set(value1 TRUE)
set(value2 0)

if(value1)
    message("value1 true")
endif()

if(value2)
    message("value2 true")
endif()
ÇIKTI
value1 true

Burada koşula yazılan doğruluk veya yanlışlığa nasıl karar verildi? CMake’te doğru-yanlış kavramı programlama dillerinden biraz farklı olarak işlemektedir:

  • Eğer bir değer tırnak içine alınmamış şu sabitlerden biriyse; 1, ON, YES, TRUE, Y veya sıfırdan farklı bir sayıysa (ondalıklı sayılar da dahil), bu değer CMake’te doğru (true) olarak kabul edilir.
  • Eğer bir değer tırnak içine alınmamış şu sabitlerden biriyse; 0, OFF, NO, FALSE, N, IGNORE, NOTFOUND veya boş bir String ise veya -NOTFOUND son eki ile biten bir değerse, bu değer CMake’te yanlış (false) olarak kabul edilir.

Buradaki isimli sabitlerde büyük-küçük harf uyumluluğu aranmaz. Eğer if koşuluna yazdığımız şey yukarıdakilerden biri değilse, o zaman ona değişken ismi veya String olarak davranılır. Yani yukarıdaki örnekte bu gibi bir durumdan söz edilebilir. Normalde şu şekilde bir yazım yapmış olsaydınız, basitçe değişken değerleri kullanıldıkları yerde yer değiştirmiş olacaktı:

if(${value1})
    message("value1 true")
endif()

if(${value2})
    message("value2 true")
endif()

Bu da aslında şu kullanıma denktir:

if(TRUE)
    message("value1 true")
endif()

if(0)
    message("value2 true")
endif()

Eğer değişken daha önceden tanımlanmamışsa, bu durumda koşul yanlış olarak değerlendirilir:

if(nothingvalue)
    # Buraya girmez
endif()

Ancak koşul yerine çift tırnaklarla bir String yazarsanız, o zaman işler biraz değişecektir. CMake 3.1 ve sonrasında bu String’in içerisine ne yazarsanız yazın ifade yanlış olarak kabul edilecektir. Ancak CMake 3.1 öncesinde bu durum biraz farklıdır. String’in içerisinde yazdığınız değer eğer yukarıda bahsettiğimiz doğruluk-yanlışlık sabitlerinden biriyse, bu durumda bu sabitlere göre sonuç üretilecektir. Yani aşağıdaki kodda CMake 3.1 ve sonrasında hiçbir şey yazılmayacak, ancak 3.1 öncesinde mesaj ekrana basılacaktır:

if("ON")
    message("Before CMake 3.1")
endif()

Tıpkı programlama dillerinde olduğu gibi CMake’te de koşulları mantıksal operatörler ile birbirlerine bağlayabilirsiniz. Mantıksal operatörler burada birer kelime ile ifade edilirler. Bu kelimeler ve anlamları şöyledir:

  • NOT: İfadeyi DEĞİL işleminden geçirir.
  • AND: İki ifadeyi VE işlemine sokar.
  • OR: İki ifadeyi VEYA işlemine sokar.

Bu operatörler ile birlikte koşul kısmında parantezlerin kullanımı da mümkündür. Bu operatörler ile ilgili birkaç kullanım örneği verelim:

if(NOT ifade)
if(ifade1 AND ifade2)
if(ifade1 OR ifade2)
if(ifade1 AND (ifade2 OR ifade3))
if(NOT (ifade1 AND ifade2))

Burada birden fazla operatörü aynı anda kullanırken parantez kullanımını mutlaka öneririm. Yoksa öncelik sıraları gibi konularla uğraşmak zorunda kalabilirsiniz. Mantıksal operatörlere ek olarak CMake’te karşılaştırma operatörleri de bulunmaktadır. Bu operatörler programlama dillerindeki gibi “<” ve “>=” gibi sembollerle değil kelimelerle belirtilirler. Ancak CMake’te bu operatörlerle ilgili bir farklılık daha vardır. CMake’te karşılaştıracağınız değer ve ifadelerin türlerine göre operatörler ile ilgili yazacağınız kelimeler değişmektedir. İlk olarak sayısal değerleri karşılaştırabileceğiniz operatörleri inceleyelim:

OperatörAnlamı
EQUALSayısal olarak eşitlik.
LESSSayısal olarak küçüklük.
GREATERSayısal olarak büyüklük.
LESS_EQUALSayısal olarak küçük veya eşit olma.
GREATER_EQUALSayısal olarak büyük veya eşit olma.

LESS_EQUAL ve GREATER_EQUAL operatörleri CMake 3.7 ile gelmiştir. Sayısal olarak karşılaştırma operatörleri aslında sizin de beklediğiniz davranışı gerçekleştirir; sol taraf ile sağ tarafı karşılaştırır. Ancak bu operatörler ile ilgili dikkat etmeniz gereken bazı hususlar bulunmaktadır. Eğer sağ veya sol tarafta sayısal değerden ziyade bir String kullanılırsa ve bu String sadece sayısal bir değer içeriyorsa, bu durumda CMake bu karşılaştırmadan şikayet etmez ve işini yapar. Örneğin aşağıdaki ifadelerin hepsi doğrudur ve if koşuluna girecektir:

set(value1 92)
set(value2 "1")

if(3 EQUAL 3)
if(${value1} GREATER 10)
if("${value1}" LESS_EQUAL 92)
if("2" LESS 5)
if(${value1} GREATER_EQUAL ${value2})
if("${value2}" EQUAL 1)

Ancak şu şekilde bir karşılaştırma oldukça güvenilmez bir karşılaştırmadır:

if("1234abc" GREATER 1)

Bu nedenle bu gibi karşılaştırmalardan kaçınmak için sayısal karşılaştırmalarda String’leri kullanmamanız önerilir. Şimdi de karşılaştırma operatörlerinin String versiyonlarına bakalım:

OperatörAnlamı
STREQUALString olarak eşitlik.
STRLESSString olarak küçüklük.
STRGREATERString olarak büyüklük.
STRLESS_EQUALString olarak küçük veya eşit olma.
STRGREATER_EQUALString olarak büyük veya eşit olma.

Sayısal karşılaştırma operatörlerine STR ön eki getirilerek String karşılaştırma operatörlerine dönüştürülebilir. Bu operatörlerden STRLESS_EQUAL ve STRGREATER_EQUAL operatörleri CMake 3.7 ile gelmiştir. Bu operatörler String’leri sözlüksel olarak (lexicographical) karşılaştırırlar. Yani ilk harften başlayarak sırasıyla sözlüksel karşılaştırma yapılır. Örnek:

set(value1 "test1")
set(value2 "test0")

if("Mustafa" STREQUAL "Musa")
if("Venezuala" STRGREATER "Gine")
if("${value1}" STRLESS_EQUAL "test2")
if("2" STRLESS "3")
if(${value1} STRGREATER_EQUAL ${value2})
if("${value2}" STREQUAL "test0")

Yukarıda ilk if haricinde diğer bütün if yapılarına girilecektir. Şimdi CMake’te bir diğer karşılaştırma türü olan versiyon karşılaştırma operatörlerine bakalım. Bu operatörlerin karşılaştırma biçimleri oldukça ilginç olsa da çoğu yerde oldukça kullanışlıdırlar. Versiyon karşılaştırma operatörleri iki tarafındaki versiyonları karşılaştırır. CMake’te versiyon bilgisinin “major[.minor[.patch[.tweak]]]” olarak ele alındığını daha önce söylemiştik. Şimdi önce bu operatörleri sıralayalım:

OperatörAnlamı
VERSION_EQUALVersiyon olarak eşitlik.
VERSION_LESSVersiyon olarak küçüklük.
VERSION_GREATERVersiyon olarak büyüklük.
VERSION_LESS_EQUALVersiyon olarak küçük veya eşit olma.
VERSION_GREATER_EQUALVersiyon olarak büyük veya eşit olma.

Tablodan anlayabildiğiniz gibi normal karşılaştırma operatörlerinden tek farkı, önlerine VERSION_ ekinin gelmesidir. VERSION_LESS_EQUAL ve VERSION_GREATER_EQUAL operatörleri yine CMake 3.7 ile birlikte gelmiştir. Şimdi birkaç örnek ile durumu tam olarak anlamaya çalışalım:

set(version1 2.4.5)
set(version2 3.0)

if(2.4 VERSION_EQUAL 2.4.0) # True
    message("1")
endif()

if(3 VERSION_GREATER 2.4) # True
    message("2")
endif()

if(3.4.5.3 VERSION_LESS 3.4.5.2) # False
    message("3")
endif()

if("${version1}" VERSION_LESS_EQUAL 2.4) # False
    message("4")
endif()

if(${version2} VERSION_GREATER_EQUAL 3) # True
    message("5")
endif()
ÇIKTI
1
2
5

Çok kısa bir örnek ile düzenli ifade (regular expression) karşılaştırmasını da göstermek istiyorum. Bu karşılaştırmada MATCHES operatörü kullanılır. Bu operatörün sağına düzenli ifade yazılırken sol tarafına ise eşleşmesi kontrol edilecek String veya değişken yazılır. Örnek:

set(word "test2")

if(${word} MATCHES test[123]*) # True
    message("Result: ${CMAKE_MATCH_0}")
endif()
ÇIKTI
Result: test2

Eşleşmeden elde edilen sonuçlar CMAKE_MATCH_<n> değişkenlerinde saklanacaktır. Bu değişken CMake 3.9 ile gelmiştir. Burada “<n>” yerine 0 ile 9 arası bir rakam yazılır. 0 yazarsanız bu bütün eşleşmeyi belirtecektir. 1 ile 9 arası ise alt grup sonuçlarını belirtmektedir. Bu kullanımlarla ilgili daha fazla bilgi öğrenmek istiyorsanız bazı örnekler ile kendiniz kurcalamanızı tavsiye ederim. Şimdi CMake’te bizim için oldukça faydalı olan dosya sistemi ile alakalı operatörlere geçelim. Bu anahtar kelimeler ile dosya sisteminde çeşitli şeyleri sorgulayabilir ve if ile duruma göre belli kodları çalıştırabiliriz:

Anahtar KelimeAnlamı
EXISTSBelirtilen dosya veya dizin sistemde varsa doğru, yoksa yanlış döndürür. Ancak düzgün bir sonuç alınması için yolun tam olarak verilmesi (full path) tavsiye edilir. Eğer verilen yol bilgisi bir sembolik link ise bu sembolik link çözümlenerek işlem yapılır.
IS_NEWER_THANEğer sol tarafında belirtilen dosya sağ tarafındakinden yeniyse doğru, değilse yanlış döndürür. Düzgün sonuç almak için yolun tam olarak verilmesi (full path) tavsiye edilir. Eğer dosyaların zaman bilgileri birebir birbiriyle aynıysa da doğru döndürülür.
IS_DIRECTORYBelirtilen yol bir dizin ise doğru, değilse yanlış döndürür. Düzgün sonuç almak için yolun tam olarak verilmesi (full path) tavsiye edilir.
IS_SYMLINKBelirtilen yol bir sembolik link ise doğru, değilse yanlış döndürür. Düzgün sonuç almak için yolun tam olarak verilmesi (full path) tavsiye edilir.
IS_ABSOLUTEBelirtilen yol bir tam yol (absolute path) ise doğru, değilse yanlış döndürür. Bazı özel durumları bulunmaktadır.

Burada IS_ABSOLUTE ile ilgili özel durumları açıklamakta fayda var. Bu özel durumlardan ilki, yol bilgisi boş verildiğinde sonucun yanlış olarak döndüğüdür. İkincisi ise, Windows’ta yol ismi C: gibi sürücü ismi ile, düz kesme işareti ile (/) veya ters kesme işareti ile (\) başlıyorsa, bunlardan sonra gelen kısmın göreceli yol (relative path) olup olmadığına bakılmaksızın sonuç doğru döndürülür. Bu anahtar kelime ile ilgili son özel durum ise, Windows olmayan sistemlerde tilde işareti (~) ile başlayan yol bilgilerinin doğru olarak döndürülmesidir. Şimdi örneklere geçebiliriz:

set(path C:\\Users\\yemural\\Documents\\temp)

if(EXISTS ${path})
    message("Path is exists!")

    if(IS_ABSOLUTE ${path})
        message("Path is absolute!")
    endif()

    if(IS_DIRECTORY ${path})
        if(${path}\\file1.txt IS_NEWER_THAN ${path}\\file2.txt)
            message("file1.txt is newer than file2.txt")
        else()
            message("file2.txt is newer than file1.txt")
        endif()

    elseif(IS_SYMLINK ${path})
        message("Symbolic link: Nothing to do")
    else()
        message("File: Nothing to do")
    endif()
endif()

Yukarıdaki örnek aslında hem if-else kullanımına güzel bir açıklama getiren hem de dosya sistemi ile ilgili operatörlere gerçek bir kullanım senaryosu gösteren bir örnektir. Bu örnekte yer alan yol bilgisinin ve dosyaların var olduğunu ve ayrıca file1.txt dosyasının file2.txt dosyasından daha yeni olduğunu varsayarak şöyle bir çıktıya ulaşabilmemiz mümkündür:

ÇIKTI
Path is exists!
Path is absolute!
file1.txt is newer than file2.txt

Yine bu yol (path) konusuyla ilgili CMake 3.24 ile gelen bir özelliğe değinmek istiyorum. CMake 3.24 ile PATH_EQUAL isimli bir operatör gelmektedir. Bu operatör ile iki yol bilgisinin birbiri ile karşılaştırmanız mümkündür. Peki bu neden önemli olsun? Doğrudan String karşılaştırması yapamaz mıydık? Şu örneği inceleyelim:

if ("/a//b/c" STREQUAL "/a/b/c") # False

Burada yer alan yol bilgileri mantıksal olarak birbirinin aynısı olsa da String olarak karşılaştırıldıklarında sonuç yanlış çıkacak ve if içine girilmeyecektir. Ancak şöyle bir karşılaştırma doğru olacaktır:

if ("/a//b/c" PATH_EQUAL "/a/b/c") # True

Bu bizi verilen yol bilgileri üzerinde ekstra düzenleme ve kontrolleri yapmaktan kurtaracaktır. Şimdi de karşılaştırma ile ilgili son konumuz olan varlık kontrolüne (existence check) geçelim. Burada yine tek terim alan operatörler bulunmaktadır ve bu operatörler ile çeşitli şeylerin varlıklarını kontrol etmek mümkündür. Örneğin bir ismin CMake’te bir komut olup olmadığını kontrol etmek için COMMAND operatörünü kullanabiliriz:

if(COMMAND add_library)
    message("add_library is a CMake command!")
endif()

Yukarıdaki örnekte add_library bir CMake komutu olduğu için mesaj ekrana basılacaktır. COMMAND operatörünün aslında en yaygın kullanımı tanımlanan bir makro veya fonksiyonun (ilerideki yazılarda göstereceğim) varlığını kontrol etmek için kullanılmasıdır. Yani bu komut aslında yazılan ismin bir CMake komutu, bir makro veya bir fonksiyon olup olmadığını kontrol eder. İkinci varlık kontrol operatörümüz ise POLICY operatörüdür. Bu operatör CMP<NNNN> formatında verilen ismin bir politika (policy) olup olmadığını kontrol etmektedir. Politikalar ile ilgili ilerleyen yazılarda konuşacağım için şimdilik bunu geçiyorum.

Bir başka varlık kontrolü operatörümüz ise TARGET operatörüdür. Bu operatör verilen ismin add_library, add_executable veya add_custom_target ile oluşturulan bir hedef (target) olup olmadığını kontrol eder. Bu hedef projenin herhangi bir dizininde tanımlanmış olabilir. Bu operatör karmaşık projeler için oldukça kullanışlıdır. Örnek:

if(TARGET Engine)
    message("Engine target has been found!")
endif()

Bir başka varlık kontrolü operatörümüz olan TEST operatörü ise ismin add_test komutu ile eklenen bir test olup olmadığını kontrol eder. Bu komut CMake 3.3 versiyonu ile gelmiştir. Testlerden de ilerideki yazılarımda bahsedeceğim için şimdilik geçiyorum. Şimdi belki de en kullanışlı operatörlerden biri olan DEFINED operatörüne geçelim. Bu operatör verilen ismin önceden tanımlanmış bir değişken, Cache değişkeni veya ortam değişkeni olup olmadığını kontrol eder. Kullanımı şu şekildedir:

if(DEFINED var)
if(DEFINED CACHE{var})
if(DEFINED ENV{var})

İlk kontrolde var değişkeninin normal bir değişken olarak tanımlanıp tanımlanmadığı; ikinci kontrolde var değişkeninin bir Cache değişkeni olarak tanımlanıp tanımlanmadığı ve son kontrolde de var değişkeninin bir ortam değişkeni (environment variable) olarak tanımlanıp tanımlanmadığı kontrol edilir. Bu operatörde değişkenin değerinin ne olduğu önemli değilidir. Ayrıca makro argümanlarına verilen isimler tanımlı değişken olarak kabul edilmezler. Aslında ilk form ile de Cache değişkenlerini kontrol edebilirsiniz. İkinci formun farkı, ismin doğrudan bir Cache değişkeni olup olmadığını kontrol etmesidir.

Bir önceki yazıda listeleri görmüştük. Peki bir elemanın listede olup olmadığını if kullanarak basitçe nasıl test ederiz? CMake 3.3 ile gelen IN_LIST operatörü ile bunu kolayca yapabilmek mümkündür. Ancak bu operatörü düzgün bir biçimde kullanabilmek için CMP0057 isimli politikanın yeni davranışını kullanmanız gerekir (politikalardan daha sonra bahsedilecektir):

cmake_policy(SET CMP0057 NEW)
set(testList a b c d)

if("a" IN_LIST testList)
    message("a in list")
endif()

Bu operatör ile birlikte if koşul yapısını ve onunla birlikte kullanılan operatörleri tamamlamış olduk. Şimdi if ile ilgili birkaç örnek verip döngü yapılarına geçeceğim. if kontrol yapısının CMake’te belki de en fazla kullanıldığı yer platform ve derleyiciye özel işlemler yapmak veya derleme türüne (Debug, Release vb.) göre işlemler yapmaktır diyebiliriz. Aslında bunun için CMake’te önceden tanımlı değişkenler (predefined variables) yer almaktadır. Bu değişkenler if ile kullanıldıklarında, CMake’e belli bir sisteme özel iş yaptırabiliriz. Bu değişkenlerden bazılarını ve anlamlarını şöyle sıralayabiliriz:

Önceden Tanımlı DeğişkenAnlamı
ANDROIDHedef sistem Android ise değeri 1, değilse 0 olur. CMake 3.7 versiyonu ile eklenmiştir.
APPLEHedef sistem bir Apple platformu ise (macOS, iOS, tvOS veya watchOS) değeri “True” olur.
BORLANDEğer kullanılan derleyici Borland ise değeri “True” olur.
CYGWINEğer kullanılan derleyici Cygwin ise değeri “True” olur.
IOSHedef sistem iOS ise değeri 1, değilse 0 olur. CMake 3.14 versiyonu ile eklenmiştir.
MINGWEğer kullanılan derleyici MinGW ise değeri “True” olur.
MSVCEğer kullanılan derleyici herhangi bir Microsoft Visual C++ sürümü ise değeri “True” olur.
UNIXEğer hedef sistem UNIX veya UNIX benzeri (Apple, Cygwin vb.) bir sistem ise değeri “True” olur.
WIN32Eğer hedef sistem Windows ise (Win64 de dahil) değeri “True” olur.
WINDOWS_PHONEEğer hedef sistem Windows Phone ise değeri “True” olur. CMake 3.1 versiyonu ile eklenmiştir.
XCODEXcode Generator (oluşturucu) kullanıldığında değeri “True” olur. CMake 3.7 versiyonu ile eklenmiştir.

Örneğin bir kaynak kod dosyasının işletim sistemlerine göre farklı varyasyonlarını kullanmak için şöyle bir kod yazabiliriz:

if(WIN32)
    set(engineImpl engine_win.cpp)
elseif(IOS)
    set(engineImpl engine_ios.cpp)
elseif(ANDROID)
    set(engineImpl engine_android.cpp)
else()
    set(engineImpl engine_generic.cpp)
endif()

add_library("Engine" STATIC ${engineImpl})

Yine bir derleyiciye özel ayar yapmak istediğinizde, bu önceden tanımlı değişkenleri if yapısı ile kullanıp kolayca amacınıza ulaşabilirsiniz:

if (MSVC)
    # warning level 4 and all warnings as errors
    add_compile_options(/W4 /WX)
else()
    # lots of warnings and all warnings as errors
    add_compile_options(-Wall -Wextra -pedantic -Werror)
endif()

CMake’te bir Generator (Oluşturucu) seçtiğinizde CMAKE_GENERATOR değişkeninin değeri bu Generator’ın ismini alır (Unix Makefiles, Ninja, Xcode vb.). Bu değişkeni STREQUAL operatörü ile istediğiniz bir String ile karşılaştırarak, Generator’a özel kod yazmanız mümkündür:

if(CMAKE_GENERATOR STREQUAL "Ninja")
    # Ninja specific codes
elseif(CMAKE_GENERATOR STREQUAL "Xcode")
    # Xcode specific codes
else()
    # For other generators
endif()

Son olarak kullanıcıya sunulan seçeneğe göre bir kütüphane derleme işini if ile nasıl yapabileceğimize bakalım:

option(BUILD_MATH_LIB "Enable building the math target")
if(BUILD_MATH_LIB)
    add_library(math sin.cpp cos.cpp tan.cpp)
endif()

CMake’te listenin elemanları üzerinde dolaşım sağlayıp işlem yapmak veya aynı işlemleri birden fazla kez yapabilmek için döngülerden yararlanırız. CMake’te döngüleri kullanabilmek için iki yapı yer almaktadır: foreach ve while. Aslında ikisi de sırasıyla programlama dillerindeki for ve while döngülerine benzerler. Öncelikle foreach döngüsünden başlayalım. Bu döngünün genel kullanımı şu şekildedir:

foreach(<loop_var> <items>)
<commands>
endforeach()

Burada “<items>” kısmına üzerinde dolaşılacak elemanlar yazılmaktadır. “<loop_var>” ise dolaştığımız her bir elemanı geçici olarak üzerinde tutan değişkendir. Biz elemanları döngünün her bir turunda bu değişken ile döngü içinde ele alabiliriz. Örnek:

foreach(value a b c d)
    message("Current value: ${value}")
endforeach()
ÇIKTI
Current value: a
Current value: b
Current value: c
Current value: d

Tabi üzerinde dolaşılacak öğeleri tek tek bu şekilde yazmak pek kullanışlı değildir. Bu nedenle CMake’te liste tutan değişkenleri kullanabilmek için foreach döngüsünün daha genel bir formu kullanılmaktadır:

foreach(<loop_var> IN [LISTS [<lists>]] [ITEMS [<items>]])

Burada LISTS kısmına liste değişkenleri, ITEMS kısmına ise yine eskisi gibi öğeler gelmektedir. Bu ikisini aynı anda veya tek tek kullanmak tamamen sizin tercihinize bağlıdır. Örnek:

set(list1 a b)
set(list2 c)
set(list3 "d;e;f")

foreach(value IN LISTS list1 list2 list3 ITEMS item1 item2)
    message("Current value: ${value}")
endforeach()
ÇIKTI
Current value: a
Current value: b
Current value: c
Current value: d
Current value: e
Current value: f
Current value: item1
Current value: item2

Gördüğünüz gibi bu döngü yapısının kullanımı oldukça esnektir. foreach döngüsünün bir başka formu ise belli bir sayısal aralığı ele almasıyla ilgilidir:

foreach(<loop_var> RANGE <stop>)

foreach(<loop_var> RANGE <start> <stop> [<step>])

Yukarıdaki ilk formda döngü 0’dan başlayarak “<stop>” kısmına yazdığınız negatif olmayan tamsayıya kadar (bu tamsayı da dahil) “<loop_var>” değişkenine değer atayarak ilerleyecektir. İkinci formda ise “<start>” kısmına başlanacak tamsayıyı, “<step>” kısmına ise kaçar kaçar atlanarak ilerleneceği bilgisini yazabilirsiniz. Örnekler:

foreach(value RANGE 3)
    message("Current value for first foreach: ${value}")
endforeach()

foreach(value RANGE 4 10 2)
    message("Current value for second foreach: ${value}")
endforeach()
ÇIKTI
Current value for first foreach: 0
Current value for first foreach: 1
Current value for first foreach: 2
Current value for first foreach: 3
Current value for second foreach: 4
Current value for second foreach: 6
Current value for second foreach: 8
Current value for second foreach: 10

Burada value değişkeninin ilk döngüde ayrı, ikinci döngüde ayrı bir değişken olarak ele alındığına da dikkat edin. Şimdi de while döngüsünden çok kısa bahsedelim. Bu döngünün genel kullanım biçimi şöyledir:

while(<condition>)
<commands>
endwhile()

Burada “<condition>” kısmına if koşul yapısındaki gibi bir koşul ifadesi yazılır. Bu koşul doğru olduğu sürece döngü dönmeye devam edecektir. Çok kısa bir örnekle zaten basitçe anlaşılabilir:

while(NOT a STREQUAL "xxxxx")
    set(a "${a}x")
    message("Mustafa Yemural")
endwhile()
ÇIKTI
Mustafa Yemural
Mustafa Yemural
Mustafa Yemural
Mustafa Yemural
Mustafa Yemural

Bu döngü yapısında bazen koşulu manipüle etmek zor ve bazen de kodu anlaşılmaz kıldığı için, while kullanımından ziyade foreach kullanımı daha çok tercih edilmektedir. Programlama dillerinde döngü akışını kontrol edebileceğiniz break ve continue gibi yapılar CMake’te de yer almaktadır:

foreach(a RANGE 5 10)
    if(a EQUAL 6)
        continue()
    elseif(a EQUAL 9)
        break()
    endif()

    message("Value: ${a}")
endforeach()
ÇIKTI
Value: 5
Value: 7
Value: 8

Bu yazıda CMake’te yer alan koşul ve döngü yapılarını gördük. Koşul ifadelerinde kullanılan çeşitli kategorilerdeki operatörleri inceledik ve her biriyle ilgili örnekler yapmaya çalıştık. if kullanımı CMake’te oldukça önemli olduğundan bazı gerçek senaryolar üzerinde de örnek verdik. son olarak döngü yapılarıyla ilgili çeşitli formları açıklayıp en sonunda break ve continue komutlarından bahsettik. Bu yazı oldukça uzun oldu, ancak CMake’in ana konularından biri olduğu için bence buna değdi. Bir sonraki yazıda CMake’i büyük projelerde nasıl kullanabileceğimizle ilgili fikir sahibi olabileceğimiz, alt dizinlerle CMake kullanma konusunu anlatacağım.

0 0 votes
Article Rating
Subscribe
Bildir
guest

0 Yorum
Inline Feedbacks
View all comments