Skip to content

SwiftBuild: Fix executable can't find dynamic lib runtime error #8650

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: main
Choose a base branch
from

Conversation

pmattos
Copy link
Contributor

@pmattos pmattos commented May 10, 2025

Update LD_RUNPATH_SEARCH_PATHS of executable built using the new Swift Build backend.

Motivation:

For the Swift Build backend, fix a runtime issue when building & running an executable linked against a dynamic library. Given the following packages:

Package App

let package = Package(
    name: "App",
    dependencies: [
        .package(path: "../Lib")
    ],
    targets: [
        .executableTarget(
            name: "App",
            dependencies: [
                .product(name: "Utils", package: "Lib")
            ]
        )
    ]
)

Package Lib

let package = Package(
    name: "Lib",
    products: [
        .library(
            name: "Utils",
            type: .dynamic,
            targets: ["Utils"]
        )
    ],
    targets: [
        .target(name: "Utils")
    ]
)

This the error that I get:

$ swift run --package-path /App --build-system swiftbuild

dyld[89079]: Library not loaded: @rpath/libUtils.dylib
Referenced from: 
<98AF2F50-2C62-318C-8430-C6326D8468F1> /App/.build/arm64-apple-macosx/Products/Debug/App
Reason: tried: 
'/usr/lib/swift/libUtils.dylib' (no such file, not in dyld cache), 
'/App/.build/arm64-apple-macosx/Products/Debug/PackageFrameworks/libUtils.dylib' (no such file),
'/System/Volumes/Preboot/Cryptexes/OS/usr/lib/swift/libUtils.dylib' (no such file), 
'/System/Volumes/Preboot/Cryptexes/OS/App/.build/arm64-apple-macosx/Products/Debug/PackageFrameworks/libUtils.dylib' (no such file),

...but works just fine when using --build-system native.

When comparing the runpath, we get this:

# Swift Build
.build/arm64-apple-macosx/Products/Debug/App
/usr/lib/swift
/Users/pmattos/SamplePackages/App+DynamicLib/App/.build/arm64-apple-macosx/Products/Debug/PackageFrameworks

# Native
.build/arm64-apple-macosx/debug/App
/usr/lib/swift
@loader_path
/Applications/Xcode_17A233.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/swift-5.5/macosx

PS. To get the runpath, run otool -l path/to/bin | awk '/LC_RPATH/ {getline; getline; print $2}'.

Modifications:

Update the build setting LD_RUNPATH_SEARCH_PATHS for executables, appending @loader_path. This is actually an "imparted" build setting coming from dynamic libraries targets, so that's where I made the change.

Result:

Now both configurations are capable of running the built App:

$ swift run --package-path /App --build-system swiftbuild --configuration debug
Hello from Lib

$ swift run --package-path /App --build-system swiftbuild --configuration release
Hello from Lib

For reference, this is the PIF for the sample package App above. Note my change affects the bottom PACKAGE-TARGET:Utils target only. The build setting is then "imparted" on the top PACKAGE-PRODUCT:App target.

@pmattos pmattos changed the title SwiftBuild: Fix executable can't find dynamic lib SwiftBuild: Fix executable can't find dynamic lib error May 10, 2025
@pmattos pmattos changed the title SwiftBuild: Fix executable can't find dynamic lib error SwiftBuild: Fix executable can't find dynamic lib runtime error May 10, 2025
@pmattos
Copy link
Contributor Author

pmattos commented May 10, 2025

@swift-ci test

//
// An imparted build setting on C will propagate back to both B and A.
let additionalRunPaths = if productType == .framework {
["$(BUILT_PRODUCTS_DIR)/PackageFrameworks"]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is changing the behavior a bit from previously since the settings are no longer being added to a separate debugImpartedSettings variable. Since $(BUILT_PRODUCTS_DIR)/PackageFrameworks is an absolute path to the build outputs, that should only be present in debug configurations, not in release configurations.

@loader_path on the other hand should be present in both places.

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