我有一个 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
的文件.
幸运的是,我们可以通过一些后期处理来解决这两个问题:
- 删除
PrivateHeaders
框架包中的文件夹。 - 创建一个单独的模块映射并删除所有
header "XYZ.h"
语句,例如,将其命名为public.modulemap
. - 将所有这些放在
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/