Розбираємося із CMakeLists

Ознайомлюємося із тим, як працюють файли CMakeLists.txt

CMake

У нашому вступному підручнику ми використали CMake як систему збирання для нашої програми, але приділили увагу лише одному з файлів CMakeLists.txt. Цей розділ ми присвятимо докладнішому вивченню принципів його роботи.

CMake є корисним, оскільки за його допомогою ми можемо автоматизувати більшу частину роботи, яку слід виконати до компіляції.

Кореневий CMakeLists.txt

Можливо, ви пам'ятаєте цей файл 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
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)

Перший рядок, cmake_minimum_required(), встановлює версію CMake, яку буде викликано.

Після цього project(kirigami-tutorial) визначає назву проєкту.

Далі, маємо розділ, у якому ми включимо декілька потрібних параметрів CMake і KDE за допомогою extra-cmake-modules. Вони забезпечують роботу корисних допоміжних інструментів:

  • У KDEInstallDirs передбачено зручні змінні, зокрема ${KDE_INSTALL_TARGETS_DEFAULT_ARGS}, ${KDE_INSTALL_QMLDIR}, ${KDE_INSTALL_BINDIR} та ${KDE_INSTALL_LIBDIR}.
  • KDECMakeSettings містить речі, подібні до CMAKE_AUTORCC ON, ціль uninstall, якою можна скористатися за допомогою cmake --build build/ --target uninstall, і ENABLE_CLAZY.
  • KDECompilerSettings містить мінімальний стандарт C++, прапорці компіляції, зокрема -pedantic, і найкращі стандартні макроси, зокрема -DQT_NO_CAST_FROM_ASCII для встановлення вимог щодо явних перетворень, зокрема QStringLiteral().
  • ECMFindQmlModule містить спосіб забезпечення виявлення динамічної залежності QML під час компіляції.
  • ECMQmlModule містить команди CMake, зокрема ecm_add_qml_module() і ecm_target_qml_sources().

Наступний розділ є важливим, оскільки у ньому визначають, які залежності ми використовуватимемо під час компіляції. Погляньмо на початок:

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() знаходить і завантажує зовнішню бібліотеку та її компоненти.
  • REQUIRED повідомляє CMake, що програма має повідомити про помилку, якщо пакунок не буде знайдено.
  • COMPONENTS — параметр, який передує специфічним компонентам бібліотеки, які ми включатимемо.
  • Кожне слово після COMPONENTS вказує на компонент бібліотеки.

Рядок встановлення повідомляє CMake, що слід встановити стільничний файл до ${KDE_INSTALL_APPDIR}, що у Linux буде перенесено як $XDG_DATA_DIRS/applications, зазвичай /usr/share/applications, а у Windows буде перенесено як C:/Program Files/${PROJECT_NAME}/bin/data/applications:

32
add_subdirectory(src)

Останній рядок дозволяє CMake вивести список пакунків, які було знайдено, і призводить до негайного припинення компіляції, якщо станеться помилка:

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

А вище, add_subdirectory(src) вказує CMake каталог src/, де програма знайде інший файл 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})

Перший файл обробляв метадані і пошук бібліотек, цей файл містить команди обробки залежностей та встановлення програми. У ньому містяться такі виклики CMake:

  • add_executable() створює виконуваний файл, яким ми користуватимемося для запуску результатів нашого проєкту.
  • ecm_add_qml_module() створює ціль — модуль QML, — доступ до якої здійснюватиметься за допомогою імпортування «org.kde.tutorial».
  • target_sources() додає файли початкового коду мовою C++ до виконуваної цілі проєкту.
  • ecm_target_qml_sources() додає до модуля файли QML.
  • target_link_libraries() пов'язує ціль із бібліотеками C++, які використовуються у нашому коді для створення виконуваного файла. Kirigami не включено сюди, оскільки ми використовуємо лише її модуль QML.
  • install() встановлює виконуваний файл у системі.

Документацію з двох команд ECM можна знайти у програмному інтерфейсі extra-cmake-modules для ECMQmlModule.

Виклик ecm_add_qml_module() тут було використано, щоб модифікувати традиційну ціль збирання початкового коду C++, виконуваний файл, і перетворити її на щось, що може приймати файли QML, і початковий код C++ буде доступним з QML у межах того, що називається використання виконуваного файла як резервної цілі для модуля QML. Це означає, що файли QML запускаються безпосередньо як частина програми, що часто буває для програм.

Ви також можете створити окремий модуль QML, який не використовує виконуваний файл як резервну ціль, за допомогою ecm_add_qml_module(). У цьому випадку ви могли б створити ціль-бібліотеку з використанням add_library(), скомпонувати її із наявною ціллю — виконуваним файлом — за допомогою target_link_libraries(), і на додачу до встановлення бібліотеки за допомогою install() вам довелося б створити остаточну версію модуля QML за допомогою ecm_finalize_qml_module(), так, щоб він міг створити два файли: qmldir і qmltypes. Ці файли програми QtQuick використовують для пошуку окремих модулів QML.

Спосіб створення окремого модуля QML краще описано у розділі щодо використання окремих файлів.

Ці доповнення, які містяться у extra-cmake-modules, призначено для спрощення використання декларативної реєстрації Qt (заміни файлів ресурсів Qt).

Документацію до усіх трьох команд ECM можна знайти у програмному інтерфейсі extra-cmake-modules для ECMQmlModule.

src/components/CMakeLists.txt

У підручнику щодо того, як поділити ваш код на окремі файли було впроваджено новий файл CMake для забезпечення роботи окремих модулів QML:

 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
    AddEditDialog.qml
    KountdownDelegate.qml
)

ecm_finalize_qml_module(kirigami-hello-components)

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

Щоб цей файл прочитала CMake слід додати виклик add_subdirectory() до src/CMakeLists.txt, який вказуватиме сюди.

Ми створюємо нову ціль із назвою kirigami-hello-components, а потім перетворюємо її на модуль QML за допомогою using ecm_add_qml_module() із назвою імпортування org.kde.tutorial.components і додаємо пов'язані файли QML.

Виклик add_library() створює нову ціль із назвою kirigami-hello-components. Ця ціль матиме власний набір файлів початкового коду, файлів QML, компонує власні бібліотеки тощо, але її доведеться скомпонувати з виконуваним файлом, а коли її буде скомпільовано, її слід скомпонувати із виконуваним файлом, який створено у src/CMakeLists.txt. Ця мета досягається додаванням назви цілі до списку бібліотек, які буде скомпоновано до виконуваного файла у target_link_libraries().

Виклик ecm_add_qml_module() змінює бібліотеку так, щоб дозволити їй приймати файли QML, як і раніше, але цього разу нам слід скористатися GENERATE_PLUGIN_SOURCE. Якщо як резервну ціль використано виконуваний файл (як у kirigami-hello), вона не потребує створення коду додатка, оскільки її буде зібрано до виконуваного файла; із окремими модулями QML, зокрема kirigami-hello-components, код додатка є обов'язковим.

Основна команда Qt qt_add_qml_module() типово створює додаток разом із модулем QML, а ecm_add_qml_module() KDE типово не робить цього з міркувань зворотної сумісності.

Іншою річчю, яка потрібна для окремих модулів QML, є завершення цілі. Це, в основному, означає, що CMake створює два файли, qmldir і qmltypes, які описують наші модулі QML та експортує їхні символи для використання у бібліотеці. Вони є важливими при встановленні вашої програми, щоб запущений виконуваний файл міг знайти місце, де перебувають файли кожного модуля QML, щоб їх можна було автоматично додати до цілі.

Далі ви можете встановити ціль, як і раніше.

Наступного разу, коли вам буде потрібно додати файли QML, не забудьте включити їх до цього файла. Файли C++, які використовують ключове слово QML_ELEMENT, які ми побачимо набагато пізніше у цьому підручнику, також можна додати саме тут за допомогою target_sources(). Якщо потрібно, ви можете логічно розділити ваш код створенням додаткових модулів QML із різними імпортуваннями.

Такими налаштуваннями можна скористатися для створення більшості програм на основі Kirigami.