Skip to content

Commit

Permalink
feat(setup): find and load external RIME plugins as shared libs #431
Browse files Browse the repository at this point in the history
  • Loading branch information
lotem authored and LEOYoon-Tsaw committed Jan 18, 2021
1 parent c5ffba6 commit 27563c7
Show file tree
Hide file tree
Showing 7 changed files with 150 additions and 6 deletions.
17 changes: 17 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,11 @@ option(ENABLE_LOGGING "Enable logging with google-glog library" ON)
option(BOOST_USE_CXX11 "Boost has been built with C++11 support" OFF)
option(BOOST_USE_SIGNALS2 "Boost use signals2 instead of signals" ON)
option(ENABLE_ASAN "Enable Address Sanitizer (Unix Only)" OFF)
option(INSTALL_PRIVATE_HEADERS "Install private headers (usually needed for externally built Rime plugins)" OFF)
option(ENABLE_EXTERNAL_PLUGINS "Enable loading of externally built Rime plugins (from directory set by RIME_PLUGINS_DIR variable)" OFF)

set(RIME_DATA_DIR "${CMAKE_INSTALL_FULL_DATADIR}/rime-data" CACHE STRING "Target directory for Rime data")
set(RIME_PLUGINS_DIR "${CMAKE_INSTALL_FULL_LIBDIR}/rime-plugins" CACHE STRING "Target directory for externally built Rime plugins")

if(WIN32)
set(ext ".exe")
Expand Down Expand Up @@ -183,6 +186,7 @@ if(${CMAKE_SYSTEM_NAME} MATCHES "Linux|FreeBSD|DragonFly|GNU")
set(bindir "${CMAKE_INSTALL_FULL_BINDIR}")
set(libdir "${CMAKE_INSTALL_FULL_LIBDIR}")
set(pkgdatadir "${RIME_DATA_DIR}")
set(pluginsdir "${RIME_PLUGINS_DIR}")
set(includedir "${CMAKE_INSTALL_FULL_INCLUDEDIR}")
configure_file(
${PROJECT_SOURCE_DIR}/rime.pc.in
Expand All @@ -198,6 +202,19 @@ install(FILES cmake/RimeConfig.cmake
file(GLOB rime_public_header_files ${PROJECT_SOURCE_DIR}/src/*.h)
install(FILES ${rime_public_header_files}
DESTINATION ${CMAKE_INSTALL_FULL_INCLUDEDIR})
if(INSTALL_PRIVATE_HEADERS)
file(GLOB rime_private_header_files
${PROJECT_SOURCE_DIR}/src/rime/*.h
${PROJECT_BINARY_DIR}/src/rime/*.h)
install(FILES ${rime_private_header_files}
DESTINATION ${CMAKE_INSTALL_FULL_INCLUDEDIR}/rime)
foreach(rime_private_header_files_dir algo config dict gear lever)
file(GLOB rime_private_header_files
${PROJECT_SOURCE_DIR}/src/rime/${rime_private_header_files_dir}/*.h)
install(FILES ${rime_private_header_files}
DESTINATION ${CMAKE_INSTALL_FULL_INCLUDEDIR}/rime/${rime_private_header_files_dir})
endforeach()
endif()

if(BUILD_DATA)
file(GLOB rime_preset_data_files ${PROJECT_SOURCE_DIR}/data/preset/*.yaml)
Expand Down
9 changes: 6 additions & 3 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -42,21 +42,24 @@ release:
cmake . -B$(build) \
-DCMAKE_INSTALL_PREFIX=$(prefix) \
-DCMAKE_BUILD_TYPE=Release \
-DBUILD_MERGED_PLUGINS=OFF
-DBUILD_MERGED_PLUGINS=OFF \
-DENABLE_EXTERNAL_PLUGINS=ON
cmake --build $(build)

merged-plugins:
cmake . -B$(build) \
-DCMAKE_INSTALL_PREFIX=$(prefix) \
-DCMAKE_BUILD_TYPE=Release \
-DBUILD_MERGED_PLUGINS=ON
-DBUILD_MERGED_PLUGINS=ON \
-DENABLE_EXTERNAL_PLUGINS=OFF
cmake --build $(build)

debug:
cmake . -B$(build) \
-DCMAKE_INSTALL_PREFIX=$(prefix) \
-DCMAKE_BUILD_TYPE=Debug \
-DBUILD_MERGED_PLUGINS=OFF
-DBUILD_MERGED_PLUGINS=OFF \
-DENABLE_EXTERNAL_PLUGINS=ON
cmake --build $(build)

install:
Expand Down
20 changes: 17 additions & 3 deletions plugins/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,26 @@ set(RIME_SOURCE_DIR ${PROJECT_SOURCE_DIR})
set(LIBRARY_OUTPUT_PATH ${PROJECT_BINARY_DIR}/lib)

# work around CMake build issues on macOS
aux_source_directory(. rime_plugins_src)
set(rime_plugin_boilerplate_src "plugin.cc")

set(plugins_module_src "plugins_module.cc")

unset(plugins_objs)
unset(plugins_deps)
unset(plugins_modules)

if(ENABLE_EXTERNAL_PLUGINS)
add_library(rime-plugins-objs OBJECT ${plugins_module_src})
if(BUILD_SHARED_LIBS)
set_target_properties(rime-plugins-objs
PROPERTIES
POSITION_INDEPENDENT_CODE ON)
endif()

set(plugins_objs ${plugins_objs} $<TARGET_OBJECTS:rime-plugins-objs>)
set(plugins_modules ${plugins_modules} "plugins")
endif()

if(DEFINED ENV{RIME_PLUGINS})
set(plugins $ENV{RIME_PLUGINS})
message(STATUS "Prescribed plugins: ${plugins}")
Expand Down Expand Up @@ -38,12 +52,12 @@ foreach(plugin ${plugins})
set(plugins_modules ${plugins_modules} ${plugin_modules})
else()
message(STATUS "Plugin ${plugin_name} provides modules: ${plugin_modules}")
add_library(${plugin_name} ${rime_plugins_src} ${plugin_objs})
add_library(${plugin_name} ${rime_plugin_boilerplate_src} ${plugin_objs})
target_link_libraries(${plugin_name} ${plugin_deps})
if(XCODE_VERSION)
set_target_properties(${plugin_name} PROPERTIES INSTALL_NAME_DIR "@rpath")
endif(XCODE_VERSION)
install(TARGETS ${plugin_name} DESTINATION ${CMAKE_INSTALL_FULL_LIBDIR})
install(TARGETS ${plugin_name} DESTINATION ${RIME_PLUGINS_DIR})
endif()
endforeach(plugin)

Expand Down
103 changes: 103 additions & 0 deletions plugins/plugins_module.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
//
// Copyright RIME Developers
// Distributed under the BSD License
//

#include <boost/algorithm/string.hpp>
#include <boost/dll.hpp>
#include <boost/filesystem.hpp>
#include <rime/build_config.h>
#include <rime/common.h>
#include <rime/component.h>
#include <rime/module.h>
#include <rime/registry.h>
#include <rime_api.h>

namespace fs = boost::filesystem;

namespace rime {

class PluginManager {
public:
void LoadPlugins(fs::path plugins_dir);

static string plugin_name_of(fs::path plugin_file);

static PluginManager& instance();

private:
PluginManager() = default;

map<string, boost::dll::shared_library> plugin_libs_;
};

void PluginManager::LoadPlugins(fs::path plugins_dir) {
const fs::perms exe_perms = (fs::owner_exe | fs::group_exe | fs::others_exe);
ModuleManager& mm = ModuleManager::instance();
if (!fs::is_directory(plugins_dir)) {
return;
}
LOG(INFO) << "loading plugins from " << plugins_dir;
for (fs::directory_iterator iter(plugins_dir), end; iter != end; ++iter) {
fs::path plugin_file = iter->path();
if (plugin_file.extension() == boost::dll::shared_library::suffix()) {
fs::file_status plugin_file_status = fs::status(plugin_file);
if (fs::is_regular_file(plugin_file_status) &&
fs::status(plugin_file).permissions() & exe_perms) {
DLOG(INFO) << "found plugin: " << plugin_file;
string plugin_name = plugin_name_of(plugin_file);
if (plugin_libs_.find(plugin_name) == plugin_libs_.end()) {
LOG(INFO) << "loading plugin '" << plugin_name
<< "' from " << plugin_file;
try {
auto plugin_lib = boost::dll::shared_library(plugin_file);
plugin_libs_[plugin_name] = plugin_lib;
} catch (const std::exception& ex) {
LOG(ERROR) << "error loading plugin " << plugin_name << ": "
<< ex.what();
continue;
}
}
if (RimeModule* module = mm.Find(plugin_name)) {
mm.LoadModule(module);
LOG(INFO) << "loaded plugin: " << plugin_name;
} else {
LOG(WARNING) << "module '" << plugin_name
<< "' is not provided by plugin library " << plugin_file;
}
}
}
}
}

string PluginManager::plugin_name_of(fs::path plugin_file) {
string name = plugin_file.stem().string();
// remove prefix "(lib)rime-"
if (boost::starts_with(name, "librime-")) {
boost::erase_first(name, "librime-");
} else if (boost::starts_with(name, "rime-")) {
boost::erase_first(name, "rime-");
}
// replace dash with underscore, for the plugin name is part of the module
// initializer function name.
boost::replace_all(name, "-", "_");
return name;
}

PluginManager& PluginManager::instance() {
static the<PluginManager> s_instance;
if (!s_instance) {
s_instance.reset(new PluginManager);
}
return *s_instance;
}

} // namespace rime

static void rime_plugins_initialize() {
rime::PluginManager::instance().LoadPlugins(RIME_PLUGINS_DIR);
}

static void rime_plugins_finalize() {}

RIME_REGISTER_MODULE(plugins)
1 change: 1 addition & 0 deletions rime.pc.in
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ exec_prefix=@exec_prefix@
libdir=@libdir@
includedir=@includedir@
pkgdatadir=@pkgdatadir@
pluginsdir=@pluginsdir@

Name: Rime
Description: Rime Input Method Engine
Expand Down
3 changes: 3 additions & 0 deletions src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,9 @@ set(rime_optional_deps "")
if(Gflags_FOUND)
set(rime_optional_deps ${rime_optional_deps} ${Gflags_LIBRARY})
endif()
if(ENABLE_EXTERNAL_PLUGINS)
set(rime_optional_deps ${rime_optional_deps} dl)
endif()

set(rime_core_deps
${Boost_LIBRARIES}
Expand Down
3 changes: 3 additions & 0 deletions src/rime/build_config.h.in
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,7 @@
#cmakedefine RIME_BOOST_SIGNALS2
#cmakedefine RIME_ENABLE_LOGGING

#cmakedefine RIME_DATA_DIR "@RIME_DATA_DIR@"
#cmakedefine RIME_PLUGINS_DIR "@RIME_PLUGINS_DIR@"

#endif // RIME_BUILD_CONFIG_H_

0 comments on commit 27563c7

Please sign in to comment.