Merge pull request #13887 from relkochta:relkochta/make-clangd-work

PiperOrigin-RevId: 955584848
This commit is contained in:
gVisor bot
2026-07-29 10:22:35 -07:00
10 changed files with 212 additions and 17 deletions
+5 -4
View File
@@ -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
View File
@@ -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/"
+3
View File
@@ -9,3 +9,6 @@
/repo.key
# Generate webhook admission.
/test/kubernetes/gvisor-injection-admission-webhook.yaml
# clangd compilation database
/.cache/clangd
/compile_commands.json
+2 -1
View File
@@ -1,6 +1,7 @@
{
"recommendations": [
"bazelbuild.vscode-bazel",
"golang.go"
"golang.go",
"llvm-vs-code-extensions.vscode-clangd"
]
}
+6
View File
@@ -1,4 +1,10 @@
{
"files.associations": {
".clangd": "yaml"
},
"clangd.arguments": [
"--enable-config"
],
"go.toolsEnvVars": {
"GOPACKAGESDRIVER": "${workspaceFolder}/tools/gopackagesdriver.sh"
},
+16 -8
View File
@@ -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`.
`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
+2 -2
View File
@@ -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.
+49
View File
@@ -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"],
)
+23
View File
@@ -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
+103
View File
@@ -0,0 +1,103 @@
#!/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())