mirror of
https://github.com/FairRootGroup/FairMQ.git
synced 2025-10-12 16:21:13 +00:00
FairMQ: Add plugin mechanism (Plugin and PluginManager classes)
This commit is contained in:
parent
ac69607250
commit
60d929b0bd
|
@ -88,6 +88,8 @@ set(FAIRMQ_HEADER_FILES
|
|||
options/FairMQSuboptParser.h
|
||||
options/FairProgOptions.h
|
||||
options/FairProgOptionsHelper.h
|
||||
Plugin.h
|
||||
PluginManager.h
|
||||
runFairMQDevice.h
|
||||
shmem/FairMQMessageSHM.h
|
||||
shmem/FairMQPollerSHM.h
|
||||
|
@ -141,6 +143,8 @@ set(FAIRMQ_SOURCE_FILES
|
|||
options/FairMQProgOptions.cxx
|
||||
options/FairMQSuboptParser.cxx
|
||||
options/FairProgOptions.cxx
|
||||
Plugin.cxx
|
||||
PluginManager.cxx
|
||||
shmem/FairMQMessageSHM.cxx
|
||||
shmem/FairMQPollerSHM.cxx
|
||||
shmem/FairMQSocketSHM.cxx
|
||||
|
|
26
fairmq/Plugin.cxx
Normal file
26
fairmq/Plugin.cxx
Normal file
|
@ -0,0 +1,26 @@
|
|||
/********************************************************************************
|
||||
* Copyright (C) 2017 GSI Helmholtzzentrum fuer Schwerionenforschung GmbH *
|
||||
* *
|
||||
* This software is distributed under the terms of the *
|
||||
* GNU Lesser General Public Licence (LGPL) version 3, *
|
||||
* copied verbatim in the file "LICENSE" *
|
||||
********************************************************************************/
|
||||
|
||||
#include <fairmq/Plugin.h>
|
||||
#include <FairMQLogger.h>
|
||||
|
||||
using namespace std;
|
||||
|
||||
fair::mq::Plugin::Plugin(const string name, const Version version, const string maintainer, const string homepage)
|
||||
: fkName(name)
|
||||
, fkVersion(version)
|
||||
, fkMaintainer(maintainer)
|
||||
, fkHomepage(homepage)
|
||||
{
|
||||
LOG(DEBUG) << "Loaded plugin: " << *this;
|
||||
}
|
||||
|
||||
fair::mq::Plugin::~Plugin()
|
||||
{
|
||||
LOG(DEBUG) << "Unloaded plugin: " << *this;
|
||||
}
|
91
fairmq/Plugin.h
Normal file
91
fairmq/Plugin.h
Normal file
|
@ -0,0 +1,91 @@
|
|||
/********************************************************************************
|
||||
* Copyright (C) 2017 GSI Helmholtzzentrum fuer Schwerionenforschung GmbH *
|
||||
* *
|
||||
* This software is distributed under the terms of the *
|
||||
* GNU Lesser General Public Licence (LGPL) version 3, *
|
||||
* copied verbatim in the file "LICENSE" *
|
||||
********************************************************************************/
|
||||
|
||||
#ifndef FAIR_MQ_PLUGIN_H
|
||||
#define FAIR_MQ_PLUGIN_H
|
||||
|
||||
#include <fairmq/tools/Strings.h>
|
||||
#include <boost/dll/alias.hpp>
|
||||
#include <boost/optional.hpp>
|
||||
#include <boost/program_options.hpp>
|
||||
#include <functional>
|
||||
#include <ostream>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <tuple>
|
||||
#include <utility>
|
||||
|
||||
namespace fair
|
||||
{
|
||||
namespace mq
|
||||
{
|
||||
|
||||
/**
|
||||
* @class Plugin Plugin.h <fairmq/Plugin.h>
|
||||
* @brief Base class for FairMQ plugins
|
||||
*
|
||||
* The plugin base class encapsulates the plugin metadata.
|
||||
*/
|
||||
class Plugin
|
||||
{
|
||||
public:
|
||||
|
||||
struct Version
|
||||
{
|
||||
const int fkMajor, fkMinor, fkPatch;
|
||||
|
||||
friend auto operator< (const Version& lhs, const Version& rhs) -> bool { return std::tie(lhs.fkMajor, lhs.fkMinor, lhs.fkPatch) < std::tie(rhs.fkMajor, rhs.fkMinor, rhs.fkPatch); }
|
||||
friend auto operator> (const Version& lhs, const Version& rhs) -> bool { return rhs < lhs; }
|
||||
friend auto operator<=(const Version& lhs, const Version& rhs) -> bool { return !(lhs > rhs); }
|
||||
friend auto operator>=(const Version& lhs, const Version& rhs) -> bool { return !(lhs < rhs); }
|
||||
friend auto operator==(const Version& lhs, const Version& rhs) -> bool { return std::tie(lhs.fkMajor, lhs.fkMinor, lhs.fkPatch) == std::tie(rhs.fkMajor, rhs.fkMinor, rhs.fkPatch); }
|
||||
friend auto operator!=(const Version& lhs, const Version& rhs) -> bool { return !(lhs == rhs); }
|
||||
friend auto operator<<(std::ostream& os, const Version& v) -> std::ostream& { return os << v.fkMajor << "." << v.fkMinor << "." << v.fkPatch; }
|
||||
};
|
||||
|
||||
Plugin() = delete;
|
||||
Plugin(const std::string name, const Version version, const std::string maintainer, const std::string homepage);
|
||||
virtual ~Plugin();
|
||||
|
||||
auto GetName() const -> const std::string& { return fkName; }
|
||||
auto GetVersion() const -> const Version { return fkVersion; }
|
||||
auto GetMaintainer() const -> const std::string& { return fkMaintainer; }
|
||||
auto GetHomepage() const -> const std::string& { return fkHomepage; }
|
||||
|
||||
friend auto operator==(const Plugin& lhs, const Plugin& rhs) -> bool { return std::make_tuple(lhs.GetName(), lhs.GetVersion()) == std::make_tuple(rhs.GetName(), rhs.GetVersion()); }
|
||||
friend auto operator!=(const Plugin& lhs, const Plugin& rhs) -> bool { return !(lhs == rhs); }
|
||||
friend auto operator<<(std::ostream& os, const Plugin& p) -> std::ostream&
|
||||
{
|
||||
return os << "'" << p.GetName() << "', "
|
||||
<< "version '" << p.GetVersion() << "', "
|
||||
<< "maintainer '" << p.GetMaintainer() << "', "
|
||||
<< "homepage '" << p.GetHomepage() << "'";
|
||||
}
|
||||
static auto NoProgramOptions() -> const boost::optional<boost::program_options::options_description> { return boost::none; }
|
||||
|
||||
private:
|
||||
|
||||
const std::string fkName;
|
||||
const Version fkVersion;
|
||||
const std::string fkMaintainer;
|
||||
const std::string fkHomepage;
|
||||
|
||||
}; /* class Plugin */
|
||||
|
||||
} /* namespace mq */
|
||||
} /* namespace fair */
|
||||
|
||||
#define REGISTER_FAIRMQ_PLUGIN(KLASS, NAME, VERSION, MAINTAINER, HOMEPAGE, PROGOPTIONS) \
|
||||
static auto Make_##NAME##_Plugin() -> std::shared_ptr<KLASS> \
|
||||
{ \
|
||||
return std::make_shared<KLASS>(std::string{#NAME}, VERSION, std::string{MAINTAINER}, std::string{HOMEPAGE}); \
|
||||
} \
|
||||
BOOST_DLL_ALIAS(Make_##NAME##_Plugin, make_##NAME##_plugin) \
|
||||
BOOST_DLL_ALIAS(PROGOPTIONS, get_##NAME##_plugin_progoptions)
|
||||
|
||||
#endif /* FAIR_MQ_PLUGIN_H */
|
199
fairmq/PluginManager.cxx
Normal file
199
fairmq/PluginManager.cxx
Normal file
|
@ -0,0 +1,199 @@
|
|||
/********************************************************************************
|
||||
* Copyright (C) 2017 GSI Helmholtzzentrum fuer Schwerionenforschung GmbH *
|
||||
* *
|
||||
* This software is distributed under the terms of the *
|
||||
* GNU Lesser General Public Licence (LGPL) version 3, *
|
||||
* copied verbatim in the file "LICENSE" *
|
||||
********************************************************************************/
|
||||
|
||||
#include <fairmq/PluginManager.h>
|
||||
#include <fairmq/Tools.h>
|
||||
#include <boost/program_options.hpp>
|
||||
#include <algorithm>
|
||||
#include <iterator>
|
||||
#include <memory>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
|
||||
using namespace std;
|
||||
using fair::mq::tools::ToString;
|
||||
using fair::mq::tools::ToStrVector;
|
||||
using fair::mq::Plugin;
|
||||
namespace fs = boost::filesystem;
|
||||
namespace po = boost::program_options;
|
||||
namespace dll = boost::dll;
|
||||
using boost::optional;
|
||||
|
||||
const std::string fair::mq::PluginManager::fgkLibPrefix = "FairMQPlugin_";
|
||||
|
||||
fair::mq::PluginManager::PluginManager()
|
||||
: fSearchPaths({"."})
|
||||
{
|
||||
}
|
||||
|
||||
auto fair::mq::PluginManager::ValidateSearchPath(const fs::path& path) -> void
|
||||
{
|
||||
if (path.empty()) throw BadSearchPath{"Specified path is empty."};
|
||||
// we ignore non-existing search paths
|
||||
if (fs::exists(path) && !fs::is_directory(path)) throw BadSearchPath{ToString(path, " is not a directory.")};
|
||||
}
|
||||
|
||||
auto fair::mq::PluginManager::SetSearchPaths(const vector<fs::path>& searchPaths) -> void
|
||||
{
|
||||
for_each(begin(searchPaths), end(searchPaths), ValidateSearchPath);
|
||||
fSearchPaths = searchPaths;
|
||||
}
|
||||
|
||||
auto fair::mq::PluginManager::AppendSearchPath(const fs::path& path) -> void
|
||||
{
|
||||
ValidateSearchPath(path);
|
||||
fSearchPaths.push_back(path);
|
||||
}
|
||||
|
||||
auto fair::mq::PluginManager::PrependSearchPath(const fs::path& path) -> void
|
||||
{
|
||||
ValidateSearchPath(path);
|
||||
fSearchPaths.insert(begin(fSearchPaths), path);
|
||||
}
|
||||
|
||||
auto fair::mq::PluginManager::ProgramOptions() -> const optional<po::options_description>
|
||||
{
|
||||
auto plugin_options = po::options_description{"Plugin Manager"};
|
||||
plugin_options.add_options()
|
||||
("plugin-search-path,S", po::value<vector<string>>()->multitoken(),
|
||||
"List of plugin search paths.\n\n"
|
||||
"* Override default search path, e.g.\n"
|
||||
" -S /home/user/lib /lib\n"
|
||||
"* Append(>) or prepend(<) to default search path, e.g.\n"
|
||||
" -S >/lib </home/user/lib\n"
|
||||
"* If you mix the overriding and appending/prepending syntaxes, the overriding paths act as default search path, e.g.\n"
|
||||
" -S /usr/lib >/lib </home/user/lib /usr/local/lib results in /home/user/lib,/usr/local/lib,/usr/lib/,/lib")
|
||||
("plugin,P", po::value<vector<string>>(),
|
||||
"List of plugin names to load in order, e.g. if the file is called 'libFairMQPlugin_example.so', just list 'example' or 'd:example' here. To load a static plugin, list 's:example' here (The plugin must be already linked into the executable).");
|
||||
return plugin_options;
|
||||
}
|
||||
|
||||
auto fair::mq::PluginManager::MakeFromCommandLineOptions(const vector<string> args) -> shared_ptr<PluginManager>
|
||||
{
|
||||
// Parse command line options
|
||||
auto options = ProgramOptions().value();
|
||||
auto vm = po::variables_map{};
|
||||
try
|
||||
{
|
||||
auto parsed = po::command_line_parser(args).options(options).allow_unregistered().run();
|
||||
po::store(parsed, vm);
|
||||
po::notify(vm);
|
||||
} catch (const po::error& e)
|
||||
{
|
||||
throw ProgramOptionsParseError{ToString("Error occured while parsing the 'Plugin Manager' program options: ", e.what())};
|
||||
}
|
||||
|
||||
// Process plugin search paths
|
||||
auto append = vector<fs::path>{};
|
||||
auto prepend = vector<fs::path>{};
|
||||
auto searchPaths = vector<fs::path>{};
|
||||
if (vm.count("plugin-search-path"))
|
||||
{
|
||||
for (const auto& path : vm["plugin-search-path"].as<vector<string>>())
|
||||
{
|
||||
if (path.substr(0, 1) == "<") { prepend.emplace_back(path.substr(1)); }
|
||||
else if (path.substr(0, 1) == ">") { append.emplace_back(path.substr(1)); }
|
||||
else { searchPaths.emplace_back(path); }
|
||||
}
|
||||
}
|
||||
|
||||
// Create PluginManager with supplied options
|
||||
auto mgr = make_shared<PluginManager>();
|
||||
mgr->SetSearchPaths(searchPaths);
|
||||
for(const auto& path : prepend) { mgr->PrependSearchPath(path); }
|
||||
for(const auto& path : append) { mgr->AppendSearchPath(path); }
|
||||
if (vm.count("plugin"))
|
||||
{
|
||||
for (const auto& plugin : vm["plugin"].as<vector<string>>())
|
||||
{
|
||||
mgr->LoadPlugin(plugin);
|
||||
}
|
||||
}
|
||||
|
||||
// Return the plugin manager and command line options, that have not been recognized.
|
||||
return mgr;
|
||||
}
|
||||
|
||||
auto fair::mq::PluginManager::LoadPlugin(const string& pluginName) -> void
|
||||
{
|
||||
if (pluginName.substr(0,2) == "s:")
|
||||
{
|
||||
// Mechanism A: static
|
||||
LoadPluginStatic(pluginName.substr(2));
|
||||
}
|
||||
else if (pluginName.substr(0,2) == "d:")
|
||||
{
|
||||
// Mechanism B: dynamic
|
||||
LoadPluginDynamic(pluginName.substr(2));
|
||||
}
|
||||
else
|
||||
{
|
||||
// Mechanism B: dynamic (default)
|
||||
LoadPluginDynamic(pluginName);
|
||||
}
|
||||
}
|
||||
|
||||
auto fair::mq::PluginManager::LoadPluginStatic(const string& pluginName) -> void
|
||||
{
|
||||
// Load symbol
|
||||
if (fPluginFactories.find(pluginName) == fPluginFactories.end())
|
||||
{
|
||||
try
|
||||
{
|
||||
LoadSymbols(pluginName, dll::program_location());
|
||||
}
|
||||
catch (boost::system::system_error& e)
|
||||
{
|
||||
throw PluginLoadError(ToString("An error occurred while loading static plugin ", pluginName, ": ", e.what()));
|
||||
}
|
||||
}
|
||||
|
||||
InstantiatePlugin(pluginName);
|
||||
}
|
||||
|
||||
auto fair::mq::PluginManager::LoadPluginDynamic(const string& pluginName) -> void
|
||||
{
|
||||
// Search plugin and load, if found
|
||||
if (fPluginFactories.find(pluginName) == fPluginFactories.end())
|
||||
{
|
||||
auto success = false;
|
||||
for(const auto& searchPath : SearchPaths())
|
||||
{
|
||||
try
|
||||
{
|
||||
LoadSymbols(
|
||||
pluginName,
|
||||
searchPath / ToString(LibPrefix(), pluginName),
|
||||
dll::load_mode::append_decorations
|
||||
);
|
||||
|
||||
success = true;
|
||||
break;
|
||||
}
|
||||
catch (boost::system::system_error& e)
|
||||
{
|
||||
if(string{e.what()}.find("No such file or directory") == string::npos)
|
||||
{
|
||||
throw PluginLoadError(ToString("An error occurred while loading dynamic plugin ", pluginName, ": ", e.what()));
|
||||
}
|
||||
}
|
||||
}
|
||||
if(!success) throw PluginLoadError(ToString("The plugin ", pluginName, " could not be found in the plugin search paths."));
|
||||
}
|
||||
|
||||
InstantiatePlugin(pluginName);
|
||||
}
|
||||
|
||||
auto fair::mq::PluginManager::InstantiatePlugin(const string& pluginName) -> void
|
||||
{
|
||||
if (fPlugins.find(pluginName) == fPlugins.end())
|
||||
{
|
||||
fPlugins[pluginName] = fPluginFactories[pluginName]();
|
||||
fPluginOrder.push_back(pluginName);
|
||||
}
|
||||
}
|
116
fairmq/PluginManager.h
Normal file
116
fairmq/PluginManager.h
Normal file
|
@ -0,0 +1,116 @@
|
|||
/********************************************************************************
|
||||
* Copyright (C) 2017 GSI Helmholtzzentrum fuer Schwerionenforschung GmbH *
|
||||
* *
|
||||
* This software is distributed under the terms of the *
|
||||
* GNU Lesser General Public Licence (LGPL) version 3, *
|
||||
* copied verbatim in the file "LICENSE" *
|
||||
********************************************************************************/
|
||||
|
||||
#ifndef FAIR_MQ_PLUGINMANAGER_H
|
||||
#define FAIR_MQ_PLUGINMANAGER_H
|
||||
|
||||
#include <fairmq/Plugin.h>
|
||||
#include <fairmq/Tools.h>
|
||||
#define BOOST_FILESYSTEM_VERSION 3
|
||||
#define BOOST_FILESYSTEM_NO_DEPRECATED
|
||||
#include <boost/filesystem.hpp>
|
||||
#include <boost/optional.hpp>
|
||||
#include <boost/program_options.hpp>
|
||||
#include <boost/dll/import.hpp>
|
||||
#include <boost/dll/shared_library.hpp>
|
||||
#include <boost/dll/runtime_symbol_info.hpp>
|
||||
#include <functional>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <tuple>
|
||||
#include <tuple>
|
||||
#include <vector>
|
||||
|
||||
namespace fair
|
||||
{
|
||||
namespace mq
|
||||
{
|
||||
|
||||
/**
|
||||
* @class PluginManager PluginManager.h <fairmq/PluginManager.h>
|
||||
* @brief manages and owns plugin instances
|
||||
*
|
||||
* The plugin manager is responsible for the whole plugin lifecycle. It
|
||||
* facilitates three plugin mechanisms:
|
||||
* A static (built-in) plugins
|
||||
* B dynamic plugins (shared libraries)
|
||||
*/
|
||||
class PluginManager
|
||||
{
|
||||
public:
|
||||
|
||||
using PluginFactory = std::shared_ptr<fair::mq::Plugin>();
|
||||
using PluginProgOptions = const boost::optional<boost::program_options::options_description>();
|
||||
|
||||
PluginManager();
|
||||
|
||||
auto SetSearchPaths(const std::vector<boost::filesystem::path>&) -> void;
|
||||
auto AppendSearchPath(const boost::filesystem::path&) -> void;
|
||||
auto PrependSearchPath(const boost::filesystem::path&) -> void;
|
||||
auto SearchPaths() const -> const std::vector<boost::filesystem::path>& { return fSearchPaths; }
|
||||
struct BadSearchPath : std::invalid_argument { using std::invalid_argument::invalid_argument; };
|
||||
|
||||
auto LoadPlugin(const std::string& pluginName) -> void;
|
||||
auto LoadPlugins(const std::vector<std::string>& pluginNames) -> void { for(const auto& pluginName : pluginNames) { LoadPlugin(pluginName); } }
|
||||
struct PluginLoadError : std::runtime_error { using std::runtime_error::runtime_error; };
|
||||
|
||||
static auto ProgramOptions() -> const boost::optional<boost::program_options::options_description>;
|
||||
static auto MakeFromCommandLineOptions(const std::vector<std::string>) -> std::shared_ptr<PluginManager>;
|
||||
struct ProgramOptionsParseError : std::runtime_error { using std::runtime_error::runtime_error; };
|
||||
|
||||
static auto LibPrefix() -> const std::string& { return fgkLibPrefix; }
|
||||
|
||||
auto ForEachPlugin(std::function<void (Plugin&)> func) -> void { for(const auto& p : fPluginOrder) { func(*fPlugins[p]); } }
|
||||
auto ForEachPluginProgOptions(std::function<void (const boost::program_options::options_description&)> func) const -> void { for(const auto& pair : fPluginProgOptions) { func(pair.second); } }
|
||||
|
||||
private:
|
||||
|
||||
static auto ValidateSearchPath(const boost::filesystem::path&) -> void;
|
||||
|
||||
auto LoadPluginStatic(const std::string& pluginName) -> void;
|
||||
auto LoadPluginDynamic(const std::string& pluginName) -> void;
|
||||
template<typename... Args>
|
||||
auto LoadSymbols(const std::string& pluginName, Args&&... args) -> void
|
||||
{
|
||||
using namespace boost::dll;
|
||||
using fair::mq::tools::ToString;
|
||||
|
||||
auto lib = shared_library{std::forward<Args>(args)...};
|
||||
|
||||
fPluginFactories[pluginName] = import_alias<PluginFactory>(
|
||||
shared_library{lib},
|
||||
ToString("make_", pluginName, "_plugin")
|
||||
);
|
||||
|
||||
try
|
||||
{
|
||||
fPluginProgOptions.insert({
|
||||
pluginName,
|
||||
lib.get_alias<PluginProgOptions>(ToString("get_", pluginName, "_plugin_progoptions"))().value()
|
||||
});
|
||||
}
|
||||
catch (const boost::bad_optional_access& e) { /* just ignore, if no prog options are declared */ }
|
||||
}
|
||||
|
||||
auto InstantiatePlugin(const std::string& pluginName) -> void;
|
||||
|
||||
static const std::string fgkLibPrefix;
|
||||
std::vector<boost::filesystem::path> fSearchPaths;
|
||||
std::map<std::string, std::function<PluginFactory>> fPluginFactories;
|
||||
std::map<std::string, std::shared_ptr<Plugin>> fPlugins;
|
||||
std::vector<std::string> fPluginOrder;
|
||||
std::map<std::string, boost::program_options::options_description> fPluginProgOptions;
|
||||
|
||||
}; /* class PluginManager */
|
||||
|
||||
} /* namespace mq */
|
||||
} /* namespace fair */
|
||||
|
||||
#endif /* FAIR_MQ_PLUGINMANAGER_H */
|
|
@ -12,6 +12,7 @@
|
|||
// IWYU pragma: begin_exports
|
||||
#include <fairmq/tools/CppSTL.h>
|
||||
#include <fairmq/tools/Network.h>
|
||||
#include <fairmq/tools/Strings.h>
|
||||
// IWYU pragma: end_exports
|
||||
|
||||
#endif // FAIR_MQ_TOOLS_H
|
||||
|
|
|
@ -77,6 +77,58 @@ add_testsuite(FairMQ.Device
|
|||
RUN_SERIAL ON
|
||||
)
|
||||
|
||||
set(VERSION_MAJOR 1)
|
||||
set(VERSION_MINOR 1)
|
||||
set(VERSION_PATCH 0)
|
||||
set(VERSION ${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_PATCH})
|
||||
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/helper/plugins/dummy.h.in ${CMAKE_CURRENT_BINARY_DIR}/helper/plugins/dummy.h)
|
||||
add_testlib(FairMQPlugin_test_dummy
|
||||
SOURCES
|
||||
${CMAKE_CURRENT_BINARY_DIR}/helper/plugins/dummy.h
|
||||
helper/plugins/dummy.cxx
|
||||
|
||||
LINKS FairMQ
|
||||
INCLUDES ${CMAKE_CURRENT_BINARY_DIR}/helper/plugins
|
||||
HIDDEN
|
||||
VERSION ${VERSION}
|
||||
)
|
||||
|
||||
set(VERSION_MAJOR 2)
|
||||
set(VERSION_MINOR 2)
|
||||
set(VERSION_PATCH 0)
|
||||
set(VERSION ${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_PATCH})
|
||||
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/helper/plugins/dummy2.h.in ${CMAKE_CURRENT_BINARY_DIR}/helper/plugins/dummy2.h)
|
||||
add_testlib(FairMQPlugin_test_dummy2
|
||||
SOURCES
|
||||
${CMAKE_CURRENT_BINARY_DIR}/helper/plugins/dummy2.h
|
||||
helper/plugins/dummy2.cxx
|
||||
|
||||
LINKS FairMQ
|
||||
INCLUDES ${CMAKE_CURRENT_BINARY_DIR}/helper/plugins
|
||||
HIDDEN
|
||||
VERSION ${VERSION}
|
||||
)
|
||||
|
||||
add_testsuite(FairMQ.Plugins
|
||||
SOURCES
|
||||
plugins/runner.cxx
|
||||
plugins/_plugin.cxx
|
||||
plugins/_plugin_manager.cxx
|
||||
|
||||
LINKS FairMQ
|
||||
DEPENDS FairMQPlugin_test_dummy FairMQPlugin_test_dummy2
|
||||
TIMEOUT 10
|
||||
)
|
||||
|
||||
add_testsuite(FairMQ.PluginsStatic
|
||||
SOURCES
|
||||
plugins/runner.cxx
|
||||
plugins/_plugin_manager_static.cxx
|
||||
|
||||
LINKS FairMQ FairMQPlugin_test_dummy FairMQPlugin_test_dummy2
|
||||
TIMEOUT 10
|
||||
)
|
||||
|
||||
##############################
|
||||
# Aggregate all test targets #
|
||||
##############################
|
||||
|
|
9
fairmq/test/helper/plugins/dummy.cxx
Normal file
9
fairmq/test/helper/plugins/dummy.cxx
Normal file
|
@ -0,0 +1,9 @@
|
|||
/********************************************************************************
|
||||
* Copyright (C) 2017 GSI Helmholtzzentrum fuer Schwerionenforschung GmbH *
|
||||
* *
|
||||
* This software is distributed under the terms of the *
|
||||
* GNU Lesser General Public Licence (LGPL) version 3, *
|
||||
* copied verbatim in the file "LICENSE" *
|
||||
********************************************************************************/
|
||||
|
||||
#include <dummy.h>
|
62
fairmq/test/helper/plugins/dummy.h.in
Normal file
62
fairmq/test/helper/plugins/dummy.h.in
Normal file
|
@ -0,0 +1,62 @@
|
|||
/********************************************************************************
|
||||
* Copyright (C) 2017 GSI Helmholtzzentrum fuer Schwerionenforschung GmbH *
|
||||
* *
|
||||
* This software is distributed under the terms of the *
|
||||
* GNU Lesser General Public Licence (LGPL) version 3, *
|
||||
* copied verbatim in the file "LICENSE" *
|
||||
********************************************************************************/
|
||||
|
||||
#ifndef FAIR_MQ_TEST_PLUGIN_DUMMY
|
||||
#define FAIR_MQ_TEST_PLUGIN_DUMMY
|
||||
|
||||
#include <fairmq/Plugin.h>
|
||||
|
||||
#include <string>
|
||||
#include <tuple>
|
||||
#include <vector>
|
||||
|
||||
namespace fair
|
||||
{
|
||||
namespace mq
|
||||
{
|
||||
namespace test
|
||||
{
|
||||
|
||||
class DummyPlugin : public fair::mq::Plugin
|
||||
{
|
||||
public:
|
||||
|
||||
DummyPlugin(const std::string name, const Version version, const std::string maintainer, const std::string homepage)
|
||||
: Plugin(name, version, maintainer, homepage)
|
||||
{
|
||||
}
|
||||
|
||||
}; /* class DummyPlugin */
|
||||
|
||||
auto DummyPluginProgramOptions() -> const boost::optional<boost::program_options::options_description>
|
||||
{
|
||||
using namespace boost::program_options;
|
||||
using std::string;
|
||||
|
||||
auto plugin_options = options_description{"Dummy Plugin"};
|
||||
plugin_options.add_options()
|
||||
("custom-dummy-option", value<string>(), "Cool custom option.");
|
||||
("custom-dummy-option2", value<string>(), "Another cool custom option.");
|
||||
return plugin_options;
|
||||
}
|
||||
|
||||
REGISTER_FAIRMQ_PLUGIN(
|
||||
DummyPlugin, // Class name
|
||||
test_dummy, // Plugin name (string, lower case chars only)
|
||||
(fair::mq::Plugin::Version{@VERSION_MAJOR@,@VERSION_MINOR@,@VERSION_PATCH@}), // Version
|
||||
"Mr. Dummy <dummy@test.net>", // Maintainer
|
||||
"https://git.test.net/dummy.git", // Homepage
|
||||
fair::mq::test::DummyPluginProgramOptions // Free function which declares custom program options for the plugin
|
||||
// signature: () -> boost::optional<boost::program_options::options_description>
|
||||
)
|
||||
|
||||
} /* namespace test */
|
||||
} /* namespace mq */
|
||||
} /* namespace fair */
|
||||
|
||||
#endif /* FAIR_MQ_TEST_PLUGIN_DUMMY */
|
9
fairmq/test/helper/plugins/dummy2.cxx
Normal file
9
fairmq/test/helper/plugins/dummy2.cxx
Normal file
|
@ -0,0 +1,9 @@
|
|||
/********************************************************************************
|
||||
* Copyright (C) 2017 GSI Helmholtzzentrum fuer Schwerionenforschung GmbH *
|
||||
* *
|
||||
* This software is distributed under the terms of the *
|
||||
* GNU Lesser General Public Licence (LGPL) version 3, *
|
||||
* copied verbatim in the file "LICENSE" *
|
||||
********************************************************************************/
|
||||
|
||||
#include <dummy2.h>
|
44
fairmq/test/helper/plugins/dummy2.h.in
Normal file
44
fairmq/test/helper/plugins/dummy2.h.in
Normal file
|
@ -0,0 +1,44 @@
|
|||
/********************************************************************************
|
||||
* Copyright (C) 2017 GSI Helmholtzzentrum fuer Schwerionenforschung GmbH *
|
||||
* *
|
||||
* This software is distributed under the terms of the *
|
||||
* GNU Lesser General Public Licence (LGPL) version 3, *
|
||||
* copied verbatim in the file "LICENSE" *
|
||||
********************************************************************************/
|
||||
|
||||
#ifndef FAIR_MQ_TEST_PLUGIN_DUMMY2
|
||||
#define FAIR_MQ_TEST_PLUGIN_DUMMY2
|
||||
|
||||
#include <fairmq/Plugin.h>
|
||||
|
||||
namespace fair
|
||||
{
|
||||
namespace mq
|
||||
{
|
||||
namespace test
|
||||
{
|
||||
|
||||
class Dummy2Plugin : public fair::mq::Plugin
|
||||
{
|
||||
public:
|
||||
|
||||
Dummy2Plugin(const std::string name, const Version version, const std::string maintainer, const std::string homepage)
|
||||
: Plugin(name, version, maintainer, homepage)
|
||||
{
|
||||
}
|
||||
}; /* class Dummy2Plugin */
|
||||
|
||||
REGISTER_FAIRMQ_PLUGIN(
|
||||
Dummy2Plugin,
|
||||
test_dummy2,
|
||||
(Plugin::Version{@VERSION_MAJOR@,@VERSION_MINOR@,@VERSION_PATCH@}),
|
||||
"Mr. Dummy <dummy@test.net>",
|
||||
"https://git.test.net/dummy.git",
|
||||
fair::mq::Plugin::NoProgramOptions
|
||||
)
|
||||
|
||||
} /* namespace test */
|
||||
} /* namespace mq */
|
||||
} /* namespace fair */
|
||||
|
||||
#endif /* FAIR_MQ_TEST_PLUGIN_DUMMY */
|
52
fairmq/test/plugins/_plugin.cxx
Normal file
52
fairmq/test/plugins/_plugin.cxx
Normal file
|
@ -0,0 +1,52 @@
|
|||
/********************************************************************************
|
||||
* Copyright (C) 2017 GSI Helmholtzzentrum fuer Schwerionenforschung GmbH *
|
||||
* *
|
||||
* This software is distributed under the terms of the *
|
||||
* GNU Lesser General Public Licence (LGPL) version 3, *
|
||||
* copied verbatim in the file "LICENSE" *
|
||||
********************************************************************************/
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
#include <fairmq/Plugin.h>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
using namespace std;
|
||||
using fair::mq::Plugin;
|
||||
|
||||
TEST(Plugin, Operators)
|
||||
{
|
||||
auto p1 = Plugin{"dds", {1, 0, 0}, "Foo Bar <foo.bar@test.net>", "https://git.test.net/dds.git"};
|
||||
auto p2 = Plugin{"dds", {1, 0, 0}, "Foo Bar <foo.bar@test.net>", "https://git.test.net/dds.git"};
|
||||
auto p3 = Plugin{"file", {1, 0, 0}, "Foo Bar <foo.bar@test.net>", "https://git.test.net/file.git"};
|
||||
EXPECT_EQ(p1, p2);
|
||||
EXPECT_NE(p1, p3);
|
||||
}
|
||||
|
||||
TEST(Plugin, OstreamOperators)
|
||||
{
|
||||
auto p1 = Plugin{"dds", {1, 0, 0}, "Foo Bar <foo.bar@test.net>", "https://git.test.net/dds.git"};
|
||||
stringstream ss;
|
||||
ss << p1;
|
||||
EXPECT_EQ(ss.str(), string{"'dds', version '1.0.0', maintainer 'Foo Bar <foo.bar@test.net>', homepage 'https://git.test.net/dds.git'"});
|
||||
}
|
||||
|
||||
TEST(PluginVersion, Operators)
|
||||
{
|
||||
struct Plugin::Version v1{1, 0, 0};
|
||||
struct Plugin::Version v2{1, 0, 0};
|
||||
struct Plugin::Version v3{1, 2, 0};
|
||||
EXPECT_EQ(v1, v2);
|
||||
EXPECT_NE(v1, v3);
|
||||
EXPECT_GT(v3, v2);
|
||||
EXPECT_LT(v1, v3);
|
||||
EXPECT_GE(v3, v2);
|
||||
EXPECT_GE(v2, v1);
|
||||
EXPECT_LE(v1, v2);
|
||||
EXPECT_LE(v2, v3);
|
||||
}
|
||||
|
||||
} /* namespace */
|
103
fairmq/test/plugins/_plugin_manager.cxx
Normal file
103
fairmq/test/plugins/_plugin_manager.cxx
Normal file
|
@ -0,0 +1,103 @@
|
|||
/********************************************************************************
|
||||
* Copyright (C) 2017 GSI Helmholtzzentrum fuer Schwerionenforschung GmbH *
|
||||
* *
|
||||
* This software is distributed under the terms of the *
|
||||
* GNU Lesser General Public Licence (LGPL) version 3, *
|
||||
* copied verbatim in the file "LICENSE" *
|
||||
********************************************************************************/
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
#include <fairmq/PluginManager.h>
|
||||
#include <FairMQLogger.h>
|
||||
#include <fstream>
|
||||
#include <vector>
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
using namespace fair::mq;
|
||||
using namespace boost::filesystem;
|
||||
using namespace boost::program_options;
|
||||
using namespace std;
|
||||
|
||||
TEST(PluginManager, LoadPlugin)
|
||||
{
|
||||
auto mgr = PluginManager{};
|
||||
mgr.PrependSearchPath("./lib");
|
||||
|
||||
ASSERT_NO_THROW(mgr.LoadPlugin("test_dummy"));
|
||||
ASSERT_NO_THROW(mgr.LoadPlugin("test_dummy2"));
|
||||
|
||||
// check order
|
||||
auto expected = vector<string>{"test_dummy", "test_dummy2"};
|
||||
auto actual = vector<string>{};
|
||||
mgr.ForEachPlugin([&](Plugin& plugin){ actual.push_back(plugin.GetName()); });
|
||||
ASSERT_TRUE(actual == expected);
|
||||
|
||||
// program options
|
||||
auto count = 0;
|
||||
mgr.ForEachPluginProgOptions([&count](const options_description& d){ ++count; });
|
||||
ASSERT_EQ(count, 1);
|
||||
}
|
||||
|
||||
TEST(PluginManager, Factory)
|
||||
{
|
||||
const auto args = vector<string>{"-l", "debug", "--help", "-S", ">/lib", "</home/user/lib", "/usr/local/lib", "/usr/lib"};
|
||||
auto mgr = PluginManager::MakeFromCommandLineOptions(args);
|
||||
const auto path1 = path{"/home/user/lib"};
|
||||
const auto path2 = path{"/usr/local/lib"};
|
||||
const auto path3 = path{"/usr/lib"};
|
||||
const auto path4 = path{"/lib"};
|
||||
const auto expected = vector<path>{path1, path2, path3, path4};
|
||||
ASSERT_TRUE(static_cast<bool>(mgr));
|
||||
ASSERT_TRUE(mgr->SearchPaths() == expected);
|
||||
}
|
||||
|
||||
TEST(PluginManager, SearchPathValidation)
|
||||
{
|
||||
const auto path1 = path{"/tmp/test1"};
|
||||
const auto path2 = path{"/tmp/test2"};
|
||||
const auto path3 = path{"/tmp/test3"};
|
||||
auto mgr = PluginManager{};
|
||||
|
||||
mgr.SetSearchPaths({path1, path2});
|
||||
auto expected = vector<path>{path1, path2};
|
||||
ASSERT_EQ(mgr.SearchPaths(), expected);
|
||||
|
||||
mgr.AppendSearchPath(path3);
|
||||
expected = vector<path>{path1, path2, path3};
|
||||
ASSERT_EQ(mgr.SearchPaths(), expected);
|
||||
|
||||
mgr.PrependSearchPath(path3);
|
||||
expected = vector<path>{path3, path1, path2, path3};
|
||||
ASSERT_EQ(mgr.SearchPaths(), expected);
|
||||
}
|
||||
|
||||
TEST(PluginManager, SearchPaths)
|
||||
{
|
||||
const auto temp = temp_directory_path() / unique_path();
|
||||
create_directories(temp);
|
||||
const auto non_existing_dir = temp / "non-existing-dir";
|
||||
const auto existing_dir = temp / "existing-dir";
|
||||
create_directories(existing_dir);
|
||||
const auto existing_file = temp / "existing-file.so";
|
||||
std::fstream fs;
|
||||
fs.open(existing_file.string(), std::fstream::out);
|
||||
fs.close();
|
||||
const auto empty_path = path{""};
|
||||
|
||||
auto mgr = PluginManager{};
|
||||
ASSERT_NO_THROW(mgr.AppendSearchPath(non_existing_dir));
|
||||
ASSERT_NO_THROW(mgr.AppendSearchPath(existing_dir));
|
||||
ASSERT_THROW(mgr.AppendSearchPath(existing_file), PluginManager::BadSearchPath);
|
||||
ASSERT_NO_THROW(mgr.PrependSearchPath(non_existing_dir));
|
||||
ASSERT_NO_THROW(mgr.PrependSearchPath(existing_dir));
|
||||
ASSERT_THROW(mgr.PrependSearchPath(existing_file), PluginManager::BadSearchPath);
|
||||
ASSERT_NO_THROW(mgr.SetSearchPaths({non_existing_dir, existing_dir}));
|
||||
ASSERT_THROW(mgr.SetSearchPaths({non_existing_dir, existing_file}), PluginManager::BadSearchPath);
|
||||
ASSERT_THROW(mgr.SetSearchPaths({non_existing_dir, empty_path}), PluginManager::BadSearchPath);
|
||||
|
||||
remove_all(temp);
|
||||
}
|
||||
|
||||
} /* namespace */
|
28
fairmq/test/plugins/_plugin_manager_static.cxx
Normal file
28
fairmq/test/plugins/_plugin_manager_static.cxx
Normal file
|
@ -0,0 +1,28 @@
|
|||
/********************************************************************************
|
||||
* Copyright (C) 2017 GSI Helmholtzzentrum fuer Schwerionenforschung GmbH *
|
||||
* *
|
||||
* This software is distributed under the terms of the *
|
||||
* GNU Lesser General Public Licence (LGPL) version 3, *
|
||||
* copied verbatim in the file "LICENSE" *
|
||||
********************************************************************************/
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
#include <fairmq/PluginManager.h>
|
||||
#include <FairMQLogger.h>
|
||||
#include <vector>
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
using namespace fair::mq;
|
||||
using namespace std;
|
||||
|
||||
TEST(PluginManager, LoadPluginStatic)
|
||||
{
|
||||
auto mgr = PluginManager{};
|
||||
|
||||
ASSERT_NO_THROW(mgr.LoadPlugin("s:test_dummy"));
|
||||
ASSERT_NO_THROW(mgr.LoadPlugin("s:test_dummy2"));
|
||||
}
|
||||
|
||||
} /* namespace */
|
16
fairmq/test/plugins/runner.cxx
Normal file
16
fairmq/test/plugins/runner.cxx
Normal file
|
@ -0,0 +1,16 @@
|
|||
/********************************************************************************
|
||||
* Copyright (C) 2017 GSI Helmholtzzentrum fuer Schwerionenforschung GmbH *
|
||||
* *
|
||||
* This software is distributed under the terms of the *
|
||||
* GNU Lesser General Public Licence (LGPL) version 3, *
|
||||
* copied verbatim in the file "LICENSE" *
|
||||
********************************************************************************/
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
::testing::InitGoogleTest(&argc, argv);
|
||||
::testing::FLAGS_gtest_death_test_style = "threadsafe";
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
|
@ -5,6 +5,7 @@
|
|||
|
||||
#include <fairmq/tools/CppSTL.h>
|
||||
#include <fairmq/tools/Network.h>
|
||||
#include <fairmq/tools/Strings.h>
|
||||
|
||||
namespace FairMQ
|
||||
{
|
||||
|
@ -17,6 +18,8 @@ using fair::mq::tools::getHostIPs;
|
|||
using fair::mq::tools::getInterfaceIP;
|
||||
using fair::mq::tools::getDefaultRouteNetworkInterface;
|
||||
|
||||
using fair::mq::tools::S;
|
||||
|
||||
} // namespace tools
|
||||
} // namespace FairMQ
|
||||
|
||||
|
|
|
@ -36,7 +36,7 @@ namespace tools
|
|||
{
|
||||
|
||||
// returns a map with network interface names as keys and their IP addresses as values
|
||||
int getHostIPs(std::map<std::string, std::string>& addressMap)
|
||||
inline int getHostIPs(std::map<std::string, std::string>& addressMap)
|
||||
{
|
||||
struct ifaddrs *ifaddr, *ifa;
|
||||
int s;
|
||||
|
@ -73,7 +73,7 @@ int getHostIPs(std::map<std::string, std::string>& addressMap)
|
|||
}
|
||||
|
||||
// get IP address of a given interface name
|
||||
std::string getInterfaceIP(std::string interface)
|
||||
inline std::string getInterfaceIP(std::string interface)
|
||||
{
|
||||
std::map<std::string, std::string> IPs;
|
||||
getHostIPs(IPs);
|
||||
|
@ -89,7 +89,7 @@ std::string getInterfaceIP(std::string interface)
|
|||
}
|
||||
|
||||
// get name of the default route interface
|
||||
std::string getDefaultRouteNetworkInterface()
|
||||
inline std::string getDefaultRouteNetworkInterface()
|
||||
{
|
||||
std::array<char, 128> buffer;
|
||||
std::string interfaceName;
|
||||
|
|
53
fairmq/tools/Strings.h
Normal file
53
fairmq/tools/Strings.h
Normal file
|
@ -0,0 +1,53 @@
|
|||
/********************************************************************************
|
||||
* Copyright (C) 2017 GSI Helmholtzzentrum fuer Schwerionenforschung GmbH *
|
||||
* *
|
||||
* This software is distributed under the terms of the *
|
||||
* GNU Lesser General Public Licence (LGPL) version 3, *
|
||||
* copied verbatim in the file "LICENSE" *
|
||||
********************************************************************************/
|
||||
|
||||
#ifndef FAIR_MQ_TOOLS_STRINGS_H
|
||||
#define FAIR_MQ_TOOLS_STRINGS_H
|
||||
|
||||
#include <initializer_list>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace fair
|
||||
{
|
||||
namespace mq
|
||||
{
|
||||
namespace tools
|
||||
{
|
||||
|
||||
/// @brief concatenates a variable number of args with the << operator via a stringstream
|
||||
/// @param t objects to be concatenated
|
||||
/// @return concatenated string
|
||||
template<typename ... T>
|
||||
auto ToString(T&&... t) -> std::string
|
||||
{
|
||||
std::stringstream ss;
|
||||
(void)std::initializer_list<int>{(ss << t, 0)...};
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
/// @brief convert command line arguments from main function to vector of strings
|
||||
inline auto ToStrVector(const int argc, const char* argv[], const bool dropProgramName = true) -> std::vector<std::string>
|
||||
{
|
||||
auto res = std::vector<std::string>{};
|
||||
if (dropProgramName)
|
||||
{
|
||||
res.assign(argv + 1, argv + argc);
|
||||
} else
|
||||
{
|
||||
res.assign(argv, argv + argc);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
} /* namespace tools */
|
||||
} /* namespace mq */
|
||||
} /* namespace fair */
|
||||
|
||||
#endif /* FAIR_MQ_TOOLS_STRINGS_H */
|
Loading…
Reference in New Issue
Block a user