Nova Flow OS
KDE Developer Platform
KDE Developer Platform
  • KDE Developer Platform
    • Getting started
      • Building KDE software
        • KDE software
        • Where to find the development team
        • Learning more
        • Choose what to work on
        • Source code cross-referencing
        • Installing build dependencies
        • Set up a development environment
        • Building KDE software with kdesrc-build
        • Basic troubleshooting
        • Tips and tricks
        • IDE Configuration
          • Setting up an IDE for KDE development
          • Visual Studio Code
          • Qt Creator
          • Kate
          • KDevelop
          • CLion
          • Sublime Text
        • Building KDE software manually
        • Building KDE software with distrobox and podman
      • Kirigami
        • KDE is ours
        • Setting up and getting started
        • Explaining pages
        • Layouts, ListViews, and Cards
        • Adding actions
        • Adding a dialog
        • Using separate files
        • Next steps
        • Colors and themes in Kirigami
        • Typography
        • Actions based components
        • Page rows and page stacks
        • Scrollable pages and list views
        • Cards
        • Drawers
        • Chips
        • Dialog types
        • Controls and interactive elements
        • Form layouts
        • Inline messages
        • Action toolbars
        • Progress bars and indicators
        • List views
        • Understanding CMakeLists
        • Figuring out main.cpp
        • Connect logic to your QML user interface
        • Connect models to your QML user interface
        • About page
        • Introduction to Kirigami Addons
        • FormCard About pages
        • Form delegates in your settings pages
      • KXmlGui
        • Getting started with KXmlGui
        • Hello World!
        • Creating the main window
        • Using actions
        • Saving and loading
        • Command line interface
      • Python with Kirigami
        • Apps with QML and Python
        • Your first Python + Kirigami application
        • Creating a Python package
        • Creating a Flatpak
      • Common programming mistakes
      • Adding a new KDE project
    • Features
      • Icons
      • Configuration
        • The KConfig Framework
        • Introduction to KConfig
        • Using KConfig XT
        • KDE Frameworks 6 porting guide
        • Settings module (KCM) development
        • KConfigDialog
      • D-Bus
        • What is D-Bus practically useful for?
        • Introduction to D-Bus
        • Accessing D-Bus interfaces
        • Intermediate D-Bus
        • Creating D-Bus interfaces
        • Using custom types with D-Bus
        • D-Bus autostart services
      • Create your own mouse cursor theme
      • Session management
      • Archives
      • Desktop file
      • KAuth
        • Privilege Escalation
        • Using actions in your applications
      • KIdleTime
      • Akonadi: personal information management
        • Debugging Akonadi Resources
        • Using Akonadi in applications
      • Concurrent programming
      • Solid
      • Sonnet
    • Plasma themes and plugins
      • Getting started
      • Plasma Widget tutorial
        • How to create a plasmoid
        • Setup
        • Porting Plasmoids to KF6
        • Testing
        • QML
        • Plasma's QML API
        • Widget Properties
        • Configuration
        • Translations / i18n
        • Examples
        • C++ API
      • KWin Effects
      • Plasma Desktop scripting
        • Javascript Interaction With Plasma Shells
        • Templates
        • Examples
        • API documentation
        • Configuration keys
      • Plasma Style tutorial
        • Creating a Plasma Style quickstart
        • Understanding Plasma Styles
        • SVG elements and Inkscape
        • Background SVG format
        • System and accent colors
        • Theme elements reference
        • Porting themes to Plasma 5
        • Porting themes to Plasma 6
      • Aurorae window decorations
      • KWin scripting tutorial
        • Quick start
        • KWin scripting API
      • Wallpapers
      • Plasma comic
        • Tutorial
        • Testing and debugging
        • Examples
      • Create a custom Window Switcher
      • KRunner C++ Plugin
        • Basic Anatomy of a Runner
        • KRunner metadata format
    • Applications
      • Creating sensor faces
      • Dolphin
        • Creating Dolphin service menus
      • Kate
        • Kate plugin tutorial
      • KMines
        • Making a KMines theme
      • Writing tests
        • Appium automation testing
    • Packaging
      • Android
        • KDE on Android
        • Building applications for Android
        • Packaging and publishing applications for Android
        • Publishing on Google Play
          • Introduction
          • Packaging your app
          • Adding your app to Google Play
          • Publishing your app
          • Releasing new versions of old apps
        • Porting applications to Android
          • Basic porting
          • Making applications run well on Android
          • Metadata
      • Windows
        • Packaging and publishing applications for Windows
        • Publish your app in the Microsoft Store
          • Packaging your app for the Microsoft Store
          • Submitting your app to the Microsoft Store
      • Plasma Mobile
        • KDE on mobile devices
        • Porting a new device to Plasma Mobile
        • KDE Telephony stack
          • General Overview
          • Kernel layer
          • System daemons
            • General overview
            • Developing Telephony functionality
            • ModemManager Telephony functions
          • Session daemons
          • QML declarative plugin layer
          • KDE application layer
        • Execute applications
      • Distributing KDE software as Flatpak
        • Your first Flatpak
        • Extending your package
        • Nightly Flatpaks and Flathub
        • Testing your Flatpak
    • System administration
      • Shell scripting with KDE dialogs
      • Kiosk: Simple configuration management for large deployment
        • Abstract
        • Introduction to Kiosk
        • Kiosk keys
    • Contribute to the documentation
    • About
      • Readme
      • License
        • Creative Commons Attribution-ShareAlike 4.0 International
        • GNU General Public License 3.0 or later
Powered by GitBook
On this page
  • CMake
  • The root CMakeLists.txt
  • src/CMakeLists.txt
  • src/components/CMakeLists.txt
  1. KDE Developer Platform
  2. Getting started
  3. Kirigami

Understanding CMakeLists

Getting to grips with how CMakeLists.txt files work

PreviousList viewsNextFiguring out main.cpp

Last updated 8 months ago

CMake

In our introductory tutorial, we used as the build system for our application, but we only really paid close attention to one of our CMakeLists.txt files. Here, we're going to go over how it works in a bit more detail.

CMake is useful because it allows us to automate much of the stuff that needs to be done before compilation.

The root CMakeLists.txt

You might remember this CMakeLists.txt file from the first tutorial:

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)

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.

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

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

find_package(KF6 REQUIRED COMPONENTS
    Kirigami
    I18n
    CoreAddons
    QQC2DesktopStyle
  • REQUIRED tells CMake to exit with an error if the package cannot be found.

  • COMPONENTS is a parameter that precedes the specific components of the framework we will include.

  • Each word after COMPONENTS refers to a specific component of the library.

Note

find_package(KF6 COMPONENTS Kirigami)

Pay close attention to your included components, as omitting ones used in our code will stop our application from compiling.

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:

add_subdirectory(src)

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

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

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

src/CMakeLists.txt

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

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:

  • ecm_add_qml_module() creates a QML module target that will be accessible via the "org.kde.tutorial" import.

  • ecm_target_qml_sources() adds QML files to the module.

Note

These libraries should match the components that we included in our previous CMakeLists.txt file, otherwise these components will not be included and our application won't compile.

src/components/CMakeLists.txt

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

The requirement for this file to be read by CMake is adding a call to add_subdirectory() in the src/CMakeLists.txt pointing to it.

You can then just install the target as before.

This setup will be useful when developing most Kirigami apps.

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

provides convenience variables such as ${KDE_INSTALL_TARGETS_DEFAULT_ARGS}, ${KDE_INSTALL_QMLDIR}, ${KDE_INSTALL_BINDIR} and ${KDE_INSTALL_LIBDIR}.

provides things like CMAKE_AUTORCC ON, an uninstall target that can be used with cmake --build build/ --target uninstall, and ENABLE_CLAZY.

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

provides a way to ensure a runtime QML dependency is found at compile time.

provides CMake commands like ecm_add_qml_module() and ecm_target_qml_sources().

finds and loads the external library and its components.

If you are looking to add any components listed in the to your application, you may check the right sidebar for how to add the component with CMake. For instance, for , you will find something like find_package(KF6Kirigami), which with the addition of ECM becomes:

creates the executable target we will use to run our project.

adds C++ source files to the executable target.

links the C++ libraries used in our code to our executable. Kirigami is not included here because we are using only its QML module.

installs the executable to the system.

The documentation for the two ECM commands can be found in the .

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 . 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 , 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 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 .

These are additions provided by extra-cmake-modules to make the use of (the ) easier.

The documentation for all three commands can be found in the .

In the tutorial about , a new CMake file was introduced to allow for separate QML modules:

We create a new target called kirigami-hello-components and then turn it into a QML module using under the import name org.kde.tutorial.components and add the relevant QML files.

The call to generates a new target called kirigami-hello-components. This target will have its own set of source code files, QML files, link its own libraries and so on, but it needs to be linked to the executable, but once it is compiled it needs to be linked to the executable created in the src/CMakeLists.txt. This is done by adding the target name to the list of libraries that will be linked to the executable in target_link_libraries().

The call to ecm_add_qml_module() changes the library to allow it to accept QML files as before, but this time we need to use . When the executable is used as a backing target (like with kirigami-hello) it doesn't need to generate plugin code since it's built into the executable; with separate QML modules like kirigami-hello-components the plugin code is necessary.

Upstream Qt's by default generates a plugin together with the QML module, but KDE's ecm_add_qml_module() by default does not for backwards compatibility.

Another thing that is necessary for separate QML modules is to finalize the target. This mainly means CMake generates two files, , which describe the QML modules we have and exports their symbols for use in the library. They are important when installing your application so that the executable being run is able to find where the QML files for each module are, so they are automatically added to the target.

Next time you need to add more QML files, remember to include them in this file. C++ files that use the 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.

CMake
extra-cmake-modules
KDEInstallDirs
KDECMakeSettings
KDECompilerSettings
ECMFindQmlModule
ECMQmlModule
find_package()
KDE API documentation
Kirigami
add_executable()
target_sources()
target_link_libraries()
install()
extra-cmake-modules API for ECMQmlModule
using the executable as backing target for a QML module
add_library()
ecm_finalize_qml_module()
Using separate files
Qt declarative registration
replacement to Qt resource files
extra-cmake-modules API for ECMQmlModule
how to split your code into separate files
ecm_add_qml_module()
add_library()
GENERATE_PLUGIN_SOURCE
qt_add_qml_module()
qmldir and qmltypes
QML_ELEMENT