
Selama proses pengembangan, saya suka mengganti kompiler, membangun mode, versi dependensi, melakukan analisis statis, mengukur kinerja, mengumpulkan cakupan, menghasilkan dokumentasi, dll. Dan saya sangat menyukai CMake, karena memungkinkan saya untuk melakukan semua yang saya inginkan.
Banyak yang mengkritik CMake, dan seringkali sepatutnya, tetapi jika Anda perhatikan, tidak semuanya buruk, dan baru-baru ini sangat bagus , dan arah pengembangannya cukup positif.
Pada artikel ini saya ingin mengatakan betapa sederhananya untuk mengatur pustaka header C ++ di sistem CMake untuk mendapatkan fungsionalitas berikut:
- Majelis;
- Tes autostart;
- Pengukuran cakupan kode;
- Instalasi;
- Mendokumentasikan otomatis
- Pembuatan kotak pasir online;
- Analisis Statis
Siapa pun yang sudah mengerti pro dan s-make hanya dapat mengunduh template proyek dan mulai menggunakannya.
Isi
- Proyeksikan dari dalam ke luar
- Struktur proyek
- File CMake Utama (./CMakeLists.txt)
- Informasi Proyek
- Opsi Proyek
- Opsi kompilasi
- Tujuan utama
- Instalasi
- Tes
- Dokumentasi
- Kotak pasir online
- Script untuk tes (test / CMakeLists.txt)
- Pengujian
- Cakupan
- Script untuk dokumentasi (doc / CMakeLists.txt)
- Skrip untuk kotak pasir online (online / CMakeLists.txt)
- Proyek di luar
- Majelis
- Generasi
- Majelis
- Opsi
- MYLIB_COVERAGE
- MYLIB_TESTING
- MYLIB_DOXYGEN_LOC
- Tujuan perakitan
- Secara default
- mylib-unit-tes
- periksa
- cakupan
- doc
- kotak tongkat
- Contohnya
- Alat-alatnya
- Analisis statis
- Kata penutup
. โโโ 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.cpp
Kami terutama akan berbicara tentang bagaimana mengatur skrip CMake, sehingga mereka akan dianalisis secara rinci. Setiap orang dapat melihat sisa file secara langsung di halaman templat proyek .
Pertama-tama, Anda perlu meminta versi sistem CMake yang tepat. CMake berkembang, tanda tangan tim, perilaku dalam kondisi yang berbeda berubah. Agar CMake segera memahami apa yang kita inginkan darinya, kita harus segera memperbaiki persyaratan kami untuknya.
cmake_minimum_required(VERSION 3.13)
Kemudian kami menunjuk proyek kami, namanya, versi, bahasa yang digunakan, dll. (Lihat project
).
Dalam hal ini, kami menentukan bahasa CXX
(yang berarti C ++) sehingga CMake tidak berusaha dan tidak mencari kompiler bahasa C (secara default, dua bahasa termasuk dalam CMake: C dan C ++).
project(Mylib VERSION 1.0 LANGUAGES CXX)
Di sini Anda dapat segera memeriksa apakah proyek kami termasuk dalam proyek lain sebagai sub proyek. Ini akan sangat membantu di masa depan.
get_directory_property(IS_SUBPROJECT PARENT_DIRECTORY)
Kami menyediakan dua opsi.
Opsi pertama - MYLIB_TESTING
- untuk mematikan pengujian unit. Ini mungkin diperlukan jika kami yakin bahwa semuanya sesuai dengan tes, dan kami ingin, misalnya, hanya menginstal atau mengemas proyek kami. Atau proyek kami dimasukkan sebagai sub proyek - dalam hal ini, pengguna proyek kami tidak tertarik untuk menjalankan pengujian kami. Anda tidak menguji dependensi yang Anda gunakan?
option(MYLIB_TESTING " " ON)
Selain itu, kami akan membuat opsi terpisah MYLIB_COVERAGE
untuk mengukur cakupan kode dengan pengujian, tetapi akan membutuhkan alat tambahan, jadi Anda harus mengaktifkannya secara eksplisit.
option(MYLIB_COVERAGE " " OFF)
Tentu saja, kami adalah programmer plus keren, jadi kami ingin tingkat maksimum diagnostik waktu kompilasi dari kompiler. Tidak ada satu mouse pun yang akan lolos.
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 )
Kami juga akan menonaktifkan ekstensi untuk sepenuhnya mematuhi standar bahasa C ++. Secara default, mereka termasuk dalam CMake.
if(NOT CMAKE_CXX_EXTENSIONS) set(CMAKE_CXX_EXTENSIONS OFF) endif()
Perpustakaan kami hanya terdiri dari file header, yang berarti bahwa kami tidak memiliki knalpot dalam bentuk perpustakaan statis atau dinamis. Di sisi lain, untuk menggunakan perpustakaan kami di luar, Anda harus menginstalnya, Anda harus dapat menemukannya di sistem dan menghubungkannya ke proyek Anda, dan pada saat yang sama header yang sama ini, serta, mungkin, beberapa yang tambahan properti.
Untuk tujuan ini, kami membuat perpustakaan antarmuka.
add_library(mylib INTERFACE)
Ikat header ke perpustakaan front-end kami.
Penggunaan CMake yang modern, modis, dan muda menyiratkan bahwa header, properti, dll. ditransmisikan melalui satu tujuan. Dengan demikian, cukup untuk mengatakan target_link_libraries(target PRIVATE dependency)
, dan semua header yang terkait dengan target dependency
akan tersedia untuk sumber-sumber yang termasuk dalam target target
. Dan tidak ada [target_]include_directories
diperlukan. Ini akan didemonstrasikan di bawah ini saat mem -parsing skrip CMake untuk pengujian unit .
Perlu juga memperhatikan apa yang disebut -: $<...>
.
Perintah ini mengaitkan header yang kita butuhkan dengan pustaka front-end kita, dan jika pustaka kita terhubung ke target dalam hirarki CMake yang sama, maka header dari direktori ${CMAKE_CURRENT_SOURCE_DIR}/include
akan dikaitkan dengan itu, dan jika kita punya Karena pustaka diinstal pada sistem dan terhubung ke proyek lain menggunakan find_package
, header dari direktori include
relatif ke direktori instalasi akan dikaitkan dengannya.
target_include_directories(mylib INTERFACE $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include> $<INSTALL_INTERFACE:include> )
Tetapkan standar bahasa. Tentu saja, yang terakhir. Pada saat yang sama, kami tidak hanya menyertakan standar, tetapi juga mendistribusikannya kepada mereka yang akan menggunakan perpustakaan kami. Ini dicapai karena fakta bahwa properti yang diset memiliki kategori INTERFACE
(lihat perintah target_compile_features ).
target_compile_features(mylib INTERFACE cxx_std_17)
Kami membuat alias untuk perpustakaan kami. Apalagi untuk kecantikan, ia akan berada di "namespace" khusus. Ini akan berguna ketika modul yang berbeda muncul di perpustakaan kami, dan kami akan menghubungkannya secara independen satu sama lain. Seperti di Boost, misalnya .
add_library(Mylib::mylib ALIAS mylib)
Menginstal tajuk kami dalam sistem. Semuanya sederhana di sini. Kami mengatakan bahwa folder dengan semua header harus di direktori include
relatif terhadap lokasi instalasi.
install(DIRECTORY include/mylib DESTINATION include)
Selanjutnya, kami memberi tahu sistem build bahwa kami ingin dapat memanggil find_package(Mylib)
dalam proyek pihak ketiga dan mendapatkan target Mylib::mylib
.
install(TARGETS mylib EXPORT MylibConfig) install(EXPORT MylibConfig NAMESPACE Mylib:: DESTINATION share/Mylib/cmake)
Mantra berikutnya harus dipahami sebagai berikut. Ketika kita memanggil find_package(Mylib 1.2.3 REQUIRED)
dalam proyek pihak ketiga, dan dalam hal ini versi nyata dari pustaka yang diinstal akan tidak kompatibel dengan versi 1.2.3
, CMake akan secara otomatis menghasilkan kesalahan. Artinya, Anda tidak perlu mengikuti versi secara manual.
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)
Jika tes dimatikan secara eksplisit menggunakan opsi yang sesuai atau jika proyek kami adalah sub proyek, yaitu, terhubung ke proyek CMake lain menggunakan add_subdirectory
, kami tidak akan melangkah lebih jauh dalam hierarki, dan skrip yang menggambarkan perintah untuk membuat dan menjalankan tes tidak dimulai .
if(NOT MYLIB_TESTING) message(STATUS " Mylib ") elseif(IS_SUBPROJECT) message(STATUS "Mylib ") else() add_subdirectory(test) endif()
Dokumentasi juga tidak akan dihasilkan dalam kasus subproyek.
if(NOT IS_SUBPROJECT) add_subdirectory(doc) endif()
Demikian pula, sub proyek juga tidak memiliki kotak pasir online.
if(NOT IS_SUBPROJECT) add_subdirectory(online) endif()
Pertama-tama, kami menemukan paket dengan kerangka uji yang diinginkan (ganti dengan favorit Anda).
find_package(doctest 2.3.3 REQUIRED)
Kami membuat file yang dapat dieksekusi dengan tes. Biasanya, saya hanya menambahkan file di mana fungsi main
akan langsung ke biner yang dapat dieksekusi.
add_executable(mylib-unit-tests test_main.cpp)
Dan file yang menjelaskan tes itu sendiri ditambahkan kemudian. Tetapi ini tidak perlu.
target_sources(mylib-unit-tests PRIVATE mylib/myfeature.cpp)
Kami menghubungkan dependensi. Harap perhatikan bahwa kami hanya target_include_directories
target CMake yang kami butuhkan ke biner kami, dan tidak memanggil perintah target_include_directories
. Header dari kerangka uji dan dari Mylib::mylib
kami, serta parameter build (dalam kasus kami, ini adalah standar bahasa C ++) merangkak bersama dengan tujuan-tujuan ini.
target_link_libraries(mylib-unit-tests PRIVATE Mylib::mylib doctest::doctest )
Terakhir, buat target fiktif, "rakitan" yang setara dengan menjalankan tes, dan tambahkan target ini ke rakitan default (atribut ALL
bertanggung jawab untuk ini). Ini berarti bahwa perakitan secara default memulai peluncuran tes, yaitu, kita tidak akan pernah lupa untuk menjalankannya.
add_custom_target(check ALL COMMAND mylib-unit-tests)
Selanjutnya, kami mengaktifkan pengukuran cakupan kode, jika opsi yang sesuai ditentukan. Saya tidak akan merinci, karena mereka lebih terkait dengan alat untuk mengukur cakupan daripada CMake. Penting untuk dicatat bahwa, berdasarkan hasil, tujuan coverage
akan dibuat, yang dengannya nyaman untuk mulai mengukur cakupan.
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()
Ditemukan Doxygen .
find_package(Doxygen)
Selanjutnya, kami memeriksa apakah pengguna telah menetapkan variabel bahasa. Jika ya, maka jangan sentuh, jika tidak, maka ambil Rusia. Kemudian konfigurasikan file sistem Doxygen. Semua variabel yang diperlukan, termasuk bahasa, sampai di sana selama proses konfigurasi (lihat configure_file
).
Kemudian kami membuat target doc
, yang akan memulai pembuatan dokumentasi. Karena generasi dokumentasi bukanlah kebutuhan terbesar dalam proses pengembangan, secara default tujuan tidak akan diaktifkan, itu harus dimulai secara eksplisit.
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 ()
Di sini kita menemukan Python ketiga dan membuat target wandbox
yang menghasilkan permintaan yang cocok dengan API layanan Wandbox dan mengirimkannya. Sebagai tanggapan, tautan ke kotak pasir selesai datang.
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()
Sekarang pertimbangkan bagaimana menggunakan itu semua.
Perakitan proyek ini, seperti proyek lainnya pada sistem perakitan CMake, terdiri dari dua tahap:
cmake -S // -B /// [ ...]
Jika perintah di atas tidak berfungsi karena CMake versi lama, coba hilangkan -S
:
cmake // -B /// [ ...]
Lebih lanjut tentang opsi .
cmake --build /// [--target target]
Baca lebih lanjut tentang sasaran perakitan .
cmake -S ... -B ... -DMYLIB_COVERAGE=ON [ ...]
Termasuk target coverage
, yang dengannya Anda dapat mulai mengukur cakupan kode dengan tes.
cmake -S ... -B ... -DMYLIB_TESTING=OFF [ ...]
Memberikan kemampuan untuk mematikan unit uji unit dan target check
. Akibatnya, pengukuran cakupan kode dengan tes MYLIB_COVERAGE
(lihat MYLIB_COVERAGE
).
Selain itu, pengujian dinonaktifkan secara otomatis jika proyek terhubung ke proyek lain sebagai sub-proyek menggunakan add_subdirectory
.
cmake -S ... -B ... -DMYLIB_DOXYGEN_LANGUAGE=English [ ...]
Mengganti bahasa dokumentasi yang dihasilkan oleh target doc
ke yang ditentukan. Untuk daftar bahasa yang tersedia, lihat situs web sistem Doxygen .
Secara default, bahasa Rusia diaktifkan.
cmake --build path/to/build/directory cmake --build path/to/build/directory --target all
Jika target tidak ditentukan (yang setara dengan all
sasaran), kumpulkan semua yang mungkin, dan juga panggil target check
.
cmake --build path/to/build/directory --target mylib-unit-tests
Menyusun tes unit. Diaktifkan secara default.
cmake --build /// --target check
Berjalan dikumpulkan (mengumpulkan, jika belum) tes unit. Diaktifkan secara default.
Lihat juga mylib-unit-tests
.
cmake --build /// --target coverage
Menganalisis menjalankan (menjalankan, jika belum) unit test untuk mencakup kode dengan tes menggunakan program gcovr .
Knalpot pelapisan akan terlihat seperti ini:
------------------------------------------------------------------------------ GCC Code Coverage Report Directory: /path/to/cmakecpptemplate/include/ ------------------------------------------------------------------------------ File Lines Exec Cover Missing ------------------------------------------------------------------------------ mylib/myfeature.hpp 2 2 100% ------------------------------------------------------------------------------ TOTAL 2 2 100% ------------------------------------------------------------------------------
Target hanya tersedia ketika MYLIB_COVERAGE
.
Lihat juga check
.
cmake --build /// --target doc
Mulai pembuatan dokumentasi kode menggunakan sistem Doxygen .
cmake --build /// --target wandbox
Respons dari layanan terlihat seperti ini:
{ "permlink" : "QElvxuMzHgL9fqci", "status" : "0", "url" : "https://wandbox.org/permlink/QElvxuMzHgL9fqci" }
Untuk melakukan ini, gunakan layanan Wandbox . Saya tidak tahu bagaimana server karet yang mereka miliki, tetapi saya pikir kesempatan ini tidak boleh disalahgunakan.
Perakitan proyek dalam mode debug dengan pengukuran cakupan
cmake -S // -B /// -DCMAKE_BUILD_TYPE=Debug -DMYLIB_COVERAGE=ON cmake --build /// --target coverage --parallel 16
Instalasi proyek tanpa perakitan dan pengujian awal
cmake -S // -B /// -DMYLIB_TESTING=OFF -DCMAKE_INSTALL_PREFIX=/// cmake --build /// --target install
Bangun dalam mode rilis oleh kompiler yang ditentukan
cmake -S // -B /// -DCMAKE_BUILD_TYPE=Release -DCMAKE_CXX_COMPILER=g++-8 -DCMAKE_PREFIX_PATH=///// cmake --build /// --parallel 4
Pembuatan dokumentasi bahasa Inggris
cmake -S // -B /// -DCMAKE_BUILD_TYPE=Release -DMYLIB_DOXYGEN_LANGUAGE=English cmake --build /// --target doc
CMake 3.13
Bahkan, CMake 3.13 hanya diperlukan untuk menjalankan beberapa perintah konsol yang dijelaskan dalam bantuan ini. Dari sudut pandang sintaksis skrip CMake, versi 3.8 sudah cukup jika generasi dipanggil dengan cara lain.
Pustaka pengujian Doctest
Pengujian dapat dinonaktifkan (lihat MYLIB_TESTING
).
Doksigen
Untuk mengalihkan bahasa tempat dokumentasi akan dibuat, opsi MYLIB_DOXYGEN_LANGUAGE
.
Penerjemah Python 3
Untuk secara otomatis menghasilkan kotak pasir online .
Dengan CMake dan beberapa alat bagus, Anda dapat memberikan analisis statis dengan gerakan tubuh minimal.
Cppcheck
CMake memiliki dukungan bawaan untuk alat analisis statis Cppcheck .
Untuk melakukan ini, gunakan opsi CMAKE_CXX_CPPCHECK
:
cmake -S // -B /// -DCMAKE_BUILD_TYPE=Debug -DCMAKE_CXX_CPPCHECK="cppcheck;--enable=all;-I///include"
Setelah itu, analisis statis akan dimulai secara otomatis setiap kali selama kompilasi dan kompilasi ulang sumber. Anda tidak perlu melakukan apa pun ekstra.
Dentang
Dengan menggunakan alat scan-build
bagus, Anda juga dapat menjalankan analisis statis dalam dua hal:
scan-build cmake -S // -B /// -DCMAKE_BUILD_TYPE=Debug scan-build cmake --build ///
Di sini, tidak seperti halnya Cppcheck, Anda harus menjalankan perakitan setiap kali melalui scan-build
.
CMake adalah sistem yang sangat kuat dan fleksibel yang memungkinkan Anda menerapkan fungsionalitas untuk setiap selera dan warna. Dan, meskipun sintaksis terkadang meninggalkan banyak yang diinginkan, iblis masih tidak begitu mengerikan seperti yang ia lukis. Gunakan sistem bangun CMake untuk kepentingan masyarakat dan kesehatan.
โ Unduh template proyek