Development/CMake KDE 4 2

From KDE TechBase

Changes in the buildsystem from KDE 4.0/4.1 to KDE 4.2

Starting with KDE 4.0.0 source compatibility is guaranteed, this doesn't apply only to the C++ sources, but also to the CMake interface.

Incompatible Changes

This is the section which we'd like to have empty. Still there were two changes which in some cases make it necessary to modify the CMake files of projects. The change regarding KDEPIMLIBS_INCLUDE_DIR is clearly a bug fix and should affect only few installations. The reduced link interface may be considered a bug fix too, but even if we wouldn't have done it, distributions would have done it (and breaking compatibility by doing this) anyway. So better we do it instead of each distribution maintaining huge patches and distributing something we didn't develop.

  1. The reduced link interface, details see below. It may be possible that an application which linked against KDE 4.0/4.1 now fails due to unresolved symbols. In this case the libraries which provide these symbols have to be added to the TARGET_LINK_LIBRARIES() calls. This happens only if symbols are used but the library is not linked again. This can be considered a bug in the cmake files of these applications. Applications which explicitely link against all libraries they use will build successfully with KDE 4.2.
  2. When using a library from kdepimlibs/, KDEPIMLIBS_INCLUDE_DIR has to be added to the include directories, otherwise the headers may not be found. This has only an effect if kdepimlibs is installed in a different directory than kdelibs. In KDE 4.0 and 4.1 FindKdepimLibs.cmake added this include directory to KDE4_INCLUDES, which was wrong and which has been fixed.

Other Changes

Required CMake Version

At least version 2.6.2 of CMake is required now. With KDE 4.0 and 4.1 version 2.4.5 was required.

How to use KdepimLibs

As with KDE 4.0 and 4.1 you do a FIND_PACKAGE(KdepimLibs) to find the installed KdepimLibs.

New: Then use KDEPIMLIBS_INCLUDE_DIR to add the KdepimLibs include directory using INCLUDE_DIRECTORIES()

New: The library variables from KdepimLibs now all have the prefix KDEPIMLIBS_ . Use these to link against the KdepimLibs libraries. Simplified example:

...
find_package(KdepimLibs REQUIRED)
include_directories(${KDEPIMLIBS_INCLUDE_DIRS})
...
kde4_add_executable(kfoo ${kfooSrcs})
target_link_libraries(kfoo ${KDEPIMLIBS_AKONADI_LIBS} )

Note: In KDE 4.0 and 4.1 these libraries all had the prefix "KDE4_" instead of "KDEPIMLIBS_". This was wrong because the "KDE4_" prefix is reserved exclusively for stuff coming from kdelibs. To keep compatibility, these variables are still available.

For details see the documentation at the top of kdelibs/cmake/modules/FindKdepimLibs.cmake .

How to use libraries from kdebase/workspace/

In kdebase/workspace/ there are several libraries which are installed and can be used by other projects. These are libraries for kwin, plasma, the screensaver and some more, see the documentation at the top of kdelibs/cmake/modules/FindKDE4Workspace.cmake for the full list of variables (they are all prefixed with "KDE4WORKSPACE_" ).

New: Exporting and importing targets

Starting with version 2.6 CMake can "export" targets. These exported targets can then be imported again by projects using these targets (libraries). These imported targets can then be used in the same way as normal targets and can be used to link against them. This is necessary to provide better linking support. At least on Windows it is necessary to link against debug versions of libraries in debug mode and against release versions in release mode, otherwise there will be crashes. This was kind-of supported by CMake via the debug and optimized keywords for TARGET_LINK_LIBRARIES(), but this was inflexible and and problematic. Exported targets now fully support different configuration types and flexible mapping between them.

Additionally it is now possible to have full control over the so-called link interface of a library. The link interface of library <foo> is the set of libraries against which projects linking against <foo> will be linked additionally. Details see below.

Exporting targets is a two-step process. First the targets have to be added to a set of exported targets, and then this export set has to be installed. You can give this export set arbitrary names. Both executable and library targets can be exported. This is done in the INSTALL(TARGETS ... ) command:

add_executable(hello ${helloSrcs})
add_library(bar SHARED ${barSrcs})

install(TARGETS hello EXPORT ExportedTargets DESTINATION bin )
install(TARGETS bar   EXPORT ExportedTargets DESTINATION lib )

The code shown above puts the two targets hello and bar into the (implicitely created) export set named ExportedTargets. This can be done in multiple directories to collect targets into export sets. Then the export set has to be installed, this means a CMake script file which defines these targets will be written by CMake and installed. This is done using install(EXPORT ):

install(EXPORT ExportedTargets DESTINATION ${SOME_INSTALL_DIR} 
                               FILENAME MyPackageConfig.cmake 
                               NAMESPACE MyPackage__ )

The command above specifies the install directory of the export file and the filename it will be given. NAMESPACE has the effect that the names of the exported targets will be the names of the original targets in the project prefixed with the given string. This makes name clashes between the imported targets and targets in other projects basically impossible. So this export files basically looks like this:

add_executable(MyPackage__hello IMPORTED)
add_library(MyPackage__bar SHARED IMPORTED)

set_target_properties(MyPackage__hello PROPERTIES 
                                       IMPORTED_LOCATION /usr/bin/hello )

set_target_properties(MyPackage__bar PROPERTIES 
                                     ...configuration specific properties )
...

This is automatically generated by CMake and you actually don't have to care about the concrete contents of this file.

Other projects can then include this file and use these targets.

More details at http://www.cmake.org/Wiki/CMake_2.6_Notes#Exporting_and_Importing_Targets .

New: The reduced link interface

In KDE 4.0 and 4.1 EXPORT_LIBRARY_DEPENDENCIES() was used in kdelibs/ and kdepimlibs/ for the installed libraries. This creates a file which lists all libraries of that project, and against which libraries they link. This file then had to be include()d by other projects. This way those projects transitively also linked against all libraries against which these libraries were linked when using these libraries. This is called the "link interface". Here's an example:

add_library(foo ${fooSrcs})
target_link_libraries(foo ${BAR_LIBS} ${BLUB_LIBS} ${ZOT_LIBS})
export_library_dependencies(...)

When then another project loads this file and links something against foo, it will be also linked against BAR_LIBS, BLUB_LIBS and ZOT_LIBS. While this is necessary for static libraries, it is not necessary for everything for shared libraries. For shared libraries only those libraries which are exposed in the public interface of the library should be in the link interface. If e.g. foo uses class BlubWidget in a public header, BLUB_LIBS should be in the link interface. This is done using the TARGET_LINK_LIBRARIES() command. The link interface set there is not considered when using EXPORT_LINK_LIBRARIES(), but it is used when exporting the target. So in order to actually benefit from the link interface, the export feature has to be used:

add_library(foo ${fooSrcs})
target_link_libraries(foo ${BAR_LIBS} ${BLUB_LIBS} ${ZOT_LIBS})
target_link_libraries(foo LINK_INTERFACE_LIBRARIES ${BLUB_LIBS})

install(foo EXPORT MyExports DESTINATION lib ...)

The second TARGET_LINK_LIBRARIES() doesn't cause any additional libraries to be linked against foo, it only specifies the link interface of the library foo when eported.

How to install libraries correctly now

Libraries are still installed using INSTALL(TARGETS ...).

But during installation projects can now also install cmake scripts which export targets, so they can be used by other projects.

So if a project installs a library, and creates an exported target for it, which can be imported by other projects then, two things have to be done additionally:

  1. Set the link interface
  2. Export the target

In KDE this is the case for all libraries in kdelibs, kdepimlibs and the libraries in kdebase/workspace (and I think also kdevplatform).


Set the link interface

This defines against which libraries a package which links to this library will be linked additionally automatically. By default the link interface is empty. This is only the case for libraries created using KDE4_ADD_LIBRARY(), if a library is created using the standard ADD_LIBRARY() the link interface is "full" by default. Put those libraries in the link interface from which symbols are used in the public interface of your library. If e.g. a QWidget appears in your public headers, but nothing from zlib, put QT_QTGUI_LIBRARY in the link interface:

kde4_add_library(kfoo ${kfooSrcs})
target_link_libraries(kfoo ${ZLIB_LIBRARIES} ${QT_QTGUI_LIBRARY})
target_link_libraries(kfoo LINK_INTERFACE_LIBRARIES ${QT_QTGUI_LIBRARY})

Export the library

This is done in two steps. First add the library to a set of exported targets, and then install this export set as explained above. Here we chose the name MyExports for the export set:

kde4_add_library(kfoo ${kfooSrcs})
...
install(TARGETS kfoo EXPORT MyExports ${INSTALL_TARGETS_DEFAULT_ARGS})
install(EXPORT MyExports args_see_above... )

New option: KDE4_USE_COMMON_CMAKE_PACKAGE_CONFIG_DIR

With KDE 4.2 and the required CMake 2.6.2 the FIND_PACKAGE() command now supports finding installed configuration files of packages. If the package is named <package>, their configuration file is named <package>Config.cmake. CMake 2.6.2 supports searching these files in lib/<package>/cmake/, a version number may also be included in the directory name: lib/<package>-x.y.z/cmake/. Starting with version 2.6.3 of CMake (soon to be released as of January 2009), it also supports searching these configuration files in lib/cmake/<package>/ (also with optional version number). This may be preferred by packagers. If a KDE package installs such a configuration file it should check this option, and if enabled install its <package>Config.cmake file to lib/cmake/<package>/ instead of lib/<package>/cmake/. More at http://www.cmake.org/Wiki/CMake_2.6_Notes#Packages .

This option is only available with CMake >= 2.6.3, in this case it is ON by default. Otherwise it is OFF.

If a package has been installed with this option enabled (which can only be the case with CMake >= 2.6.3), at least CMake 2.6.3 is also required for building packages which use this package. CMake < 2.6.3 will not find their installed <package>Config.cmake files.

Currently only kdepimlibs/ and kdebase/workspace/ install such configuration files.

Removed option: KDE4_ENABLE_EXPERIMENTAL_LIB_EXPORT

The option KDE4_ENABLE_EXPERIMENTAL_LIB_EXPORT was new in KDE 4.1 and enabled the reduced link interface. This is now always the case, so the option became unnecessary.

Removed option: KDE4_USE_ALWAYS_FULL_RPATH

KDE4_USE_ALWAYS_FULL_RPATH was by default on. In this case executables, libraries and plugins were linked with RPATH. This caused relinking of these binaries during make install, which takes some time. If it was off, only executables were linked with RPATH, and their RPATH had to be sufficient also for the shared libraries and plugins. This can be the case, but doesn't have to. Applications which were not marked as RUN_UNINSTALLED were built directly with their install RPATH. This saved a lot of time during make install but the resulting install might not get the correct libraries. Since version 2.6 CMake can now edit the RPATH inside binaries, which compared to relinking takes no time.

That's why this option is not necessary anymore, and now all executables, libraries and plugins are always built with their build tree RPATH and then installed with their install tree RPATH, so they always get their correct libraries, both when executed from the build tree as well as when executed from the install tree.

So, if you set this option somewhere, this is no problem with KDE 4.2, it now always does the right thing.

Guide for writing CMake files

This is not really a change.

Now using all-lowerspace for commands is recommended. It is even more recommended to use consistent casing within one file.

There is now a guide available: Coding style for CMake files

And there is also a guide on how to keep source compatibility for CMake FindXXX.cmake modules: Source compatiblity with CMake

Specifying the minimum required version of some package

With KDE 4.0/4.1 the minimum required version of some package could by specified using module-specific variables. E.g. for the minimum version of kdelibs you had to do the following:

set(KDE_MIN_VERSION "4.1.0")
find_package(KDE4 REQUIRED)

Now with CMake 2.6 the FIND_PACKAGE() command supports a minimum version directly as first argument after the package name, but it also has to be supported by the respective FindXXX.cmake module. So now you can do:

find_package(KDE4 "4.2" REQUIRED)

This works, but there is one problem when doing this for finding KDE4: this way to specify the version is not supported by KDE 4.0/4.1, they just ignore this version information. So to make sure this also works with KDE 4.0/4.1 you have to use the KDE_MIN_VERSION variable.

New modules should support only the new way to do it.

Misc. new CMake features

Finding installed packages

Starting with CMake 2.6 the FIND_XXX() commands now search by default the respective subdirectories under the current CMAKE_INSTALL_PREFIX and the install prefix of CMake itself.

Additionally now the environment variable CMAKE_PREFIX_PATH is supported. This is a list of directories which are searched by the FIND_XXX() commands with the respective subdirectories appended, i.e. include/ for FIND_PATH() and FIND_FILE(), lib/ for FIND_LIBRARY() and bin/ for FIND_PROGRAM(). These directories are searched before all other directories, so this can be used to make CMake find the versions of packages you want it to find.

E.g. the following can be used to make CMake find required packages in the given directories instead of e.g. a Qt4 installed in /usr/lib :

$ export CMAKE_PREFIX_PATH=/opt/qt-copy:/opt/kdesupport:/opt/kdelibs:/opt/kde4
$ cmake args...

Comparing version strings

CMake now supports comparing version strings consisting of version 1 to 4 version components:

  • if(<str> VERSION_GREATER "1.2.3")
  • if(<str> VERSION_LESS "1.2.3")

Functions

CMake now has a FUNCTION() and an ENDFUNCTION() commands. They can be used similar to MACRO()/ENDMACRO(), but variables created (i.e. SET() ) inside them stay local. To modify a variable outside the function, e.g. as return value, do the following:

function(foo)
   set(var1 "hello")
   set(var2 "world" PARENT_SCOPE)
endfunction(foo)

foo()
message(STATUS "${var1} ${var2}")

This will print only "world", since var1 is local to the function.

Return()

CMake now has a RETURN(), which can be used to return from function()s and also from processed files. See the man page for details.

Elseif()

CMake now has a ELSEIF().

Simple calculations

CMake has a MATH() command, which can be used for simple calculations, it supports the basic mathematical operations.

cmake-gui

cmake-gui is a Qt4 based GUI to CMake. It makes running CMake, setting options, checking results and selecting the generator easier. There are four ways how to invoke cmake-gui:

  • in the build directory, the source directory as argument:
kdebase/build/$ cmake-gui ..
  • in any directory, the build directory as argument: if the directory contains already a CMakeCache.txt, cmake-gui will work on this build tree:
kdebase/build/ $ cmake-gui .

As said before, this works only if the build tree has already been configured before.

  • Using "make edit_cache": the edit_cache target invokes the CMake cache editor. By default this is the curses-based ccmake. But if the build tree was generated using cmake-gui, or if you executed cmake-gui previously on the build tree, then "make edit_cache" invokes cmake-gui instead.
  • in any directory without arguments: then it starts with the source and build directories from the previous run


http://www.neundorf.net/pics/cmake-gui-kdelibs.png .

CMake policies

Starting with version 2.6.0 CMake supports so called policies. These policies are used to provide full compatibility between different CMake versions. In some cases bugfixes in CMake can mean that CMake handles something different now. This may break projects which were set up to expect the buggy behaviour. In order to keep them working for each such change a policy is introduced. Each policy can be set to NEW or OLD behaviour. With OLD behaviour the old (buggy) behaviour is active, with NEW behaviour the new and fixed behaviour is active.

The default for the policies depends on the required minimum version of CMakeas defined with CMAKE_MINIMUM_REQUIRED(). The policies can be manipulated using the new CMAKE_POLICY() command.

The policies are set up for KDE4 in FindKDE4Internal.cmake. They are mostly set to be 2.4.x compatible in order to keep source compatibility with KDE 4.0/4.1.

More details at http://www.cmake.org/Wiki/CMake_Policies .

Improved documentation

CMake can generate the documentation additionally to HTML, plain text and man pages now also in docbook format. This can be processed further with docbook utilities to other formats.

Also the documentation has been split into multiple pages:

  • cmake: everything all-in-one
  • cmakecommands: the recommended commands
  • cmakecompat: commands not recommended for use, kept for compatibility
  • cmakemodules: the documentation for all modules coming with CMake
  • cmakevars: documentation for variables
  • cmakeprops: documentation for global, directory, target and source file properties
  • cmakepolicies: documentation of the CMake policies

Support for Cross compiling

Since version 2.6 CMake supports cross compiling. But KDE4 is not yet ready for cross compiling. Adding support for cross compiling KDE4 is on the TODO for 4.3.

Support for KDevelop3, Eclipse and CodeBlocks projects

CMake is not just a Makefile generator but a project generator. This means, it can not only generator Makefiles, but also project files for a variety of IDEs. CMake 2.4 already supported generating project files for KDevelop3, XCode and MS Visual Studio. Version 2.6 adds support for Eclipse CDT4 and CodeBlocks. This means you can now tell CMake to generate project files for these IDEs and use them to work on your KDE project.

You can do this either via the command line, this is quite a lot of typing:

$ cmake -G "CodeBlocks - Unix Makefiles" moreargs...

You can also use cmake-gui instead, which makes this much easier.