Skip to content

VS Code for Qt Applications – Part 2 A Technical Guide

In the last blog post we saw an essential, C++ oriented, Visual Studio Code setup. That was enough to get going right away, but we can still definitely do more and better. Here I’ll show you how to get a complete setup for your qmake and CMake projects, all this while also wearing a Qt hat (on top of my C++ hat) and having a deeper look at the Qt side.

Build qmake Qt projects

Qmake is not integrated with Visual Studio Code the way CMake is, so setting up a qmake project for build is slightly more convoluted than doing the same with CMake. This means we’ll have to define our own build tasks. We’re going to do this in two stages: build steps definition and build steps combination, leveraging the fact that Visual Studio Code implements task dependencies and ordered sequential execution of dependencies.

Create build steps

As far as build steps are concerned, the following are, in a nutshell, the ones that will cover most cases:

  • Create the build directory (in a way that doesn’t fail if the directory already exists)
    {
      "label": "Create build dir",
      "type": "shell",
      "command": "mkdir -Force path/to/build/dir"
    }
    

    Here, -Force is a powershell parameter that will prevent the command to fail if the directory already exists. On Unix based systems, you can use mkdir -p instead.

  • Run qmake
    {
      "label": "Run qmake",
      "type": "shell",
      "command": "qmake",
      "arg": [ ... add your qmake arguments here ... ],
      "options": {
        "cwd": "path/to/build/dir"
      }
    }
    
  • Run make/nmake/jom, depending on the platform
    {
      "label": "Run make",
      "type": "shell",
      "command": "jom",
      "options": {
        "cwd": "path/to/build/dir"
      }
    }
    
  • Clear build folder This can mean different things depending on how the build file is configured. It could be a simple make clean, or a more thorough removal of the whole content of the build folder.

    {
      "label": "Clear build folder",
      "type": "shell",
      "command": "jom clean",
      "options": {
        "cwd": "path/to/build/dir"
      }
    }
    
Combine build steps

Now that our steps are defined, we can go on and define the actual build tasks. We’ll prepare two for this example, one for running a build, and one for running a clean build. Let’s see the task for a regular build:

{
  "label": "Build",
  "dependsOn": [
    "Create build dir",
    "Run qmake",
    "Run make"
  ],
  "dependsOrder": "sequence"
}

There are two new properties here: "dependsOn" is a list of task labels, and it means that those tasks need to be executed before the current task is built, while "dependsOrder", when set to "sequence", will tell Visual Studio Code to run all dependent tasks sequentially and in the given order.

The task for a clean build is very similar and will only have an extra step where the project is cleaned before being built again:

{
  "label": "Clean build",
  "dependsOn": [
    "Clear build folder",
    "Create build dir",
    "Run qmake",
    "Run make"
  ],
  "dependsOrder": "sequence"
}

And that’s it, now it’s just a matter to open the command palette (Ctrl+Shift+P), select “Task: Run Task” and then “Build”.

Use a default task

As an alternative (or better, an addition) to selecting manually the build task from a list every time, Visual Studio Code also allows to run a default task with a key combination (Ctrl+Shift+B). To mark a task as default, you need to add a few extra lines to the task configuration:

{
  // ... task configuration
  "group": {
    "kind": "build",
    "isDefault": true
  }
}
Use your own Qt

If Qt is not configured at a system level, or you want to use a Qt version other than the default one installed and configured in your system, you need to explicitly configure the environment so that every task is run with the right Qt version in the path. Visual Studio Code allows you to do this every time a terminal is launched for running a task, so our environment customizations are set before running the task command.

This is done in the settings file (or in the workspace settings if you’re working with a workspace), and the property name for this setting is system dependent: either "terminal.integrated.env.windows", "terminal.integrated.env.linux", or "terminal.integrated.env.osx". The property requires an object, where each property is the name of an environment variable, and the associated value is the value for the variable. Below is an example configuration for Windows:

{
  // All other settings...
  "terminal.integrated.env.windows": {
    "PATH": "C:/Qt/5.12.4/msvc2017_64/bin;${env:PATH}"
  }
}

Build CMake Qt projects

Setting up a CMake based project using the CMake extension doesn’t require any settings manipulation if Qt is already configured on your system. What you will need is to select a CMake kit (the CMake extension finds them automatically), a build variant, and launch the build with F7.

Short video showing how to launch a CMake build with Visual Studio Code

However, you may want to use extra arguments in the configuration step or specify your build directory so for instance it doesn’t end up being inside the source directory. You can customize CMake configuration arguments by setting the property "cmake.configureSettings" in your settings file. This property expects a list of string arguments that will be passed to CMake during the configuration step:

"cmake.configureSettings": {
  "CMAKE_PREFIX_PATH": "my/prefix/path",
  "ENABLE_FEATURE": "1",
  "ENABLE_OTHER_FEATURE": "0"
}

To customize the build directory, just set "cmake.buildDirectory" to the desired path. This value may contain variables, so it can be configured, for instance, to point a path relative to the project folder:

"cmake.buildDirectory": "${workspaceFolder}/../build-cmake-project"

If you want to use a custom Qt version, or Qt is not configured system-wide (as is the case on Windows) it’s enough to set CMAKE_PREFIX_PATH properly in the "cmake.configureSettings" property in the settings file. For example:

"cmake.configureSettings": {
  "CMAKE_PREFIX_PATH": "otherprefixpath;C:/Qt/5.12.4/msvc2017_64"
  // ... other args
]

You can find a complete documentation for the CMake Tools extension here, featuring a guide on how to use CMake Tools from the UI, and a documentation for all available settings.

Running and debugging our Qt application

Now that your application has been built, let’s see how we can launch it and, most importantly, debug it.

Running qmake projects

For projects built with qmake, we don’t have any help from extensions and the only option we have is to bake our own launch configurations in the way we’ve seen in the last blog post. This is done in the launch configurations file (launch.json) or in the workspace file, and this is how a launch configuration looks:

{
  "name": "My application",
  "type": "cppvsdbg",
  "request": "launch",
  "program": "path/to/application",
  "stopAtEntry": false,
  "cwd": "${workspaceFolder}",
  "environment": [],
  "externalConsole": false
}

You can run launch configurations both with or without debugger, using “Debug: Start Debugging” (F5) or “Run: Start Without Debugging” (Ctrl+F5) respectively. If Qt is not configured at a system level, or you want to use a custom Qt version, the corresponding launch configuration will need to be explicitly configured to include Qt in its path.

You can do this by updating the "environment" property in the launch configuration. Below is an example for Windows, setting only the "PATH" environment variable. Configurations for other systems need to be adjusted but are essentially similar.

"environment": [
  {
    "name": "PATH",
    "value": "C:/Qt/5.12.4/msvc2017_64/bin;${env:PATH}"
  }
]

Side note: here ${env:PATH} means whaterever value the environment variable PATH has before the launch configuration is run. In general, the syntax ${env:VARNAME} can be used to get an environment variable in a task or a launch configuration.

Running CMake projects

Working with CMake is easier in principle, as the CMake extension provides the commands “CMake: Run Without Debugging” and “CMake: Debug”, allowing you to respectively launch and debug CMake targets.

However, this approach has a number of shortcomings:

  • It’s not possible to specify per-target run arguments for debug runs.
  • It’s not possible at all to specify run arguments for non-debug runs.
  • Some debugging options such as source mapping or custom views with natvis are not configurable using cmake settings.

So in conclusion using the CMake extension for running targets is not really convenient if you want a comprehensive debugging experience, and the best way to go is still to create your own launch configurations.

The CMake extension provides a few convenient variables for launch configurations:

  • ${command:cmake.launchTargetPath}: resolves to the full path of the executable for the target selected from the launch target menu.
  • ${command:cmake.launchTargetDirectory}: resolves to the directory containing the executable for the target selected from the launch target menu.

Qt aware debugging

What we’ve seen until now will let you build and run your Qt applications, using either your system provided Qt or your own. Debugging will work out of the box already, as long as the application code is involved. But wouldn’t it be great to also be able to peek inside Qt’s source code while debugging? Or if we had a better visualization for Qt specific types?

Turns out we can do both with little manipulation on launch configurations. Let’s see how.

Configure debug symbols

Usually Qt debug symbols are distributed alongside libraries, so there’s no real need to explicitly configure debug symbols paths. If that’s not the case, you can configure the debug symbols path by setting the "symbolSearchPath" property on a launch configuration. This property is a string and contains a list of paths separated by a semicolon.

"symbolSearchPath": "otherSearchPath;C:/Qt/5.12.4/msvc2017_64/bin"

This of course can be useful for adding debug symbols for other libraries too.

Source mapping

If the source directory for your Qt differs from the actual source directory (or directories) used while building it, you can configure the debugger to resolve those paths correctly. This happens for instance with binary Qt releases on Windows. You can enable source mapping in launch configurations by adding the "sourceFileMap" property. This property requires an object where each key is the source folder as it’s provided by the debug symbols, and the corresponding value is the path where the source code is in your system. This is how it can be configured for a binary Qt release on Windows:

"sourceFileMap": {
    "C:/work/build/qt5_workdir/w/s": "C:/Qt/5.12.4/Src",
    "Q:/qt5_workdir/w/s": "C:/Qt/5.12.4/Src",
    "C:/Users/qt/work/install": "C:/Qt/5.12.4/Src",
    "C:/Users/qt/work/qt": "C:/Qt/5.12.4/Src"
}
Using Natvis for Qt aware objects visualization

Natvis is a Visual Studio framework that allows you to customize how native C++ objects are visualized in the debugger. Natvis visualization rules are specified through xml files with a specific schema. A natvis file lists visualization rules for each C++ type, and every visualization rule consists in a series of properties. Such properties are meant to be user friendly and will be displayed on the debug window when visualizing objects of the corresponding type.

To name a few examples, a QString is visualized as the string it contains and has a size property and a number of items corresponding to its characters, and QRect will have a width and a height property instead of just the bare (and less intuitive) internal representation of the top left and bottom right points (x1, y1, x2, y2).

If you want to enable natvis in a debug run, just set the "visualizerFile" property in your launch configuration so that it points to the natvis file.

"visualizerFile": "path/to/qt5.natvis"

Debug pane before and after configuring natvis

You can find a ready to use natvis file for Qt 5 at this link.

Updating the code model

In order to be able to navigate Qt headers and enable IntelliSense for the Qt API, it’s enough to adjust the C++ settings for our project (c_cpp_properties.json) by adding the Qt include folder (and all its subfolders):

{
  // ...
  "includePath": [
    // ...
    "C:/Qt/5.12.4/msvc2017_64/include/**"
  ]
}

If you’re working on a CMake project, it’s also possible to use the CMake plugin as a configuration provider. Doing so, include paths and defines will be bound to the currently configured CMake build, and won’t need to be specified manually. This simplifies the C++ properties file considerably, as it’s shown in the example below:

{
  "configurations": [
    {
      "name": "Win32",
      "intelliSenseMode": "msvc-x64",
      "configurationProvider": "vector-of-bool.cmake-tools"
    }
  ],
  "version": 4
}

A note about using Visual Studio compilers on Windows

Visual Studio provides batch files that automate the environment setup necessary to use their C++ compiler and linker. In the last post we saw how it’s possible to configure a task so that it sets up the environment through the vcvars.bat script before running a command.

However, if you need to configure the environment with vcvars.bat for most of your build steps, it is also possible to configure Visual Studio Code so that it runs the batch file for every task. To do so, you need to tweak the configured shell (which is powershell by default on windows) and pass a few args. The setting name for doing this is “terminal.integrated.shellArgs.windows” and it’s set as follows:

"terminal.integrated.shellArgs.windows": [
  "Invoke-BatchFile 'C:/Program Files (x86)/Microsoft Visual Studio/2017/Professional/VC/Auxiliary/Build/vcvars64.bat' amd64",
  "; powershell"
]

What’s going on here is this: Visual Studio Code will launch by default every shell task by calling this command:

powershell <terminal.integrated.shellargs.windows> -Command <task command> <task argument list>

So, if you set “terminal.integrated.shellArgs.windows” this way, the final command will look like this:

powershell Invoke-BatchFile 'path/to/vcvars' ; powershell -Command <task command> <task argument list>

As a result, task commands will be effectively run in a powershell with the right environment set.

And that’s it for now. Many new things on the table, and some advanced features too. Hopefully this will help you with your workflow.

But there is still more to say, so make sure you don’t miss the next post!

About KDAB

If you like this article and want to read similar material, consider subscribing via our RSS feed.

Subscribe to KDAB TV for similar informative short video content.

KDAB provides market leading software consulting and development services and training in Qt, C++ and 3D/OpenGL. Contact us.

FacebookTwitterLinkedInEmail

5 thoughts on “VS Code for Qt Applications – Part 2”

  1. This is very cool if it can be done on VS Code. I never thought this was possible. I going to give it a try soon!

  2. followed all steps but I’m getting the error below when I click on run button

    QQmlApplicationEngine failed to load component
    qrc:/qml/main.qml:5:1: module “c2000.app” is not installed

    c2000.app is a sub-project

    any idea what I missed?

    I greatly appreciate it
    Afshin

  3. “terminal.integrated.shellArgs.windows” is not recognized by Visual Studio Code at the moment.

    1. That’s correct, shellArgs were deprecated for a while and have been removed definitively in June 2023.

      The new recommended way is to configure powershell through terminal.integrated.profiles.windows.

      Once you created a profile there you can either:

      • make it default by setting terminal.integrated.defaultProfile.windows or
      • use the profile explicitly by launching the command “Terminal: Create New Terminal (With Profile)” and picking the right profile

      See also: [1][2].

Leave a Reply

Your email address will not be published. Required fields are marked *