Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

vscode: add a script to generate debugger config #12917

Merged
merged 5 commits into from
Sep 21, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .devcontainer/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ This task is needed to run everytime after:
- Changing a BUILD file that add/remove files from a target, changes dependencies
- Changing API proto files

There are additional tools for VS Code located in [`tools/vscode`](../tools/vscode) directory.

## Advanced Usages

### Using Remote Build Execution
Expand Down
1 change: 1 addition & 0 deletions .devcontainer/devcontainer.json
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
"zxh404.vscode-proto3",
"bazelbuild.vscode-bazel",
"llvm-vs-code-extensions.vscode-clangd",
"vadimcn.vscode-lldb",
"webfreak.debug",
"ms-python.python"
]
Expand Down
35 changes: 35 additions & 0 deletions tools/vscode/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# Tools for VSCode

This directory contains tools which is useful for developers using VSCode.

## Recommended VSCode setup

It is recommended to use [devcontainer](../.devcontainer/README.md), or setting up an equivalent
environment. Recommended extensions and settings are listed in
[devcontainer.json](../.devcontainer/devcontainer.json).

## Refresh compilation database

`tools/vscode/refresh_compdb.sh` is a script to refresh compilation database, it may take a while
to generate all dependencies for code completion, such as protobuf generated codes, external dependencies.
If you changed proto definition, or changed any bazel structure, rerun this to get code completion
correctly.

Note that it is recommended to disable VSCode Microsoft C/C++ extension and use `vscode-clangd` instead for
C/C++ code completion.

## Generate debug config

`tools/vscode/generate_debug_config.py` is a script to generate VSCode debug config in `.vscode/launch.json`.
The generated config will be named `<debugger type> <bazel target>`.

For example:
```
tools/vscode/generate_debug_config.py //source/exe:envoy-static --args "-c envoy.yaml"
```

Generates an entry named `gdb //source/exe:envoy-static` for GDB in `launch.json`. It can be
used to generate config for tests also.

The generated `gdb` config are compatible with [Native Debug](https://marketplace.visualstudio.com/items?itemName=webfreak.debug) extension,
`lldb` config are compatible with [VSCode LLDB](https://marketplace.visualstudio.com/items?itemName=vadimcn.vscode-lldb) extension.
123 changes: 123 additions & 0 deletions tools/vscode/generate_debug_config.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
#!/usr/bin/env python3

import argparse
import json
import os
import pathlib
import shlex
import shutil
import subprocess

BAZEL_OPTIONS = shlex.split(os.environ.get("BAZEL_BUILD_OPTIONS", ""))


def bazelInfo(name, bazel_extra_options=[]):
return subprocess.check_output(["bazel", "info", name] + BAZEL_OPTIONS +
bazel_extra_options).decode().strip()


def getWorkspace():
return bazelInfo("workspace")


def getExecutionRoot(workspace):
# If compilation database exists, use its execution root, this allows setting
# breakpoints with clangd navigation easier.
try:
compdb = pathlib.Path(workspace, "compile_commands.json").read_text()
return json.loads(compdb)[0]['directory']
except:
return bazelInfo("execution_root")


def binaryPath(bazel_bin, target):
return pathlib.Path(
bazel_bin,
*[s for s in target.replace('@', 'external/').replace(':', '/').split('/') if s != ''])


def buildBinaryWithDebugInfo(target):
targets = [target, target + ".dwp"]
subprocess.check_call(["bazel", "build", "-c", "dbg"] + BAZEL_OPTIONS + targets)

bazel_bin = bazelInfo("bazel-bin", ["-c", "dbg"])
return binaryPath(bazel_bin, target)


def getLaunchJson(workspace):
try:
return json.loads(pathlib.Path(workspace, ".vscode", "launch.json").read_text())
except:
return {"version": "0.2.0"}


def writeLaunchJson(workspace, launch):
launch_json = pathlib.Path(workspace, ".vscode", "launch.json")
backup_launch_json = pathlib.Path(workspace, ".vscode", "launch.json.bak")
if launch_json.exists():
shutil.move(str(launch_json), str(backup_launch_json))

launch_json.write_text(json.dumps(launch, indent=4))


def gdbConfig(target, binary, workspace, execroot, arguments):
return {
"name": "gdb " + target,
"request": "launch",
"arguments": arguments,
"type": "gdb",
"target": str(binary),
"debugger_args": ["--directory=" + execroot],
"cwd": "${workspaceFolder}",
"valuesFormatting": "disabled"
}


def lldbConfig(target, binary, workspace, execroot, arguments):
return {
"name": "lldb " + target,
"program": str(binary),
"sourceMap": {
"/proc/self/cwd": workspace,
"/proc/self/cwd/external": execroot + "/external",
"/proc/self/cwd/bazel-out": execroot + "/bazel-out"
},
"cwd": "${workspaceFolder}",
"args": shlex.split(arguments),
"type": "lldb",
"request": "launch"
}


def addToLaunchJson(target, binary, workspace, execroot, arguments, debugger_type):
launch = getLaunchJson(workspace)
new_config = {}
if debugger_type == "lldb":
new_config = lldbConfig(target, binary, workspace, execroot, arguments)
else:
new_config = gdbConfig(target, binary, workspace, execroot, arguments)

configurations = launch.get("configurations", [])
for config in configurations:
if config.get("name", None) == new_config["name"]:
config.clear()
config.update(new_config)
break
else:
configurations.append(new_config)

launch["configurations"] = configurations
writeLaunchJson(workspace, launch)


if __name__ == "__main__":
parser = argparse.ArgumentParser(description='Build and generate launch config for VSCode')
parser.add_argument('--debugger', default="gdb")
parser.add_argument('--args', default='')
parser.add_argument('target')
args = parser.parse_args()

workspace = getWorkspace()
execution_root = getExecutionRoot(workspace)
debug_binary = buildBinaryWithDebugInfo(args.target)
addToLaunchJson(args.target, debug_binary, workspace, execution_root, args.args, args.debugger)