# Using Cmake with Qt

First of all, we have to utilize Qt Creator to generate a CMakeLists.txt which would be the skeleton of our project.

By creating new project with Build System as CMake, we'll get the following content in the generated CMakeLists.txt under the root folder of our project:

cmake_minimum_required(VERSION 3.5)

project(ucwqt LANGUAGES CXX)

set(CMAKE_INCLUDE_CURRENT_DIR ON)

set(CMAKE_AUTOUIC ON)
set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTORCC ON)

set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

# QtCreator supports the following variables for Android, which are identical to qmake Android variables.
# Check http://doc.qt.io/qt-5/deployment-android.html for more information.
# They need to be set before the find_package(Qt5 ...) call.

#if(ANDROID)
#    set(ANDROID_PACKAGE_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/android")
#    if (ANDROID_ABI STREQUAL "armeabi-v7a")
#        set(ANDROID_EXTRA_LIBS
#            ${CMAKE_CURRENT_SOURCE_DIR}/path/to/libcrypto.so
#            ${CMAKE_CURRENT_SOURCE_DIR}/path/to/libssl.so)
#    endif()
#endif()

find_package(Qt5 COMPONENTS Widgets LinguistTools REQUIRED)

set(TS_FILES ucwqt_zh_CN.ts)

if(ANDROID)
  add_library(ucwqt SHARED
    main.cpp
    MainWindow.cpp
    MainWindow.h
    MainWindow.ui
    ${TS_FILES}
  )
else()
  add_executable(ucwqt
    main.cpp
    MainWindow.cpp
    MainWindow.h
    MainWindow.ui
    ${TS_FILES}
  )
endif()

target_link_libraries(ucwqt PRIVATE Qt5::Widgets)

qt5_create_translation(QM_FILES ${CMAKE_SOURCE_DIR} ${TS_FILES})
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51

Now we can build the project and get a empty window like this:

Empty MainWindow.

Now let's get started from the CMakeLists.txt. Note these:

set(CMAKE_AUTOUIC ON)
set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTORCC ON)
1
2
3

they are necessary for qt projects built on CMake system. Without them, the .cpp file that contains a QObject-inherited class will not be converted to .moc file as the standard c++ compiler may not recognize the Qt-based classes, and .ui file will not be converted to .h and then the ui_MainWindow.h won't be found on the build dir.

find_package(Qt5 COMPONENTS Widgets LinguistTools REQUIRED)
target_link_libraries(ucwqt PRIVATE Qt5::Widgets)

qt5_create_translation(QM_FILES ${CMAKE_SOURCE_DIR} ${TS_FILES})
1
2
3
4

There are many components provided by Qt, here we used Widgets and LinguistTools. In qmake formats only Widgets component should be included, translation can be directly added into the project:

Qt += widgets
TRANSLATIONS += ucwqt_zh_CN.ts
1
2

As you may have noticed that the CMakeLists.txt before also supports android which is similar as qmake. If you have installed jdk, android-sdk as well as Android-support-version Qt kit, you could use that kit to build for a android app. That's not our focus here.

# Use a common module for configuraion

We have to add many qt-specified commands in each CMakeLists.txt and that's troublesome. We could have a basic configuration cmake files in public place and include it, then the CMakeLists.txt will be simplified.

Create a folder named cmake under the project folder. Then create a file named QtCommon.cmake (the name could be whatever you like, but suffix should be .cmake).

We can add some functions in QtCommon.cmake and calls them after declaring:

macro(get_git_tag)
    execute_process(COMMAND git rev-parse --short HEAD
        WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}"
        OUTPUT_VARIABLE PACKAGE_GIT_VERSION
        ERROR_QUIET
        OUTPUT_STRIP_TRAILING_WHITESPACE
    )
endmacro(get_git_tag)

macro(init_qt)
    # Let's do the CMake job for us
    set(CMAKE_AUTOMOC ON) # For meta object compiler
    set(CMAKE_AUTORCC ON) # Resource files
    set(CMAKE_AUTOUIC ON) # UI files
    set(CMAKE_CXX_STANDARD 11)
    set(CMAKE_CXX_STANDARD_REQUIRED ON)
endmacro()

init_qt()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

I think the cmake language just like python.

Now we can make the CMakeLists.txt simpler, add the location of our QtCommon.cmake, include it:

# project() ....

LIST(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_LIST_DIR}/cmake)
include(QtCommon)

# other commands ....
1
2
3
4
5
6

Yeah, if we put the QtCommon.cmake module into a public share location on the computer (for example, cmake standard module location <CMake Install Dir>\share\cmake-3.xx\Modules), each project is able to include that module and use the functions defined there.

# Add icon for .exe

Now we add the icon for our application, in QtCommon.cmake:

macro(add_project_meta FILES_TO_INCLUDE)
    configure_file("${PROJECT_SOURCE_DIR}/cmake/windows_metafile.rc.in"
        "windows_metafile.rc"
    )
    set(RES_FILES "windows_metafile.rc")
    set(CMAKE_RC_COMPILER_INIT windres)
    ENABLE_LANGUAGE(RC)
endmacro(add_project_meta)
1
2
3
4
5
6
7
8

on windows we should have an rc file, just copy the content into file <project dir>/cmake/windows_metafile.rc.in:

#include "winver.h"

IDI_ICON1               ICON    DISCARDABLE     "@ICON_FILE@"

VS_VERSION_INFO VERSIONINFO
 FILEVERSION @PROJECT_VERSION_MAJOR@,@PROJECT_VERSION_MINOR@,@PROJECT_VERSION_PATCH@,@PROJECT_VERSION_TWEAK@
 PRODUCTVERSION @PROJECT_VERSION_MAJOR@,@PROJECT_VERSION_MINOR@,@PROJECT_VERSION_PATCH@,@PROJECT_VERSION_TWEAK@
 FILEFLAGS 0x0L
 FILEFLAGSMASK 0x3fL
 FILEOS 0x00040004L
 FILETYPE 0x1L
 FILESUBTYPE 0x0L
BEGIN
    BLOCK "StringFileInfo"
    BEGIN
        BLOCK "000004b0"
        BEGIN
            VALUE "CompanyName", "@COMPANY@"
            VALUE "FileDescription", "@PROJECT_NAME@"
            VALUE "FileVersion", "@PROJECT_VERSION@"
            VALUE "LegalCopyright", "@COPYRIGHT@"
            VALUE "InternalName", "@PROJECT_NAME@"
            VALUE "OriginalFilename", "@PROJECT_NAME@.exe"
            VALUE "ProductName", "@PROJECT_NAME@"
            VALUE "ProductVersion", "@PROJECT_VERSION@"
        END
    END
END
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28

call the add_project_meta macro in CMakeLists.txt:

project(ucwqt VERSION "1.0.0" LANGUAGES CXX)

fix_project_version()
set(RES_FILES)
add_project_meta(RES_FILES)

add_executable(ucwqt
    main.cpp
    MainWindow.cpp MainWindow.h MainWindow.ui
    ${TS_FILES}
    ${RES_FILES}
)
1
2
3
4
5
6
7
8
9
10
11
12

Now build the project, you will see the icon is embeded into the .exe:

exe with icon

# Useful Cmake Macros

macro(GetSubModule Dir)
    message("[CT] Sub library ${CMAKE_CURRENT_SOURCE_DIR}/${Dir} Not Exists, downloading it...")
    execute_process( COMMAND git --version
        COMMAND git submodule init
        COMMAND git submodule update
        WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
        OUTPUT_VARIABLE gitCloneRes ERROR_VARIABLE gitCloneRes RESULT_VARIABLE gitCloneResult
    )
    if(gitCloneResult)
        message("[CT] ${gitCloneResult}")
    endif(gitCloneResult)
    if(gitCloneRes)
        message("[CT] ${gitCloneRes}")
    endif(gitCloneRes)
endmacro(GetSubModule)

## Usage
set(MyProject ${CMAKE_CURRENT_SOURCE_DIR}/MyProject)
if( IS_DIRECTORY ${MyProject} )
    add_subdirectory(${MyProject})
else()
    GetSubModule("MyProject")
endif()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

# Reference

Demo Project On Github

  1. qt-cmake-manual
  2. cmake
Last Updated: 3/19/2020, 4:31:35 AM