After numerous promises of how Qbs will be the Qt’s default build system, The Qt Company suddenly killed it, announced that qmake is a dead man walking too, and actually Qt is switching to CMake.

So I guess we should start using CMake for building Qt applications as well. Let’s see then what it takes to switch from qmake to CMake with a couple of basic examples.

Qt, CMake and Visual Studio Code

I also wanted to try Qt development with Visual Studio Code, and now is a good occasion for that.

I tried it on Windows 10 with MSVC 2017, however it will work on other systems too.

CMakeLists.txt

I’ve never used CMake for anything before, so to get familiar with it I decided to create two very basic applications, one with Qt Widgets and another with Qt Quick.

Instead of qmake’s .pro file CMake uses CMakeLists.txt. And having spent some time I’ve managed to create correct CMakeLists.txt files for both projects, but I can’t tell that it was very straightforward.

Lots of examples from the internet didn’t work and were way too complex and overloaded in general. Surprisingly, the simplest one turned out to be the one from the official Qt documentation - who would have thought as usually it’s the other way around.

Qt Widgets

Anyway, here the project file for a Qt Widgets based application:

cmake_minimum_required(VERSION 3.10)

# your project name
project("qt-widgets-cmake")

# find includes in the corresponding build directories
set(CMAKE_INCLUDE_CURRENT_DIR ON)
# run moc automatically
set(CMAKE_AUTOMOC ON)
# if you're using Widgets and editing .ui forms in Designer
set(CMAKE_AUTOUIC ON)

message("Looking for Qt...")
# Qt modules (https://doc.qt.io/qt-5/qtmodules.html) you're using in your application
find_package(Qt5 REQUIRED Widgets)
if (${Qt5_FOUND})
    message("Found Qt " ${Qt5_VERSION})
else()
    message("Couldn't find Qt")
endif()

# your source files
set(sources
    mainwindow.ui
    mainwindow.cpp
    main.cpp
)
# name of the .exe file, window flag and the list of things to compile
add_executable(${CMAKE_PROJECT_NAME} WIN32 ${sources})

# also tell CMake to link with the libraries from the line 14
# otherwise it'll think that you wanted to find those just for fun and won't link them
target_link_libraries(
    ${CMAKE_PROJECT_NAME}
    Qt5::Widgets
    )

Essential thing here is find_package() - I have no idea what black magic it uses to find Qt in my system, but it did find it despite the fact it is installed in D:\programs\qt\ (not the most common/standard installation path). Most likely it just checks the PATH system variable, because I have D:\programs\qt\5.12.3\msvc2017_64\bin\ there.

When I tried this on Mac OS, the configure command failed, so I provided that path manually and that worked too:

SET(CMAKE_INSTALL_PREFIX "~/programs/qt/5.12.3/clang_64/")

cmake_minimum_required(VERSION 3.10)

# your project name
project("qt-widgets-cmake")

...

What I don’t get is why do I need to set target_link_libraries() in addition to find_package(). I mean, why would I want to find some library if not to link with it?

WIN32 is an important flag if you’re building a GUI application on Windows - without it the build will fail. Obviously, you don’t need it on Mac OS or Linux.

Note the ${CMAKE_PROJECT_NAME} construction - that’s how you use variables in CMake. So instead of writing the name of the project (qt-widgets-cmake) I just refer to the CMAKE_PROJECT_NAME variable. And of course you can define your own variables: for instance, I used my variable sources to gather all the source files for add_executable().

Qt Quick

CMake project file for a Qt Quick application is not that difficult either. However, there are some differences:

  • there is no need in CMAKE_AUTOUIC as you won’t be using widgets-based .ui forms;
  • instead of Widgets you now need Quick and Qml modules;
  • since QML files are part of resources, you need to tell CMake about that.

So here’re the changes to be made in CMakeLists.txt:

...

#set(CMAKE_AUTOUIC ON)

find_package(Qt5 REQUIRED Quick Qml)

# your sources
set(sources
    main.cpp
)

# specify resource file names here, required for QML
qt5_add_resources(qrc qml.qrc)

add_executable(${CMAKE_PROJECT_NAME} WIN32 ${sources} ${qrc})

target_link_libraries(
    ${CMAKE_PROJECT_NAME}
    Qt5::Quick
    Qt5::Qml
    )

The source code of both projects including CMakeLists.txt files is available here.

Visual Studio Code

First, install CMake Tools extension, because it conveniently does all the work of calling CMake with the right set of arguments for configure and build tasks.

Open your project folder in Visual Studio Code, press Ctrl + Shift + P and run CMake: Configure. First time it will ask you for a kit/toolchain - what compiler should be used for building your application. On Windows that choice can look like this:

Visual Studio Code, choosing kit on Windows

And here’s the configure output:

[cmake] The C compiler identification is MSVC 19.16.27030.1
[cmake] The CXX compiler identification is MSVC 19.16.27030.1
[cmake] Check for working C compiler: E:/tools/vs/VC/Tools/MSVC/14.16.27023/bin/Hostx64/x64/cl.exe
[cmake] Check for working C compiler: E:/tools/vs/VC/Tools/MSVC/14.16.27023/bin/Hostx64/x64/cl.exe -- works
[cmake] Detecting C compiler ABI info
[cmake] Detecting C compiler ABI info - done
[cmake] Detecting C compile features
[cmake] Detecting C compile features - done
[cmake] Check for working CXX compiler: E:/tools/vs/VC/Tools/MSVC/14.16.27023/bin/Hostx64/x64/cl.exe
[cmake] Check for working CXX compiler: E:/tools/vs/VC/Tools/MSVC/14.16.27023/bin/Hostx64/x64/cl.exe -- works
[cmake] Detecting CXX compiler ABI info
[cmake] Detecting CXX compiler ABI info - done
[cmake] Detecting CXX compile features
[cmake] Detecting CXX compile features - done
[cmake] Looking for Qt...
[cmake] Found Qt 5.12.3
[cmake] Configuring done
[cmake] Generating done

If it’s all good, run CMake: Build and you’ll get your application executable in the build folder. Here’s its output:

[build] Starting build
[proc] Executing command: e:\tools\cmake\bin\cmake.EXE --build e:/code/qt-widgets-cmake/build --config Debug --target all -- -j 18
[build] [1/5  20% :: 0.115] Automatic MOC and UIC for target qt-widgets-cmake
[build] [4/5  40% :: 0.904] Building CXX object CMakeFiles\qt-widgets-cmake.dir\qt-widgets-cmake_autogen\mocs_compilation.cpp.obj
[build] [4/5  60% :: 0.984] Building CXX object CMakeFiles\qt-widgets-cmake.dir\main.cpp.obj
[build] [4/5  80% :: 0.985] Building CXX object CMakeFiles\qt-widgets-cmake.dir\mainwindow.cpp.obj
[build] [5/5 100% :: 1.463] Linking CXX executable qt-widgets-cmake.exe
[build] Build finished with exit code 0

Your application is ready to run.

Well, it turned out to be easier than I thought - to use CMake for building Qt applications, though I spent a couple of days till I made it work. But as I read in several places, making simple things with CMake is simple, it’s custom things that make everything horrible and awful.

And when it comes to Visual Studio Code as an IDE for Qt development, I have to admit - Qt Creator is still better. Especially it becomes obvious when you iterate between Designer and VS Code, or constantly read the Qt documentation because auto-complete feature sucks big ass for QML at the moment (yes, even bigger one than in Qt Creator - there is simply none).