Download

Emacs, Clangd and Unreal Engine

I am an emacs user and am trying to get emacs and clangd work with Unreal Engine Code. I have used UBT to generate the compile-database with the argument -mode=GenerateClangDatabase.

I run emacs and clangd in Windows Subsystem for Linux (WSL). Though clangd could parse the compile-database file correctly, it seems that it still need something else to compile the code. In the log of clangd I could find something like this:

I[22:35:10.483] Indexed /mnt/d/workplace/UnrealEngine/Engine/Source/Runtime/RenderCore/Private/RayTracingInstanceCopyShader.cpp (23607 symbols, 77274 refs, 469 files)
I[22:35:10.490] Failed to compile /mnt/d/workplace/UnrealEngine/Engine/Source/Runtime/RenderCore/Private/RayTracingInstanceCopyShader.cpp, index may be incomplete

As a result, I cannot take advantage of the full functionality clangd provides. It can’t find definitions of some symbol. To be more specific, clangd could not find definitions for most of the symbols in a header file. It works for symbols in some simple structs at the beginning of the header. However, it failed to find definitions for everything else in the file. For example, in the file PlayerCameraManager.h, clangd could find definitions for the struct FMinimalViewInfo, but it could not find definition for AActor which appears as the base class of APlayerCameraManager.

I am not sure this is a problem of the compile-database file which is produced by UBT, or it is a problem of clangd. Since we can already compile Unreal Engine using Clang, I suppose clangd could also parse Unreal Engine source code. I hope someone in the Unreal community could help me with that since I have no knowledge about programming language, compiler and Language Server Protocol (LSP), but I really want to combine these powerful tools: emacs, LSP, clangd and Unreal Engine.

1 Like

I dig into the code and found more useful information.

Include Directories

First the command I used to generate the compile database is

 UnrealBuildTool.exe -mode=GenerateClangDatabase -project="YourProject.uproject" -game -engine ProjectName Development Win64

Then I checked some of the error messages clangd prompted. First, it said it can not find the include file sal.h. After some simple research I found that this file is a Windows exclusive file and does not exist in WSL. This is reasonable since the command specified that the target platform is Win64. I came up two methods to solve this problem.

Switch to Another Platform

First, I could change the UBT command to specify other platform such as Clang or Linux. Here is the output:

PS D:\workplace\UnrealEngine\Engine\Binaries\DotNET> .\UnrealBuildTool.exe -mode=GenerateClangDatabase -project="D:\workplace\LearnUnreal\LearnUnreal.uproject" -game -engine LearnUnreal Development Clang
ERROR: No platforms specified for target
PS D:\workplace\UnrealEngine\Engine\Binaries\DotNET> .\UnrealBuildTool.exe -mode=GenerateClangDatabase -project="D:\workplace\LearnUnreal\LearnUnreal.uproject" -game -engine LearnUnreal Development Linux
ERROR: Platform Linux is not a valid platform to build. Check that the SDK is installed properly.

I am not familiar with UBT and am still reading these documents, so for now I do not know how to proceed in this way.

Add Include Directories

The other method is to either configure UBT to include the Windows specific include directories in the output compile database or we add them to the compile database manually. Again, I do not know how to configure UBT to include those additional include directories. Although I could add them manually myself, it looks much like a hack not a formal solution.

Macro Substitution

Then I found that Cland has difficulties in expanding Unreal Engine macros. For example, UCLASS is expanded to

BODY_MACRO_COMBINE(CURRENT_FILE_ID,_,__LINE__,_PROLOG)

The macro is defined as

#if UE_BUILD_DOCS || defined(__INTELLISENSE__ )
#define UCLASS(...)
#else
#define UCLASS(...) BODY_MACRO_COMBINE(CURRENT_FILE_ID,_,__LINE__,_PROLOG)
#endif

Microsoft Visual Studio could expand this macro correctly because the flag __INTELLISENSE__ is defined. However, this is not true for the compile database generated for Clang. Again, we could manually add the definition to the command database, but I prefer more general solution.

Another macro is GENERATED_BODY, it is defined as:

#define GENERATED_BODY(...) BODY_MACRO_COMBINE(CURRENT_FILE_ID,_,__LINE__,_GENERATED_BODY);

Clangd complains this with C++ requires a type specifier for all declarations. I do not know how MSVS deals with this, but it is obvious these macros are preventing Clangd from compiling the code. Thus it can not correctly find definitions for all the symbols.

I hope there will be some one who can help with this.

Hi there!

You can try a project of mine which consists of two parts:

  1. Unreal Editor plug-in adds Emacs to the “Source Code Editor” combo box and makes Emacs a default editor for a project source files. This means Unreal Editor will open Emacs instead of Xcode/VisualStudio/Whatever.
  2. A patch for the UnrealBuildTool creates a Clang compilation database in the project’s root directory. The database could be used by lsp, rtags and any other Emacs autocompletion backend. You must have Emacs selected as a source code editor for the project to make it work. The Unreal Editor will invoke the plug-in’s magic each time it wants to refresh the project.

As I said, you have to set up an autocompletion backend on your own. The plug-in doesn’t do that for you. Personally, I use lsp. You can look into my Emacs configuration directory for an inspiration on how to configure it. The main points of interest, I believe, are:
init-company-mode.el
init-cpp.el
init-snippets.el

Beware, both the plug-in and patch “work on my computer” under macOS, but I believe they should work on GNU/Linux and Windows. The project has a README.md which explains how to install and use it.

Let me know if this helps.

Thanks for your help. I will check that later. Actually, I found a way to make my setup work, but only for a while. I defined the __INTELLISENSE__ macro for clangd. This makes those UCLASS expand to nothing which finally makes clangd could parse the source code.

However, the UBT generated code is not included in the compilation database thus it is invisible to clangd. Unfortunately, there are some access modifiers generated in those .generated.h files which makes clangd “incorrectly” recognize the accessibility of some members. While, in the perspective of clangd, it works well, but it just not play nice with Unreal code under the current compilation database setting.

Further more, when clangd is compiling in the background, I found it frequently crashed when it encountered some particular file. I do not know which file makes it crash yet.

To some extend, I start believing that it is almost impossible to make clangd works with Unreal code, because Unreal code is NOT standard C++ and clangd is designed for standard C++.

Anyway, thanks for your reply. I will try your setup and see if it could work for me.

Cheer up! :slight_smile: I believe it is possible. There is no non-standard C++ once Unreal Header Tool is run on the project.

I also found that the existing compilation database generation in UBT uses “Intellisense” mode which doesn’t work well on macOS. What I do instead, I generate the database using the same environment UBT uses to build the project.

Thanks, I some how make it work. Although I did not use your project directly, but I got a lot of inspiration from it. Basically, what I was trying to do was to compiling Unreal Code using a Linux version clang in WSL, which is difficult to achieve since it at least require I set up the cross-compiling for the project.

After I switch to the Windows native Emacs and Clangd, everything works fine. During this process, I also have learned more about how these macro works actually with the help of UHT.

However, there is still another problem bothering me. The thing is when I edit some content of a header file, to be specific, when I make some edit which changes the line number of the GENERATED_BODY macro, the header file is broken. This is due to the #define in the .generated.h file can’t match the GENERATED_BODY after the line number has changed. Before the next time I run the UHT, those error messages clangd send to me persist.

The only way I know how to run the UHT is through the IDE’s building process, which it automatically run UHT and generate the headers. If I could find out how to run UHT manually, then I can update my generated header files without building the project after I made some simple changes to my header file. Sometimes, those changes are too naive to issue another build, e.g., including another header file or forward declaring a class name, etc…

I have tried Jet Brain’s Rider for Unreal and it works that way, I mean, automatically running UHT in background to update generated header file to make everything in the same page.

Unreal document talks really little about how to execute UHT manually, if you know how to do that, please tell me, that would be a really great help.

P.S. Clangd is somehow slow to reflect changes in header files, even UHT is not involved.

Running UHT manually is complicated because UBT generates a special manifest file for UHT with build modules metadata and who-knows-what-else.

However, you can do this instead:

bash "/Users/Shared/Epic Games/UE_4.27/Engine/Build/BatchFiles/Mac/Build.sh" MyProjectNameEditor Mac DebugGame -SkipBuild -project=/Path/To/MyProjectName.uproject

The -SkipBuild flag instructs UBT to generate makefiles, run UHT if needed and stop. And this is fast :slight_smile:

Despite not being targeted exactly at Emacs, I think this video will give you most information you need:

I also can not recommend this channel enough:

This guy requires way more attention than he gets. His videos are so amazing, high effort content.

This helps a lot, now I can generate those .generated.h files without building my whole project every time I just make a tiny editing to my header files. Now I am pretty satisfied with my programming environment using Emacs, Clangd and Unreal Engine.

Thank you a lot!

Thanks, I’ll take a look later.