Κατά τη διάρκεια της ανάπτυξης, μου αρέσει να αλλάζω μεταγλωττιστές, να δημιουργώ τρόπους λειτουργίας, εκδόσεις εξάρτησης, να εκτελώ στατική ανάλυση, να μετρώ την απόδοση, να συλλέγω κάλυψη, να δημιουργώ τεκμηρίωση και ούτω καθεξής. Και αγαπώ πολύ το CMake γιατί μου επιτρέπει να κάνω ό,τι θέλω.
Πολλοί επιπλήττουν το CMake, και συχνά επάξια, αλλά αν κοιτάξετε, δεν είναι τόσο κακό, αλλά τελευταία καθόλου κακό, και η κατεύθυνση ανάπτυξης είναι αρκετά θετική.
Σε αυτήν τη σημείωση, θέλω να σας πω πόσο εύκολο είναι να οργανώσετε μια βιβλιοθήκη κεφαλίδων C++ σε ένα σύστημα CMake για να έχετε την ακόλουθη λειτουργικότητα:
συνέλευση;
Δοκιμές αυτόματης εκτέλεσης.
Μέτρηση κάλυψης κωδικού.
εγκατάσταση;
Αυτόματη τεκμηρίωση;
Online παραγωγή sandbox.
Στατική ανάλυση.
Όποιος καταλαβαίνει ήδη τα πλεονεκτήματα και το si-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).
Παίρνουμε ένα ψευδώνυμο για τη βιβλιοθήκη μας. Και για ομορφιά, θα είναι σε ειδικό «χώρο ονομάτων». Αυτό θα είναι χρήσιμο όταν εμφανίζονται διαφορετικές λειτουργικές μονάδες στη βιβλιοθήκη μας και πηγαίνουμε να τις συνδέσουμε ανεξάρτητα το ένα από το άλλο. Όπως στη Μπούστα, για παράδειγμα.
Εγκατάσταση των κεφαλίδων μας στο σύστημα. Όλα είναι απλά εδώ. Λέμε ότι ο φάκελος με όλες τις κεφαλίδες πρέπει να πέσει στον κατάλογο include σχετικά με τη θέση εγκατάστασης.
Στη συνέχεια, λέμε στο σύστημα κατασκευής ότι θέλουμε να μπορούμε να καλούμε την εντολή σε έργα τρίτων find_package(Mylib) και βάλε στόχο Mylib::mylib.
Η ακόλουθη επωδία πρέπει να γίνει κατανοητή έτσι. Όταν σε ένα side project καλούμε την εντολή find_package(Mylib 1.2.3 REQUIRED), και ταυτόχρονα η πραγματική έκδοση της εγκατεστημένης βιβλιοθήκης δεν θα είναι συμβατή με την έκδοση 1.2.3, το CMake θα δημιουργήσει αυτόματα ένα σφάλμα. Δηλαδή, δεν θα χρειαστεί να παρακολουθείτε τις εκδόσεις χειροκίνητα.
Εάν οι δοκιμές είναι ρητά απενεργοποιημένες με αντίστοιχη επιλογή ή το έργο μας είναι υποέργο, δηλαδή συνδέεται με άλλο έργο CMake χρησιμοποιώντας την εντολή add_subdirectory, δεν πηγαίνουμε πιο κάτω στην ιεραρχία και το σενάριο, το οποίο περιγράφει τις εντολές για τη δημιουργία και την εκτέλεση δοκιμών, απλά δεν ξεκινά.
if(NOT MYLIB_TESTING)
message(STATUS "Тестирование проекта Mylib выключено")
elseif(IS_SUBPROJECT)
message(STATUS "Mylib не тестируется в режиме подмодуля")
else()
add_subdirectory(test)
endif()
Πρώτα απ 'όλα, βρίσκουμε ένα πακέτο με το επιθυμητό πλαίσιο δοκιμής (αντικαταστήστε το με το αγαπημένο σας).
find_package(doctest 2.3.3 REQUIRED)
Δημιουργούμε το εκτελέσιμο αρχείο μας με δοκιμές. Συνήθως, απευθείας στο εκτελέσιμο δυαδικό, προσθέτω μόνο το αρχείο στο οποίο θα βρίσκεται η συνάρτηση main.
add_executable(mylib-unit-tests test_main.cpp)
Και τα αρχεία στα οποία περιγράφονται τα ίδια τα τεστ, τα προσθέτω αργότερα. Δεν είναι όμως απαραίτητο να το κάνουμε.
Συνδέουμε εξαρτήσεις. Λάβετε υπόψη ότι επισυνάψαμε μόνο τους στόχους 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 ()
Εδώ βρίσκουμε την τρίτη Python και δημιουργούμε έναν στόχο wandbox, το οποίο δημιουργεί ένα αίτημα που αντιστοιχεί στο API υπηρεσίας Ραβδί, και το στέλνει. Σε απάντηση, έρχεται ένας σύνδεσμος προς το έτοιμο sandbox.
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 είναι επαρκής εάν η παραγωγή καλείται με άλλους τρόπους.
Μετά από αυτό, η στατική ανάλυση θα εκκινείται αυτόματα κάθε φορά κατά τη μεταγλώττιση και την εκ νέου μεταγλώττιση των πηγών. Δεν χρειάζεται να γίνει τίποτα επιπλέον.
Κλαγγή
Με ένα υπέροχο εργαλείο scan-build Μπορείτε επίσης να εκτελέσετε στατική ανάλυση σε ελάχιστο χρόνο:
Το CMake είναι ένα πολύ ισχυρό και ευέλικτο σύστημα που σας επιτρέπει να εφαρμόσετε λειτουργικότητα για κάθε γούστο και χρώμα. Και, αν και η σύνταξη μερικές φορές αφήνει πολλά να είναι επιθυμητά, ο διάβολος εξακολουθεί να μην είναι τόσο τρομερός όσο είναι ζωγραφισμένος. Χρησιμοποιήστε το σύστημα κατασκευής CMake προς όφελος της κοινωνίας και της υγείας.