Evet arkadaşlar CMake ile ilgili ikinci yazımıza hoş geldiniz. İlk yazı için aşağıdaki linki takip edebilirsiniz:
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:
İçerik
Değişken kullanımı:
* Değişkenleri aşağıdaki gibi tanımlayabiliyoruz (bunu ilk yazımdan hatırlıyorsunuzdur)
1 |
set (MY_VAR "hello") |
sonrasında ise
1 |
set (OTHER_VAR "${MY_VAR} world!") |
ş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:
1 |
cmake -DMY_VAR=Hello |
“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:
1 |
include_directories( BEFORE ${MY_SOURCE_DIR}/include ) |
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):
1 2 3 4 5 6 7 |
│ CMakeLists.txt ├───build ├───include │ Example.h └───src Example.cpp main.cpp |
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.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
cmake_minimum_required(VERSION 2.8.9) project(directory_test) # Gerekli başlık dosyalarını ekleyelim (Example.h) include_directories(include) # Asagidaki gibi el ile ilgili dosyaları ekleyebiliriz: #set(SOURCES src/main.cpp src/Example.cpp) # Ya da daha kolay bir şekilde file(GLOB...)komutu ile de bu işi halledebiliriz: file(GLOB SOURCES "src/*.cpp") add_executable(seperateFolderExample ${SOURCES}) |
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.
1 2 3 4 |
cmake_minimum_required(VERSION 3.9.1) project(CMakeHello) set(CMAKE_CXX_STANDARD 14) add_executable(cmake_hello main.cpp) |
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.
1 |
set(CMAKE_CXX_FLAGS "-Wall") |
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:
1 |
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall") |
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.
1 |
set(CMAKE_CXX_EXTENSIONS OFF) |
Ç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:
1 |
target_compile_features(<target> <PRIVATE|PUBLIC|INTERFACE> <feature> [...]) |
“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.
1 |
message("Supported features = ${CMAKE_CXX_COMPILE_FEATURES}") |
Aşağıda örnek bazı kullanımları görebilirsiniz:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
// Ornek 1 target_compile_features(myTarget PUBLIC cxx_variadic_templates cxx_nullptr PRIVATE cxx_lambdas ) // Catch 2 kütüphanesinden bir örnek dil özellikleri ihtiyaç listesi target_compile_features(Catch2 INTERFACE cxx_alignas cxx_alignof cxx_attributes cxx_auto_type cxx_constexpr cxx_defaulted_functions cxx_deleted_functions cxx_final cxx_lambdas cxx_noexcept cxx_override cxx_range_for cxx_rvalue_references cxx_static_assert cxx_strong_enums cxx_trailing_return_types cxx_unicode_literals cxx_user_literals cxx_variadic_macros ) |
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:
1 2 3 4 5 6 7 8 |
├───build ├───lib │ └───math │ mathLib.cpp │ mathLib.h │ └───src main.cpp |
İ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:
1 2 3 4 5 6 7 8 9 |
cmake_minimum_required(VERSION 3.9.1) project(mainSimpleLib) set(CMAKE_CXX_STANDARD 14) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall") set(EXECUTABLE_OUTPUT_PATH ${CMAKE_BINARY_DIR}/bin) set(SOURCES main.cpp lib/math/mathLib.cpp lib/math/mathLib.hpp) add_executable(mainSimpleLib ${SOURCES}) |
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.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
cmake_minimum_required(VERSION 3.9.1) project(seperateLibFirstApproach) # C++ standardını atayalım set(CMAKE_CXX_STANDARD 14) # Bazı derleyici ayarlarını girelim set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -W0") # ilgili include pathi ekleyelim include_directories(lib) # Çıktı dizinlerini ayarlayalım set(EXECUTABLE_OUTPUT_PATH ${CMAKE_BINARY_DIR}/bin) set(LIBRARY_OUTPUT_PATH ${CMAKE_BINARY_DIR}/lib) # Bilgilendirme message(${CMAKE_BINARY_DIR}) # Kütüphane komutları #add_library(math SHARED lib/math/mathLib.h lib/math/mathLib.cpp) add_library(math STATIC lib/math/mathLib.h lib/math/mathLib.cpp) # Çalıştırılabilir uygulama kodu add_executable(seperateLibFirstApproach src/main.cpp) # Kütüphaneleri bağlayalım target_link_libraries(seperateLibFirstApproach math) |
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:
1 2 3 4 5 6 7 |
cmake_minimum_required(VERSION 3.9.1) # Çıktı dizinlerini ayarlayalım set(LIBRARY_OUTPUT_PATH ${CMAKE_BINARY_DIR}/lib) # Kütüphane komutları add_library(math STATIC mathLib.h mathLib.cpp) |
Tepe dizindeki CMakeLists.txt dosyasını da aşağıdaki gibi güncelleyelim:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
cmake_minimum_required(VERSION 3.9.1) project(seperateLibSecondApproach) # C++ standardını atayalım set(CMAKE_CXX_STANDARD 14) # Bazı derleyici ayarlarını girelim set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -W0") # ilgili pathi ekleyelim include_directories(lib) # Çıktı dizinlerini ayarlayalım set(EXECUTABLE_OUTPUT_PATH ${CMAKE_BINARY_DIR}/bin) # Bilgilendirme message(${CMAKE_BINARY_DIR}) # Kütüphaneye ilişkin oluşturma dosyasına olan bağlantı add_subdirectory(lib/math) # Çalıştırılabilir uygulama kodu add_executable(seperateLibSecondApproach src/main.cpp) # Kütüphaneleri bağlayalım target_link_libraries(seperateLibSecondApproach math) |
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.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
#ifndef _SHARED_EXPORTS_H__ #define _SHARED_EXPORTS_H__ #ifdef _WIN32 #ifdef SHARED_EXPORTS #define SHARED_EXPORT __declspec(dllexport) #else #define SHARED_EXPORT __declspec(dllimport) #endif #else #define SHARED_EXPORT #endif #endif /* _SHARED_EXPORTS_H__ */ |
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.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
cmake_minimum_required(VERSION 3.9.1) # Çıktı dizinlerini ayarlayalım set(LIBRARY_OUTPUT_PATH ${CMAKE_BINARY_DIR}/lib) # Sembol tanımlamaları için include (GenerateExportHeader) include_directories(${CMAKE_BINARY_DIR}/lib/math) # Kütüphane komutları add_library(math SHARED mathLib.h mathLib.cpp) # generates the export header shared_EXPORTS.h automatically GENERATE_EXPORT_HEADER(math BASE_NAME math EXPORT_MACRO_NAME SHARED_EXPORTS EXPORT_FILE_NAME SHARED_EXPORTS.h STATIC_DEFINE SHARED_EXPORTS_BUILT_AS_STATIC) |
Tepe dizindeki CMakeLists.txt ye de sadece ilgili tanımlama dosyasına erişmek için bir “include()” komutu ekleyeceğiz
1 2 |
# Bu dizini olusturulacak olan dll tanimlamalari icin (sadece DLL olmasi durumu icin gecerli) include_directories(${CMAKE_BINARY_DIR}/lib/math) |
Son olarak matematik sınıfımızı aşağıdaki gibi güncelleyelim.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
#ifndef MATHLIB_HPP #define MATHLIB_HPP #include "Shared_Exports.h" class SHARED_EXPORTS MathLib { public: int Sum(const int &a, const int &b); int Mult(const int &a, const int &b); int Div(const int &a, const int &b); int Sub(const int &a, const int &b); }; #endif //MATHLIB_HPP |
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
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:
1 |
option(<option_variable> "help string describing option" [initial value]) |
ş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:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
cmake_minimum_required(VERSION 3.9.1) project(seperateLibSecondApproach) # C++ standardını atayalım set(CMAKE_CXX_STANDARD 14) # Burada DLL ya da lib olusturmak icin bir opsiyon ekliyoruz option(OPTIONS_SHARED_LIBRARY_ENABLED "Shared library generation" ON) # Bazı derleyici ayarlarını girelim set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -W0") # ilgili pathi ekleyelim include_directories(lib) if (OPTIONS_SHARED_LIBRARY_ENABLED) # Bu dizini olusturulacak olan dll tanimlamalari icin (sadece DLL olmasi durumu icin gecerli) include_directories(${CMAKE_BINARY_DIR}/lib/math) endif(OPTIONS_SHARED_LIBRARY_ENABLED) # Çıktı dizinlerini ayarlayalım set(EXECUTABLE_OUTPUT_PATH ${CMAKE_BINARY_DIR}/bin) # Bilgilendirme message(${CMAKE_BINARY_DIR}) # Kütüphaneye ilişkin oluşturma dosyasına olan bağlantı add_subdirectory(lib/math) # Çalıştırılabilir uygulama kodu add_executable(seperateLibSecondApproach src/main.cpp) # Kütüphaneleri bağlayalım target_link_libraries(seperateLibSecondApproach math) |
Kütüphane dosyamız da:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
cmake_minimum_required(VERSION 3.9.1) # Çıktı dizinlerini ayarlayalım set(LIBRARY_OUTPUT_PATH ${CMAKE_BINARY_DIR}/lib) if (OPTIONS_SHARED_LIBRARY_ENABLED) include (GenerateExportHeader) include_directories(${CMAKE_BINARY_DIR}/lib/math) endif (OPTIONS_SHARED_LIBRARY_ENABLED) # Kütüphane komutları if (OPTIONS_SHARED_LIBRARY_ENABLED) add_library(math SHARED mathLib.h mathLib.cpp) else() add_library(math STATIC mathLib.h mathLib.cpp) endif (OPTIONS_SHARED_LIBRARY_ENABLED) if (OPTIONS_SHARED_LIBRARY_ENABLED) GENERATE_EXPORT_HEADER(math # generates the export header shared_EXPORTS.h automatically BASE_NAME math EXPORT_MACRO_NAME SHARED_EXPORTS EXPORT_FILE_NAME SHARED_EXPORTS.h STATIC_DEFINE SHARED_EXPORTS_BUILT_AS_STATIC) endif (OPTIONS_SHARED_LIBRARY_ENABLED) |
ş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
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/