CMake usage requirements in KDE Frameworks

Stephen Kelly steveire at gmail.com
Sat Mar 16 13:24:16 UTC 2013


Hi there,

== What happened? ==

I've pushed a commit which removed much of the CMake code which calls 
include_directories(). 

 http://commits.kde.org/kdelibs/56d571a223e00ffed4cb682eb098c5b1e347fa70

This mail is an explanation of things you might see in the patch or in cmake 
code you read in frameworks.

== Why? ==

As of CMake 2.8.11, they are not needed for certain targets, namely Qt and 
in-tree KDE Frameworks targets. Specifying incorrect include dirs and 
incompletely specifying include dirs is a common source of build problems in 
KDE, so this should be more convenient for everyone hacking on the KF5 
frameworks repo.

This will be expanded later to also cover installed KDE targets, which means 
downstreams such as plasma-frameworks won't need to use 
include_directories(${KF5_INCLUDE_DIRS}) etc anymore.

== How do we use it? ==

As of CMake 2.8.11, targets (those created by add_library and 
add_executable) can now define an INTERFACE_INCLUDE_DIRECTORIES property. 
When a downstream links to an upstream which has such a property, it is read 
and the include directories are used. For example:

 add_library(foo ...)
 set_property(TARGET foo PROPERTY INTERFACE_INCLUDE_DIRECTORIES 
"/opt/foo/include")

 add_library(bar ...)
 # This line not only links bar to foo, but also makes bar compile with -
I/opt/foo/include, because that is from the interface of foo
 target_link_libraries(bar foo)

We can't just remove all include_directories() calls for other packages like 
zlib for example, because some action is required to make the new features 
work, and that action has only been taken for Qt and KDE targets so far.

The target_link_libraries command also now has similar behavior for 
COMPILE_DEFINITIONS, and whether to use POSITION_INDEPENDENT_CODE or not, 
and whether to link to qtmain.lib on Windows.

If CMake is not already mysterious magic to you, read on to see how it 
works. 

My talk at Qt DevDays last year has all the information too: 
http://www.youtube.com/watch?v=GJ0kMsLbk6Q 

The wiki also has more info:

 http://community.kde.org/Frameworks/Epics/CMake_target_usage_requirements

== How does it work? ==

Of course, in-tree include dirs that should be used are different from 
installed dirs. For example, when karchive is used by a target in the 
frameworks buildsystem, the include dirs used should be

 -I${CMAKE_CURRENT_SOURCE_DIR}/ -I${CMAKE_CURRENT_BINARY_DIR}/

but when it is used from its installed location, the include dirs used 
should be:

 -I${INSTALL_PREFIX}/include

The new features also support that, and it looks like this:

 set_property(TARGET foo
   PROPERTY INTERFACE_INCLUDE_DIRECTORIES
      "$<BUILD_INTERFACE:${CMAKE_CURRENT_BINARY_DIR};
${CMAKE_CURRENT_SOURCE_DIR}>"
      "$<INSTALL_INTERFACE:$<INSTALL_PREFIX>/include>"
 )

There is also a convenience built-in to CMake for the BUILD_INTERFACE

 set(CMAKE_BUILD_INTERFACE_INCLUDES ON)

which automatically adds

 "$<BUILD_INTERFACE:${CMAKE_CURRENT_BINARY_DIR};
${CMAKE_CURRENT_SOURCE_DIR}>"

to the INTERFACE_INCLUDE_DIRECTORIES of each target. In the future there 
will also be a convenience to add

 "$<INSTALL_INTERFACE:$<INSTALL_PREFIX>/include>"

to each target, but we don't have that yet. To avoid history-noise, I 
haven't defined the INSTALL_INTERFACE at all for the Frameworks targets 
until we have the convenience.

Note also that some includes are 'PUBLIC', some are 'PRIVATE', and some are 
'INTERFACE only'. Private include directories are directories which are 
needed to build a target, but not needed to use it. For example KArchive 
uses zlib internally but the zlib headers don't appear in public karchive 
headers, so those includes are private. However, it uses QtCore headers in 
its public headers, so users of KArchive also have to compile with the 
include dirs of QtCore. That means that QtCore include directories are 
PUBLIC for KArchive.

Private include directories are listed in the INCLUDE_DIRECTORIES property. 
Public include directories are listed in the INCLUDE_DIRECTORIES and in the 
INTERFACE_INCLUDE_DIRECTORIES property. If a directory appears in the 
INTERFACE_INCLUDE_DIRECTORIES but not in the INCLUDE_DIRECTORIES property of 
a target, that means that it will not be used to compile the target, but 
will be used by the user of the target.

A new command is also introduced in CMake 2.8.11 to set this stuff 
conveniently:

 add_library(foo ...)

 target_include_directories(foo PRIVATE "/foo/private")
 target_include_directories(foo PUBLIC "/foo/public")
 target_include_directories(foo INTERFACE "/foo/interface/only")

For some targets in KDE Frameworks, the ${CMAKE_CURRENT_SOURCE_DIR} and 
${CMAKE_CURRENT_BINARY_DIR} are not the correct directories to use for the 
BUILD_INTERFACE, because the source is split into multiple directories. 
Therefore, for some targets like kcoreaddons you'll see this to set the 
correct directories needed for building KCoreAddons itself, and for users of 
KCoreAddons: 

 set(kcoreaddons_INCLUDE_DIRS
     ${CMAKE_CURRENT_BINARY_DIR}/io/
     ${CMAKE_CURRENT_SOURCE_DIR}/io/
     ${CMAKE_CURRENT_SOURCE_DIR}/jobs/
     ${CMAKE_CURRENT_SOURCE_DIR}/randomness/
     ${CMAKE_CURRENT_SOURCE_DIR}/kernel/
     ${CMAKE_CURRENT_SOURCE_DIR}/text/
     ${CMAKE_CURRENT_SOURCE_DIR}/caching/
 )

 target_include_directories(KCoreAddons PUBLIC
   "$<BUILD_INTERFACE:${kcoreaddons_INCLUDE_DIRS}>")


In some cases, you'll also see more complex expressions used as 'include 
directories':

 target_include_directories(kdecore PRIVATE
  $<TARGET_PROPERTY:Qt5::PrintSupport,INTERFACE_INCLUDE_DIRECTORIES>
 )

This is a generator expression. It means 'kdecore should be compiled with 
the INTERFACE_INCLUDE_DIRECTORIES from Qt5::PrintSupport'. Usually this is 
done automatically when using

 target_link_libraries(kdecore Qt5::PrintSupport)

but in the case of kdecore, it does not need to link to Qt5::PrintSupport, 
it only needs the include directories from it, so we can specify only that.

Run

 cmake --help-command target_include_directories
 
to see what generator expressions are possible.

This feature is called 'usage requirements'. Targets specify the 
requirements for using them, which can so far include 
INTERFACE_INCLUDE_DIRECTORIES, INTERFACE_COMPILE_DEFINITIONS and 
INTERFACE_POSITION_INDEPENDENT_CODE.

The INTERFACE_COMPILE_DEFINITIONS work similarly to the 
INTERFACE_INCLUDE_DIRECTORIES, and there is a similar porcelain command:

 target_compile_definitions(foo PRIVATE BUILDING_FOO)
 target_compile_definitions(foo PUBLIC BUILDING_AND_USING_FOO)
 target_compile_definitions(foo INTERFACE ONLY_USING_FOO)

 target_link_libraries(bar foo)

will cause foo to be compiled with -DBUILDING_FOO -DBUILDING_AND_USING_FOO 
and bar to be compiled with -DONLY_USING_FOO.

This solves the 'Qt testlib problem'. The problem is that QTEST_MAIN might 
be defined to use QCoreApplication, QGuiApplication or QApplication, 
depending on whether QT_GUI_LIB or QT_WIDGETS_LIB is defined. In the past, 
this caused linking errors if the correct define was not defined and 
consistent with what was linked. As the Qt5::Gui target now defines 
QT_GUI_LIB in its INTERFACE_COMPILE_DEFINITIONS, and Qt5::Widgets defines 
QT_WIDGETS_LIB in its INTERFACE_COMPILE_DEFINITIONS, using

 target_link_libraries(sometest Qt5::Widgets)

is enough to do the right thing and add the correct define.

The third part of 'usage requirements' defined in CMake 2.8.11 so far is the 
INTERFACE_POSITION_INDEPENDENT_CODE. With Qt 5 up to now we've had to have 
code like this in our CMakeLists:

 if (Qt5_POSITION_INDEPENDENT_CODE)
   set(CMAKE_POSITION_INDEPENDENT_CODE ON)
 endif()

because otherwise code using Qt 5 would not compile. Code like that is no 
longer necessary as target_link_libraries is now aware that Qt5::Core 
requires POSITION_INDEPENDENT_CODE:

 add_executable(foo)
 # The POSITION_INDEPENDENT_CODE property is enabled on foo, causing -fPIE 
to be used:
 target_link_libraries(foo Qt5::Core)

Of course, this doesn't require explicit listing of Qt5::Core, but linking 
to any Qt5 target will have the same effect:

 target_link_libraries(foo Qt5::Widgets)

Note that these features only work in KDE Frameworks currently because we 
use ECM to populate the INTERFACE_ properties on the Qt5 imported targets:

 https://projects.kde.org/projects/kdesupport/extra-cmake-
modules/repository/revisions/master/entry/find-
modules/FindQt5Transitional.cmake#L57

Using ECM for that is a temporary situation until CMake 2.8.11 final is 
released and

 https://codereview.qt-project.org/#change,46437

can be committed. So, be aware that you should use Qt5Transitional to find 
Qt5 modules like this:

 find_package(Qt5Transitional REQUIRED Svg ...)

Not like this (until the above patch lands):

 find_package(Qt5Svg ...)


The fourth part of the 'usage requirements' feature in CMake 2.8.11 is that 
the qtmain.lib library is automatically linked to executables on Windows if 
the WIN32_EXECUTABLE property is enabled.

So this works:

 add_executable(foo WIN32 ...)
 target_link_libraries(foo Qt5::Widgets) # No need to list Qt5::WinMain

== Debugging ==

Because the origin of include directories is somewhat 'hidden' now in the 
target_link_libraries calls, a new debugging facility has been added which 
gives a backtrace showing where each include directory comes from for a 
target. That is enabled by using:

 set(CMAKE_DEBUG_TARGET_PROPERTIES INCLUDE_DIRECTORIES)

== What about Qt4? ==

These features are also enabled for Qt 4 in CMake 2.8.11, so you can use 
them in personal projects which can require that version of CMake:

 find_package(Qt4 REQUIRED)

 add_executable(foo ...)
 # Uses the correct includes and defines, no need for QT_USE_FILE, 
QT_INCLUDES, QT_DEFINITIONS or QT_QTMAIN_LIBRARIES on Windows.
 target_link_libraries(foo Qt4::QtGui)


== Questions? ==

If you have any questions or feedback about this, please don't hesitate to 
ask. Especially if you hit a problem with this stuff, please let me know.

Thanks,

Steve.





More information about the Kde-buildsystem mailing list