Fast incremental C++ builds (UnrealBuildTool makefiles)

We’re working on some changes to make compiling your C++ game fast. This post is about one of the changes we’re testing now for the upcoming 4.8 release. For those of you brave enough to use the latest master branch code, you may have noticed that UnrealBuildTool will startup very quickly – even when working with huge game projects and the full engine source code. Here is a quick overview of this new feature. Would love to your hear feedback!

TL;DR version

UnrealBuildTool now starts up fast and checks dependencies very quickly. After the first time building a target, compiling a change should begin almost instantly. The first time you trigger a build, we create a “Makefile” with everything needed to compile that target. It will reuse that makefile in subsequent runs. There is some new console output that shows you what’s going on.

!! IMPORTANT !! When you add new C++ source files, you now must update your Visual Studio project files! Either add the new file to the project yourself using the IDE, or just regenerate all project files. This is a C++ workflow change, as previously UBT would be able to detect added or removed source files automatically. Similarly, if you change a *Build.cs or *Target.cs file, or modify your global BuildConfiguration settings, you must refresh project files manually before compiling again! We’re still investigating whether we can make this automatic in a future version.

If you have any problems, you can revert to the old system by turning off bUseUBTMakefiles in your BuildConfiguration.

Long version below

So I’ve been on a crusade to make it fast for programmers to make small C++ changes quickly, even in big projects. There are many challenges, but one of the most obvious was that it could take UnrealBuildTool up to 15 seconds to figure out whether it even need to compile anything!

There wasn’t a lot of low hanging fruit with what UBT was doing to prepare a project for building, so we decided to cache everything off into a Makefile so it could be used over and over again as you work. The first time you compile a target, we create a Makefile for it. This is called the “gather” step. In subsequent builds for that target, we load the makefile, check for outdated source files, run UnrealHeaderTool (if needed), then kick off the compiler. This is the “assembling” step. The assembling step is really stripped down – it just GOES.

UBT still quickly scans thousands of source files every time you build, but we do it very efficiently by caching off a flat list of resolved file paths for any given translation unit. C++ dependency scanning has been rewritten and now takes almost no time at all. It no longer has to load source file contents each time you build, it only checks timestamps. I also borrowed an idea from the excellent Ninja build system: We no longer spider through source files to update our include graph unless a translation unit was already out of date. Think about it – unless a source file itself was modified, it can never become outdated by includes other than the ones we already knew about. So on your very first build of a target we scan includes, but then only again if a translation unit was outdated (either because you changed the source file, or you touched a header that was already known to be included by it.)

Even better, we can update our include graph for these modified files asynchronously on a thread, while UBT is busy compiling your changed code. So effectively this now takes no time at all. (As an aside, Ninja solved this by using the /showIncludes option every time you compile, but it was very noisy and requires platform-specific handling as well as special PCH handling.)

If you were building a target, then you switch to building a different target, UBT detects that and will force a rescan of the full include graph. This is important because it can’t tell whether you modified a shared source file while you were working with that other target, so it needs to rebuild it’s cache. Hopefully it should just work automatically.


Hot Reload is fully supported! This is really cool because Hot Reload can now complete in less than a second in the best case. Awesome! Because every time a hot reload is initiated, we need to make up new file names for the output DLLs (to avoid collisions with already-loaded DLLs), supporting hot reload requires us to “patch” loaded makefiles to update the output file names as well as a few other things. We store a different Makefile for hot reload, so that you can ping pong between hot reloading and regular builds without having to wait for a makefile to be rebuilt.


!! Important !! With this feature, UBT cannot always detect when a new Makefile is needed. If you add new source files or delete source files, you need to make sure your project file is updated first! You can either regenerate project files, or you can manually make the change to the project and save it. UBT will notice this and create a new Makefile for that target.

There are other changes that UBT cannot detect, and you need to regenerate project files manually. (You can also rebuild UnrealBuildTool.exe (faster), which will force a new Makefile to be generated.)

  • Syncing down new code from Perforce or GitHub
  • Adding a UCLASS to a file that didn’t have one before (new generated code)
  • Changing global build settings (BuildConfiguration)
  • Added new source plugins
  • Changing your system environment variables
  • Installing a new platform SDK
  • Dropping in source for a new plugin
  • Changing UnrealHeaderTool itself

Sometimes UBT can detect itself that you need a new Makefile, such as if the project files were saved since you built last, or if UnrealBuildTool itself was recompiled. It will print out information to the console when building that shows what’s going on, and why a new Makefile was needed. Hopefully we can make it even smarter in the future without compromising performance.


There are some new intermediate files for this feature. These all get deleted if you do a clean.

  • Makefile.ubt: This is the actual makefile we generated. It has everything needed to build a target.
  • HotReloadMakefile.ubt: Hot reload version of the makefile (if needed).
  • LastBuildTargets.txt: This tells UBT what you were building last. If you switch targets, UBT needs to do more work.
  • FlatCPPIncludes.bin: Contains a mapping of every .cpp file to all of its included files (directly or indirectly). Used for fast outdatedness checking.

Note: UBT will no longer automatically recompile UnrealHeaderTool during incremental builds. It will only do that the first time, when your Makefile is created. We thought this was a good enough trade-off, but be wary if you are working in UnrealHeaderTool!


Bugs!! It was a complicated project, so there are still probably some issues left. Many of the known issues are notated in the code with “@anonymous_user_5851a2121 ubtmake”. For example, the feature doesn’t work at all with RPC or with certain platforms. And there are some edge cases if UBT is terminated unexpectedly where your include cache can become invalid. We’re going to keep working on this feature in hopes of using it by default in UE 4.8.

–Mike

Will hotreload work with code plugins?

This is a really good news, Mike!
I was waiting for this since you talked about that a while ago (during summer of I recall well).

The point is I will try this as soon as possible, and report any problem I encounter.

This worked the last time I tried it, but you need to invoke the hot reload from the editor either using a console command (“hotreload <modulename>”) or using the “Modules UI” in the Developer Tools menu.

–Mike

I read on the documentation that UBT will change to c++ in future

Is that planned anytime soon and will that help performance even more ??

When will we see this in non-source builds? First 4.8 preview or sooner?

Does it say that in the documentation? If it does let me know. There is no plan to make this change anytime soon. The performance issues with UBT aren’t related to programming language – it is just trying to do too much all at once. This is the motivation for some of the changes I wrote about above.

Yes, we’re hoping for the first 4.8 Preview build to have these changes in non-source.

–Mike

Will try it out right now :smiley: Thanks Mike, it was quite strange that starting the whole process sometimes took more than the actual compiling time! I love to see such improvements, are there even more planned?

Cheers,
Moss

Great Mr. Fricker, thank you!

Hmm, this sounds like a good improvement. Can I please ask that you make sure there is a way of cleaning a project’s makefiles out (I mean so we absolutely know they are the updated versions). Perhaps from the BUILD menu have a “CLEAN ALL” option that scrubs all makefiles and rebuilds them.

I ask that because often when I’m working with my class (I teach UE4 at a University) I want to absolutely know that their build environment is up-to-date and hasn’t got some remnants of previous efforts or partially compiled sources/dll’s. Its a bit like the clean rebuild option of visual studio, its a way of sanity testing a build environment to essentially recreate it having previously cleaned.

Using the “Rebuild” feature from Visual Studio will forcibly delete any existing Makefiles (along with the other intermediate files listed above.)

Will this also improve the speed and responsiveness of the Intellisense in Visual Studio without Visual Assist or isn’t it effected by it?

Wow these nuggets shouldn’t be kept hidden :)! I gave up on hot reload because it wasn’t working for plugins, but now after trying with the console commands, there is a way! Probably will have to bind specific keys to the console command for improving workflow, but this is still ten times better than reloading the editor each time…

Question, why doesn’t this work if you recompile from VC++? The game code gets reloaded as expected but plugin code doesn’t reload without the commands above, ideally both of the recompiled targets should reload.

This is a badly needed feature, vanilla intellisense is close to useless in most cases as it is. Sadly, Github search is currently a better way to look up engine code references…

We just haven’t implemented that yet actually. I would like for us to support it (it’s in our task database). Getting the game code reloading was the highest priority, but we should be able to re-use most of that flow for reloading plugins too. We’ll get there.

–Mike

This is something I’m very passionate about improving. It should be possible in the future to have an “IntelliSense PCH” for Visual Studio that would make symbol lookup very fast. But we aren’t sure how to do this yet with Makefile projects – there doesn’t appear to be a way to give Visual Studio different command-line arguments for the IntelliSense compiler on a per-source file basis. This would work with native VC++ project files, which might be what we end up having to switch to in order to get this feature working.

Aside from making it faster, we’re also trying to make it work more correctly with our codebase (fewer false squiggles or Error List window entries.) Some of these improvements will be in the upcoming 4.7 release!

–Mike

Sorry, but this isn’t true for me.

I’m running a project which includes the editor source code (I had to slightly mod the engine). IF I right click on my game project and select “Rebuild”, then it will clean EVERYTHING and completely rebuild the editor (which takes about 30 minutes) and then it will build my game (which is my intent). It won’t clear out my OBJ files in the “Intermediate\Build\Win64[ProjectName]Editor\DebugGame[ProjectName]” folder either, which is necessary in some cases – such as removing a class method – in order to prevent linker errors.

Thanks Slayemin, good catch. We’re fixing this now.

EDIT: Fixed this just now in this commit. Thanks again.

–Mike

Any chance of the makefile build system supporting other IDE & Compiers. Like Code Blocks and MinGW. Would love to compare code speed between MinGW and VS

This is on our wishlist but we have no immediate plans for it. The developers at Epic who are most familiar with the build system are currently busy on more important tasks. It is definitely something we’d try to collaborate with the community on if there was interest in helping out!

Given time, probably the next thing we would tackle is generation of “native” project files for Visual Studio and Xcode (at least for game projects that do not need to compile the engine.) It has advantages with IntelliSense, dependency checking and general usability that we think would be a win for small teams.