【问题标题】:Link to fat Static Library inside Swift Package链接到 Swift 包中的胖静态库
【发布时间】:2022-01-02 15:39:16
【问题描述】:

我正在尝试构建一个 Swift 包,其中包含一个用 C 编写的胖静态库:libndi_advanced_ios.a,来自NewTek's Apple Advanced NDI SDK

我无法将预编译的库(只有头文件和 .a 二进制包可用)链接到我的 Swift 包。我做了很多研究并尝试了不同的解决方案,但都没有奏效。这是一个快速列表:

  • 不能bundle in an XCFramework 因为libndi_advanced_ios.a 支持多个平台(arm_v7、i386、x86_64、arm64)并且xcodebuild -create-xcframework 返回错误binaries with multiple platforms are not supported(这个解决方案也在Swift Forums 上讨论过);

  • 按照SPM Documentation(即outdated)的建议在targets 中使用.linkedLibrary 会发出警告system packages are deprecated; use system library targets instead,我什至不记得它是否构建成功;

  • 使用不同的标志和设置(如linkerSettings)没有成功。也许我只是错过了正确的组合。

我可以链接几十个 Stackoverflow 的问题和其他没有帮助的帖子,但这将毫无用处(abc)。

目前我有这个配置:

Package.swift 包含以下代码:

let package = Package(
    name: "swift-ndi",
    platforms: [.iOS(.v12)],
    products: [
        .library(
            name: "swift-ndi",
            targets: ["swift-ndi"])
    ],
    dependencies: [],
    targets: [
        .target(name: "CiOSNDI", path: "Libraries"),
        .target(
            name: "swift-ndi",
            dependencies: ["CiOSNDI"]),
        .testTarget(
            name: "swift-ndiTests",
            dependencies: ["swift-ndi"]),
    ]
    
)

您可以在alessionossa/swift-ndi找到整个项目。
目前唯一的结果是一些警告和模块 CiOSNDI 没有构建:

我也试过.systemLibrary(name: "CiOSNDI", path: "Libraries/"),这个配置:alessionossa/swift-ndi/tree/systemLibrary;但我收到这些错误:

注意

NDI_include 实际上是/Library/NDI Advanced SDK for Apple/include 的别名/符号链接,而NDI_iOS_lib 指向/Library/NDI Advanced SDK for Apple/lib/iOS

我总是在更改Package.swift 后清理构建文件夹。

2022 年 10 月 1 日更新libndi_advanced_ios.a 需要 libc++.tbd。这可以在 Build Phases -> Link Binary With Libraries 中的应用中轻松链接,但我不知道如何在 Swift 包中链接。

【问题讨论】:

  • 看起来您可以使用linkerSettings 目标参数链接到 Swift 包中。

标签: ios swift static-libraries swift-package-manager swift-package


【解决方案1】:

二进制目标需要用.binary_target 指定。请参阅docs 和示例here

一个包裹在.xcframework 中的静态库的示例在文件命令中如下所示:

$ file GoogleAppMeasurement.xcframework/ios-arm64_armv7/GoogleAppMeasurement.framework/GoogleAppMeasurement
GoogleAppMeasurement.xcframework/ios-arm64_armv7/GoogleAppMeasurement.framework/GoogleAppMeasurement: Mach-O universal binary with 2 architectures: [arm_v7:current ar archive] [arm64]
GoogleAppMeasurement.xcframework/ios-arm64_armv7/GoogleAppMeasurement.framework/GoogleAppMeasurement (for architecture armv7):  current ar archive
GoogleAppMeasurement.xcframework/ios-arm64_armv7/GoogleAppMeasurement.framework/GoogleAppMeasurement (for architecture arm64):  current ar archive

创建.xcframework 文件的一种方法是使用Firebase 的ZipBuilder,它从使用CocoaPods podspec 文件指定的静态库中创建.xcframework 文件。

【讨论】:

  • .binary_target 在 SPM 中要求库是 XCFramework 或工件包,正如我上面写的,在这种情况下这是不可能的
  • 嗯,我没关注。 .xcframework 只是一个包含.frameworks 的目录结构。我的理解是任何.framework 都可以转换为.xcframework
  • 首先,我没有 .framework 库,而是 .a 静态库。其次,我尝试使用 xcodebuild -create-xcframework 创建 XCFramework,就像在 Apple Documentation 中一样,但是您不能从支持多个平台的库(如 libndi_advanced_ios.a)创建 XCFramework:XCFramework 仅捆绑为单个平台编译的二进制文件,并且由于该库是闭源库,我无法重新编译它以与 XCFramework 兼容。
  • 静态框架是一个包含静态库和头文件的目录结构。应该可以从带有文件系统命令的静态库创建一个静态框架,然后从带有其他文件系统命令的框架创建一个 xcframework。
  • 我尝试实施您的解决方案,但在作为本地 XCFramework 导入时出现“没有此类模块 'NDI_iOS'”错误。这是一个已知问题 (1),尽管它们在 Xcode 13.2 (77465707) 中标记为已解决。通过 URL 导入二进制文件时,我遇到了同样的问题。您可以在 alessionossa/swift-ndi in the dedicated branch 找到实现。因此,这不是一个可行的解决方案。
【解决方案2】:

我还需要在我的应用中添加 NDI SDK 作为 Swift 包依赖项。

我发现了几种可行的方法:

您可以通过从通用库中提取瘦 arm64 库来创建 XCFramework 包:

lipo "/Library/NDI SDK for Apple/lib/iOS/libndi_ios.a" -thin arm64 -output "$STAGING_DIRECTORY/libndi.a"

然后创建一个 XCFramework 包:

xcodebuild -create-xcframework -library "$STAGING_DIRECTORY/libndi.a" -headers "/Library/NDI SDK for Apple/include" -output "$STAGING_DIRECTORY/Clibndi.xcframework"

我最终没有使用这种方法,因为将 XCFramework 作为可下载的二进制版本托管在 GitHub 上需要我公开我的存储库(请参阅 this issue)。

相反,我在我的 Package.swift 中使用系统库目标:

    targets: [
        .target(
            name: "WrapperLibrary",
            dependencies: ["Clibndi"], 
            linkerSettings: [
                .linkedFramework("Accelerate"),
                .linkedFramework("VideoToolbox"),
                .linkedLibrary("c++")
            ]),
        .systemLibrary(name: "Clibndi")
    ]

然后,我的 WrapperLibrary/Sources/Clibndi/module.modulemap 看起来像:

module Clibndi {
  header "/Library/NDI SDK for Apple/include/Processing.NDI.Lib.h"
  link "ndi_ios"
  export *
}

最后,我的应用程序目标(Xcode 项目的一部分,不是 Swift 包)依赖于 WrapperLibrary,我必须将“/Library/NDI SDK for Apple/lib/iOS”(包括引号)添加到“ “构建设置”选项卡中的库搜索路径。

作为修改应用程序目标构建设置的替代方法,您可以将 pkg-config 文件添加到 pkg-config 搜索路径中的目录中。例如,/usr/local/lib/pkgconfig/libndi_ios.pc:

NDI_SDK_ROOT=/Library/NDI\ SDK\ for\ Apple

Name: NDI SDK for iOS
Description: The NDI SDK for iOS
Version: 5.1.1
Cflags: -I${NDI_SDK_ROOT}/include
Libs: -L${NDI_SDK_ROOT}/lib/iOS -lndi_ios

然后在你的包清单中使用.systemLibrary(name: "Clibndi", pkgconfig: "libndi_ios")。但是,我发现这对用户来说比将设置添加到我的应用程序目标更不方便。

理想情况下,您也可以将 NDI SDK 的依赖库和框架添加到 pkg-config 文件 (Libs: -L${NDI_SDK_ROOT}/lib/iOS -lndi_ios -lc++ -framework Accelerate -framework VideoToolbox),但似乎 Swift 对 -framework 参数的 pkg-config 解析存在错误,所以我提交了一个错误:SR-15933

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-01-10
    • 1970-01-01
    • 1970-01-01
    • 2018-04-15
    • 1970-01-01
    相关资源
    最近更新 更多