178 lines
4.7 KiB
C++
178 lines
4.7 KiB
C++
#include "plugin_loader.h"
|
|
|
|
#include <sstream>
|
|
|
|
#include "utils/logger.h"
|
|
|
|
#ifdef _WIN32
|
|
#include <windows.h>
|
|
#else
|
|
#include <dlfcn.h>
|
|
#endif
|
|
|
|
namespace rk3588 {
|
|
|
|
namespace {
|
|
|
|
void* LoadLibraryHandle(const std::string& path, std::string& err) {
|
|
#ifdef _WIN32
|
|
HMODULE handle = LoadLibraryA(path.c_str());
|
|
if (!handle) {
|
|
err = "LoadLibrary failed for " + path;
|
|
}
|
|
return handle;
|
|
#else
|
|
void* handle = dlopen(path.c_str(), RTLD_LAZY);
|
|
if (!handle) {
|
|
const char* dl_err = dlerror();
|
|
err = dl_err ? dl_err : "dlopen failed";
|
|
}
|
|
return handle;
|
|
#endif
|
|
}
|
|
|
|
void CloseLibraryHandle(void* handle) {
|
|
if (!handle) return;
|
|
#ifdef _WIN32
|
|
FreeLibrary(static_cast<HMODULE>(handle));
|
|
#else
|
|
dlclose(handle);
|
|
#endif
|
|
}
|
|
|
|
void* LoadSymbol(void* handle, const char* name) {
|
|
#ifdef _WIN32
|
|
return reinterpret_cast<void*>(GetProcAddress(static_cast<HMODULE>(handle), name));
|
|
#else
|
|
return dlsym(handle, name);
|
|
#endif
|
|
}
|
|
|
|
std::string SharedLibExtension() {
|
|
#ifdef _WIN32
|
|
return ".dll";
|
|
#elif __APPLE__
|
|
return ".dylib";
|
|
#else
|
|
return ".so";
|
|
#endif
|
|
}
|
|
|
|
} // namespace
|
|
|
|
PluginLoader::PluginLoader(std::string plugin_dir)
|
|
: plugin_dir_(std::move(plugin_dir)) {}
|
|
|
|
PluginLoader::PluginLoader(PluginLoader&& other) noexcept
|
|
: plugin_dir_(std::move(other.plugin_dir_)), cache_(std::move(other.cache_)) {
|
|
other.cache_.clear();
|
|
}
|
|
|
|
PluginLoader& PluginLoader::operator=(PluginLoader&& other) noexcept {
|
|
if (this == &other) return *this;
|
|
|
|
for (auto& kv : cache_) {
|
|
CloseLibraryHandle(kv.second.handle);
|
|
}
|
|
cache_.clear();
|
|
|
|
plugin_dir_ = std::move(other.plugin_dir_);
|
|
cache_ = std::move(other.cache_);
|
|
other.cache_.clear();
|
|
return *this;
|
|
}
|
|
|
|
PluginLoader::~PluginLoader() {
|
|
for (auto& kv : cache_) {
|
|
CloseLibraryHandle(kv.second.handle);
|
|
}
|
|
}
|
|
|
|
void PluginLoader::SetPluginDir(std::string plugin_dir) {
|
|
if (plugin_dir == plugin_dir_) return;
|
|
for (auto& kv : cache_) {
|
|
CloseLibraryHandle(kv.second.handle);
|
|
}
|
|
cache_.clear();
|
|
plugin_dir_ = std::move(plugin_dir);
|
|
}
|
|
|
|
std::string PluginLoader::BuildLibraryPath(const std::string& type) const {
|
|
#ifdef _WIN32
|
|
return plugin_dir_ + "/" + type + SharedLibExtension();
|
|
#else
|
|
return plugin_dir_ + "/lib" + type + SharedLibExtension();
|
|
#endif
|
|
}
|
|
|
|
bool PluginLoader::LoadPlugin(const std::string& type, PluginHandle& out, std::string& err) {
|
|
std::string path = BuildLibraryPath(type);
|
|
void* handle = LoadLibraryHandle(path, err);
|
|
if (!handle) return false;
|
|
|
|
auto create = reinterpret_cast<CreateNodeFn>(LoadSymbol(handle, "CreateNode"));
|
|
auto destroy = reinterpret_cast<DestroyNodeFn>(LoadSymbol(handle, "DestroyNode"));
|
|
auto get_type = reinterpret_cast<GetTypeFn>(LoadSymbol(handle, "GetNodeType"));
|
|
auto get_abi = reinterpret_cast<GetAbiFn>(LoadSymbol(handle, "GetAbiVersion"));
|
|
if (!create || !destroy || !get_type || !get_abi) {
|
|
err = "Missing required symbols in plugin: " + path;
|
|
CloseLibraryHandle(handle);
|
|
return false;
|
|
}
|
|
if (get_abi() != kNodeAbiVersion) {
|
|
std::ostringstream oss;
|
|
oss << "ABI mismatch for plugin " << type << ": expected " << kNodeAbiVersion
|
|
<< " got " << get_abi();
|
|
err = oss.str();
|
|
CloseLibraryHandle(handle);
|
|
return false;
|
|
}
|
|
|
|
if (const char* declared = get_type()) {
|
|
if (type != declared) {
|
|
err = "Plugin type mismatch: requested '" + type + "' but plugin reports '" +
|
|
std::string(declared) + "'";
|
|
CloseLibraryHandle(handle);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
out.handle = handle;
|
|
out.create = create;
|
|
out.destroy = destroy;
|
|
out.get_type = get_type;
|
|
out.get_abi = get_abi;
|
|
return true;
|
|
}
|
|
|
|
Result<NodePtr> PluginLoader::CreateNode(const std::string& type) {
|
|
auto it = cache_.find(type);
|
|
if (it == cache_.end()) {
|
|
PluginHandle handle;
|
|
std::string err;
|
|
if (!LoadPlugin(type, handle, err)) {
|
|
LogError("[PluginLoader] Failed to load plugin '" + type + "': " + err);
|
|
return Error("Failed to load plugin '" + type + "': " + err);
|
|
}
|
|
it = cache_.emplace(type, std::move(handle)).first;
|
|
}
|
|
|
|
PluginHandle& handle = it->second;
|
|
NodePtr node(handle.create(), NodeDeleter{handle.destroy});
|
|
if (!node) {
|
|
return Error("CreateNode returned null for type " + type);
|
|
}
|
|
return node;
|
|
}
|
|
|
|
NodePtr PluginLoader::Create(const std::string& type, std::string& err) {
|
|
auto result = CreateNode(type);
|
|
if (result.Failed()) {
|
|
err = result.ErrMessage();
|
|
return NodePtr(nullptr, NodeDeleter{});
|
|
}
|
|
return std::move(result).Take();
|
|
}
|
|
|
|
} // namespace rk3588
|