【问题标题】:How to compile C code into Swift Framework如何将 C 代码编译成 Swift 框架
【发布时间】:2016-07-08 19:57:50
【问题描述】:

我有一个 Swift 框架,我们将其编译并分发给第 3 方开发人员。我想在项目中包含一些 C 代码,但我能够使其工作的唯一方法是将 C 标头导入我的框架的标头并将 C 标头设置为公共。这会将所有 C 代码暴露给第 3 方开发人员,这不是我们想要的。

我可以在网上找到的有关如何执行此操作的所有内容都是此方法的变体:https://spin.atomicobject.com/2015/02/23/c-libraries-swift/

但此方法的警告是“请注意,您使用此技术构建的任何框架的使用者也必须将模块添加到他们的 Swift 搜索路径中。”我们已经尝试过了,果然我们的第 3 方开发人员收到了一个错误 Missing required module 'CGeodesic',这是我们定义的指向 C 标头的模块。

我们如何才能将 C 代码直接编译到我们的框架中,同时保持代码的私密性,并且不需要第 3 方开发人员更改他们的构建设置以使其工作?我不介意在我们的项目中设置这些子模块,但最终我希望它直接编译成一个单一的平面框架二进制文件,没有动态链接或搜索路径或类似的东西。

编辑


该项目是 C 代码与 Swift 代码并行。我的项目中有一个子目录,如下所示:

  • 测地线/
    • 测地线.h
    • Geodesic.c
    • Geodesic.swift

Swift 文件封装了 C 代码,使其更易于使用。我在该目录中也有一个 module.modulemap 文件,但就像我之前所说的那样,它不起作用。我更愿意将 C 代码与我的 Swift 代码并排保存,但我愿意放弃它以解决这个问题。

【问题讨论】:

  • 您当前的设置是什么? C 代码与 Swift 代码并列?
  • developer.apple.com/library/ios/documentation/Swift/Conceptual/…。这有帮助吗? C 代码是 Objective-C,看起来可以用 C 和 Swift 混合编写一个框架,并且在用户看来就像是用单一语言编写的一样。我自己还没试过。
  • @OmniProg 这就是我们目前正在做的事情。该方法的问题在于它必须公开所有这些标头,这是我们试图避免的。

标签: c xcode swift llvm ios-frameworks


【解决方案1】:

只是为了改写您的问题:您基本上想在 Swift 中包装 C 代码并防止直接访问封装的 C 代码。这意味着要阻止 Swift 和 Objective-C 的访问。

这是可能的,但需要对您的构建过程和一些后期处理进行一些调整。

框架中的 C 和 Swift 互操作

您想在 Swift 中包装的任何 C 代码都需要在编译时通过伞形头文件或使用模块映射公开。这里的重要部分:在编译期间。框架没有完整性检查,这意味着您可以对它们应用后期处理(例如,使用lipo 创建一个胖二进制框架)。这就是我们需要在这里做的。

准备构建

您也可以将它们放入module.modulemap 文件中,而不是将所有C 头文件依赖项放入伞头文件中。这有一个优势:您可以在 Headers 构建阶段将您的标头设为私有。

因此,在您的项目中创建一个module.modulemap 文件,并在Packaging 部分(xcconfig 文件中的MODULEMAP_FILE)的Module Map File 构建设置中设置它的路径,例如$(SRCROOT)/MyFramework/module.modulemap

您的module.modulemap 文件应包含以下内容:

# module.modulemap

framework module MyFramework {

    umbrella header "MyFramework.h"  

    export *
    module * {export *}

    # All C Files you want to wrap in your Swift code

    header "A.h"
    header "B.h"
}

到目前为止一切顺利。您现在应该能够构建您的代码并从 Swift 和 Objective-C 访问它。唯一的问题:你仍然可以在 Swift 和 Objective-C 中访问你的 C 头文件。

后处理

如果您查看最终的框架包,您会注意到您的所有私有头文件都已复制到MyFramework.framework/PrivateHeaders 文件夹。这意味着您仍然可以在 Objective-C 中通过 #import <MyFramework/A.h> 访问它们。

在 Swift 中,您仍然可以访问 C 代码,因为我们将头文件放在已复制到 MyFramework.framework/Modules/module.modulemapmodule.modulemap 文件中。

幸运的是,我们可以通过一些后期处理来解决这两个问题:

  1. 从框架包中删除 PrivateHeaders 文件夹。
  2. 创建一个单独的模块映射并删除所有header "XYZ.h" 语句,例如,将其命名为public.modulemap
  3. 将所有这些都放在 Run Script 构建阶段

这是公共模块图:

# public.modulemap

framework module MyFramework {

    umbrella header "MyFramework.h"  

    export *
    module * {export *}

    # No more C Headers here
}

以及应该添加到框架构建阶段末尾的运行脚本:

# Delete PrivateHeaders folder
rm -rf ${TARGET_BUILD_DIR}/${PRODUCT_NAME}${WRAPPER_SUFFIX}/PrivateHeaders

# Remove module.modulemap file
rm ${TARGET_BUILD_DIR}/${PRODUCT_NAME}${WRAPPER_SUFFIX}/Modules/module.modulemap

# Copy public.modulemap file and rename it to module.modulemap
cp ${SRCROOT}/test/public.modulemap ${TARGET_BUILD_DIR}/${PRODUCT_NAME}${WRAPPER_SUFFIX}/Modules/module.modulemap

# Append the Swift module so you can access you Swift code in Objective-C via @import MyFramework.Swift
echo "module ${PRODUCT_NAME}.Swift { header \"${PRODUCT_NAME}-Swift.h\" }" >> ${TARGET_BUILD_DIR}/${PRODUCT_NAME}${WRAPPER_SUFFIX}/Modules/module.modulemap

希望有帮助!

【讨论】:

  • 完美运行!谢谢!
  • 这个答案当然令人难以置信,@JensMeder,谢谢。一件事——也许您可以添加一个更简单的案例的简短附录,其中根本不需要来防止直接访问。这可能会澄清答案。我敢打赌这对很多人都有帮助。 (www 上所有关于此的旧文章都随着时间的推移而消失了!:))
  • 在 Xcode 11 下,我遇到了 Redefinition of module 构建错误。将模块映射文件命名为 MyFramework.modulemap 而不是 module.modulemap 克服了这个问题。我还必须确保我的 c 头文件在我的框架的 Headers Build Phase 中被列为 Private 并且我的伞头文件被列为 Public 以避免 Umbrella header not found 构建错误并启用自动完成。
  • 感谢您的回答。我发现的一个问题(至少对我而言)是,在 C 标头上定义的任何函数都会在您使用框架时 cmd+单击模块名称时出现。知道如何防止这种情况发生吗?
【解决方案2】:

有一个解决方案,虽然不推荐。您可以在 .Swift 文件中使用 use @_silgen_name 而不是使用 C 标头。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2018-07-02
    • 2018-04-15
    • 1970-01-01
    • 1970-01-01
    • 2021-11-03
    • 1970-01-01
    • 1970-01-01
    • 2020-01-22
    相关资源
    最近更新 更多