Pre/Post build step in UBT

The question is simple: how to set up a Pre build event and a post build event properly as the Unreal devs intend to?

The current method I am using is not intended to be used like this, however pre/post/clean build steps are a very essential part of a build system, and therefore UBT must provide a way for developpers to implement them. Typically this is used to copy binaries (such as dlls) to the binary folder.

This is a follow up to my previous question : Alternative to UnrealBuildTool.GetUProjectPath() in 4.11 - Platform & Builds - Epic Developer Community Forums

Yes I would like this feature too. Copy DLLs/PDB/EXE to certain folders after build is successful.

Hello,

I’ve entered a feature request for the ability to re-locate those files to new folders, which you can track the status of here: Unreal Engine Issues and Bug Tracker (UE-34899)

Have a great day

Hi Sean. Thanks for following up on this. Honestly having a specific ability to move files is great, but having a place to code a pre/post build step is even better. It is a basic functionnality of most if not all build systems out there, and as such is it paramount that UBT supports it.

Thanks for the response. I’ll go ahead and add that info to the request.

Yes besides move/copy files there are other useful stuff such as the ability to run certain script eg to play beep after a long build time. Just keep it open.

This feature already exists. You can add shell commands to the end of builds by adding something like this to your .uproject file:

"PostBuildSteps":
{
    "Win64": [
        "copy $(ProjectDir)\Binaries\$(TargetPlatform)\$(TargetName).exe D:\SomeDir"
    ]
}

There’s a PreBuildSteps section too. Check out WriteCustomBuildStepScripts in UEBuildTarget.cs in UBT to see all the available variables.

2 Likes

It’s the name of a function in Engine\Source\Programs\UnrealBuildTool\Configuration\UEBuildTarget.cs which executes custom build steps. You can see all the variable names which can be used there.

				Variables.Add("EngineDir", UnrealBuildTool.EngineDirectory.FullName);
				Variables.Add("ProjectDir", ProjectDirectory.FullName);
				Variables.Add("TargetName", TargetName);
				Variables.Add("TargetPlatform", Platform.ToString());
				Variables.Add("TargetConfiguration", Configuration.ToString());
				Variables.Add("TargetType", TargetType.ToString());
				if(ProjectFile != null)
				{
					Variables.Add("ProjectFile", ProjectFile.FullName);
				}
				if(Pair.Item2 != null)
				{
					Variables.Add("PluginDir", Pair.Item2.Directory.FullName);
				}

Where is this information?

Check out WriteCustomBuildStepScripts in UEBuildTarget.cs in UBT

I don’t understand the reference and I’d like to add a pre-build step.

Thanks

The source code for that program may only be available in GitHub. I posted the relevant section above though - to summarize, you can use the $(EngineDir), $(ProjectDir), $(TargetName), $(TargetConfiguration), $(TargetType), $(ProjectFile), $(PluginDir) variables in those commands…

I don’t find that file (UEBuildTarget.cs) anywhere in my UE project folder or program folder. All I find is an UnrealHeaderTool I’m using UE4 (4.12).

Thanks for your incredibly quick response to my last post.

Looks like what I need. I will check it out!

If I may comment on the general design of this feature though, it feels more like a “hack” in the sense that it isn’t well integrated in the build system. Everything else is neatly defined in UBT c# files but these can only be command line arguments?

Ideally it’s not only the project that should define these pre/post build steps but each module itself, in its Build.cs file, with full access to the UBT (c#) context of what is being built.

If i want to make a more complex, and more importantly platform independent post build step, it gets even worse!

I need to either use a platform agnostic scripting language and make sure all my team members have the environment setup on all their machines (overhead), or i need to compile an executable just for this. It would be so much better to have it all in C#, all in one place.

It is not meant to be used for complex pre/post build steps. You can create your own program and run that if you need something elaborate; this is the same model that Visual Studio uses for per-project extensibility.

UBT does not have any sort of encapsulated public API, so encouraging people to write code against it would be a backwards compatibility nightmare. Maybe one day there will be, but there is little demand for it now.

If you want to encapsulate custom build steps with a module (or set of modules), the same syntax is supported from .uplugin files.

Maybe it is a matter of perspective.

Yes this is as limited as what MSVC proposes but this is one of the reasons people move on to CMake, ninja or others, more featured build systems. You get a lot more control, and especially when dealing with a large number of platforms and configurations, it becomes a nightmare to manage using only native files, whereas using a programmatic approach to generating build scripts (which would obviously include pre/post build steps) enables much more freedom while reducing complexity. Obviously i’m not teaching you anything, this is exactly why Unreal made UBT, but then why restrict ourselves? Having this all in one place keeps things simple and straightforward, and the portability of c# is especially suited for this.

We deal with backward compatibility issues very well with UE’s API already. Having a similar approach (deprecating functionnality for a version or two before removing it completely) is more than enough and has native support in c#. I honestly don’t see how this would be worse, especially considering the build system will eventually reach much more stability than the engine API will.

UE4 is designed as an engine with established entry points, whereas UBT is not. There is no sort of API layer at all. Any refactoring (of which much is needed) would not be possible if everything had to be deprecated first.

What do you actually want to access inside UBT?

You already do have public API even if you do not see it as such. Each module has to implement ModuleRules and TargetRules, and all the parameters that are passed to those functions inherently need to be stable or you will break people’s build scripts as they upgrade.

Ideally I’d want to make all my scripts as abstracted as possible. In my current example of copying the binaries (oftentimes DLL for third party libs) into the binary folder, i still have to create “hacks” to determine where is the output directory from the module directory. If you change anything in how the path to my binaries is generated, which you should be able to as these are temporary directories, i need to update my logic as well. There could be more examples but this is one.

You can’t tell me that using something like this is pretty :

  public string GetUProjectPath()
  {
        return Directory.GetParent(ModulePath).Parent.Parent.ToString();
  }

AFAIK there is no other clean way for me to determine output directory in my build.cs file, and therefore my module is no longer working independently of its context.

Basically instead of doing string replaces in command line arguments and having a very limited set of variables, let me access a certain set of internals from a part of UBT that is explicitly designed to be a public API. Give me methods to override for Pre/Post build steps and whatever else I would need to customize (selecting which files to include and overriding filters come to mind).

You would get a much better design, with everything pertaining to a module’s build process all in one file, all in one language, all platform agnostic. Note that this is how other popular meta-build systems work.

Honestly, even if you don’t expose anything more than you already do, having it all in c# trumps all just because it will run similarly on all platforms. I’m not sure how I could even make different scripts for different platforms with what you are giving me now.

The ModuleRules and TargetRules do form a stable API, you’re right.

The problem with copying files from that location is that UBT will cache the actions needed to build a target in a makefile, so it will not always compile and construct your .build.cs file at runtime. It is not fast to do that runtime compilation, and there have always been distant plans to move away from using C# to specify module rules as a result.

The current shell command are cached out as a list of commands into the makefile, so can be executed without reconstructing the target dependency graph.

For the pattern you’re talking about, we always submit the binaries to their final locations in source control so no post-build steps are needed in the first place.

Yes of course you could submit binaries to source control but that seems a bit strange to me as, again, my third party modules are now no longer self-contained and portable as a single folder.

For me, Binaries is as a core a temporary folder, which should not be under source control. I can safely delete this and temporaries and expect to get the same output if I perform a rebuild. I suspect most people have it excluded in their source control by nature.

I’d be glad to give as much input as much as you like especially if you guys are planning a refactor of UBT. I do believe it is a tool with great potential, and I have myself developped and used similar (c#) custom build systems within other game companies, so this is definitely achievable.

Anyway, to get back on track: with shell commands only, how can I make my pre/post build step either platform agnostic, or specialized for a platform? Shell commands are by definition not portable.

The example I posted above shows how you execute a custom build step for the Win64 host platform. You can add another section for Mac or Linux as required.

You can run any other program you like from there, or spawn a language-agnostic scripting language like Python if you like.

Oh right, I did not read properly. Well my bad. Thanks for all the help!