How to build a CLI tool which links a Swift framework? (OSX)


(Karl) #1

Hi

I’ve got a cross-platform Swift framework which is used in a project, and I want to make a CLI tool which also uses it. I’m having a problem getting the framework and CLI tool to work together, because of the Swift standard libraries:

[1] If I just create a new CLI app and link it to my framework (setting rpath for the CLI app to “@executable_path” so it can find MyFramework sitting adjacent to it), the first error I receive is:

dyld: Library not loaded: @rpath/libswiftAppKit.dylib
  Referenced from: /blah/<MyFramework>.framework/Versions/A/<MyFramework>
  Reason: image not found

OK, that’s fair enough - the rpath is set to ../Frameworks (because <MyFramework> is assuming it will be embedded), but the CLI tool isn’t a package and can’t embed anything. So let’s help it by going to Xcode and selecting “Always Embed Swift Standard Libraries” for the framework.

[2] Now it can find the libraries, but the dynamic linker complains about duplicate definitions. The application doesn’t actually work because it trips up an assertion:

objc[99642]: Class _SwiftNativeNSDataBase is implemented in both /blah/<MyFramework>.framework/Versions/A/Frameworks/libswiftCore.dylib (0x109d7e488) and /blah/MyApp (0x109444350). One of the two will be used. Which one is undefined.
objc[99642]: Class _SwiftNativeNSCharacterSetBase is implemented in both /blah/<MyFramework>.framework/Versions/A/Frameworks/libswiftCore.dylib (0x109d7e4d8) and /blah/MyApp (0x1094443a0). One of the two will be used. Which one is undefined.
objc[99642]: Class _SwiftNativeNSIndexSetBase is implemented in both /blah/<MyFramework>.framework/Versions/A/Frameworks/libswiftCore.dylib (0x109d7e528) and /blah/MyApp (0x1094443f0). One of the two will be used. Which one is undefined.

Assertion failed: (registered == c && "objc_readClassPair failed to instantiate the class in-place"), function swift_instantiateObjCClass, file /Spring/Projects/3rdParty/OpenSource/Apple/swift/stdlib/public/runtime/SwiftObject.mm, line 1273.

···

So what I’d like to know is — is there a way for this to work?

The CLI app itself embeds the standard libraries, and the framework wants to dynamically link them. I can’t tell the framework not to dynamically load the libraries, and I also can’t make the framework in to a static library, because that’s not supported with Swift (yet?).

The only workaround I can think of is compiling the framework source files directly in to the CLI app. The downside is that it’s more work to manage and isn’t easily scalable to more CLI tools. Do we have anything better?

Thanks

Karl


(Quinn “The Eskimo!”) #2

Yep, I know this problem well.

What I do is use Xcode to create an app target, remove all the app-specific stuff from that, add then a `main.swift`, and add my command line code to that. If I need to provide easy access to the ‘tool’ from Terminal, a quick alias or symlink will do the trick.

Share and Enjoy

···

On 25 Aug 2016, at 11:28, Karl via swift-users <swift-users@swift.org> wrote:

I’m having a problem getting the framework and CLI tool to work together, because of the Swift standard libraries …

--
Quinn "The Eskimo!" <http://www.apple.com/developer/>
Apple Developer Relations, Developer Technical Support, Core OS/Hardware