CMakeLists begrijpen

Grip krijgen op hoe bestanden CMakeLists.txt werken

CMake

In ons inleidende handboek gebruiken we CMake als het bouwsysteem voor onze toepassing, maar we hebben slechts alleen wat meer aandacht besteed aan een van onze bestanden CMakeLists.txt. Hier gaan we het in meer detail hebben over dit werkt.

CMake is nuttig omdat het ons is staat stelt om veel van de zaken die gedaan moeten worden alvorens te compileren te automatiseren.

De basis CMakeLists.txt

Misschien herinnert u zich dit bestand CMakeLists.txt uit de eerste handleiding:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
cmake_minimum_required(VERSION 3.20)
project(kirigami-tutorial)

find_package(ECM 6.0.0 REQUIRED NO_MODULE)
set(CMAKE_MODULE_PATH ${ECM_MODULE_PATH})

include(KDEInstallDirs)
include(KDECMakeSettings)
include(KDECompilerSettings NO_POLICY_SCOPE)
include(ECMFindQmlModule)
include(ECMQmlModule)

find_package(Qt6 REQUIRED COMPONENTS
    Core
    Quick
    Test
    Gui
    QuickControls2
    Widgets
)

find_package(KF6 REQUIRED COMPONENTS
    Kirigami
    I18n
    CoreAddons
    QQC2DesktopStyle
    IconThemes
)

ecm_find_qmlmodule(org.kde.kirigami REQUIRED)

add_subdirectory(src)

install(PROGRAMS org.kde.tutorial.desktop DESTINATION ${KDE_INSTALL_APPDIR})

feature_summary(WHAT ALL INCLUDE_QUIET_PACKAGES FATAL_ON_MISSING_REQUIRED_PACKAGES)

De eerste regel, cmake_minimum_required() stelt de versie van CMake in die we willen aanroepen.

Daarna, definieert project(kirigami-tutorial) de naam van het project.

Daarna krijgen we een sectie waar we een aantal noodzakelijke CMake en KDE-instellingen invoegen met gebruik van extra-cmake-modules. Ze leveren een set van nuttige hulpmiddelen:

  • KDEInstallDirs levert gemaksvariabelen zoals ${KDE_INSTALL_TARGETS_DEFAULT_ARGS}, ${KDE_INSTALL_QMLDIR}, ${KDE_INSTALL_BINDIR} en ${KDE_INSTALL_LIBDIR}.
  • KDECMakeSettings levert zaken zoals CMAKE_AUTORCC ON, een uninstall doel dat gebruikt kan worden met cmake --build build/ --target uninstall en ENABLE_CLAZY.
  • KDECompilerSettings levert minimale C++ standaard compilervlaggen zoals -pedantic, en beste praktijk-macro's zoals -DQT_NO_CAST_FROM_ASCII om expliciete conversies zoals QStringLiteral() te vereisen.
  • ECMFindQmlModule levert een manier om een runtime QML afhankelijkheid te verzekeren die gevonden is bij compileren.
  • ECMQmlModule levert CMake commando's zoals ecm_add_qml_module() en ecm_target_qml_sources().

De volgende sectie is belangrijk, omdat het specificeert welke afhankelijkheden we zullen inbrengen bij compileren. Laten we naar de eerste kijken:

13
14
15
16
17
18
19
20
21
22
23
24
25
26
find_package(Qt6 REQUIRED COMPONENTS
    Core
    Quick
    Test
    Gui
    QuickControls2
    Widgets
)

find_package(KF6 REQUIRED COMPONENTS
    Kirigami
    I18n
    CoreAddons
    QQC2DesktopStyle
  • find_package() zoekt en laadt de externe bibliotheek en zijn componenten.
  • REQUIRED vertelt CMake te eindigen met een fout als het pakket niet gevonden kan worden.
  • COMPONENTS is een parameter die vooraf gaat aan de specifieke componenten van het framework dat we zullen invoegen.
  • Elk woord na COMPONENTS refereert naar een specifieke component van de bibliotheek.

De installatieregel instrueert CMake om het desktop-bestand in ${KDE_INSTALL_APPDIR} te installeren, die onder Linux vertaalt naar $XDG_DATA_DIRS/applications, gewoonlijk /usr/share/applications en onder Windows vertaalt naar C:/Program Files/${PROJECT_NAME}/bin/data/applications:

32
add_subdirectory(src)

De laatste regel laat CMake tonen welke pakketten het heeft gevonden en het laat compileren mislukken onmiddellijk als het een fout ontdekt:

34
install(PROGRAMS org.kde.tutorial.desktop DESTINATION ${KDE_INSTALL_APPDIR})

En bovendien dat add_subdirectory(src) CMake laat wijzen naar de map 'src/', waar het een ander bestand CMakeLists.txt vindt:

src/CMakeLists.txt

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
add_executable(kirigami-hello)

ecm_add_qml_module(kirigami-hello
    URI
    org.kde.tutorial
)

target_sources(kirigami-hello
    PRIVATE
    main.cpp
)

ecm_target_qml_sources(kirigami-hello
    SOURCES
    Main.qml
)

target_link_libraries(kirigami-hello
    PRIVATE
    Qt6::Quick
    Qt6::Qml
    Qt6::Gui
    Qt6::QuickControls2
    Qt6::Widgets
    KF6::I18n
    KF6::CoreAddons
    KF6::IconThemes
)

install(TARGETS kirigami-hello ${KDE_INSTALL_TARGETS_DEFAULT_ARGS})

Terwijl het eerste bestand metagegevens behandelt en bibliotheken zoekt, zal deze bestaan uit behandeling van afhankelijkheden en de toepassing installeren. Deze heeft de volgende CMake aanroepen:

  • add_executable maakt het uitvoerbare doel dat we willen gebruiken om het project te runnen.
  • ecm_add_qml_module() maakt een QML-moduledoel aan die toegankelijk zal zijn via het importeren van "org.kde.tutorial".
  • target_sources() voegt C++ bronbestanden toe aan het uitvoerbare doel.
  • ecm_target_qml_sources() voegt QML-bestanden toe aan de module.
  • target_link_libraries() koppelt de C++-bibliotheken, die gebruikt worden in onze code, aan ons uitvoerbare bestand. Kirigami is hier niet meegenomen omdat we alleen zijn QML-module gebruiken.
  • install() installeert het uitvoerbare bestand in het systeem.

De documentatie voor de twee ECM commando's is te vinden in de extra-cmake-modules API voor ECMQmlModule.

De aanroep van ecm_add_qml_module() was hier gebruikt om het uitvoerbare doel van de traditionele C++ broncode te wijzigen en het om te zetten in iets dat QML-bestanden en C++ broncode kan accepteren die toegankelijk is vanuit QML in wat wordt genoemd het uitvoerbare bestand als achtergronddoel gebruiken voor een QML-module. Dit betekent dat de QML-bestanden direct worden uitgevoerd als onderdeel van de toepassing, wat vaak het geval is voor toepassingen.

U kunt ook een aparte QML-module aanmaken die niet het uitvoerbare bestand gebruikt als doel op de achtergrond met ecm_add_qml_module(). In zo'n geval zou u een bibliotheekdoel moeten aanmaken met add_library() en het koppelen aan een bestaand uitvoerbaardoel met target_link_libraries() en bovendien het installeren van de bibliotheek met install() dan moet u de QML-module afmaken met ecm_finalize_qml_module() het kan dus twee bestanden genereren: qmldir en qmltypes. Deze bestanden worden gebruikt door QtQuick toepassingen om aparte QML-modules te vinden.

De methode voor aanmaken van een aparte QML-module wordt beter geïllustreerd in Aparte bestanden gebruiken.

Dit zij toevoegingen geleverd door extra-cmake-modules om het gebruik van Qt declarative registration (de vervanging van Qt hulpbronbestanden) gemakkelijker te maken.

De documentatie voor alle drie commando's is te vinden in de extra-cmake-modules API voor ECMQmlModule.

src/components/CMakeLists.txt

In de instructie over hoe uw code in separate bestanden te splitsen, werd een nieuw CMake-bestand geïntroduceerd om separate QML modules mogelijk te maken:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
add_library(kirigami-hello-components)

ecm_add_qml_module(kirigami-hello-components
    URI "org.kde.tutorial.components"
    GENERATE_PLUGIN_SOURCE
)

ecm_target_qml_sources(kirigami-hello-components
    SOURCES
    AddDialog.qml
    KountdownDelegate.qml
)

ecm_finalize_qml_module(kirigami-hello-components)

install(TARGETS kirigami-hello-components ${KDE_INSTALL_TARGETS_DEFAULT_ARGS})

Om dit bestand door CMake te laten lezen is het aanroepen (call) van add_subdirectory() vereist in src/CMakeLists.txt dat er naar verwijst.

We creëren een nieuw doel kirigami-hello-components genaamd en zetten het om naar een QML module met de hulp van ecm_add_qml_module() met de importnaam org.kde.tutorial.components en voegen daar de relevante QML-bestanden toe.

De aanroep naar add_library() genereert een nieuw doel genaamd kirigami-hello-components. Dit doel zal zijn eigen set broncode-bestanden, QML-bestanden, zijn eigen bibliotheken (libraries) aankoppelen enz., maar het moet nog wel gelinkt worden naar de executable, ook moet het nadat het gecompileerd is nog gelinkt worden naar de executable gecreëerd aan de hand van src/CMakeLists.txt. Dit wordt gedaan door de doelnaam toe te voegen aan de lijst van bibliotheken (libraries) die gelinkt worden met de executable in target_link_libraries().

De aanroep naar ecm_add_qml_module() stelt de bibliotheek zodanig in dat het net zoals eerder QML-bestanden accepteert, maar deze keer moeten we [GENERATE_PLUGIN_SOURCE] (https://api.kde.org/ecm/module/ECMQmlModule.html) gebruiken. Als het programma wordt gebruikt als een achtergronddoel (zoals bij kirigami-hello) dan hoeft het geen plugin code te generen omdat het in het uitvoerbare bestand is ingebouwd; bij separate QML modules zoals kirigami-hello-componenten is de plugin code noodzakelijk.

Upstream Qt's qt_add_qml_module() genereert standaard samen met de QML module een plugin, maar KDE's ecm_add_qml_module() doet dat standaard niet vanwege de backwards compatibility.

Daarnaast is het noodzakelijk om de QML modules te scheiden om het doel af te maken. Dit houd hoofdzakelijk is dat CMake twee bestanden genereert, [qmldir en qmltypes] (https://doc.qt.io/qt-6/qtqml-modules-qmldir.html), die de QML modules beschrijven die we hebben en hun symbolen exporteren om in de bibliotheek te gebruiken. Ze zijn belangrijk bij het installeren van uw programma zodat het uitvoerbare bestand voor elke module de QML-bestanden kan vinden, zodat ze automatisch aan het doel kunnen worden toegevoegd.

Het doel kan dan gewoon net zoals vroeger geïnstalleerd worden.

De volgende keer dat u meer QML-bestanden moet toevoegen, onthoud om ze in te voegen in dit bestand. C++ bestanden die de QML_ELEMENT sleutelwoorden gebruiken die we veel later zullen zien in de handleiding kunnen hier ook toegevoegd worden met target_sources(). U kunt uw code logisch scheiden door meer QML-modulen aan te maken door naar behoefte verschillende zaken te importeren.

Deze opzet zal bruikbaar zijn bij ontwikkelen van de meeste Kirigami toepassingen.