C++ وCMake - إخوة إلى الأبد، الجزء الثاني

C++ وCMake - إخوة إلى الأبد، الجزء الثاني

في الجزء السابق تحدثت هذه القصة المسلية عن تنظيم مكتبة رأسية داخل منشئ نظام البناء CMake.

هذه المرة سنضيف إليها مكتبة مجمعة، ونتحدث أيضًا عن ربط الوحدات مع بعضها البعض.

كما كان من قبل، يمكن لأولئك الذين نفاد صبرهم أن يفعلوا ذلك على الفور انتقل إلى المستودع المحدث ولمس كل شيء بيديك.


محتوى

  1. يقسم
  2. يغزو

يقسم

أول شيء يتعين علينا القيام به لتحقيق هدفنا النبيل هو تقسيم البرامج التي نطورها إلى كتل عالمية ومعزولة تكون موحدة من وجهة نظر المستخدم.

في الجزء الأول، تم وصف هذه الكتلة القياسية - مشروع بمكتبة رأسية. الآن دعونا نضيف مكتبة مجمعة لمشروعنا.

للقيام بذلك، دعونا نخرج تنفيذ الوظيفة myfunc في منفصلة .cpp-ملف:

diff --git a/include/mylib/myfeature.hpp b/include/mylib/myfeature.hpp
index 43db388..ba62b4f 100644
--- a/include/mylib/myfeature.hpp
+++ b/include/mylib/myfeature.hpp
@@ -46,8 +46,5 @@ namespace mylib

         ~  see mystruct
      */
-    inline bool myfunc (mystruct)
-    {
-        return true;
-    }
+    bool myfunc (mystruct);
 }
diff --git a/src/mylib/myfeature.cpp b/src/mylib/myfeature.cpp
new file mode 100644
index 0000000..abb5004
--- /dev/null
+++ b/src/mylib/myfeature.cpp
@@ -0,0 +1,9 @@
+#include <mylib/myfeature.hpp>
+
+namespace mylib
+{
+    bool myfunc (mystruct)
+    {
+        return true;
+    }
+}

ثم نحدد المكتبة التي سيتم تجميعها (myfeature)، والتي ستتكون مما تم الحصول عليه في الخطوة السابقة .cpp-ملف. من الواضح أن المكتبة الجديدة تتطلب رؤوسًا موجودة، ومن أجل توفير ذلك، يمكن ويجب ربطها بالغرض الحالي mylib. علاوة على ذلك فإن الاتصال بينهما عام، مما يعني أن كل ما سيرتبط به الهدف myfeature، سوف يتلقى التحميل والهدف تلقائيًا mylib (المزيد عن طرق الحياكة).

diff --git a/CMakeLists.txt b/CMakeLists.txt
index 108045c..0de77b8 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -64,6 +64,17 @@ target_compile_features(mylib INTERFACE cxx_std_17)

 add_library(Mylib::mylib ALIAS mylib)

+###################################################################################################
+##
+##      Компилируемая библиотека
+##
+###################################################################################################
+
+add_library(myfeature src/mylib/myfeature.cpp)
+target_link_libraries(myfeature PUBLIC mylib)
+
+add_library(Mylib::myfeature ALIAS myfeature)
+

بعد ذلك، سنتأكد من تثبيت المكتبة الجديدة أيضًا على النظام:

@@ -72,7 +83,7 @@ add_library(Mylib::mylib ALIAS mylib)

 install(DIRECTORY include/mylib DESTINATION include)

-install(TARGETS mylib EXPORT MylibConfig)
+install(TARGETS mylib myfeature EXPORT MylibConfig)
 install(EXPORT MylibConfig NAMESPACE Mylib:: DESTINATION share/Mylib/cmake)

 include(CMakePackageConfigHelpers)

وتجدر الإشارة إلى أن لهذا الغرض myfeature، أما بالنسبة لل mylib تم إنشاء اسم مستعار ببادئة Mylib::. يتم كتابة الشيء نفسه لكلا الغرضين عند تصديرهما للتثبيت على النظام. هذا يجعل من الممكن العمل بشكل موحد مع الأهداف لأي شخص مخطط ملزم.

بعد ذلك، كل ما تبقى هو ربط اختبارات الوحدة بالمكتبة الجديدة (function myfunc تم حذفه من العنوان، لذا فأنت بحاجة الآن إلى الارتباط):

diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt
index 5620be4..bc1266c 100644
--- a/test/CMakeLists.txt
+++ b/test/CMakeLists.txt
@@ -4,7 +4,7 @@ add_executable(mylib-unit-tests test_main.cpp)
 target_sources(mylib-unit-tests PRIVATE mylib/myfeature.cpp)
 target_link_libraries(mylib-unit-tests
     PRIVATE
-        Mylib::mylib
+        Mylib::myfeature
         doctest::doctest
 )

العناوين (Mylib::mylib) الآن لا تحتاج إلى الاتصال بشكل منفصل، لأنه، كما ذكرنا سابقًا، يتم توصيلهما تلقائيًا مع المكتبة (Mylib::myfeature).

ودعنا نضيف بعض الفروق الدقيقة لضمان قياسات التغطية مع مراعاة المكتبة التي وصلت حديثًا:

@@ -15,11 +15,16 @@ if(MYLIB_COVERAGE AND GCOVR_EXECUTABLE)
     target_compile_options(mylib-unit-tests PRIVATE --coverage)
     target_link_libraries(mylib-unit-tests PRIVATE gcov)

+    target_compile_options(myfeature PRIVATE --coverage)
+    target_link_libraries(myfeature PRIVATE gcov)
+
     add_custom_target(coverage
         COMMAND
             ${GCOVR_EXECUTABLE}
-                --root=${PROJECT_SOURCE_DIR}/include/
-                --object-directory=${CMAKE_CURRENT_BINARY_DIR}
+                --root=${PROJECT_SOURCE_DIR}/
+                --filter=${PROJECT_SOURCE_DIR}/include
+                --filter=${PROJECT_SOURCE_DIR}/src
+                --object-directory=${PROJECT_BINARY_DIR}
         DEPENDS
             check
     )

يمكنك إضافة المزيد من المكتبات والملفات التنفيذية وما إلى ذلك. لا يهم مدى ارتباطهم ببعضهم البعض داخل المشروع. الشيء المهم الوحيد هو تحديد الأهداف التي تمثل واجهة وحدتنا، أي أنها تظل بارزة.

يغزو

الآن لدينا وحدات كتلة قياسية، ويمكننا السيطرة عليها: تركيبها في بنية بأي تعقيد، أو تثبيتها في نظام أو ربطها معًا داخل نظام تجميع واحد.

التثبيت في النظام

أحد خيارات استخدام الوحدة هو تثبيت الوحدة الخاصة بنا في النظام.

cmake --build путь/к/сборочной/директории --target install

وبعد ذلك يتم توصيله بأي مشروع آخر باستخدام الأمر find_package.

find_package(Mylib 1.0 REQUIRED)

الاتصال كوحدة فرعية

هناك خيار آخر وهو توصيل المجلد بمشروعنا بمشروع آخر كوحدة فرعية باستخدام الأمر add_subdirectory.

استخدام

تختلف طرق الربط لكن النتيجة واحدة. في كلتا الحالتين، ستكون الأهداف متاحة في المشروع باستخدام وحدتنا Mylib::myfeature и Mylib::mylib، والتي يمكن استخدامها، على سبيل المثال، مثل هذا:

add_executable(some_executable some.cpp sources.cpp)
target_link_libraries(some_executable PRIVATE Mylib::myfeature)

وعلى وجه التحديد في حالتنا، المكتبة Mylib::myfeature يجب أن تكون متصلاً عندما يكون من الضروري الارتباط بالمكتبة libmyfeature. إذا كان هناك ما يكفي من الرؤوس، فمن المفيد استخدام المكتبة Mylib::mylib.

يمكن أن تكون أهداف CMake صعبة، على سبيل المثال، تهدف فقط إلى إعادة توجيه بعض الخصائص والتبعيات وما إلى ذلك. وفي الوقت نفسه، يتم العمل معهم بنفس الطريقة.

هذا ما كنا بحاجة للحصول عليه.

المصدر: www.habr.com

إضافة تعليق