
Geliştirme sırasında derleyicileri değiştirmeyi, modları, bağımlılık sürümlerini değiştirmeyi, statik analiz gerçekleştirmeyi, performansı ölçmeyi, kapsamı toplamayı, belge oluşturmayı vb. seviyorum. Ve CMake'i gerçekten seviyorum çünkü istediğim her şeyi yapmamı sağlıyor.
Pek çok kişi CMake'i eleştiriyor ve çoğu zaman bunu hak ediyor, ancak ona bakarsanız her şeyin o kadar da kötü olmadığını görüyorsunuz ve son zamanlarda hiç de fena değilve gelişme yönü oldukça olumlu.
Bu notta, aşağıdaki işlevleri elde etmek için CMake sisteminde C++ 'da bir başlık kitaplığını nasıl düzenleyeceğinizi anlatmak istiyorum:
- Toplantı;
- Otomatik çalıştırma testleri;
- Kod kapsamı ölçümü;
- Kurulum;
- Otomatik belgeleme;
- Çevrimiçi sanal alan oluşturma;
- Statik analiz.
Avantajlarını ve C yapımını zaten anlayan herkes kolayca yapabilir ve kullanmaya başlayın.
Içerik
.
├── CMakeLists.txt
├── README.en.md
├── README.md
├── doc
│ ├── CMakeLists.txt
│ └── Doxyfile.in
├── include
│ └── mylib
│ └── myfeature.hpp
├── online
│ ├── CMakeLists.txt
│ ├── mylib-example.cpp
│ └── wandbox.py
└── test
├── CMakeLists.txt
├── mylib
│ └── myfeature.cpp
└── test_main.cppBiz esas olarak CMake scriptlerinin nasıl organize edileceğinden bahsedeceğiz dolayısıyla bunlar detaylı olarak ele alınacaktır. Herkes dosyaların geri kalanını doğrudan görüntüleyebilir .
Öncelikle CMake sisteminin gerekli sürümünü talep etmeniz gerekiyor. CMake gelişiyor, komut imzaları ve farklı koşullardaki davranışlar değişiyor. CMake'in ondan ne istediğimizi hemen anlayabilmesi için ona yönelik gereksinimlerimizi hemen kaydetmemiz gerekiyor.
cmake_minimum_required(VERSION 3.13)Daha sonra projemizi, adını, versiyonunu, kullanılan dilleri vb. belirleyeceğiz (bkz. ).
Bu durumda dili belirtiyoruz CXX (ve bu C++ anlamına gelir) böylece CMake bir C dili derleyicisini zorlamaz ve aramaz (CMake varsayılan olarak iki dil içerir: C ve C++).
project(Mylib VERSION 1.0 LANGUAGES CXX)Buradan projemizin alt proje olarak başka bir projeye dahil olup olmadığını hemen kontrol edebilirsiniz. Bu gelecekte çok yardımcı olacaktır.
get_directory_property(IS_SUBPROJECT PARENT_DIRECTORY)
İki seçenek sunacağız.
İlk seçenek — birim testlerini devre dışı bırakmak için. Testlerde her şeyin yolunda olduğundan eminsek ancak örneğin yalnızca projemizi kurmak veya paketlemek istiyorsak bu gerekli olabilir. Veya projemiz bir alt proje olarak dahil edilmiştir - bu durumda projemizin kullanıcısı testlerimizi yürütmekle ilgilenmez. Kullandığınız bağımlılıkları test etmiyorsunuz, değil mi?
option(MYLIB_TESTING "Включить модульное тестирование" ON)Ayrıca ayrı bir seçenek yapacağız kod kapsamını testlerle ölçmek için, ancak ek araçlar gerektireceğinden açıkça etkinleştirilmesi gerekecektir.
option(MYLIB_COVERAGE "Включить измерение покрытия кода тестами" OFF)
Elbette biz harika artı programcılarız, bu nedenle derleyiciden maksimum düzeyde derleme zamanı tanılaması istiyoruz. Tek bir fare bile kaymaz.
add_compile_options(
-Werror
-Wall
-Wextra
-Wpedantic
-Wcast-align
-Wcast-qual
-Wconversion
-Wctor-dtor-privacy
-Wenum-compare
-Wfloat-equal
-Wnon-virtual-dtor
-Wold-style-cast
-Woverloaded-virtual
-Wredundant-decls
-Wsign-conversion
-Wsign-promo
)C++ dil standardına tam uyum sağlamak için uzantıları da devre dışı bırakacağız. CMake'te varsayılan olarak etkindirler.
if(NOT CMAKE_CXX_EXTENSIONS)
set(CMAKE_CXX_EXTENSIONS OFF)
endif()
Kütüphanemiz sadece başlık dosyalarından oluşuyor, yani statik veya dinamik kütüphaneler şeklinde herhangi bir egzozumuz yok. Öte yandan kütüphanemizi harici olarak kullanabilmek için yüklü olması, sistemde algılanabilir olması ve projenize bağlanması ve aynı zamanda aynı başlıkların yanı sıra muhtemelen bazı ek başlıkların olması gerekir. onun özelliklerine bağlıdır.
Bu amaçla bir arayüz kütüphanesi oluşturuyoruz.
add_library(mylib INTERFACE)Başlıkları arayüz kütüphanemize bağlarız.
CMake'in modern, modaya uygun, gençlik kullanımı, başlıkların, özelliklerin vb. tek bir hedef üzerinden iletilir. yani şunu söylemem yeterli ve hedefle ilişkili tüm başlıklar dependency, hedefe ait kaynaklar için mevcut olacak target. Ve hiçbirine ihtiyacın yok [target_]include_directories. Bu, aşağıda analizde gösterilecektir. .
Ayrıca sözde dikkat etmeye değer. .
Bu komut, ihtiyacımız olan başlıkları arayüz kitaplığımızla ilişkilendirir ve eğer kitaplığımız aynı CMake hiyerarşisi içindeki herhangi bir hedefe bağlıysa dizindeki başlıklar onunla ilişkilendirilir. ${CMAKE_CURRENT_SOURCE_DIR}/include, ve eğer kütüphanemiz sistemde kuruluysa ve komutu kullanarak başka bir projeye bağlıysa , daha sonra dizindeki başlıklar onunla ilişkilendirilecek include kurulum dizinine göre.
target_include_directories(mylib INTERFACE
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:include>
)Dil standardını belirleyelim. Tabii ki en sonuncusu. Aynı zamanda sadece standardı dahil etmiyoruz, kütüphanemizi kullanacak kişilere de yaygınlaştırıyoruz. Bu, set özelliğinin bir kategoriye sahip olması nedeniyle elde edilir. INTERFACE (Bkz. ).
target_compile_features(mylib INTERFACE cxx_std_17)Kütüphanemiz için bir takma ad oluşturalım. Üstelik güzellik için özel bir “isim alanı” içinde olacak. Kütüphanemizde farklı modüller göründüğünde ve bunları birbirinden bağımsız olarak bağlamaya gittiğimizde bu faydalı olacaktır. .
add_library(Mylib::mylib ALIAS mylib)
Başlıklarımızı sisteme yüklüyoruz. Burada her şey basit. Tüm başlıkların bulunduğu klasörün dizine gitmesi gerektiğini söylüyoruz include Kurulum konumuna göre.
install(DIRECTORY include/mylib DESTINATION include)Daha sonra derleme sistemine üçüncü taraf projelerde komutu çağırabilmek istediğimizi bildiriyoruz. find_package(Mylib) ve bir gol bul Mylib::mylib.
install(TARGETS mylib EXPORT MylibConfig)
install(EXPORT MylibConfig NAMESPACE Mylib:: DESTINATION share/Mylib/cmake)Bir sonraki büyü bu şekilde anlaşılmalıdır. Üçüncü taraf bir projedeyken komutu çağırıyoruz find_package(Mylib 1.2.3 REQUIRED)ve yüklü kitaplığın gerçek sürümü, sürümle uyumsuz olacaktır. 1.2.3CMake otomatik olarak bir hata üretecektir. Yani sürümleri manuel olarak takip etmenize gerek kalmayacak.
include(CMakePackageConfigHelpers)
write_basic_package_version_file("${PROJECT_BINARY_DIR}/MylibConfigVersion.cmake"
VERSION
${PROJECT_VERSION}
COMPATIBILITY
AnyNewerVersion
)
install(FILES "${PROJECT_BINARY_DIR}/MylibConfigVersion.cmake" DESTINATION share/Mylib/cmake)
Testler açıkça kullanılarak devre dışı bırakılırsa veya projemiz alt projedir yani başka bir CMake projesine şu komutla bağlanır , hiyerarşide daha fazla ilerlemeyiz ve testleri oluşturmaya ve çalıştırmaya yönelik komutları açıklayan komut dosyası çalışmaz.
if(NOT MYLIB_TESTING)
message(STATUS "Тестирование проекта Mylib выключено")
elseif(IS_SUBPROJECT)
message(STATUS "Mylib не тестируется в режиме подмодуля")
else()
add_subdirectory(test)
endif()
Bir alt proje durumunda da dokümantasyon oluşturulmayacaktır.
if(NOT IS_SUBPROJECT)
add_subdirectory(doc)
endif()
Benzer şekilde alt projenin de çevrimiçi bir sanal alanı olmayacaktır.
if(NOT IS_SUBPROJECT)
add_subdirectory(online)
endif()
Öncelikle gerekli test çerçevesine sahip bir paket buluyoruz (en sevdiğinizle değiştirin).
find_package(doctest 2.3.3 REQUIRED)Çalıştırılabilir dosyamızı testlerle oluşturalım. Genellikle doğrudan çalıştırılabilir ikili dosyaya yalnızca işlevi içerecek dosyayı eklerim main.
add_executable(mylib-unit-tests test_main.cpp)Ve daha sonra testlerin açıklandığı dosyaları ekliyorum. Ama bunu yapmak zorunda değilsin.
target_sources(mylib-unit-tests PRIVATE mylib/myfeature.cpp)Bağımlılıkları birbirine bağlıyoruz. Lütfen yalnızca ihtiyacımız olan CMake hedeflerini ikili dosyamıza bağladığımızı ve komutu çağırmadığımızı unutmayın. target_include_directories. Test çerçevesinden ve bizimkilerden başlıklar Mylib::mylib, ve derleme parametreleri (bizim durumumuzda bu, C++ dil standardıdır) bu hedeflerle birlikte ortaya çıktı.
target_link_libraries(mylib-unit-tests
PRIVATE
Mylib::mylib
doctest::doctest
)Son olarak, "derlemesi" test çalıştırmaya eşdeğer olan yapay bir hedef oluştururuz ve bu hedefi varsayılan yapıya ekleriz (bundan nitelik sorumludur) ALL). Bu, varsayılan yapının testleri çalıştırmayı tetiklediği anlamına gelir; bu da onları çalıştırmayı asla unutmayacağımız anlamına gelir.
add_custom_target(check ALL COMMAND mylib-unit-tests)
Daha sonra uygun seçeneğin belirtilmesi durumunda kod kapsamı ölçümünü etkinleştiriyoruz. Ayrıntılara girmeyeceğim çünkü bunlar CMake'ten çok kapsamı ölçmek için kullanılan bir araçla ilgilidir. Sadece sonuçlara göre bir hedefin oluşturulacağını unutmamak önemlidir. Kapsamı ölçmeye başlamanın uygun olduğu.
find_program(GCOVR_EXECUTABLE gcovr)
if(MYLIB_COVERAGE AND GCOVR_EXECUTABLE)
message(STATUS "Измерение покрытия кода тестами включено")
target_compile_options(mylib-unit-tests PRIVATE --coverage)
target_link_libraries(mylib-unit-tests PRIVATE gcov)
add_custom_target(coverage
COMMAND
${GCOVR_EXECUTABLE}
--root=${PROJECT_SOURCE_DIR}/include/
--object-directory=${CMAKE_CURRENT_BINARY_DIR}
DEPENDS
check
)
elseif(MYLIB_COVERAGE AND NOT GCOVR_EXECUTABLE)
set(MYLIB_COVERAGE OFF)
message(WARNING "Для замеров покрытия кода тестами требуется программа gcovr")
endif()
.
find_package(Doxygen)Daha sonra kullanıcının dil değişkenini ayarlayıp ayarlamadığını kontrol ederiz. Eğer evet ise, o zaman ona dokunmayız, değilse, o zaman Rusça alırız. Daha sonra Doxygen sistem dosyalarını yapılandırıyoruz. Dil de dahil olmak üzere gerekli tüm değişkenler yapılandırma işlemi sırasında oraya gider (bkz. ).
Daha sonra bir hedef oluşturuyoruz , dokümantasyon oluşturmaya başlayacak. Belge oluşturmak geliştirme sürecindeki en büyük ihtiyaç olmadığından hedef varsayılan olarak etkinleştirilmeyecek; açıkça başlatılması gerekecek.
if (Doxygen_FOUND)
if (NOT MYLIB_DOXYGEN_LANGUAGE)
set(MYLIB_DOXYGEN_LANGUAGE Russian)
endif()
message(STATUS "Doxygen documentation will be generated in ${MYLIB_DOXYGEN_LANGUAGE}")
configure_file(Doxyfile.in Doxyfile)
add_custom_target(doc COMMAND ${DOXYGEN_EXECUTABLE} ${CMAKE_CURRENT_BINARY_DIR}/Doxyfile)
endif ()
Burada üçüncü Python'u buluyoruz ve bir hedef oluşturuyoruz hizmet API'sine karşılık gelen bir istek üreten ve onu uzaklaştırır. Yanıt, tamamlanmış sanal alana bir bağlantıyla birlikte gelir.
find_program(PYTHON3_EXECUTABLE python3)
if(PYTHON3_EXECUTABLE)
set(WANDBOX_URL "https://wandbox.org/api/compile.json")
add_custom_target(wandbox
COMMAND
${PYTHON3_EXECUTABLE} wandbox.py mylib-example.cpp "${PROJECT_SOURCE_DIR}" include |
curl -H "Content-type: application/json" -d @- ${WANDBOX_URL}
WORKING_DIRECTORY
${CMAKE_CURRENT_SOURCE_DIR}
DEPENDS
mylib-unit-tests
)
else()
message(WARNING "Для создания онлайн-песочницы требуется интерпретатор ЯП python 3-й версии")
endif()
Şimdi tüm bunların nasıl kullanılacağına bakalım.
Bu projenin oluşturulması, CMake derleme sistemindeki diğer projeler gibi iki aşamadan oluşur:
cmake -S путь/к/исходникам -B путь/к/сборочной/директории [опции ...]CMake'in eski bir sürümü nedeniyle yukarıdaki komut işe yaramadıysa, atlamayı deneyin
-S:cmake путь/к/исходникам -B путь/к/сборочной/директории [опции ...]
.
cmake --build путь/к/сборочной/директории [--target target].
cmake -S ... -B ... -DMYLIB_COVERAGE=ON [прочие опции ...]Hedef içerir testlerle kod kapsamını ölçmeye başlayabilirsiniz.
cmake -S ... -B ... -DMYLIB_TESTING=OFF [прочие опции ...]Birim testi derlemesini ve hedefini devre dışı bırakma yeteneği sağlar . Sonuç olarak, testlerle kod kapsamının ölçümü kapatılır (bkz. ).
Proje, komut kullanılarak başka bir projeye alt proje olarak bağlanırsa test de otomatik olarak devre dışı bırakılır. .
cmake -S ... -B ... -DMYLIB_DOXYGEN_LANGUAGE=English [прочие опции ...]Hedefin oluşturduğu belgelerin dilini değiştirir verilen kişiye. Kullanılabilir dillerin listesi için bkz. .
Rusça varsayılan olarak etkindir.
cmake --build path/to/build/directory
cmake --build path/to/build/directory --target allHedef belirtilmemişse (ki bu hedefe eşdeğerdir) all), toplayabildiği her şeyi toplar ve aynı zamanda hedefi çağırır .
cmake --build path/to/build/directory --target mylib-unit-testsBirim testlerini derler. Varsayılan olarak etkindir.
cmake --build путь/к/сборочной/директории --target checkToplanan (henüz değilse toplanan) birim testlerini çalıştırır. Varsayılan olarak etkindir.
См. также .
cmake --build путь/к/сборочной/директории --target coverageProgramı kullanarak yapılan testlerle kod kapsamı için çalışan birim testlerini analiz eder (henüz değilse çalıştırır) .
Kaplama egzozu şuna benzer:
------------------------------------------------------------------------------
GCC Code Coverage Report
Directory: /path/to/cmakecpptemplate/include/
------------------------------------------------------------------------------
File Lines Exec Cover Missing
------------------------------------------------------------------------------
mylib/myfeature.hpp 2 2 100%
------------------------------------------------------------------------------
TOTAL 2 2 100%
------------------------------------------------------------------------------Hedef yalnızca seçenek etkinleştirildiğinde kullanılabilir .
См. также .
cmake --build путь/к/сборочной/директории --target docSistemi kullanarak kod belgelerinin oluşturulmasını başlatır .
cmake --build путь/к/сборочной/директории --target wandboxHizmetten gelen yanıt şuna benzer:
{
"permlink" : "QElvxuMzHgL9fqci",
"status" : "0",
"url" : "https://wandbox.org/permlink/QElvxuMzHgL9fqci"
}Hizmet bunun için kullanılıyor . Sunucularının ne kadar esnek olduğunu bilmiyorum ama bu fırsatın suiistimal edilmemesi gerektiğini düşünüyorum.
Kapsam ölçümüyle projeyi hata ayıklama modunda oluşturun
cmake -S путь/к/исходникам -B путь/к/сборочной/директории -DCMAKE_BUILD_TYPE=Debug -DMYLIB_COVERAGE=ON
cmake --build путь/к/сборочной/директории --target coverage --parallel 16Ön montaj ve test yapılmadan proje kurulumu
cmake -S путь/к/исходникам -B путь/к/сборочной/директории -DMYLIB_TESTING=OFF -DCMAKE_INSTALL_PREFIX=путь/к/установойной/директории
cmake --build путь/к/сборочной/директории --target installBelirli bir derleyiciyle yayın modunda derleme
cmake -S путь/к/исходникам -B путь/к/сборочной/директории -DCMAKE_BUILD_TYPE=Release -DCMAKE_CXX_COMPILER=g++-8 -DCMAKE_PREFIX_PATH=путь/к/директории/куда/установлены/зависимости
cmake --build путь/к/сборочной/директории --parallel 4İngilizce dokümantasyon oluşturma
cmake -S путь/к/исходникам -B путь/к/сборочной/директории -DCMAKE_BUILD_TYPE=Release -DMYLIB_DOXYGEN_LANGUAGE=English
cmake --build путь/к/сборочной/директории --target doc
3.13
Aslında CMake sürüm 3.13 yalnızca bu yardımda açıklanan konsol komutlarından bazılarını çalıştırmak için gereklidir. CMake komut dosyalarının sözdizimi açısından bakıldığında, nesil başka yollarla çağrılırsa sürüm 3.8 yeterlidir.
Test kütüphanesi
Test devre dışı bırakılabilir (bkz. ).
Dokümantasyonun oluşturulacağı dili değiştirmek için bir seçenek sağlanmıştır .
Dil tercümanı
Otomatik nesil için .
CMake ve birkaç iyi araçla minimum çabayla statik analiz sağlayabilirsiniz.
Cpp kontrolü
CMake, statik analiz aracı için yerleşik desteğe sahiptir .
Bunu yapmak için seçeneği kullanmanız gerekir :
cmake -S путь/к/исходникам -B путь/к/сборочной/директории -DCMAKE_BUILD_TYPE=Debug -DCMAKE_CXX_CPPCHECK="cppcheck;--enable=all;-Iпуть/к/исходникам/include"Bundan sonra, kaynak her derlendiğinde ve yeniden derlendiğinde statik analiz otomatik olarak başlatılacaktır. İlave bir şey yapmaya gerek yoktur.
çınlama
Harika bir aracın yardımıyla Ayrıca statik analizi hemen çalıştırabilirsiniz:
scan-build cmake -S путь/к/исходникам -B путь/к/сборочной/директории -DCMAKE_BUILD_TYPE=Debug
scan-build cmake --build путь/к/сборочной/директорииBurada, Cppcheck'teki durumun aksine, derlemeyi her seferinde çalıştırmanız gerekir. scan-build.
CMake, her zevke ve renge uygun işlevsellik uygulamanıza olanak tanıyan çok güçlü ve esnek bir sistemdir. Ve sözdizimi bazen arzulanan çok şey bıraksa da, şeytan hâlâ resmedildiği kadar korkunç değildir. CMake derleme sistemini toplumun ve sağlığın yararına kullanın.
→
Kaynak: habr.com
