Run a custom command before build

I would like to insert a custom build step (e.g. running a batch file) before the compilation begins. How would I go about adding a custom build step to the build process.

Sorry for the duplicate, it said it had failed.

UE4 has it own building system called UBT and uses c# build script which you can find in source, you might try to plug yourself there

Yeah, I’ve been looking through the ModuleRule.cs to see if there was a custom commands list. I was hoping to not have to make any changes to the UBT itself since I’d have thought this was a fairly standard requirement to be able to hook custom rules in to the pipeline.

Inspired by 's comment I have come up with a very hacky solution and would very much like to see the proper way of doing it.

I realised that the constructor from the projects ModuleRules must be executed so I just bunged a Process.Start into there.

There are a few caveats:

Firstly, this is called twice in a build, so if your operation is time consuming this could be a problem.

Secondly, if you launch a batch file the working directory will be [Unreal Engine Location]\Engine\Source. To get the path, I came up with another awful hack of a solution:

StackTrace st = new StackTrace(new StackFrame(true));
StackFrame sf = st.GetFrame(0);
string fileName = sf.GetFileName();
DirectoryInfo ContaingingDirectory = new DirectoryInfo(Path.GetDirectoryName(fileName));

DirectoryInfo CorrectDirectory = ContainginDirectory.Parent.Parent;

The CorrectDirectory is the root directory of your project.

You can use PreBuildSteps and PostBuildSteps string arrays in your *.Target.cs file.

// Specifies a list of steps which should be executed before/after this target is built, in the context of the host platform's shell.
// The following variables will be expanded before execution:
// $(EngineDir), $(ProjectDir), $(TargetName), $(TargetPlatform), $(TargetConfiguration), $(TargetType), $(ProjectFile).

Emphasis on “in the context of the host platform’s shell”.

To further clarify and save time for future searchers. (UE 5.3)

Ignore the older advice of adding a PreBuildSteps JSON field to your .uproject file.

Instead, as @Flassari indicates, add them inside your MyProject\Source\MyProject.target.cs and/or MyProject\Source\MyProjectEditor.target.cs files by adding string entries to the TargetRules.PreBuildSteps list.

This gives you C# access to the TargetInfo.cs class where you have access to the Platform, Configuration, Architecture, ProjectFile and more properties which can be easily and more reliably passed to an external script.

I use PowerShell instead of a batch file to perform Pre-Build actions, passing in any appropriate properties I require. As an example, here is the creation of a build-time injected timestamp.

MyProject.Target.cs

using UnrealBuildTool;
using System.Collections.Generic;

public class MyProjectTarget : TargetRules
{
	public MyProjectTarget(TargetInfo Target) : base(Target)
	{
		Type = TargetType.Game;
		DefaultBuildSettings = BuildSettingsVersion.V4;

		ExtraModuleNames.AddRange( new string[] { "MyProject" } );

		PreBuildSteps.Add("echo *** Running PreBuildSteps ***");

		PreBuildSteps.Add("pwsh \"" + ProjectFile.Directory.ToString() + "\\PreBuildTasks.ps1\" -projectDir \"" + ProjectFile.Directory.ToString() + "\"");

		PreBuildSteps.Add("echo *** Finished PreBuildSteps ***");
	}
}

PreBuildTasks.ps1

<# Powershell 7 script for performing pre-build configuration tasks.#>

# parameters that should be made available from the shell.
param(
    [string]$projectDir
);

# Inject a build timestamp into the project as a global macro value by overwriting the BuildTimeStamp.h file.
Function BuildTimeStamp{
	$pragmaOnce = "#pragma once";
	$timeStamp = (Get-Date).ToString('MM/dd/yyyy hh:mm tt');
	$tsDefine = "#define BUILD_TIMESTAMP TEXT(`"$timeStamp`")";

	$path = $projectDir + "\Source\MyProject\Content\Scripts\BuildTimeStamp.h"

	$pragmaOnce + "`r`n"+ $tsDefine | Out-File -FilePath $path -Encoding utf8 -Force
}

<# Perform the following tasks #>
BuildTimeStamp

#close when finished
Write-Host "Finished tasks in PreBuildTasks.ps1";
exit

Simple example for sure, but it could be anything including multi-threaded packaging and encryption of custom non-UE asset data. (Just ensure the main thread doesn’t exit until after all spawned threads have finalized.)