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.
@GaryRudolph

Copy link
Copy Markdown

Independent confirmation of this bug and of the fix in this PR.

Affected consumer: Desk Hound, a macOS app, pulls USearch 2.25.3 → NumKong 7.7.0 transitively. Our Xcode project is generated by XcodeGen and built via xcodebuild. The build fails at link time with:

Build input file cannot be found: '…/CNumKong.o'

exactly as described in #353. swift build/swift test are unaffected — it only reproduces through the .xcodeproj/xcodebuild SwiftPM integration.

The shim approach in this PR fixes it. We independently worked around it by vendoring a local copy of NumKong with a trivial translation unit in include/ — functionally the same as cnumkong_module.c here, but we'd taken the comment-only dummy.c route (the same shape you rightly rejected in #352). The real exported symbol + apple/swift-system precedent in this PR is the better form.

Verified end-to-end: our full xcodebuild test pipeline goes green with the shim in place — i.e. this fix is validated through the actual Xcode integration, not just swift build.

Would be great to get this reviewed and into a patch release within USearch's from: 7.5.0 range so downstream apps can drop their NumKong workarounds. Happy to test a release candidate against our pipeline.

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.

2 participants