背景

在现代软件开发中,依赖管理是一个不可或缺的环节。随着项目规模的扩大和技术的发展,项目往往需要依赖众多的外部库和框架。这些依赖关系如果管理不当,很容易导致项目构建失败、版本冲突或者运行时错误。因此,一个有效的依赖管理工具对于确保项目的稳定性和可维护性至关重要。

Unreal 引擎目前缺乏一个依赖管理工具去处理个人或者项目引入的第三方库,因此往往采用动态库(依赖的静态库编译进动态库)去隐藏符号信息,避免与项目内部的第三库发生冲突。

Unreal 引擎在不同平台接入动态库

以下设置在 Build.cs 中设置即可

Windows

设置第三方库头文件的路径

PublicIncludePaths.Add(Path.Combine(ThirdPartyPath, "Include"));

将第三方库头文件所在的文件夹路径添加到这个变量里面,就可让该模块查找到第三方库的头文件。

设置第三方库导入库的路径

PublicAdditionalLibraries.Add(Path.Combine(ThirdPartyPath, "Lib", "ThirdPartyDLL.lib"));

引入 lib 文件

设置第三动态库的路径

RuntimeDependencies.Add("AbsoluteTargetDirectoryPath/DLLFileName.dll", "AbsoluteDLLFilePath/DLLFileName.dll");

第一个字符串参数指定了需要将动态库文件复制到哪个文件夹内,一般来说将该参数设置为字符串 "$(BinaryOutputDir)/DLLFileName.dll" 即可,第二个字符串指定了动态库文件所在的路径。这个语句实际上执行的操作就是将第三方动态库文件复制到该模块编译好的动态库文件的同级目录下,这样在加载该模块的时候可以同时加载第三方库的动态库文件。

补充

至于为什么 windows 动态库包含 lib 和 dll,可以参考之前写得文章:https://www.chongchenshi.top/archives/1711291096267#CMake%20%E4%B8%AD%E7%9A%84%E9%9D%99%E6%80%81%E5%BA%93%E4%B8%8E%E5%8A%A8%E6%80%81%E5%BA%93 ,为什么需要库的章节。

Xbox

集成方法

与 windows 平台一致

PS5

集成方法

与 windows 平台一致。

此外需要在代码中显示加载

const FString DllPath = ".so"
void* Handle = FPlatformProcess::GetDllHandle(*DllPath);
check(Handle);

Android

集成方法

使用 PublicIncludePaths 引入 Include 文件,使用 PublicAdditionalLibraries 引入 so 文件。除此之外,还需要使用虚幻插件语言 UPL 来将动态库拷贝到指定目录,这样才能将动态库打包进 apk 里。新建一个 xml 文件,命名为 xxx_APL.xml,APL 即(Android Program Language),参考格式如下:

<?xml version="1.0" encoding="utf-8"?>
<!-- steps to add to build additions -->
<root xmlns:android="http://schemas.android.com/apk/res/android">        
    <!-- init section is always evaluated once per architecture -->        
    <init>                
        <setBool result="bSupported" value="false"/>                
        <isArch arch="arm64-v8a">                        
            <setBool result="bSupported" value="true"/>                
        </isArch>        
     </init>        
     <!-- optional files or directories to copy to Intermediate/Android/APK -->        
     <resourceCopies>                
         <isArch arch="arm64-v8a">                        
             <copyFile src=".../xxx.so"                                                
                 dst="$S(BuildDir)/libs/arm64-v8a/xxx.so" />                
         </isArch>       
     </resourceCopies>
</root>

Build.cs 中添加:

AdditionalPropertiesForReceipt.Add("AndroidPlugin", ".../xxx.xml");

此外,在代码中也需要显式加载:

const FString SoPath = "xxx.so";        
void* Handle = FPlatformProcess::GetDllHandle(*SoPath);        
check(Handle);

iOS

补充

概要

iOS 平台的第三方库通常以 framework 的形式进行分发。framework 包含静态库或者动态库,头文件,module descriptions,和一个可选的 resource bundle。

在 Unreal Engine 工程中集成 iOS framework,通常会出现两个常见的问题,而这两个问题都与链接库有关。

Crash on startup “DYLD Library not loaded” or “DYLD Library missing”

如果你的 app 在启动时出现 "DYLD Library missing" 或者 "DYLD Library not loaded",这意味着 dyld 在 @rpath 中没有找到第三方库。通常 crash 现场如下:

Exception Type:  EXC_CRASH (SIGABRT)
Exception Codes: 0x0000000000000000, 0x0000000000000000
Exception Note:  EXC_CORPSE_NOTIFY
Termination Reason: DYLD 1 Library missing
Library not loaded: '@rpath/swift_framework_dynamic.framework/swift_framework_dynamic'
Referenced from: '/Volumes/VOLUME/*/MyApp.app/MyApp'
Reason: tried: '' (no such file)

Triggered by Thread:  0

Thread 0 Crashed:
0   dyld   0x11cf4cb14 __abort_with_payload + 8
1   dyld   0x11cf526cc abort_with_payload_wrapper_internal + 104
2   dyld   0x11cf52700 abort_with_payload + 16
3   dyld   0x11cf22a00 dyld4::halt(char const*) + 580
4   dyld   0x11cf1fa20 dyld4::prepare(dyld4::APIs&, dyld3::MachOAnalyzer const*) + 3560
5   dyld   0x11cf1dd84 start + 488

Dynamic linker(dyld) 是应用程序的入口点。在应用启动之前,dyld 从内存中加载必要的库。库的搜索路径是应用二进制文件的一个特殊属性 @rpath

@rpath 代表运行时搜索路径,这个值是在 build 时设置的,既可以使用 Xcode LD_RUNPATH_SEARCH_PATH 设置也可以使用 ld linker command tool 的 -rpath 参数进行设置。@rpath 可以被设置成不同的值,其中一个常见的设置是 @executable_path/Frameworks,应用程序包中的 Frameworks 子目录。

是否设置成功,可以通过命令 otool -l MyApp.app/MyApp 查看输出中的 LC_RPATH

Load command 92
          cmd LC_RPATH
      cmdsize 40
         path @executable_path/Frameworks (offset 12)

UE5 修复 crash

在 Unreal 5 中,UnrealBuildTool::ModuleRules::Framework 类中有一个字段 bCopyFramework。把这个字段设置为 true 就可以将 framework 拷贝到 app bundle 中,Build.cs 如下:

PublicAdditionalFrameworks.Add(
   new Framework(
      "swift_framework_dynamic",
      "swift_framework_dynamic.embeddedframework.zip",
      null,
      true // bCopyFramework
   )
);

UE4 修复 crash

需要增加一个 UPL 文件:

<?xml version="1.0" encoding="utf-8"?>
<root>
    <init>
        <copyDir src="$S(EngineDir)/Intermediate/UnzippedFrameworks/swift_framework_dynamic/swift_framework_dynamic.embeddedframework/swift_framework_dynamic.framework" dst="$S(BuildDir)/Frameworks/swift_framework_dynamic.framework" />
    </init>
</root>

使用 Swift 静态库时出现 "Could not find or use auto-linked library"

如果需要将包含 Swift 类的静态库添加到 Unreal Engine 项目中时,需要告诉链接器在哪里查找 Swift 库。在 Xcode 中,需要再 target 的 Build Settings 中设置 "Library Search Paths"。

如果出现如下错误:

ld: warning: Could not find or use auto-linked library 'swiftFoundation'
ld: warning: Could not find or use auto-linked library 'swiftsimd'
ld: warning: Could not find or use auto-linked library 'swiftMetal'
ld: warning: Could not find or use auto-linked library 'swiftDarwin'
ld: warning: Could not find or use auto-linked library 'swiftCoreFoundation'
ld: warning: Could not find or use auto-linked library 'swiftObjectiveC'
ld: warning: Could not find or use auto-linked library 'swiftCore'
ld: warning: Could not find or use auto-linked library 'swiftAVFoundation'
ld: warning: Could not find or use auto-linked library 'swiftCoreMedia'
ld: warning: Could not find or use auto-linked library 'swiftCoreGraphics'
ld: warning: Could not find or use auto-linked library 'swiftCoreImage'
ld: warning: Could not find or use auto-linked library 'swiftCoreAudio'

这是因为第三方库需要链接 Swift libraries。默认情况下,Unreal 没有添加 Swift libraries 的搜索路径,所以需要在 Build.cs 添加:

string SDKROOT = Utils.RunLocalProcessAndReturnStdOut("/usr/bin/xcrun", "--sdk iphoneos --show-sdk-path");
PublicSystemLibraryPaths.Add(SDKROOT + "/usr/lib/swift");
PublicSystemLibraryPaths.Add(SDKROOT + "../../../../../../Toolchains/XcodeDefault.xctoolchain/usr/lib/swift/iphoneos");
PublicSystemLibraryPaths.Add(SDKROOT + "../../../../../../Toolchains/XcodeDefault.xctoolchain/usr/lib/swift-5.0/iphoneos");
PublicSystemLibraryPaths.Add(SDKROOT + "../../../../../../Toolchains/XcodeDefault.xctoolchain/usr/lib/swift-5.5/iphoneos");

集成方法

使用 PublicIncludePaths 引入 Include 文件。

其余步骤参考补充。

https://www.cnblogs.com/u-n-owen/p/15129758.html,https://supervj.top/2021/03/11/UBT%E5%92%8CUHT/,

https://blog.csdn.net/qq_21438461/article/details/136369974,

https://blog.csdn.net/Jingsongmaru/article/details/104588434,

https://rassadin.net/swift-frameworks-unreal/