در طول توسعه، من دوست دارم کامپایلرها را تغییر دهم، حالتهای ساخت، نسخههای وابستگی را انجام دهم، تجزیه و تحلیل استاتیک انجام دهم، عملکرد را اندازهگیری کنم، پوشش را جمعآوری کنم، اسناد تولید کنم و غیره. و من واقعاً CMake را دوست دارم زیرا به من اجازه می دهد هر کاری را که می خواهم انجام دهم.
بسیاری از مردم CMake را مورد انتقاد قرار می دهند، و اغلب سزاوار آن هستند، اما اگر به آن نگاه کنید، همه چیز آنقدر بد نیست و اخیرا اصلا بد نیست، و جهت توسعه کاملا مثبت است.
در این یادداشت، می خواهم به شما بگویم که چگونه به سادگی یک کتابخانه هدر را در C++ در سیستم CMake سازماندهی کنید تا عملکرد زیر را به دست آورید:
مونتاژ؛
تست های اتوران؛
اندازه گیری پوشش کد؛
نصب و راه اندازی؛
مستندسازی خودکار؛
تولید سندباکس آنلاین؛
تجزیه و تحلیل استاتیک.
هر کسی که قبلاً مزایا و C-make را درک کرده باشد، می تواند به سادگی دانلود قالب پروژه و شروع به استفاده از آن کنید.
ما عمدتاً در مورد نحوه سازماندهی اسکریپت های CMake صحبت خواهیم کرد، بنابراین آنها به تفصیل مورد بحث قرار خواهند گرفت. هر کسی می تواند بقیه فایل ها را مستقیماً مشاهده کند در صفحه پروژه الگو.
ابتدا باید نسخه مورد نیاز سیستم CMake را درخواست کنید. CMake در حال تکامل است، امضاهای فرمان و رفتار در شرایط مختلف در حال تغییر است. برای اینکه CMake فوراً بفهمد ما از آن چه میخواهیم، باید فوراً نیازهای خود را برای آن ثبت کنیم.
cmake_minimum_required(VERSION 3.13)
سپس پروژه خود، نام، نسخه، زبانهای مورد استفاده و غیره را مشخص میکنیم (نگاه کنید به. команду project).
در این مورد ما زبان را نشان می دهیم CXX (و این به معنی C++ است) تا CMake فشار نیاورد و کامپایلر زبان C را جستجو نکند (به طور پیش فرض CMake شامل دو زبان C و C++ است).
project(Mylib VERSION 1.0 LANGUAGES CXX)
در اینجا می توانید بلافاصله بررسی کنید که آیا پروژه ما در پروژه دیگری به عنوان پروژه فرعی گنجانده شده است یا خیر. این در آینده کمک زیادی خواهد کرد.
گزینه اول این است MYLIB_TESTING - برای غیرفعال کردن تست های واحد. این ممکن است در صورتی ضروری باشد که مطمئن باشیم همه چیز در آزمایشها درست است، اما فقط میخواهیم مثلاً پروژه خود را نصب یا بسته بندی کنیم. یا پروژه ما به عنوان یک پروژه فرعی گنجانده شده است - در این مورد، کاربر پروژه ما علاقه ای به اجرای تست های ما ندارد. شما وابستگی هایی را که استفاده می کنید آزمایش نمی کنید، درست است؟
علاوه بر این، ما یک گزینه جداگانه ایجاد خواهیم کرد MYLIB_COVERAGE برای اندازهگیری پوشش کد توسط آزمایشها، اما به ابزارهای اضافی نیاز دارد، بنابراین باید به طور صریح فعال شود.
کتابخانه ما فقط از فایل های هدر تشکیل شده است، به این معنی که ما هیچ اگزوزی به شکل کتابخانه های ایستا یا پویا نداریم. از طرف دیگر، برای استفاده از کتابخانه ما به صورت خارجی، باید نصب شود، باید در سیستم قابل شناسایی باشد و به پروژه شما متصل شود و در عین حال همین هدرها و همچنین احتمالاً تعدادی اضافی، خواص به آن متصل می شوند.
برای این منظور یک کتابخانه رابط ایجاد می کنیم.
add_library(mylib INTERFACE)
ما هدرها را به کتابخانه رابط خود متصل می کنیم.
استفاده مدرن، مد روز و جوان از CMake به این معنی است که هدرها، ویژگی ها و غیره. از طریق یک هدف منفرد منتقل می شود. پس گفتن کافی است target_link_libraries(target PRIVATE dependency)، و تمام هدرهایی که با هدف مرتبط هستند dependency، برای منابع متعلق به هدف در دسترس خواهد بود target. و شما به هیچکدام نیاز ندارید [target_]include_directories. این در زیر در تجزیه و تحلیل نشان داده خواهد شد اسکریپت CMake برای تست های واحد.
این دستور هدرهای مورد نیاز ما را با کتابخانه رابط ما مرتبط می کند و اگر کتابخانه ما به هر هدفی در همان سلسله مراتب CMake متصل باشد، سرصفحه های دایرکتوری با آن مرتبط می شوند. ${CMAKE_CURRENT_SOURCE_DIR}/includeو اگر کتابخانه ما روی سیستم نصب شده باشد و با استفاده از دستور به پروژه دیگری متصل شده باشد find_package، سپس هدرهای دایرکتوری با آن مرتبط می شود include نسبت به دایرکتوری نصب
بیایید یک استاندارد زبان تعیین کنیم. البته آخرین مورد. در عین حال، ما نه تنها استاندارد را درج می کنیم، بلکه آن را به کسانی که از کتابخانه ما استفاده می کنند نیز تعمیم می دهیم. این به دلیل این واقعیت است که ویژگی مجموعه دارای یک دسته است INTERFACE (مراجعه کنید. دستور target_compile_features).
بیایید یک نام مستعار برای کتابخانه خود ایجاد کنیم. علاوه بر این، برای زیبایی، در یک "فضای نام" خاص خواهد بود. این زمانی مفید خواهد بود که ماژول های مختلف در کتابخانه ما ظاهر می شوند و ما می خواهیم آنها را مستقل از یکدیگر متصل کنیم. مثلاً در بوستا.
در مرحله بعد به سیستم ساخت اطلاع می دهیم که می خواهیم بتوانیم دستور را در پروژه های شخص ثالث فراخوانی کنیم find_package(Mylib) و به هدف برسید Mylib::mylib.
طلسم بعدی را باید اینگونه فهمید. وقتی در یک پروژه شخص ثالث دستور را فراخوانی می کنیم find_package(Mylib 1.2.3 REQUIRED)، و نسخه واقعی کتابخانه نصب شده با نسخه ناسازگار خواهد بود 1.2.3CMake به طور خودکار خطا ایجاد می کند. یعنی نیازی به ردیابی نسخه ها به صورت دستی نخواهید داشت.
اگر آزمایش ها به صراحت با استفاده از گزینه مربوطه یا پروژه ما یک پروژه فرعی است، یعنی با استفاده از دستور به پروژه CMake دیگری متصل می شود add_subdirectory، ما در طول سلسله مراتب بیشتر حرکت نمی کنیم و اسکریپت که دستورات تولید و اجرای تست ها را توصیف می کند، به سادگی اجرا نمی شود.
if(NOT MYLIB_TESTING)
message(STATUS "Тестирование проекта Mylib выключено")
elseif(IS_SUBPROJECT)
message(STATUS "Mylib не тестируется в режиме подмодуля")
else()
add_subdirectory(test)
endif()
ما وابستگی ها را به هم وصل می کنیم. لطفاً توجه داشته باشید که ما فقط اهداف CMake مورد نیاز خود را به باینری خود پیوند دادیم و دستور را فراخوانی نکردیم target_include_directories. عناوین از چارچوب آزمون و از ما Mylib::mylibو همچنین پارامترهای ساخت (در مورد ما، این استاندارد زبان C++ است) همراه با این اهداف به دست آمد.
در نهایت، یک هدف ساختگی ایجاد میکنیم که «build» آن معادل آزمایشهای در حال اجرا است، و این هدف را به ساخت پیشفرض اضافه میکنیم (ویژگی مسئول این است. ALL). این بدان معنی است که ساخت پیش فرض تست ها را برای اجرا راه اندازی می کند، به این معنی که ما هرگز اجرای آنها را فراموش نمی کنیم.
add_custom_target(check ALL COMMAND mylib-unit-tests)
در مرحله بعد، در صورت مشخص شدن گزینه مناسب، اندازه گیری پوشش کد را فعال می کنیم. من وارد جزئیات نمی شوم، زیرا آنها بیشتر به ابزاری برای اندازه گیری پوشش مربوط می شوند تا CMake. فقط توجه به این نکته مهم است که بر اساس نتایج یک هدف ایجاد می شود coverage، که با آن شروع به اندازه گیری پوشش راحت است.
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()
بعد، بررسی می کنیم که آیا کاربر متغیر زبان را تنظیم کرده است یا خیر. اگر بله، پس ما آن را لمس نمی کنیم، اگر نه، پس ما روسی می گیریم. سپس فایل های سیستم Doxygen را پیکربندی می کنیم. تمام متغیرهای لازم، از جمله زبان، در طول فرآیند پیکربندی به آنجا می روند (نگاه کنید به. команду configure_file).
سپس یک هدف ایجاد می کنیم doc، که شروع به تولید اسناد می کند. از آنجایی که تولید اسناد بزرگترین نیاز در فرآیند توسعه نیست، هدف به طور پیشفرض فعال نمیشود، بلکه باید به صراحت راهاندازی شود.
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 ()
در اینجا ما پایتون سوم را پیدا کرده و یک هدف ایجاد می کنیم wandbox، که یک درخواست مربوط به API سرویس ایجاد می کند جعبه عصا، و او را می فرستد. پاسخ همراه با پیوندی به جعبه شنی تمام شده است.
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()
امکان غیرفعال کردن ساخت و هدف آزمایش واحد را فراهم می کند check. در نتیجه، اندازه گیری پوشش کد توسط تست ها خاموش می شود (نگاه کنید به. MYLIB_COVERAGE).
همچنین اگر پروژه با استفاده از دستور به پروژه دیگری به عنوان پروژه فرعی متصل شود، آزمایش به طور خودکار غیرفعال می شود add_subdirectory.
در واقع، نسخه 3.13 CMake فقط برای اجرای برخی از دستورات کنسول توضیح داده شده در این راهنما مورد نیاز است. از نقطه نظر نحو اسکریپت های CMake، اگر تولید به روش های دیگر فراخوانی شود، نسخه 3.8 کافی است.
CMake یک سیستم بسیار قدرتمند و منعطف است که به شما امکان می دهد عملکردی را برای هر سلیقه و رنگی پیاده سازی کنید. و، اگرچه نحو گاهی اوقات چیزهای زیادی برای دلخواه باقی می گذارد، اما شیطان هنوز آنقدر که نقاشی شده است وحشتناک نیست. از سیستم ساخت CMake به نفع جامعه و سلامت استفاده کنید.