Written by: Johan Thelin
Qt comes with the QMake tool for handling cross platform building issues.However, there are other build systems available such as autotools, SCons andCMake. These tools meet different criterias, for example external dependencies.
When the KDE project shifted from Qt 3 to Qt 4 the project changed build toolfrom autotools to CMake. This has given CMake a special position in the Qtworld &emdash; both from the number of users point and from a featuresupport and quality point. Seen from a workflow point of view, Qt Creatorsupports CMake since version 1.1 (1.3 if you want to use a Microsoft toolchain).
In this article we will focus on CMake itself, and how to use it in conjunctionwith Qt. To do this, let's start with an overview of a simple, but typicalCMake-based project. As you can tell from the listing below, the projectconsists of some source files and a text file.
$ ls CMakeLists.txt hellowindow.cpp hellowindow.h main.cpp
Basically, the CMakeLists.txt file replaces the projectfile used by QMake. Tobuild the project, create a build directory and run cmake and then make fromthere. The reason for creating a build directory is that CMake has been builtwith out-of-source building in mind from the very start. It is possible toconfigure QMake to place intermediate files outside the source, but it requiresextra steps. With CMake, it is the default.
$ mkdir build $ cd build $ cmake .. && make
CMake building a basic project.
The Qt build system.
When using CMake, the intermediate steps must be handled explicitly. This meansthat headers with Q_OBJECT macros must be run through moc, user interface formsmust be processed by uic and resource files must pass through rcc.
In the example that we started with the world is slightly easier, though. It islimited to a single header file that needs to meet moc. But first, theCMakeLists.txt defines a project name and includes the Qt4 package as a requiredcomponent.
PROJECT(helloworld) FIND_PACKAGE(Qt4 REQUIRED)
Then all sources involved in the build process are assigned to two variables.The SET command assigns the variable listed first with the values that follow.The names, helloworld_SOURCES and helloworld_HEADERS, is by convention. You canname them either way you like.
SET(helloworld_SOURCES main.cpp hellowindow.cpp) SET(helloworld_HEADERS hellowindow.h)
Notice that the headers only include the headers that needs to be processed bymoc. All other headers can be left out of the CMakeLists.txt file. This alsoimplicates that if you add a Q_OBJECT macro to any of your classes you mustensure that it is listed here.
To invoke moc, the macro QT4_WRAP_CPP is used. It assigns the names of theresulting files to the variable listed first. In this case the line looks asfollows.
QT4_WRAP_CPP(helloworld_HEADERS_MOC ${helloworld_HEADERS})
What happens is that all headers are processed by moc and the names of theresulting source files are listed in the helloworld_HEADERS_MOC variable. Again,the variable name is by convention rather than forced.
In order to build a Qt application, the Qt include directories needs to be addedas well as a range of defines need to be set. This is handled through thecommands INCLUDE and ADD_DEFINITIONS.
INCLUDE(${QT_USE_FILE}) ADD_DEFINITIONS(${QT_DEFINITIONS})
Finally, CMake needs to know the name of the resulting executable and what tolink it to. This is conveniently handled by by the commands ADD_EXECUTABLE andTARGET_LINK_LIBRARIES. Now CMake knows what to build, from what and throughwhich steps.
ADD_EXECUTABLE(helloworld ${helloworld_SOURCES} ${helloworld_HEADERS_MOC}) TARGET_LINK_LIBRARIES(helloworld ${QT_LIBRARIES})
When reviewing the listing above, it relies on a number of variables startingwith QT_. These are generated by the Qt4 package. However, as a developer, youmust explicitly refer to them as CMake is not build as tightly to suite Qt asQMake.
Moving beyond the initial example, we now look at a project with both resourcesand user interface forms. The resulting application will look quite similar toits predecessor, but all the magic takes place under the hood.
The CMakeLists.txt file start by naming the project and including the Qt4package - the complete file can be downloaded as a source code packageaccompanying this article. Then all the input files are listed and assigned totheir corresponding variables.
SET(helloworld_SOURCES main.cpp hellowindow.cpp) SET(helloworld_HEADERS hellowindow.h) SET(helloworld_FORMS hellowindow.ui) SET(helloworld_RESOURCES images.qrc)
The new file types are then handled by QT4_WRAP_UI and QT4_ADD_RESOURCES. Thesemacros operate in the same ways as QT4_WRAP_CPP. This means that the resultingfiles are assigned to variable given as the left-most argument. Notice that theheader files generated by uic are needed as we need to build a dependencyrelationship between them and the final executable. Otherwise they will not becreated.
QT4_WRAP_CPP(helloworld_HEADERS_MOC ${helloworld_HEADERS}) QT4_WRAP_UI(helloworld_FORMS_HEADERS ${helloworld_FORMS}) QT4_ADD_RESOURCES(helloworld_RESOURCES_RCC ${helloworld_RESOURCES})
All the resulting files are then added as dependencies to the ADD_EXECUTABLEmacro. This includes the uic generated headerfiles. This establishes thedependency from the executable to the hellowindow.ui file through theintermediary ui_hellowindow.h header.
ADD_EXECUTABLE(helloworld ${helloworld_SOURCES} ${helloworld_HEADERS_MOC} ${helloworld_FORMS_HEADERS} ${helloworld_RESOURCES_RCC})
Before this file can be used to build the project there is a small caveat tohandle. As all intermediate files are generated outside the source tree, theheader file generated by uic will not be located by the compiler. In order tohandle this, the build directory needs to be added to the list of includedirectories.
INCLUDE_DIRECTORIES(${CMAKE_CURRENT_BINARY_DIR})
With this line, all intermediary files will be available in the include path.
Until now we have relied on the QtCore and QtGui modules. To be able to use moremodules, the CMake environment must be tuned to enable them. By setting them toTRUE using the SET command, the included modules can be controlled.
For instance, to enable OpenGL support add the following line to yourCMakeLists.txt.
SET(QT_USE_QTOPENGL TRUE)
The most commonly used modules are controlled using the following variables.
In addition to these, the variable QT_DONT_USE_QTGUI can be used to disable theuse to QtGui. There is a similar variable to disable QtCore, but that is more tobe feature complete than to actually add much useful value.
It is not as trivial to use CMake as QMake, but the rewards are more features.The most notable point when moving from QMake is CMake's built in support forout-of-source builds. It can really change habits, and thus make versioning codemuch easier.
It is also possible to add conditionals for various platforms and buildscenarios. For instance, use different libraries for different platforms, aswell as tuning the same project differently for different situations.
Other powerful features are the ability to generate multiple executables andlibraries in one build as well as using said executables and libraries in thesame build. This, in combination with the QtTest module can handle complex buildsituations from a single configuration.
The choice between CMake and QMake is really quite easy. For straight forward Qtprojects, QMake is the obvious choice. When the build requirements passes thecomplexity threshold for QMake, CMake can take its place. With Qt Creator'ssupport for CMake, the same tools can still be used.
参考文献:
http://qt-project.org/quarterly/view/using_cmake_to_build_qt_projects