Kernels documentation
IDE setup with direnv and the kernel devshell
IDE setup with direnv and the kernel devshell
Introduction
Language servers do not interpret build.toml, so IDE completion for
CUDA, ROCm, framework headers, and the kernel’s Python wrapper does not
work out of the box. This guide shows how to configure VS Code so that
completion resolves against the same toolchain kernel-builder
uses.
The setup has three pieces:
kernel-builder create-pyprojectto emit CMake and setuptools files the IDE can read (see Local Development).- The kernel-builder devshell, which provides the toolchain (CUDA, ROCm, Torch headers, etc.) from the Nix store.
direnvto activate the devshell oncd, so VS Code inherits the environment through the shell.
Pinning the toolchain through Nix keeps IDE completion aligned with
the build. It also makes switching between CUDA, ROCm, or XPU a
one-line change in .envrc.
Installing direnv and nix-direnv
On non-NixOS systems, install both via nix profile:
$ nix profile install nixpkgs#nix-direnvAdd the direnv hook to your shell rc (~/.bashrc or
~/.zshrc, for example):
eval "$(direnv hook bash)" # or: direnv hook zshSource the rc file (or open a new shell) so the hook is active in the current session:
$ source ~/.bashrc # or: source ~/.zshrcWire nix-direnv into direnv:
$ mkdir -p ~/.config/direnv
$ echo 'source $HOME/.nix-profile/share/nix-direnv/direnvrc' \
>> ~/.config/direnv/direnvrcOn NixOS
or with home-manager,
enable programs.direnv with
nix-direnv instead. See
terraform/nixos-configuration.nix
for a working example.
Activating the devshell with direnv
From the kernel root directory (the one containing flake.nix and
build.toml), tell direnv to use the flake’s default devshell:
$ echo 'use flake' > .envrc
$ direnv allowdirenv now activates the default devshell whenever you cd into the
project. The devshell’s shellHook creates and activates a .venv on
first entry. Confirm it picked up the toolchain and venv:
$ which nvcc
/nix/store/.../bin/nvcc
$ ls -ld .venv
drwxr-xr-x ... .venv
$ which python
/path/to/kernel/.venv/bin/pythonIf .venv is missing, re-run direnv reload and check the output for
the Creating new venv environment in path: './.venv' line from the
shellHook.
To pin a non-default build variant, name it explicitly:
$ echo 'use flake .#devShells.torch211-cxx11-rocm71-x86_64-linux' > .envrc
$ direnv allowSee Build Variants for the variant list.
Generating IDE-facing project files
direnv puts the toolchain on PATH, but the C++ language server still
needs a CMake-derived compile_commands.json to resolve per-file
include paths. Generate the CMake/setuptools project and the file:
$ kernel-builder create-pyproject -f
$ cmake -B build-ext -DCMAKE_EXPORT_COMPILE_COMMANDS=ON
$ ln -sf build-ext/compile_commands.json compile_commands.json-DCMAKE_EXPORT_COMPILE_COMMANDS=ON is required: the generated CMake
does not set it. The symlink lets the language server find the file
at the project root.
As noted in Local Development, do not commit the generated files.
Configuring VS Code
Install the mkhl.direnv
extension. It activates the project’s .envrc when VS Code opens
the workspace, so language servers and the integrated terminal see
the devshell environment without launching code from a shell.
Alternatively, skip the extension and open the project from a direnv-activated shell — VS Code inherits the environment that way too:
$ cd path/to/kernel
$ code .Install one of the following first-party extensions for C++/CUDA completion:
llvm-vs-code-extensions.vscode-clangd(recommended for CUDA).ms-vscode.cpptools(Microsoft C/C++).
Add .vscode/settings.json (do not commit):
{
"python.defaultInterpreterPath": "${workspaceFolder}/.venv/bin/python",
// clangd
"clangd.arguments": ["--compile-commands-dir=${workspaceFolder}"],
// Microsoft C/C++ extension
"C_Cpp.default.compileCommands": "${workspaceFolder}/compile_commands.json"
}Depending on the extension being used, the configuration above behaves differently:
- With
clangd, theclangd.argumentsline is optional. clangd already looks in the parent directories of each source file forcompile_commands.jsonand will find the workspace-root symlink on its own (clangd docs). Setting it explicitly does no harm. - With the Microsoft C/C++ extension, the
C_Cpp.default.compileCommandsline is required. The extension does not pick upcompile_commands.jsonfrom the workspace root on its own, unless another extension (such as CMake Tools) tells it where to look.
To verify, open torch-ext/torch_binding.cpp and hover an
#include <torch/torch.h> directive. The resolved path should point
into /nix/store/..., not a system path.
Remote development
Use the VS Code Remote-SSH extension and put the direnv hook in the
remote shell’s rc. The remote integrated terminal activates the
devshell on cd, and VS Code’s language servers — which run on the
remote — inherit that environment. The
terraform/
setup is already configured this way.
Switching toolchains
Change the use flake line in .envrc to point at a different
variant. For example:
# CUDA 13.0
use flake .#devShells.torch211-cxx11-cu130-x86_64-linux
# ROCm 7.1
use flake .#devShells.torch211-cxx11-rocm71-x86_64-linux
# XPU
use flake .#devShells.torch211-cxx11-xpu20253-x86_64-linuxRemove .venv/ first if it was created against a different variant,
then reload direnv to recreate it via the new devshell’s shellHook:
$ rm -rf .venv
$ direnv reloadnoarch kernels
For Python-only (noarch) kernels, skip the CMake step in “Generating
IDE-facing project files” and the C++ portions of the VS Code
configuration. The direnv setup and python.defaultInterpreterPath
are all that is needed.