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_dun...@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@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@swift.org > https://lists.swift.org/mailman/listinfo/swift-users > > >
_______________________________________________ swift-users mailing list swift-users@swift.org https://lists.swift.org/mailman/listinfo/swift-users