How Unreal builds your code

#pragma once · why Build.cs is C# · unity builds from scratch · 10 min

In one breath

When you press Compile, a C# program called UnrealBuildTool runs your Build.cs to learn the module's settings. It runs the header tool to generate the .generated.h files, glues your .cpp files into a few big unity files so they compile faster, then hands those to the C++ compiler. #pragma once stops any one header being pasted in twice along the way.

The whole trip, in order

  1. UnrealBuildTool (UBT) starts. It's written in C#.
  2. UBT compiles and runs every .Build.cs and .Target.cs to find out each module's dependencies and settings.
  3. UBT runs UnrealHeaderTool (UHT). UHT reads your headers, finds every UCLASS, UPROPERTY and UFUNCTION, and writes the .generated.h and .gen.cpp files (the reflection code that lets Blueprint, the editor and the garbage collector see your stuff).
  4. UBT writes the unity files, which glue many .cpp files together.
  5. UBT runs the actual C++ compiler (MSVC on Windows) on them, then the linker.
  6. Out comes UnrealEditor-WadOfGore.dll, which the editor loads.

Why is there a Build.cs when Unreal doesn't use C#?

Your game doesn't use C#, but the build tool does. A Build.cs isn't part of your game at all. It's a small C# program that UBT runs to ask your module questions: "what other modules do you need? which PCH setting? any special flags?"

public class WadOfGore : ModuleRules            // a C# class
{
    public WadOfGore(ReadOnlyTargetRules Target) : base(Target)   // UBT calls this
    {
        PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs;
        PublicDependencyModuleNames.AddRange(new string[] { "Core", "Engine", "GameplayAbilities" });
    }
}

UBT compiles every Build.cs into a little "rules" library (that's the Intermediate/Build/BuildRulesProjects/WadOfGoreModuleRules folder in your project), creates your class, and reads the lists you filled in. From those it builds the compiler and linker command lines.

Why a real programming language instead of a settings file? Because the settings sometimes need logic:

if (Target.bBuildEditor)                       // only in editor builds...
    PrivateDependencyModuleNames.Add("UnrealEd");  // ...link the editor module
if (Target.Platform == UnrealTargetPlatform.Win64) { ... }

Your ModularShapes plugin does exactly this, gating editor-only dependencies behind Target.bBuildEditor. UBT is already C#, so letting rules be C# means no separate config language to invent, and Visual Studio / Rider autocomplete works on them.

FileDescribes
WadOfGore.Build.csOne module: its dependencies and settings
WadOfGore.Target.csWhat gets built: the game
WadOfGoreEditor.Target.csWhat gets built: the editor version

#pragma once

#include is dumber than it looks: it literally copies and pastes the whole header into your file before compiling. Now imagine:

// WarriorHeroCharacter.cpp
#include "Characters/WarriorHeroCharacter.h"  // which includes WarriorBaseCharacter.h
#include "Characters/WarriorBaseCharacter.h"  // pasted again!

Without protection, AWarriorBaseCharacter would be pasted twice into the same file, and the compiler would stop with "class type redefinition". #pragma once at the top of a header tells the compiler: "if this file has already been pasted into this .cpp, skip it".

The old way, which you'll still see in older code, is an include guard:

#ifndef WARRIOR_BASE_CHARACTER_H
#define WARRIOR_BASE_CHARACTER_H
// ... the header ...
#endif

Same effect, more typing, and you can mess up the name. #pragma once isn't technically in the C++ standard, but every compiler Unreal supports understands it, and it's Epic's convention. Every header in WadOfGore has it (25 out of 25).

What pragma once does NOT do

It works per .cpp file. Ten different .cpp files can each include the header once, which is normal and necessary. So it won't save you from defining a variable in a header: each .cpp gets its own copy and the linker complains about duplicates. That's why the gameplay tags are declared in the .h and defined in one .cpp (tags page).

Unity builds, from scratch

The problem. Every .cpp is compiled separately, and every one starts by including big engine headers (CoreMinimal.h, Engine.h…), tens of thousands of lines. 25 .cpp files means the compiler reads those same headers 25 times. The engine has thousands of .cpp files.

The trick. UBT writes a generated .cpp that just #includes a batch of your .cpp files, one after another, and compiles that instead. Shared headers get read once per batch instead of once per file. Here's yours, from Intermediate/.../Module.WadOfGore.cpp:

// This file is automatically generated at compile-time to include
// some subset of the user-created cpp files.
#include ".../UHT/DataAsset_HeroStartupData.gen.cpp"
#include ".../UHT/WarriorBaseCharacter.gen.cpp"
...
#include ".../Source/WadOfGore/Private/AbilitySystem/Abilities/WarriorGameplayAbility.cpp"
#include ".../Source/WadOfGore/Private/AnimInstances/Hero/WarriorHeroAnimInstance.cpp"
...

Your module is small, so everything fits in one unity file. Big modules get split into several.

Analogy: 25 separate trips to the shop, each buying the same staples, vs one big trip.

The side effects (why it's an interview question)

Turn it off to check for hidden problems: bUseUnity = false in Build.cs, or -DisableUnity on the command line.

And the .generated.h include?

Step 3 above. UHT writes WarriorHeroCharacter.generated.h from your header, and GENERATED_BODY() pastes its contents into your class. It must be the last include in the header, because UHT's code expects everything above it to already be defined.

Quiz yourself

Unreal games are C++. Why is Build.cs written in C#?

It's run by UnrealBuildTool, which is a C# program. Build.cs is a small C# class UBT executes to learn a module's dependencies and settings. It's never part of the game.

Why use real code for build settings instead of a plain config file?

Settings sometimes need logic, e.g. if (Target.bBuildEditor) to add editor-only dependencies, or per-platform choices.

Build.cs vs Target.cs?

Build.cs describes one module (dependencies, settings). Target.cs describes what gets built (the game, or the editor).

What does #include actually do?

Copies and pastes the header's contents into the file before compiling.

What problem does #pragma once solve?

A header reaching the same .cpp twice (often through other headers), which would define its classes twice and cause redefinition errors. It makes the second include a no-op.

What's the older alternative to #pragma once?

Include guards: #ifndef NAME / #define NAME / #endif around the header.

Does #pragma once stop a variable defined in a header from causing duplicate-symbol errors?

No. It only works within one .cpp. Each .cpp that includes the header gets its own definition, and the linker sees duplicates. Declare in the .h, define in one .cpp.

What is a unity build, and why does Unreal use it?

UBT generates a .cpp that includes a batch of your .cpp files and compiles that, so shared headers are parsed once per batch instead of once per file. Much faster full builds.

Name two side effects of unity builds.

Hidden missing includes (borrowed from a neighbour in the batch); name clashes between file-local helpers; bigger recompiles per change (eased by adaptive unity).

In what order do UBT, UHT and the C++ compiler run?

UBT starts and runs the Build.cs rules → UHT generates .generated.h / .gen.cpp → UBT writes unity files → the C++ compiler and linker run.