Trong quá trình phát triển, tôi muốn thay đổi trình biên dịch, chế độ xây dựng, phiên bản phụ thuộc, thực hiện phân tích tĩnh, đo lường hiệu suất, thu thập phạm vi bảo hiểm, tạo tài liệu, v.v. Và tôi thực sự yêu thích CMake vì nó cho phép tôi làm mọi thứ tôi muốn.
Nhiều người chỉ trích CMake, và thường là đáng bị như vậy, nhưng nếu nhìn vào nó, không phải mọi thứ đều tệ đến thế, và gần đây không xấu cảvà hướng phát triển khá tích cực.
Trong lưu ý này, tôi muốn cho bạn biết cách tổ chức thư viện tiêu đề trong C++ trong hệ thống CMake để có được chức năng sau:
Cuộc họp;
Kiểm tra tự động chạy;
Đo lường phạm vi mã;
Cài đặt;
Tài liệu tự động;
Tạo sandbox trực tuyến;
Phân tích tĩnh.
Bất kỳ ai đã hiểu rõ ưu điểm và C-make có thể chỉ cần tải mẫu dự án và bắt đầu sử dụng nó.
Chúng tôi sẽ chủ yếu nói về cách sắp xếp các tập lệnh CMake, vì vậy chúng sẽ được thảo luận chi tiết. Bất kỳ ai cũng có thể xem trực tiếp phần còn lại của tệp trên trang dự án mẫu.
Trước hết, bạn cần yêu cầu phiên bản bắt buộc của hệ thống CMake. CMake đang phát triển, chữ ký lệnh và hành vi trong các điều kiện khác nhau đang thay đổi. Để CMake hiểu ngay những gì chúng ta muốn từ nó, chúng ta cần ghi lại ngay những yêu cầu của mình đối với nó.
cmake_minimum_required(VERSION 3.13)
Sau đó, chúng tôi sẽ chỉ định dự án của mình, tên, phiên bản, ngôn ngữ được sử dụng, v.v. (xem. команду project).
Trong trường hợp này chúng tôi chỉ ra ngôn ngữ CXX (và điều này có nghĩa là C++) để CMake không phải căng thẳng và tìm kiếm trình biên dịch ngôn ngữ C (theo mặc định, CMake bao gồm hai ngôn ngữ: C và C++).
project(Mylib VERSION 1.0 LANGUAGES CXX)
Tại đây bạn có thể kiểm tra ngay xem dự án của chúng tôi có được đưa vào dự án khác dưới dạng dự án con hay không. Điều này sẽ giúp ích rất nhiều trong tương lai.
Tùy chọn đầu tiên là MYLIB_TESTING - để vô hiệu hóa các bài kiểm tra đơn vị. Điều này có thể cần thiết nếu chúng tôi chắc chắn rằng mọi thứ đều ổn với các thử nghiệm, nhưng chúng tôi chỉ muốn cài đặt hoặc đóng gói dự án của mình chẳng hạn. Hoặc dự án của chúng tôi được đưa vào dưới dạng một dự án con - trong trường hợp này, người dùng dự án của chúng tôi không quan tâm đến việc chạy thử nghiệm của chúng tôi. Bạn không kiểm tra các phần phụ thuộc mà bạn sử dụng, phải không?
Ngoài ra, chúng tôi sẽ thực hiện một tùy chọn riêng MYLIB_COVERAGE để đo mức độ bao phủ mã bằng các thử nghiệm, nhưng nó sẽ yêu cầu các công cụ bổ sung, vì vậy nó sẽ cần phải được kích hoạt một cách rõ ràng.
Tất nhiên, chúng tôi là những lập trình viên tuyệt vời, vì vậy chúng tôi muốn mức độ chẩn đoán thời gian biên dịch tối đa từ trình biên dịch. Không một con chuột nào lọt qua được.
Thư viện của chúng tôi chỉ bao gồm các tệp tiêu đề, có nghĩa là chúng tôi không có bất kỳ nguồn thải nào ở dạng thư viện tĩnh hoặc động. Mặt khác, để sử dụng thư viện của chúng tôi từ bên ngoài, nó cần phải được cài đặt, nó cần phải được phát hiện trong hệ thống và được kết nối với dự án của bạn, đồng thời các tiêu đề tương tự này, cũng như có thể một số tiêu đề bổ sung, được gắn vào các thuộc tính của nó.
Với mục đích này, chúng tôi tạo ra một thư viện giao diện.
add_library(mylib INTERFACE)
Chúng tôi liên kết các tiêu đề với thư viện giao diện của chúng tôi.
Việc sử dụng CMake hiện đại, thời trang, trẻ trung ngụ ý rằng các tiêu đề, thuộc tính, v.v. truyền qua một mục tiêu duy nhất. Nói thế là đủ rồi target_link_libraries(target PRIVATE dependency)và tất cả các tiêu đề được liên kết với mục tiêu dependency, sẽ có sẵn cho các nguồn thuộc về mục tiêu target. Và bạn không cần bất kỳ [target_]include_directories. Điều này sẽ được chứng minh dưới đây trong phần phân tích Tập lệnh CMake cho các bài kiểm tra đơn vị.
Lệnh này liên kết các tiêu đề chúng ta cần với thư viện giao diện của chúng ta và nếu thư viện của chúng ta được kết nối với bất kỳ mục tiêu nào trong cùng hệ thống phân cấp CMake, thì các tiêu đề từ thư mục sẽ được liên kết với nó ${CMAKE_CURRENT_SOURCE_DIR}/includevà nếu thư viện của chúng tôi được cài đặt trên hệ thống và được kết nối với dự án khác bằng lệnh find_package, khi đó các tiêu đề từ thư mục sẽ được liên kết với nó include liên quan đến thư mục cài đặt.
Hãy thiết lập một tiêu chuẩn ngôn ngữ. Tất nhiên, cái cuối cùng. Đồng thời, chúng tôi không chỉ đưa vào tiêu chuẩn mà còn mở rộng nó cho những người sẽ sử dụng thư viện của chúng tôi. Điều này đạt được là do thuộc tính set có một danh mục INTERFACE (Xem. lệnh target_compile_features).
Hãy tạo bí danh cho thư viện của chúng tôi. Hơn nữa, để làm đẹp, nó sẽ nằm trong một “không gian tên” đặc biệt. Điều này sẽ hữu ích khi các mô-đun khác nhau xuất hiện trong thư viện của chúng tôi và chúng tôi sẽ kết nối chúng độc lập với nhau. Như ở Busta chẳng hạn.
Cài đặt các tiêu đề của chúng tôi vào hệ thống. Mọi thứ đều đơn giản ở đây. Chúng tôi nói rằng thư mục có tất cả các tiêu đề sẽ nằm trong thư mục include liên quan đến vị trí lắp đặt.
Tiếp theo, chúng tôi thông báo cho hệ thống xây dựng rằng chúng tôi muốn có thể gọi lệnh trong các dự án của bên thứ ba find_package(Mylib) và có được bàn thắng Mylib::mylib.
Câu thần chú tiếp theo nên được hiểu theo cách này. Khi ở trong dự án của bên thứ ba, chúng tôi gọi lệnh find_package(Mylib 1.2.3 REQUIRED)và phiên bản thực của thư viện đã cài đặt sẽ không tương thích với phiên bản 1.2.3CMake sẽ tự động phát sinh lỗi. Tức là bạn sẽ không cần phải theo dõi các phiên bản theo cách thủ công.
Nếu các bài kiểm tra bị vô hiệu hóa một cách rõ ràng bằng cách sử dụng tùy chọn tương ứng hoặc dự án của chúng tôi là một dự án con, nghĩa là nó được kết nối với một dự án CMake khác bằng lệnh add_subdirectory, chúng tôi không di chuyển xa hơn dọc theo hệ thống phân cấp và tập lệnh mô tả các lệnh để tạo và chạy thử nghiệm đơn giản là không chạy.
if(NOT MYLIB_TESTING)
message(STATUS "Тестирование проекта Mylib выключено")
elseif(IS_SUBPROJECT)
message(STATUS "Mylib не тестируется в режиме подмодуля")
else()
add_subdirectory(test)
endif()
Chúng tôi kết nối các phụ thuộc. Xin lưu ý rằng chúng tôi chỉ liên kết các mục tiêu CMake mà chúng tôi cần với tệp nhị phân của mình và không gọi lệnh target_include_directories. Các tiêu đề từ khung kiểm tra và từ chúng tôi Mylib::mylib, cũng như các tham số bản dựng (trong trường hợp của chúng tôi, đây là tiêu chuẩn ngôn ngữ C++) đã đạt được cùng với các mục tiêu này.
Cuối cùng, chúng tôi tạo một mục tiêu giả, “bản dựng” tương đương với việc chạy thử nghiệm và thêm mục tiêu này vào bản dựng mặc định (thuộc tính chịu trách nhiệm cho việc này ALL). Điều này có nghĩa là bản dựng mặc định sẽ kích hoạt các thử nghiệm để chạy, nghĩa là chúng tôi sẽ không bao giờ quên chạy chúng.
add_custom_target(check ALL COMMAND mylib-unit-tests)
Tiếp theo, chúng tôi kích hoạt đo lường phạm vi mã nếu tùy chọn thích hợp được chỉ định. Tôi sẽ không đi sâu vào chi tiết vì chúng liên quan nhiều đến công cụ đo lường mức độ phù hợp hơn là CMake. Điều quan trọng cần lưu ý là dựa trên kết quả, mục tiêu sẽ được tạo ra coverage, thuận tiện để bắt đầu đo phạm vi bao phủ.
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()
Tiếp theo, chúng tôi kiểm tra xem người dùng đã đặt biến ngôn ngữ chưa. Nếu có thì không chạm vào, nếu không thì chúng ta lấy tiếng Nga. Sau đó, chúng tôi định cấu hình các tệp hệ thống Doxygen. Tất cả các biến cần thiết, bao gồm cả ngôn ngữ, đều có ở đó trong quá trình cấu hình (xem. команду configure_file).
Sau đó, chúng tôi tạo ra một mục tiêu doc, thao tác này sẽ bắt đầu tạo tài liệu. Vì việc tạo tài liệu không phải là nhu cầu lớn nhất trong quá trình phát triển nên mục tiêu sẽ không được bật theo mặc định; nó sẽ phải được khởi chạy một cách rõ ràng.
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 ()
Ở đây chúng tôi tìm thấy Python thứ ba và tạo mục tiêu wandbox, tạo ra một yêu cầu tương ứng với API dịch vụ Hộp đựng đũa phép, và đuổi anh ta đi. Phản hồi đi kèm với một liên kết đến hộp cát đã hoàn thành.
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()
Cung cấp khả năng vô hiệu hóa mục tiêu và xây dựng thử nghiệm đơn vị check. Kết quả là, việc đo lường mức độ bao phủ mã bằng các thử nghiệm bị tắt (xem phần XNUMX). MYLIB_COVERAGE).
Việc kiểm tra cũng tự động bị vô hiệu hóa nếu dự án được kết nối với dự án khác dưới dạng dự án con bằng lệnh add_subdirectory.
Dịch vụ này được sử dụng cho việc này Hộp đựng đũa phép. Tôi không biết máy chủ của họ linh hoạt đến mức nào, nhưng tôi nghĩ rằng không nên lạm dụng cơ hội này.
Trên thực tế, CMake phiên bản 3.13 chỉ được yêu cầu để chạy một số lệnh console được mô tả trong trợ giúp này. Từ quan điểm về cú pháp của tập lệnh CMake, phiên bản 3.8 là đủ nếu việc tạo được gọi theo những cách khác.
CMake là một hệ thống rất mạnh mẽ và linh hoạt cho phép bạn triển khai chức năng cho mọi sở thích và màu sắc. Và, mặc dù cú pháp đôi khi còn nhiều điều chưa được mong muốn, nhưng con quỷ vẫn không khủng khiếp như được vẽ ra. Sử dụng hệ thống xây dựng CMake vì lợi ích của xã hội và sức khỏe.