ECM-MODULES(7)
πΈ ECM-MODULES(7) β the bestie edition πΈ
okay so this is the ECM-MODULES(7) page from Extra CMake Modules, and honestly? it's a lot, but we're gonna get through it together π«Ά
β¨ INTRODUCTION β¨
Extra CMake Modules (aka ECM) hands you a whole little collection of modules that give CMake scripts useful functions. And bestie, ECM actually ships three flavors of modules you can use from CMake scripts:
- the ones that extend what
find_packagecan do β those live over inecm-find-modules(7) - the ones that give you standard settings for software produced by the KDE community β those are in
ecm-kde-modules(7) - and then the rest β the general-use macros and functions for CMake scripts β which are documented right here π
To actually use any of these, you gotta tell CMake to find the ECM package, and then add either ${ECM_MODULE_PATH} or ${ECM_MODULE_DIR} to the CMAKE_MODULE_PATH variable, like:
find_package(ECM REQUIRED NO_MODULE)
set(CMAKE_MODULE_PATH ${ECM_MODULE_DIR})
Using ${ECM_MODULE_PATH} will also make the find modules and the KDE modules available. the more you know π«
ngl there are also toolchain modules, documented in ecm-toolchains(7) β but those are used by the users building the software, not by developers writing CMake scripts. different audience, different vibe.
π ALL MODULES π
π§ CheckAtomic
Checks whether your compiler supports std:atomic right out of the box, or if libatomic is needed for atomic support. If it is needed, libatomic gets added to CMAKE_REQUIRED_LIBRARIES. so after running CheckAtomic you can just use std:atomic β slay β¨
Since 5.75.0.
π€ ECMAddAndroidApk
Functions for creating Android APK packages using Qt6's androiddeployqt tool, plus the associated Fastlane metadata. iconic behavior honestly.
ecm_add_android_apk(<target>
[ANDROID_DIR <dir>]
[PACKAGE_NAME <name>]
# TODO extra args?
)
Creates an Android APK for the given target.
If ANDROID_DIR is given, the Android manifest file β as well as any potential Gradle build system files or Java/Kotlin source files β are taken from that directory. If it's not set, the standard template shipped with Qt6 is used, which is usually not what you want for production applications. (likeβ¦ bestie, please set it π)
If PACKAGE_NAME is given, it's used as the name for the Android APK. If not set, the target name is used. Since 6.10.0.
Using this function creates a build target called create-apk-<target> which will run androiddeployqt to produce an (unsigned) APK, and also convert Appstream application metadata (if present) into the Fastlane format used by F-Droid and Play store automation. π
There's also a create-apk convenience target created for you, which will build all APKs defined in a project. one target to rule them all π
When building for another platform than Android, this function does nothing. it's giving "no thoughts, head empty" and we respect it.
The following variables impact the behavior:
ECM_ADDITIONAL_FIND_ROOT_PATHβ see the documentation in the Android toolchain file.ECM_APK_STAGING_ROOT_PATHβ for use with Craft's image directory. If set, this is used as the source for all content of the APK rather than the search paths used for building. This lets you separate e.g. development files from what ends up in the APK.
Since 6.0.0.
For automatically deriving APK versions from CMake, this creates an ecm-version.gradle file which defines the following variables:
ecmVersionName: set toPROJECT_VERSIONecmVersionCode: derived from the current time in local builds, and fromCI_PIPELINE_CREATED_ATin builds from Gitlab pipelines. This ensures a strictly increasing version code, as well as synchronized version codes for APKs of multiple architectures when built in a Gitlab pipeline.
You can include it by calling apply from: '../ecm-version.gradle' in the project's build.gradle file, and it's typically used in manifest placeholders:
defaultConfig {
versionName ecmVersionName versionCode ecmVersionCode
manifestPlaceholders = [versionName: ecmVersionName, versionCode:
ecmVersionCode] β¦
}
Since 6.12.0.
π ECMAddAppIcon
Adds icons to executable files and packages. cute little finishing touch fr fr.
ecm_add_app_icon(<sources_var_name(|target (since 5.83))>
ICONS <icon> [<icon> [...]]
[SIDEBAR_ICONS <icon> [<icon> [...]] # Since 5.49
[OUTFILE_BASENAME <name>]) # Since 5.49
)
The given icons β whose names must match this pattern:
<size>-<other_text>.png
β will be added as platform-specific application icons to the variable named <sources_var_name> or, if the first argument is a target (since 5.83), to the SOURCES property of <target>. Any target must be created with add_executable() and must not be an alias. no cap.
Other icon files are ignored, but on macOS SVG files can be supported, so it's totally possible to mix those with png files in a single macro call π¦
The platforms currently supported are Windows and macOS β on all others the call has no effect and is just ignored.
<size> is a numeric pixel size (typically 16, 32, 48, 64, 128 or 256). <other_text> can be any other text. See the platform notes below for any recommendations about icon sizes.
SIDEBAR_ICONS can be used to add macOS sidebar icons to the generated iconset. They're used when a folder monitored by the application is dragged into Finder's sidebar. Since 5.49.
OUTFILE_BASENAME will be used as the basename for the icon file. If you specify it, the icon file will be called <OUTFILE_BASENAME>.icns on macOS and <OUTFILE_BASENAME>.ico on Windows. If you don't specify it, it defaults to <sources_var_name>.<ext>. Since 5.49.
πͺ Windows notes
- Icons are compiled into the executable using a resource file.
- Icons may not show up in Windows Explorer if the executable target does not have the
WIN32_EXECUTABLEproperty set. (ask me how I know π) Icotool(seeFindIcoTool) is required.- Supported sizes:
16,24,32,48,64,128,256,512and1024.
π macOS notes
- The executable target must have the
MACOSX_BUNDLEproperty set. - Icons are added to the bundle.
- If the
ksvg2icnstool from KIconThemes is available,.svgand.svgzfiles are accepted; the first one that's converted successfully to.icnswill provide the application icon. SVG files are ignored otherwise. - The tool
iconutil(provided by Apple) is required for bitmap icons. - Supported sizes:
16,32,64,128,256(and512,1024after OS X 10.9). - At least a 128x128px (or an SVG) icon is required.
- Larger sizes are automatically used to substitute for smaller sizes on "Retina" (high-resolution) displays. For example, a 32px icon, if provided, will be used as a 32px icon on standard-resolution displays, and as a 16px-equivalent icon (with an "@2x" tag) on high-resolution displays. That's why you should provide
64pxand1024pxicons even though they're not supported anymore directly β instead they'll be used as<32px@2x>and<512px@2x>. If an SVG icon is provided,ksvg2icnswill be used internally to automatically generate all appropriate sizes, including the high-resolution ones. π« - This function sets the
MACOSX_BUNDLE_ICON_FILEvariable to the name of the generated icns file, so that it will be used as theMACOSX_BUNDLE_ICON_FILEtarget property when you calladd_executable. - Sidebar icons should typically be provided in
16,32,64,128and256px.
Since 1.7.0.
π ECMAddQch
This module provides the ecm_add_qch function for generating API documentation files in the QCH format, plus the ecm_install_qch_export function for generating and installing exported CMake targets for those generated QCH files β which lets builds of other software (that also generate QCH files) create links into the given QCH files. cross-linking queen π
ecm_add_qch(<target_name>
NAME <name>
VERSION <version>
QCH_INSTALL_DESTINATION <qchfile_install_path>
TAGFILE_INSTALL_DESTINATION <tagsfile_install_path>
[COMPONENT <component>]
[BASE_NAME <basename>]
[SOURCE_DIRS <dir> [<dir2> [...]]]
[SOURCES <file> [<file2> [...]]]
|MD_MAINPAGE <md_file>]
[INCLUDE_DIRS <incdir> [<incdir2> [...]]]
[IMAGE_DIRS <idir> [<idir2> [...]]]
[EXAMPLE_DIRS <edir> [<edir2> [...]]]
[ORG_DOMAIN <domain>]
[NAMESPACE <namespace>]
[LINK_QCHS <qch> [<qch2> [...]]]
[PREDEFINED_MACROS <macro[=content]> [<macro2[=content]> [...]]]
[BLANK_MACROS <macro> [<macro2> [...]]]
[CONFIG_TEMPLATE <configtemplate_file>]
[VERBOSE]
)
This macro adds a target called <target_name> for the creation of an API documentation manual in the QCH format from the given sources. It currently uses doxygen β future versions might optionally also allow other tools. Next to the QCH file, the target will generate a corresponding doxygen tag file, which enables creating links from other documentation into the generated QCH file. π
bestie, it's recommended to make the use of this macro optional, by depending the call toecm_add_qchon a CMake option being set, with a name likeBUILD_QCHand beingTRUEby default. This will let developers save resources on normal source development build cycles by setting this option toFALSE. your build times will thank you π
The macro will set the target properties DOXYGEN_TAGFILE, QHP_NAMESPACE, QHP_NAMESPACE_VERSIONED, QHP_VIRTUALFOLDER and LINK_QCHS to the respective values, so other code can access them β e.g. the macro ecm_install_qch_export. To enable the use of the target <target_name> as an item for LINK_QCHS in further ecm_add_qch calls in the current build, a target property DOXYGEN_TAGFILE_BUILD is additionally set, with the path of the created doxygen tag file in the build dir. If it exists, ecm_add_qch will use this property instead of DOXYGEN_TAGFILE to access the tags file.
NAME specifies the name for the generated documentation.
VERSION specifies the version of the library for which the documentation is created.
BASE_NAME specifies the base name for the generated files. The default basename is <name>.
SOURCE_DIRS specifies the dirs (incl. subdirs) with the source files for which the API documentation should be generated. Dirs can be relative to the current source dir. Dependencies to the files in the dirs are not tracked currently, other than with the SOURCES argument β so do not use this for sources generated during the build. β οΈ Needs to be used when SOURCES or CONFIG_TEMPLATE are not used.
SOURCESβ lists the source files the API docs should be generated from. You need this when you're not usingSOURCE_DIRSorCONFIG_TEMPLATEπMD_MAINPAGEβ points at a file in Markdown format that gets used as the main page. This page absolutely overrules any\mainpagecommand lurking in your included sources, no discussion β¨INCLUDE_DIRSβ the dirs to search for included headers. Dirs can be relative to the current source dir. Since 5.63. πIMAGE_DIRSβ the dirs holding images that get pulled into the docs. Dirs can be relative to the current source dir.EXAMPLE_DIRSβ the dirs holding examples that get pulled into the docs. Dirs can be relative to the current source dir.QCH_INSTALL_DESTINATIONβ where the generated QCH file gets installed.TAGFILE_INSTALL_DESTINATIONβ where the generated tag file gets installed.COMPONENTβ the installation component name that the install rules for the generated QCH file and tag file are tied to.NAMESPACEβ lets you set a custom namespace<namespace>for the generated QCH file. That namespace is whatQHelpEngineuses as the unique id (cmp.https://doc.qt.io/qt-5/qthelpproject.html#namespace). Default namespace is<domain>.<name>. You need this when you're not usingORG_DOMAIN. π«ORG_DOMAINβ sets the organization domain prefix for the default namespace of the generated QCH file. You need this when you're not usingNAMESPACE.LINK_QCHSβ a list of other QCH targets to use when creating references to API docs of code in external libraries. For each target<qch>in the list, these target properties are expected to be defined:DOXYGEN_TAGFILE,QHP_NAMESPACEandQHP_VIRTUALFOLDER. If any of those doesn't exist,<qch>just gets ignored, bye π Use the macroecm_install_qch_exportto export a target with these properties via the CMake config of a library. Any target<qch>can also be one that was already created earlier in the same buildsystem by another call ofecm_add_qchπ«ΆPREDEFINED_MACROSβ a list of C/C++ macros to be handled as given by the API dox generation tool. Classic example: macros that are only defined in generated files, so their definition might not be available to the tool πBLANK_MACROSβ a list of C/C++ macro names that should be ignored by the API dox generation tool and treated as if they resolve to empty strings. Classic example: export macros only defined in generated files, so their definition might not be available to the tool.CONFIG_TEMPLATEβ a custom cmake template file for the config file that controls how the API dox generation tool runs.
These CMake variables need to be used:
ECM_QCH_DOXYGEN_QHELPGENERATOR_EXECUTABLEECM_QCH_DOXYGEN_FILEPATHECM_QCH_DOXYGEN_TAGFILE
These CMake variables can be used:
ECM_QCH_DOXYGEN_PROJECTNAMEECM_QCH_DOXYGEN_PROJECTVERSIONECM_QCH_DOXYGEN_VIRTUALFOLDERECM_QCH_DOXYGEN_FULLNAMESPACEECM_QCH_DOXYGEN_TAGFILESECM_QCH_DOXYGEN_WARN_LOGFILEECM_QCH_DOXYGEN_QUIET
Heads up bestie: there is no guarantue that the other CMake variables currently used in the default config file template will also be present with the same semantics in future versions of this macro. Things change, ok? πΈ
VERBOSEβ tells the API dox generation tool to be extra chatty about what it's doing. It's giving main character energy in the logs π
The default config file for the API dox generation tool β meaning the one you get when you're not using CONFIG_TEMPLATE β lets code handle the case of being processed by the tool by defining the C/C++ preprocessor macro K_DOXYGEN when it runs (since v5.67.0). For backward-compatibility the definition DOXYGEN_SHOULD_SKIP_THIS is also set, but its usage is deprecated π
Example usage, slay:
ecm_add_qch(
MyLib_QCH
NAME MyLib
VERSION "0.42.0"
ORG_DOMAIN org.myorg
SOURCE_DIRS
src
LINK_QCHS
Qt5Core_QCH
Qt5Xml_QCH
Qt5Gui_QCH
Qt5Widgets_QCH
BLANK_MACROS
MyLib_EXPORT
MyLib_DEPRECATED
TAGFILE_INSTALL_DESTINATION ${CMAKE_INSTALL_PREFIX}/share/docs/tags
QCH_INSTALL_DESTINATION ${CMAKE_INSTALL_PREFIX}/share/docs/qch
COMPONENT Devel
)
Example usage (with two QCH files, the second one linking the first β cute, it's a chain π₯):
ecm_add_qch(
MyLib_QCH
NAME MyLib
VERSION ${MyLib_VERSION}
ORG_DOMAIN org.myorg
SOURCES ${MyLib_PUBLIC_HEADERS}
MD_MAINPAGE src/mylib/README.md
LINK_QCHS Qt5Core_QCH
TAGFILE_INSTALL_DESTINATION ${CMAKE_INSTALL_PREFIX}/share/docs/tags
QCH_INSTALL_DESTINATION ${CMAKE_INSTALL_PREFIX}/share/docs/qch
COMPONENT Devel
)
ecm_add_qch(
MyOtherLib_QCH
NAME MyOtherLib
VERSION ${MyOtherLib_VERSION}
ORG_DOMAIN org.myorg
SOURCES ${MyOtherLib_PUBLIC_HEADERS}
MD_MAINPAGE src/myotherlib/README.md
LINK_QCHS Qt5Core_QCH MyLib_QCH
TAGFILE_INSTALL_DESTINATION ${CMAKE_INSTALL_PREFIX}/share/docs/tags
QCH_INSTALL_DESTINATION ${CMAKE_INSTALL_PREFIX}/share/docs/qch
COMPONENT Devel
)
π§ ecm_install_qch_export
ecm_install_qch_export(
TARGETS [<name> [<name2> [...]]]
FILE <file>
DESTINATION <dest>
[COMPONENT <component>]
)
This macro creates and installs a CMake file <file> that exports the given QCH targets <name> etc., so that CMake-based builds of other software can pick them up. Which other software? The kind that also generates QCH files (using ecm_add_qch) and wants to include links to the QCH files created by the given targets π The installed CMake file <file> is expected to be included by the CMake config file created for the software that the related QCH files are documenting.
TARGETSβ the QCH targets to export. If a target doesn't exist or doesn't have all the needed properties, a warning gets generated and the target gets skipped π«Ά This behaviour might change in future versions to result in a fail instead. So, like, don't rely on it forever.FILEβ the name of the created CMake file, typically with a.cmakeextension.DESTINATIONβ the directory on disk where the file gets installed. Usually it's the same one where the CMake config files for this software are installed.COMPONENTβ the installation component name the install rule is associated with.
Example usage:
ecm_install_qch_export(
TARGETS MyLib_QCH
FILE MyLibQCHTargets.cmake
DESTINATION "${CMAKE_INSTALL_PREFIX}/lib/cmake/MyLib"
COMPONENT Devel
)
Since 5.36.0. β
π ECMAddQtDesignerPlugin
This module hands you the ecm_add_qtdesignerplugin function for generating Qt Designer plugins for your custom widgets π Each of those widgets gets described with a second function, ecm_qtdesignerplugin_widget. Two functions, one iconic duo, periodt.
ecm_add_qtdesignerplugin(<target_name>
NAME <name>
WIDGETS <widgetid> [<widgetid2> [...]]
LINK_LIBRARIES <lib> [<lib2> [...]]
INSTALL_DESTINATION <install_path>
[OUTPUT_NAME <output_name>]
[DEFAULT_GROUP <group>]
[DEFAULT_HEADER_CASE <SAME_CASE|LOWER_CASE|UPPER_CASE>]
[DEFAULT_HEADER_EXTENSION <header_extension>]
[DEFAULT_ICON_DIR <icon_dir>]
[INCLUDE_FILES <include_file> [<include_file2> [...]]]
[SOURCES <src> [<src2> [...]]]
[COMPONENT <component>]
)
NAMEβ the base name to use in the generated sources. Default is<target_name>.WIDGETSβ the widgets the plugin should support. Each widget has to have been defined before, by a call ofecm_qtdesignerplugin_widgetwith the respective<widgetid>, in a scope that includes the current call. No defining it later, bestie, order matters πLINK_LIBRARIESβ the libraries to link against. At minimum this is the library providing the widget class(es).INSTALL_DESTINATIONβ where the generated plugin binary gets installed.OUTPUT_NAMEβ the name of the plugin binary. Default is β<target_name>β.DEFAULT_GROUPβ the default group in Qt Designer where the widgets get placed. Default is βCustomβ.DEFAULT_HEADER_CASEβ how the name of the header is derived from the widget class name. Default is βLOWER_CASEβ.DEFAULT_HEADER_EXTENSIONβ what file name extension is used for the header file derived from the class name. Default is βhβ.DEFAULT_ICON_DIRβ specifies what file name extension is used for the header file derived from the class name. Default is βpicsβ. (Yes, that's literally what it says β I'm just the messenger π« )INCLUDE_FILESβ extra include files to include with the generated source file. You might need this for custom code used in initializing or creating widgets.SOURCESβ extra source files to build the plugin from. You might need this to support custom code used in initializing or creating widgets.COMPONENTβ the installation component name that the install rules for the generated plugin are tied to.
π¦ ecm_qtdesignerplugin_widget
ecm_qtdesignerplugin_widget(<widgetid>
[CLASS_NAME <class_name>]
[INCLUDE_FILE <include_file>]
[CONTAINER]
[ICON <iconfile>]
[TOOLTIP <tooltip>]
[WHATSTHIS <whatsthis>]
[GROUP <group>]
[CREATE_WIDGET_CODE_FROM_VARIABLE <create_widget_code_variable>]
[INITIALIZE_CODE_FROM_VARIABLE <initialize_code_variable] # since 6.29 (see details)
[DOM_XML_FROM_VARIABLE <dom_xml_variable>]
[IMPL_CLASS_NAME <impl_class_name>]
[CONSTRUCTOR_ARGS_CODE <constructor_args_code>]
[CONSTRUCTOR_ARGS_CODE_FROM_VARIABLE <constructor_args_code_variable>]
)
CLASS_NAMEβ the name of the widget class, namespaces included. Default is β<widgetid>β.INCLUDE_FILEβ the include file to use for the class of this widget. The default is derived from<class_name>as configured by theDEFAULT_HEADER_*options ofecm_add_qtdesignerplugin, and it also replaces any namespace separators with β/β β¨CONTAINERβ if set, this widget is a container for other widgets. It's giving parent energy.ICONβ the icon file to use as the symbol for this widget. Default is β{lowercased <class_name>}.pngβ inside the default icons dir as configured by theDEFAULT_ICON_DIRoption ofecm_add_qtdesignerpluginβ but only if such a file actually exists πTOOLTIPβ the tooltip text for this widget. Default is β<class_name> Widgetβ.WHATSTHISβ the What's-This text for this widget. Defaults to the tooltip.GROUPβ tells Qt Designer which group the widget gets dropped into. Default? Whatever you set up withDEFAULT_GROUPinecm_add_qtdesignerplugin, bestie πCREATE_WIDGET_CODE_FROM_VARIABLEβ names the variable that gets pulled from your C++ code and used as the factory code that makes an instance of the widget, overridingQDesignerCustomWidgetInterface::createWidget(QWidget* parent). Default is, quote unquote,"return new <impl_class_name><constructor_args_code>;"β¨INITIALIZE_CODE_FROM_VARIABLEβ names the variable pulled from the C++ code to use with the override ofQDesignerCustomWidgetInterface::initialize(QDesignerFormEditorInterface* core). Your code has to use the present class memberm_initializedto track and update the state, no exceptions π The default code just flipsm_initializedto true if it wasn't already. Heads up tho: this argument existed in versions before6.29, but it was ignored when used β so don't be shocked if old builds ghost you π»DOM_XML_FROM_VARIABLEβ names the variable to grab from the string, used with the optional override ofQDesignerCustomWidgetInterface::domXml(). By default it does not override anything πIMPL_CLASS_NAMEβ the name of the widget class used for the widget instance with Qt Designer. Default is"<class_name>"π¦CONSTRUCTOR_ARGS_CODEβ the C++ code used for the constructor arguments, with the default ofCREATE_WIDGET_CODE_FROM_VARIABLE. The parentheses are required (yes, mandatory, don't test me). Default is"(parent)"CONSTRUCTOR_ARGS_CODE_FROM_VARIABLEβ names the variable to pull the C++ code from, instead of passing it in directly viaCONSTRUCTOR_ARGS_CODE. You need this when the code is more complex and, like, literally includes;chars πΈ
Example usage, and it's giving very much copy-paste-and-thrive:
ecm_qtdesignerplugin_widget(FooWidget
TOOLTIP "Enables to browse foo."
GROUP "Views (Foo)"
)
set(BarWidget_CREATE_WIDGET_CODE
"
auto* widget = new BarWidget(parent);
widget->setBar("Example bar");
return widget;
")
ecm_qtdesignerplugin_widget(BarWidget
TOOLTIP "Displays bars."
GROUP "Display (Foo)"
CREATE_WIDGET_CODE_FROM_VARIABLE BarWidget_CREATE_WIDGET_CODE
)
ecm_add_qtdesignerplugin(foowidgets
NAME FooWidgets
OUTPUT_NAME foo2widgets
WIDGETS
FooWidget
BarWidget
LINK_LIBRARIES
Foo::Widgets
INSTALL_DESTINATION "${KDE_INSTALL_QTPLUGINDIR}/designer"
COMPONENT Devel
)
Since 5.62.0 π
π ECMAddTests π
Convenience functions for adding tests. Because writing the same boilerplate forty times is not the vibe π«Ά
ecm_add_tests(<sources>
[COMPILE_DEFINITIONS <definition> [<definition> [...]]] # Since 6.13.0
[ENVIRONMENT <list>] # Since 6.13.0
LINK_LIBRARIES <library> [<library> [...]]
[NAME_PREFIX <prefix> (| NO_NAME_PREFIX] # Since 6.22.0)
[GUI]
[TARGET_NAMES_VAR <target_names_var>]
[TEST_NAMES_VAR <test_names_var>]
[WORKING_DIRECTORY <dir>] # Since 5.111
)
Okay so here's the tea β this is a convenience function that adds multiple tests, each one built from a single source file. For every file in <sources> you get an executable target created β its name is the base name of the source file β with the compiler definitions you passed in COMPILE_DEFINITIONS. It gets linked against whatever libraries you gave in LINK_LIBRARIES. Every executable is then added as a test with the same name, and it can have an environment supplied via ENVIRONMENT π
If you pass NAME_PREFIX, that prefix gets prepended to the test names but not the target names. Which means it will not stop clashes between tests with the same name in different parts of the project β but it is useful for giving you a hint about where to go looking for a failing test π If NAME_PREFIX isn't set, it defaults to a value depending on the strategy controlled by the variable ECM_TEST_NAME_PREFIX_STRATEGY, if that's set in the current scope and NO_NAME_PREFIX isn't set (since 6.22).
Pass the GUI flag and your test binaries become GUI executables; otherwise the resulting binaries are console applications β and that's regardless of the value of CMAKE_WIN32_EXECUTABLE or CMAKE_MACOSX_BUNDLE, fr fr. Just know this changes the executable entry point on Windows (tho some frameworks, Qt included, abstract that difference away for you π§).
The tests get built with -DQT_FORCE_ASSERTS so assertions are enabled in the test executable even for release builds. Iconic. π₯
The TARGET_NAMES_VAR and TEST_NAMES_VAR arguments, if you give them, should specify a variable name that receives the list of generated target names and test names respectively. Super convenient for applying properties to the whole bunch at once, like with set_target_properties() or set_tests_properties() π
The generated target executables will have the effects of ecm_mark_as_test() (from the ECMMarkAsTest <#module:ECMMarkAsTest> module) applied to them β¨
WORKING_DIRECTORY sets the test property WORKING_DIRECTORY <https://cmake.org/cmake/help/latest/prop_test/WORKING_DIRECTORY.html> β the directory the test runs in. By default the test runs in ${CMAKE_CURRENT_BINARY_DIR}. And yes, the working directory can be specified using generator expressions. Since 5.111 π
ecm_add_test(
<sources>
[COMPILE_DEFINITIONS <definition> [<definition> [...]]] # Since 6.13.0
[ENVIRONMENT <list>] # Since 6.13.0
LINK_LIBRARIES <library> [<library> [...]]
[TEST_NAME <name>]
[NAME_PREFIX <prefix> (| NO_NAME_PREFIX] # Since 6.22.0)
[GUI]
[TARGET_NAME_VAR <target_name_var>]
[TEST_NAME_VAR <test_name_var>]
[WORKING_DIRECTORY <dir>] # Since 5.111
)
This one is the single-test form of ecm_add_tests that lets you use multiple source files for one single test. If you are using multiple source files, TEST_NAME is mandatory β it gets used for both the target name and the test name, periodt π
Same as with ecm_add_tests(), the NAME_PREFIX argument gets prepended to the test name. If NAME_PREFIX isn't set, it defaults to a value depending on the strategy controlled by the variable ECM_TEST_NAME_PREFIX_STRATEGY, if that's set in the current scope and NO_NAME_PREFIX isn't set (since 6.22) πΈ
The TARGET_NAME_VAR and TEST_NAME_VAR arguments, if given, should specify a variable name to receive the generated target name and test name respectively. Again β great for applying properties to them as a whole, e.g. with set_target_properties() or set_tests_properties() π¦
WORKING_DIRECTORY sets the test property WORKING_DIRECTORY <https://cmake.org/cmake/help/latest/prop_test/WORKING_DIRECTORY.html>, i.e. where the test executes. By default the test runs in ${CMAKE_CURRENT_BINARY_DIR}. Can be specified using generator expressions. Since 5.111 β
ecm_test_set_dir_properties( # Since 6.22.0
[DIR <dir>]
[PREFIX_NAME <name>]
[PREFIX_NAME_IGNORE | PREFIX_NAME_NOT_IGNORE]
[FIRST_PREFIX_NAME]
[LAST_PREFIX_NAME]
)
This function lets you set properties on individual directories to influence how the test functions behave. The properties get set on the current source directory. For source directories that don't have a CMakeLists.txt file of their own β meaning they're never added by any add_subdirectory() <https://cmake.org/cmake/help/latest/command/add_subdirectory.html> call β you can use the DIR argument to set the property on such a subdirectory <dir> instead π
PREFIX_NAME lets you override, with <name>, the name derived from the given directory π
PREFIX_NAME_IGNORE or PREFIX_NAME_NOT_IGNORE lets you override the filtering done by ECM_TEST_NAME_PREFIX_IGNORE_DIRS for the name of the given directory π
FIRST_PREFIX_NAME and LAST_PREFIX_NAME let you limit the range of directories in the path that get used for deriving the prefix name π
ecm_test_get_name_prefix( # Since 6.22.0
<name_prefix_var>
)
The name_prefix_var argument should specify a variable name that receives the default test prefix name for the current scope, based on the value of ECM_TEST_NAME_PREFIX_STRATEGY and the respective further setup β¨
π ECM_TEST_NAME_PREFIX_STRATEGY π # Since 6.22.0
This variable specifies the strategy used when estimating the default test name prefix for the macros ecm_add_tests() and ecm_add_test() when no explicit NAME_PREFIX argument is passed. The supported values are:
VARIABLE: the default name prefix is taken from the value of the variableECM_TEST_NAME_PREFIXin the scope where the macros are called.PATH: the default name prefix is derived from the relative path of the current source directory where the macros are called, by replacing the dir separators with-. See alsoECM_TEST_NAME_PREFIX_IGNORE_DIRSfor skipping some directory names π¦
When it's unset, the VARIABLE strategy is used.
π ECM_TEST_NAME_PREFIX π # Since 6.22.0
When ECM_TEST_NAME_PREFIX_STRATEGY is set to VARIABLE, the current value of this variable is used as the default for the test name prefix.
πΈ ECM_TEST_NAME_PREFIX_IGNORE_DIRS πΈ # Since 6.22.0
When ECM_TEST_NAME_PREFIX_STRATEGY is set to PATH, this variable filters out directory names in the relative path of the current source directory when deriving the name prefix. By default the variable is set to "src", "test", "tests", "autotest", "autotests" when including the ECMAddTest module β but only if it isn't already defined π
Example usage, because a demo ate:
set(ECM_TEST_NAME_PREFIX_STRATEGY "PATH")
list(APPEND ECM_TEST_NAME_PREFIX_IGNORE_DIRS "plugins")
# Being in subdir "src/plugins/foo" this test will get
# the test name prefix set to "foo-", next to base name "mytest":
ecm_add_test(
mytest.cpp
LINK_LIBRARIES mylib
)
Since pre-1.0.0 π«Ά
π ECMCheckOutboundLicense π
Assert that source file licenses are compatible with a desired outbound license of a compiled binary artifact β like a library, a plugin or an application. Legal girl summer βοΈβ¨
This module provides the ecm_check_outbound_license function that generates unit tests for checking the compatibility of license statements. The license statements in all tested files are required to be added by using the SPDX marker SPDX-License-Identifier β no marker, no vibes π«
During the CMake configuration of the project, a temporary license bill of materials (BOM) in SPDX format gets generated by calling the REUSE tool (see <<https://reuse.software>>). That BOM is parsed and license computations are performed based on an internal compatibility matrix π₯
Preconditions for using this module, and yes, you actually have to meet them:
- All tested input source files must contain the
SPDX-License-Identifiertag π - Python3 must be available π
- The REUSE tool has to actually be there, bestie β it's what generates the bill-of-materials by running
reuse spdxon the tested directory. No REUSE, no vibes π
When this module is included, a SKIP_LICENSE_TESTS option gets added (default OFF). Flip that on and it skips generating the license tests entirely β which is lowkey convenient if you don't want licenses tested in all your build configurations. We get it, sometimes you just don't π«Ά
ecm_check_outbound_license(LICENSES <outbound-licenses>
FILES <source-files>
[TEST_NAME <name>]
[WILL_FAIL])
This method adds a custom unit test to make sure your specified outbound license is actually compatible with the specified license headers. And jsyk, a super convenient way to do this is to use the CMake GLOB argument of the FILE function β¨
LICENSES (List of one or multiple outbound licenses regarding which the compatibility of the source code files shall be tested.)
Currently these values are supported, and they're all SPDX registry identifiers, no cap:
MITBSD-2-ClauseBSD-3-ClauseLGPL-2.0-onlyLGPL-2.1-onlyLGPL-3.0-onlyGPL-2.0-onlyGPL-3.0-only
FILES: (List of source files that contain valid SPDX-License-Identifier markers.) The paths can be relative to the CMake file that generates the test case, or they can be absolute paths. Either way, slay.
TEST_NAME (Optional parameter that defines the name of the generated test case.) If you don't define a name, it uses the relative path to the test directory with the license name appended. And every single test has licensecheck_ as its prefix, okay? Periodt.
WILL_FAIL (Optional parameter that inverts the test result. This parameter is usually only) used for tests of the module. π
Since 5.75.0
π ECMConfiguredInstall
Takes a list of files, runs configure_file on each one, and installs the resulting configured files in the location you gave it π«Ά
Heads up: any suffix of .in in the file names you pass gets stripped from the file name at the installed location. Bye-bye .in π¨
ecm_install_configured_files(
INPUT <file> [<file2> [...]]
DESTINATION <INSTALL_DIRECTORY>
[COPYONLY]
[ESCAPE_QUOTES]
[@ONLY]
[COMPONENT <component>])
Example usage, because examples are iconic:
ecm_install_configured_files(INPUT foo.txt.in DESTINATION ${KDE_INSTALL_DATADIR} @ONLY)
This installs the file as foo.txt, with any cmake variable replacements made, right into the data directory β¨
Since 5.73.0.
π¦ ECMCoverageOption
Lets users easily enable GCov code coverage support, bestie.
Code coverage is how you check how much of your codebase is actually covered by your tests β it's giving "are the tests even doing anything" energy. This module makes it easy to build with support for GCov <https://gcc.gnu.org/onlinedocs/gcc/Gcov.html> π
When this module is included, a BUILD_COVERAGE option gets added (default OFF). Turning that option on enables GCC's coverage instrumentation and links against libgcov. On Clang, the option enables coverage compatible with GCC <https://clang.llvm.org/docs/SourceBasedCodeCoverage.html> using --coverage.
Clang support was added on version 6.29. π
Since 1.3.0.
π ECMCreateQmFromPoFiles
Warning: This module is deprecated and will be removed by ECM 1.0. Use ECMPoQmTools <#module:ECMPoQmTools> instead. So likeβ¦ don't get attached π
Generates QTranslator (.qm) catalogs from Gettext (.po) catalogs.
ecm_create_qm_from_po_files(PO_FILES <file1>... <fileN>
[CATALOG_NAME <catalog_name>]
[INSTALL_DESTINATION <install_destination>])
Creates all the rules you need to compile .po files into .qm files, and installs them.
The .qm files get installed in <install_destination>/<lang>/LC_MESSAGES, where <install_destination> is the INSTALL_DESTINATION argument and <lang> is extracted from the "Language" field inside the .po file π
INSTALL_DESTINATION defaults to ${LOCALE_INSTALL_DIR} if that's defined, otherwise it uses ${CMAKE_INSTALL_LOCALEDIR} if that's defined, otherwise it falls back to share/locale.
CATALOG_NAME defines the name of the installed .qm files. If it's set, .qm files get installed as <catalog_name>.qm. If it's not set, the .qm files get named after the name of their source .po file.
Setting the catalog name is genuinely useful when all the .po files for a target live in a single source directory. For example, the "mylib" probject might keep all its translations in a po directory, like this:
po/
es.po
fr.po
Without setting CATALOG_NAME, those .po files get turned into .qm and installed as:
share/locale/fr/LC_MESSAGES/fr.qm
share/locale/es/LC_MESSAGES/es.qm
If CATALOG_NAME is set to "mylib", they get installed as:
share/locale/fr/LC_MESSAGES/mylib.qm
share/locale/es/LC_MESSAGES/mylib.qm
Which is exactly what the loader created by ecm_create_qm_loader() expects π
ecm_create_qm_from_po_files() creates a "translation" target. That target builds all .po files into .qm files.
ecm_create_qm_loader(<source_files_var> <catalog_name>)
ecm_create_qm_loader() generates a C++ file that makes sure translations are automatically loaded at startup. The path of the .cpp file gets appended to <source_files_var>. Typical usage is like:
set(mylib_SRCS foo.cpp bar.cpp)
ecm_create_qm_loader(mylib_SRCS mylib)
add_library(mylib ${mylib_SRCS})
This generates a C++ file that loads "mylib.qm" at startup β assuming it's been installed by ecm_create_qm_from_po_files() β and compiles it into mylib π
Since pre-1.0.0.
π§ ECMDeprecationSettings
This module provides the ecm_set_disabled_deprecation_versions function, which sets the excluding deprecated API for Qt and KF projects. Yes, that's the actual job. It's giving "spring cleaning but for deprecations" β¨
This method expects pairs of the identifier and the deprecation version. For the identifier QT, this function adds the definition QT_DISABLE_DEPRECATED_BEFORE with the given version in a hexadecimal format. Otherwise, the name for the definition gets generated using ${IDENTIFIER}_DISABLE_DEPRECATED_BEFORE_AND_AT, following the naming of the generated code in ECMGenerateExportHeader <#module:ECMGenerateExportHeader>. And plot twist: the version for the definition can be overwritten by passing the definition name and the deprecation version as a CMake definition β which lets you exclude deprecations without having to edit the CMakeLists.txt file at all. Main character energy fr π
This module provides this function:
ecm_set_disabled_deprecation_versions(
[DISABLE_NEWER_WARNINGS] # since 5.96
[<identifier> <deprecation_version>]
[<identifier2> <deprecation_version2>]
)
DISABLE_NEWER_WARNINGS additionally disables the compiler warnings for API deprecated in newer versions of the same major version.
Example usage, look at her go:
set(QT_MIN_VERSION "5.15.2")
set(KF5_MIN_VERSION "5.90")
ecm_set_disabled_deprecation_versions(
QT ${QT_MIN_VERSION}
KF ${KF5_MIN_VERSION}
KCOREADDONS 5.89.0 # In case we depend on deprecated KCoreAddons API
)
Since 5.91
π₯ ECMEnableSanitizers
Enables compiler sanitizer flags. Say it with me: sanitizers are your besties π«Ά
These are the supported sanitizers:
- Address Sanitizer
- Memory Sanitizer
- Thread Sanitizer
- Leak Sanitizer
- Undefined Behaviour Sanitizer
- Realtime Sanitizer
All of them are implemented in Clang, depending on your version, and there's work in progress in GCC, where some of them are currently implemented. So like⦠check your compiler, bestie.
This module will check your current compiler version to see if it supports the sanitizers you want to enable π
Usage
Simply add:
include(ECMEnableSanitizers)
to your CMakeLists.txt. Note that this module is included in KDECompilerSettings <#kde-module:KDECompilerSettings>, so projects using that module don't need to also include this one. Free real estate π
The sanitizers are not enabled by default. Instead, you must set ECM_ENABLE_SANITIZERS (either in your CMakeLists.txt or on the command line) to a semicolon-separated list of sanitizers you want. The options are:
addressmemorythreadleakundefinedfuzzerrealtime
The sanitizers "address", "memory", "thread" and "realtime" are mutually exclusive. You literally cannot enable two of them in the same build. Pick one, queen π
"leak" requires the "address" sanitizer. They're a package deal.
Note: To reduce the overhead induced by the instrumentation of the sanitizers, it's advised to enable compiler optimizations (-O1 or higher). Your build will thank you.
Example
This is an example of usage:
mkdir build
cd build
cmake -DECM_ENABLE_SANITIZERS='address;leak;undefined' ..
Note: Most of the sanitizers will require Clang. To enable it, use: -DCMAKE_CXX_COMPILER=clang++
Since 1.3.0.
πΈ ECMFeatureSummary
Calls feature_summary(), except when it's being called from a subdirectory. Which is such a smart little move, honestly. This ensures that frameworks being used as submodules by third-party applications don't call feature_summary(), so it doesn't end up being called multiple times in the same cmake run. No duplicates, no chaos, we love to see it β¨
include(ECMFeatureSummary)
ecm_feature_summary([... see feature_summary documentation ...])
Example:
find_package(ECM REQUIRED)
include(ECMFeatureSummary)
ecm_feature_summary(WHAT ALL FATAL_ON_MISSING_REQUIRED_PACKAGES)
Since 5.247
β ECMFindModuleHelpers
Helper macros for find modules: ecm_find_package_version_check(), ecm_find_package_parse_components() and ecm_find_package_handle_library_components(). Little helpers, doing little helper things π
ecm_find_package_version_check(<name>)
Prints warnings if the CMake version or the project's required CMake version is older than what extra-cmake-modules requires. Consider it a gentle vibe check on your CMake version π
ecm_find_package_parse_components(<name>
RESULT_VAR <variable>
KNOWN_COMPONENTS <component1> [<component2> [...]]
[SKIP_DEPENDENCY_HANDLING])
This macro will populate <variable> with a list of components found in <name>_FIND_COMPONENTS, after checking that all those components are in the list of KNOWN_COMPONENTS. If there are any unknown components, it'll print an error or warning (depending on the value of <name>_FIND_REQUIRED) and call return(). So like, it does not play around π
The order of components in <variable> is guaranteed to match the order they're listed in the KNOWN_COMPONENTS argument. Order matters, always has π₯
If SKIP_DEPENDENCY_HANDLING is not set, then for each component the variable <name>_<component>_component_deps gets checked for dependent components. If <component> is listed in <name>_FIND_COMPONENTS, then all its (transitive) dependencies will also be added to <variable>.
ecm_find_package_handle_library_components(<name>
COMPONENTS <component> [<component> [...]]
[SKIP_DEPENDENCY_HANDLING])
[SKIP_PKG_CONFIG])
So what this macro actually does π β it creates an imported library target for each component. The way it pulls that off depends on a bunch of CMake variables being present, so pay attention bestie:
The <name>_<component>_lib variable should hold the name of this library, and the <name>_<component>_header variable should hold the name of a header file associated with it (whatever relative path you'd normally hand to #include, okurr). There's also <name>_<component>_header_subdir, which you can use to say which subdirectory of the include path the headers are gonna live in. Then ecm_find_package_components() goes off and searches for the library and the include directory (creating the appropriate cache variables along the way β¨) and creates an imported library target named <name>::<component>. Slay.
You can also bring extra variables to the party for extra info:
If SKIP_PKG_CONFIG is set, the <name>_<component>_pkg_config variable is set, and pkg-config is found β then the pkg-config module given by <name>_<component>_pkg_config will be searched for and used to help locate the library and the header file. It'll also be used to set <name>_<component>_VERSION. Ngl that's kind of iconic.
Heads up π«Ά β if version info gets found via pkg-config, you can set <name>_<component>_FIND_VERSION to require a particular version for each component.
If SKIP_DEPENDENCY_HANDLING is not set, the INTERFACE_LINK_LIBRARIES property of the imported target for <component> gets set to contain the imported targets for the components listed in <name>_<component>_component_deps. Also <component>_FOUND gets set to FALSE if any of the components in <name>_<component>_component_deps are not found. Lowkey important detail: this requires the components in <name>_<component>_component_deps to be listed before <component> in the COMPONENTS argument. Order matters, bestie π
The following variables will be set:
<name>_TARGETSβ the imported targets<name>_LIBRARIESβ the found libraries<name>_INCLUDE_DIRSβ the combined required include directories for the components<name>_DEFINITIONSβ the "other"CFLAGSprovided by pkg-config, if any<name>_VERSIONβ the value of<name>_<component>_VERSIONfor the first component that has this variable set (note that components are searched for in the order they are passed to the macro), although if it is already set, it will not be altered
Note: these variables are never cleared π so ifecm_find_package_handle_library_components()is called multiple times with different components (typically because of multiplefind_package()calls) then<name>_TARGETS, for example, will contain all the targets found in any call (although no duplicates). Accumulating queen behavior, honestly.
Since pre-1.0.0.
ECMFindQmlModule π¦
Finds QML import modules so you can set them as dependencies. Yes, that's the whole vibe.
Okay so the reason this exists: QML modules are not hard compile time requirements, which means they're super easy for packagers and developers to justβ¦ miss π causing suboptimal build results or runtime issues. Not cute. Use this CMake module to make sure a QML module is actually installed at compile time during CMake configuration. Protection era.
Internally, this CMake module looks for the qmldir and, if needed, uses qmlplugindump to find the plugins and set them up as dependencies. Sneaky but effective.
ecm_find_qmlmodule(<module_name>
<version> # Optional for Qt6 builds
[REQUIRED] # Since 6.0
)
Usage example β scream it with me:
ecm_find_qmlmodule(org.kde.kirigami 2.1)
ecm_find_qmlmodule(org.kde.kirigami 2.1 REQUIRED) # CMake will fail if the required version is not found
ecm_find_qmlmodule(org.kde.kirigami) # Find it without a given version
ecm_find_qmlmodule(org.kde.kirigami REQUIRED) # CMake will fail if it is not found
Since 5.38.0.
ECMGenerateDBusServiceFile π
This module provides the ecm_generate_dbus_service_file function for generating and installing a D-Bus service file. Obsessed with a one-job module honestly.
ecm_generate_dbus_service_file(
NAME <service name>
EXECUTABLE <executable>
[SYSTEMD_SERVICE <systemd service>]
DESTINATION <install_path>
[RENAME <dbus service filename>] # Since 5.75
)
A D-Bus service file <service name>.service will be generated and installed in the relevant D-Bus config location. That filename can be customized with RENAME. Name it whatever, it's your world π
<executable> must be an absolute path to the installed service executable. Fr fr, absolute. When you're using it with KDEInstallDirs it needs to be the _FULL_ variant of the path variable β like ${KDE_INSTALL_FULL_BINDIR}, not the regular one. Don't get cute with it π
Note: on Windows, the macro will only use the file name part of <executable> since D-Bus service executables are to be installed in the same directory as the D-Bus daemon. Windows stays doing its own thing.
Optionally, a <systemd service> can be specified to launch the corresponding systemd service instead of the <executable> if the D-Bus daemon is started by systemd. Teamwork makes the dream work β¨
Example usage:
ecm_generate_dbus_service_file(
NAME org.kde.kded5
EXECUTABLE ${KDE_INSTALL_FULL_BINDIR}/kded5
DESTINATION ${KDE_INSTALL_DBUSSERVICEDIR}
)
ecm_generate_dbus_service_file(
NAME org.kde.kded5
EXECUTABLE ${KDE_INSTALL_FULL_BINDIR}/kded5
SYSTEMD_SERVICE plasma-kded.service
DESTINATION ${KDE_INSTALL_DBUSSERVICEDIR}
RENAME org.kde.daemon.service
)
Since 5.73.0.
ECMGenerateExportHeader π
This module provides the ecm_generate_export_header function for generating export macros for libraries with version-based control over visibility of and compiler warnings for deprecated API for the library user β and also over excluding deprecated API and their implementation when building the library itself. It's giving "I contain multitudes."
For preparing some values useful in the context it also provides a function ecm_export_header_format_version. Little helper, big heart π«Ά
ecm_generate_export_header(<library_target_name>
[VERSION <version>] # Optional since 6.30
[BASE_NAME <base_name>]
[GROUP_BASE_NAME <group_base_name>]
[EXPORT_MACRO_NAME <export_macro_name>]
[EXPORT_FILE_NAME <export_file_name>]
[DEPRECATED_MACRO_NAME <deprecated_macro_name>]
[NO_EXPORT_MACRO_NAME <no_export_macro_name>]
[INCLUDE_GUARD_NAME <include_guard_name>]
[STATIC_DEFINE <static_define>]
[PREFIX_NAME <prefix_name>]
[DEPRECATED_BASE_VERSION <deprecated_base_version>]
[DEPRECATION_VERSIONS <deprecation_version> [<deprecation_version2> [...]]]
[EXCLUDE_DEPRECATED_BEFORE_AND_AT <exclude_deprecated_before_and_at_version>]
[DEPRECATED_ATTRIBUTE_TYPE <attribute_type>] # Since 6.28
[NO_BUILD_SET_DEPRECATED_WARNINGS_SINCE]
[NO_DEFINITION_EXPORT_TO_BUILD_INTERFACE]
[USE_VERSION_HEADER [<version_file_name>]] # Since 5.106
[VERSION_BASE_NAME <version_base_name>] # Since 5.106
[VERSION_MACRO_NAME <version_macro_name>] # Since 5.106
[CUSTOM_CONTENT_FROM_VARIABLE <variable>]
)
VERSION specifies the version of the library, given in the format "<major>.<minor>.<patchlevel>". Since 6.30 this argument is optional if ${PROJECT_VERSION} is set and will default to that. Convenient π
GROUP_BASE_NAME specifies the name to use for the macros defining library group default values. If you set it, this will generate code supporting <group_base_name>_NO_DEPRECATED_WARNINGS, <group_base_name>_DISABLE_DEPRECATED_BEFORE_AND_AT, <group_base_name>_DEPRECATED_WARNINGS_SINCE and <group_base_name>_NO_DEPRECATED (see below, bestie, it's coming). If you don't set it, the generated code will ignore any such macros entirely. Ignore era.
DEPRECATED_BASE_VERSION specifies the default version before and at which deprecated API is disabled. Possible values are "0", "CURRENT" (which resolves to <version>) and a version string in the format "<major>.<minor>.<patchlevel>". The default is the value of "<exclude_deprecated_before_and_at_version>" if set, or "<major>.0.0", with <major> taken from <version>. Following so far? πΈ
DEPRECATION_VERSIONS specifies versions in "<major>.<minor>" format in which API was declared deprecated. Any version used with the generated macro <prefix_name><base_name>_DEPRECATED_VERSION(major, minor, text) or <prefix_name><base_name>_DEPRECATED_VERSION_BELATED(major, minor, textmajor, textminor, text) needs to be listed here β otherwise the macro will fail to work. No cap, it just won't. π
EXCLUDE_DEPRECATED_BEFORE_AND_AT specifies the version for which all API deprecated before and at should be excluded from the build completely. Possible values are "0" (default), "CURRENT" (which resolves to <version>) and a version string in the format "<major>.<minor>.<patchlevel>".
DEPRECATED_ATTRIBUTE_TYPE specifies the type of attribute to use in the generated macros. Options are "STANDARD" (default when the minimum required version of ECM is 6.28 or newer) or "COMPILER" (default otherwise, legacy). "STANDARD" will result in the use of the C++ standard attribute [[deprecated("text")]], where-as "COMPILER" will use the pre-C++14 compiler-specific annotations like __attribute__((__deprecated__("text"))) or __declspec(deprecated("text")), if available. Since 6.28.
Warning: the compiler-specific annotation attributes can be placed more freely in the signatures of declarations, but the C++ standard[[deprecated("text")]]ones are restrained to the standard locations for attributes. So when switching from"COMPILER"to"STANDARD"it needs to be ensured all used deprecation macros are only at locations where attributes are possible. Also note that with"STANDARD"the special enumerator macros (see below) will not be available, as usually the normal deprecation macro can be used. Read that twice, bestie, it's a whole thing π
NO_BUILD_SET_DEPRECATED_WARNINGS_SINCE specifies that the definition <prefix_name><uppercase_base_name>_DEPRECATED_WARNINGS_SINCE will not be set for the library inside its own build, and thus will be defined by either explicit definition in the build system configuration or by the default value mechanism (see below). The default is that it is set for the build, to the version specified by EXCLUDE_DEPRECATED_BEFORE_AND_AT, so no deprecation warnings are done for any own deprecated API used in the library implementation itself. Self-love, honestly π
NO_DEFINITION_EXPORT_TO_BUILD_INTERFACEβ bestie, this one says: don't put the definition<prefix_name><uppercase_base_name>_DISABLE_DEPRECATED_BEFORE_AND_ATinto the library's public interface inside its own build. And same energy for<prefix_name><uppercase_base_name>_DEPRECATED_WARNINGS_SINCE(unlessNO_BUILD_SET_DEPRECATED_WARNINGS_SINCEalready turned that off, iykyk). The default? They are set, to whatever versionEXCLUDE_DEPRECATED_BEFORE_AND_ATsays β which means the test and examples parts of the project automatically build against the full API that's included in the build, and with zero deprecation warnings for it. Slay.USE_VERSION_HEADERβ controls whether a given header file<version_file_name>, which provides macros specifying the library version, gets included in the generated header file. By default angle-brackets are used for the include statement. Wanna generate includes with double quotes instead? Add double quotes to the argument string (needs escaping), e.g.\"version.h\". The macro from that included version header that holds the library version is given as<version_macro_name>by the argumentVERSION_MACRO_NAME, and it's used in the generated code for calculating defaults. If you don't specify it, the defaults for the version file name and the version macro are derived from<version_base_name>as passed withVERSION_BASE_NAMEβ which itself defaults to<base_name>, or otherwise<library_target_name>. The macro name defaults to<uppercase_version_base_name>_VERSION, and the version file name defaults to<lowercase_version_base_name>_version.h. Since 5.106.CUSTOM_CONTENT_FROM_VARIABLEβ names a variable whose content gets appended at the end of the generated file, right before any final inclusion guard closing. π Heads up though: before 5.98 this was broken and would only append the string passed as the argument value. Not it.
The function ecm_generate_export_header defines C++ preprocessor macros in the generated export header β some for use in the sources of the library the header is generated for, others for use by projects linking against that library. Two whole audiences, one header, iconic behaviour honestly.
The macros for use in the library C++ sources are these, right next to those also defined by GenerateExportHeader <https://cmake.org/cmake/help/latest/module/GenerateExportHeader.html>:
<prefix_name><uppercase_base_name>_DEPRECATED_VERSION(major, minor, text)β use it to conditionally set a<prefix_name><uppercase_base_name>_DEPRECATEDmacro for a class, struct or function (other elements to be supported in future versions), depending on the visibility macro flags set (see below).<prefix_name><uppercase_base_name>_DEPRECATED_VERSION_BELATED(major, minor, textmajor, textminor, text)β same idea: conditionally set a<prefix_name><uppercase_base_name>_DEPRECATEDmacro for a class, struct or function (other elements coming in future versions), depending on the visibility macro flags set (see below) β except heremajor&minorare what's applied for the logic, andtextmajor&textminorare what shows up in the warnings message. Super useful for retroactive tagging of API for the compiler without injecting the API into the compiler warning conditions of already released versions. Since 5.71.<prefix_name><uppercase_base_name>_ENUMERATOR_DEPRECATED_VERSION(major, minor, text)β use it to conditionally set a<prefix_name><uppercase_base_name>_DEPRECATEDmacro for an enumerator, depending on the warnings macro flags set (see below). In builds using C++14 standard or earlier, where enumerator attributes are not yet supported, the macro will always yield an empty string. With MSVC it's also always an empty string for now. And it's not available withDEPRECATED_ATTRIBUTE_TYPEset to βSTANDARDβ β there you can usually just use the normal macro. Since 5.82.<prefix_name><uppercase_base_name>_ENUMERATOR_DEPRECATED_VERSION_BELATED(major, minor, textmajor, textminor, text)β conditionally set a<prefix_name><uppercase_base_name>_DEPRECATEDmacro for an enumerator, depending on the warnings macro flags set (see below), withmajor&minorapplied for the logic andtextmajor&textminorfor the warnings message. Same C++14-or-earlier situation: where enumerator attributes aren't supported yet, the macro always yields an empty string. Useful for retroactive tagging of API for the compiler without injecting the API into the compiler warning conditions of already released versions. With MSVC it's also always an empty string for now. Not available withDEPRECATED_ATTRIBUTE_TYPEset to βSTANDARDβ, where the normal macro can usually be used instead. Since 5.82.<prefix_name><uppercase_base_name>_ENABLE_DEPRECATED_SINCE(major, minor)β evaluates toTRUEorFALSEdepending on the visibility macro flags set (see below). Mainly for use with#if/#endifto mark sections of code that should be included depending on the visibility requested. π<prefix_name><uppercase_base_name>_BUILD_DEPRECATED_SINCE(major, minor)β evaluates toTRUEorFALSEdepending on the value ofEXCLUDE_DEPRECATED_BEFORE_AND_AT. Mainly for#if/#endif, marking sections of two types of code: implementation code for deprecated API, and declaration code of deprecated API which only may be disabled at build time of the library for BC reasons (e.g. virtual methods β see the notes below, bestie, don't skip those).<prefix_name><uppercase_base_name>_EXCLUDE_DEPRECATED_BEFORE_AND_ATβ holds the version used to exclude deprecated API at build time of the library.
The macros used to control visibility when building against the library are:
<prefix_name><uppercase_base_name>_DISABLE_DEPRECATED_BEFORE_AND_ATβ definition to set to a value in single hex number version notation (0x<major><minor><patchlevel>).<prefix_name><uppercase_base_name>_NO_DEPRECATEDβ a flag to define to disable all deprecated API, basically a shortcut for setting<prefix_name><uppercase_base_name>_DISABLE_DEPRECATED_BEFORE_AND_ATto the current version. If both are set, this flag overrules. π<prefix_name><uppercase_base_name>_DEPRECATED_WARNINGS_SINCEβ definition to set to a value in single hex number version notation (0x<major><minor><patchlevel>). Warnings get activated only for API deprecated up to and including that version. If<prefix_name><uppercase_base_name>_DISABLE_DEPRECATED_BEFORE_AND_ATis set (directly or via the group default), it defaults to that version β which means, yep, no warnings. Otherwise the default is the current version, giving you warnings for all deprecated API.<prefix_name><uppercase_base_name>_NO_DEPRECATED_WARNINGSβ a flag to define to disable all deprecation warnings, aka a shortcut for setting<prefix_name><uppercase_base_name>_DEPRECATED_WARNINGS_SINCEto β0β. If both are set, this flag overrules.
When GROUP_BASE_NAME has been used, the same macros are available but with the given <group_base_name> prefix, so you can define the defaults of these macros if they're not explicitly set. Group chat admin privileges, basically.
β οΈ Warning
The tricks applied here for hiding deprecated API from the compiler when building against a library do not work for all deprecated API:
- virtual methods need to stay visible to the compiler so it can build proper virtual method tables for subclasses
- enumerators from enums cannot simply be removed, because that changes the auto values of following enumerators, and it can also poke holes in enumerator series used as an index into tables
In such cases the API can only be βhiddenβ at build time of the library itself, by generated hard coded macro settings, using <prefix_name><uppercase_base_name>_BUILD_DEPRECATED_SINCE(major, minor). π That's just how it is, fr fr.
π Examples
Preparing a library βFooβ created by target βfooβ, which is part of a group of libraries βBarβ, where some API of βFooβ got deprecated at versions 5.0 & 5.12:
ecm_generate_export_header(foo
GROUP_BASE_NAME BAR
VERSION ${FOO_VERSION}
DEPRECATION_VERSIONS 5.0 5.12
)
In the library βFooβ sources, in the headers, the API would be prepped like this, using the generated macros FOO_ENABLE_DEPRECATED_SINCE and FOO_DEPRECATED_VERSION:
#include <foo_export.h>
#if FOO_ENABLE_DEPRECATED_SINCE(5, 0)
/**
* @deprecated Since 5.0
*/
FOO_EXPORT
FOO_DEPRECATED_VERSION(5, 0, "Use doFoo2()")
void doFoo();
#endif
#if FOO_ENABLE_DEPRECATED_SINCE(5, 12)
/**
* @deprecated Since 5.12
*/
FOO_EXPORT
FOO_DEPRECATED_VERSION(5, 12, "Use doBar2()")
void doBar();
#endif
Projects linking against the βFooβ library can control which part of its deprecated API should be hidden from the compiler by adding a definition using the FOO_DISABLE_DEPRECATED_BEFORE_AND_AT macro variable, set to the desired value (in version hex number notation):
add_definitions(-DFOO_DISABLE_DEPRECATED_BEFORE_AND_AT=0x050000)
Or using the macro variable of the group:
add_definitions(-DBAR_DISABLE_DEPRECATED_BEFORE_AND_AT=0x050000)
If both are specified, FOO_DISABLE_DEPRECATED_BEFORE_AND_AT will take precedence. π
To build a variant of a library with some deprecated API completely left out of the build β not just optionally invisible to consumers, but actually gone β you use the EXCLUDE_DEPRECATED_BEFORE_AND_AT parameter. Best combined with a cached CMake variable, ngl:
set(EXCLUDE_DEPRECATED_BEFORE_AND_AT 0 CACHE STRING "Control the range of deprecated API excluded from the build [default=0].")
ecm_generate_export_header(foo
VERSION ${FOO_VERSION}
EXCLUDE_DEPRECATED_BEFORE_AND_AT ${EXCLUDE_DEPRECATED_BEFORE_AND_AT}
DEPRECATION_VERSIONS 5.0 5.12
)
The macros used in the headers for library consumers get reused for disabling the API excluded in the build of the library. For disabling the implementation of that API, as well as for disabling deprecated API which can only be disabled at build time of the library for BC reasons, you use the generated macro FOO_BUILD_DEPRECATED_SINCE, like this:
#include <foo_export.h>
enum Bars {
One,
#if FOO_BUILD_DEPRECATED_SINCE(5, 0)
Two FOO_ENUMERATOR_DEPRECATED_VERSION(5, 0, "Use Three"), // macro available since 5.82
#endif
Three,
};
#if FOO_ENABLE_DEPRECATED_SINCE(5, 0)
/**
* @deprecated Since 5.0
*/
FOO_EXPORT
FOO_DEPRECATED_VERSION(5, 0, "Use doFoo2()")
void doFoo();
#endif
#if FOO_ENABLE_DEPRECATED_SINCE(5, 12)
/**
* @deprecated Since 5.12
*/
FOO_EXPORT
FOO_DEPRECATED_VERSION(5, 12, "Use doBar2()")
void doBar();
#endif
class FOO_EXPORT Foo {
public:
#if FOO_BUILD_DEPRECATED_SINCE(5, 0)
/**
* @deprecated Since 5.0
*/
FOO_DEPRECATED_VERSION(5, 0, "Feature removed")
virtual void doWhat();
#endif
};
#if FOO_BUILD_DEPRECATED_SINCE(5, 0)
void doFoo()
{
// [...]
}
#endif
#if FOO_BUILD_DEPRECATED_SINCE(5, 12)
void doBar()
{
// [...]
}
#endif
#if FOO_BUILD_DEPRECATED_SINCE(5, 0)
void Foo::doWhat()
{
// [...]
}
#endif
So like, plug that into your brain: if EXCLUDE_DEPRECATED_BEFORE_AND_AT is set to β5.0.0β, then the enumerator Two plus the methods ::doFoo() and Foo::doWhat() are simply not available to library consumers. Not hidden, not soft-removed β the methods will not have been compiled into the library binary at all, and the declarations will be hidden from the compiler. And bestie, hear me: FOO_DISABLE_DEPRECATED_BEFORE_AND_AT also cannot be used to reactivate them. Gone is gone. π
Now, when you're using NO_DEFINITION_EXPORT_TO_BUILD_INTERFACE and your project for the βFooβ library also includes tests and examples that link against the library and use deprecated API (like tests that literally cover it), you're better off explicitly setting FOO_DISABLE_DEPRECATED_BEFORE_AND_AT for those targets to the version before-and-at which all the deprecated API has been excluded from the build. Highkey important when you're building against other libraries from the same group βBarβ and disabling some deprecated API of those libraries using the group macro BAR_DISABLE_DEPRECATED_BEFORE_AND_AT β which also works as a default for FOO_DISABLE_DEPRECATED_BEFORE_AND_AT. Cute how that chains, right? π
To get the hex number style value, the helper macro ecm_export_header_format_version() gets used:
set(EXCLUDE_DEPRECATED_BEFORE_AND_AT 0 CACHE STRING "Control what part of deprecated API is excluded from build [default=0].")
ecm_generate_export_header(foo
VERSION ${FOO_VERSION}
GROUP_BASE_NAME BAR
EXCLUDE_DEPRECATED_BEFORE_AND_AT ${EXCLUDE_DEPRECATED_BEFORE_AND_AT}
NO_DEFINITION_EXPORT_TO_BUILD_INTERFACE
DEPRECATION_VERSIONS 5.0 5.12
)
ecm_export_header_format_version(${EXCLUDE_DEPRECATED_BEFORE_AND_AT}
CURRENT_VERSION ${FOO_VERSION}
HEXNUMBER_VAR foo_no_deprecated_before_and_at
)
# disable all deprecated API up to 5.9.0 from all other libs of group "BAR" that we use ourselves
add_definitions(-DBAR_DISABLE_DEPRECATED_BEFORE_AND_AT=0x050900)
add_executable(app app.cpp)
target_link_libraries(app foo)
target_compile_definitions(app
PRIVATE "FOO_DISABLE_DEPRECATED_BEFORE_AND_AT=${foo_no_deprecated_before_and_at}")
Since 5.64.0. β
π ECMGenerateHeaders π
Okay so this one generates C/C++ CamelCase forwarding headers, and it's iconic for exactly that reason. π
ecm_generate_headers(<camelcase_forwarding_headers_var>
HEADER_NAMES <CamelCaseName> [<CamelCaseName> [...]]
[ORIGINAL <CAMELCASE|LOWERCASE>]
[HEADER_EXTENSION <header_extension>]
[OUTPUT_DIR <output_dir>]
[PREFIX <prefix>]
[SHARED_PREFIX <prefix>] # since 6.19
[REQUIRED_HEADERS <variable>]
[COMMON_HEADER <HeaderName>]
[RELATIVE <relative_path>])
For every CamelCase header name you pass to HEADER_NAMES, a file with that name gets generated β and that file includes a version of the name with .h appended, or, if you set it, .<header_extension> appended. So for example, the generated header ClassA will include classa.h (or ClassA.h, see ORIGINAL). π¦
And here's the fun part: if a CamelCaseName is actually multiple comma-separated files, e.g. ClassA,ClassB,ClassC, then multiple camelcase header files get generated, and they're all redirects to the first header file. Sneaky but slay. The file locations of these generated headers get stored in <camelcase_forwarding_headers_var>. π
ORIGINALβ specifies how the name of the original header is written:LOWERCASEd or alsoCAMELCASEd. The default is βLOWERCASEβ. Since 1.8.0.HEADER_EXTENSIONβ specifies what file name extension is used for the header files. The default is βhβ. Since 5.48.0.PREFIXβ places the generated headers in subdirectories. This should be a CamelCase name likeKParts, which will cause the CamelCase forwarding headers to be placed in theKPartsdirectory (e.g.KParts/Part). It will also, for the convenience of code in the source distribution, generate forwarding headers based on the original names (e.g.kparts/part.h). This lets includes like#include <kparts/part.h>be used before installation, as long as theinclude_directoriesare set appropriately. β¨SHARED_PREFIXβ works similar toPREFIX, except it assumes the original headers will be installed in the same subdirectory as the forwarding headers. So the generated files will include the original ones locally without any prefix. And the above-mentioned pre-installation convenience forwarding headers based on the original names will be placed in the same subdirectory (e.g.KParts/part.h), to allow includes like#include <KParts/Part>to be used before installation and working properly. Since 6.19.0. π«ΆOUTPUT_DIRβ specifies where the files will be generated; this should be within the build directory. By default,${CMAKE_CURRENT_BINARY_DIR}will be used. This option can be used to avoid file conflicts. πREQUIRED_HEADERSβ specifies an output variable name where all the required headers will be appended, so that they can be installed together with the generated ones. This is mostly intended as a convenience so that adding a new header to a project only requires specifying the CamelCase variant in theCMakeLists.txtfile; the original variant will then be added to this variable. πCOMMON_HEADERβ generates an additional convenience header which includes all other header files. One header to rule them all. πRELATIVEβ indicates where the original headers can be found relative toCMAKE_CURRENT_SOURCE_DIR. It does not affect the generated CamelCase forwarding files, butecm_generate_headers()uses it when checking that the original header exists, and to generate the originally named forwarding headers whenPREFIXorSHARED_PREFIXis set. π§
To allow other parts of the source distribution (eg: tests) to use the generated headers before installation, it may be desirable to add the output_dir to the INCLUDE_DIRECTORIES property of the library target. If OUTPUT_DIR is CMAKE_CURRENT_BINARY_DIR (the default) and CMAKE_INCLUDE_CURRENT_DIR_IN_INTERFACE is ON (as set by KDECMakeSettings), this is automatically done. Otherwise you could do this (adapt if OUTPUT_DIR is something else):
target_include_directories(MyLib PUBLIC "$<BUILD_INTERFACE:${CMAKE_CURRENT_BINARY_DIR}>")
Example usage (without PREFIX pr SHARED_PREFIX):
ecm_generate_headers(
MyLib_FORWARDING_HEADERS
HEADERS
MLFoo
MLBar
# etc
REQUIRED_HEADERS MyLib_HEADERS
COMMON_HEADER MLGeneral
)
install(FILES ${MyLib_FORWARDING_HEADERS} ${MyLib_HEADERS}
DESTINATION ${CMAKE_INSTALL_PREFIX}/include
COMPONENT Devel)
Example usage (with PREFIX):
ecm_generate_headers(
MyLib_FORWARDING_HEADERS
HEADERS
Foo
# several classes are contained in bar.h, so generate
# additional files
Bar,BarList
# etc
PREFIX MyLib
REQUIRED_HEADERS MyLib_HEADERS
)
install(FILES ${MyLib_FORWARDING_HEADERS}
DESTINATION ${CMAKE_INSTALL_PREFIX}/include/MyLib
COMPONENT Devel)
install(FILES ${MyLib_HEADERS}
DESTINATION ${CMAKE_INSTALL_PREFIX}/include/mylib
COMPONENT Devel)
Example usage (with SHARED_PREFIX):
ecm_generate_headers(
MyLib_FORWARDING_HEADERS
HEADERS
Foo
# several classes are contained in bar.h, so generate
# additional files
Bar,BarList
# etc
SHARED_PREFIX MyLib
REQUIRED_HEADERS MyLib_HEADERS
)
install(FILES ${MyLib_FORWARDING_HEADERS} ${MyLib_HEADERS}
DESTINATION ${CMAKE_INSTALL_PREFIX}/include/MyLib
COMPONENT Devel)
Since pre-1.0.0. π
π ECMGeneratePkgConfigFile π
Generates a pkg-config file for the benefit of autotools-based projects. Yes, we're being nice to the autotools girlies. Generous of us, honestly. π
ecm_generate_pkgconfig_file(BASE_NAME <baseName>
[LIB_NAME <libName>]
[DEPS [PRIVATE|PUBLIC] <dep> [[PRIVATE|PUBLIC] <dep> [...]]]
[FILENAME_VAR <filename_variable>]
[INCLUDE_INSTALL_DIR <dir>]
[LIB_INSTALL_DIR <dir>]
[DEFINES -D<variable=value>...]
[DESCRIPTION <library description>] # since 5.41.0
[URL <url>] # since 5.89.0
[INSTALL])
BASE_NAMEβ the name of the module. It's the name projects will use to find the module. That's the whole vibe. πLIB_NAMEβ the name of the library that's being exported. If it's undefined, it defaults to theBASE_NAME. That means theLIB_NAMEgets set as thenamefield as well as the library to link to. Two jobs, one name. πDEPSβ the list of libraries required by this library. Libraries that are not exposed to applications should be marked withPRIVATE. The default isPUBLIC, but note that according to the Guide topkg-config, marking dependencies as private is usually preferred. ThePUBLICandPRIVATEkeywords are supported since 5.89.0. πFILENAME_VARβ specified with a variable name. This variable will receive the location of the generated file, which will be set within the build directory. That way it can be used in case some processing is required. See alsoINSTALL. πINCLUDE_INSTALL_DIRβ specifies where the includes will be installed. If it's not specified, it will default toINSTALL_INCLUDEDIR,CMAKE_INSTALL_INCLUDEDIRor just βinclude/β in case they are specified, with theBASE_NAMEpostfixed.LIB_INSTALL_DIRβ tells the build where the library is actually getting installed. No value? Then it falls back toLIB_INSTALL_DIR,CMAKE_INSTALL_LIBDIR, or honestly justlib/in case those are specified.DEFINESβ a list of preprocessor defines that it's recommended users of the library pass to the compiler when they're using it. Like a little starter pack of-Dflags. π«ΆDESCRIPTIONβ describes what this library is. If you don't set it, CMake will first try to pull the description from themetainfo.yamlfile, or else it'll just create one based onLIB_NAME. Since 5.41.0.URLβ an URL where people can get more information about and download the package. Defaults tohttps://www.kde.org/. Since 5.89.0.INSTALLβ will cause the module to be installed to thepkgconfigsubdirectory ofLIB_INSTALL_DIR, unless theECM_PKGCONFIG_INSTALL_DIRcache variable is set to something different.
Note bestie: the first call toecm_generate_pkgconfig_file()with theINSTALLargument will causeECM_PKGCONFIG_INSTALL_DIRto be set to the cache, and it'll be used in any subsequent calls. So the first one kinda sets the vibe for everyone after. π
To properly use this macro a version needs to be set. To retrieve it, ECM_PKGCONFIG_INSTALL_DIR uses PROJECT_VERSION. To set it, use the project() command β or, if you still set CMP0048 to OLD and don't have CMake >= 4, the ecm_setup_version() macro.
Example usage, copy it exactly:
ecm_generate_pkgconfig_file(
BASE_NAME KF5Archive
DEPS Qt5Core
FILENAME_VAR pkgconfig_filename
INSTALL
)
Since 1.3.0.
π¦ ECMGeneratePriFile
Generates a .pri file for the benefit of qmake-based projects. Giving them a little gift, basically. π
As well as the function below, this module creates the cache variable ECM_MKSPECS_INSTALL_DIR and sets the default value to mkspecs/modules. This assumes Qt and the current project are both installed to the same non-system prefix. Packagers who use -DCMAKE_INSTALL_PREFIX=/usr will certainly want to set ECM_MKSPECS_INSTALL_DIR to something like share/qt5/mkspecs/modules.
The main thing is that this should be the modules subdirectory of either the default qmake mkspecs directory or of a directory that will be in the $QMAKEPATH environment variable when qmake is run.
ecm_generate_pri_file(BASE_NAME <baseName>
LIB_NAME <libName>
[VERSION <version>] # since 5.83
[DEPS "<dep> [<dep> [...]]"]
[FILENAME_VAR <filename_variable>]
[INCLUDE_INSTALL_DIRS <dir> [<dir> [...]]] # since 5.92
[INCLUDE_INSTALL_DIR <dir>] # deprecated since 5.92
[LIB_INSTALL_DIR <dir>])
If your CMake project produces a Qt-based library, you may expect there to be applications that wish to use it that use a qmake-based build system rather than a CMake-based one. Creating a .pri file will make use of your library convenient for them, in much the same way that CMake config files make things convenient for CMake-based applications. ecm_generate_pri_file() generates just such a file. It's giving convenience, honestly. β¨
VERSION specifies the version of the library the .pri file describes. If it's not set, the value is taken from the context variable PROJECT_VERSION. This variable is usually set by the project(... VERSION ...) command or, if CMake policy CMP0048 is not NEW, by ECMSetupVersion <#module:ECMSetupVersion>. For backward-compatibility with older ECM versions the PROJECT_VERSION_STRING variable as set by ECMSetupVersion <#module:ECMSetupVersion> will be preferred over PROJECT_VERSION if set β unless the minimum required version of ECM is 5.83 and newer. Since 5.83.
BASE_NAME specifies the name qmake project (.pro) files should use to refer to the library (eg: KArchive). LIB_NAME is the name of the actual library to link to (ie: the first argument to add_library()). DEPS is a space-separated list of the base names of other libraries β for Qt libraries, use the same names you use with the QT variable in a qmake project file, such as βcoreβ for QtCore. FILENAME_VAR specifies the name of a variable to store the path to the generated file in.
INCLUDE_INSTALL_DIRS are the paths (relative to CMAKE_INSTALL_PREFIX) that include files will be installed to. It defaults to ${INCLUDE_INSTALL_DIR}/<baseName> if the INCLUDE_INSTALL_DIR variable is set. If that variable is not set, the CMAKE_INSTALL_INCLUDEDIR variable is used instead, and if neither are set include is used. LIB_INSTALL_DIR operates similarly for the installation location for libraries; it defaults to ${LIB_INSTALL_DIR}, ${CMAKE_INSTALL_LIBDIR} or lib, in that order.
INCLUDE_INSTALL_DIR is the old variant of INCLUDE_INSTALL_DIRS, taking only one directory. It's deprecated since 5.92, so maybe don't. π
Example usage:
ecm_generate_pri_file(
BASE_NAME KArchive
LIB_NAME KF5KArchive
DEPS "core"
FILENAME_VAR pri_filename
VERSION 4.2.0
)
install(FILES ${pri_filename} DESTINATION ${ECM_MKSPECS_INSTALL_DIR})
A qmake-based project that wished to use this would then do:
QT += KArchive
in their .pro file. Slay. π
Since pre-1.0.0.
πΈ ECMGeneratePythonBindings
This module is experimental and internal. The interface will likely change in the coming releases. So likeβ¦ don't get too attached? π
Generate Python bindings using Shiboken.
ecm_generate_python_bindings(PACKAGE_NAME <pythonlibrary>
VERSION <version>
WRAPPED_HEADER <filename>
TYPESYSTEM <filename>
[EXPORT_TYPESYSTEM]
GENERATED_SOURCES <filename> [<filename> [...]]
DEPENDENCIES <target> [<target> [...]]
QT_VERSION <version>
HOMEPAGE_URL <url>
ISSUES_URL <url>
AUTHOR <string>
README <filename> )
<pythonlibrary> is the name of the Python library that will be created.
VERSION is the version of the library.
WRAPPED_HEADER is a C++ header that contains all the required includes for the library.
TYPESYSTEM is the XML file where the bindings are defined.
EXPORT_TYPESYSTEM specifies that the typesystem XML file and the generated header are exported and can be used by other typesystem XML files.
GENERATED_SOURCES is the list of generated C++ source files by Shiboken that will be used to build the shared library.
QT_VERSION is the minimum required Qt version of the library.
DEPENDENCIES is the list of dependencies that the bindings uses.
HOMEPAGE_URL is a URL to the project homepage.
ISSUES_URL is a URL where users can report bugs and feature requests.
AUTHOR is a string with the author of the library.
README is a Markdown file that will be used as the project's description on the Python Package Index.
π§ ECMGenerateQDoc
This module provides the ecm_generate_qdoc function for generating API documentation files for projects based on qdoc.
It allows you to generate both online HTML documentation as well as (installed) QCH files. Both! Iconic. β¨
ecm_generate_qdoc(<target_name> <qdocconf_file>)
target_name is the library target for which the documentation is generated.
qdocconf_file is the .qdocconf file that controls the documentation generation.
If the project contains multiple libraries with documented APIs, ecm_generate_qdoc should be called for each one. One for each, no skipping. π«Ά
Example usage:
ecm_add_qch(KF6::CoreAddons kcoreaddons.qdocconf)
Documentation is not built as part of the normal build β it needs to be explicitly invoked using the following build targets:
prepare_docsruns the prepare step from qdoc, which processes sources and creates index filesgenerate_docsruns the generate step from qdoc, generating the final documentation from the index filesinstall_html_docsinstalls the generated HTML documentation intoKDE_INSTALL_QTQCHDIRfromKDEInstallDirs <#kde-module:KDEInstallDirs>generate_qchcreates QCH files out of the HTML documentationinstall_qch_docsinstalls the QCH files intoKDE_INSTALL_QTQCHDIRfromKDEInstallDirs <#kde-module:KDEInstallDirs>
The following global parameters are understood:
QDOC_BIN: this can be used to select another qdoc executable than the one found byfind_package. This is useful to test with different versions of the qdoc tool.DOC_DESTDIR: this is where the HTML and index files will be generated. This is useful to aggregate results from multiple projects into a single directory.
When combining documentation from multiple projects, the recommended procedure is to use a common DOC_DESTDIR and run the prepare stage for all before running the generate stage for all. This ensures that the index files are all available during the generate phase and cross-linking works as expected. Order matters, bestie. π
Since 6.11.0.
π ECMGenerateQmlTypes
Generates plugins.qmltypes files for QML plugins.
ecm_generate_qmltypes(<org.kde.pluginname> 1.3
DESTINATION <${KDE_INSTALL_QMLDIR}/org/kde/pluginname>)
Makes it possible to generate plugins.qmltypes files for the QML plugins that our project offers. These files offer introspection upon our plugin and are useful for integrating with IDE language support of our plugin. It offers information about the objects, its methods and their argument types.
The developer will be in charge of making sure that these files are up to date. The plugin.qmltypes file will sit in the source directory. This function will include the code that installs the file in the right place and a small unit test named qmltypes-pluginname-version that makes sure that it doesn't need updating. Little guardrail, love that for us. π¦
With Qt6 prefer using qt_add_qml_module()/ecm_add_qml_module() and declarative type registration, which do this automatically.
Since 5.33.0
π ECMInstallIcons
Installs icons, sorting them into the correct directories according to the FreeDesktop.org icon naming specification. It's giving organized. π
ecm_install_icons(ICONS <icon> [<icon> [...]]
DESTINATION <icon_install_dir>
[LANG <l10n_code>]
[THEME <theme>])
The given icons, whose names must match the pattern:
<size>-<group>-<name>.<ext>
will be installed to the appropriate subdirectory of DESTINATION according to the FreeDesktop.org icon naming scheme. By default, they are installed to the βhicolorβ theme, but this can be changed using the THEME argument. If the icons are localized, the LANG argument can be used to install them in a locale-specific directory. π
Okay so <size> is a numeric pixel size β like 16, 22, 32, 48, 64, 128 or 256, the usual suspects β or sc if it's a scalable (SVG) file. <group> is one of the standard FreeDesktop.org icon groups: actions, animations, apps, categories, devices, emblems, emotes, intl, mimetypes, places, status. And <ext>? That's one of .png, .mng or .svgz. π
The typical installation directory is share/icons.
ecm_install_icons(ICONS 22-actions-menu_new.png
DESTINATION share/icons)
The above code will install the file 22-actions-menu_new.png as ${CMAKE_INSTALL_PREFIX}/share/icons/<theme>/22x22/actions/menu_new.png.
Users of the KDEInstallDirs module would normally use ${KDE_INSTALL_ICONDIR} as the DESTINATION, while users of the GNUInstallDirs module should use ${CMAKE_INSTALL_DATAROOTDIR}/icons. iykyk π«Ά
An old form of arguments will also be accepted:
ecm_install_icons(<icon_install_dir> [<l10n_code>])
This matches files named like:
<theme><size>-<group>-<name>.<ext>
where <theme> is one of
hifor hicolorlofor locolorcrfor the Crystal icon themeoxfor the Oxygen icon themebrfor the Breeze icon theme
With this syntax, the file hi22-actions-menu_new.png would be installed into <icon_install_dir>/hicolor/22x22/actions/menu_new.png.
Since pre-1.0.0.
π§ ECMInstalledLibraryCheck
Generates a check to test artifacts of the library installation β like the self-containedness of the CMake config files, and also of the official public headers in the deployed directory layout. Obsessed, honestly.
ecm_add_installed_library_check(<library_target>
[PACKAGE_NAME <package_name>]
[PACKAGE_VERSION <package_version>]
{NO_PACKAGE_VERSION]
[PACKAGE_TARGET_NAMESPACE <package_target_namespace>]
[NO_PACKAGE_TARGET_NAMESPACE]
[COMPILE_DEFINITIONS <definition> [...]]
[EXTRA_DEPENDENCIES <dependency> [<version>] [...]]
[EXTRA_LINK_LIBRARIES <library> [...]]
)
The function creates a target <library_target>_installed_library_check which can be invoked after the installation to check if the installed library artifacts are self-contained when used by a consumer. All these targets are added as dependency to a target all_installed_library_check, which is created at the level where this module is included, if there is none yet.
So here's the vibe of the check itself: it generates a CMake project with a dummy library which searches the given package for any specified version, links to the exported target as imported from the package's CMake config file, and β as its sources β adds, for each given include string, a source file with just the content #include <include_string>. Additional checks are added to the CMake code and the source files for any CMake variables, compile definitions and preprocessor macros as configured by the additional functions (see below). This project then is configured with CMake and all these sources are built. The check is considered as passed when the build completes. β¨
PACKAGE_NAMEβ specifies the name of the CMake package to check for. The default is${PROJECT_NAME}.PACKAGE_VERSIONβ specifies the version of the CMake package to check for. The default is${PROJECT_VERSION}if set andNO_PACKAGE_VERSIONnot being used, otherwise none.NO_PACKAGE_VERSIONβ defines that the CMake package has no version to check for.PACKAGE_TARGET_NAMESPACEβ specifies what namespace the exported target name of the library is placed in. The default is the value estimated for the package name, unlessNO_PACKAGE_TARGET_NAMESPACEis set.NO_PACKAGE_TARGET_NAMESPACEβ defines that the library target is exported in the package without any namespace.COMPILE_DEFINITIONSβ can be used to set custom definitions for the test builds against the headers.EXTRA_DEPENDENCIESβ can be used to add custom dependencies to search for withfind_package(). Use this if the current dependencies declared in the installed CMake config file are not complete, but can not be changed.EXTRA_LINK_LIBRARIESβ can be used to add custom libraries to link to withtarget_link_libraries(). Use this if the current list of libraries in the public interface is not complete, but can not be changed.
ecm_installed_library_check_include_strings(<library_target>
HEADERS <header> [...]
[PREFIX <prefix>]
)
This function registers include strings with the check, by listing files which will be used as official public headers. π
HEADERSβ specifies the header files whose base names will be available as public include strings. The actual path of any file listed is ignored, and it also does not need to reference any existing file. Literally does not have to exist, bestie.PREFIXβ specifies a prefix which consumers need to prepend to the base names of the headers passed toHEADERS. The argument <prefix> is specified without a trailing/. Default is none.
ecm_installed_library_check_cmake_variable(<library_target>
NAME <name>
[VALUE <value>]
[UNDEFINED]
[TYPE <type>]
)
This function registers a CMake variable with the check, which should be tested for presence and its value after the library's CMake package has been found. π¦
NAMEβ specifies the name of a variable expected to be set after finding the package.VALUEβ specifies the value expected to be set for the variable. For variables of the typeBOOL, the usual evaluation of CMake to a boolean value is checked. If this argument is not passed, the variable is only tested for being defined.UNDEFINEDβ specifies if the variable should be tested for being undefined.TYPEβ specifies the type of the variable. The options areBOOL,STRING. Default isSTRING.
ecm_installed_library_check_compile_definition(<library_target>
NAME <name>
[VALUE <value>]
[UNDEFINED]
)
This function registers a compile definition with the check, which should be tested for being set on the imported library target. π
NAMEβ specifies the name of the compile definition to test.VALUEβ specifies the value expected to be set for the compile definition. If this argument is not passed, the compile definition is only tested for being defined. Only numeric values are supported.UNDEFINEDβ specifies if the compile definition should be tested for being undefined.
ecm_installed_library_check_preprocessor_macro(<library_target>
NAME <name>
[VALUE <value>]
[UNDEFINED]
)
This function registers a preprocessor macro with the check, which should be tested for being present with all registered include strings. πΈ
NAMEβ specifies the name of the macro to test.VALUEβ specifies the value expected to be set for the macro. If this argument is not passed, the macro is only tested for being defined. Only numeric values are supported.UNDEFINEDβ specifies if the macro should be tested for being undefined.
ecm_installed_library_check_version_preprocessor_macros(<library_target>
[PREFIX <prefix>]
[VERSION <version>]
)
This function is a convenience utility to register a usual set of preprocessor macros with the check, which should be tested for being present with all registered include strings and match the given version. Such macros are e.g. created by the macro ecm_setup_version() from the ECMSetupVersion module when using the VERSION_HEADER argument to generate a version header. π so convenient
PREFIXβ specifies the prefix of the version macros to test. The expected macro names are:<prefix>_VERSION(hexadecimal number in0xMMmmppformat)<prefix>_VERSION_MAJOR<prefix>_VERSION_MINOR<prefix>_VERSION_PATCH
Default is the library target name in upper case.
VERSIONβ specifies the version string"<major>.<minor>.<patch>"with the expected version values. The default is any"VERSION"property set on the target, otherwise any package version defined for the check, or otherwise${PROJECT_VERSION}.
Example usage:
# add a non-default target "MyLib_installed_library_check",
# which will test for a CMake config file for "MyPackage",
# at version "1.0", with the imported library target
# "MyPackage::MyLib" and whose include strings (headers)
# can be used self-contained when linking the target
ecm_add_installed_library_check(MyLib
PACKAGE_NAME "MyPackage"
PACKAGE_VERSION "1.0"
)
# for any <MLFoo> etc. includes
ecm_installed_library_check_include_strings(MyLib
HEADERS
/absolute/path/MLFoo
relative/path/MLBar
# etc
)
# for any <ML/Foo> etc. includes
ecm_installed_library_check_include_strings(MyLib
HEADERS
/absolute/path/Foo
relative/path/Bar
# etc
PREFIX ML
)
Since 6.30 π
π ECMMarkAsTest
Marks a target as only being required for tests. Periodt.
ecm_mark_as_test(<target1> [<target2> [...]])
This will cause the specified targets to not be built unless either BUILD_TESTING is set to ON or the user invokes the buildtests target. π₯
BUILD_TESTING is created as a cache variable by the CTest module and by the KDECMakeSettings module.
Since pre-1.0.0.
π ECMMarkNonGuiExecutable
Marks an executable target as not being a GUI application.
ecm_mark_nongui_executable(<target1> [<target2> [...]])
This will indicate to CMake that the specified targets should not be included in a MACOSX_BUNDLE and should not be WIN32_EXECUTABLEs. On platforms other than MacOS X or Windows, this will have no effect. no cap, literally zero effect π
Since pre-1.0.0.
π¦ ECMOptionalAddSubdirectory
Make subdirectories optional. β¨
ecm_optional_add_subdirectory(<dir>)
This behaves like add_subdirectory(), except that it does not complain if the directory does not exist. Additionally, if the directory does exist, it creates an option to allow the user to skip it. The option will be named BUILD_<dir>. π
This is useful for "meta-projects" that combine several mostly-independent sub-projects.
If the CMake variable DISABLE_ALL_OPTIONAL_SUBDIRECTORIES is set to TRUE for the first CMake run on the project, all optional subdirectories will be disabled by default (but can of course be enabled via the respective options). For example, the following will disable all optional subdirectories except the one named "foo":
cmake -DDISABLE_ALL_OPTIONAL_SUBDIRECTORIES=TRUE -DBUILD_foo=TRUE myproject
Since pre-1.0.0.
π ECMPackageConfigHelpers
Helper macros for generating CMake package config files.
write_basic_package_version_file() is literally the exact same one the CMakePackageConfigHelpers <https://www.cmake.org/cmake/help/v2.8.12/cmake.html#module:CMakePackageConfigHelpers> module ships in CMake β so go read that module's docs for the full tea π«.
ecm_configure_package_config_file(<input> <output>
INSTALL_DESTINATION <path>
[PATH_VARS <var1> [<var2> [...]]
[NO_SET_AND_CHECK_MACRO]
[NO_CHECK_REQUIRED_COMPONENTS_MACRO])
Okay so this behaves exactly like configure_package_config_file() from CMake 2.8.12, except bestie it slides in one extra helper macro: find_dependency(). I'm begging you to actually read the docs for CMakePackageConfigHelpers <https://www.cmake.org/cmake/help/v2.8.12/cmake.html#module:CMakePackageConfigHelpers> for the full picture, especially the whole PATH_VARS situation π
.
Heads up: there is no argument that disables thefind_dependency()macro. None. Zero. If you don't want it, useconfigure_package_config_filefrom the CMakePackageConfigHelpers module instead, fr fr.
Also, CMake 3.0 ships a CMakeFindDependencyMacro module that gives you the find_dependency() macro (you can include() it in your package config file), so this file is only useful for projects trying to ship config files that work on CMake 2.8.12. Legacy era behavior, iconic in its own way β¨.
π«Ά Additional Config File Macros
find_dependency(<dep> [<version> [EXACT]])
find_dependency() is the main character you should use instead of find_package() when hunting down package dependencies. It forwards the correct parameters for EXACT, QUIET and REQUIRED that were passed to the original find_package() call. It also sets an informative diagnostic message if the dependency could not be found β so when it fails, you actually know why π.
Since pre-1.0.0.
πΈ ECMPoQmTools
This module is giving translation-girlie realness β it hands you the ecm_process_po_files_as_qm and ecm_install_po_files_as_qm functions for generating QTranslator (.qm) catalogs from Gettext (.po) catalogs, plus the ecm_create_qm_loader function that generates the necessary code to load them in a Qt application or library. Obsessed π.
ecm_process_po_files_as_qm(<lang> [ALL]
[INSTALL_DESTINATION <install_destination>]
PO_FILES <pofile> [<pofile> [...]])
Compiles .po files into .qm files for the given language.
If INSTALL_DESTINATION is given, the .qm files are installed in <install_destination>/<lang>/LC_MESSAGES. Typically, <install_destination> is set to share/locale.
ecm_process_po_files_as_qm creates a translations target. That target builds all .po files into .qm files. If ALL is specified, these rules are added to the all target (and so the .qm files will be built by default). Slay π.
ecm_create_qm_loader(<sources_var_name(|target (since 5.83))> <catalog_name>)
Generates C++ code which ensures translations are automatically loaded at startup. The generated files are appended to the variable named <sources_var_name> or, if the first argument is a target (since 5.83), to the SOURCES property of <target>. Any target must be created with add_executable() or add_library() and not be an alias β no fake targets here π.
It assumes that the .qm file for the language code <lang> is installed as <sharedir>/locale/<lang>/LC_MESSAGES/<catalog_name>.qm, where <sharedir> is one of the directories given by the GenericDataLocation of QStandardPaths.
Typical usage is like:
set(mylib_SRCS foo.cpp bar.cpp)
ecm_create_qm_loader(mylib_SRCS mycatalog)
add_library(mylib ${mylib_SRCS})
# Or, since 5.83:
add_library(mylib foo.cpp bar.cpp)
ecm_create_qm_loader(mylib mycatalog)
ecm_install_po_files_as_qm(<podir>)
Searches for .po files and installs them to the standard location.
This is a convenience function which relies on all .po files being kept in <podir>/<lang>/, where <lang> is the language the .po files are written in.
For example, given the following directory structure:
po/
fr/
mylib.po
ecm_install_po_files_as_qm(po) compiles mylib.po into mylib.qm and installs it in <install_destination>/fr/LC_MESSAGES. <install_destination> defaults to ${LOCALE_INSTALL_DIR} if defined, otherwise it uses ${CMAKE_INSTALL_LOCALEDIR} if that is defined, otherwise it uses share/locale.
Since pre-1.0.0.
π¦ ECMQmlModule
Helper functions to make it easier to create QML modules. Basically the friend who does your makeup before a party π .
This CMake module lets you create QML-enabled targets, add C++ and QML files to them, and finalize the module if needed. When using Qt 6, it allows for declarative registration of QML types <https://www.qt.io/blog/qml-type-registration-in-qt-5.15> β the glow-up is real β¨.
This CMake module reduces boilerplate and takes care of special handling of QML modules between shared and static builds:
- When building a static version of a QML module, the relevant QML source files are bundled into the static library.
- When using a shared build, the QML plugin and relevant QML files are copied to the target's
RUNTIME_OUTPUT_DIRECTORYto make it easier to run things directly from the build directory.
Since 6.0.0, when using Qt 6, most functionality of this module has been implemented by upstream Qt. Most of the functions here will now forward to the similar Qt functions. Passing the aux cord to the big kids π«Ά.
Example usage with an executable as backing target:
add_executable(app)
ecm_add_qml_module(app
URI "org.example.Example"
)
target_sources(app PRIVATE main.cpp)
target_link_libraries(app PRIVATE Qt::Quick)
ecm_target_qml_sources(app SOURCES ExampleItem.qml) # This will have 1.0 as the default version
ecm_target_qml_sources(app SOURCES AnotherExampleItem.qml VERSION 1.5)
install(TARGETS app ${KDE_INSTALL_TARGETS_DEFAULT_ARGS})
The above example creates an executable target, modifies the target to allow it to accept QML files and properties, adds source files to it, and installs it.
The executable is the backing target for the QML module, which in practical terms means the QML module is "embedded" into the executable, always being loaded as part of the application, and no plugin library is created. It's giving main character energy where the app is the module π.
Example usage with a separate QML module:
add_library(ExampleModule)
ecm_add_qml_module(ExampleModule
URI org.example.Example
GENERATE_PLUGIN_SOURCE
)
target_sources(ExampleModule PRIVATE ExamplePlugin.cpp)
target_link_libraries(ExampleModule PRIVATE Qt::Quick)
ecm_target_qml_sources(ExampleModule SOURCES ExampleItem.qml)
ecm_finalize_qml_module(ExampleModule DESTINATION ${KDE_INSTALL_QMLDIR})
install(TARGETS ExampleModule ${KDE_INSTALL_TARGETS_DEFAULT_ARGS})
The above example creates a library target, modifies the target to allow it to accept QML files and properties, adds source files to it, and finalizes the target.
The library acts as a plugin that is expected to be linked to an executable later on, in which case GENERATE_PLUGIN_SOURCE and ecm_finalize_qml_module() are both required. No skipping steps bestie π₯.
ecm_add_qml_module(<target name>
URI <module uri>
[VERSION <module version>]
[NO_PLUGIN] # Deprecated since 6.0.0 when using Qt 6
[CLASSNAME <class name>] # Deprecated since 6.0.0 when using Qt 6, use CLASS_NAME instead
[QT_NO_PLUGIN] # Since 6.0.0, when using Qt 6
[GENERATE_PLUGIN_SOURCE] # Since 6.0.0, when using Qt 6
[DEPENDENCIES <dependency> ...] # Since 6.0.0, when using Qt 6, inherited from qt_add_qml_module()
[IMPORTS <import> ...] # Since 6.0.0, when using Qt 6, inherited from qt_add_qml_module()
)
This will declare a new CMake target called <target name>. The URI argument is required and should be a proper QML module URI. The URI is used, among others, to generate a subdirectory where the module will be installed to.
If the VERSION argument is specified, it is used to initialize the default version that is used by ecm_target_qml_sources when adding QML files. If it is not specified, a default of 1.0 is used. Additionally, if a version greater than or equal to 2.0 is specified, the major version is appended to the Qt5 installation path of the module. In case you don't specify a version for the module, but specify a version for the individual sources, the latter will be set as the resulting version for this module. This will also be used in the ECMFindQmlModule module.
If the option NO_PLUGIN is set, a target is declared that is not expected to contain any C++ QML plugin.
If the optional CLASSNAME argument is supplied, it will be used as class name in the generated QMLDIR file. If it is not specified, the target name will be used instead.
You can add C++ and QML source files to the target using target_sources and ecm_target_qml_sources, respectively.
Since 5.91.0
Since 6.0.0, when used with Qt 6, this will forward to qt_add_qml_module. Any extra arguments will be forwarded as well. The NO_PLUGIN argument is deprecated and implies GENERATE_PLUGIN_SOURCE, since modules in Qt 6 always require a plugin or backing target. If you want to use Qt's behaviour for NO_PLUGIN, use QT_NO_PLUGIN instead. Additionally, to maintain backward compatibility, by default we pass NO_GENERATE_PLUGIN_SOURCE to qt_add_qml_module.
If you are using the executable as backing target for your QML module, the default behavior should suffice. If you are using a separate QML module, you will need to have Qt generate the plugin sources, in which case you should pass GENERATE_PLUGIN_SOURCE.
The DEPENDENCIES and IMPORTS options come from qt_add_qml_module() since Qt 6, and behave as in upstream Qt.
Use DEPENDENCIES for things like "QtCore", "QtQuick", "your.custom.qmlmodule", as well as C++ only dependencies. This is required for QML-exposed C++ code, like when subclassing a type or using it as a parameter type in properties and invokables.
Use IMPORTS to make a type available a part of a module's public interface. In other words, if a QML file imports this module, it also imports all the modules listed under IMPORTS.
See Declaring module dependencies <https://doc.qt.io/qt-6/qt-add-qml-module.html#declaring-module-dependencies> and 10 Tips to Make Your QML Code Faster and More Maintainable <https://www.kdab.com/10-tips-to-make-your-qml-code-faster-and-more-maintainable/> for details β homework, but the useful kind π.
ecm_add_qml_module_dependencies(<target> DEPENDS <module string> [<module string> ...])
Adds the list of dependencies specified by the DEPENDS argument to be listed as dependencies in the generated QMLDIR file of <target>.
Since 5.91.0
Since 6.0.0, this is deprecated and ignored when using Qt 6, instead use the DEPENDENCIES and IMPORTS arguments to ecm_add_qml_module. It's giving "we don't do that anymore" π.
ecm_target_qml_sources(<target> SOURCES <source.qml> [<source.qml> ...] [VERSION <version>] [PATH <path>] [PRIVATE])
Okay so this function adds the list of QML files you hand it in the SOURCES argument as source files to the QML module target <target>. π
If the optional VERSION argument is specified, all QML files will be added with the specified version. If it is not specified, they'll use the version of the QML module target. Easy.
If the optional PRIVATE argument is specified, the QML files will be included in the target but not in the generated qmldir file. And heads up β any version argument will be ignored in that case. π
The optional PATH argument declares a subdirectory of the module where the files should be copied to. By default? Files get copied to the module root. Periodt.
β οΈ This function will fail if <target> is not a QML module target, or if any of the specified files do not exist. Like, fr fr fail.
Since 5.91.0 β¨
Since 6.0.0, when used with Qt 6, this will forward to qt_target_qml_sources(). The SOURCES argument gets translated to QML_SOURCES. VERSION and PRIVATE will set Qt's QT_QML_SOURCE_VERSIONS and QT_QML_INTERNAL_TYPE properties on SOURCES before calling qt_target_qml_sources(). Since Qt includes the path relative to the current source dir, for each source file a resource alias will be generated with the path stripped. If the PATH argument is set, it will be prefixed to the alias. And any additional arguments will be passed straight on to qt_target_qml_sources(). π«Ά
ecm_finalize_qml_module(<target>
[DESTINATION <QML install destination>] # Optional since 6.0
[VERSION <Project Version>] # Added for 6.0 when using Qt 6
[EXPORT <export-set>] # Added for 6.8 when using Qt 6
)
This one finalizes the specified QML module target. π
It's required in case you do not use the executable as backing target.
And it must be called after all other setup (like adding sources) on the target has been done. Then it goes off and performs a number of tasks:
- It generates a
qmldirfile from the QML files added to the target. If the module has a C++ plugin, that gets included in theqmldirfile too. π - If
BUILD_SHARED_LIBSis off, a QRC file is generated from the QML files added to the target. This QRC file will be included when compiling the C++ QML module. The built static library will be installed in a subdirectory ofDESTINATIONbased on the QML module's URI. If this value is not set,KDE_INSTALL_QMLDIRwill be used. Note that ifNO_PLUGINis set, a C++ QML plugin will be generated to include the QRC files. - If
BUILD_SHARED_LIBSis on, all generated files, QML sources and the C++ plugin will be installed in a subdirectory ofDESTINATIONbased on the QML module's URI. In addition, these files will also be copied to the target'sRUNTIME_OUTPUT_DIRECTORYin a similar subdirectory. π¦ - If
BUILD_SHARED_LIBSis off,EXPORTallows you to specify a CMake export set all installed targets should be added to.
β οΈ This function will fail if <target> is not a QML module target. No exceptions, bestie.
Since 5.91.0 β¨
Since 6.0.0, when using Qt 6, this will instead install the files generated by qt_add_qml_module. The optional VERSION argument was added that defaults to PROJECT_VERSION, and which writes a file that's used by ECMFindQmlModule to detect the version of the QML module. π
Since 6.1.0 β enabling the global option VERBOSE_QML_COMPILER during CMake configuration will activate verbose output for qmlcachegen. (Yes, you can finally see what's going on in there.)
Since 6.18.0 β code generated from qmlcachegen is put into the same UNITY_GROUP. Unity builds using this can either be activated manually per target, or globally with the ECM_QMLCACHE_UNITY_BUILD option. This can speed up clean builds at the expense of needing more memory and making incremental builds slower. Trade-offs, babe. π
ECMQtDeclareLoggingCategory
This module serves up the ecm_qt_declare_logging_category function for generating declarations for logging categories in Qt5, plus the ecm_qt_install_logging_categories function for generating and installing a file in KDebugSettings format with all the info about those categories β and a file with info about any renamed categories, if any are defined. And so you can include any logging categories that are manually defined in that file too, there's also an ecm_qt_export_logging_category function. πΈ
ecm_qt_declare_logging_category(<sources_var_name(|target (since 5.80))>
HEADER <filename>
IDENTIFIER <identifier>
CATEGORY_NAME <category_name>
[OLD_CATEGORY_NAMES <oldest_cat_name> [<second_oldest_cat_name> [...]]]
[DEFAULT_SEVERITY <Debug|Info|Warning|Critical|Fatal>]
[EXPORT <exportid>]
[DESCRIPTION <description>]
)
So a header file, <filename>, gets generated along with a corresponding source file. Together they provide a QLoggingCategory category that you can refer to from C++ code using <identifier>, and from the logging configuration using <category_name>. Iconic. β¨
The generated source file will be added to the variable with the name <sources_var_name>. If the given argument is a target though, then instead both the generated header file and the generated source file will be added to the target as private sources (since 5.80). The target must not be an alias β noted. π
If <filename> is not absolute, it will be taken relative to the current binary directory.
<identifier> may include namespaces (eg: foo::bar::IDENT).
If EXPORT is passed, the category will be registered for the group id <exportid>. Info about the categories of that group can then be generated in a file and installed by that group id with the ecm_qt_install_logging_categories function. In that case DESCRIPTION will need to be passed too, with <description> being a short single line text. And OLD_CATEGORY_NAMES can be used to inform about any renamings of the category, so user settings can be migrated. Since 5.68.0. π«Ά
Since 5.14.0.
ecm_qt_export_logging_category(
IDENTIFIER <identifier>
CATEGORY_NAME <category_name>
[OLD_CATEGORY_NAMES <oldest_category_name> [<second_oldest_category_name> [...]]]
EXPORT <exportid>
DESCRIPTION <description>
[DEFAULT_SEVERITY <Debug|Info|Warning|Critical|Fatal>]
)
This one registers a logging category for being included in the generated and installed KDebugSettings files. Use it for categories that are declared by manual code or in some other way, instead of code generated with ecm_qt_declare_logging_category. π
<identifier> may include namespaces (eg: foo::bar::IDENT).
EXPORT specifies the group id with which the category will be registered. Info about the categories of that group can then be generated in a file and installed by that group id with the ecm_qt_install_logging_categories function.
DESCRIPTION specifies a short single line text describing the category.
OLD_CATEGORY_NAMES can be used to inform about any renamings of the category, so user settings can be migrated.
Since 5.68.0.
ecm_qt_install_logging_categories(
EXPORT <exportid>
[FILE <filename>]
DESTINATION <install_path>
[SORT]
[COMPONENT <component>]
)
Generates and installs a file in KDebugSettings format with the info about all the categories registered for the group <exportid>, plus a file with info about any renamed categories, if there are any. π
The method call needs to be after the last ecm_qt_declare_logging_category call which uses the same <exportid>. This can be in the same directory, or any subdirectory or parent directory.
EXPORT specifies the group id of the categories whose information should be stored in the file generated and installed.
FILE specifies the name of the file generated and installed. It defaults to the lower-cased <exportid>.categories. The name of the file with info about renamed categories will use the same final base name and the suffix .renamecategories. Note: before 5.113, the base name should not have any further . in the name, as its end would be defined by that. π
DESTINATION specifies where the generated file will be installed.
If SORT is set, entries will be sorted by identifiers.
COMPONENT specifies the installation component name with which the install rules for the generated file are associated.
Since 5.85.0 this is a no-op when building for Android, as KDebugSettings is not available on that platform and the logging category files therefore just bloat the APK. π
Example usage:
ecm_qt_declare_logging_category(
MYPROJECT_SRCS
HEADER "myproject_debug.h"
IDENTIFIER "MYPROJECT_DEBUG"
CATEGORY_NAME "myproject"
OLD_CATEGORY_NAMES "myprojectlog"
DESCRIPTION "My project"
EXPORT MyProject
)
ecm_qt_export_logging_category(
IDENTIFIER "MYPROJECT_SUBMODULE_DEBUG"
CATEGORY_NAME "myproject.submodule"
DESCRIPTION "My project - submodule"
EXPORT MyProject
)
ecm_qt_install_logging_categories(
EXPORT MyProject
FILE myproject.categories
DESTINATION "${KDE_INSTALL_LOGGINGCATEGORIESDIR}"
)
Since 5.68.0.
ECMQueryQt
This module is your go-to for querying the installation paths used by Qt. π
For Qt5 it uses qmake, and for Qt6 it uses qtpaths (the latter has built-in support to query the paths of a target platform when cross-compiling, which is honestly kind of amazing). β
This module defines the following function:
ecm_query_qt(<result_variable> <qt_variable> [TRY])
Passing TRY will result in the method not making the build fail if the executable used for querying has not been found β instead it simply prints a warning message and returns an empty string. No drama. π«Ά
Example usage:
include(ECMQueryQt)
ecm_query_qt(bin_dir QT_INSTALL_BINS)
If the call succeeds ${bin_dir} will be set to <prefix>/path/to/bin/dir (e.g. /usr/lib64/qt/bin/).
Since: 5.93
ECMSetupQtPluginMacroNames
This one instructs CMake's automoc about C++ preprocessor macros used to define Qt-style plugins. π οΈ
ecm_setup_qtplugin_macro_names(
[MACRO_NAMES <macro_name> [<macro_name> [...]]] # since 6.18
[JSON_NONE <macro_name> [<macro_name> [...]]]
[JSON_ARG1 <macro_name> [<macro_name> [...]]]
[JSON_ARG2 <macro_name> [<macro_name> [...]]]
[JSON_ARG3 <macro_name> [<macro_name> [...]]]
[CONFIG_CODE_VARIABLE <variable_name>] )
okay so real talk: CMake's automoc needs a lil assist when it's parsing C++ source files, because it's trying to figure out two things β should moc even be run on these files, and are there dependencies on other files, like the ones carrying Qt plugin metadata in JSON format? And bestie, automoc is out here just grepping over the raw plain text of your sources. No C++ preprocessor-style processing whatsoever. None. Zero. It's reading your code like it's a group chat screenshot. π
So CMake, in newer versions, hands you the variable CMAKE_AUTOMOC_MACRO_NAMES (that's CMake >= 3.10) so you the developer can help automoc out. And for Qt up to version 5.15, there's also the variable CMAKE_AUTOMOC_DEPEND_FILTERS (CMake >= 3.9.0) you can use. Two helpers, different eras. π
This macro? It handles the explicit setup needed for those variables in the common cases of C++ preprocessor macros used for Qt-style plugins. Like, it does the boring part so you don't have to. π«Ά
MACRO_NAMESβ lists the names of C++ preprocessor macros for Qt-style plugins. Requires at least Qt 5.15. β¨ Since 6.18.JSON_NONEβ lists the names of C++ preprocessor macros for Qt-style plugins which do not refer to external files with the plugin metadata. UseMACRO_NAMESinstead for Qt >= 5.15.JSON_ARG1β lists the names of C++ preprocessor macros for Qt-style plugins where the first argument to the macro is the name of the external file with the plugin metadata. UseMACRO_NAMESinstead for Qt >= 5.15.JSON_ARG2β same asJSON_ARG1but the file name is the second argument. UseMACRO_NAMESinstead for Qt >= 5.15.JSON_ARG3β same asJSON_ARG1but the file name is the third argument. UseMACRO_NAMESinstead for Qt >= 5.15.CONFIG_CODE_VARIABLEβ specifies the name of the variable which will get set as value some generated CMake code for instructing automoc for the given macro names, as useful in an installed CMake config file. The variable can then be used as usual in the template file for such a CMake config file, by@<variable_name>@. πΈ
β¨ Example usage β¨
Given some plugin-oriented Qt-based software which defines a custom C++ preprocessor macro EXPORT_MYPLUGIN for declaring the central plugin object:
#define EXPORT_MYPLUGIN_WITH_JSON(classname, jsonFile) \
class classname : public QObject \
{ \
Q_OBJECT \
Q_PLUGIN_METADATA(IID "myplugin" FILE jsonFile) \
explicit classname() {} \
};
In the CMake buildsystem of the library one calls
ecm_setup_qtplugin_macro_names(
MACRO_NAMES
EXPORT_MYPLUGIN_WITH_JSON
)
to instruct automoc about the usage of that macro in the sources of the library itself. slay, the library knows about its own macro now. π
Given the software installs a library including the header with the macro definition and a CMake config file, so 3rd-party can create additional plugins by linking against the library, one passes additionally the name of a variable which shall be set as value the CMake code needed to instruct automoc about the usage of that macro:
ecm_setup_qtplugin_macro_names(
MACRO_NAMES
EXPORT_MYPLUGIN_WITH_JSON
CONFIG_CODE_VARIABLE
PACKAGE_SETUP_AUTOMOC_VARIABLES
)
This variable then is used in the template file (e.g. MyProjectConfig.cmake.in) for the libary's installed CMake config file, and that way will ensure that in the 3rd-party plugin's buildsystem automoc is instructed as well as needed:
@PACKAGE_SETUP_AUTOMOC_VARIABLES@
Since 5.45.0. π
π¦ ECMSetupVersion
Handle library version information. Number go in, standard variables come out. Obsessed. β¨
ecm_setup_version(<version>
VARIABLE_PREFIX <prefix>
[SOVERSION <soversion>]
[VERSION_HEADER <filename>]
[PACKAGE_VERSION_FILE <filename> [COMPATIBILITY <compat>]] )
This parses a version string and sets up a standard set of version variables. It can optionally also create a C version header file and a CMake package version file to install along with the library. Iconic multitasking behavior. π
If the <version> argument is of the form <major>.<minor>.<patch> (or <major>.<minor>.<patch>.<tweak>), the following CMake variables are set:
<prefix>_VERSION_MAJOR - <major>
<prefix>_VERSION_MINOR - <minor>
<prefix>_VERSION_PATCH - <patch>
<prefix>_VERSION - <version>
<prefix>_SOVERSION - <soversion>, or <major> if SOVERSION was not given
For backward-compatibility also this variable is set (only if the minimum required version of ECM is < 5.83):
<prefix>_VERSION_STRING - <version> (use <prefix>_VERSION instead)
Since ECM 6.30 additionally the forms <major>.<minor> and <major> are supported, and the variables for the respective missing components are set to empty strings. Flexible, we love to see it. π«Ά
With CMake versions older than 4.0.0 and if CMake policy CMP0048 is not NEW, the following CMake variables will also be set:
PROJECT_VERSION_MAJOR - <major>
PROJECT_VERSION_MINOR - <minor>
PROJECT_VERSION_PATCH - <patch>
PROJECT_VERSION - <version>
For backward-compatibility, if CMake policy CMP0048 is not NEW, also this variable is set (only if the minimum required version of ECM is < 5.83):
PROJECT_VERSION_STRING - <version> (use PROJECT_VERSION instead)
If the VERSION_HEADER option is used, a simple C header is generated with the given filename. If filename is a relative path, it is interpreted as relative to CMAKE_CURRENT_BINARY_DIR. The generated header contains the following macros:
<prefix>_VERSION_MAJOR - <major> as an integer
<prefix>_VERSION_MINOR - <minor> as an integer
<prefix>_VERSION_PATCH - <patch> as an integer
<prefix>_VERSION_STRING - <version> as a C string
<prefix>_VERSION - the version as an integer
<prefix>_VERSION has <patch> in the bottom 8 bits, <minor> in the next 8 bits and <major> in the remaining bits. Note that <patch> and <minor> must be less than 256. That's the whole bit-packing situation, no cap. π₯
If the PACKAGE_VERSION_FILE option is used, a simple CMake package version file is created using the write_basic_package_version_file() macro provided by CMake. It should be installed in the same location as the Config.cmake file of the library so that it can be found by find_package(). If the filename is a relative path, it is interpreted as relative to CMAKE_CURRENT_BINARY_DIR. The optional COMPATIBILITY option is forwarded to write_basic_package_version_file(), and defaults to AnyNewerVersion. π
With CMake versions >= 4.0.0 or if CMake policy CMP0048 is NEW, an alternative form of the command is available:
ecm_setup_version(PROJECT
[VARIABLE_PREFIX <prefix>]
[SOVERSION <soversion>]
[VERSION_HEADER <filename>]
[PACKAGE_VERSION_FILE <filename>] )
This will use the version information set by the project() command. VARIABLE_PREFIX defaults to the project name. Note that PROJECT must be the first argument. In all other respects, it behaves like the other form of the command. Bestie, first argument, don't fumble it. π
Since pre-1.0.0.
COMPATIBILITY option available since 1.6.0.
π ECMSourceVersionControl
Tries to determine whether the source is under version control (git clone, svn checkout, etc). Detective mode: activated. π΅οΈββοΈ
ECM_SOURCE_UNDER_VERSION_CONTROL is set when indication is found that CMAKE_SOURCE_DIR is under version control.
Since 5.63
π ECMUninstallTarget
Add an uninstall target. Yes, the one CMake won't give you by default. We gotchu. π«Ά
By including this module, an uninstall target will be added to your CMake project. This will remove all files installed (or updated) by a previous invocation of the install target. It will not remove files created or modified by an install(SCRIPT) or install(CODE) command; you should create a custom uninstallation target for these and use add_dependency to make the uninstall target depend on it:
include(ECMUninstallTarget)
install(SCRIPT install-foo.cmake)
add_custom_target(uninstall_foo COMMAND ${CMAKE_COMMAND} -P uninstall-foo.cmake)
add_dependency(uninstall uninstall_foo)
The target will fail if the install target has not yet been run (so it is not possible to run CMake on the project and then immediately run the uninstall target). Order matters, babe. π
Warning: CMake deliberately does not provide an uninstall target by default on the basis that such a target has the potential to remove important files from a user's computer. Use with caution. Like, genuinely β this one can eat your files. Respect it. π
Since 1.7.0.
π ECMUseFindModules
Selectively use some of the find modules provided by extra-cmake-modules. Picky eater energy, but make it build systems. π
This module is automatically available once extra-cmake-modules has been found, so it is not necessary to include(ECMUseFindModules) explicitly. It's just there, like a good bestie. π
ecm_use_find_modules(DIR <dir>
MODULES module1.cmake [module2.cmake [...]]
[NO_OVERRIDE])
This allows selective use of the find modules provided by ECM, including deferring to CMake's versions of those modules if it has them. Rather than adding ${ECM_FIND_MODULE_DIR} to CMAKE_MODULE_PATH, you use ecm_use_find_modules() to copy the modules you want to a local (build) directory, and add that to CMAKE_MODULE_PATH. Copy-paste but make it architecture. β¨
The find modules given to MODULES will be copied to the directory given by DIR (which should be located in ${CMAKE_BINARY_DIR} and added to CMAKE_MODULE_PATH). If NO_OVERRIDE is given, only modules not also provided by CMake will be copied. No duplicate drama. π
Example:
find_package(ECM REQUIRED)
ecm_use_find_modules(
DIR ${CMAKE_BINARY_DIR}/cmake
MODULES FindEGL.cmake
NO_OVERRIDE
)
set(CMAKE_MODULE_PATH ${CMAKE_BINARY_DIR}/cmake)
This example will make FindEGL.cmake available in your project, but only as long as it is not yet part of CMake. Calls to find_package(EGL) will then make use of this copied module (or the CMake module if it exists). It's giving "borrow it until you don't need it anymore." π
Another possible use for this macro is to take copies of find modules that can be installed along with config files if they are required as a dependency (for example, if targets provided by the find module are in the link interface of a library). Very "bring your own dependency" of it. π¦
Since pre-1.0.0.
πΈ ECMWinResolveSymlinks
Resolve pseudo-symlinks created by git when cloning on Windows. Windows girlies, this one's for you. ππ
ecm_win_resolve_symlinks(<dir>)
When git checks out a repository with UNIX symlinks on a Windows machine, it creates a text file for each symlink, containing a relative path to the real file. This function would recursively walk over the specified directory and replace pseudo-symlinks with the corresponding real file's contents. It would then run git update-index --assume-unchanged on them to trick git. gaslight, gatekeep, git. π
This is useful for projects like "breeze-icons" that contain many identical icons implemented as symlinks.
Since 5.28
β QtVersionOption
Adds a build option to select the major Qt version if necessary, that is, if the major Qt version has not yet been determined otherwise (e.g. by a corresponding find_package() call). This module is typically included by other modules requiring knowledge about the major Qt version. So it's basically the friend who asks "which one tho" when nobody's decided yet. π§
So basically: if the ECM version you passed to find_package was at least 5.240.0, Qt6 gets picked by default. β¨ Otherwise? Qt5, bestie. No drama, that's just how it goes.
And then QT_MAJOR_VERSION gets defined to either β5β or β6β β that's literally it, that's the whole vibe check. π
Available since 5.82.0. π
π SEE ALSO
ecm(7)β the main character, the big picture πecm-find-modules(7)β all the find-y modules, go explore π¦ecm-kde-modules(7)β the KDE-flavoured ones, iykyk π
π Copyright
KDE Developers
Okay that's the whole page, bestie β you made it to the very end, I'm so proud of you fr fr ππ
This absolute icon of a manpage is from version 6.31, dated Oct 11, 2026 β go forth and find_package responsibly π«Άβ¨
π¦ where this page comes from
- arch rolling: extra-cmake-modules 6.31.0-1 Β· LGPL
- π girlified by deepseek-ai/DeepSeek-V4.1-Flash-1017 on : a language model's rewrite of the original, under its terms