|
| 1 | +# IDE setup with direnv and the kernel devshell |
| 2 | + |
| 3 | +## Introduction |
| 4 | + |
| 5 | +Language servers do not interpret `build.toml`, so IDE completion for |
| 6 | +CUDA, ROCm, framework headers, and the kernel's Python wrapper does not |
| 7 | +work out of the box. This guide shows how to configure VS Code so that |
| 8 | +completion resolves against the same toolchain `kernel-builder` |
| 9 | +uses. |
| 10 | + |
| 11 | +The setup has three pieces: |
| 12 | + |
| 13 | +- `kernel-builder create-pyproject` to emit CMake and setuptools files |
| 14 | + the IDE can read (see [Local Development](./local-dev.md)). |
| 15 | +- The kernel-builder devshell, which provides the toolchain (CUDA, ROCm, |
| 16 | + Torch headers, etc.) from the Nix store. |
| 17 | +- `direnv` to activate the devshell on `cd`, so VS Code inherits the |
| 18 | + environment through the shell. |
| 19 | + |
| 20 | +Pinning the toolchain through Nix keeps IDE completion aligned with |
| 21 | +the build. It also makes switching between CUDA, ROCm, or XPU a |
| 22 | +one-line change in `.envrc`. |
| 23 | + |
| 24 | +## Installing direnv and nix-direnv |
| 25 | + |
| 26 | +On non-NixOS systems, install both via `nix profile`: |
| 27 | + |
| 28 | +```bash |
| 29 | +$ nix profile install nixpkgs#nix-direnv |
| 30 | +``` |
| 31 | + |
| 32 | +Add the direnv hook to your shell rc (`~/.bashrc` or |
| 33 | +`~/.zshrc`, for example): |
| 34 | + |
| 35 | +```bash |
| 36 | +eval "$(direnv hook bash)" # or: direnv hook zsh |
| 37 | +``` |
| 38 | + |
| 39 | +Source the rc file (or open a new shell) so the hook is |
| 40 | +active in the current session: |
| 41 | + |
| 42 | +```bash |
| 43 | +$ source ~/.bashrc # or: source ~/.zshrc |
| 44 | +``` |
| 45 | + |
| 46 | +Wire `nix-direnv` into direnv: |
| 47 | + |
| 48 | +```bash |
| 49 | +$ mkdir -p ~/.config/direnv |
| 50 | +$ echo 'source $HOME/.nix-profile/share/nix-direnv/direnvrc' \ |
| 51 | + >> ~/.config/direnv/direnvrc |
| 52 | +``` |
| 53 | + |
| 54 | +On [NixOS](https://github.com/nix-community/nix-direnv#via-system-configuration-on-nixos) |
| 55 | +or with [home-manager](https://github.com/nix-community/nix-direnv#via-home-manager), |
| 56 | +enable `programs.direnv` with |
| 57 | +`nix-direnv` instead. See |
| 58 | +[`terraform/nixos-configuration.nix`](https://github.com/huggingface/kernels/tree/main/terraform/nixos-configuration.nix) |
| 59 | +for a working example. |
| 60 | + |
| 61 | +## Activating the devshell with direnv |
| 62 | + |
| 63 | +From the kernel root directory (the one containing `flake.nix` and |
| 64 | +`build.toml`), tell direnv to use the flake's default devshell: |
| 65 | + |
| 66 | +```bash |
| 67 | +$ echo 'use flake' > .envrc |
| 68 | +$ direnv allow |
| 69 | +``` |
| 70 | + |
| 71 | +`direnv` now activates the default devshell whenever you `cd` into the |
| 72 | +project. The devshell's `shellHook` creates and activates a `.venv` on |
| 73 | +first entry. Confirm it picked up the toolchain and venv: |
| 74 | + |
| 75 | +```bash |
| 76 | +$ which nvcc |
| 77 | +/nix/store/.../bin/nvcc |
| 78 | +$ ls -ld .venv |
| 79 | +drwxr-xr-x ... .venv |
| 80 | +$ which python |
| 81 | +/path/to/kernel/.venv/bin/python |
| 82 | +``` |
| 83 | + |
| 84 | +If `.venv` is missing, re-run `direnv reload` and check the output for |
| 85 | +the `Creating new venv environment in path: './.venv'` line from the |
| 86 | +`shellHook`. |
| 87 | + |
| 88 | +To pin a non-default build variant, name it explicitly: |
| 89 | + |
| 90 | +```bash |
| 91 | +$ echo 'use flake .#devShells.torch211-cxx11-rocm71-x86_64-linux' > .envrc |
| 92 | +$ direnv allow |
| 93 | +``` |
| 94 | + |
| 95 | +See [Build Variants](./build-variants.md) for the variant list. |
| 96 | + |
| 97 | +## Generating IDE-facing project files |
| 98 | + |
| 99 | +direnv puts the toolchain on `PATH`, but the C++ language server still |
| 100 | +needs a CMake-derived `compile_commands.json` to resolve per-file |
| 101 | +include paths. Generate the CMake/setuptools project and the file: |
| 102 | + |
| 103 | +```bash |
| 104 | +$ kernel-builder create-pyproject -f |
| 105 | +$ cmake -B build-ext -DCMAKE_EXPORT_COMPILE_COMMANDS=ON |
| 106 | +$ ln -sf build-ext/compile_commands.json compile_commands.json |
| 107 | +``` |
| 108 | + |
| 109 | +`-DCMAKE_EXPORT_COMPILE_COMMANDS=ON` is required: the generated CMake |
| 110 | +does not set it. The symlink lets the language server find the file |
| 111 | +at the project root. |
| 112 | + |
| 113 | +As noted in [Local Development](./local-dev.md), do not commit the |
| 114 | +generated files. |
| 115 | + |
| 116 | +## Configuring VS Code |
| 117 | + |
| 118 | +Install the [`mkhl.direnv`](https://github.com/direnv/direnv-vscode) |
| 119 | +extension. It activates the project's `.envrc` when VS Code opens |
| 120 | +the workspace, so language servers and the integrated terminal see |
| 121 | +the devshell environment without launching `code` from a shell. |
| 122 | + |
| 123 | +Alternatively, skip the extension and open the project from a |
| 124 | +direnv-activated shell — VS Code inherits the environment that way |
| 125 | +too: |
| 126 | + |
| 127 | +```bash |
| 128 | +$ cd path/to/kernel |
| 129 | +$ code . |
| 130 | +``` |
| 131 | + |
| 132 | +Install one of the following first-party extensions for C++/CUDA |
| 133 | +completion: |
| 134 | + |
| 135 | +- `llvm-vs-code-extensions.vscode-clangd` (recommended for CUDA). |
| 136 | +- `ms-vscode.cpptools` (Microsoft C/C++). |
| 137 | + |
| 138 | +Add `.vscode/settings.json` (do not commit): |
| 139 | + |
| 140 | +```jsonc |
| 141 | +{ |
| 142 | + "python.defaultInterpreterPath": "${workspaceFolder}/.venv/bin/python", |
| 143 | + |
| 144 | + // clangd |
| 145 | + "clangd.arguments": ["--compile-commands-dir=${workspaceFolder}"], |
| 146 | + |
| 147 | + // Microsoft C/C++ extension |
| 148 | + "C_Cpp.default.compileCommands": "${workspaceFolder}/compile_commands.json" |
| 149 | +} |
| 150 | +``` |
| 151 | + |
| 152 | +Depending on the extension being used, the configuration above behaves |
| 153 | +differently: |
| 154 | + |
| 155 | +- With `clangd`, the `clangd.arguments` line is optional. clangd already |
| 156 | + looks in the parent directories of each source file for |
| 157 | + `compile_commands.json` and will find the workspace-root symlink on its |
| 158 | + own ([clangd docs](https://clangd.llvm.org/installation#project-setup)). |
| 159 | + Setting it explicitly does no harm. |
| 160 | +- With the Microsoft C/C++ extension, the `C_Cpp.default.compileCommands` |
| 161 | + line is required. The extension does not pick up |
| 162 | + `compile_commands.json` from the workspace root on its own, unless |
| 163 | + another extension (such as CMake Tools) tells it where to look. |
| 164 | + |
| 165 | +To verify, open `torch-ext/torch_binding.cpp` and hover an |
| 166 | +`#include <torch/torch.h>` directive. The resolved path should point |
| 167 | +into `/nix/store/...`, not a system path. |
| 168 | + |
| 169 | +## Remote development |
| 170 | + |
| 171 | +Use the VS Code Remote-SSH extension and put the direnv hook in the |
| 172 | +remote shell's rc. The remote integrated terminal activates the |
| 173 | +devshell on `cd`, and VS Code's language servers — which run on the |
| 174 | +remote — inherit that environment. The |
| 175 | +[`terraform/`](https://github.com/huggingface/kernels/tree/main/terraform) |
| 176 | +setup is already configured this way. |
| 177 | + |
| 178 | +## Switching toolchains |
| 179 | + |
| 180 | +Change the `use flake` line in `.envrc` to point at a different |
| 181 | +variant. For example: |
| 182 | + |
| 183 | +```bash |
| 184 | +# CUDA 13.0 |
| 185 | +use flake .#devShells.torch211-cxx11-cu130-x86_64-linux |
| 186 | + |
| 187 | +# ROCm 7.1 |
| 188 | +use flake .#devShells.torch211-cxx11-rocm71-x86_64-linux |
| 189 | + |
| 190 | +# XPU |
| 191 | +use flake .#devShells.torch211-cxx11-xpu20253-x86_64-linux |
| 192 | +``` |
| 193 | + |
| 194 | +Remove `.venv/` first if it was created against a different variant, |
| 195 | +then reload direnv to recreate it via the new devshell's `shellHook`: |
| 196 | + |
| 197 | +```bash |
| 198 | +$ rm -rf .venv |
| 199 | +$ direnv reload |
| 200 | +``` |
| 201 | + |
| 202 | +## noarch kernels |
| 203 | + |
| 204 | +For Python-only (noarch) kernels, skip the CMake step in "Generating |
| 205 | +IDE-facing project files" and the C++ portions of the VS Code |
| 206 | +configuration. The `direnv` setup and `python.defaultInterpreterPath` |
| 207 | +are all that is needed. |
0 commit comments