c - 如何将 C 代码编译成 Swift Framework

标签 c xcode swift llvm ios-frameworks

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

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

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

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

编辑


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

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

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

最佳答案

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

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

框架中的 C 和 Swift 互操作

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

准备构建

除了将所有 C 头文件依赖项放入伞头文件之外,您还可以将它们放入 module.modulemap 中。文件。这有一个好处:您可以在 Headers 中将 header 设为私有(private)。构建阶段。

因此,创建一个 module.modulemap项目中的文件并在 Module Map File 中设置它的路径Packaging 中的build设置部分(xcconfig 文件中的 MODULEMAP_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 头文件。

后处理

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

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

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

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

这是公共(public)模块图:

# 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

希望对您有所帮助!

关于c - 如何将 C 代码编译成 Swift Framework,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38274484/

相关文章:

c++ - 为什么像/* */need '<' 这样的 C 注释?

c - 使用 printf 的可变长度空间

ios - "Expression are not allowed at top level"在 Swift 类的函数调用中

ios - 我向下转换了我的变量,但我无法调用新类的函数

ios - 缩放后为不同的标签设置相同的字体大小

let (hello, world) :(String, String) = ("hello","world") 的 Swift 语法

c - Postgresql C 如何将 uint64_t 转换为数字?

c - 竞争条件场景

ios - 应用程序仅在真实设备上启动时崩溃

iphone - 应用首次加载时如何显示视频?