Home About Recent Bazel Build with Visual Studio Code Martial Responsibility For Safety The Buffalo Trap Longsword Solo Drills - Meÿer Square Starting Historical European Martial Arts Group Game development Bazel Build with Visual Studio Code Generic A* for Games Procedural Island Generation Data-Oriented Design Matters Misconceptions of Component-Based Entity Systems Level of Detail Experiments Planet Generation - Part II Planet Generation - Part I Procedural Generation in Games Oculus Rift Integration Android Favorite Android Games NDK with Android Studio Android NDK Programming - Part III Android NDK Programming - Part II Android NDK Programming - Part I Personal Personal Stuff: Running! Global Game Jam 2014 Experiences Anime Claymore The Twelve Kingdoms Games Favorite Android Games Dungeons & dragons D&D Journal: I Historical european martial arts Martial Responsibility For Safety The Buffalo Trap Longsword Solo Drills - Meÿer Square Starting Historical European Martial Arts Group Historical European Martial Arts - Equipment Longsword Martial Responsibility For Safety The Buffalo Trap Longsword Solo Drills - Meÿer Square

I mostly use C++ for game development and rendering and I needed an easy way to build my game across Windows, Linux, OSX and sometimes Android and other platforms, so I switched to Google's Bazel build system and Visual Studio Code to have a nice cross-platform solution.

In this blog post I'll go over the process of making Bazel work well with Visual Studio Code, including debugging C++ code.

Debugging Code

I'll do this in 5 steps:

  1. Setting up Visual Studio Code.
  2. Setting up Bazel.
  3. Setting up a Bazel project.
  4. Configuring the build task.
  5. Configuring the debug settings.

Note: I'm going to assume a Windows OS because that's the most complicated one to get it all to work. For OSX/Linux it's pretty much the same.

Visual Studio Code

Let's start with the easy part. We need to get Visual Studio Code and the C++ extension.

  • Get Visual Studio Code.
  • Open Visual Studio Code.
  • Click the Extensions View icon (Extensions) on the Sidebar, or use the shortcut Ctrl+Shift+X.
  • Search for C++.
  • Click Install, then click Reload.

Ok, that was easy. Thanks Microsoft! Now let's get Bazel.

Bazel

Now we need to get Bazel sorted. I'm going to assume you're using Windows as it's the most complicated setup.

  1. Go to Bazel's website and press Get Started.
  2. (Make sure you've installed the prerequisites)
  3. Follow the instructions under Getting Bazel
  4. After you've downloaded Bazel and set it up, then follow the "Build C++" instructions on this page.

Ok, that wasn't as clean as installing Visual Studio Code, and admittingly I deferred most of the instructions, but hopefully you got it through. Now we can do the fun part!

Setting up a Bazel project

In this section we'll create a super basic C++ project in Bazel so we can demonstrate our build and debugging. Skip this step if you already know Bazel and have a project you're working with.

We need three files:

  • WORKSPACE
  • main.cpp
  • BUILD.bazel

WORKSPACE

You must have an existing WORKSPACE file to build with Bazel, but it can be empty. So simply create an empty file named WORKSPACE.

Main

This is our super simple main.cpp:

#include <iostream>
#include <string>

int FunctionToDebug(int x, int y) {
  int sum = x + y;
  return sum;
}

int main() {
  std::cout << "Hello world.\n";

  int sum = FunctionToDebug(2, 4);
  std::cout << "Sum of 2 + 4 = " << sum << ".\n";

  return 0;
}

BUILD.bazel

And for our BUILD.bazel:

cc_binary(
    name = "example",
    srcs = [ 
        "main.cpp",
    ],
    visibility = ["//visibility:public"],
)

We can test that this is all working by opening terminal/cmd in this folder and running:

bazel run :example

It should give you a print similar to this:

C:\Projects\example> bazel run :example
Starting local Bazel server and connecting to it...
INFO: Analyzed target //:example (10 packages loaded, 76 targets configured).
INFO: Found 1 target...
Target //:example up-to-date:
  C:/users/shanee/_bazel_shanee/rimt6fvp/execroot/__main__/bazel-out/x64_windows-fastbuild/bin/example.exe
INFO: Elapsed time: 49.794s, Critical Path: 4.01s
INFO: 2 processes: 2 local.
INFO: Build completed successfully, 6 total actions
INFO: Running command line: C:/users/shanee/_bazel_shane/rimt6fvp/execroot/__maINFO: Build completed successfully, 6 total actions
Hello world.
Sum of 2 + 4 = 6.
PS C:\Projects\example>

Configuring the build task

Now for the fun part. Let's configure our Build Task in Visual Studio Code.

What is this?

The build task configures some operation for Visual Studio Code to run and build our code. In our case the operation will be to call bazel build.

If you don't already have any build tasks set (otherwise skip this step):

  1. Go to the Terminal tab and click Configure Tasks...,
  2. Select Create tasks.json file from template
  3. Finally select Others.

This will open a generated tasks.json file.

If you skipped the above step, open your tasks.json file.

We're going to make a simple task to build our app. If you just made the tasks.json file, simply replace it with this:

{
  "version": "2.0.0",
  "tasks": [
    {
      "label": "Build Example (Debug)",
      "type": "shell",
      "command": "bazel build :example -c dbg",
      "windows": {
        "command": "bazel build :example --experimental_enable_runfiles -c dbg"
      },
      "osx": {
        "command": "bazel build :example -c dbg --spawn_strategy=standalone",
      },
      "group": {
        "kind": "build",
        "isDefault": true
      },
    }
  ]
}

Two things to note here:

-c dbg

The "-c dbg" flag is Bazel's way of compiling in debug mode so we can debug the app in the next state. You can change your build to to optimized by using "-c opt". Default is "fastbuild".

We have a special tag for Windows. That's because bazel creates symlinks in Linux and OSX but not in Windows, so we add a special flag for it to create symlinks in windows.

--experimental_enable_runfiles

As shown here:

"windows": {
  "command": "bazel build :example --experimental_enable_runfiles -c dbg"
},

You want this, especially if you're a game developer with asset files, because otherwise your app won't find your files.

Now we can run our build task from Visual Studio Code (Terminal->Run Task, or Ctrl+B).

Note: that this will only build the app, but not run it. If you want a task that also runs your app, change the bazel build command to bazel run. Make sure you changed it in both command tags.

Workaround for OSX Debugging

Some people had trouble getting debugging to work on OSX. Try adding --spawn_strategy=standalone to the command to disable sandboxing:

"osx": {
  "command": "bazel build :example -c dbg --spawn_strategy=standalone"
},

Configuring the debug settings

Now to make debugging work. In this section we'll edit the configuration file which tells Visual Studio Code how to debug our code. This will allow us to add breakpoints, see values of variables and step through our code.

  1. Press the Debug View icon (Debug) on the Sidebar, or use the shortcut Ctrl+Shift+D.
  2. Press the little down arrow next to the play button and select "Add Configuration..."
    Add Debug Configuration
  3. Select C++ (Windows) if you're on windows, or C++ (GDB/LLDB) otherwise.

This will create a launch.json file. Let's edit it:

name

Change name to whatever you wish. "Example" will do for me.

preLaunchTask

You don't have it yet. Add a preLaunchTask line with the name of the build task. For example:

"preLaunchTask": "Build Example (Debug)",

The name has to perfectly match the build task's name.

type

This is the type of debugging configuration. For Windows it should be cppvsdbg, while for Linux and OSX it is cppdbg.

The Linux and OSX configurations also require a "MIMode" with the value of gdb (Linux) or lldb (OSX).

program

This is the path to the binary that will be built through the bazel build command. If you're using my example app on Windows it'll be "${workspaceFolder}/bazel-out/x64_windows-dbg/bin/example.exe", while on OSX it should be "${workspaceFolder}/bazel-out/darwin-dbg/bin/example".

Note the x64_windows-dbg and darwin-dbg in the path.

cwd

This is the working directory for our binary. Similarly to the previous section, note the difference in the path.

For Windows: "${workspaceFolder}/bazel-out/x64_windows-dbg/bin/example.exe.runfiles/"

Or for OSX: "${workspaceFolder}/bazel-out/darwin-dbg/bin/example.runfiles/main/"

Note, you can set the above three with the following:

"windows": {
  "type": "cppvsdbg",
  "cwd": "${workspaceFolder}/bazel-out/x64_windows-dbg/bin/example.exe.runfiles/__main__/",
  "program": "${workspaceFolder}/bazel-out/x64_windows-dbg/bin/example.exe",
},
"osx": {
  "type": "cppdbg",
  "MIMode": "lldb",
  "program": "${workspaceFolder}/bazel-out/darwin-dbg/bin/example",
  "cwd": "${workspaceFolder}/bazel-out/darwin-dbg/bin/example.runfiles/__main__/",
},
"linux": {
  "type": "cppdbg",
  "MIMode": "gdb",
  "program": "${workspaceFolder}/bazel-out/local_linux-dbg/bin/example",
  "cwd": "${workspaceFolder}/bazel-out/local_linux-dbg/bin/example.runfiles/__main__/",
},

But you still need to have a default configuration. In my full file (below) I've set Windows as the default.


And now we can debug by pressing the green play button in the Debug view! Make sure to add a breakpoint somewhere.

Debugging Code

Except you'll notice the file that was opened was some weird copy of your main.cpp file. This is because of Bazel and we can fix it by adding a sourceFileMap tag to our launch configuration.

This is slightly tricky however because Bazel will put your files in a some hashed location. For example, mine ended looking like this:

"sourceFileMap": {
  "C:/Users/shane/_bazel_shane/rimt6fvp/execroot/__main__/": "${workspaceFolder}",
},

The easiest way to get the path is by debugging once with a breakpoint and right clicking on the file tab and selecting "Copy Path" (Shortcut: Shift+Alt+C):

Source Path

Here's my final launch.json for reference.

{
  "version": "0.2.0",
  "configurations": [
    {
      "name": "Example",
      "type": "cppvsdbg",
      "request": "launch",
      "args": [],
      "stopAtEntry": false,
      "preLaunchTask": "Build Example (Debug)",
      "cwd": "${workspaceFolder}/bazel-out/x64_windows-dbg/bin/example.exe.runfiles/__main__/",
      "program": "${workspaceFolder}/bazel-out/x64_windows-dbg/bin/example.exe",
      "externalConsole": false,
      "windows": {
        "type": "cppvsdbg",
        "cwd": "${workspaceFolder}/bazel-out/x64_windows-dbg/bin/example.exe.runfiles/__main__/",
        "program": "${workspaceFolder}/bazel-out/x64_windows-dbg/bin/example.exe",
        "sourceFileMap": {
          "C:/Users/shane/_bazel_shane/rimt6fvp/execroot/__main__/": "${workspaceFolder}",
        },
      },
      "osx": {
        "type": "cppdbg",
        "MIMode": "lldb",
        "program": "${workspaceFolder}/bazel-out/darwin-dbg/bin/example",
        "cwd": "${workspaceFolder}/bazel-out/darwin-dbg/bin/example.runfiles/__main__/",
        "sourceFileMap": {
          "/var/tmp/_bazel_shanee/50aa63f545057969f77c2d16ce9f31a9/execroot/__main__/": "${workspaceFolder}",
        },
      },
      "linux": {
        "type": "cppdbg",
        "MIMode": "gdb",
        "program": "${workspaceFolder}/bazel-out/local_linux-dbg/bin/example",
        "cwd": "${workspaceFolder}/bazel-out/local_linux-dbg/bin/example.runfiles/__main__/",
      },
    }
  ]
}

And that's it! I hope someone found it useful :)

Back
Home About Recent Bazel Build with Visual Studio Code Martial Responsibility For Safety The Buffalo Trap Longsword Solo Drills - Meÿer Square Starting Historical European Martial Arts Group Game development Bazel Build with Visual Studio Code Generic A* for Games Procedural Island Generation Data-Oriented Design Matters Misconceptions of Component-Based Entity Systems Level of Detail Experiments Planet Generation - Part II Planet Generation - Part I Procedural Generation in Games Oculus Rift Integration Android Favorite Android Games NDK with Android Studio Android NDK Programming - Part III Android NDK Programming - Part II Android NDK Programming - Part I Personal Personal Stuff: Running! Global Game Jam 2014 Experiences Anime Claymore The Twelve Kingdoms Games Favorite Android Games Dungeons & dragons D&D Journal: I Historical european martial arts Martial Responsibility For Safety The Buffalo Trap Longsword Solo Drills - Meÿer Square Starting Historical European Martial Arts Group Historical European Martial Arts - Equipment Longsword Martial Responsibility For Safety The Buffalo Trap Longsword Solo Drills - Meÿer Square