Mac m1 上编译阿里 OSS c++ 供 UE 使用

环境:

  • 操作系统:macOS 12.5
  • 芯片:Apple M1
  • Xcode:13.4.1
  • UE:5.0.3

环境准备

编译 openssl

起初,我是在 M1 通过 brew 安装的 openssl。通过安装命令 $ brew install openssl 最新版本的openssl。安装后的目录为

  • /opt/homebrew/opt/openssl
  • /opt/homebrew/opt/openssl/lib
  • /opt/homebrew/opt/openssl/include/

发现不行,需要自己编译 x86_64 版本。

从 https://www.openssl.org/source/ 下载 openssl-1.1.1g.tar.gz。解压后将文件夹修改为 openssl-1.1.1g-x86_64,在此文件夹下生成 x86_64 版本的 openssl。终端进入该目录,开始编译。

export MACOSX_DEPLOYMENT_TARGET=10.15
cd openssl-1.1.1g-x86_64
./Configure darwin64-x86_64-cc shared
make

成功的话,就可以在文件夹下看到生成的动态库和静态库文件:

  • libcrypto.1.1.dylib
  • libcrypto.a
  • libcrypto.dylib
  • libssl.1.1.dylib
  • libssl.a
  • libssl.dylib

通过生成多个架构的 openssl,可以参考:How to build openssl for M1 and for Intel?。

安装 CMake

CMake 是一个跨平台的编译工具,可以用简单的语句来描述所有平台的编译过程。到 CMake 的下载页面,下载并安装 CMake。

在 Mac 中自带了 CMake,可以通过命令行的方式编译阿里 OSS sdk,也可以安装 CMake,生成 Xcode 项目编译阿里 OSS sdk。

编译 c++ SDK

首先查看阿里文档。根据提示,下载 c++ SDK 的安装包并解压:

Mac m1 上编译阿里 OSS c++ 供 UE 使用_第1张图片
打开终端,进入目录aliyun-oss-cpp-sdk-master。创建 build 文件夹,并进入。

mkdir build
cd build

配置 cmake:

cmake -DOPENSSL_ROOT_DIR=/Users/...../openssl-1.1.1g-x86_64  \
      -DOPENSSL_INCLUDE_DIRS=/Users/...../openssl-1.1.1g-x86_64/include/ \
      -DCMAKE_OSX_ARCHITECTURES=x86_64 ..

其中:

  • DOPENSSL_ROOT_DIR、DOPENSSL_LIBRARIES、DOPENSSL_INCLUDE_DIRS:指定 openssl 的目录,此处需要修改为上面自己编译的 openssl 的目录。
  • DCMAKE_OSX_ARCHITECTURES:指定 CPU 架构。因为 UE 编译时为 x86_64 架构,所以在 M1 上编译时需要指定。如果同时编译多个架构,可以设置为 arm64;x86_64

经过测试 DOPENSSL_LIBRARIES 并不需要指定。

打开 build/CMakeCache.txt的文件,找到下面的内容:

//Minimum OS X version to target for deployment (at runtime); newer
// APIs weak linked. Set to empty string for default value.
CMAKE_OSX_DEPLOYMENT_TARGET:STRING=

设置 CMAKE_OSX_DEPLOYMENT_TARGET:STRING=10.15系统版本,因为 UE 的支持的最低版本为 10.15。如果不设置的话,在之后的使用当中,会报如下的错误:

libalibabacloud-oss-cpp-sdk.a(OssClient.cc.o)) was built for newer macOS version (12.0) than being linked (10.15)

这个值应该怎么通过命令进行设置呢?我暂时没有找到。

设置之后,开始编译:

make

结果报错了

/Users/fuyoufang/Desktop/UE_OSS/aliyun-oss-cpp-sdk-master/sample/src/bucket/BucketSample.cc:440:25: error: loop variable 'rule' creates a copy from type 'const AlibabaCloud::OSS::LifecycleRule' [-Werror,-Wrange-loop-construct]
        for (auto const rule : outcome.result().LifecycleRules()) {
                        ^
/Users/fuyoufang/Desktop/UE_OSS/aliyun-oss-cpp-sdk-master/sample/src/bucket/BucketSample.cc:440:14: note: use reference type 'const AlibabaCloud::OSS::LifecycleRule &' to prevent copying
        for (auto const rule : outcome.result().LifecycleRules()) {
             ^~~~~~~~~~~~~~~~~
                        &
/Users/fuyoufang/Desktop/UE_OSS/aliyun-oss-cpp-sdk-master/sample/src/bucket/BucketSample.cc:473:29: error: loop variable 'origin' creates a copy from type 'const std::string' [-Werror,-Wrange-loop-construct]
            for (auto const origin : rule.AllowedOrigins()) {
                            ^
/Users/fuyoufang/Desktop/UE_OSS/aliyun-oss-cpp-sdk-master/sample/src/bucket/BucketSample.cc:473:18: note: use reference type 'const std::string &' to prevent copying
            for (auto const origin : rule.AllowedOrigins()) {
                 ^~~~~~~~~~~~~~~~~~~
                            &
/Users/fuyoufang/Desktop/UE_OSS/aliyun-oss-cpp-sdk-master/sample/src/bucket/BucketSample.cc:471:25: error: loop variable 'rule' creates a copy from type 'const AlibabaCloud::OSS::CORSRule' [-Werror,-Wrange-loop-construct]
        for (auto const rule : outcome.result().CORSRules()) {
                        ^
/Users/fuyoufang/Desktop/UE_OSS/aliyun-oss-cpp-sdk-master/sample/src/bucket/BucketSample.cc:471:14: note: use reference type 'const AlibabaCloud::OSS::CORSRule &' to prevent copying
        for (auto const rule : outcome.result().CORSRules()) {
             ^~~~~~~~~~~~~~~~~
                        &
3 errors generated.
make[2]: *** [sample/CMakeFiles/cpp-sdk-sample.dir/src/bucket/BucketSample.cc.o] Error 1
make[1]: *** [sample/CMakeFiles/cpp-sdk-sample.dir/all] Error 2
make: *** [all] Error 2

Mac m1 上编译阿里 OSS c++ 供 UE 使用_第2张图片

应该是编辑条件比较严格,for (auto const rule : outcome.result().LifecycleRules()) { },会进行拷贝操作,应该使用引用。打开 aliyun-oss-cpp-sdk-master/sample/src/bucket/BucketSample.cc 文件,在 440,471,473 行,修一下报错的地方,修改为引用使用,即在变量前加 &。重新编译。

make

此时,应该编译成功,就可以看到编译好的 build/lib/libalibabacloud-oss-cpp-sdk.a 文件了。

Mac m1 上编译阿里 OSS c++ 供 UE 使用_第3张图片

UE 中使用

创建一个 C++ 类型的 UE 项目 TestAliOSS,来测试编译好的库文件。

创建插件

给 UE 项目 TestAliOSS,创建一个插件 AliOSS,用于封装阿里 OSS sdk。

Mac m1 上编译阿里 OSS c++ 供 UE 使用_第4张图片

此处创建一个空白的插件,命名为 AliOSS

Mac m1 上编译阿里 OSS c++ 供 UE 使用_第5张图片

AliOSS 的源码路径 Source 下,新建文件 OSSThirdParty文件,然后做以下操作:

  1. 复制编译好的 libalibabacloud-oss-cpp-sdk.a 文件到 OSSThirdParty/Lib 文件下;
  2. 复制阿里 SDK 源文件中的 aliyun-oss-cpp-sdk-master/sdk/include 到 OSSThirdParty/include 文件下,即阿里 OSS 相关的头文件
  3. 复制阿里 SDK 源文件中的 aliyun-oss-cpp-sdk-master/third_party/include/curl 到 OSSThirdParty/include/ 文件下,即 curl 相关的头文件( 我发现复制过来的 openssl 头文件不行)
  4. 复制 openssl-1.1.1g-x86_64/include/openssl 到 OSSThirdParty/include 文件下,即 openssl 相关的头文件;
  5. 复制 openssl-1.1.1g-x86_64 下的 libcrypto.a 和 libssl.a 文件到 ~~OSSThirdParty/lib/Mac_x86_64~~ 文件下,即 openssl 的库文件;(发现这一步可以省掉,应该使用 UE 的库文件)
  6. 将 /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX12.3.sdk/usr/lib/libcurl.tbd 拷贝一份,放到 OSSThirdParty/lib/Mac_x86_64 文件夹下

第 6 步中的 libcurl.tbd 文件是什么?

libcurl 是 mac 的一个库文件。我是在阿里源码中,通过 Cmake 生成的项目文件中,看到需要 link 这样一个文件。拷贝这个文件到插件当中,感觉有点奇怪,但是暂时没办通过其他方式不依赖这个文件。

Mac m1 上编译阿里 OSS c++ 供 UE 使用_第6张图片

现在目录结构为

  • Plugins/AliOSS/Source
    • AliOSS
    • OSSThirdParty
    • include
      • alibabacloud/oss/…
      • curl
      • openssl
    • lib
      • libalibabacloud-oss-cpp-sdk.a
      • Mac_x86_64/libcurl.4.tbd

修改 AliOSS.Build.cs文件为下面的内容:

// Copyright Epic Games, Inc. All Rights Reserved.
using System;
using System.IO;
using UnrealBuildTool;

public class AliOSS : ModuleRules
{
    private string OSSThirdPartyPath
    {
        get
        {
            // ModuleDirectory 为插件的 项目路径/Plugins/SimpleOSS/Source/SimpleOSS/ 目录
            return Path.GetFullPath(Path.Combine(ModuleDirectory, "../OSSThirdParty/"));
        }
    }

    public AliOSS(ReadOnlyTargetRules Target) : base(Target)
    {
        PCHUsage = ModuleRules.PCHUsageMode.UseExplicitOrSharedPCHs;
        PublicIncludePaths.AddRange(
        new string[] {
                // ... add public include paths required here ...
            }
        );

        PrivateIncludePaths.AddRange(
            new string[] {
                // ... add other private include paths required here ...
            }
        );

        PublicDependencyModuleNames.AddRange(
            new string[]
            {
                "Core",
                // ... add other public dependencies that you statically link with here ...
            }
        );

        PrivateDependencyModuleNames.AddRange(
            new string[]
            {
                "CoreUObject",
                "Engine",
                "Slate",
                "SlateCore",
                // ... add private dependencies that you statically link with here ...
            }
        );

        DynamicallyLoadedModuleNames.AddRange(
            new string[]
            {
                // ... add any modules that your module loads dynamically here ...
            }
        );

        string OssincludePath = Path.Combine(OSSThirdPartyPath, "include");

        PublicIncludePaths.Add(OssincludePath);
        string AlibabacloudPath = Path.Combine(OssincludePath, "alibabacloud/oss");
        PublicIncludePaths.Add(AlibabacloudPath);

        PublicIncludePaths.Add(Path.Combine(AlibabacloudPath, "auth"));
        PublicIncludePaths.Add(Path.Combine(AlibabacloudPath, "client"));
        PublicIncludePaths.Add(Path.Combine(AlibabacloudPath, "encryption"));
        PublicIncludePaths.Add(Path.Combine(AlibabacloudPath, "http"));
        PublicIncludePaths.Add(Path.Combine(AlibabacloudPath, "model"));
        PublicIncludePaths.Add(Path.Combine(AlibabacloudPath, "utils"));

        // PublicIncludePaths.Add(Path.Combine(OssincludePath, "curl"));
        PublicIncludePaths.Add(Path.Combine(OssincludePath, "openssl"));

        string LibPath = Path.Combine(OSSThirdPartyPath, "lib");
        PublicIncludePaths.Add(LibPath);
        PublicAdditionalLibraries.Add(Path.Combine(LibPath, "libalibabacloud-oss-cpp-sdk.a"));

        // 被注释掉的两个,可以使用下面的替换
        //PublicAdditionalLibraries.Add(Path.Combine(LibPath, "Mac_x86_64", "libcrypto.a"));
        //PublicAdditionalLibraries.Add(Path.Combine(LibPath, "Mac_x86_64", "libssl.a"));
        AddEngineThirdPartyPrivateStaticDependencies(Target, "libcurl");
        AddEngineThirdPartyPrivateStaticDependencies(Target, "OpenSSL");
        
        PublicAdditionalLibraries.Add(Path.Combine(LibPath, "Mac_x86_64", "libcurl.4.tbd"));
    }
}

注意添加头文件:

using System;
using System.IO;

在插件中添加测试程序,新建 Test.h 和 Test.cpp,创建一个 OSSTest 类,内容分别如下:

#pragma once
#include "CoreMinimal.h"

namespace OSSTest
{
    // 加上导出宏,否则其他模块无法使用
    // 导出宏的格式为,`插件名字的大写_API`
    ALIOSS_API int32 MainOSS();
}
#include "Test.h"
#include "alibabacloud/oss/OssClient.h"

using namespace AlibabaCloud::OSS;

int32 OSSTest::MainOSS()
{
    std::string AccessKeyId = "";
    std::string AccessKeySecret = "";
    std::string Endpoint = "";
    std::string BucketName = "";
    /* 初始化网络等资源 */
    InitializeSdk();


    return 0;
}

注意,插件中的方法要在其他模块中使用时,需要加上导出宏,否则其他模块无法使用。导出宏的格式为,插件名字的大写_API。比如插件的名字为AliOSS时,导出宏为:ALIOSS_API

UE 中测试插件

在 UE 工程中的 TestAliOSS.Build.cs 中添加依赖 AliOSS

PublicDependencyModuleNames.AddRange(new string[] { "Core", "CoreUObject", "Engine", "InputCore", "AliOSS" });

在 ATestOSSGameModeBase 的 BeginPlay() 方法中调用测试方法 MainOSS

#pragma once

#include "CoreMinimal.h"
#include "GameFramework/GameModeBase.h"
#include "TestOSSGameModeBase.generated.h"

/**
 * 
 */
UCLASS()
class TESTOSS2_API ATestOSSGameModeBase : public AGameModeBase
{
GENERATED_BODY()
public:
    virtual void BeginPlay() override;
};

#include "TestOSSGameModeBase.h"
#include "Test.h"

void ATestOSSGameModeBase::BeginPlay()
{
    Super::BeginPlay();

    OSSTest::MainOSS();
}

结果编译错误:

Declaration shadows a variable in namespace ‘AlibabaCloud::OSS’

Mac m1 上编译阿里 OSS c++ 供 UE 使用_第7张图片

不知道具体原始是什么,暂时直接将 URL 改成 URL1

enumType
{
  URL1 = 0,
  JSON
};

发现可以通过编译了。

其他错误

错误:UnrealEditor.app: No such file or directory

不知道为什么在 Mac M1 下,UE5 经常遇到下面的问题:

CodeSign /Volumes/devfang/App/UE_5.0/Engine/Binaries/Mac/UnrealEditor.app
    cd /Volumes/devfang/com/Test/TestAliOSS/Intermediate/ProjectFiles
    export CODESIGN_ALLOCATE=/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/codesign_allocate

Signing Identity:     "-"

    /usr/bin/codesign --force --sign - --generate-entitlement-der --entitlements /Volumes/devfang/com/Test/TestAliOSS/Intermediate/ProjectFiles/build/TestAliOSS.build/Development\ Editor/TestAliOSS.build/UnrealEditor.app.xcent --timestamp=none /Volumes/devfang/App/UE_5.0/Engine/Binaries/Mac/UnrealEditor.app
/Volumes/devfang/App/UE_5.0/Engine/Binaries/Mac/UnrealEditor.app: No such file or directory
Command /usr/bin/codesign failed with exit code 1

解决方案为:重新校验 UE5 。

Mac m1 上编译阿里 OSS c++ 供 UE 使用_第8张图片

你可能感兴趣的:(Unreal,Engine,c,++,macos,c++,xcode,unreal,engine,5)