SwiftPM C Library Wrapper Can’t Find Library (auto-link?)

I just followed this guide to make a simple wrapper around libgdal, which exists on my macOS 11 system at /usr/local/lib. My code is available here. Unfortunately, although it compiles, it does not find the library, and I’m not sure how to proceed:

TestGDAL/ (main) $ swift run

ld: warning: Could not find or use auto-linked library 'gdal'
Undefined symbols for architecture x86_64:
  "_GDALVersionInfo", referenced from:
      _main in main.swift.o
ld: symbol(s) not found for architecture x86_64
[0/1] Linking TestGDAL

Any suggestions? I tried setting DLYD_FALLBACK_LIBRARY_PATH to point to /usr/local/lib but it didn't work. Both dynamic and static libraries exist, and their associated command-line tools execute just fine:

$ ls -l /usr/local/lib/libgdal.*
lrwxr-xr-x  1 rmann  admin    43B Apr  6 06:37 /usr/local/lib/libgdal.28.dylib@ -> ../Cellar/gdal/3.2.2_1/lib/libgdal.28.dylib
lrwxr-xr-x  1 rmann  admin    36B Apr  6 06:37 /usr/local/lib/libgdal.a@ -> ../Cellar/gdal/3.2.2_1/lib/libgdal.a
lrwxr-xr-x  1 rmann  admin    40B Apr  6 06:37 /usr/local/lib/libgdal.dylib@ -> ../Cellar/gdal/3.2.2_1/lib/libgdal.dylib

$ file /usr/local/lib/libgdal.dylib 
/usr/local/lib/libgdal.dylib: Mach-O 64-bit dynamically linked shared library x86_64

$ nm -g /usr/local/lib/libgdal.dylib | grep GDALVersionInfo
000000000051a184 T _GDALVersionInfo

A simple C program seems to work just fine:

#include <gdal.h>

int
main(int inC, char** inV)
{
	const char* v = GDALVersionInfo("RELEASE_NAME");
	printf("Version: %s\n", v);
	return 0;
}
$ clang -lgdal tg.c 
$ ./a.out 
Version: 3.2.2

At a glance the only thing I see that might be wrong is the bridging header should be in the same directory as the module map.

Other then that it looks okay to me.


Actually I think I got that mixed up. A module map can’t reference multiple headers. The path might not be restricted to package. Not at a computer so can’t easily check.

Another project I made does work. It's very similar in layout, but the header is called shim.h. One difference is it has an intermediate library target that wraps the C library. Not sure why that would make a difference, but:

When I search online for “ld warning Could not find or use auto-linked library,” all the results are about Xcode (not Swift on the command line), and all of the accepted solutions seem to be to add an empty .swift file to the library and enable bridging. I don’t know if that’s somehow what's happening with my SDL project.

Note that neither .modulemap imports more than one header. Also note that it does manage to find the header and allow the code to compile. It just won’t link. Or “auto-link,” whatever that is.

I also tried rearranging SwiftGDAL to exactly match the layout of SwiftSDL (for the system library target only; I didn't create a Swift wrapper): I put the .modulemap and shim.h in Sources/CGDAL. It made no difference.

I just tried adding a new package that mimics the structure of my working SwiftSDL package; it’s in the source repo under directory GDAL. Same error.

This is SR-14493. There are very few available workarounds at this time.

However, I should clarify something: while your code does compile, it does not link. This means it’s not a runtime error, and so DYLD_LIBRARY_PATH will not help you here.

2 Likes

Derp. Of course. I just assumed it was a failure at runtime.

But I have another project that links fine, with its libraries in the same place (ie not in the macOS SDK). Is this behavior inconsistent? It’s certainly pretty egregious.

I believe the behaviour is influenced by whether there is a .pc file floating around. SwiftPM’s use of pkg-config files on macOS is very inconsistent. I think it uses the brew directive to find them, meaning that packages installed from Homebrew tend to work correctly, while those installed elsewhere (or Homebrew packages without pkg-config files) do not work correctly.

Here's an example of how you could use spm to compile GDAL as a swift module instead of requiring users to install it separately.

Unfortunately GDAL is not organized well for this approach, and is rather large to reasonably be able to modify. It doesn't remove platform specific source with pre-processor macros like a lot of projects do, which means you'd need to modify the source by deleting source files or adding macros. So it would become difficult to maintain with the latest GDAL source changes.

But the Package.swift is an example that could be helpful to you for other, less massive, libraries. I personally used this approach to make a libpng, zlib, and vorbisogg available as cross platform swift modules.

1 Like

Yeah, I ran into pkg-config issues with SDL2, in that it uses sdl-config or something like that, and SPM couldn't invoke it (its output was otherwise identical to pkg-config). Perhaps I can add a dummy .pc file somewhere?

(Speaking of .brew directives, are those supposed to enable downloading and installing those packages via SPM? I couldn't figure out how to make that work. swift package resolve didn't do it.)

The bug you referenced mentioned unsafeFlags as a workaround, but didn't go into any detail.

I suppose Xcode can do this all, is that correct? I need some way to continue development until Swift gets fixed.

Okay, I managed to work around this problem with the following Package.swift. It's a pretty lame workaround, in part because it can't be placed in the .systemLibrary target, but rather needs to be placed in the client's Package.swift. But adding linkerSettings: [.unsafeFlags(["-L/usr/local/lib"])]) fixed it:

// swift-tools-version:5.3
// The swift-tools-version declares the minimum version of Swift required to build this package.

import PackageDescription

let package = Package(
    name: "TestGDAL",
    dependencies: [
        .package(path: "../CGDAL"),
    ],
    targets: [
        .target(
            name: "TestGDAL",
            dependencies: ["CGDAL"],
            linkerSettings: [.unsafeFlags(["-L/usr/local/lib"])]),
        .testTarget(
            name: "Tests",
            dependencies: ["TestGDAL"]),
    ]
)

This is clumsy and onerous to do, and fragile, but it will allow me to continue working. I sure hope Apple/Swift can sort out these pretty basic build issues.

That says Resolved. Any idea what's the resolution? It says nothing in the bug.

1 Like

It has been resolved due to what appears to be an invalid account compromise on the bugs.swift.org server (cc @mishal_shah)

The "security researcher" was a compromised account? How ironic. Also, what a weird thing to do, maliciously close tickets?

1 Like
Terms of Service

Privacy Policy

Cookie Policy