Introduction
This tutorial will show you how to create a CMake project that associated with Boost and QT library. For better understanding, please download my completed tutorial project from Download Complete ProjectThe source project use Qwidget, Qml, boost Signal2 and boost thread. It also contains .UI and .RCC file. The app will be built on Windows and Kubuntu Linux (the KDE version of Ubuntu) using MSVC and GCC.
Background
C++ is available on many platforms. So are the QT and Boost library. Unlike C#, C++ compiler generates the native binary file which cannot be run on other OS. Therefore, C++ source code has to be complied on certain compiler/OS in order to create a valid application. This is seemed to be an overhead, but the performance of native application is worth the trouble. See this article.CMake
CMake is a free opensource tool for managing the build
system. CMake will read your project infomation from CMakeList.txt and then, generate native build project file (ie. .sln , MakeFile).
QT
QT is a GUI and aplication framework. QT has their own specific compiling steps. Fortunately,
CMake already has a feature to handle that. Such features are AUTO_MOC, AUTO_UIC and AUTO_RCC.
Boost
A programing language without framework is powerless. The standard C++11 library
is still small compare to other frameworks such as .NET. Thus, we need Boost
library which contains almost everything you need for general work.
Installing CMake, Boost and QT
CMake, Boost and QT are opensource. You can choose to either build from source or install from binaries. This session will show you how to install CMake and QT from binary distribution, while Boost will be built from source code.-
Installing CMake
Obtains the CMake installer from CMake download link . Then, run the installer wizard.
For Kubuntu Linux, modify the file permissions and run with these commands.
$ chmod a+x cmake-3.2.1-Linux-i386.sh
$ ./cmake-3.2.1-Linux-i386.sh
-
Installing QT
Obtains the QT installer from QT download link .
On that page, I personally prefer choosing the offline installer. Click the “view all download“ link to show the offline installers.
Make sure that you have downloaded the correct version and architecture of complier. For example, if you plan to build Boost by visual C++ 2013 32 bit, choose Qt 5.4.1 for Windows 32-bit (VS 2013, OpenGL, 695 MB). For the install location, please read "Managing Source Code and Build Information".
For Kubuntu Linux, modify the file permissions and run the .run file.
$ chmod a+x qt-opensource-linux-x86-5.4.1.run
$ ./qt-opensource-linux-x86-5.4.1.run
-
Installing Boost
First, obtains the source code from Boost download link . Then, extract the zip (or tar) file. Open command prompt, and cd to the extracted folder. Inside that directory (boost_1_57_0.7z\boost_1_57_0\), run these commands- Run bootstrap.bat (bootstrap.sh for Linux)
C:\boost_1_57_0\boost_1_57_0>bootstrap
- Run b2.exe (b2 for Linux). See Table 1 for the detail of command arguments.
C:\boost_1_57_0\boost_1_57_0>b2 toolset=msvc-12.0 link=static install address-model=32 --build-type=complete --build-dir=C:\Boost\Build --prefix=C:\Boost
- Run bootstrap.bat (bootstrap.sh for Linux)
toolset=msvc-12.0 | Use the visual C++ 12.0 2013 complier (For Linux, use toolset=gcc-4.8). |
link=static | Build as static lib. |
Address-model=32 | Set binarie's architecture to 32 bit. Use Address-model=64 for 64 bit. |
Install | This argument will copy the output file to the prefix folder. |
--build-type=complete | Build both debug and release version. |
--build-dir=C:\Boost\Build | Directory for object files and caches (just an intermediate files). |
--prefix=C:\Boost | The location of “output” library. In this tutorial, I will use --prefix=C:\Boost64 for 64 bit build. For Linux, I will set prefix to --prefix=/home/apivan/boost.To customize this please see my CMakeList.txt. |
Table 1 Detail of command arguments
Please notice the difference between double dash “--” and no dash. You possibly have some built failure on python-related libs. You can simply ignore it, or just installs python.
For Kubuntu Linux, make sure that you have OpenGL installed.
$ sudo apt-get install freeglut3
$ sudo apt-get install freeglut3-dev
$ sudo apt-get install libgl1-mesa-dev
$ sudo apt-get install freeglut3-dev
$ sudo apt-get install libgl1-mesa-dev
Managing Source Code and Build Information
All the source code will be located here in the source directory. In other word, only files in this directory should be pushed into your version control.You can skip this session if you only interested in building the tutorial project.Below are the files in this tutorial project.
SimpleApp
|-CMakeLists.txt
|-main.cpp
|-mainwindow.h
|-Resources.qrc
|-SimpleQml.qml
|-mainwindow.cpp
|-mainwindow.ui
|-CMakeLists.txt
|-main.cpp
|-mainwindow.h
|-Resources.qrc
|-SimpleQml.qml
|-mainwindow.cpp
|-mainwindow.ui
Let observe the CMakeList.txt file.
CMAKE_MINIMUM_REQUIRED( VERSION 2.8.11) PROJECT(SimpleApp) SET(CMAKE_INCLUDE_CURRENT_DIR ON) # Check the environment of build system IF(CMAKE_CXX_COMPILER_ID MATCHES "GNU") # Enable C++11 on GCC (the MSVC will enable C++11 by default. # The syntax below will append -std=c++11 to the existing # CMAKE_CXX_FLAGS. SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11") ENDIF() # Check the architecture of complier. # Then, add Boolean value to ARCH_32BIT and ARCH_64BIT. SET(ARCH_32BIT ${CMAKE_SIZEOF_VOID_P} EQUAL 4) SET(ARCH_64BIT ${CMAKE_SIZEOF_VOID_P} EQUAL 8)
As you can see, the code is very straight forward. The architecture of complier can be checked by reading the size of void pointer.
The code below manages QT lib. It will check the current build environment, and store that value to QT_INSTALLED_PATH. That path is the path that you was asked by QT's installer wizard. To change the QT installed path, change the value in QT_INSTALLED_PATH.
IF(WIN32 AND ${ARCH_32BIT}) SET(QT_INSTALLED_PATH "C:/QtMSVCX86/Qt5.4.1/5.4/msvc2013_opengl") ELSEIF(WIN32 AND ${ARCH_64BIT}) SET(QT_INSTALLED_PATH "C:/QtMSVCX64/Qt5.4.1/5.4/msvc2013_64_opengl") ELSEIF(UNIX AND NOT MINGW AND ${ARCH_32BIT}) SET(QT_INSTALLED_PATH "/opt/Qt5.4.1/5.4/gcc/" ) ELSEIF(UNIX AND NOT MINGW AND ${ARCH_64BIT}) SET(QT_INSTALLED_PATH "/opt/Qt5.4.1/5.4/gcc_64/" ) ENDIF() # Add path environment for CMake. SET(CMAKE_PREFIX_PATH ${CMAKE_PREFIX_PATH} ${QT_INSTALLED_PATH} CACHE PATH "") # These code will enable the auto MOC, UIC and RCC feature. # See http://www.cmake.org/cmake/help/v3.0/manual/cmake-qt.7.html SET(CMAKE_AUTOMOC ON) SET(CMAKE_AUTOUIC ON) SET(CMAKE_AUTORCC ON) # These code will add packages that was used in the source files. FIND_PACKAGE(Qt5Widgets) FIND_PACKAGE(Qt5Qml) FIND_PACKAGE(Qt5Quick)
Note that the WIN32 refer to both Windows 32 bit and Windows 64 bit.
The code below manages BOOST lib.
# Add search with prefix "lib". # On windows, b2 generate static lib with that prefix # such as libboost_signals-vc120-s-1_57.lib. SET(CMAKE_FIND_LIBRARY_PREFIXES lib) # Disable Boost_NO_BOOST_CMAKE. # This is to remove "BOOST_DIR not found". SET(Boost_NO_BOOST_CMAKE ON) IF( WIN32 AND ${ARCH_32BIT}) SET(BOOST_INSTALLED_PATH C:/Boost) # ... Code skipped ... ENDIF() SET(BOOST_ROOT ${BOOST_INSTALLED_PATH}) SET(BOOST_LIBRARYDIR ${BOOST_INSTALLED_PATH}/lib) SET(BOOST_INCLUDE_DIR ${BOOST_INSTALLED_PATH}/include) SET(BOOST_USE_STATIC_LIBS ON) SET(BOOST_USE_MULTITHREADED ON) # Use default runtime-link (thing like vc redist ..) SET(BOOST_USE_STATIC_RUNTIME OFF) FIND_PACKAGE(Boost 1.57.0 COMPONENTS signals system thread REQUIRED) INCLUDE_DIRECTORIES( ${Boost_INCLUDE_DIR}) LINK_DIRECTORIES( ${BOOST_LIBRARYDIR})
Similar to that of QT, the code is very straight forward. Make sure that you have installed boost to the correct path. Please check “b2” argument and BOOST_INSTALLED_PATH.
The code below will compile and link the source codes to the exe file.
ADD_EXECUTABLE(SimpleApp main.cpp mainwindow.cpp Resources.qrc) TARGET_LINK_LIBRARIES( SimpleApp Qt5::Widgets Qt5::Qml Qt5::Quick ${Boost_LIBRARIES})
And, here are the codes in mainwindows.cpp and .h file
#pragma once #include <QMainWindow> #include <QtQuick/QQuickView> #include <boost/signals2/signal.hpp> #include <memory> class MainWindow : public QMainWindow { Q_OBJECT // ... Code skipped ... private: std::unique_ptr<Ui::MainWindow> m_Ui; std::unique_ptr<QQuickView> m_QmlView; boost::signals2::signal<void()> m_BoostSignal; };.Cpp
// ... Code inside constrctor m_BoostSignal.connect( [this](){ boost::future<QString> f = boost::async( [](){return QString("Hello from boost future");}); this->m_Ui->plainTextEdit->appendPlainText (f.get()); }); connect(m_Ui->pushButton, &QPushButton::clicked, [this](){ m_BoostSignal(); }); connect(m_QmlView->rootObject(), SIGNAL(qmlSignal(QString)), this, SLOT(OnQmlSignaled()));
Generating the Native Build Project
In this sesssion, I will show you how generate the native build project defined in CMakeList.txt.- Open the “cmake-gui” then click “Browse Source” to fill “Where is the source code” field. Then, click “Browse build” to fill “Where to build the binaries” as shown in Figure 1.
- Click “configure” and select your compiler. In this tutorial, external libraries (Boost and QT) binaries are “MSVC-12.0” on windows and “GCC” on Linux. Thus, to make this tutorial working. For Windows, please select “Visual Studio 12 2013”. For Linux, please select “Unix Makefiles” as shown in Figure 2.
|
Figure
2
Selecting the native build project generator
|
- Click “Configure” again, and then click “Generate”. If everything was setup correctly,
you should see the message like these. See Figure 3.Figure 3 Successfully generated native build project
For those who have to build on the command line, create a new folder and cd to that
folder. That folder is the “Where to build the binaries” folder. Then, type
>cmake -G "Visual Studio 12 2013" .\SimpleApp
>cmake -G "Visual Studio 12 2013" .\SimpleApp
Building the Generated Project
Open the command prompt, cd to the “Where to build the binaries” directory.For windows, Run the vcvarshell.bat to prepare environment for MSbuild. Then, build the project. See comands below.
>"C:\Program Files\Microsoft Visual Studio 12.0\VC\vcvarsall.bat"
> msbuild /P:Configuration=Release ALL_BUILD.vcxproj
> msbuild /P:Configuration=Release ALL_BUILD.vcxproj
To run the built app, add Qt’s Dll to the environment and run SimpleApp.exe. See commands below. If everything is correct, you should see the app running as shown in Figure 4.
> set path=%path%;C:\QtMSVCX86\Qt5.4.1\5.4\msvc2013_opengl\bin
> Release\SimpleApp.exe
> Release\SimpleApp.exe
For Linux, run
$ make
To run the built app,
$ export PATH=$PATH:/opt/Qt5.4.1/5.4/gcc/bin/
$ ./SimpleApp
$ ./SimpleApp
Figure 4 Running the App on windows |
Figure 5 Running the app on Kubuntu Linux |
Developing and Debugging under IDE
For windows, the MSVC project will contain both debug and release configuration.To run the app, you must add QT’s Dll to the development environment.
On Solution Explorer, right click at the “Simple App” project, choose properties.
Then add the path to the QT binaries in Configuration Properties > Debugging > Environment. Please see Figure 6.
Type the QT path such as “path=%path%;C:\QtMSVCx86\QT5.4.1\5.4\msvc2014_opengl\bin” to that field. Before start debugging, right click at “SimpleApp” project and choose “Set as Startup project”. The running project should be able to hit the break point as shown in Figure 7.
For Linux , you can create the code block project by choosing “CodeBlock – Unix Makefiles” (See Figure 2).
Then, add the “Debug” value on “CMAKE_BUILD_TYPE” so that you can use code block to debug project. See Figure 8.
Figure 9 Show the debugging mode of code::block.
|
Figure 7 Debuging the app on visual studio (Windows) |
Figure 8 Set build type to Debug |
Figure 9 Debuging the app on code block (Kubuntu Linux) |
Packaging
You might notice that in order to run the app, one must add the environment path to QT’s dll (set path=%path%...). Otherwise, you must copy the depended DLL and QML to the app’s directory. You can use dependency walker to find out which .dll you needs. Then, ship your app with those dlls. You can use CMakeList.txt to copy the dlls to the installing package. To do so, install “WIX toolset”. Grasp it from here Wix toolset. The code below is the CMakeList.txt that generate the WIX’s project files and finally create the .msi installer.IF(WIN32) SET(CMAKE_INSTALL_PREFIX "C:/DevBuild/MySimpleApp") SET(CPACK_GENERATOR WIX) # Lookup for wix toolset IF(${ARCH_32BIT}) SET(CPACK_WIX_ROOT "C:/Program Files/WiX Toolset v3.8" CACHE PATH "") # ... code skipped ... ENDIF() SET(CPACK_WIX_UPGRADE_GUID "8E9F80D5-452B-4333-B7A4-7FDAAE60CFAC") INSTALL( TARGETS SimpleApp RUNTIME DESTINATION programs COMPONENT applications ) # Copy QT's plugins # It is not necessary to copy all of these. INSTALL( DIRECTORY ${QT_INSTALLED_PATH}/plugins/platforms DESTINATION programs COMPONENT applications ) INSTALL( DIRECTORY ${QT_INSTALLED_PATH}/qml/QtQuick DESTINATION programs COMPONENT applications ) INSTALL( DIRECTORY ${QT_INSTALLED_PATH}/qml/QtQuick.2 DESTINATION programs COMPONENT applications ) # Copy QT's dynamic libs INSTALL(FILES "${QT_INSTALLED_PATH}/bin/Qt5Core.dll" "${QT_INSTALLED_PATH}/bin/Qt5Widgets.dll" "${QT_INSTALLED_PATH}/bin/Qt5Quick.dll" "${QT_INSTALLED_PATH}/bin/Qt5Qml.dll" "${QT_INSTALLED_PATH}/bin/Qt5Gui.dll" "${QT_INSTALLED_PATH}/bin/Qt5Network.dll" "${QT_INSTALLED_PATH}/bin/icuuc53.dll" "${QT_INSTALLED_PATH}/bin/icuin53.dll" "${QT_INSTALLED_PATH}/bin/icudt53.dll" DESTINATION programs COMPONENT applications) # Do not pack vc++ redist SET(CMAKE_INSTALL_SYSTEM_RUNTIME_LIBS_SKIP TRUE) INCLUDE(InstallRequiredSystemLibraries) INSTALL(PROGRAMS ${CMAKE_INSTALL_SYSTEM_RUNTIME_LIBS} DESTINATION programs COMPONENT applications) INCLUDE(CPack) ENDIF()Finally, you can build the installer package by build the “PACKAGE.vcproj”. Figure 10 shows the default instller UI of the built app.
Figure 10 UI of the installer
|
Disscusion
Creating cross-platfrom C++ project is not that difficult in these days. The setup may take time, but you will do this only once. Most of the code on CMakeList.txt are just if-else statement for choosing the right lib for compiler/OS. Adding new source file or referring to the module in external library are very easy. It is as simple as adding assambly reference in C#.If you are looking for the way to build Boost by CMake, please read this discussion: http://stackoverflow.com/questions/18354398/what-is-the-best-way-to-build-boost-with-cmake
I am also looking for the best solution for building Boost too.
Update 26-03-2015: I have just found out that boost signal2 is header-only library (but boost signal1 does not). So, I add boost thread to the tutorial project. To ensure that the build system really use static lib.
If you have any trouble on following this tutorial, please inform me.