Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to compile/debug a C++ application in Docker with Visual Studio Code on Windows

I'm new on Visual Studio Code and Docker. Now I want to use Visual Studio Code to edit my C++ code and Docker to compile/debug.

I don't know how to write the launch.json and task.json correctly files, so that I can use Docker to compile/debug my C++ application just under Visual Studio Code development environment. Is there a solution for this problem?

Here is my platform information:

OS: Windows 10
Visual Studio Code: v1.25.1
OS in Docker: Ubuntu 16.04 (Xenial Xerus)
Compiler in Docker: g++

like image 351
xiangyue1993 Avatar asked Jul 20 '18 01:07

xiangyue1993


People also ask

How do I compile C project in Visual Studio Code?

Open your C++ code file in Text Editor, then use shortcut Ctrl+Alt+N , or press F1 and then select/type Run Code , or right click the Text Editor and then click Run Code in context menu, the code will be compiled and run, and the output will be shown in the Output Window.

How to build a docker image using Visual Studio Code?

Build the image using docker build -t gdb . in the working directory, or in Visual Studio Code run the preconfigured task build docker gdb from F1 → Run Task. 2. Building the application In the project, run docker run --rm -it -v $ {pwd}:/work --workdir /work gcc make debug from a PowerShell window in the working directory.

How to debug apps in a local Docker container?

To debug apps in a local Docker container, the following tools must be installed: Visual Studio 2019 with the Web Development workload installed To run Docker containers locally, you must have a local Docker client. You can use Docker for Windows, which uses Hyper-V and requires Windows 10.

Is it possible to debug gdbserver in Docker Linux containers?

With this article debugging with gdbserver in docker linux containers is doable from within Visual Studio 2019. How about in a more complex visual studio 2019 solution where some projects are in c# and others in c++.

What is the Docker extension in VS Code?

The Docker extension provides a docker debug configuration provider that manages how VS Code will launch an application and/or attach a debugger to the application in a running Docker container. This provider is configured via entries within launch.json , with configuration being specific to each application platform supported by the provider.


2 Answers

This answer assumes that you are not trying to do anything with multiple containers... I'm assuming that you just want to use a single container to build some C++ code, and that all of your code is in a folder called C:\vsc_docker_cc_gdb. I also assume you have the C++ and Docker extensions from Microsoft installed in Visual Studio Code.

Let's start with a simple C++ file, called hello.cc:

#include <iostream>
int main(int argc, char **argv) {
  std::cout << "Hello from Docker" << std::endl;
}

Let's also add a Makefile:

CXXFLAGS = -O3 -ggdb -m64
LDFLAGS  = -m64

all: hello.exe
.PRECIOUS: hello.exe hello.o
.PHONY: all clean

%.o: %.cc
    $(CXX) -c $< -o $@ $(CXXFLAGS)

%.exe: %.o
    $(CXX) $^ -o $@ $(LDFLAGS)

clean:
    rm -f hello.o hello.exe

Here's a Dockerfile that extends gcc:latest by adding GDB and gdbserver (note: I'm not sure gdbserver is needed):

FROM gcc:latest
LABEL Name=vsc_docker_cc_gdb Version=0.0.2
RUN apt-get -y update
RUN apt-get -y install gdb gdbserver
WORKDIR /root

Here's .vscode/tasks.json:

{
    "version": "2.0.0",
    "tasks": [
        {
            "label": "build (in container)",
            "type": "shell",
            "command": "docker run --privileged -v c:/vsc_docker_cc_gdb/:/root vsc_docker_cc_gdb make",
            "group": {
                "kind": "build",
                "isDefault": true
            },
            "problemMatcher": {
                "owner": "cpp",
                "fileLocation": [
                    "relative",
                    "${workspaceFolder}"
                ],
                "pattern": {
                    "regexp": "^(.*):(\\d+):(\\d+):\\s+(warning|error):\\s+(.*)$",
                    "file": 1,
                    "line": 2,
                    "column": 3,
                    "severity": 4,
                    "message": 5
                }
            }
        },
        {
            "label": "clean (in container)",
            "type": "shell",
            "command": "docker run --privileged -v c:/vsc_docker_cc_gdb/:/root vsc_docker_cc_gdb make clean",
            "group": "build",
            "problemMatcher": []
        },
        {
            "label": "remove containers",
            "type": "shell",
            "command": "docker ps -a -q | % { docker rm $_ }",
            "problemMatcher": []
        },
        {
            "label": "run the code",
            "type": "shell",
            "command": "docker run --privileged -v c:/vsc_docker_cc_gdb/:/root vsc_docker_cc_gdb ./hello.exe",
            "group": "build",
            "problemMatcher": []
        },
        {
            "label": "prepare to debug",
            "type": "shell",
            "command": "docker run --privileged -v c:/vsc_docker_cc_gdb/:/root --name debug_vsc -it vsc_docker_cc_gdb ",
            "group": "build",
            "problemMatcher": []
        }
    ]
}

And finally, .vscode/launch.json:

{
    "version": "0.2.0",
    "configurations": [{
        "name": "(gdb) Pipe Launch",
        "type": "cppdbg",
        "request": "launch",
        "program": "/root/hello.exe",
        "cwd": "/root",
        "args": [],
        "stopAtEntry": true,
        "environment": [],
        "externalConsole": true,
        "pipeTransport": {
            "debuggerPath": "/usr/bin/gdb",
            "pipeProgram": "docker.exe",
            "pipeArgs": ["exec", "-i", "debug_vsc", "sh", "-c"],
            "pipeCwd": "${workspaceRoot}"
        },
        "MIMode": "gdb",
        "setupCommands": [{
            "description": "Enable pretty-printing for gdb",
            "text": "-enable-pretty-printing",
            "ignoreFailures": true
        }]
    }, ]
}

There are two important things here. The first is that you'll notice that parts of launch.json are referring to paths in the container (/root/) and others are referring to paths on the Windows host (workspaceRoot). That is important.

The second is that you'll need to have a container running, and then you can launch a debug process into it. Here's a recipe to go from zero to starting that special container and launching a debugger in it.

  • From PowerShell: docker pull gcc
  • From Visual Studio Code: F1, Docker: Build Image (pick vsc_docker_cc_gdb:latest)
  • From Visual Studio Code: Ctrl + Shift + B to build the code
  • From Visual Studio Code: F1, Tasks: Run Task (pick "remove containers")
  • From Visual Studio Code: F1, Tasks: Run Task (pick "prepare to debug")
  • From Visual Studio Code: F5 to start the debugger

From there, the Visual Studio Code Debug Console should work, and you should be able to set breakpoints, watch variables, and enter debug commands.

like image 179
Mike Spear Avatar answered Sep 28 '22 10:09

Mike Spear


I set up a minimal working example on GitHub: https://github.com/fschwaiger/docker-cpp-vscode

The idea is as follows, assuming you have the ms-vscode.cpptools extension:

  1. You need containers with gcc and gdb installed (can be the same)
  2. You build the application in the container
  3. You run gdb from within the container

1. Get the images for gcc and gdb

gcc is available directly from Docker Hub: docker pull gcc. I did not find gdb there, so there is a Dockerfile to build it:

FROM gcc:latest
RUN apt-get update && apt-get install -y gdb
RUN echo "set auto-load safe-path /" >> /root/.gdbinit

It builds on gcc:latest and installs gdb, so you can use the same image to compile and debug. It also sets option set auto-load safe-path / in /root/.gdbinit to suppress a warning when running gdb in the container. Safety should not be a concern for local development.

Build the image using docker build -t gdb . in the working directory, or in Visual Studio Code run the preconfigured task build docker gdb from F1Run Task.

2. Building the application

In the project, run docker run --rm -it -v ${pwd}:/work --workdir /work gcc make debug from a PowerShell window in the working directory. Using Visual Studio Code, this can be done by the preconfigured task make debug from F1Run Task.

3. Debug the application

You want to configure Visual Studio Code to run /usr/bin/gdb from within the container. You can use the pipeTransport option in launch.json for that and make it run:

docker run --rm --interactive --volume ${workspaceFolder}:/work --workdir /work --privileged gdb sh -c /usr/bin/gdb

Explanation:

  • --privileged: allow binary debugging
  • --volume ${workspaceFolder}:/work --workdir /work: mount the project folder into the container
  • --rm: remove the container after exit
  • --interactive: VSCode will issue interactive commands to the gdb shell
  • sh -c: defines a shell entrypoint within GDB is run

The overall launch.json looks like follows. Notice that program and cwd are the paths within the container. sourceFileMap allows the debugger to match the breakpoints with the source files. The rest is the default template stuff from the C++ extension.

{
    "version": "0.2.0",
    "configurations": [
        {
            "name": "(gdb) Docker",
            "type": "cppdbg",
            "request": "launch",
            "program": "build/apps/program",
            "args": [],
            "stopAtEntry": true,
            "cwd": "/work",
            "environment": [],
            "externalConsole": true,
            "preLaunchTask": "make debug",
            "targetArchitecture": "x64",
            "sourceFileMap": { "/work": "${workspaceFolder}" },
            "pipeTransport": {
                "debuggerPath": "/usr/bin/gdb",
                "pipeProgram": "docker.exe",
                "pipeArgs": ["run","--rm","--interactive","--volume","${workspaceFolder}:/work","--workdir","/work","--privileged","gdb","sh","-c"],
                "pipeCwd": ""
            },
            "MIMode": "gdb",
            "setupCommands": [
                {
                    "description": "Enable pretty-printing for gdb",
                    "text": "-enable-pretty-printing",
                    "ignoreFailures": true
                }
            ]
        }
    ]
}

With this setup, all you need to do is press play in the debug workspace.

like image 44
fschwaiger Avatar answered Sep 28 '22 10:09

fschwaiger