Search code examples
swiftswift4swift-package-manager

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


I want to implement a C wrapper for Bullet physics in Swift. Basically my plan is to implement a C language target which wraps the necessary functionality I am using in bullet, and to depend on this C target in my swift code. The problem is, I can't get the bullet headers to be found from within the C language target.

My initial approach has been to create 3 modules managed by Swift Package Manager: a system module (bullet), a C language target (the wrapper) and a Swift module (the client code).

My system module looks like this:

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 *
}

The other two modules are part of the same package:

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"]),
    ]
)

From inside my CBulletWrapper.cpp implementation file, I try to include the Cbullet headers like so:

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

But I get an error:

"btBulletCollisionCommon.h" file not found

I have also tried including the bullet headers from their explicit path:

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

But then I get file not found errors on all the imports within btBulletDynamicsCommon.h (for example #include "LinearMath/btVector3.h"

So the question is, what is the correct way to link to a system library from inside a C language target built by Swift Pacakage Manager?


Solution

  • Check out the Swift Package Manager C Language Target Support Proposal under "Proposed Solution":

    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.

    I think your best bet is to:

    1. Add a module.modulemap file in the include folder of your C language target source folder (CBulletWrapper) that includes the header and link statements necessary to reference the system (Brew) provided library. Maybe something like:

      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. Omit your existing external system module (Cbullet) dependency altogether (in your "Physics" package's Package.swift file)

    Note: AFAIK there is no way to implement the "pkgConfig", "providers" declarations from the "Cbullet" system module package's Package.swift in your "Physics" module's Package.swift file. It appears that this issue is being addressed in the following proposal: Package Manager System Library Targets.

    I've had some luck recently with this approach. Also, you save the additional effort of maintaining an external system module map package repository (if its not going to be shared with other projects). This makes development MUCH easier if you need to link additional external C libraries to your main "Physics" package later on: Just add another C language target with its own module.modulemap and thin/thick wrapper code.