Skip to content

Using CMake with Qt 5

CMake is a buildsystem generator developed in the open, and widely used for Qt based development. Especially when creating large or complex software, CMake can be more suitable to use than QMake. KDE was even the tipping point for the popularity of CMake in general, and with Qt 4 in particular, according to Bill Hoffman. KDAB engineers have contributed some features to CMake to ensure that the Qt 5 support for CMake is as good as possible.

KDAB contributed some new CMake Config files for Qt 5 to ensure that the integration between Qt and CMake can be even better. The updated documentation for using CMake with Qt 5 is has been reviewed and generated and repeats the relevant information in this blog post.

Finding Qt 5

One of the major changes when using CMake with Qt is the result of increased modularity in Qt itself. Whereas when finding Qt 4, with CMake you use

  find_package(Qt4 COMPONENTS QTCORE QTGUI)

To find Qt 5 you can find all of the modules you wish to use with separate commands:

  find_package(Qt5Widgets)
  find_package(Qt5Declarative)

There is likely to be a way in the future to specify the specific modules in one command, but this will not be available with Qt 5.0:

  find_package(Qt5 COMPONENTS Widgets Declarative)

Building Qt 5 projects with CMake

Once the package has been found, Qt 4 users would use the CMake variables ${QT_INCLUDES} to set the include directories while compiling, and ${QT_LIBRARIES} or ${QT_GUI_LIBRARIES} while linking. Users of CMake with Qt 4 may have also used the ${QT_USE_FILE} to semi-automatically include the required include directories and required defines.

With the modular Qt 5 system, the variables will instead be ${Qt5Widgets_INCLUDE_DIRS}, ${Qt5Widgets_LIBRARIES}, ${Qt5Declarative_INCLUDE_DIRS}, ${Qt5Declarative_LIBRARIES} etc for each module used.

This is a source-incompatibility in your CMake based buildsystem which will affect porting from Qt 4 to Qt 5. Luckily though, it is easy to add source compatibility back to the CMake variables and macros using some simple variable mappings.

Building executables with Qt 5 is slightly more complex than with Qt 4. One of the changes to how Qt 5 is built and packaged compared to Qt 4 is that the -reduce-relocations configure option became the default. The effect of this is that compilations are run with the -Bsymbolic-functions option, which makes function pointer comparison ineffective, unless the -fPIE flag is also supplied when building executables, or -fPIC when building libraries for position independent code.

If Qt is configured manually, it is of course possible to configure with -no-reduce-relocations and avoid the issue, but the default will be a requirement for all third parties to add compiler flags for position independent code. This can be done in the normal way with CMake:

  set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}
    ${Qt5Widgets_EXECUTABLE_COMPILE_FLAGS}")

There is a Qt5&ltmodule>_EXECUTABLE_COMPILE_FLAGS variable for each module available in Qt 5 which expands to either -fPIE or the empty string, depending on how Qt was configured. Hoewever, the -fPIE flag is really only for executables and should not be used for libraries.

Setting -fPIC globally even when building executables may also work sufficiently, but shouldn’t be the first option.

  set(CMAKE_CXX_FLAGS "-fPIC")

Together with some other newer features in CMake, such as automated moc invokation, a simple CMake based build system using Qt 5 which is roughly equivalent to a Qt 4 buildsystem will look something like this:

  cmake_minimum_required(2.8.7)
  project(hello-world)

  # Tell CMake to run moc when necessary:
  set(CMAKE_AUTOMOC ON)
  # As moc files are generated in the binary dir, tell CMake
  # to always look for includes there:
  set(CMAKE_INCLUDE_CURRENT_DIR ON)

  # Widgets finds its own dependencies (QtGui and QtCore).
  find_package(Qt5Widgets REQUIRED)

  # The Qt5Widgets_INCLUDES also includes the include directories for
  # dependencies QtCore and QtGui
  include_directories(${Qt5Widgets_INCLUDES})

  # We need add -DQT_WIDGETS_LIB when using QtWidgets in Qt 5.
  add_definitions(${Qt5Widgets_DEFINITIONS})

  # Executables fail to build with Qt 5 in the default configuration
  # without -fPIE. We add that here.
  set(CMAKE_CXX_FLAGS "${Qt5Widgets_EXECUTABLE_COMPILE_FLAGS}")

  add_executable(hello_world main.cpp mainwindow.cpp)

  # The Qt5Widgets_LIBRARIES variable also includes QtGui and QtCore
  target_link_libraries(hello_world ${Qt5Widgets_LIBRARIES})

You can see though that there is a lot of repetition in the snippet, and a lot of things to take care of using.

The Qt5Widgets_EXECUTABLE_COMPILE_FLAGS use is particularly forgettable, and even difficult to get right – it should not be used if building shared libraries for example, so it requires some maintenance if that is required within the same scope as building an executable.

There is even a subtle bug which people who have unit tested their Qt 4 core-only code will know. If the -DQT_GUI_LIB definition is set (which happens if using QT_USE_FILE for example), all unit tests need to link to QtGui, even though it is not used by the tests.

That is because of some magic in the header files of QtTest. The workaround is either careful scoping of targets and directories, or tricky manual manipulation of the definitions or variables controlling how Qt 4 is found.

Towards more modern CMake usage

Starting with CMake 2.8.8, we can do a lot better:

  cmake_minimum_required(2.8.8)
  project(hello-world)

  # Tell CMake to run moc when necessary:
  set(CMAKE_AUTOMOC ON)
  # As moc files are generated in the binary dir, tell CMake
  # to always look for includes there:
  set(CMAKE_INCLUDE_CURRENT_DIR ON)

  # Widgets finds its own dependencies.
  find_package(Qt5Widgets REQUIRED)

  add_executable(hello_world main.cpp mainwindow.cpp)

  qt5_use_modules(hello_world Widgets)

The qt5_use_modules CMake function encapsulates all of the set-up required to use a Qt module. It can be used with multiple arguments at once for brevity, such as:

  qt5_use_modules(hello_world Widgets Declarative)

This is similar to how qmake operates:

  TARGET = hello_world
  QT += widgets declarative

All properties are scoped to the particular target the function is used with, instead of being scoped to the CMakeLists.txt file it appears in, and affecting all libraries and executables.

For example in this snippet:

  add_executable(hello_world main.cpp mainwindow.cpp)
  add_library(hello_library lib.cpp)
  add_executable(hello_coretest test.cpp)

  find_package(Qt5Widgets)

  qt5_use_package(hello_world Widgets)
  qt5_use_package(hello_library Core)
  qt5_use_package(hello_coretest Test)

Because all of the settings are scoped to the target (executable or library) they operate on, the -fPIE doesn’t get used when building the hello_library library, and the -DQT_GUI_LIB doesn’t get used when building hello_coretest.

It’s a much cleaner way to write CMake based build systems.

Looking forward, we expect a similar but more powerful feature to be possible in upstream CMake too, so that you can expect to be able to use a similar function with any CMake package.

Implementation details

One of the features of CMake that many developers who used it will be familiar with is Find files. The idea is to write a Find file for each dependency your project depends on, or use an existing Find file provided elsewhere. CMake provides a large set of Find files already, and KDE is preparing to make the Find files developed through the years available to all users of CMake.

One of the Find files provided by CMake is the FindQt4.cmake file. This file is responsible for finding Qt on your system so that you can invoke:

  find_package(Qt4)

That Find file makes available the variables ${QT_INCLUDES} to set the location of header files, and ${QT_QTGUI_LIBRARIES} for linking, for example.

One of the disadvantages of this file being part of CMake, and not part of Qt, is that the file could get out-of-date. For example, when Qt 4.6 was released in December 2009 it included a new QtMultimedia module. Support for that module was not complete until CMake 2.8.2, released in June 2010.

That means that if someone wanted to use QtMultimedia, they could either have to wait for and then depend on the new CMake release, or attempt to copy the Find file into their project to work with their existing CMake version. The copying may not even work if the updated Find file uses features of the newer CMake.

Behind the scenes, Qt 5 is now found in a slightly different way too. Apart from making it possible to find your dependencies using a Find file, CMake is also able to read files provided by the dependency itself to locate the libraries and header files. Such files are called Config files, and usually they are generated by CMake itself.

A Qt 5 build, will also generate the CMake Config files, but without causing Qt 5 to depend on CMake itself.

The primary benefit of this is that the features of Qt (and the modules of Qt) which can be used with CMake do not depend on the version of CMake being used. All Qt Essentials Modules and Qt Addons Modules will create their own CMake Config file, and the features provided by the modules will be made available through the CMake macros and variables immediately.

As new modules become available, they will also be usable with CMake as soon as they are checked into a repository, so experimenting or prototyping can begin immediately and does not have to wait for a new CMake release.

Another benefit of using Config files instead of find files is that the config files have access to all aspects of the build of Qt itself including how it was configured and where it is installed to, which helps keep complexity under control when supporting static builds, and cross compiling for example.

FacebookTwitterLinkedInEmail

Categories: CMake / KDAB Blogs / KDAB on Qt

32 thoughts on “Using CMake with Qt 5”

  1. One of the main advantages of:

    find_package(Qt4 COMPONENTS QTCORE QTGUI)

    over:

    find_package(Qt5Widgets)
    find_package(Qt5Declarative)

    is the QT_LIBRARIES would contain all the required libraries, i. e. Qt5Widgets and its dependencies, therefore you were able to do just:

    target_link_libraries( myqtapp ${QT_LIBRARIES})

    How is this resolved with this “one FindQtModuleName.cmake per Qt module” approach?

  2. steveire

    Hi Pau,

    There are two solutions to that issue. One of them is that we will likely have a single Qt5Config.cmake file in the future (Qt 5.1 maybe?) which will probably work similar to how you describe.

    The initial feature set for the CMake stuff couldn’t be complete for Qt 5.0 because of time constraints, and because CMake required some new features (Alex Neundorf added lots of required stuff in CMake 2.8.8).

    The other solution is that finding Qt5Widgets already finds its dependencies. So

    find_package(Qt5Widgets)

    is enough for that.

    I think it should be preferred to just use only

    find_package(Qt5Core)

    and then use

    qt5_use_modules(myqtapp Widgets Qml)

    to find and link to those dependencies. You still need to list the required modules in only one place in the code.

    1. Stephen,

      Currently you can already do:

      find_package(Qt4 COMPONENTS QtDeclarative)

      and it will find the dependencies of QtDeclarative, no need for the additional find_package(Qt4Core).

      What’s the advantage of the one-finder-per-module you are proposing over single-finder-for-all-modules like we have for Qt4?

      All this “qt5_use_modules” looks a lot like what we had in the past with FindQt4.cmake, when you had to use variables:

      set(QT_USE_QTDECLARATIVE TRUE)

      To me, it’s like we are going 4 years in the past 😕

      1. steveire

        Hi Pau,

        Yes, FindQt4 is also dependencies-aware, as you say.

        One reason we went for the one-finder-per-module solution (It’s in the repo and working, not just a proposal) instead of single-finder-for-all-modules was that CMake upstream didn’t have all the required features when the development started (http://thread.gmane.org/gmane.comp.programming.tools.cmake.devel/3119). At the time development started, CMake 2.8.6 was the most recent version. CMake 2.8.8 possibly now has all the required features, but it will still need to be written and tested.

        Additionally, this is what we were able to achieve concensus on, which allowed the introduction of a catch-all solution in the future (http://lists.qt-project.org/pipermail/development/2011-October/000119.html).

        The main difference between set(QT_USE_QTDECLARATIVE TRUE) and qt5_use_modules(myqtap Declarative) is that the latter is scoped to the target, instead of being scoped to the directory it is used in. That is very much forward-looking in the way CMake is used, not a 4 year old concept.

        I’m sure the monolithic directory-scoped method can work, but extra hands to make it finished are of course welcome. So if you want to be able to do that, feel free to start the work :).

        Beyond that, I think a more suitable place to have this discussion would be the kde-buildsystem or Qt development mailing list. 🙂

        Thanks for this feedback!

  3. # The Qt5Widgets_INCLUDES also includes the include directories for
    # dependencies QtCore and QtGui
    include_directories(${Qt5Widgets_INCLUDES})

    -> This should probably be INCLUDE_DIRS to avoid the issues for the next reader like me. 🙂

  4. You have mentioned that in the description, but I presume people also build libraries, not just executables. I have not tried yet as I am not front of my development environment, but I am curious: can we use “qt5_use_modules” for “add_library” too as it would make sense to me to simplify the process for that, too, and not just executables?

  5. Due to the previous reasoning, I would personally prefer the variable “Qt5&ltmodule>_EXECUTABLE_COMPILE_FLAGS” be “Qt5&ltmodule>_COMPILE_FLAGS” as it could not be only used for executables, and the naming could be more generic.

    Perhaps it is too late now unfortunately to change as people started to use it. It is a good question if there is a use case where executables and libraries have to be separated into different variables because cmake has something that cannot figure out behind the scene.

  6. This declaration

    set(CMAKE_CXX_FLAGS “${Qt5Widgets_EXECUTABLE_COMPILE_FLAGS}”)

    overwrites the previous value of CMAKE_CXX_FLAGS. You should instead do this:

    set(CMAKE_CXX_FLAGS “${CMAKE_CXX_FLAGS} ${Qt5Widgets_EXECUTABLE_COMPILE_FLAGS}”)

    1. steveire

      True. Even better though would be to use CMake 2.8.11 and not need to write that at all, or to use CMake 2.8.9 and use


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

  7. Are there tools to create Qt4/Qt5 agnostic CMakeLists from Qt4 oriented CMakeLists? OK, I see I can use FindQt5Transitional.cmake to change my find lines, but it’s not enough. It’s a pain to change manually all lines like QT4_WRAP_CPP, QT4_WRAP_UI, QT_USE_FILE, …

    1. steveire

      I recommend using sed to port things like that. FindQt5Transitional.cmake wraps the macros you mention anyway.

      1. Sorry for late response (I thought I would get notification to my mail on reply).

        So all I have to do to switch from Qt 4 to Qt 5 in the case of CMake is to add line:
        include(“FindQt5Transitional.cmake”)
        Am I right?

  8. Is there any easy way to use .ui files in a Qt5 CMake based project ? That’s the only thing I need to finally port my project using Qt5 from QMake to CMake.

    Kevina

  9. The .cmake files for Qt Essentials Modules are not included in the standard download for windows. CMake is unable to find these files and cannot generate my project which depends on QT5. You said QT5 generates these files, but they are not present on my PC. How do I instruct it to generate them?

    1. steveire

      I think the Qt 5.0.0 release didn’t generate the cmake files on Windows. Which version did you test?

  10. I have followed the steps in your article, but I keep getting the same error: QGLWidget: No such file or directory. Is QGLWidget outdated?

  11. I can’t find a cmake or QT forum for newbies & even Google can’t find the answer to simple question:
    am trying to build a prog in Mint 15 using cmake gui, have installed QT5 but get error QT5_DIR-NOTFOUND plus Add the installation prefix of “Qt5” to CMAKE_PREFIX_PATH or set “Qt5_DIR”
    to a directory containing one of the above files. If “Qt5” provides a
    separate development package or SDK, be sure it has been installed.

    If someone can explain what that means & how to resolve it, or at least point me to a source that would answer this & any future questions would be grateful.

  12. Where can one download these modules? I need to be able to use Qt5 in with cmake scripts for my client. Do you have a beta copy somewhere? I see no direct links on this page.

  13. Am am trying to build amarok in my ubuntu 12. I have installed Qt5 and Qt4.8.1 both in my PC. When I am trying to build and I am getting the following error in Cmake.

    CMake Error at /usr/share/cmake-2.8/Modules/FindPackageHandleStandardArgs.cmake:108 (message): Could NOT find Qt4: Found unsuitable version “4.8.2”, but required is at least “4.8.3” (found /usr/bin/qmake) Call Stack (most recent call first): /usr/share/cmake-2.8/Modules/FindPackageHandleStandardArgs.cmake:313 (_FPHSA_FAILURE_MESSAGE) /usr/share/cmake-2.8/Modules/FindQt4.cmake:1393 (FIND_PACKAGE_HANDLE_STANDARD_ARGS) CMakeLists.txt:83 (find_package)

  14. This “modularity” is fucking up my complete compile process.
    Moving to QT5 triples the size of my cmake files and makes them completely unmanagable. Especially in combination with cmake. I will reconsider using QT at all, if you think we all have the time to rewrite our building process just to adjust to QT.

  15. If I understand well, the occurrences of qt5_use_package in your blog post should be replaced by qt5_use_modules.

    Thanks for this great explanation anyway.

Leave a Reply

Your email address will not be published. Required fields are marked *

By continuing to use the site, you agree to the use of cookies. More information

The cookie settings on this website are set to "allow cookies" to give you the best browsing experience possible. If you continue to use this website without changing your cookie settings or you click "Accept" below then you are consenting to this.

Close