cancel
Showing results for 
Search instead for 
Did you mean: 

getting clangd to work in a multiroot project

sb_st
Senior

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 start with a fresh workspace, and ONLY the STM32Cube vscode extension pack installed. 
  • I add two folders to my workspace:
    • an stm32 project folder, as created by cubeMX
    • a shared_code folder, which contains multiple subfolders, each of which contain .c/.h files. Nothing particularly fancy here. 

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.workspace

All 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:

  • Am I approaching this the right way?
  • Where does ST expect VScode workspace files to be saved?
  • Why doesn't ${workspaceFolder:stm32_project} work in the above example?

 

1 ACCEPTED SOLUTION

Accepted Solutions
ankes
Associate III

The virus scan of your attachment completed and I managed to obtain a copy. After extracting and creating a workspace on the two folders I can reproduce your problem, and I believe I have an idea how to go about it.

When you use a multi-root workspace (that's the official term btw) then CMake Tools "reconfigures" itself based on the file you have open in the editor, and the location of that file in the overall scheme of things.

As an example, if I open the "sysmem.c" file and look at the CMake tab, I can see this:

ankes_0-1765480174902.png

If I swap to "my_driver.c" the extension's state changes accordingly:

ankes_1-1765480219172.png

As you can see, the "configure" section of the second file shows "__unspec__" in the selected kit row which indicates that CMake is in an "unconfigured" state. This alreay hints us at what's going awry: the second workspace root ("my_shared_code") does not contain any CMakeLists.txt or CMakePresets.json files. In short, CMake doesn't have any work to do.

This setup that you are trying to build is a bit exotic to say the least. It looks like you want to build some kind of a library from the shared code but what kind, exactly? If it is an ordinary static library then it needs the headers from CMSIS and HAL to compile. If it is a dynamic library then it will also need the source files from HAL. You can also create a CMake "interface library" like the CubeMX does for HAL but that's a bit outside my forte so I can't help you there.

However, I take a stab at guessing that you want a static library. To facilitate this I have patched through the example you provided and added the necessary glue bits. Extract the files to a directory and diff the directories to get the big picture, but here are the key pointers:

  • Upgraded CMake version requirement to 4.0.1 (STM32 extension provides this so it's no problem)
  • CMakeLists.txt in the "my_shared_code" defines a static library that is then imported in "my_stm32f4_project"
  • CMakePresets.json (version 10) in the "my_shared_code" links to the actual file in "my_stm32f4_project" side
  • In "my_stm32f4_project" side, the CMakePresets.json is modified to use "fileDir" in the toolchain path; this allows the include statement to find the right file, instead of looking inside "my_shared_code"

You can take the archive, and turn the workspace folders in whichever order you want. Everything still works.

View solution in original post

15 REPLIES 15
Cartu38 OpenDev
Lead III

@sb_st 
Works for me or am I missing something specific you're trying to achieve ?

Cartu38OpenDev_0-1764915215469.png

What is key is compile_commands.json file yes this one is driving fully clangd. If your project complie fine this file should be fine too so all should work as a charm.

Have you any CMake project as part of your shared folder ... if so another story please confirm if so.

Cartu38 OpenDev
Lead III

Works the very same if relocating somewhere else ...

Cartu38OpenDev_0-1764915585091.png

 

sb_st
Senior

Hm. I am not sure what I'm doing differently, but this is definitely not my experience - none of this is "working like a charm". 

I have created an example project structure here that exemplifies this problem - I am attaching a zip file of it here. 

Here shows the structure of what I'm trying to do:

broken_1.png

 So you can see my directory structure (which differs slightly from your example - I am using a series of nested subfolders in my shared code area). We can see that this project compiles fine - so cmake itself appears to be working. 

(note: I have tried structuring the subfolders in my shared code area as separate Cmake libraries. So, each component gets its own CMakeLists.txt file. This produces some complaints from the ST extension, but it compiles fine. It does not solve my issue with clangd, however). 

So, I try saving my workspace, and adding this line to my workspace file:

compile_commands_using_specific_workspace.png

 It's my understanding that the above SHOULD work, but it does not. If I modify it slightly though, we can see it seems to make clangd happier:

this_works.png

 However, if I reorder my folders in the Explorer pane, it breaks again:

broken_again.png

I agree that all this *seems* like it looks much like your example. However, it sure doesn't seem to be working, and I can't understand what I'm doing wrong. 

@sb_st 

I've pain to get back your attached material  Cartu38OpenDev_1-1764955249800.png

But anyway from my project trying to mimic your snapshots I'm getting


Cartu38OpenDev_0-1764955235073.png
So works the same ok

Maybe this is not ok to you ?

Cartu38OpenDev_2-1764955374386.png

 

 

Cartu38 OpenDev
Lead III

@sb_st 
I would be agree about "if nok to you" in real ...
I guess I've understood what issue is ... have to find proper solution then.

Moving to exact same as you my indexign experience is bad too and issue is reported Thanks clangd output channel trace:

Cartu38OpenDev_3-1764956910881.png

Means because editing a file as part of shared area, clangd integration because multi root workspace is doing kind of project auto switch. No compile database is part of this shared project root so the stuff is blind not being able to index.

Maybe adding some CMake material as part of shared project is the key. Possibly https://cmake.org/cmake/help/latest/module/ExternalProject.html is helpful.

Thanks @Cartu38 OpenDev, yeah your solution of using:

#include "../params/myAdderParam.h"

instead of:

#include "myAdderParam.h"

seems like more of a workaround rather than a 'real' solution (though I certainly appreciate you digging in to help me out with this!)

It does seem that this issue might be related to header-only shared code files. I don't seem to have problems with components in my shared code area that have .c files, because those become proper build targets, which seems like what clangd wants to see?

@sb_st 
Source files (.c) are working fine because explicitly pointed as part of your project CmakeLists.txt. Header files (.h) are not ... reason why.
In real from clangd perspective none of them are processed right because project context switch ...

I understand. 

I guess it's more-specifically correct to say that .c files work fine because they appear in the compile_commands.json file that Cmake produces...as I understand it, this compile_commands.json is what clangd is using to 'understand' things.

ChatGPT is telling me that one 'solution' here is to have a .clangd file in the top-level directory, 'above' both the project and shared_code folders. But this requires specifically pointing the shared_code area to the project, which sort of defeats the purpose of it being a shared_code area. 

This is how I came to learn about adding this line to my workspace file:

"--compile-commands-dir=${workspaceFolder}/build/Debug"

https://clangd.llvm.org/faq#how-do-i-fix-errors-i-get-when-opening-headers-outside-of-my-project-directory

which only seems to halfway work.

ankes
Associate III

The virus scan of your attachment completed and I managed to obtain a copy. After extracting and creating a workspace on the two folders I can reproduce your problem, and I believe I have an idea how to go about it.

When you use a multi-root workspace (that's the official term btw) then CMake Tools "reconfigures" itself based on the file you have open in the editor, and the location of that file in the overall scheme of things.

As an example, if I open the "sysmem.c" file and look at the CMake tab, I can see this:

ankes_0-1765480174902.png

If I swap to "my_driver.c" the extension's state changes accordingly:

ankes_1-1765480219172.png

As you can see, the "configure" section of the second file shows "__unspec__" in the selected kit row which indicates that CMake is in an "unconfigured" state. This alreay hints us at what's going awry: the second workspace root ("my_shared_code") does not contain any CMakeLists.txt or CMakePresets.json files. In short, CMake doesn't have any work to do.

This setup that you are trying to build is a bit exotic to say the least. It looks like you want to build some kind of a library from the shared code but what kind, exactly? If it is an ordinary static library then it needs the headers from CMSIS and HAL to compile. If it is a dynamic library then it will also need the source files from HAL. You can also create a CMake "interface library" like the CubeMX does for HAL but that's a bit outside my forte so I can't help you there.

However, I take a stab at guessing that you want a static library. To facilitate this I have patched through the example you provided and added the necessary glue bits. Extract the files to a directory and diff the directories to get the big picture, but here are the key pointers:

  • Upgraded CMake version requirement to 4.0.1 (STM32 extension provides this so it's no problem)
  • CMakeLists.txt in the "my_shared_code" defines a static library that is then imported in "my_stm32f4_project"
  • CMakePresets.json (version 10) in the "my_shared_code" links to the actual file in "my_stm32f4_project" side
  • In "my_stm32f4_project" side, the CMakePresets.json is modified to use "fileDir" in the toolchain path; this allows the include statement to find the right file, instead of looking inside "my_shared_code"

You can take the archive, and turn the workspace folders in whichever order you want. Everything still works.