ExeBinariesSubFolder and RequiresUniqueBuildEnvironment

I would like to configure UE to build its various targets - Game, Client, Editor, and Server - in different subdirectories under `Binaries\Win64`, rather than them all going into the same directory.

The main reason I would like to do this is because our process requires other files to be copied in alongside the binaries, and these can have conflicting requirements. For example, Steam has a `steam_appid.txt` file which needs to exist in that directory for dedicated server builds, but which non-shipping client executables will delete when the process ends, causing interesting race conditions depending on which process was built most recently versus which one was executed most recently.

I found that `TargetRules` has the `ExeBinariesSubFolder` property, which appears to be exactly what I need. However, it is affected by the `[RequiresUniqueBuildEnvironment]` attribute, and it’s not entirely clear what this means as I’m not sure exactly what has a ‘Shared’ environment and what has a ‘Unique’ one.

When I want a server build, I can set this `ExeBinariesSubFolder` value in the relevant target.cs to ‘Server’ and it correctly drops all the files into that Server subdirectory, with a .target metadata file in the main Win64 directory - all good. I’m assuming this target has a Unique environment by default.

Similarly, when I want a client build, I set the value to ‘Client’ , and everything goes into Client except the accompanying .target file, which is what I want.

But when I create an editor build, it complains that our program’s editor project shares an environment with UnrealEditor.

`YourGameEditor modifies the values of properties: [ ExeBinariesSubFolder: Editor != ]. This is not allowed, as YourGameEditor has build products in common with UnrealEditor.`

So, I set `BuildEnvironment = TargetBuildEnvironment.Unique` in the target to try and resolve this, and this has unexpected results: while it does produce the ‘Editor’ subdirectory as I expect, it also drops hundreds of .dlls and .pdbs into the Win64 directory, plus it creates empty subdirectories for all the platforms I am not building - Android, GDK, iOS, Linux, etc. This is inside `Binaries\Win64` so it’s clearly not right.

Is it possible to do what I want, including an Editor target configuration, and having each target completely restricted to its own subdirectory?

Hi Ben,

For the steam_appid file, you could fiddle with the macros around the code that generates the file so that it runs on your server target (WITH_SERVER in ConfigureSteamInitDevOptions). That would likely be the easiest way to sort this out.

As far as the build environment (Shared\Unique), this determines the way the Engine\Editor modules are being compiled. The default is the Shared environment were the modules are compiled within the Engine folder hierarchy and will be used by multiple projects that are set to share. In this case, the default compilation settings are forced and UBT will generate errors when Target or module rules are making changes that affect the way the code should be compiled. Setting the project to a Unique environment will compile the Engine\Editor modules specifically for the project which is the reason you see all the extra DLLs in Project\Binaries. The empty subdirectories would be for the platform specific editor modules which usually contain helper code to “massage” the data (shader compiler, texture compressor…). Those modules are usually only compiled if the platform specific code and their SDKs are present.

You can avoid the unique environment by forcing TargetRules.bOverrideBuildEnvironment to true. The side effect is that compiling the UE5 targets doesn’t generate the same binaries than the same targets in your project. You can’t flip between compiling the projects as this will result in a full recompilation every time you change (UE5<->Project).

Your post seem to imply there are other reasons that you are trying to isolate the exe in their own folder. Can you share them?

Regards,

Martin

Hi Martin,

I don’t seem to have the `ConfigureSteamInitDevOptions` that you mention in the code base here - perhaps that’s a 5.7 or later change?

I could potentially edit FSteamServerInstanceHandler in SteamSharedModule.cpp to make servers write the steam_appid.txt file out as well, but this feels like a bad way to do it. Usually processes shouldn’t assume they can write to their own binary directory (and Steam assuming it can write that out for the client’s benefit in non-shipping builds is not ideal either, but at least it doesn’t attempt that in shipping builds).

Thanks for explaining the shared/unique thing but I must confess I don’t really understand most of the details there :smiley: I appreciate that, for a given build configuration (debug/shipping/etc) that many modules can be shared across multiple target configurations (client/server/etc). What I don’t understand is why that connection needs to be broken if I just want the binaries to be written to a different location? The binaries location doesn’t contain any of these shared intermediate files, so I don’t understand why ExeBinariesSubFolder needs to be flagged as [RequiresUniqueBuildEnvironment].

From looking at it more closely I now understand that the editor in primarily in UnrealEngine\Engine\Binaries\Win64 rather than in \MyGameProject\Binaries\Win64, and hence moving that binary causes a bunch of unexpected changes. I’m guessing this is also why I only get the warning about ‘sharing an environment’ when building the Editor version, and not when I build Clients and Servers? Perhaps the Editor is the only target where [RequiresUniqueBuildEnvironment] is truly relevant here?

If I continue down this path, I might treat the editor target as completely separate from client/game/server. With those builds, with ExeBinariesSubFolder set for them (and not for Editor), everything works mostly as I expect, although one problem remains: I can’t launch the Game executable from Rider due to it having a “Failed to open descriptor file” error - it’s not taking the added directory nesting level into account when trying to find the .uproject file.

Regarding the general desire to have target exes in their own folder, the main benefit is really just the generalised version of the specific problem I mention here - that they are really different programs and tend to have different requirements on the files that are deployed alongside them. Apart from steam_appid.txt we also have various other DLLs and ‘sidecar’ files that plugins and add-ons require, some of which are only relevant to the Client, some only to the Server. Most of the time having them all mixed in together is merely clutter and not a problem, but sometimes - especially when a 3rd party add-on assumes the binary directory handles a single executable only - there will be an incompatibility, such as with how Steamworks treats steam_appid.txt.

Hi Ben,

Seems like I was living in the past and was looking at the 5.5 code. There were changes in the 5.6 version where ConfigureSteamInitDevOptions doesn’t exist anymore…

Regarding steam_appid.txt, I don’t see a problem for the dedicated server to write within the binary directory as this will be running in a controlled environment that is not exposed directly to the public. I do like your idea of treating the Editor separately from the runtime targets. Both solutions would be appropriate in this case.

Regards,

Martin