Header Search Path Issues and C++ Interop

Hi. I have some c++ code that uses some embedded (copied over) files from another open source project (Asio). I have previously managed to make it compile as a standalone CMake library using c++ only (c++20).

Now I am trying to use SPM (Swift 6.0) to build a mixed language project, but it can never find my asio.hpp when I run swift build. Here is a link to the repository: GitHub - RussBaz/lumengine

Can anyone please advise how to properly set up my Package.swift file for this case? I am pretty sure it is related to the headerSearchPath, but I can never find the correct path.

Thank you!

I have progressed a bit further. By adding a public header path and a .cpp file and runing swift build, I am now getting different errors. Such as:

/Users/user/Projects/GitHub/lumengine/Sources/cxxLumengine/asio/include/asio/detail/io_uring_null_buffers_op.hpp:32:41: error: expected class name
 30 |
 31 | template <typename Handler, typename IoExecutor>
 32 | class io_uring_null_buffers_op : public io_uring_operation
    |                                         `- error: expected class name
 33 | {
 34 | public:

These errors are not part of my code, though.

However, this is not the strangest part because I managed to compile the project myself using clang++ and swiftc directly! Here are the two commands that I used successfully (from the project root):

clang++ -I./Sources/cxxLumengine/asio/include -I./Sources/cxxLumengine/include -D ASIO_STANDALONE -std=c++20 -c ./Sources/cxxLumengine/cxxLumengine.cpp -o output.o

swiftc -I ./Sources/cxxLumengine/include -Xcc -I./Sources/cxxLumengine/asio/include -D ASIO_STANDALONE -Xcc -std=c++20 -cxx-interoperability-mode=default -o lumengine.dylib -emit-library ./Sources/lumengine/lumengine.swift ./output.o

I don't think you've included your actual sources. I feel your pain - I had to wrap a complicated library with near zero C++ skills.

I found I needed to be absolutely explicit about which C and C++ sources needed to be included. Which is a useful feature I suppose, if only a subset needs to be used.

It seems that compiler knows to look in a folder with the same name as the target for the sources, so these can be relative to that folder.

Your target needs to look something like this:

        .target(
            name: "CLibrary",
            sources: [
              "Implementation.cpp", // This is relative to the CLibrary folder
...
            ],
            cxxSettings: [
              .headerSearchPath("include"),
            ]
        ),

Thank you for your suggestions, but in the end, thankfully, I did not have to manually enumerate every file (fixed it only today). Here is how I structured my Package.swift:

// swift-tools-version: 6.0

import PackageDescription

let package = Package(
    name: "lumengine",
    products: [
        .library(
            name: "lumengine",
            targets: ["lumengine"]
        ),
    ],
    targets: [
        // Header only dependency Asio (Non-Boost)
        // Repository: https://github.com/chriskohlhoff/asio
        // Commit SHA: 4730c543ba2b8f9396ef1964a25ccc26eb1aea64
        .target(
            name: "cxxAsio",
            cxxSettings: [
                .define("ASIO_STANDALONE"),
            ]
        ),
        // Custom CPP server implementation that uses Asio under the hood
        .target(
            name: "cxxLumengine",
            dependencies: [
                "cxxAsio",
            ]
        ),
        .target(
            name: "lumengine",
            dependencies: [
                "cxxLumengine",
            ],
            swiftSettings: [
                .interoperabilityMode(.Cxx),
            ]
        ),
        .testTarget(
            name: "lumengineTests",
            dependencies: ["lumengine"],
            swiftSettings: [
                .interoperabilityMode(.Cxx),
            ]
        ),
    ],
    cxxLanguageStandard: .cxx20
)

I moved my header only C++ dependency into a separate target, and it finally worked!