Here is my small Swift and C project:
// swift-tools-version: 5.9
// The swift-tools-version declares the minimum version of Swift required to build this package.
import PackageDescription
let package = Package(
name: "TestApp",
platforms: [.macOS(.v14), .iOS(.v13), .watchOS(.v6)],
products: [
.executable(name: "MyApp", targets: ["MyApp"]),
.library(name: "MySwiftLib", type: .static, targets: ["MySwiftLib"])
],
targets: [
.executableTarget(name: "MyApp",
cSettings: [
.headerSearchPath("../../.build/x86_64-apple-macosx/debug/MySwiftLib.build")
]/*,
linkerSettings: [
.unsafeFlags(["-Xlinker", "-rpath", "-Xlinker", "../../.build/x86_64-apple-macosx/debug/"]),
.linkedLibrary("MySwiftLib")
]*/
),
.target(name: "MySwiftLib")
]
)
Sources/MySwiftLib/MySwiftLib.swift
@_cdecl("mySwiftFunc")
public func mySwiftFunc() {
print("Hello from Swift")
}
#include <stdio.h>
#include "MySwiftLib-Swift.h"
void main() {
puts("Hello World from C!");
mySwiftFunc();
}
-
First step: Building the static or dynamic library is not an issue: swift build --product MySwiftLib
It produces ./.build/x86_64-apple-macosx/debug/libMySwiftLib.a and ./.build/x86_64-apple-macosx/debug/MySwiftLib.build/MySwiftLib-Swift.h when building a static library - and ./.build/x86_64-apple-macosx/debug/libMySwiftLib.dylib and ./.build/x86_64-apple-macosx/debug/MySwiftLib.build/MySwiftLib-Swift.h when building a dynamic library.
-
The second step is to build the C app. I use swift run and it seems to require objc:
/Users/olivier/dev/test_swift_lib/.build/x86_64-apple-macosx/debug/MySwiftLib.build/module.modulemap:1:8: error: module 'MySwiftLib' requires feature 'objc'
module MySwiftLib {
^
/Users/olivier/dev/test_swift_lib/Sources/MyApp/main.c:3:10: note: submodule of top-level module 'MySwiftLib' implicitly imported here
#include "MySwiftLib-Swift.h"
^
/Users/olivier/dev/test_swift_lib/Sources/MyApp/main.c:7:5: error: call to undeclared function 'mySwiftFunc'; ISO C99 and later do not support implicit function declarations [-Wimplicit-function-declaration]
mySwiftFunc();
^
2 errors generated.
[0/2] Compiling MyApp main.c
I have just tried to explicitly specify interoperabilityMode(.C) in Package.swift but module.modulemap still specifies requires objc:
// swift-tools-version: 5.9
// The swift-tools-version declares the minimum version of Swift required to build this package.
import PackageDescription
let package = Package(
name: "TestApp",
platforms: [.macOS(.v14), .iOS(.v13), .watchOS(.v6)],
products: [
.executable(name: "MyApp", targets: ["MyApp"]),
.library(name: "MySwiftLib", type: .static, targets: ["MySwiftLib"])
],
targets: [
(...)
.target(name: "MySwiftLib",
swiftSettings: [
.interoperabilityMode(.C)
]
)
]
)
Would you mind filing a bug report on the SwiftPM repository with a self-contained reproduction example and details about your system like Xcode and Swift versions that you're using?
ole
(Ole Begemann)
5
Isn’t the fundamental issue here that Swift does not have bidirectional C interop in the way it has for Objective-C and C++?
As I understand it:
- Swift functions can't generally be called from C because they don't follow the C calling convention. There is
@_cdecl, but that's not an official feature.
- The "C header" the Swift compiler can generate for a Swift module is in fact always an Objective-C header because only
@objc constructs (classes/methods/properties) can be called anyway.
Now that we have bidirectional C++ interop, it would be nice to have the same for plain C.
1 Like
Joe_Groff
(Joe Groff)
6
It is not yet official, but the implementation of @_cdecl with regards to generated bridging headers should, to my knowledge, be mostly complete. If you aren't in fact using any Objective-C specific bridging features then the generated header ought to be usable as a plain C header, and I'd call it a bug if it's not.
3 Likes
ole
(Ole Begemann)
7
Ah, I didn't know that. Thanks!
jrose
(Jordan Rose)
8
The main missing thing I remember was that the compiler won’t help you stick to the C subset. And Bool comes out as BOOL rather than Boolean. But yeah.
2 Likes
Nobody1707
(Nobody1707)
9
I mean, BOOL is probably more correct than Boolean, if only because on 64-bit iPhones and iWatch it actually is the C bool type instead of a char.
jrose
(Jordan Rose)
10
>< I meant boolean and got attacked by autocorrect, forgetting that the C type is bool anyway. Too much Java for work!
2 Likes