mirror of
https://github.com/hanzoai/visor.git
synced 2026-09-24 17:04:14 +00:00
make clangd work out of the box
- Adds a gen_compile_commands.py script to generate a `compile_commands.json` for clangd, similar to Linux's `scripts/clang-tools/gen_compile_commands.py` - Configures the dev container to generate the database before the editor is opened - Adds `clangd` vscode extension to the dev container (and installs `clangd` in the default image) - Updates CONTRIBUTING.md with instructions for generating a `compile_commands.json` database when *not* using a dev container Also switches to a less confusing link for the Dev Containers extension.
This commit is contained in:
Executable → Regular
+5
-4
@@ -1,5 +1,3 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# Copyright 2026 The gVisor Authors.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@@ -14,5 +12,8 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
# Running `go version` ensures that the appropriate Go binaries are downloaded.
|
||||
exec bazel run @io_bazel_rules_go//go version
|
||||
CompileFlags:
|
||||
Remove:
|
||||
# Remove gcc flags that clangd doesn't support
|
||||
- -fcanonical-system-headers
|
||||
- -fno-canonical-system-headers
|
||||
+3
-2
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"dockerFile": "images/default/Dockerfile",
|
||||
"overrideCommand": true,
|
||||
"postStartCommand": "tools/devcontainer-init-go.sh",
|
||||
"postStartCommand": "tools/devcontainer-init.sh",
|
||||
"waitFor": "postStartCommand",
|
||||
"mounts": [
|
||||
"source=/var/run/docker.sock,target=/var/run/docker-host.sock,type=bind"
|
||||
@@ -15,7 +15,8 @@
|
||||
"vscode": {
|
||||
"extensions": [
|
||||
"bazelbuild.vscode-bazel",
|
||||
"golang.go"
|
||||
"golang.go",
|
||||
"llvm-vs-code-extensions.vscode-clangd"
|
||||
],
|
||||
"settings": {
|
||||
"go.goroot": "${workspaceFolder}/bazel-${workspaceFolderBasename}/external/rules_go++go_sdk+main___download_0/"
|
||||
|
||||
@@ -9,3 +9,6 @@
|
||||
/repo.key
|
||||
# Generate webhook admission.
|
||||
/test/kubernetes/gvisor-injection-admission-webhook.yaml
|
||||
# clangd compilation database
|
||||
/.cache/clangd
|
||||
/compile_commands.json
|
||||
|
||||
Vendored
+2
-1
@@ -1,6 +1,7 @@
|
||||
{
|
||||
"recommendations": [
|
||||
"bazelbuild.vscode-bazel",
|
||||
"golang.go"
|
||||
"golang.go",
|
||||
"llvm-vs-code-extensions.vscode-clangd"
|
||||
]
|
||||
}
|
||||
|
||||
Vendored
+6
@@ -1,4 +1,10 @@
|
||||
{
|
||||
"files.associations": {
|
||||
".clangd": "yaml"
|
||||
},
|
||||
"clangd.arguments": [
|
||||
"--enable-config"
|
||||
],
|
||||
"go.toolsEnvVars": {
|
||||
"GOPACKAGESDRIVER": "${workspaceFolder}/tools/gopackagesdriver.sh"
|
||||
},
|
||||
|
||||
+15
-7
@@ -56,16 +56,15 @@ practices around AI tool usage. This policy includes, but is not limited to:
|
||||
### Visual Studio Code
|
||||
|
||||
The easiest way to work on gVisor in VSCode is using the
|
||||
[Dev Containers](https://code.visualstudio.com/docs/devcontainers/containers)
|
||||
extension.
|
||||
[Dev Containers][vscode-dev-containers] extension.
|
||||
|
||||
Open the gVisor repository in VSCode, and a "Folder contains a Dev Container"
|
||||
popup will appear. Press "Reopen in Container". The first load will take a few
|
||||
minutes while the dev container is built and the Go packages are loaded.
|
||||
Afterwards, Go language server features (hover, autocomplete, reference search,
|
||||
format on save, etc) should work "out of the box". VSCode's terminal will open
|
||||
in the dev container, where you can run `bazel build`, `bazel test`, and so on
|
||||
to work on gVisor.
|
||||
minutes while the dev container is built, the Go packages are loaded, and the
|
||||
clangd compilation database is generated. Afterwards, language server features
|
||||
(hover, autocomplete, reference search, format on save, etc) should work "out of
|
||||
the box" for both Go and C++. VSCode's terminal will open in the dev container,
|
||||
where you can run `bazel build`, `bazel test`, and so on to work on gVisor.
|
||||
|
||||
For example, you can build and run `runsc` in the dev container as follows:
|
||||
|
||||
@@ -91,10 +90,17 @@ For non-interactive commands, `make run` can be used as a shorthand as follows:
|
||||
make run TARGETS=//runsc ARGS="--rootless do echo hello world"
|
||||
```
|
||||
|
||||
In addition, for language server support in C++ files (e.g. the syscall tests):
|
||||
install [clangd][clangd] on the host, then run `tools/gen_compile_commands.py`
|
||||
to generate a `compile_commands.json`. After reloading the editor window,
|
||||
clangd should work.
|
||||
|
||||
### Other Editors
|
||||
|
||||
For LSP integration in other editors, ensure that the environment variable
|
||||
`GOPACKAGESDRIVER=tools/gopackagesdriver.sh` is passed along to `gopls`.
|
||||
Also, ensure `--allow-config` is passed to `clangd`, which will allow gVisor's
|
||||
`.clangd` configuration to take effect.
|
||||
|
||||
#### Using GOPATH (not recommended)
|
||||
|
||||
@@ -239,3 +245,5 @@ one above, the
|
||||
[gvisor-dev-list]: https://groups.google.com/forum/#!forum/gvisor-dev
|
||||
[go]: https://go.dev/doc/install
|
||||
[bazelisk]: https://github.com/bazelbuild/bazelisk
|
||||
[clangd]: https://clangd.llvm.org/installation
|
||||
[vscode-dev-containers]: https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-containers
|
||||
|
||||
@@ -14,8 +14,8 @@ RUN apt-get update && apt-get install -y curl gnupg2 git \
|
||||
apt-transport-https ca-certificates gnupg-agent \
|
||||
software-properties-common \
|
||||
pkg-config libffi-dev patch diffutils libssl-dev iptables kmod \
|
||||
clang crossbuild-essential-amd64 erofs-utils busybox-static libbpf-dev \
|
||||
iproute2 netcat-openbsd libnuma-dev
|
||||
clang clangd crossbuild-essential-amd64 erofs-utils busybox-static \
|
||||
libbpf-dev iproute2 netcat-openbsd libnuma-dev
|
||||
|
||||
# This package is needed to build eBPF on amd64, but not on arm64 where it
|
||||
# doesn't exist.
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
# Copyright 2026 The gVisor Authors.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
"""Exposes the generated files a C++ compile reads.
|
||||
|
||||
Used by tools/gen_compile_commands.py to avoid compiling the whole test suite
|
||||
to generate compile_commands.json for clangd.
|
||||
"""
|
||||
|
||||
load("@rules_cc//cc/common:cc_info.bzl", "CcInfo")
|
||||
|
||||
# Source extensions that end up in the compile commands database.
|
||||
_CC_SRC_EXTENSIONS = (".c", ".cc", ".cpp", ".cxx", ".c++", ".S", ".s", ".inc")
|
||||
|
||||
def _cc_compile_inputs_impl(target, ctx):
|
||||
inputs = []
|
||||
|
||||
# Transitively covers every header the target may include, generated ones
|
||||
# included, plus the _virtual_includes symlink trees that -I flags name.
|
||||
if CcInfo in target:
|
||||
inputs.append(target[CcInfo].compilation_context.headers)
|
||||
|
||||
# Generated sources are compiled, so they are entries in the database in
|
||||
# their own right; the header set above does not include them.
|
||||
inputs.append(depset([
|
||||
f
|
||||
for f in getattr(ctx.rule.files, "srcs", [])
|
||||
if not f.is_source and f.path.endswith(_CC_SRC_EXTENSIONS)
|
||||
]))
|
||||
|
||||
return [OutputGroupInfo(cc_compile_inputs = depset(transitive = inputs))]
|
||||
|
||||
# Propagates over deps so that a generated source belonging to a dependency is
|
||||
# built too; headers alone would already be complete at the top-level target.
|
||||
cc_compile_inputs = aspect(
|
||||
implementation = _cc_compile_inputs_impl,
|
||||
attr_aspects = ["deps"],
|
||||
)
|
||||
Executable
+23
@@ -0,0 +1,23 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# Copyright 2026 The gVisor Authors.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
# Running `go version` ensures that the appropriate Go binaries are downloaded.
|
||||
echo "====== Fetching Go... ======"
|
||||
bazel run @io_bazel_rules_go//go version
|
||||
|
||||
# Generate a compile_commands.json database so clangd lsp works on C++ tests.
|
||||
echo "====== Generating compile_commands.json... ======"
|
||||
python3 tools/gen_compile_commands.py
|
||||
Executable
+105
@@ -0,0 +1,105 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
# Copyright 2026 The gVisor Authors.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
import argparse
|
||||
import json
|
||||
import os
|
||||
from pathlib import Path
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
|
||||
def _bazel(*args):
|
||||
"""Runs bazel, returning its stdout."""
|
||||
|
||||
return subprocess.run(
|
||||
("bazel",) + args, check=True, text=True, stdout=subprocess.PIPE
|
||||
).stdout
|
||||
|
||||
|
||||
def _compile_actions(targets):
|
||||
"""Yields the C++ compile actions for the targets' dependencies."""
|
||||
|
||||
query = 'mnemonic("CppCompile", deps({}))'.format(" union ".join(targets))
|
||||
return json.loads(
|
||||
_bazel("aquery", "--output=jsonproto", "--noshow_progress", query)
|
||||
)["actions"]
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(
|
||||
description="Generates compile_commands.json for gVisor's C++ code."
|
||||
)
|
||||
parser.add_argument(
|
||||
"targets",
|
||||
nargs="*",
|
||||
default=["//test/..."],
|
||||
help="Bazel targets to index (default: %(default)s).",
|
||||
)
|
||||
args = parser.parse_args()
|
||||
targets = args.targets
|
||||
|
||||
workspace = Path(__file__).parent.parent.resolve()
|
||||
os.chdir(workspace)
|
||||
|
||||
_bazel(
|
||||
"build",
|
||||
"--aspects=//tools:cc_compile_inputs.bzl%cc_compile_inputs",
|
||||
"--output_groups=cc_compile_inputs",
|
||||
*targets,
|
||||
)
|
||||
execroot = Path(_bazel("info", "execution_root").strip())
|
||||
|
||||
database = []
|
||||
seen = set()
|
||||
for action in _compile_actions(targets):
|
||||
arguments = action["arguments"]
|
||||
if "-c" not in arguments:
|
||||
continue
|
||||
source = Path(arguments[arguments.index("-c") + 1])
|
||||
|
||||
# Skip third-party code.
|
||||
if "external" in source.parts:
|
||||
continue
|
||||
|
||||
# A source can be compiled by several actions.
|
||||
# Skip all but the first.
|
||||
if source in seen:
|
||||
continue
|
||||
seen.add(source)
|
||||
|
||||
# Add the compile command to the database.
|
||||
database.append(
|
||||
{
|
||||
# Special-case for generated files in bazel-out/. When LSP jump is used,
|
||||
# it will navigate to the execroot path, so match that in the database.
|
||||
"file": str(
|
||||
(execroot if source.parts[0] == "bazel-out" else workspace) / source
|
||||
),
|
||||
"directory": str(execroot),
|
||||
"arguments": arguments,
|
||||
}
|
||||
)
|
||||
|
||||
path = os.path.join(workspace, "compile_commands.json")
|
||||
with open(path, "w") as out:
|
||||
json.dump(database, out, indent=4)
|
||||
print(f"generated {path}: {len(database)} entries")
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
Reference in New Issue
Block a user