When I start to use a programming language I always have the following questions:
In Visual Studio 2019 and later, you can add configurations and customize their settings by using the CMake settings editor. The editor is intended to be a simpler alternative to manually editing the CMakeSettings.json file, but if you prefer to edit the file directly, you can click the Edit JSON link in the upper right of the editor. CMake is an open-source, cross-platform tool that uses compiler and platform independent configuration files to generate native build tool files specific to your compiler and platform. The CMake Tools extension integrates Visual Studio Code and CMake to make it easy to configure, build, and debug your C project. CMake tools includes a bundled vswhere.exe which it uses to ask about existing Visual Studio instances installed on the system. For each of x86, amd64, x86amd64, x86arm, x86arm64, amd64x86, amd64arm, and amd64arm64, CMake Tools will check // for installed Visual C environments. // A kit is generated for each existing MSVC toolchain. I want to use the CMake Tools extension for developing a CMake project in Visual Studio Code. I build the project in the command line with following command: PS projectbuild cmake -G'Visual Studio 14 2015 Win64' -DBOOSTROOT=somepath -DQTROOT=anotherpath projectpath. This is a quick video to show how to quickly get a CMake project configured, built, executed, and debugged in VS Code.
- How do I build/run my program?
- How do I run tests?
- How do I install dependencies?
- How do I integrate all of this in my text editor?
Some languages such as Go come with simple answers to these questions: go build ./...
, go test ./...
, go get <my module>
, install the official Go extension for your editor. Done. But of course in the case of C++, things are ... different 🙃.
The C++ world is way more fragmented, there is a lot of choices of build and meta build tools, various test frameworks (each with its own test runner), lot of way to manage dependencies, etc. Because so much choices can be daunting for people who just want to start with the language, and because I know I will forget how things work, in this article I will document what I've done to get to a fully working environment that answers all my questions in a simple and coherent way. At the end of this guide we will be able to build and run tests in just one click (or command line if that's your thing).
Tools popularity can vary quite a lot from years to years, for example solutions to manage dependencies are still competing against each other and new ones are popping all the time, so I want to be clear that this document is my setup in 2020, hopefully it will stay evergreen in the future, but I wouldn't bet too much on it.
What we will do:
- Setup and use
vcpkg
to download, build, and install open source dependencies such as catch2 (for our tests), and fmt (for our code). - Use CMake to manage our builds and run our tests.
- Configure Visual Studio Code so that we can control our builds and tests directly from the editor, and have autocompletion for dependencies installed via
vcpkg
.
Note: I'm currently using Windows 10 as my main operating system, everything I document here has also been tested on Linux via WSL, and should work as well on macOS. You should be able to follow without facing OS specific issues, but we never know. Don't hesitate to contact me if you find issues so that I can update this guide.
Requirements
To follow this guide you will need a C++ compiler suite and git.
If you are in a UNIX environment that will likely be gcc
or clang
, they can be installed from the package manager of your system.
If you are running Windows you can install Microsoft's compiler. For this you need to install either Visual Studio (not Visual Studio Code), or if you do not want to install a full IDE you can decide to install only the build tools (select 'Build Tools for Visual Studio 2019').
Another option that isn't always well known for Windows: Windows 10 has a feature named 'WSL' that makes it possible to run a Linux subsystem. That makes it simple to install gcc
or clang
and work on cross-platform application without the need to switch to a different operating system. It's a fantastic feature, I won't cover it here but it's worth knowing that exists. Checkout Microsoft's documentation if you're curious about it. Both Visual Studio and Visual Studio Code have support for WSL.
vcpkg
, Microsoft's solution to C++ library management
vcpkg is one of the latest addition to the list of existing solutions to manage C++ dependencies. It's use is quite easy and should feel familiar to people who used package managers such as NPM. Though, two things surprised me when I first tried to use it:
- You don't install it in a conventional way, instead of downloading a command line tool that you add to our PATH then run it from your project directory, what you have to do is
git clone
thevcpkg
repository from GitHub, then build the command line tool via a bootstrap script, and use the resultingvcpkg
binary it from there. - Packages are installed directly in the directory of the cloned repository, meaning that anything installed are somehow global, but you can also clone the repository multiple time in different directories if that's an issue.
Let's see how to set it up.
First be sure to be in the directory where you want the command tool to be located (the vcpkg team recommands C:srcvcpkg
or C:devvcpkg
, which is a bit weird in my opinion, I personally clone it in my C:UsersSamDevelopment
directory. Just pick something that is good for you). Then clone the repository:
And... that's it. The tool is now compiling, after some time you will get a vcpkg
binary (vcpkg.exe
on Windows).
Now let's install two open sourced libraries, Catch2
and fmt
.
First we can see what is available for catch
to have an idea of the version and dependency name we should use:
As we can see catch
is referencing a previous version of the library that is now deprecated, and we should use the name catch2
if we want to get the latest version.
Important note: by default vcpkg
will install the x86
version of the library, which is likely to cause issues if you're building for an x64
target. It's better to always specify explicitely the 'triplet', in my case that's catch2:x64-windows
. If at some point CMake tells you that an installed library cannot be found, be sure to check that you installed it for the correct architecture and platform! I lost quite a lot of time debugging my setup before I realized that. I would personally prefer that it forces me to always specify the triplet to avoid that kind of mistake.
vcpkg
is really helpful here as it directly tells us what we need to use the library from CMake, which is exactly what we will do. If at any point you want to see again the CMake targets, just run the install command again for the same library tripplet.
Note: one important detail. In the case of fmt
, vcpkg outputs links both fmt::fmt
and fmt::fmt-header-only
, but we should really only use one or the other, and not both. I didn't see this issue for other libraries.
And that's it, our two dependencies are now installed. If you're curious you can check the installed
directory located in the vcpkg clone directory, that's where libraries source code and headers are located.
CMake, the meta-build system
CMake is its own beast. I won't go too far into its details because that can feel overwhelming quite fast. So let's defines what we want to use it for:
- Define a
main
target, that will be our main binary. It should link against our project source code and thefmt
library. - Define a
run_tests
target, that will be our test runner. It should link against our project and test source code, and thecatch2
library. - Once that's done we should be able to build our targets and run our tests in just one command.
The first thing is of course to install cmake
. Just download the installer from the website or install from your platform package manager. Once done you should have a cmake
command line tool available from your terminal.
Then move to your project directory, which I assume looks something like this:
We need to add a file CMakeLists.txt
to our project directory, with the following content:
As you can see the find_package
and target_link_libraries
are based on vcpkg's output that we got after installing fmt
and catch2
.
Note: CMake is an incredibly flexible system that can be used to adjust build settings to almost any requirements. But that flexibility comes with a cost in complexity. What I want to do in this guide is to present the minimum required to get started with your project, you can then tweak your build configuration to match your specific needs. The secret to not be overwhelmed is to start with something simple, that works, then gradually add more features when needed.
If you want to dive deeper into CMake I recommend to check out CGold: The Hitchhiker’s Guide to the CMake.
We can now run CMake from the command line to configure then build our targets. The first time will take a bit of time as CMake has to detect some properties such as which compiler to use. Though everything will be cached next time you call cmake
it should be fast.
Visual Studio Build Tools Cmake
Important notes here:
-B build
specifies that we want to use./build
as the CMake build directory-S .
specify the path to our sources-DCMAKE_TOOLCHAIN_FILE=C:/Users/Sam/Development/vcpkg/scripts/buildsystems/vcpkg.cmake
is the magic to make CMake aware of vcpkg installed libraries. The path should match the location where you installedvcpkg
.
If everything went well, that's great, you are basically done.
If you got errors Could not find a package configuration file provided by <library name> with any of the following names: [...]
, be sure to check
- that you installed the library with the correct triplets. That has been one of my main source of frustration.
- that the path you provided to
-DCMAKE_TOOLCHAIN_FILE=
is targeting thevcpkg
root where you installed the library - that you didn't mispell the name of the library in
CMakeLists.txt
- that you're using the CMake statement as mentioned by
vcpkg
when you runvcpkg install <library name and triplet>
. The output varies from library to library, so be sure to triple check this one (for example sqlite3 requiresfind_package(unofficial-sqlite3 CONFIG REQUIRED)
) - delete the
build
directory and try again
Ok, let's assume the configure step went well, we can now build our targets. Your output will of course vary if you use a different compiler than I do.
Run your tests via ctest
CMake also comes with CTest, a tool that can be used to run tests in a more generic way. It has to be run from within CMake build directory.
Setup Visual Studio Code for an IDE experience
I'm assuming that you already have Visual Studio Code installed and that you are already familiam with it, I will only talk about the steps required to integratem CMake and vcpkg.
We need 4 extensions:
- C/C++ for Visual Studio Code from Microsoft. Syntax highlighting and intellisense for C++.
- CMake by twxs. Syntax highlighting and intellisense for CMake.
- CMake Tools also from Microsoft. When installed and enabled you get the ability to configure, build, and run CMake targets. It also comes with a nice graphical interface integrated in VSCode.
- C++ TestMate by Mate Pek. Gives us support for tests directly in VSCode, also with a great graphical interface integration.
Run Cmake In Visual Studio Code
Install them directly from VSCode. I recommend to only enable 'C++ TestMate' and 'CMake Tools' for your current workspace as they only make sense in some specific C++ projects and can get in your way if a project is detected as CMake based when that's not the case (I had that situation when working on Arduino projects). Just enable them for the workspace every time you start a new CMake project.
By default they should already work correctly and if you followed the rest of this guide you should now have something like this:
We will only need to do a few tweaks to make them work well with vcpkg
. For this add a .vscode/settings.json
file in your project:
And specify 'CMake Tools' as the autocompletion provider in your C++ settings in .vscode/c_cpp_properties.json
:
Conclusion
And we are finally done! We can now manage our dependencies via vcpkg
, define our builds from our CMake file, and configure, build, and run all our targets and tests from Visual Studio Code for a fancy development experience. And all of this by only using Open Source projects :)
vcpkg is a Godsend, that makes dealing with dependencies and CMake SO MUCH BETTER than anything else I tried before! No more ugly CMake scripts to download a test framework and runner and some simple dependencies! It's fantastic to see recent developments from folks at Microsoft, the amount of time and energy spent on improving the C++ dev experience via open source contributions is admirable.
It took me quite some time to get something I'm satisfied with, I spent a few hours fighting with CMake and vcpkg, reading their documentation, testing VSCode settings and plugins, etc. Hopefully that can help others who are looking for some guidance! ✌