Static Linux SDK Cross Compiling - Linking to Linux Versions of Library

:white_check_mark: [Solved] See @al45tair message below, thanks again:

:red_question_mark: Original Problem:
Playing around with cross-compiling with the static linux sdk with a unique situation:

Package contains a system library sourced via pkgconfig
This is already dark territory but here. I have a "app" that utilizes a Swift library (FDB) that is a wrapper around a C library "CFDB" under the name libfdb_c.

import PackageDescription

let package = Package(
    name: "qipg",
    platforms: [
        .macOS(.v13)
    ],
    products: [
        .executable(
            name: "app",
            targets: ["app"]),
        .library(
            name: "FDBSwift",
            targets: ["FDB"]
        ),
    ],
    dependencies: [
        ...
    ],
    targets: [
        .executableTarget(
            name: "app",
            dependencies: [
                ....
                "FDB",
            ]
        ),
        ...
        .systemLibrary(name: "CFDB", pkgConfig: "libfdb"),
        .target(
            name: "FDB",
            dependencies: [
                "CFDB",
                ...
            ]
        ),
    ]
)

Pkgconfig:
This wrapper is all working and there is a pkgconfig defined for it that is the same for macOS/Linux.

prefix=/usr/local
exec_prefix=${prefix}
includedir=${prefix}/include
libdir=${exec_prefix}/lib

Name: fdb
Description: FoundationDB library
Version: 6.3.23
Cflags: -I${includedir}
Libs: -L${libdir} -lfdb_c

Building Issues
When I kick off a build for macOS in your standard semantics to problems:

swift build

Obviously, for the linux build it would fail because the one install on macOS is not for it.

> swift build --swift-sdk x86_64-swift-linux-musl
[1/1] Planning build
Building for debugging...
error: link command failed with exit code 1 (use -v to see invocation)
clang: warning: argument unused during compilation: '-pie' [-Wunused-command-line-argument]
ld.lld: error: unable to find library -lfdb_c
clang: error: linker command failed with exit code 1 (use -v to see invocation)
[2/3] Linking app

However, is it as simple as placing the linux libfdb_c.so into a folder and passing -L<path to folder>? It appears not.

swift build --swift-sdk x86_64-swift-linux-musl -Xlinker -L<path to>/lib/
[1/1] Planning build
Building for debugging...
error: link command failed with exit code 1 (use -v to see invocation)
clang: warning: argument unused during compilation: '-pie' [-Wunused-command-line-argument]
ld.lld: error: unable to find library -lfdb_c
clang: error: linker command failed with exit code 1 (use -v to see invocation)

I did reference this:

Which seems to suggest its 'possible'.

Why wouldn't that work here if I have a properly cross-compiled .so? Is there some voodoo I am missing?

Yeah, this won’t work. The problem you’ll have is that your shared object isn’t linked with the same C library as the Swift runtime and its dependencies.

If you’re using the Static SDK and you want a dependency, it really needs to be a static archive (a .a file), and it ideally also needs to be built against the same C library the Static SDK uses, otherwise it might have picked up incompatible type definitions or think functions are available when they aren’t.

We don’t have anything written down right now about how you could do such a build. I might see about posting a worked example somewhere so you can see what to do.

OK, so here's a worked example, building libpng for the Static SDK; I'm using CMake here because it's much easier than getting GNU configure to do the right thing.

First, we fetch ourselves the libpng sources, then extract them to a directory with

$ tar xvzf /path/to/libpng.tar.gz

Next, we go into the libpng directory that extracted and build ourselves a toolchain file for CMake:

$ cd libpng
$ mkdir build
$ cd build
$ cat >> toolchain.cmake <<EOF
# You'll need to set these yourself
set(MY_HOME "/Users/myuser")
set(SWIFT_VERSION "swift-6.2-DEVELOPMENT-SNAPSHOT-2025-06-17-a")

set(STATIC_SDK "${MY_HOME}/.swiftpm/swift-sdks/${SWIFT_VERSION}_static-linux-0.0.1.artifactbundle/${SWIFT_VERSION}_static-linux-0.0.1")
set(XCTOOLCHAIN "${MY_HOME}/Library/Developer/Toolchains/${SWIFT_VERSION}.xctoolchain")
set(RESOURCE_DIR "${STATIC_SDK}/swift-linux-musl/musl-1.2.5.sdk/x86_64/usr/lib/swift/clang")
set(STATIC_SDK_ROOT "${STATIC_SDK}/swift-linux-musl/musl-1.2.5.sdk")

set(CMAKE_SYSTEM_NAME Linux)
set(CMAKE_SYSTEM_PROCESSOR x86_64)
set(CMAKE_ASM_COMPILER_TARGET x86_64-swift-linux-musl)
set(CMAKE_C_COMPILER_TARGET x86_64-swift-linux-musl)
set(CMAKE_CXX_COMPILER_TARGET x86_64-swift-linux-musl)
set(CMAKE_SWIFT_COMPILER_TARGET x86_64-swift-linux-musl)
set(CMAKE_SYSROOT "${STATIC_SDK_ROOT}/x86_64")

set(CMAKE_CROSSCOMPILING YES)

set(CMAKE_C_COMPILER "${XCTOOLCHAIN}/usr/bin/clang" -resource-dir "${RESOURCE_DIR}")
set(CMAKE_CXX_COMPILER "${XCTOOLCHAIN}/usr/bin/clang++" -resource-dir "${RESOURCE_DIR}")
set(CMAKE_ASM_COMPILER "${XCTOOLCHAIN}/usr/bin/clang" -resource-dir "${RESOURCE_DIR}")
set(CMAKE_FIND_ROOT_PATH "${CMAKE_SYSROOT}")
set(CMAKE_INSTALL_PREFIX "${CMAKE_SYSROOT}/usr/local")
EOF

Then we use this file to get CMake to generate build artefacts:

$ cmake -G Ninja -DCMAKE_TOOLCHAIN_FILE=toolchain.cmake -DPNG_SHARED=NO -DPNG_STATIC=YES ..

And finally we can build

$ ninja
...

If you want to install the new libraries into your copy of the Static SDK, you can do so with

$ ninja install

Otherwise you'll need to provide the full path to the .a file when you want to link with it, and similarly you might need to add some things to your include paths.

5 Likes

Something similar will work for projects that use other build systems. Broadly speaking you need to make sure you're using the right compiler(s) and you need to pass the --sdkroot and -resource-dir options to point the compiler at the files in the Static SDK for Linux rather than wherever they look by default.

1 Like

@al45tair thank you are a legend for taking the time to put that together.

Would this be on of those things we would want to add to a blog post or a repo doc? Happy to help lend some time on that.

2 Likes

Would this be on of those things we would want to add to a blog post or a repo doc? Happy to help lend some time on that.

I think it probably is; it would be nice to provide a few more examples though — e.g. maybe some with autoconf-based build systems as well.

The Static SDK build script does something very similar and includes some non-CMake-based things as well as a number of CMake-based projects.

Cool. Might be able to whip some more examples up. At the very least I can clean up and provide this as an example.

Any particular repo I should PR into?

Sorry, just realised I owe you a reply here :slight_smile:

We could make a new document in the swift repo in docs? Or perhaps this should be the subject of a blog post. Maybe both?

@paxsonsa @al45tair I had this scribbled down on a list to follow back up on as potential docs to include with Swift Package Manager, which has some related sections on building and linking to system libraries (I've been working to update and port its documentation over to DocC - swift-package-manager/Sources/PackageManagerDocs at main · swiftlang/swift-package-manager · GitHub). That said, there's only examples and not much in docs that talks to setting up Swift with CMake or other build systems, but it's a pretty consistent question point I've heard that I'd like to help resolve.

@paxsonsa - I'd love to collaborate with you to get this detail written up and included, regardless of where it could ultimately land - docs in Swift, or within the package manager, etc.