Nested dependencies with Swift Packages and xcframeworks

I'm writing an macos app where the main xcproj depends on some swift packages. Some of these packages depend on other packages. Some of these packages provide xcframeworks for 3rd party dependencies.

I'm at a point where packages are compiling in isolation, but I get dependency related errors when integrating into the app.

I use a number of 3rd party libraries: Ceres, Eigen, gflags, glog, OpenCV. The flow of dependencies looks like this:

App -> OpenCV
App -> Eigen
App -> Ceres (which internally depends on headers from Eigen, gflags, glog)
App -> Custom Package -> Eigen, gflags, glog, OpenCV

I have Packages for each of these 3rd party libraries that provides them as an xcframework, with the exception of Eigen which is consumed as source (either by the App via build settings that point to include files within the project, or by my Custom Package via git submodule)

When I compile the App it has trouble building each dependency. I can import them fine, but at compile time I get errors from within the dependencies themselves (ie compiles errors inside Eigen, or inside OpenCV, etc)

Some questions:

  1. High level - Is my intended structure possible with swift packages? or am I attempting something that's not supported?
  2. Low level - if it is possible, can someone point me in the right direction to get my dependencies linked properly so they compile. Any build settings in XCode or flags to set in my Package.swift files?

Environment:

  • macOS 12.6
  • XCode 14.2
  • App target 12.3

Libraries

3 Likes

I have the same issue at my org and would like to know :crying_cat_face:

Having just spent a week trying to resolve some similar problems, I finally solved this issue. I'm using EIgen, Ceres and other open source and in housecpp libraries which have a complex dependency structure.

I used Conan to resolve the all dependencies beforehand and build the libraries using a single package for all my libraries, which should be output in a flat structure, all libs in the same directory.

Each library is wrapped into xcframeworks and placed in a Binaries directory. I had to do some scripting to get headers imported correctly to the xcframeworks that needed them.

I create a swift package that has a binary target, and a published product library for each, so there's no dependency handling in SPM at this point.

Something like:

let package = Package(
    name: "CPP-Libraries",
    products: [
        .library(name: "libceres", targets: [ "libceres" ] ),
        ...
        .library(name: "Eigen", targets: [ "Eigen" ] ),
    ],
    targets: [
        .binaryTarget(name: "libceres", path: "Binaries/libceres.xcframework"),
        ...
        .target(
            name: "Eigen",
            cxxSettings: [
                .headerSearchPath("Sources/Eigen/include/Eigen")
            ]
        ),
    ],
    cxxLanguageStandard: .cxx17
)

Eigen I found a bit problematic as it was a header only file, and SPM didn't like it as is. It needs it's own target, with headerSearchPath in cxxsettings set, plus adding an Eigen.cpp file which just includes the Eigen Umbrella header, and I provided a module.modulemap file that also specified the umbrella header and exported it all.

Now in seperate higher level packages, which represent the top level bridge code of the libraries/modules I want to use, I add all the original dependencies of the libraries using product(name: "libceres", package: "CPP-Libraries") . the hierarchy doesn't matter