Skip to main content
Passa al contenuto

Capire CMakeLists

Ragioniamo su come funziona CMakeLists.txt

CMake

Nel nostro tutorial introduttivo, abbiamo utilizzato CMake come sistema di compilazione per la nostra applicazione, ma abbiamo prestato molta attenzione solo a uno dei nostri file "CMakeLists.txt". Qui, esamineremo come funziona in modo un po' più dettagliato.

CMake è utile perché ci permette di automatizzare molte delle cose che bisogna fare prima della compilazione.

La radice CMakeLists.txt

Potresti ricordare questo file CMakeLists.txt dalla prima esercitazione:

 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)

La prima riga, cmake_minimum_required() imposta la versione di CMake che chiameremo.

Successivamente, project(kirigami-tutorial) definisce il nome del progetto.

Quindi arriviamo a una sezione in cui includiamo una serie di impostazioni necessarie di CMake e KDE utilizzando extra-cmake-modules. Forniscono una serie di utili utilità:

  • KDEInstallDirs fornisce variabili utili come ${KDE_INSTALL_TARGETS_DEFAULT_ARGS}, ${KDE_INSTALL_QMLDIR}, ${KDE_INSTALL_BINDIR} e ${KDE_INSTALL_LIBDIR}.
  • KDECMakeSettings fornisce cose come CMAKE_AUTORCC ON, un target uninstall che può essere utilizzato con cmake --build build/ --target uninstall e ENABLE_CLAZY.
  • KDECompilerSettings fornisce uno standard C++ minimo, flag del compilatore come -pedantic e macro di best practice come -DQT_NO_CAST_FROM_ASCII per richiedere conversioni esplicite come QStringLiteral().
  • ECMFindQmlModule fornisce un modo per garantire che venga trovata una dipendenza QML di runtime in fase di compilazione.
  • ECMQmlModule fornisce comandi CMake come ecm_add_qml_module() e ecm_target_qml_sources().

La sezione seguente è importante perché specifica quali dipendenze introdurremo in fase di compilazione. Diamo un'occhiata al primo:

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() trova e carica la libreria esterna e i suoi componenti.
  • «REQUIRED» indica a CMake di uscire con un errore se non è possibile trovare il pacchetto.
  • `COMPONENTS`` è un parametro che precede i componenti specifici del framework che includeremo.
  • Ogni parola dopo "COMPONENTI" si riferisce a un componente specifico della libreria.

La riga di installazione indica a CMake di installare il file desktop in ${KDE_INSTALL_APPDIR}, che su Linux si traduce in $XDG_DATA_DIRS/applications, solitamente /usr/share/applications, e su Windows si traduce in C:/Program Files/${PROJECT_NAME}/bin/data/applications:

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

L'ultima riga consente a CMake di stampare quali pacchetti ha trovato e fa sì che la compilazione fallisca immediatamente se incontra un errore:

36
feature_summary(WHAT ALL INCLUDE_QUIET_PACKAGES FATAL_ON_MISSING_REQUIRED_PACKAGES)

E soprattutto, "add_subdirectory(src)" punta CMake nella directory "src/", dove trova un altro file "CMakeLists.txt".

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})

Mentre il primo file gestiva i metadati e la ricerca delle librerie, questo consisterà nella gestione delle dipendenze e nell'installazione dell'applicazione. Ha le seguenti chiamate CMake:

  • add_executable() crea la destinazione eseguibile che utilizzeremo per eseguire il nostro progetto.
  • ecm_add_qml_module() crea un modulo QML di destinazione che sarà accessibile tramite l'importazione "org.kde.tutorial".
  • target_sources() aggiunge i file sorgente C++ alla destinazione eseguibile.
  • ecm_target_qml_sources() aggiunge i file QML al modulo.
  • target_link_libraries() collega le librerie C++ utilizzate nel nostro codice al nostro eseguibile. Kirigami non è incluso qui perché stiamo utilizzando solo il suo modulo QML.
  • install() installa l'eseguibile nel sistema.

La documentazione per i due comandi ECM può essere trovata nell'API extra-cmake-modules per ECMQmlModule.

La chiamata a ecm_add_qml_module() è stata utilizzata qui per modificare la tradizionale destinazione eseguibile del codice sorgente C++ e trasformarla in qualcosa che può accettare file QML e codice sorgente C++ accessibile da QML in quello che viene chiamato usando l'eseguibile come destinazione di supporto per un QML modulo. Ciò significa che i file QML vengono eseguiti direttamente come parte dell'applicazione, come spesso accade per le applicazioni.

Puoi anche creare un modulo QML separato che non utilizzi l'eseguibile come destinazione di supporto utilizzando ecm_add_qml_module(). In questo caso, creerai una libreria di destinazione utilizzando add_library(), la collegheresti a una destinazione eseguibile esistente utilizzando target_link_libraries() e oltre a installare la libreria con install() dovrai finalizzare il modulo QML con ecm_finalize_qml_module() in modo che possa generare due file: qmldir e qmltypes. Questi file vengono utilizzati dalle applicazioni QtQuick per trovare moduli QML separati.

Il metodo per creare un modulo QML separato è meglio esemplificato in Utilizzo di file separati.

Queste sono aggiunte fornite da extra-cmake-modules per rendere più semplice l'uso della registrazione dichiarativa Qt (la sostituzione ai file di risorse Qt).

La documentazione per tutti e tre i comandi può essere trovata nell'API extra-cmake-modules per ECMQmlModule.

src/components/CMakeLists.txt

Nel tutorial su come dividere il codice in file separati, è stato introdotto un nuovo file CMake per consentire moduli QML separati:

 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})

Il requisito affinché questo file venga letto da CMake è l'aggiunta di una chiamata a "add_subdirectory()" nel file "src/CMakeLists.txt" che punta ad esso.

Creiamo un nuovo target chiamato kirigami-hello-components e poi lo trasformiamo in un modulo QML utilizzando ecm_add_qml_module() con il nome di importazione org.kde.tutorial.components e aggiungiamo i file QML rilevanti.

La chiamata a add_library() genera un nuovo target chiamato "kirigami-hello-components". Questo target avrà il proprio set di file di codice sorgente, file QML, collegherà le proprie librerie e così via, ma deve essere collegato all'eseguibile, ma una volta compilato deve essere collegato all'eseguibile creato in "src/CMakeLists.txt". Questo viene fatto aggiungendo il nome di destinazione all'elenco delle librerie che verranno collegate all'eseguibile in target_link_libraries().

La chiamata a ecm_add_qml_module() modifica la libreria per consentirle di accettare file QML come prima, ma questa volta dobbiamo usare GENERATE_PLUGIN_SOURCE. Quando l'eseguibile viene utilizzato come destinazione di supporto (come con kirigami-hello) non è necessario generare codice plugin poiché è integrato nell'eseguibile; con moduli QML separati come kirigami-hello-components è necessario il codice del plugin.

Upstream qt_add_qml_module() di Qt genera per impostazione predefinita un plugin insieme al modulo QML, ma ecm_add_qml_module() di KDE per impostazione predefinita non lo fa per la compatibilità con le versioni precedenti.

Un'altra cosa necessaria per i moduli QML separati è finalizzare l'obiettivo. Ciò significa principalmente che CMake genera due file, qmldir e qmltypes, che descrivono i moduli QML di cui disponiamo ed esporta i loro simboli da utilizzare nella libreria. Sono importanti durante l'installazione dell'applicazione in modo che l'eseguibile in esecuzione sia in grado di trovare dove si trovano i file QML per ciascun modulo, in modo che vengano aggiunti automaticamente alla destinazione.

È quindi possibile installare semplicemente la destinazione come prima.

La prossima volta che dovrai aggiungere altri file QML, ricordati di includerli in questo file. I file C++ che utilizzano la parola chiave QML_ELEMENT che vedremo molto più avanti nel tutorial possono anche essere aggiunti qui utilizzando target_sources(). Puoi separare logicamente il tuo codice creando più moduli QML con importazioni diverse secondo necessità.

Questa impostazione sarà utile quando svilupperemo la maggior parte dei programmi con Kirigami.