From ee2fd466413e9a96c992d1fbbbd3e8e8b8df7a29 Mon Sep 17 00:00:00 2001 From: Weng Xuetian Date: Tue, 3 Dec 2024 07:50:27 -0800 Subject: [PATCH] Add support for shared addon loader to load multiple libraries (#1195) In general this is not very useful on linux, but for android where LD_LIBRARY_PATH doesn't work, it can be used as an built-in linker to load depenedency libraries. --- src/lib/fcitx/addonloader.cpp | 64 +++++++++++++++-------- src/lib/fcitx/addonloader_p.h | 18 ++++--- test/addon/CMakeLists.txt | 3 ++ test/addon/dummyaddondeps.cpp | 8 +++ test/addon2/fcitx5/addon/dummyaddon2.conf | 2 +- test/testaddon.cpp | 7 +++ 6 files changed, 74 insertions(+), 28 deletions(-) create mode 100644 test/addon/dummyaddondeps.cpp diff --git a/src/lib/fcitx/addonloader.cpp b/src/lib/fcitx/addonloader.cpp index 1270059ce..a069b6a1c 100644 --- a/src/lib/fcitx/addonloader.cpp +++ b/src/lib/fcitx/addonloader.cpp @@ -30,37 +30,59 @@ AddonInstance *SharedLibraryLoader::load(const AddonInfo &info, AddonManager *manager) { auto iter = registry_.find(info.uniqueName()); if (iter == registry_.end()) { - std::string libname = info.library(); - Flags flag = LibraryLoadHint::DefaultHint; - if (stringutils::startsWith(libname, "export:")) { - libname = libname.substr(7); - flag |= LibraryLoadHint::ExportExternalSymbolsHint; - } - auto file = libname + FCITX_LIBRARY_SUFFIX; - auto libs = standardPath_.locateAll(StandardPath::Type::Addon, file); - if (libs.empty()) { - FCITX_ERROR() << "Could not locate library " << file - << " for addon " << info.uniqueName() << "."; + std::vector libnames = + stringutils::split(info.library(), ";"); + + if (libnames.empty()) { + FCITX_ERROR() << "Failed to parse Library field: " << info.library() + << " for addon " << info.uniqueName(); + return nullptr; } - for (const auto &libraryPath : libs) { - Library lib(libraryPath); - if (!lib.load(flag)) { - FCITX_ERROR() - << "Failed to load library for addon " << info.uniqueName() - << " on " << libraryPath << ". Error: " << lib.error(); - continue; + + std::vector libraries; + for (std::string_view libname : libnames) { + Flags flag = LibraryLoadHint::DefaultHint; + if (stringutils::consumePrefix(libname, "export:")) { + flag |= LibraryLoadHint::ExportExternalSymbolsHint; + } + const auto file = + stringutils::concat(libname, FCITX_LIBRARY_SUFFIX); + const auto libraryPaths = + standardPath_.locateAll(StandardPath::Type::Addon, file); + if (libraryPaths.empty()) { + FCITX_ERROR() << "Could not locate library " << file + << " for addon " << info.uniqueName() << "."; } + bool loaded = false; + ; + for (const auto &libraryPath : libraryPaths) { + Library library(libraryPath); + if (library.load(flag)) { + libraries.push_back(std::move(library)); + loaded = true; + break; + } else { + FCITX_ERROR() << "Failed to load library for addon " + << info.uniqueName() << " on " << libraryPath + << ". Error: " << library.error(); + } + } + if (!loaded) { + break; + } + } + + if (libraries.size() == libnames.size()) { try { registry_.emplace(info.uniqueName(), std::make_unique( - info, std::move(lib))); + info, std::move(libraries))); } catch (const std::exception &e) { FCITX_ERROR() << "Failed to initialize addon factory for addon " << info.uniqueName() << ". Error: " << e.what(); } - break; + iter = registry_.find(info.uniqueName()); } - iter = registry_.find(info.uniqueName()); } if (iter == registry_.end()) { diff --git a/src/lib/fcitx/addonloader_p.h b/src/lib/fcitx/addonloader_p.h index e466f2455..66f1d63e8 100644 --- a/src/lib/fcitx/addonloader_p.h +++ b/src/lib/fcitx/addonloader_p.h @@ -29,16 +29,22 @@ constexpr char FCITX_ADDON_FACTORY_ENTRY[] = "fcitx_addon_factory_instance"; class SharedLibraryFactory { public: - SharedLibraryFactory(const AddonInfo &info, Library lib) - : library_(std::move(lib)) { + SharedLibraryFactory(const AddonInfo &info, std::vector libraries) + : libraries_(std::move(libraries)) { std::string v2Name = stringutils::concat(FCITX_ADDON_FACTORY_ENTRY, "_", info.uniqueName()); - auto *funcPtr = library_.resolve(v2Name.data()); + if (libraries_.empty()) { + throw std::runtime_error("Got empty libraries."); + } + + // Only resolve with last library. + auto &library = libraries_.back(); + auto *funcPtr = library.resolve(v2Name.data()); if (!funcPtr) { - funcPtr = library_.resolve(FCITX_ADDON_FACTORY_ENTRY); + funcPtr = library.resolve(FCITX_ADDON_FACTORY_ENTRY); } if (!funcPtr) { - throw std::runtime_error(library_.error()); + throw std::runtime_error(library.error()); } auto func = Library::toFunction(funcPtr); factory_ = func(); @@ -50,7 +56,7 @@ class SharedLibraryFactory { AddonFactory *factory() { return factory_; } private: - Library library_; + std::vector libraries_; AddonFactory *factory_; }; diff --git a/test/addon/CMakeLists.txt b/test/addon/CMakeLists.txt index ed4ce32fa..ba0c0b091 100644 --- a/test/addon/CMakeLists.txt +++ b/test/addon/CMakeLists.txt @@ -1,6 +1,9 @@ add_library(dummyaddon MODULE dummyaddon.cpp) target_link_libraries(dummyaddon Fcitx5::Core) +add_library(dummyaddondeps MODULE dummyaddondeps.cpp) +target_link_libraries(dummyaddondeps Fcitx5::Utils) + set(COPY_ADDON_DEPENDS unicode.conf.in-fmt quickphrase.conf.in-fmt xcb.conf.in-fmt spell.conf.in-fmt) if (ENABLE_KEYBOARD) diff --git a/test/addon/dummyaddondeps.cpp b/test/addon/dummyaddondeps.cpp new file mode 100644 index 000000000..f702003e8 --- /dev/null +++ b/test/addon/dummyaddondeps.cpp @@ -0,0 +1,8 @@ +#include + +FCITX_DEFINE_LOG_CATEGORY(dummyaddondeps, "dummyaddondeps"); + +static int init = []() { + fcitx::Log::setLogRule("default=3"); + return 0; +}(); \ No newline at end of file diff --git a/test/addon2/fcitx5/addon/dummyaddon2.conf b/test/addon2/fcitx5/addon/dummyaddon2.conf index 643d23e83..8c9a42afb 100644 --- a/test/addon2/fcitx5/addon/dummyaddon2.conf +++ b/test/addon2/fcitx5/addon/dummyaddon2.conf @@ -1,7 +1,7 @@ [Addon] Name=dummyaddon2 Type=SharedLibrary -Library=libdummyaddon +Library=libdummyaddondeps;libdummyaddon Category=Module [Addon/Dependencies] diff --git a/test/testaddon.cpp b/test/testaddon.cpp index daa141b99..5c847949d 100644 --- a/test/testaddon.cpp +++ b/test/testaddon.cpp @@ -41,5 +41,12 @@ int main() { FCITX_ASSERT(addon2); auto *addon3 = manager.addon("dummyaddon3"); FCITX_ASSERT(!addon3); + // loading dummyaddondeps will change log rule level to 3. + FCITX_ASSERT( + !fcitx::Log::defaultCategory().checkLogLevel(fcitx::LogLevel::Info)); + FCITX_ASSERT( + fcitx::Log::defaultCategory().checkLogLevel(fcitx::LogLevel::Warn)); + fcitx::Log::setLogRule("default=5"); + return 0; }