Published on Sep 01, 2020 by Elias Steurer | Kelteseth
ScreenPlay is an Open-Source Live Wallpaper and Widgets app for Windows (and soon Linux & MacOSX) written in modern C++/Qt/QML and is in active development since early 2017 on Gitlab.
When developing apps that are a bit more complex, the best solution is often try to split them into little more managable pieces. For example if you want to test your app as a separate executable, a subdirs project (a tree of projects) is the only choice here.
A subdirs projects is just a simple MyApp.pro that only contains a template and subdirs:
We now have multiple subprojects needing to share code between each other. To tell the compiler where to find the header and source files from the other project, we need to tell the linker what library to link and where to find the compiled files. The way to go in qmake is to make giant .pri files that is solely used for including files. This is similar to regular #include <xyz.h> in c++. These MyProjectName.pri are now included in other MyProjectName.pro. To fix the relative path problem you have to add the current absolute path to every line:
1.1 External dependencies
Working with external dependencies on multiple operating system mostly consists of copy pasting chunks of plattform specific paths into your .pro file. This is really a tedious work because every OS handles the paths a bit differently. E.g. we do not have a separate subfolder for debug/release on linux.
1.2 Compiling performance killer “CONFIG += ordered”
Another big problem with qmake is the seemingly random compiler races. When you have many subprojects that are libraries for other subprojects, it would randomly fail because library libA depends on library libB and libC. But libC was not yet build at the time. Most of the time a simple second recompile would fix this. But this clearly shows some serious flaws. This problem never really was fixed with libA.depends = libB. Maybe (pretty sure) I made some mistakes, but my colleagues and I never solved this issue. The only way to make sure that the build order is fixed, was to set “CONFIG += ordered” which kills all build parallelism.
2. Why QBS lost against CMake
It was a real shocker when the QtCompany announced to no longer activly support QBS. I was even one of the people who pushed to make a second community vote. QBS syntax looks nice and familiar to everyone who ever coded QML. CMake does not. After working with CMake for some months now,
I can confidently say it was the right decision to use CMake instead of QBS as the default build system from Qt6 and forward.
CMake (with has mostly syntax flaws) works solid. QBS problems are more political than technical:
This is one of the main no go for many programmers that dislike Qt for its size (both in lines of code and library size). Also, many people hate MOC. This is the pre compiler that compiles your Qt C++ into regular C++. This is for writing nice code like emit mySignal();
2.2 Yet another build system
We already have build2, CMake, meson, scons that have many projects using outside of the Qt eco system.
2.3 No support for IDEs
As far as I know QtCreator is the only IDE that ever supported QBS.
2.4 vcpkg + CMake = ❤️
Remember my rant about external dependencies from paragraph 1.1? Well for me vcpkg is the holy grail for every C++ developer. Install dependencies with one command!
#vcpkg is the single best thing that happened to c++.— Elias Steurer (@Kelteseth) June 9, 2020
3. CMake is ugly, kinda
CMake is really ugly if you click on the first 10 google results. This is because google shows you old CMake stackoverflow answers from 2008 and often redirects you to the old documentation from 2.8. CMake syntax can be quite nice, because most of the time you only these commands:
4. Ninja makes CMake fast
CMake only generates instructions for your build system of choice. This can be a big advantage when working with people that prefer VisualStudio over QtCreator. When using CMake one can (should) choose Ninja as the default build system. Compiling projects with CMake+Ninja is fun. Ninja and CMake comes shipped with the Qt Maintenance in the tools category. Iterative changes are incredible fast and clean to look at with the [13/424] progress.
It’s so fast that working with Godots scons makes me activly want to convert Godot to CMake.
5. vcpkg is where CMake really shines!
Managing dependencys in C++ is tedious, many projects even ship dlls with their git repository. This is bad because it blows up the git repo size (we ignore git-lfs for now). A drawback is that vcpkg only supports one global version of packages (you manually install different versions of vcpkg, but this is more like a hack and only seldom needed). This is a feature on their roadmap.
Need to install another library? Simply call vcpkg install myLibToInstall again and you are good to go!
Going with the flow has its advantages but comes at a cost. Build systems, like qbs, with a big potential get thrown under the bus. What to use is up to the developer and thats why my projects will use CMake from now on.
The next blog post will be about how to set-up Qt projects beyond “Hello World”. Stay tuned!
You can discuss this blog post here post in our forum.