[swift-users] Communicating with dynamically loaded swift library

Geordie Jay geojay at gmail.com
Sat Oct 7 06:44:12 CDT 2017


Ján Kosa <lopenka at gmail.com> schrieb am Sa. 7. Okt. 2017 um 13:34:

> I tried swift package clean, but it didn't help
>
> "Try to ensure the plugin provider module (libA) is (only) being compiled
> into its standalone shared library file."
> How do I go about this? It is 3rd party module, it doesn't define any
> products (https://github.com/apple/swift-protobuf.git). Is there
> something I can do in my Package files to make sure it is loaded
> dynamically?
>

When you compile a package depending on protobuf, all the relevant symbols
end up in your package’s library file. So here’s something you might try:

import protobuf into your own “PluginProvider” module (package), which has
a shared library product like this: ‘@_exported import Protobuf’ in some
compiled swift file. Then from the other dependent modules “import
PluginProvider” - the protobuf symbols should be available, and all from
one (nonconflicting) source.

Geordie



> On 6 October 2017 at 22:52, Geordie Jay <geojay at gmail.com> wrote:
>
>> I think SwiftPM is (incorrectly) compiling A.XYZ into each of the
>> modules that depend on it, as well as into your intended libA.so file.
>>
>> Try to ensure the plugin provider module (libA) is (only) being compiled
>> into its standalone shared library file. Try cleaning the swiftpm build for
>> one (swift package clean) and ensure the Package.swift files are correctly
>> set up to output the shared library.
>>
>> Sorry I can’t be more specific, I’ve had these same kinds of issues
>> before but I’m not 100% what they were.
>>
>> Geordie
>>
>>
>> Ján Kosa via swift-users <swift-users at swift.org> schrieb am Fr. 6. Okt.
>> 2017 um 14:41:
>>
>>> It worked! Took me a while to iron out details, but it is working now.
>>> Huge thanks sir, I will name my firstborn after you.
>>> Thanks for the @_cdecl("initializePlugin") tip as well, I didn't know
>>> about it and it will be very useful.
>>>
>>> I am having slightly related problem now (it was there before, but I
>>> ignored it for the time being), not sure if I should start a new thread?
>>>
>>> The PluginInterface module has one external dependency on module A,
>>> PluginConsumer has the dependency on module B which has dependency on same
>>> module A that the PluginInterface uses. When I load the plugin library, I
>>> get bunch of errors like:
>>>
>>> Class A.XYZ is implemented in both libPluginInterface.dylib and
>>> libMyPlugin.dylib
>>>
>>> I know why it is there, but I don't know how to get rid of it. I can't
>>> just remove dependency from PluginConsumer and use the one from
>>> PluginInterface (if that would even work?) because PluginConsumer does not
>>> depend on it directly, but it is going through module B first
>>>
>>> Cheers,
>>> Lope
>>>
>>> On 4 October 2017 at 22:17, Daniel Dunbar <daniel_dunbar at apple.com>
>>> wrote:
>>>
>>>> The way that I have done this in the past is pass a protocol as an
>>>> unsafe pointer to an exposed entry point:
>>>> ```swift
>>>>             let entryPoint = dlsym(handle, “initializePlugin”)
>>>>             guard entryPoint != nil else {
>>>>                 fatalError("missing plugin entry point: \(pluginPath)")
>>>>             }
>>>>             typealias PluginInitializationFunc = @convention(c)
>>>> (UnsafeRawPointer) -> ()
>>>>             let f = unsafeBitCast(entryPoint, to:
>>>> PluginInitializationFunc.self)
>>>>             f(Unmanaged.passUnretained(self).toOpaque())
>>>> ```
>>>>
>>>> and then in the plugin convert back to the appropriate type:
>>>>
>>>> ```
>>>> @_cdecl("initializePlugin")
>>>> public func initializePlugin(_ ptr: UnsafeRawPointer) {
>>>>     let manager =
>>>> Unmanaged<PluginManager>.fromOpaque(ptr).takeUnretainedValue()
>>>> ```
>>>>
>>>> HTH,
>>>>  - Daniel
>>>>
>>>> On Oct 4, 2017, at 11:02 AM, Ján Kosa via swift-users <
>>>> swift-users at swift.org> wrote:
>>>>
>>>> Hello folks,
>>>>
>>>> I have been toying with dynamic libraries, trying to implement plugin
>>>> functionality. I was able to get to the point where I can call simple
>>>> function in loaded library, but I am having troubles starting more
>>>> sophisticated communication channel.
>>>>
>>>> There are 3 projects
>>>> - PluginConsumer is an app that loads plugin libraries
>>>> - MyPlugin is a plugin implementation, output is dynamic library that
>>>> PluginConsumer loads
>>>> - PluginInterface is common interface that both MyPlugin and
>>>> PluginConsumer use, so that they know how to communicate
>>>>
>>>> My first idea was to have PluginInterface be a simple SPM project with
>>>> single file where the bare-bones PluginInterface class would be:
>>>>
>>>>
>>>> open class PluginInterface {
>>>>
>>>>     open func sayHi()
>>>>
>>>> }
>>>>
>>>>
>>>> Package.swift file:
>>>>
>>>>
>>>> // swift-tools-version:4.0
>>>>
>>>> import PackageDescription
>>>>
>>>> let package = Package(
>>>>
>>>>     name: "PluginInterface",
>>>>
>>>>     products: [ .library(name: "PluginInterface", type: .dynamic,
>>>> targets: ["PluginInterface"]) ],
>>>>
>>>>     targets: [ .target(name: "PluginInterface") ]
>>>>
>>>> )
>>>>
>>>>
>>>>
>>>> UserPlugin is also very simple project containing only one file:
>>>>
>>>>
>>>> public func getPlugin() -> AnyObject {
>>>>
>>>>     return MyPlugin()
>>>>
>>>> }
>>>>
>>>>
>>>> class MyPlugin: PluginInterface {
>>>>
>>>>     override func sayHi() {
>>>>
>>>>         print("Hi from my plugin")
>>>>
>>>>     }
>>>>
>>>> }
>>>>
>>>> Package.swift:
>>>>
>>>>
>>>> // swift-tools-version:4.0
>>>>
>>>> import PackageDescription
>>>>
>>>> let package = Package(
>>>>
>>>>     name: "MyPlugin",
>>>>
>>>>     products: [ .library(name: "MyPlugin", type: .dynamic, targets: [
>>>> "MyPlugin"]) ],
>>>>
>>>>     dependencies: [ .package(url: "url_to_PluginInterface", from:
>>>> "0.0.0"), ],
>>>>
>>>>     targets: [
>>>>
>>>>         .target(name: "PluginInterface", dependencies: [
>>>> "PluginInterface"]),
>>>>
>>>>         .target(name: "MyPlugin", dependencies: ["PluginInterface"]),
>>>>
>>>>     ]
>>>>
>>>> )
>>>>
>>>>
>>>> The PluginConsumer is bit more complicated, but here is relevant part
>>>> (lib loading and function calling):
>>>>
>>>>
>>>> typealias InitFunction = @convention(c) () -> AnyObject
>>>>
>>>>
>>>> let openRes = dlopen(pathToLib, RTLD_NOW|RTLD_LOCAL)
>>>>
>>>> if openRes != nil {
>>>>
>>>>     defer {
>>>>
>>>>         dlclose(openRes)
>>>>
>>>>     }
>>>>
>>>>     let symbolName = "mangled_symbol_name"
>>>>
>>>>     let sym = dlsym(openRes, symbolName)
>>>>
>>>>
>>>>     if sym != nil {
>>>>
>>>>         let f: InitFunction = unsafeBitCast(sym, to: InitFunction.self)
>>>>
>>>>         let plugin = f() as? PluginInterface
>>>>
>>>>     }
>>>>
>>>> }
>>>>
>>>> Package.swift file:
>>>>
>>>> // swift-tools-version:4.0
>>>>
>>>> import PackageDescription
>>>>
>>>> let package = Package(
>>>>
>>>>     name: "PluginConsumer",
>>>>
>>>>     dependencies: [ .package(url: "path_to_plugin_interface", from:
>>>> "0.0.0") ],
>>>>
>>>>     targets: [ .target(name: "PluginConsumer", dependencies: [
>>>> "PluginConsumer"]) ]
>>>>
>>>> )
>>>>
>>>>
>>>> This all compiles nicely, MyPlugin project creates dylib file that
>>>> executable created by PluginConsumer can load, but the problem is with
>>>> following line:
>>>>
>>>> let plugin = f() as? PluginInterface
>>>>
>>>> Type of the plugin is MyPlugin, but from the consumer's view, it
>>>> doesn't inherit from PluginInterface so I can't call sayHi() method. I
>>>> assume this is because there is no relation between PluginInterface class
>>>> that compiler uses for MyPlugin project one that it uses for PluginConsumer
>>>> project. After library is loaded, they are two completely different classes
>>>> that happen to share same name. Is my assumption correct and how do I go
>>>> about fixing it?
>>>>
>>>> I had an idea I could make PluginInterface emit dynamic library that
>>>> would be dynamically linked by both MyPlugin and PluginConsumer, thus
>>>> making them share same PluginInterface class, but I can't figure out how to
>>>> do that (or if it's right way of doing this).
>>>>
>>>>
>>>> Any help appreciated :)
>>>>
>>>> Lope
>>>> _______________________________________________
>>>> swift-users mailing list
>>>> swift-users at swift.org
>>>> https://lists.swift.org/mailman/listinfo/swift-users
>>>>
>>>>
>>>>
>>> _______________________________________________
>>> swift-users mailing list
>>> swift-users at swift.org
>>> https://lists.swift.org/mailman/listinfo/swift-users
>>>
>>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-users/attachments/20171007/7b38fa1f/attachment.html>


More information about the swift-users mailing list