[solved] Swift package that imports both C and C++ into Swift module fails to compile

I have a Swift package that has

  • two targets with C++ code (cpp, cppbridge),
  • one target with C code (lz4),
  • and a target with Swift code (CompositorCore).

Swift package file (redacted):

// swift-tools-version: 6.0

import PackageDescription

let package = Package(
    name: "CompositorCore",
    platforms: [.macOS(.v10_15)],
    products: [
        .library(
            name: "CompositorCore",
            type: .dynamic,
            targets: ["CompositorCore"])
    ],
    dependencies: [
        ...
    ],
    targets: [
        .target(
            name: "cpp",
            swiftSettings: [.interoperabilityMode(.Cxx)]
        ),
        .target(
            name: "cppbridge",
            dependencies: ["cpp"],
            swiftSettings: [.interoperabilityMode(.Cxx)]
        ),
        .target(
            name: "lz4"
        ),
        .target(
            name: "CompositorCore",
            dependencies: ["cppbridge", "lz4"],
            resources: [
                ...
            ],
            swiftSettings: [.interoperabilityMode(.Cxx)]
        ),
        .testTarget(
            name: "CompositorCoreTests",
            ...
        ),
    ],
    swiftLanguageModes: [.v5],
    cxxLanguageStandard: .cxx17
)

Source tree layout:

Sources
β”œβ”€β”€ CompositorCore
...
β”œβ”€β”€ cpp
β”‚   └── include
β”‚       β”œβ”€β”€ cpp.h
β”‚       └── tex.hpp
β”œβ”€β”€ cppbridge
β”‚   β”œβ”€β”€ TeXBridge.cpp
β”‚   β”œβ”€β”€ TexTypes.cpp
β”‚   └── include
β”‚       β”œβ”€β”€ TeXBridge.h
β”‚       β”œβ”€β”€ TeXErrors.h
β”‚       β”œβ”€β”€ TeXTypes.h
β”‚       └── cppbridge.h
└── lz4
    β”œβ”€β”€ include
    β”‚   └── lz4.h
    └── lz4.c

The idea with the lz4 module is to bundle an LZ4 implementation, so that the package is self-contained and works for both macOS and Windows. I was previously using Apple's Compression framework from one of the C++ files.

In want to use the C/C++ modules in Swift like so:

import CxxStdlib
import Foundation

import cppbridge
import lz4

...

When compiling the package, I'm getting

karl@10 Core % swift build -Xswiftc -suppress-warnings
Building for debugging...
<module-includes>:1:9: note: in file included from <module-includes>:1:
1 | #import "/Users/karl/Documents/sandbox/apps/compositor/Sources/Core/Sources/lz4/include/lz4.h"
  |         `- note: in file included from <module-includes>:1:
2 | 

/Users/karl/Documents/sandbox/apps/compositor/Sources/Core/Sources/lz4/include/lz4.h:661:1: error: import of C++ module 'std_stdint_h' appears within extern "C" language linkage specification
 36 | */
 37 | #if defined (__cplusplus)
 38 | extern "C" {
    | `- note: extern "C" language linkage specification begins here
 39 | #endif
 40 | 
    :
659 | 
660 | #if defined(__cplusplus) || (defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) /* C99 */)
661 | # include <stdint.h>
    | `- error: import of C++ module 'std_stdint_h' appears within extern "C" language linkage specification
662 |   typedef  int8_t  LZ4_i8;
663 |   typedef uint8_t  LZ4_byte;

/Users/karl/Documents/sandbox/apps/compositor/Sources/Core/Sources/CompositorCore/TeX/EmbeddedTeX.swift:12:8: error: could not build Objective-C module 'lz4'
 10 | 
 11 | import cppbridge
 12 | import lz4
    |        `- error: could not build Objective-C module 'lz4'
 13 | 
 14 | public typealias PageCompletion = (_ pageDviBytes: Data?, _ page: Int, _ textwidth: Int, _ stop: UnsafeMutablePointer<Bool>) -> Void
<module-includes>:1:9: note: in file included from <module-includes>:1:
1 | #import "/Users/karl/Documents/sandbox/apps/compositor/Sources/Core/Sources/lz4/include/lz4.h"
  |         `- note: in file included from <module-includes>:1:
2 | 
...

Offending lines in lz4.h:

660 | #if defined(__cplusplus) || (defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) /* C99 */)
661 | # include <stdint.h>
    | `- error: import of C++ module 'std_stdint_h' appears within extern "C" language linkage specification

Commenting out the extern "C"

//#if defined (__cplusplus)
//extern "C" {
//#endif

makes the compiler happy, but then the linker complains (because I am messing with name mangling?):

karl@10 Core % swift build -Xswiftc -suppress-warnings
Building for debugging...
error: link command failed with exit code 1 (use -v to see invocation)
Undefined symbols for architecture arm64:
  "LZ4_compressBound(int)", referenced from:
      CompositorCore.EmbeddedTeX.(lookupPageDump in _C6A6B56F6A91CC1BF8BAAC0207457FAD)(Swift.Int) -> Foundation.Data? in EmbeddedTeX.swift.o
   NOTE: found '_LZ4_compressBound' in lz4.c.o, declaration possibly missing 'extern "C"'
  "LZ4_decompress_safe(char const*, char*, int, int)", referenced from:
      closure #1 (Swift.UnsafeRawBufferPointer) -> Foundation.Data? in CompositorCore.EmbeddedTeX.(lookupPageDump in _C6A6B56F6A91CC1BF8BAAC0207457FAD)(Swift.Int) -> Foundation.Data? in EmbeddedTeX.swift.o
   NOTE: found '_LZ4_decompress_safe' in lz4.c.o, declaration possibly missing 'extern "C"'
ld: symbol(s) not found for architecture arm64
clang: error: linker command failed with exit code 1 (use -v to see invocation)
[244/245] Linking libCompositorCore.dylib

How can I get this working?

Thanks

As per the instructions in this thread, I moved the offending #include out of the extern "C" block:

# include <stdint.h>

#if defined (__cplusplus)
extern "C" {
#endif

Now it works. Sorry for not researching this better before asking.

2 Likes

Oh yeah, also wanted to say: Swift Package Manager is awesome, it has come so far!

Thanks to everybody working on SPM!

3 Likes