Skip to content

Fix: SwiftPM transitive linking for CNumKong via substantive shim source#354

Open
beshkenadze wants to merge 1 commit into
ashvardanian:mainfrom
beshkenadze:spm-fix
Open

Fix: SwiftPM transitive linking for CNumKong via substantive shim source#354
beshkenadze wants to merge 1 commit into
ashvardanian:mainfrom
beshkenadze:spm-fix

Conversation

@beshkenadze
Copy link
Copy Markdown

Refs #353. Supersedes #352.

Problem

CNumKong is declared as a regular SwiftPM target with no sources (just headers under include/numkong/ and include/module.modulemap). SwiftPM/Xcode 16+ fails at link time when this target is pulled transitively — it expects a CNumKong.o that's never produced (swiftlang/swift-package-manager#5706, open since October 2022).

Fix

Add a single C source file (include/cnumkong_module.c) that exports one real symbol, and declare it as the target's source. This produces a relocatable CNumKong.o that satisfies the linker without changing any public API.

+// CNumKong owns the public header umbrella and modulemap; the actual
+// runtime dispatch lives in CNumKongDispatch. The single `cnumkong_module.c`
+// source provides one real exported symbol so SwiftPM/Xcode 16+ can link
+// this target transitively (see swiftlang/swift-package-manager#5706).
+// Mirrors apple/swift-system's `Sources/CSystem/shims.c` pattern.
 .target(
     name: "CNumKong",
     path: "include",
+    sources: ["cnumkong_module.c"],
     publicHeadersPath: ".",
     ...
 ),

New file include/cnumkong_module.c:

// SwiftPM module marker for CNumKong.
//
// CNumKong is the header-only module facade for NumKong's public C API; the
// actual runtime dispatch implementation lives in the CNumKongDispatch target.
//
// SwiftPM/Xcode 16+ refuses to link a C target that produces no object files
// when it appears as a transitive dependency of an app target — the link step
// expects a CNumKong.o output that would otherwise never exist
// (see swiftlang/swift-package-manager#5706, open since October 2022).
//
// This file provides one real exported symbol so the relocatable link
// succeeds. The same pattern is used by apple/swift-system's
// Sources/CSystem/shims.c. There is no runtime cost — the symbol is read once
// at most by tooling that needs to verify the module is present.
#include "numkong/numkong.h"

const unsigned int nk_cnumkong_module_loaded = 1u;

Why this and not the alternatives

Approach Result
Merge CNumKong into CNumKongDispatch Cleanest structurally, but changes paths used by CMake / build.rs / setup.py. Larger blast radius — happy to send if preferred (see #353).
Substantive shim source (this PR) Smallest diff. One new file with one symbol + comment. Same pattern as apple/swift-system. No behavioural change.
_dummy.c (rejected #352) Static unused variable, no exported symbol, called «ad-hoc» in review. This PR's shim is the principled version of that idea.

Effect

  • CNumKongDispatch and downstream consumers (USearch, etc.) keep the same import surface — the public header layout doesn't change.
  • SwiftPM produces CNumKong.o with one symbol → transitive linking works in Xcode 16+.
  • No runtime cost; nk_cnumkong_module_loaded is a single 32-bit constant.

Verification

  • swift build and swift test pass in NumKong (standalone).
  • swift build passes in a downstream macOS app (Tish) that depends on USearch via SwiftPM, transitively pulling CNumKongDispatchCNumKong. Without this fix, the link step fails on missing CNumKong.o.
  • No CMake / build.rs / setup.py paths touched.

Discussion

See #353 for the design walkthrough — including the merge alternative.

Add a single C source file (include/cnumkong_module.c) exporting one real
symbol so SwiftPM/Xcode 16+ produces a CNumKong.o that satisfies transitive
linking — see swiftlang/swift-package-manager#5706 (open since October 2022).

Mirrors apple/swift-system's Sources/CSystem/shims.c pattern. Supersedes
the ad-hoc _dummy.c approach from PR ashvardanian#352.

The CNumKongDispatch target (which owns the actual runtime dispatch
implementation) and downstream consumers (USearch, etc.) keep the same
import surface — public header layout is unchanged.

Verified locally with swift build in NumKong standalone and in a downstream
macOS app (Tish) that pulls CNumKongDispatch transitively via USearch.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant