Swift Package Manager C-interop: Non-system libraries

后端 未结 1 1243
梦如初夏
梦如初夏 2021-02-04 13:17

How can I use the Swift Package Manager to include C code (in my case, a single .c file and a header file) without requiring the user to install my C libra

相关标签:
1条回答
  • 2021-02-04 14:03

    I have done that in this project on github. It replaces pthread_once_t by wrapping it in C and re-exposing it to swift. It was done as a fun exercise in getting around what Swift tries to limit you into since pthread_once_t and dispatch_once are not available directly.

    Here is a trimmed down version the Package.swift file:

    // swift-tools-version:4.0
    
    import PackageDescription
    
    let package = Package(
        name: "Once",
        products: [
            .library(
                name: "Once",
                targets: ["OnceC", "Once"]),
        ],
        dependencies: [
        ],
        targets: [
            .target(
                name: "OnceC",
                dependencies: [],
                path: "Sources/OnceC"),
            .target(
                name: "Once",
                dependencies: ["OnceC"],
                path: "Sources/Swift"),
            .testTarget(
                name: "OnceTests",
                dependencies: ["Once"]),
            ]
    )
    

    You can easily replace the product library with an executable. The main part is that the product's targets needs to contain both the C and Swift targets needed to build.

    Then in your targets section make the swift target lists the C target as a dependency.


    You can learn more about the required layout for C targets in the SwiftPM Usage.md here

    C language targets

    The C language targets are similar to Swift targets except that the C language libraries should contain a directory named include to hold the public headers.

    To allow a Swift target to import a C language target, add a target dependency in the manifest file. Swift Package Manager will automatically generate a modulemap for each C language library target for these 3 cases:

    • If include/Foo/Foo.h exists and Foo is the only directory under the include directory then include/Foo/Foo.h becomes the umbrella header.

    • If include/Foo.h exists and include contains no other subdirectory then include/Foo.h becomes the umbrella header.

    • Otherwise if the include directory only contains header files and no other subdirectory, it becomes the umbrella directory.

    In case of complicated include layouts, a custom module.modulemap can be provided inside include. SwiftPM will error out if it can not generate a modulemap w.r.t the above rules.

    For executable targets, only one valid C language main file is allowed i.e. it is invalid to have main.c and main.cpp in the same target.


    The only other important thing is how you actually do your #import in the C code once it is compiled as a compatible module. If you use the import/Foo/Foo.h organization you need to use #include <Foo/Foo.h> and if you do import/Foo.h you can use #import "Foo.h".

    0 讨论(0)
提交回复
热议问题