Skip to content

Commit

Permalink
Add support for shared addon loader to load multiple libraries (#1195)
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
wengxt authored Dec 3, 2024
1 parent 0833bbe commit ee2fd46
Show file tree
Hide file tree
Showing 6 changed files with 74 additions and 28 deletions.
64 changes: 43 additions & 21 deletions src/lib/fcitx/addonloader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<LibraryLoadHint> 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<std::string> 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<Library> libraries;
for (std::string_view libname : libnames) {
Flags<LibraryLoadHint> 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<SharedLibraryFactory>(
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()) {
Expand Down
18 changes: 12 additions & 6 deletions src/lib/fcitx/addonloader_p.h
Original file line number Diff line number Diff line change
Expand Up @@ -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<Library> 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<AddonFactory *()>(funcPtr);
factory_ = func();
Expand All @@ -50,7 +56,7 @@ class SharedLibraryFactory {
AddonFactory *factory() { return factory_; }

private:
Library library_;
std::vector<Library> libraries_;
AddonFactory *factory_;
};

Expand Down
3 changes: 3 additions & 0 deletions test/addon/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -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)
Expand Down
8 changes: 8 additions & 0 deletions test/addon/dummyaddondeps.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
#include <fcitx-utils/log.h>

FCITX_DEFINE_LOG_CATEGORY(dummyaddondeps, "dummyaddondeps");

static int init = []() {
fcitx::Log::setLogRule("default=3");
return 0;
}();
2 changes: 1 addition & 1 deletion test/addon2/fcitx5/addon/dummyaddon2.conf
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
[Addon]
Name=dummyaddon2
Type=SharedLibrary
Library=libdummyaddon
Library=libdummyaddondeps;libdummyaddon
Category=Module

[Addon/Dependencies]
Expand Down
7 changes: 7 additions & 0 deletions test/testaddon.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}

0 comments on commit ee2fd46

Please sign in to comment.