June 22, 2021
CMake Tools for Visual Studio, a feature first introduced with Visual Studio 2017, allows you to open a folder containing a CMakeLists.txt file and have Visual Studio treat it as a solution.
In exchange for convenience, CMake Tools can sometimes obscure configuration options. For instance, there is no way to access the Properties pages that are traditionally used to configure Visual Studio projects. This can lead to some unexpected results when debugging executable targets while using CMake Tools.
Consider the following demonstrative program, which depends on (i) command-line arguments and (ii) a specific working directory.
#include <iostream>
#include <fstream>
#include <string>
int main(int argc, const char** argv)
{
if (argc < 2)
{
std::cerr << "Insufficient arguments." << std::endl;
return -1;
}
std::ifstream file(argv[1]);
if (!file.is_open())
{
std::cerr << "Could not open file: " << argv[1] << "." << std::endl;
return -1;
}
std::string line;
while (std::getline(file, line))
{
std::cout << line << std::endl;
}
}
The corresponding CMakeLists.txt is minimal.
cmake_minimum_required(VERSION 3.19)
project(foo)
add_executable(foo src/main.cpp)
Notice that, until we set them, no custom command-line arguments will be passed to our program. Further, file names we pass may not be accurate, as they are resolved relative to the working directory (i.e. not where the program resides, but where the program is run from).
Both issues can be addressed by configuring the launch.vs.json file from within Visual Studio. Open this file by right-clicking the root CMakeLists.txt file and selecting the option to Add Debug Configuration. In the dialog that opens, select the Default option. This will open the launch.vs.json file, which should look something like this.
{
"version": "0.2.1",
"defaults": {},
"configurations": [
{
"type": "default",
"project": "CMakeLists.txt",
"projectTarget": "",
"name": "CMakeLists.txt"
}
]
}
It contains several entries, including a list of configurations
. This list allows us to define as many debug targets as we like. I will spare you explanations of each of the entries; for reference, you can find the full schema description here.
Let us rework the provided configuration to fit our needs.
{
"type": "default",
"project": "CMakeLists.txt",
"projectTarget": "foo.exe",
"name": "foo.exe [with args]",
"currentDir": "${workspaceRoot}",
"args": ["bar.txt"]
}
First, we set the projectTarget
to foo.exe
, which is the name of the debug executable. Second, we set name
to foo.exe [with args]
(though it could be named anything else) to indicate that this target will be debugged with arguments. Third, we set the currentDir
to our desired working directory, in this case the workspace root (by default, it is the directory where the executable exists). Fourth, we add a single argument, the name of the file to be read, to the args
list.
Now we can select foo.exe [with args]
from the Startup Item dropdown in the toolbar at the top of the page. Assuming the following directory structure
root/ (set as working directory)
- bar.txt (contains the line "Hello World!")
- CMakeLists.txt
- src/
-- main.cpp
our program should now happily run and print Hello World!.
This resolves the issues mentioned above, but a few caveats are worth mentioning. The first is that the currentDir
works for setting the working directory, but it probably shouldn't. Visual Studio Code uses a similar launch settings file, and it uses the more appropriately named cwd
entry to set the working directory. Visual Studio settings also use the cwd
entry, but for a different purpose. Documentation of this bug can be found here and here.
The second caveat is that we should be able to set many of these debug properties directly from CMakeLists.txt. Specifically, we should be able to achieve the same outcome as our launch.vs.json configuration with the following additions to our CMake file.
set_target_properties(foo PROPERTIES
VS_DEBUGGER_WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}"
VS_DEBUGGER_COMMAND_ARGUMENTS "bar.txt")
However, at least as of this writing, the foregoing properties do not work with CMake Tools for Visual Studio. It does seem to work sporadically when using the traditional method of using CMake to generate Visual Studio files, but even that appears to be bugged with respect to VS2019 (issue). At least for now, the launch.vs.json method is the way to go.
The final caveat is to that, like many Visual Studio-specific settings, the launch.vs.json lives in the .vs directory and may be ignored by your source control. If you want others to be able to be use your custom debug configurations, be sure to commit the settings file to source control.