First written on 2020-09-11.
Last updated on 2021-01-02.
In this article and several more, I will be discussing developing a very simple C++ library and application using CMake and Visual Studio Code. I will also use git and Google Test, and port the project from Windows to Linux. Most of the information is applicable to using almost any IDE, or indeed, no IDE at all.
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.
Why Use CMake?
CMake is a powerful and robust build system. You specify what you want done, not how to do it. CMake then takes that information and generates the files needed to build the system. For example, CMake can generate solution (.sln) and project files (.vcxproj) that Visual Studio and Visual Studio Code use on Windows. Similar capabilities are available for every other popular IDE. It can even create make files if you want to do everything from the command line. Because it can be called from the command line, it integrates well with continuous integration/continuous build systems.
CMake is a powerful and robust build system. You specify what you want done, not how to do it. CMake then takes that information and generates the files needed to build the system. For example, CMake can generate solution (.sln) and project files (.vcxproj) that Visual. 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.
You can specify the build tools that you want to use; for example, you can use MSVC or LLVM on Windows, and gnu or LLVM on Unix-like systems, including Linux, OSX, and MSYS or MinGW. Aside from specifying the tools to use, no other changes are required to the CMake specification files. You will see this when I port my project from Windows using the Visual Studio build tools to Linux using the gnu build tools.
With CMake, you can download, build, and use a large number of tools. I will show one example of this when I use Google Test in this project. Just about any tool that can be downloaded from the internet, and which provides CMake files for building, can be used.
Installing the Needed Tools
I will start the project on Windows using VS Code and the Visual Studio Build Tools, but if you wish, you can start with a different IDE, or even a different operating system. In a later article, I will discuss using the gnu tools on Linux (Ubuntu).
So let’s begin.
Installing Visual Studio Code and Extensions
On Windows, the latest version of Visual Studio Code is available on its download page. Select the appropriate version; click on the Windows button for the x64 version, or one of the ARM links for ARM if that is applicable to you. The download will begin. When it is completed, run the downloaded file.
Visual Studio Code Cmake Build Directory
Next, we need two VSCode extensions. Start VS Code and display the extensions panel (select View → Extensions from the main menu). In the search box, enter C++
. A number of C and C++ extensions are displayed. You want the one called C++. Make sure it is from Microsoft. This extension provides Intellisense, debugging, and browsing capabilities. Click on the Install button to install it.
The second extension is CMake Tools. Search for and install it.
Installing Visual Studio Build Tools
We need the build tools provided by Visual Studio. Don’t worry, we aren’t installing Visual Studio, just the build tools.
On the Visual Studio downloads page, move down into the All Downloads section. As I write this, the current version of Visual Studio is 2019, so I will be referring to it in this section. If a later version is available, use that instead. Select Tools for Visual Studio 2019. Click on the Download button for Build Tools for Visual Studio 2019. Download and save the file. When the download has completed, open the file. This starts Visual Studio Installer. Again, don’t worry, we are not installing Visual Studio, just the build tools. When the installer window opens select only the build tools. After some time (several minutes), the install will complete. Close the installer.
Open the Windows Start menu and start Developer Command Prompt for VS 2019; do not open the standard command prompt or Powershell. At the command prompt, enter:
The following should be displayed, although the version number may be different:
If the message says that it cannot find CMake, then the build tools did not install correctly.
You will almost always be starting VS Code from the command line of Developer Command Prompt, so you will probably want to add it to the Productivity section of the Start menu.
Installing Git
We will need git. If you have done any development work, you probably already have it installed. If not, Git for Windows is available here.
A Simple C++ Program With Library
We will start by creating a simple C++ program with a simple library. You can perform similar steps, with slight modifications, if you are on Linux or any other Unix-like system. To support the program, we will create a directory structure and start VS Code as follows:
Open a Developer Command Prompt. Enter:
In the Explorer list in VS Code, select the hello/include directory and create a new file called hello.h. Place the following code in that file and save it:
Again in the Explorer list, select hello/src and create a new file called hello.cpp. Place the following code in that file and save it:
That is all the code we need for our library. Now create the program to use the library. In the Explorer list, select the apps directory and create a new file called main.cpp. Place the following code in that file and save it:
To build the library and program, we will use CMake. There are many examples of CMake on the internet, many of which are outdated or just plain bad. For this program and library, I am following Modern CMake by Henry Schreiner and others.
In the Explorer list, select VSCODE-CMAKE-HELLO in VS Code and create a new file. Call it CMakeLists.txt. Enter the following and save the file:
In the Explorer list, select apps and create a new file. Call it CMakeLists.txt. Enter the following and save the file:
Create a file called CMakeLists.txt in the hello directory and place the following code in it:
Using CMake With Visual Studio Code
Look at the status bar on the VS Code window. If it looks similar to this:
then terminate and restart VS Code. The status bar should now look like this:
From left to right, master*
indicates that you are editing the git master branch and that changes have been made. The symbols and 0s indicate that there are currently no errors in workspace. Next is a button that will run CMake (CMake: [Debug]
). No Kit Selected
indicates that the build tools have not yet been selected; we will get to them in a moment. Following that is a Build
button, the default target, a bug button that will run the application in debug mode, and a button that will run the application without starting the debugger. The remainder of the status bar provides other information that we are not currently concerned with.
We first have to run CMake to create the build files. Click on the CMake: [Debug]
button. The first time you do so, a list of build tool kits is displayed below the main menu. Select either Unspecified
, or Visual Studio Build Tools 2019 Release - amd64
. The No Kit Selected
text in the status bar will change to [Visual Studio Build Tools 2019 Release - amd64]
, and a list of CMake configurations are displayed: Debug
, Release
, MinSizeRel
, and RelWithDebInfo
.
Select Debug
. This will execute CMake and generate a Visual Studio solution file (.sln
) and Visual Studio project files (.vcxproj
) if there are no errors. If there are errors, then something is not right with the CMakeLists.txt files or the C++ source files.
If you selected any of the other CMake actions, the executable, library, and debug related files would be placed in other subdirectories. For example, if you build release versions, they will be placed in build/Release
.
The first time you run debugging by either clicking on the Bug
button, or by selecting Run → Start Debugging
, a list of build environments will be displayed just below the main menu. Select C++ (Windows)
.
To do a clean and rebuild, all we have to do is delete the build directory and all of its contents, then run CMake
and Build
.
Debugging
After a debug build, you can debug main.exe by clicking on the bug button in the status bar. Alternatively, you can select Run → Start Debugging
from the main menu. In the latter case, the first time you do this, a list of debug environments is displayed. Select C++ (Windows)
. If the active file in VS Code is a C++ source file, a list of configurations is then displayed. You can select either cl.exe - Build and debug active file
or Default configuration
. If a different type of file is active, such as CMakeLists.txt, then the configuration list is not displayed.
In either case, a file called launch.json is added to the .vscode directory. Open that file and change the program to ${workspaceFolder}/build/apps/Debug/main.exe
Now you can run debug from either the bug button or the menu item.
Summary and What’s Next
This article discussed how to create a C++ project containing a program called main and a library called hello with Visual Studio Code using CMake. How to debug the program is also discussed.
There is still much to do, but that will be discussed in the following articles:
- Adding GoogleTest to the project.
UPDATES
The code in hello.cpp was updated to correct a typo. Thanks Vlad T. for pointing that out.
When I start to use a programming language I always have the following questions:
- 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').
Visual Studio Code Cmake Syntax Highlighting
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
.
Visual Studio Build Tools Cmake
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.
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.
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
:
Visual Studio Code Cmake Setup
Conclusion
Visual Studio Code Cmake Configure Arguments
Vs Code Cmake Tools
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.
Visual Studio Code Cmake Debug
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! ✌