C++ system library - Classes undefined

I am trying to make bindings to libcamera using SPM systemLibrary. But when I try to use it, swift cannot find the classes defined in C++.

Project structure:

├── Package.swift
├── Sources
    ├── examples
    │   └── main.swift
    └── libcamera
        ├── libcamera.hpp
        └── module.modulemap

Package.swift:

// swift-tools-version: 5.9

import PackageDescription

let package = Package(
    name: "libcamera",
    products: [
        .library(
            name: "libcamera",
            targets: ["libcamera"]),
    ],
    dependencies: [
    ],
    targets: [
        .systemLibrary(
            name: "libcamera",
            pkgConfig: "libcamera"
        ),
        .executableTarget(
            name: "examples",
            dependencies: ["libcamera"],
            swiftSettings: [.interoperabilityMode(.Cxx)]
        )
    ]
)

libcamera.hpp:

#include <libcamera/libcamera.h>

module.modulemap:

module libcamera {
  umbrella header "libcamera.hpp"
  link "camera"
}

main.swift:

import libcamera

let cameraManager = libcamera.CameraManager()
let cameras = cameraManager.cameras()
let cameraSelected = 0
let cam = cameras.get(cameraSelected)

print("Using camera:", cam.properties())

When I run:

swift run -Xcc -std=gnu++20

The output is:

building for debugging...
error: emit-module command failed with exit code 1 (use -v to see invocation)
/app/Sources/examples/main.swift:3:31: error: type 'libcamera' has no member 'CameraManager'
let cameraManager = libcamera.CameraManager()
                    ~~~~~~~~~ ^~~~~~~~~~~~~
/app/Sources/examples/main.swift:3:31: error: type 'libcamera' has no member 'CameraManager'
let cameraManager = libcamera.CameraManager()
                    ~~~~~~~~~ ^~~~~~~~~~~~~
error: fatalError

libcamera is a namespace defined in C++, so accesssing CameraManager would be like this in C++: libcamera::CameraManager.

using swift run -Xcc -std=gnu++20 -v
warning: 'app': /usr/bin/swift-frontend -frontend -c -primary-file /app/Package.swift -target aarch64-unknown-linux-gnu -Xllvm -aarch64-use-tbi -disable-objc-interop -I /usr/lib/swift/pm/ManifestAPI -vfsoverlay /tmp/TemporaryDirectory.FGg31O/vfs.yaml -swift-version 5 -package-description-version 5.9.0 -new-driver-path /usr/bin/swift-driver -disable-implicit-concurrency-module-import -disable-implicit-string-processing-module-import -empty-abi-descriptor -resource-dir /usr/lib/swift -module-name main -plugin-path /usr/lib/swift/host/plugins -plugin-path /usr/local/lib/swift/host/plugins -o /tmp/TemporaryDirectory.MxD2Za/Package-1.o
/usr/bin/swift-autolink-extract /tmp/TemporaryDirectory.MxD2Za/Package-1.o -o /tmp/TemporaryDirectory.MxD2Za/main-1.autolink
/usr/bin/clang -fuse-ld=gold -pie -Xlinker -rpath -Xlinker /usr/lib/swift/linux /usr/lib/swift/linux/aarch64/swiftrt.o /tmp/TemporaryDirectory.MxD2Za/Package-1.o @/tmp/TemporaryDirectory.MxD2Za/main-1.autolink -L /usr/lib/swift/linux -lswiftCore --target=aarch64-unknown-linux-gnu -v -L /usr/lib/swift/pm/ManifestAPI -lPackageDescription -Xlinker -rpath -Xlinker /usr/lib/swift/pm/ManifestAPI -o /tmp/TemporaryDirectory.D9hPGN/app-manifest
Swift version 5.9.2 (swift-5.9.2-RELEASE)
Target: aarch64-unknown-linux-gnu
clang version 13.0.0 (https://github.com/apple/llvm-project.git 2b42c5ce063a374fb22676e27505a22fe411ea8c)
Target: aarch64-unknown-linux-gnu
Thread model: posix
InstalledDir: /usr/bin
Found candidate GCC installation: /usr/bin/../lib/gcc/aarch64-linux-gnu/11
Found candidate GCC installation: /usr/bin/../lib/gcc/aarch64-linux-gnu/12
Selected GCC installation: /usr/bin/../lib/gcc/aarch64-linux-gnu/12
Candidate multilib: .;@m64
Selected multilib: .;@m64
 "/usr/bin/ld.gold" -pie -EL --hash-style=gnu --eh-frame-hdr -m aarch64linux -dynamic-linker /lib/ld-linux-aarch64.so.1 -o /tmp/TemporaryDirectory.D9hPGN/app-manifest /lib/aarch64-linux-gnu/Scrt1.o /lib/aarch64-linux-gnu/crti.o /usr/bin/../lib/gcc/aarch64-linux-gnu/12/crtbeginS.o -L/usr/lib/swift/linux -L/usr/lib/swift/pm/ManifestAPI -L/usr/bin/../lib/gcc/aarch64-linux-gnu/12 -L/lib/aarch64-linux-gnu -L/usr/lib/aarch64-linux-gnu -L/lib -L/usr/lib -rpath /usr/lib/swift/linux /usr/lib/swift/linux/aarch64/swiftrt.o /tmp/TemporaryDirectory.MxD2Za/Package-1.o -lswiftSwiftOnoneSupport -lswiftCore -lswiftCore -lPackageDescription -rpath /usr/lib/swift/pm/ManifestAPI -lgcc --as-needed -lgcc_s --no-as-needed -lc -lgcc --as-needed -lgcc_s --no-as-needed /usr/bin/../lib/gcc/aarch64-linux-gnu/12/crtendS.o /lib/aarch64-linux-gnu/crtn.o
Planning build
Building for debugging...
/usr/bin/swiftc -module-name examples -emit-dependencies -emit-module -emit-module-path /app/.build/aarch64-unknown-linux-gnu/debug/examples.swiftmodule -output-file-map /app/.build/aarch64-unknown-linux-gnu/debug/examples.build/output-file-map.json -incremental -c /app/Sources/examples/main.swift -I /app/.build/aarch64-unknown-linux-gnu/debug -target aarch64-unknown-linux-gnu -swift-version 5 -v -enable-batch-mode -index-store-path /app/.build/aarch64-unknown-linux-gnu/debug/index/store -Onone -enable-testing -g -j4 -DSWIFT_PACKAGE -DDEBUG -Xcc -fmodule-map-file=/app/Sources/libcamera/module.modulemap -I/usr/local/include/libcamera -module-cache-path /app/.build/aarch64-unknown-linux-gnu/debug/ModuleCache -parseable-output -Xfrontend -entry-point-function-name -Xfrontend examples_main -color-diagnostics -cxx-interoperability-mode=default -Xcc -fPIC -Xcc -std=gnu++20 -package-name app -Xcc -fno-omit-frame-pointer
Swift version 5.9.2 (swift-5.9.2-RELEASE)
Target: aarch64-unknown-linux-gnu
/usr/bin/swift-frontend -frontend -emit-module -experimental-skip-non-inlinable-function-bodies-without-types /app/Sources/examples/main.swift -target aarch64-unknown-linux-gnu -Xllvm -aarch64-use-tbi -disable-objc-interop -cxx-interoperability-mode=default -I /app/.build/aarch64-unknown-linux-gnu/debug -I /usr/local/include/libcamera -color-diagnostics -enable-testing -g -module-cache-path /app/.build/aarch64-unknown-linux-gnu/debug/ModuleCache -swift-version 5 -Onone -D SWIFT_PACKAGE -D DEBUG -new-driver-path /usr/bin/swift-driver -entry-point-function-name examples_main -empty-abi-descriptor -resource-dir /usr/lib/swift -enable-anonymous-context-mangled-names -Xcc -fmodule-map-file=/app/Sources/libcamera/module.modulemap -Xcc -fPIC -Xcc -std=gnu++20 -Xcc -fno-omit-frame-pointer -module-name examples -package-name app -plugin-path /usr/lib/swift/host/plugins -plugin-path /usr/local/lib/swift/host/plugins -emit-module-doc-path /app/.build/aarch64-unknown-linux-gnu/debug/examples.swiftdoc -emit-module-source-info-path /app/.build/aarch64-unknown-linux-gnu/debug/examples.swiftsourceinfo -emit-dependencies-path /app/.build/aarch64-unknown-linux-gnu/debug/examples.build/examples.emit-module.d -o /app/.build/aarch64-unknown-linux-gnu/debug/examples.swiftmodule
/usr/bin/swift-frontend -frontend -c -primary-file /app/Sources/examples/main.swift -emit-dependencies-path /app/.build/aarch64-unknown-linux-gnu/debug/examples.build/main.d -emit-reference-dependencies-path /app/.build/aarch64-unknown-linux-gnu/debug/examples.build/main.swiftdeps -target aarch64-unknown-linux-gnu -Xllvm -aarch64-use-tbi -disable-objc-interop -cxx-interoperability-mode=default -I /app/.build/aarch64-unknown-linux-gnu/debug -I /usr/local/include/libcamera -color-diagnostics -enable-testing -g -module-cache-path /app/.build/aarch64-unknown-linux-gnu/debug/ModuleCache -swift-version 5 -Onone -D SWIFT_PACKAGE -D DEBUG -new-driver-path /usr/bin/swift-driver -entry-point-function-name examples_main -empty-abi-descriptor -resource-dir /usr/lib/swift -enable-anonymous-context-mangled-names -Xcc -fmodule-map-file=/app/Sources/libcamera/module.modulemap -Xcc -fPIC -Xcc -std=gnu++20 -Xcc -fno-omit-frame-pointer -module-name examples -package-name app -plugin-path /usr/lib/swift/host/plugins -plugin-path /usr/local/lib/swift/host/plugins -o /app/.build/aarch64-unknown-linux-gnu/debug/examples.build/main.swift.o -index-store-path /app/.build/aarch64-unknown-linux-gnu/debug/index/store -index-system-modules
error: emit-module command failed with exit code 1 (use -v to see invocation)
/app/Sources/examples/main.swift:3:31: error: type 'libcamera' has no member 'CameraManager'
let cameraManager = libcamera.CameraManager()
                    ~~~~~~~~~ ^~~~~~~~~~~~~
/app/Sources/examples/main.swift:3:31: error: type 'libcamera' has no member 'CameraManager'
let cameraManager = libcamera.CameraManager()
                    ~~~~~~~~~ ^~~~~~~~~~~~~
error: fatalError

Could you please try to rename the module in module.modulemap from libcamera to something else? I suspect Swift might be getting confused by the module libcamera and the namespace libcamera having the same name.

Thank you for your reply. Here's what I did:

mv Sources/libcamera/ Sources/libcamera_cp

In Package.swift:

        .systemLibrary(
-           name: "libcamera",
+           name: "libcamera_cpp",
            pkgConfig: "libcamera"
        ),
        .executableTarget(
            name: "examples",
-           dependencies: ["libcamera"],
+           dependencies: ["libcamera_cpp"],
            swiftSettings: [.interoperabilityMode(.Cxx)]
        )

In module.modulemap:

- module libcamera {
+ module libcamera_cpp {
  umbrella header "libcamera.hpp"
  link "camera"
}

In main.swift:

- import libcamera
+ import libcamera_cpp

I then ran again and got the same output. So no luck with this so far.

Are you able to use any other API from libcamera from Swift?
I'm curious if the issue is specific to CameraManager type, or if it happens with any type or function from libcamera.

Can you upload it to any repo so we could help you out

Sorry, I think I don't have email notifications enabled. Here's the repo though:

You should be able to build it through docker/podman

docker build -t build-libcamera .
docker run --rm -v.:/libcamera-swift -w/libcamera-swift build-libcamera swift build -Xcc -std=gnu++20

As for @egor.zhdan's question, I tried a function defined in libcamera, but also got a not defined error.

Based on the problem you stated above

building for debugging...
error: emit-module command failed with exit code 1 (use -v to see invocation)
/app/Sources/examples/main.swift:3:31: error: type 'libcamera' has no member 'CameraManager'
let cameraManager = libcamera.CameraManager()
                    ~~~~~~~~~ ^~~~~~~~~~~~~
/app/Sources/examples/main.swift:3:31: error: type 'libcamera' has no member 'CameraManager'
let cameraManager = libcamera.CameraManager()
                    ~~~~~~~~~ ^~~~~~~~~~~~~
error: fatalError

The issue here is that the type CameraManager cannot not be directly imported into swift because it's copy constructor is deleted ie it is a c++ move only type.

My suggestions for you are either you create a shim type in c++ for bridging it into swift

#include "swift/bridging" 

struct CameraManagerShim { 
private: 
    CameraManager manager; 
public: 
     static CameraManagerShim *create() {} // This type static initializer which swift uses
     CameraManagerShim();
     ~CameraManagerShim();
     // forward all CameraManager type methods through to the manager data member;
} SWIFT_UNSAFE_REFERENCE(retain_ref, release_ref); // This ensures that is imported as class in swift


// Necessary reference counting semantic implementations
void retain_ref(CameraManagerShim *ptr) {}

void release_ref(CameraManagerShim *ptr) {}

Or wait until swift official supports c++ move only types.

Another thing to note is that this libcamera heavily rely on smart pointers which are not fully supported in swift.