Copy 3rd Party DLLs to Binaries folder does not work for Engine Plugin - How to solve?

Hey everyone!

I’m struggling with a very strange problem and I already tried out everything that I could imagine but still the problem persists and I hope that you guys can help me.

I built a code plugin that uses some Third-Party DLLs. It works perfectly as a project plugin and copies over the needed DLLs to the Binary folder using

            RuntimeDependencies.Add("$(BinaryOutputDir)/depthengine_2_0.dll", depthEngineDllPath);
            RuntimeDependencies.Add("$(BinaryOutputDir)/k4a.dll", k4aDllPath);
            RuntimeDependencies.Add("$(BinaryOutputDir)/k4abt.dll", k4abtDllPath);

However, as an engine plugin on the UE Marketplace, this does not work anymore. I already tried out multiple things, e.g.:

            RuntimeDependencies.Add("$(TargetOutputDir)/depthengine_2_0.dll", depthEngineDllPath);
            RuntimeDependencies.Add("$(TargetOutputDir)/k4a.dll", k4aDllPath);
            RuntimeDependencies.Add("$(TargetOutputDir)/k4abt.dll", k4abtDllPath);

            RuntimeDependencies.Add("$(PluginDir)/Binaries/Win64/depthengine_2_0.dll", depthEngineDllPath);
            RuntimeDependencies.Add("$(PluginDir)/Binaries/Win64/k4a.dll", k4aDllPath);
            RuntimeDependencies.Add("$(PluginDir)/Binaries/Win64/k4abt.dll", k4abtDllPath);

But still the DLLs are not copied over. I am pretty sure that the paths (k4aDllPath etc.) are correct:

string sdkPath = Path.GetFullPath(Path.Combine(PluginDirectory, "Source/ThirdParty/azure-kinect-sdk"));
string k4aDllPath = Path.Combine(sdkPath, "sdk", "windows-desktop", "amd64", "release", "bin", "k4a.dll");

What am I missing? Or how can I debug this problem best?

I really hope that you guys can help me and I’m looking forward to any helpful answer!!

1 Like

Anyone?? :confused:

Hey there, coincidentally found your post after messing with RuntimeDependencies.Add for a few hours. I’m not doing anything with the marketplace, but since I spent an unreasonable amount of time messing around thought I’d share my experience.

Having a 3rdparty library with a DLL and wanted it to be copied over. Documentation states to use $(TargetOutputDir) but the file doesn’t appear where my executable is in \Binaries\Win64. It certainly doesn’t do what I expect it to do, maybe it does copy it to some unexpected location - no clue.

Eventually tried $(PluginDir) and that worked copying the dll to the folder with the .build.cs file, as expected (still the wrong location, but at least something was copied - so I could at least rule out that the source path was wrong).

Then tried $(BinaryOutputDir) and with that the file does land next to the executable in \Binaries\Win64. I’m still a bit hesitant about using $(BinaryOutputDir) instead of $(TargetOutputDir), but so far it does what it’s supposed to do.

As a hack, you could copy it in the target.cs file in the postbuild step. I dislike that approach, but depending on how desperate you are it might be worth a try:

private void ImplPostBuildCopy(string SrcPath, string DestPath)
{
    PostBuildSteps.Add(string.Format("echo Copying {0} to {1}", SrcPath, DestPath));
    PostBuildSteps.Add(string.Format("xcopy /y /i /v \"{0}\" \"{1}\" 1>nul", SrcPath, DestPath));
}

and call it with

ImplPostBuildCopy(“$(ProjectDir)\source\Plugins\xxx\sdk\bin\win64\yyy.dll”, “$(ProjectDir)\Binaries\$(TargetPlatform)\”);

Same problem here

with Package plugin from editor, all RuntimeDependencies are staged correctly.

when BuildPlugin with RunUAT.bat, the RuntimeDependencies are not staged as Unreal documentation

1 Like

Same issue here. If anyone ever figures out how this is supposed to be done, please let us know!

For anyone is confusing with this issue you mistake the process of packing Plugin and packing Game.

when you build your plugin with

RunUAT.bat BuildPlugin 

It won’t stage the RuntimeDependencies for you, you need to do this by either

    1. puting the ThirdParty files at correct location where you refer them in your Build.cs file (for example YourPlugin/Binaries/ThirdParty/…)
      OR
    1. running some PreBuildSteps scripts to copy them to the correct location where you refer them in your Build.cs (for eamxple copy them from YourPlugin/Source/ThirdParty/… to YourPlugin/Binaries/ThirdParty/…)

But if you choose to put them directly in YourPlugin/Binaries/ThirdParty/…, you cannot submit your plugin package because this is violation against the (Fab) Technical Requirements - 4.3.7.3.d which states

Third-party dependencies may only be used with proof of permission and must be placed in a ThirdParty folder located inside the Source folder.

So the correct and available approach is 2.

  • Put your ThirdPatry files in YourPlugin/Source/ThirdParty/…

  • Add PreBuildSteps in your plugin description file YourPlugin.uplugin, here is an example

    {
      "FileVersion": 3,
      "Version": 3,
      "VersionName": "1.0.3",
      "FriendlyName": "UCefView",
      "Description": "",
      "Category": "Other",
      "CreatedBy": "",
      "CreatedByURL": "https://github.com/tishion",
      "DocsURL": "https://cefview.github.io/UCefView/",
      "FabURL": "",
      "MarketplaceURL": "",
      "SupportURL": "https://github.com/CefView/UCefView/issues",
      "CanContainContent": true,
      "EngineVersion": "5.0.0",
      "SupportedTargetPlatforms": [
        "Win64",
        "Mac",
        "Linux"
      ],
      "Modules": [
        {
          "Name": "UCefView",
          "Type": "Runtime",
          "LoadingPhase": "Default",
          "PlatformAllowList": [
            "Win64",
            "Mac",
            "Linux"
          ]
        }
      ],
    "PreBuildSteps": {
      "Win64": [
        "echo UCefView:PreBuildSteps:$(TargetType):$(TargetName)",
        "echo Copy CefViewCore runtime dependencies...",
    
      		"robocopy /s /e /v /njh /njs /np /ndl /xn /xo \"$(PluginDir)\\Source\\ThirdParty\\CefViewCore\\bin\" \"$(PluginDir)\\Binaries\\ThirdParty\\CefViewCore\"",
        "if %ERRORLEVEL% LSS 8 (exit /B 0) else (exit /B %ERRORLEVEL%)"
      ],
      "Mac": [
        "echo UCefView:PreBuildSteps:$(TargetType):$(TargetName)",
        "echo Copy CefViewCore runtime dependencies...",
    
        "mkdir -p \"$(PluginDir)/Binaries/ThirdParty/CefViewCore\"",
        "cp -vnpR \"$(PluginDir)/Source/ThirdParty/CefViewCore/bin/\" \"$(PluginDir)/Binaries/ThirdParty/CefViewCore/\"",
    
        "chmod -R +x \"$(PluginDir)/Binaries/ThirdParty/CefViewCore/Mac\""
      ],
      "Linux": [
        "echo UCefView:PreBuildSteps:$(TargetType):$(TargetName)",
        "echo Copy CefViewCore runtime dependencies...",
    
        "mkdir -p \"$(PluginDir)/Binaries/ThirdParty/CefViewCore\"",
        "cp -vnpR \"$(PluginDir)/Source/ThirdParty/CefViewCore/bin/\" \"$(PluginDir)/Binaries/ThirdParty/CefViewCore/\"",
    
        "chmod -R +x \"$(PluginDir)/Binaries/ThirdParty/CefViewCore/Linux\""
      ]
    }
    }
    

What important to know about how FAB will process your submission:

  • The PreBuildSteps scripts will be executed when you build your plugin or build the game project which consumes your plugin.
  • :warning: :warning: DO NOT USE XCOPY ON WINDOWS PLATFORM, because it won’t be executed correctly due to the output redirection, use robocopy instead!
  • When you submit your plugin package, FAB only need you to provide all source content, any built artifacts are not needed and should not be included in your submission package.
  • When FAB gets your plugin package (actually a source package), they will build your plugin with RunUAT.bat or RunUAT.sh with BuildPlugin parameter for all declared supported platforms.
  • You need to make sure the folder YourPlugin/Binaries/ThirdParty/… will be preserved after the building process by declaring the path in YourPlugin/Config/FilterPlugin.ini like:
    [FilterPlugin]
    ; This section lists additional files which will be packaged along with your plugin. 
    ; Paths should be listed relative to the root plugin directory, 
    ; and may include "...", "*", and "?" wildcards to match directories, files, and individual characters respectively.
    ;
    ; Examples:
    ;    /README.txt
    ;    /Extras/...
    ;    /Binaries/ThirdParty/*.dll
    
    ; Preserver ThirdParty folder
    /Binaries/ThirdParty/...
    
  • After all platforms were built successfully, FAB will combine all the artifacts in to single one archive file, and they will deploy the archive to the FAB market server. (actually the Intermediate folder will be included because it contains the precompiled files reference, this is important to the project using your plugin)

There is so few documents to describe the FAB build and deploy process, I figure this out by submitting my plugin more than 200 times and eventually published my product successfully.

Hope this will help all developers working on plugins with third-party references.