2025-12-04 1:15 PM
Hi, I am really struggling to get clangd to work correctly in a setup I am trying to get going. I'm new to this VSCode workflow, and am finding VSCode seems to have a lot of persnickety behaviors that really require a lot of specificity to get things working right.
Here is what I am trying to do:
I can get this setup compiling fine, but clangd is complaining about various #include files in my shared code area. I understand that the reason for this is because there is no .clangd file in my shared_code area, so clangd can't "see" it from my project area. Intrepid internet searching has led me to adding this to my code-workspace file:
{
"folders": [
{
"name": "stm32_project",
"path": "."
},
{
"name": "shared_code",
"path": "../shared_code"
}
],
"settings": {
"stm32cube-ide-clangd.arguments": [
"starm-clangd",
"--query-driver=${env:CUBE_BUNDLE_PATH}/gnu-tools-for-stm32/13.3.1+st.9/bin/arm-none-eabi-gcc",
"--query-driver=${env:CUBE_BUNDLE_PATH}/gnu-tools-for-stm32/13.3.1+st.9/bin/arm-none-eabi-g++",
"--compile-commands-dir=${workspaceFolder:stm32_project}/build/Debug"
],
"stm32cube-ide-clangd.path": "cube"
}
}It is my understanding that this *should* work. What I find, though, is that I need instead to either change that --compile-commands-dir to:
"--compile-commands-dir=${workspaceFolder}/build/Debug"OR provide an actual absolute path to my project folder. If I do either of those things, clangd works fine.
However, I notice that if I reorder the folders in the Explorer view like so:
{
"folders": [
{
"name": "shared_code",
"path": "../shared_code"
},
{
"name": "stm32_project",
"path": "."
}
],
"settings": {
"stm32cube-ide-clangd.arguments": [
"starm-clangd",
"--query-driver=${env:CUBE_BUNDLE_PATH}/gnu-tools-for-stm32/13.3.1+st.9/bin/arm-none-eabi-gcc",
"--query-driver=${env:CUBE_BUNDLE_PATH}/gnu-tools-for-stm32/13.3.1+st.9/bin/arm-none-eabi-g++",
"--compile-commands-dir=${workspaceFolder}/build/Debug"
],
"stm32cube-ide-clangd.path": "cube"
}
}clangd breaks again.
It's my understanding that the way around this is to use the "workspaceFolder:stm32_project" syntax, to be specific about what folder my compile_commands.json file lives in.
Further, if I try to save my workspace alongside my two folders - rather than inside my project folder - that the ${workspaceFolder} syntax doesn't seem to work at all (it is not obvious to me how to check the value of what this 'variable' is resolving to, so I'm not sure how to troubleshoot this).
// This seems to work, sort of
my_shared_code/
my_project/
my_workspace.workspace
// I can't get this to work (and it's more annoying to have the
// workspace file outside of the project anyway)
my_shared_code/
my_project/
my_workspace.workspaceAll of this makes me feel as though I am using vscode "wrong", and certainly makes it clear that the order of things in the Explorer pane and the location of workspace files aren't as arbitrary as I presumed they were.
At this point, I feel compelled to ask some questions:
Solved! Go to Solution.
2025-12-11 6:22 PM - edited 2025-12-11 6:23 PM
Hm, when I download this, and open the setup, and move the my_shared_code folder above the my_stm32f4_project, I still see errors in the my_driver.c file:
However, I'd like to focus on this:
This setup that you are trying to build is a bit exotic to say the least.
It's not my intent to deliberately build something exotic or difficult - I'm a hobbyist who's spent a few years coming up to speed with the stm32 ecosystem (coming from an arduino context), and learning mostly through cubeIDE. I have an f4 development board I made myself, and a few breakout boards that I've made as well, and a handful of projects that are all coded to work with this board ("project", in this context, is a cubeMX-generated stm32 project).
I often want to reuse things like drivers and constants and my own math libraries and such in these projects. It seemed like duplicating this code into each project wasn't so efficient, so I found that if I created a centralized folder that contained them, I was able to configure my "project settings" in cubeIDE such that I could bring them in and they would compile fine. I didn't realize I was doing something highly unorthodox by working this way, and I suppose it is to great credit to cubeIDE that I was apparently able to do this with very little complication and have it all "just work" for me.
I'm now trying to adapt to vscode, and I'm fully aware that this carries with it the need to learn new workflows. But...I'm inexperienced with vscode beyond just using it as a text editor, and I am not a highly-opinionated, mature, or super-experienced embedded software engineer, so I don't really know what I don't know here, or what constitutes "exotic" in terms of how I organize my code. Cmake is new to me, Clangd is new to me, vscode with this toolchain stuff is new to me. It's all new to me, and I'm just trying to come up to speed with it.
I wouldn't say that my opinions or needs are hardened enough to demand that ST's vscode development effort should bend to my setup, but I WOULD really love to know how I might better do this, or how ST imagines I SHOULD be doing it, or how others might do this differently. I'm very open to learning that. I'm eager, I just don't know where to look.
2025-12-11 10:55 PM
To begin with, let me apologize for any potential harshness of tone in my previous reply. I did not mean that there's anything inherently wrong in your setup, I just found it a bit strange at first. From a software architecture perspective there are two kinds of "shared libraries" in the very broadest sense:
An example of the first one would be the STM32 USB Device Middleware library, or even a general-purpose math library. The STM32 USB library defines things like "USB_OK", using them instead of "HAL_OK" (and thus taking a dependency on a specific HAL library which provides these definitions). In this sense, it is "independent" from the HAL, and could theoretically be used with completely different, non-STM32 devices as well. It exports several "glue functions" that the consumer is expected to call in order to inform the library about specific events happening on the USB side. The "tinyusb" project is a precisely similar construct. There's a lot of "general purpose" code in that library, and then a thin wrapper which makes it "device-specific".
An example of the second would be how CubeMX generates the "cmake/stm32cubemx/CMakeLists.txt" file in any given STM32 project. This library is not truly independent; it actually presets the consumer to use a specific STM32F4xx variant without allowing any deviation (this happens with the "MX_Defines_Syms" section, near the top of the file). It also directly includes the HAL header files and even the source files. The consumer of this library gets a very tailored set of functionality from the HAL.
The "exotic" portion comes from the way your files were organized in the example archive. The shared library code referred to the HAL library without actually enforcing the use of any specific STM32F4xx variant. At the same time, "my_driver.c" still wanted to import a key header file from the library. This was a bit backwards because in order to use STM32F4xx HAL, you must define the exact chip that you want to use, very much like the "MX_Defines_Syms" section does.
Now that you've explained how you want to use the shared driver code I believe what you want is the second option. In short, you'd like to share code that's specific to an exact STM32F4xx variant, and only usable in projects that use this specific chip. Assuming this is the case, then later today I will try to adapt the example I provided to be more along these lines, so that you can see the difference in practise.
As for the error you're experiencing now, could you provide the error message that appears when you hover over the red squiggles? Also, did you run CMake's "configure" for both projects? I cannot see the "build" folders that are generated as part of this process in your screenshot.
To run CMake's configure for both projects open "my_driver.c", the CMake's status window, and then re-configure the project. Refer to my screenshot for an example (although in here I have just opened a random file in my own project).
Do the same for "sysmem.c" in the other project. Note how the "folder" in the CMake status window should change as a result. If it does not change then a setting which allows this to happen is off somewhere in your user side preferences file. If so, just click the edit icon next to the first row under "Folder" (hover over to see it) and choose the other folder.
If you manage to configure both projects, do the red squiggles go away?
2025-12-12 4:42 AM
What a wonderfully empathetic, thoughtful, helpful, insightful, patient response! I'll start with "Thank you so very much!" but even that falls a little short of fully conveying my gratitude for you taking the time to explain some of this to me. I deeply appreciate it so much!
To your specific advice about this test setup we're working in, ah yes! I did not know I needed to "reconfigure" the cmake setup (and, indeed, hadn't noticed that little icon you pointed out, nor knew what it did). When I poke it, I indeed see the red squiggles (the error of which previously said "my_constants.h not found") went away and things behave as I'd expect. Thank you!
I'm finding there to be quite a lot of these little gotchas when switching to VScode. So far I've stubbed my toe on:
Now I can add to the list: knowing to pay closer attention to this context-switching thing that Cmake is doing, and understanding when it needs to be reconfigured (and how to do that).
It's all good stuff to learn, and I'm happy to do it. And I also understand that we tends to learn all these little things from doing, and that they're hard to document in a clean way. Part of building experience with the tool.
All this is to say: thank you for teaching me some of the little things! They're pretty big things to me at this phase!
2025-12-12 1:03 PM
I didn't manage to complete the alternative example but I will answer your questions quickly
@sb_st wrote:I'm still a little hazy on what "multiroot" means in this context (in this test setup, does "multiroot" refer to the fact that we've manually brought two folders into our 'single project' workspace?). I think I am still wrong in my understanding of this perhaps.
The VS Code workspace concept, and its extension "multi-root workspace" is explained in detail here: https://code.visualstudio.com/docs/editing/workspaces/workspaces
The "multi-root" is best summarized as "independent projects in the same VS Code window". In this context here "my_stm32f4_project" is one independent project, and "my_shared_code" is another. Before I made changes "my_shared_code" was missing all the plumbing (CMakeLists, links between CMakePresets etc.) required to make it build, and thus you had problems with it. From VS Code's perspective you had some source and header files but no instructions whatsoever what to do with them.
@sb_st wrote:Understanding the difference between compilation errors and "red squiggles", and that these two types of errors are coming from two different places. The "Problems" pane in VSCode was confusing to me, in that I could get my code to compile fine, but was seeing many "Problems" listed in that pane (which I've learned were coming from clangd, not the compiler).
This is a very common point of confusion with VS Code. It is designed to work with all languages and all project types. The "Problems" pane shows problems which are currently "visible" or "known", for example, from the previous compilation cycles or because you have a file open in the editor which has a problem.
@sb_st wrote:Being mindful that the C/Cpp Microsoft extension (which vscode hounds me about/"recommends") and clangd apparently offer two ways to - I'm not sure what to call it - parse one's code to understand function definitions and the like, and that these two can compete with one another. I learned that it's helpful to create a dedicated VSCode Profile, and to only install the STM32Cube VScode extension in that profile, to help isolate what engine is causing the red squiggles when I see them.
They are called "language servers" in VS Code world. These servers register themselves to handle specific files (file type is usually determined from the extension but this is not always the case), and when such a file is opened, the file is given to the language server for analysis. The language server speaks "language server protocol" or LSP and through this protocol tells VS Code what the file contains. It does things like identifies keywords of a language, distinguishes a variable name from its type and so on.
To understand the different facets of this I recommend this link https://code.visualstudio.com/api/language-extensions/overview.
@sb_st wrote:Whether or not to structure my shared code "things" as "Cmake Libraries" - I read up on this and then tried doing it, but it wasn't obvious to me what the gains were for my situation, and it did not seem to resolve this issue that brought me here, so I went back to following the pattern I see in the CMakeLists.txt file that cubeMX generates, which just seems to rely on providing paths directly to files I want to include in compilation. Maybe this was a boondoggle, but at least I learned how to do it.
CMake is a bit complex in some parts, but the "Mastering CMake" is a very good thing to start with https://cmake.org/cmake/help/book/mastering-cmake/index.html. Especially https://cmake.org/cmake/help/book/mastering-cmake/chapter/Key%20Concepts.html
2025-12-13 5:35 AM
@ankes wrote:I didn't manage to complete the alternative example but I will answer your questions quickly
You've been helpful far beyond any measure of reasonable expectation, so I don't feel it right for you to go do more work on providing examples - your explanations have been a fantastic toehold for me to go figure this out on my own with a much better perspective now. Thank you so much!
In this context here "my_stm32f4_project" is one independent project, and "my_shared_code" is another.
THANK YOU - this is a critical thing that I have misunderstood. My mental model of what I'm doing here in my setup isn't that I have "two projects", but rather "one project, with some supporting code over there in another spot". Understanding that I need to think of this shared code area as a project in and of itself (as far as cmake is concerned) is super helpful.
It is also helpful to understand the context-switching thing that's happening here - I often have multiple code panels open, looking at code from the shared_code area simultaneously as that of my in-project code. I guess CubeIDE 'hid' this context switching, and I have gotten used to just mashing the Build button or Debug button in CubeIDE without needing to care about which code panel is active or where my cursor is. But I see that this VSCode workflow requires I be a little more thoughtful about this, if I wish to work in this funky way I've set up for myself.
In any case, I wouldn't like to trouble you further, but thank you for all these thorough explanations, and for being an awesome, patient human in general. It's deeply appreciated.
2025-12-15 11:29 AM
You're welcome.
I did take a look into moving HAL to the shared folder but unfortunately it failed in the end. The problem is that the HAL library requires a define statement which chooses the exact chip, and this define must be present when the sources are compiled.
This means that either the CMake "shared library" that compiles HAL sources chooses it -- which then makes the "my_shared_code" entirely specific to this chip, and prevents using it in other chips of the same family -- or alternatively all code in the shared side uses CMake's INTERFACE keyword so that the chip can be chosen in the project side which uses it. However, this second approach results in no compilation, no compile_commands.json and thus no clangd support when looking at the source files living inside "my_shared_code".
Remember to mark one of the answers as "accepted answer" so that this thread gets closed.