Entendiendo CMakeLists

Familiarizarse con el funcionamiento de los archivos CMakeLists.txt

CMake

En nuestro tutorial de introducción hemos usado CMake como sistema de compilación para nuestra aplicación, aunque solo hemos prestado más atención a uno de nuestros archivos CMakeLists.txt. Aquí vamos a profundizar un poco más sobre cómo funciona.

CMake es útil porque nos permite automatizar la mayoría de las cosas necesarias que hay que hacer antes de compilar.

CMakeLists.txt

Es posible que recuerde este archivo CMakeLists.txt del primer 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().

La siguiente sección es importante, ya que especifica las dependencias necesarias durante la compilación. Veamos la primera:

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.
  • COMPONENTS es un parámetro que precede a los componentes específicos de la infraestructura que vamos a 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 configuración será útil para desarrollar la mayoría de las aplicaciones de Kirigami.