Qt4 with cmake

From qtnode

Jump to: navigation, search
Qt_with_cmake
* Qt3_with_cmake
* Qt4_with_cmake

cmake can be used for qt3 and qt4 as well as for kde in general. however the macro names for the qt4 version have also a 4 in their name as for example:

  • QT4_INSTALLED is set to TRUE if qt4 is found.
  • QT3_INSTALLED is set to TRUE if qt3 is found.

Contents

use case

initial conversion to cmake

initially developer changes project from qmake to use cmake

the first big change is that cmake uses a CMakeLists.txt per folder, that means in comparison to qmake: you have several files (all with the same name all over your project dir).

% du -a | grep CMakeLists.txt                    1032 2 pts/10 ~/Desktop/projects/springrts.com-random-map-generator
4       ./CMakeLists.txt
4       ./src/CMakeLists.txt

In the above example we have two CMakeLists.txt files. However if one wants to use doxygen to document the code one could as well add another one for generating the docs in /doc. If you want to use cmake with doxygen, see [1]. However, doing so would not be qt related, so we skip that here.

The second big change is that 'cmake' uses 'out of source' builds by default. 'qmake' could be configured to write moc/resources/uic fils into a temporary directory of choice using MOC_DIR [2] and other equivalent options but the design was not 'out of source'. Forcing 'qmake' to do so can be very tricky (a good example for doing so is the mumble/murmurd project). It can be very annoying at first finding out that the file you want to use is either in the 'project source dir' (that is the directory cmake won't touch) and not in the 'build dir' (that is the directory used only for compiling/linking the application). Note: files produced by uic/moc are NOT in the 'project source dir' but in the 'build dir' when using cmake.

  • 'project source dir' in cmake would be ${CMAKE_SOURCE_DIR}
see [3]
  • 'build dir' in cmake would be ${CMAKE_BINARY_DIR}
see [4]

Note: the two cmake variables contain absolute paths but cmake has also relative paths as CMAKE_CURRENT_BINARY_DIR and CMAKE_CURRENT_SOURCE_DIR

adding/removing files

consider you have a class 'demoClass' which does NOT inherit from QObject. this would not require moc to be executed before compiling. now you redesign that class to use QObject which involves the Q_OBJECT macro in the header file. if you just typed 'make' into a shell right after you rewrote the code you would get a 'vtable' problem when trying to link the objects into a binary. using 'qmake' one would simply type 'qmake' into a shell and then run 'make' again. qmake then would adapt the already known demoClass.cpp and demoClass.h (already known means that both files were listed in the project.pro file) to be processed by moc before compiling. this process isn't very easy to understand at first.

the same process (as already told above, namely changing from normal class to QObject inheriting class) is very different in cmake.

  • in cmake one would just list all the cpp files in general (no matter if they have anything to do with moc or not)
  • the headers which hold the class definitions and which also inherit from QObject are listed (and only these, all others are ignored) to be processed by moc with a macro called:
macro QT4_WRAP_CPP(outfiles inputfile ... OPTIONS ...)
       create moc code from a list of files containing Qt class with
       the Q_OBJECT declaration.  Per-direcotry preprocessor definitions 
       are also added.  Options may be given to moc, such as those found
       when executing "moc -help".  

so if you want to add a new file which inherits from QObject OR if you want to change a normal class into an QObject inheriting class:

  • in both cases you simply add the .h file into the list of 'to be processed by QT4_WRAP_CPP macro

that's basically all you have to know for the work flow!

this pattern applies as well for:

  • uic (.ui file / output ui_filename.h)
  • moc (.h file / output moc_filename.cxx)
  • rcc (.res files / ...)

everytime you add one of these files to your project you have to write the filename (as for example myWidget.cpp) into the CMakeLists.txt which is most likely in src/CMakeLists.txt and if it's a .h file which does inherit QObject you write it there as well. if it is a normal .h file you don't.

and maybe some others i don't think of right now. the pattern is the same -> you use a macro in cmake for that. the macro in general is already included in the cmake release as cmake 2.6 or cmake 2.8 which comes in very handy!

cmake: enable/disable parts of the qt library

in a cmake file CMakeLists.txt one can enable/disable parts of the qt library as for example: xml/opengl support

#   set(QT_USE_OPENGL TRUE)
# -> this will cause cmake to include and link against the OpenGL module
QT_DONT_USE_QTCORE
QT_DONT_USE_QTGUI
QT_USE_QT3SUPPORT
QT_USE_QTASSISTANT
QT_USE_QAXCONTAINER
QT_USE_QAXSERVER
QT_USE_QTDESIGNER
QT_USE_QTMOTIF
QT_USE_QTMAIN
QT_USE_QTNETWORK
QT_USE_QTNSPLUGIN
QT_USE_QTOPENGL
QT_USE_QTSQL
QT_USE_QTXML
QT_USE_QTSVG
QT_USE_QTTEST
QT_USE_QTUITOOLS
QT_USE_QTDBUS
QT_USE_QTSCRIPT
QT_USE_QTASSISTANTCLIENT
QT_USE_QTHELP
QT_USE_QTWEBKIT
QT_USE_QTXMLPATTERNS
QT_USE_PHONON

Finally you would included the 'selected' library parts with:

include(${QT_USE_FILE})

See http://cmake.org/cmake/help/cmake2.6docs.html#module:FindQt4 for a complete list of switches. This list might not be up2date for qt4.6

cmake for Qt 4

WARNING: for me looking at the examples below didn't work out very good, just consider looking at the examples at the end of this document.

Qt4 support in cmake is being developed in particular with the KDE project for KDE4. Qt4 support in cmake is already (cmake version 2.4.x) good at this point. Most things can be done similar as in Qt/KDE 3, so have a look at the section above for a full introduction.

qtproject/CMakeLists.txt:

project(qtproject) # the name of your project

cmake_minimum_required(VERSION 2.4.0)

find_package(Qt4 REQUIRED) # find and setup Qt4 for this project

# tell cmake to process CMakeLists.txt in that subdirectory
add_subdirectory(src)

qtproject/src/CMakeLists.txt:

# the next line sets up include and link directories and defines some variables that we will use.
# you can modify the behavior by setting some variables, e.g.
#   set(QT_USE_OPENGL TRUE)
# -> this will cause cmake to include and link against the OpenGL module
include(${QT_USE_FILE})
# see http://cmake.org/cmake/help/cmake2.6docs.html#module:FindQt4 for a complete list

# the variable "qtproject_SRCS" contains all .cpp files of this project
set(qtproject_SRCS
    main.cpp
    top.cpp
)

# tell cmake to create .moc files for all files in the variable qtproject_SRCS that require such a file.
# note: this assumes that you use #include "header.moc" in your files
qt4_automoc(${qtproject_SRCS})

# create an executable file named "qtproject" from the source files in the variable "qtproject_SRCS".
add_executable(qtproject ${qtproject_SRCS})

# link the "qtproject" target against the Qt libraries. which libraries exactly, is defined by the "include(${QT_USE_FILE})" line above, which sets up this variable.
target_link_libraries(qtproject ${QT_LIBRARIES})

  • If you need cmake to generate ui*.h files from .ui files then you need to use QT4_WRAP_UI macro like this:

SET(qtproject_UIS
    main_window.ui
)

QT4_WRAP_UI(qtproject_UIS_H ${qtproject_UIS})

# Don't forget to include output directory, otherwise
# the UI file won't be wrapped!
include_directories(${CMAKE_CURRENT_BINARY_DIR})

#Now add these generated files to the ADD_EXECUTABLE step
# If this is NOT done, then the ui_*.h files will not be generated
add_executable(qtproject ${qtproject_SRCS} ${qtproject_UIS_H} )
  • If you don't use the #include "header.moc" convention, you can use the QT4_WRAP_CPP macro. This generates a list of moc_xxxx.cxx files to be generated. You pass in the list of headers to be moc'ed, and get back a list of source files to add to your build target. This is similar to how qmake works with Qt4. An example usage is:
SET(foo_SRCS
  Class1.cpp
  Class2.cpp
  Class3.cpp
)

SET(foo_MOC_HDRS
  Class1.h
  Class2.h
  Class3.h
)

# After this call, foo_MOC_SRCS = moc_Class1.cxx moc_Class2.cxx moc_Class3.cxx.
QT4_WRAP_CPP(foo_MOC_SRCS ${foo_MOC_HDRS})

ADD_EXECUTABLE(foo ${foo_SRCS} ${foo_MOC_SRCS})

* Q (this is a question) does this imply that i have two lists of header files (one list foo_MOC_HDRS which are processed by moc and another list (say foo_HDRS) which is compiled normally?)
: .h files which inherit from QObject (or in other words which use the Q_OBJECT macro) need to be listed to be moc'ed (foo_MOC_HDRS) all other .h files (foo_HDRS) are not listed in 'CMakeLists.txt'. 'foo_HDRS' is not needed at all. In the above example Class1.h,Class2.h and Class3.h inherit from QObject, say there is a class Class4 in Class4.h it WILL NOT BE LISTED THERE.
  • To add support for Qt4 libraries like network or qttest, you need to add both the include files and corresponding libraries. For example, to add support for the network and qttest libraries, you can use:
INCLUDE_DIRECTORIES(
   ${QT_INCLUDE_DIR}
   ${QT_QTNETWORK_INCLUDE_DIR}
   ${QT_QTTEST_INCLUDE_DIR}
)

TARGET_LINK_LIBRARIES(
   ${QT_LIBRARIES}
   ${QT_QTNETWORK_LIBRARIES}
   ${QT_QTTEST_LIBRARIES}
)
  • To build a Qt plugin, you must add some defines and build your library as a shared library. For example, to build a plugin in release mode:
ADD_DEFINITIONS(${QT_DEFINITIONS})
ADD_DEFINITIONS(-DQT_PLUGIN)
ADD_DEFINITIONS(-DQT_NO_DEBUG)
ADD_DEFINITIONS(-DQT_SHARED)

...

ADD_LIBRARY(foo SHARED ${foo_SRCS})

Sample Projects

Qt based projects using cmake:

KDE based projects using cmake:

Blogs, Articles

Personal tools