Compreender o CMakeLists

Tentar compreender como funcionam os ficheiros CMakeLists.txt

CMake

No nosso tutorial introdutório, usámos o CMake como sistema de compilação para a nossa aplicação, mas só prestámos atenção a um dos nossos ficheiros CMakeLists.txt. Aqui, vamos ver como funcionam os CMakeLists com um pouco mais de detalhe.

O CMake é útil porque nos permite automatizar muitas das coisas que precisam de ser feitas antes da compilação.

CMakeLists.txt

Poderá recordar este ficheiro CMakeLists.txt do primeiro tutorial:

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

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)

The first line, cmake_minimum_required() sets the version of CMake we will be calling.

After that, project(kirigami-tutorial) defines the name of the project.

Then we get to a section where we include a number of necessary CMake and KDE settings by using extra-cmake-modules. They provide a set of useful utilities:

  • KDEInstallDirs provides convenience variables such as ${KDE_INSTALL_TARGETS_DEFAULT_ARGS}, ${KDE_INSTALL_QMLDIR}, ${KDE_INSTALL_BINDIR} and ${KDE_INSTALL_LIBDIR}.
  • KDECMakeSettings provides things like CMAKE_AUTORCC ON, an uninstall target that can be used with cmake --build build/ --target uninstall, and ENABLE_CLAZY.
  • KDECompilerSettings provides a minimum C++ standard, compiler flags such as -pedantic, and best practices macros like -DQT_NO_CAST_FROM_ASCII to require explicit conversions such as QStringLiteral().
  • ECMFindQmlModule provides a way to ensure a runtime QML dependency is found at compile time.
  • ECMQmlModule provides CMake commands like ecm_add_qml_module() and ecm_target_qml_sources().

The following section is important, because it specifies which dependencies we'll be bringing in at compile time. Let's look at the first:

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
)
  • find_package() finds and loads the external library and its components.
  • REQUIRED tells CMake to exit with an error if the package cannot be found.
  • O COMPONENTS é um parâmetro que antecede os componentes específicos da plataforma que iremos incluir.
  • Each word after COMPONENTS refers to a specific component of the library.

The install line instructs CMake to install the desktop file in ${KDE_INSTALL_APPDIR}, which on Linux translates to $XDG_DATA_DIRS/applications, usually /usr/share/applications, and on Windows translates to C:/Program Files/${PROJECT_NAME}/bin/data/applications:

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

The final line lets CMake print out which packages it has found, and it makes compilation fail immediately if it encounters an error:

34
feature_summary(WHAT ALL INCLUDE_QUIET_PACKAGES FATAL_ON_MISSING_REQUIRED_PACKAGES)

And above that, add_subdirectory(src) points CMake into the src/ directory, where it finds another CMakeLists.txt file:

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

install(TARGETS kirigami-hello ${KDE_INSTALL_TARGETS_DEFAULT_ARGS})

While the first file handled metadata and finding libraries, this one will consist of handling dependencies and installing the application. It has the following CMake calls:

  • add_executable() creates the executable target we will use to run our project.
  • ecm_add_qml_module() creates a QML module target that will be accessible via the "org.kde.tutorial" import.
  • target_sources() adds C++ source files to the executable target.
  • ecm_target_qml_sources() adds QML files to the module.
  • target_link_libraries() links the C++ libraries used in our code to our executable. Kirigami is not included here because we are using only its QML module.
  • install() installs the executable to the system.

The documentation for the two ECM commands can be found in the extra-cmake-modules API for ECMQmlModule.

The call to ecm_add_qml_module() was used here to modify the traditional C++ source code executable target and turn it into something that can accept QML files and C++ source code that is accessible from QML in what is called using the executable as backing target for a QML module. This means the QML files are run directly as part of the application, which is often the case for applications.

You may also create a separate QML module that does not use the executable as backing target using ecm_add_qml_module(). In this case, you'd create a library target using add_library(), link it to an existing executable target using target_link_libraries(), and in addition to installing the library with install() you will need to finalize the QML module with ecm_finalize_qml_module() so it can generate two files: qmldir and qmltypes. These files are used by QtQuick applications to find separate QML modules.

The method for creating a separate QML module is better exemplified in Using separate files.

These are additions provided by extra-cmake-modules to make the use of Qt declarative registration (the replacement to Qt resource files) easier.

The documentation for all three commands can be found in the extra-cmake-modules API for ECMQmlModule.

Next time you need to add more QML files, remember to include them in this file. C++ files that use the QML_ELEMENT keyword which we will see much later in the tutorial can also be added here using target_sources(). You can logically separate your code by creating more QML modules with different imports as needed.

Esta configuração será útil quando estiver a desenvolver a maioria das aplicações de Kirigami.