我如何使用 Swift Pacakge Manager 从 C 语言目标内部 link 到系统模块?

How do I link to a system module from inside a C language target with Swift Pacakge Manager?

我想在 Swift 中实现 Bullet 物理的 C 包装器。基本上我的计划是实现一个 C 语言目标,它包装了我在 bullet 中使用的必要功能,并在我的 swift 代码中依赖于这个 C 目标。问题是,我无法从 C 语言目标中找到项目符号 headers。

我最初的方法是创建 3 个由 Swift 包管理器管理的模块:一个系统模块(项目符号)、一个 C 语言目标(包装器)和一个 Swift 模块(客户端代码)。

我的系统模块是这样的:

Package.swift:

// swift-tools-version:3.1

import PackageDescription

let package = Package(
    name: "Cbullet",
    pkgConfig: "bullet",
    providers: [
        .Brew("bullet")
    ]
)

module.modulemap:

module ClibBullet [system] {
  header "/usr/local/Cellar/bullet/2.86.1_1/include/bullet/btBulletCollisionCommon.h"
  header "/usr/local/Cellar/bullet/2.86.1_1/include/bullet/btBulletDynamicsCommon.h"
  link "bullet"
  export *
}

另外两个模块属于同一个包:

Package.swift

// swift-tools-version:4.0
// The swift-tools-version declares the minimum version of Swift required to build this package.

import PackageDescription

func localModulesPath(_ moduleName: String) -> String {
    return "../\(moduleName)"
}

let package = Package(
    name: "Physics",
    products: [
        .library(
            name: "Physics",
            targets: ["Physics"]),

    ],
    dependencies: [
        .package(url: localModulesPath("Cbullet"), .branch("master")),
    ],
    targets: [
        .target(
            name: "CBulletWrapper",
            dependencies: []),
        .target(
            name: "Physics",
            dependencies: ["CBulletWrapper"]),
    ]
)

在我的 CBulletWrapper.cpp 实现文件中,我尝试像这样包含 Cbullet headers:

#include <Cbullet/btBulletCollisionCommon.h>
#include <Cbullet/btBulletDynamicsCommon.h>

但是我得到一个错误:

"btBulletCollisionCommon.h" file not found

我也试过从它们的显式路径中包含项目符号 headers:

#include "/usr/local/Cellar/bullet/2.86.1_1/include/bullet/btBulletDynamicsCommon.h"

但是 btBulletDynamicsCommon.h 中的所有导入都出现 file not found 错误(例如 #include "LinearMath/btVector3.h"

所以问题是,从 Swift Pacakage Manager 构建的 C 语言目标内部 link 到系统库的正确方法是什么?

查看 "Proposed Solution" 下的 Swift Package Manager C Language Target Support Proposal:

  1. If the include directory includes any file named "module.modulemap", then those will be presumed to define the modules for the target, and no module maps will be synthesized.

我认为你最好的选择是:

  1. 在你的C语言目标源文件夹的include文件夹中添加一个module.modulemap文件( CBulletWrapper),其中包括引用系统 (Brew) 提供的库所必需的 headerlink 语句。可能是这样的:

    module CBulletWrapper [system] {
        // This is the umbrella header (also in your "include" folder) for your existing wrapper implementation code which itself includes the "bullet" header files
        header "CBulletWrapper.h"
    
        //Link to the "bullet" library file in the Brew location (absolute path)
        link "/full/path/to/brew/library/libbullet.a"
    
        //Re-Export all included modules to packages that include this "CBulletWrapper" module 
        export *
    }
    
  2. 完全忽略现有的外部系统模块 (Cbullet) 依赖项(在 "Physics" 包的 Package.swift 文件中)

注意:据我所知,无法从 "Cbullet" 系统模块包的 Package.swift[=35 中实现 "pkgConfig"、"providers" 声明=] 在您的 "Physics" 模块的 Package.swift 文件中。这个问题似乎在以下提案中得到解决:Package Manager System Library Targets.

我最近用这种方法运气不错。此外,您还节省了维护外部系统模块映射包存储库的额外工作(如果它不与其他项目共享)。如果您稍后需要 link 将额外的外部 C 库添加到主 "Physics" 包中,这将使开发变得更加容易:只需添加另一个 C 语言目标及其自己的 module.modulemap 和 thin/thick 包装器代码。