From 693e232957ab19556d61a04b3995cae433f43930 Mon Sep 17 00:00:00 2001 From: khodowany Date: Fri, 11 Sep 2026 00:02:36 -0500 Subject: [PATCH] Clangd --- .gitignore | 1 + README.md | 4 ++++ modulus.json | 8 ++++++++ modulus.py | 23 ++++++++--------------- src/vamp_zero/app.cpp | 1 - tests/test_build_bootstrap.py | 24 ++++++++++++------------ 6 files changed, 33 insertions(+), 28 deletions(-) diff --git a/.gitignore b/.gitignore index fe95204..93b1147 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,6 @@ build/ .clangd +.nvim.lua .clang-format pyrightconfig.json diff --git a/README.md b/README.md index 973030b..d299a48 100644 --- a/README.md +++ b/README.md @@ -41,6 +41,10 @@ When running directly outside the generated Modulus Docker image, set `PICO_SDK_ Editor settings live in `modulus.json` under `tooling.editor`. Modulus generates the ignored `.clangd`, `.clang-format`, and `pyrightconfig.json` files; edit the JSON settings rather than these outputs. The clangd configuration routes Zero/shared code to the native compile database and Pico code to its firmware database. +Modulus also generates `.nvim.lua` for Neovim 0.11+ with `exrc` enabled. Review and trust it when Neovim asks; it preserves the existing LSP capabilities/keybindings and runs clangd 19 in the build container for this repo. Run `python3 modulus.py lsp` once to prepare both databases and the shared read-only header cache. Other editors can use `python3 modulus.py clangd` as their language-server command. Startup never downloads or builds anything, and never falls back to host clangd. + +`tooling.lsp.clangd` configures the server executable, arguments, trusted compiler paths, and additional header roots. Product Vamp adds the bare-metal ARM compiler and `/opt/pico-sdk`; standard-library and SDK definitions open from an image-keyed cache under `$XDG_CACHE_HOME/modulus/clangd` (default `~/.cache/modulus/clangd`). Set `tooling.editor.nvim.enabled` to `false` for a hand-managed editor setup. Existing unowned `.nvim.lua` files are preserved. + ## Package Product packaging targets Raspberry Pi OS/Debian trixie on ARM64 and optionally builds Pico firmware. Set `PICO_SDK_PATH` when building the firmware artifact: diff --git a/modulus.json b/modulus.json index 61af100..2961ee7 100644 --- a/modulus.json +++ b/modulus.json @@ -52,6 +52,14 @@ }, "lsp": { "enabled": true, + "clangd": { + "query_drivers": [ + "/usr/bin/gcc-12", "/usr/bin/g++-12", + "/usr/bin/aarch64-linux-gnu-gcc-12", "/usr/bin/aarch64-linux-gnu-g++-12", + "/usr/bin/arm-none-eabi-gcc", "/usr/bin/arm-none-eabi-g++" + ], + "header_roots": ["/opt/pico-sdk"] + }, "compile_databases": [ { "name": "pico", diff --git a/modulus.py b/modulus.py index 015b0fb..232bbf3 100755 --- a/modulus.py +++ b/modulus.py @@ -19,6 +19,7 @@ COMMANDS = ( "test", "package", "lsp", + "clangd", "run", ) COMMON_DIR = pathlib.Path("build") / "build_common" @@ -60,6 +61,7 @@ def runtime_ready(root): return ( (root / TOOLS_DIR / "configure.py").is_file() and (root / TOOLS_DIR / "lsp.py").is_file() + and (root / TOOLS_DIR / "clangd.py").is_file() and (root / TOOLS_DIR / "run.py").is_file() and (root / RUNTIME_DIR / "modulus" / "cli.py").is_file() and (root / RUNTIME_DIR / "modulus" / "export.py").is_file() @@ -485,22 +487,14 @@ def run_package(argv): def delegate(command, argv): root = repo_root() - ensure_runtime(root) + if command == "clangd": + if not runtime_ready(root): + print("Modulus clangd tooling is missing; run ./modulus.py configure first", file=sys.stderr) + return 1 + else: + ensure_runtime(root) sys.path.insert(0, str(root / RUNTIME_DIR)) import modulus.cli - original_image_available = modulus.cli.docker_image_available - - def current_build_image(context): - if not original_image_available(context): - return False - inspected = subprocess.run( - ["docker", "image", "inspect", context.docker_image, "--format", - '{{index .Config.Labels "org.khodo.product-vamp.build-requirements"}}'], - capture_output=True, text=True, check=False, - ) - return inspected.returncode == 0 and inspected.stdout.strip() == build_requirements_digest() - - modulus.cli.docker_image_available = current_build_image original_docker_run = modulus.cli.docker_run def docker_run_with_host_socket(context, command, docker_args=None, env_overrides=None): @@ -521,7 +515,6 @@ def delegate(command, argv): try: return modulus.cli.main(command) finally: - modulus.cli.docker_image_available = original_image_available modulus.cli.docker_run = original_docker_run os.chdir(previous_cwd) sys.argv = previous_argv diff --git a/src/vamp_zero/app.cpp b/src/vamp_zero/app.cpp index 3ec1781..79edaac 100644 --- a/src/vamp_zero/app.cpp +++ b/src/vamp_zero/app.cpp @@ -9,7 +9,6 @@ #include #include #include -#include #include #include #include diff --git a/tests/test_build_bootstrap.py b/tests/test_build_bootstrap.py index a00e3ac..689603b 100644 --- a/tests/test_build_bootstrap.py +++ b/tests/test_build_bootstrap.py @@ -1,7 +1,6 @@ import hashlib import importlib.util from pathlib import Path -import subprocess import sys import types import unittest @@ -21,14 +20,15 @@ class BuildBootstrapTests(unittest.TestCase): package = types.ModuleType('modulus') cli = types.ModuleType('modulus.cli') package.cli = cli - available, docker_run = Mock(return_value=True), Mock() - cli.docker_image_available, cli.docker_run = available, docker_run - cli.main = lambda command: cli.docker_image_available(types.SimpleNamespace(docker_image='test-image')) - for label, expected in [('',False), ('outdated',False), (digest,True)]: - result = subprocess.CompletedProcess([], 0, label+'\n', '') - with self.subTest(label=label), patch.dict(sys.modules, {'modulus':package, 'modulus.cli':cli}), \ - patch.object(bootstrap, 'ensure_runtime'), patch.object(bootstrap.subprocess, 'run', return_value=result), \ - patch.object(sys, 'path', list(sys.path)): - self.assertEqual(bootstrap.delegate('configure', []), expected) - self.assertIs(cli.docker_image_available, available) - self.assertIs(cli.docker_run, docker_run) + docker_run = Mock() + cli.docker_run = docker_run + cli.main = Mock(return_value=0) + with patch.dict(sys.modules, {'modulus':package, 'modulus.cli':cli}), \ + patch.object(bootstrap, 'ensure_runtime') as prepare, \ + patch.object(bootstrap, 'runtime_ready', return_value=True), \ + patch.object(sys, 'path', list(sys.path)): + self.assertEqual(bootstrap.delegate('clangd', []), 0) + prepare.assert_not_called() + docker_run.assert_not_called() + cli.main.assert_called_once_with('clangd') + self.assertIs(cli.docker_run, docker_run)