From 00c3fbde197070126086f28d769366a9738488c2 Mon Sep 17 00:00:00 2001 From: Caitlin Ross Date: Mon, 25 Oct 2021 16:20:38 -0400 Subject: [PATCH] plugin engine: remove registration api and add env var --- docs/user_guide/source/engines/plugin.rst | 6 +- examples/plugins/engine/CMakeLists.txt | 16 +++- examples/plugins/engine/ExampleReadPlugin.cpp | 14 +++ examples/plugins/engine/ExampleReadPlugin.h | 9 ++ .../plugins/engine/ExampleWritePlugin.cpp | 16 ++++ examples/plugins/engine/ExampleWritePlugin.h | 9 ++ .../engine/examplePluginEngine_read.cpp | 10 +- .../engine/examplePluginEngine_write.cpp | 10 +- source/adios2/engine/plugin/PluginEngine.cpp | 93 +++++++------------ source/adios2/engine/plugin/PluginEngine.h | 21 ----- source/adios2/engine/plugin/PluginEngine.inl | 44 --------- source/adios2/helper/adiosDynamicBinder.cpp | 21 ++++- source/adios2/helper/adiosDynamicBinder.h | 1 + testing/install/plugin/CMakeLists.txt | 18 +++- 14 files changed, 137 insertions(+), 151 deletions(-) delete mode 100644 source/adios2/engine/plugin/PluginEngine.inl diff --git a/docs/user_guide/source/engines/plugin.rst b/docs/user_guide/source/engines/plugin.rst index 28815f2f26..5f9f38f388 100644 --- a/docs/user_guide/source/engines/plugin.rst +++ b/docs/user_guide/source/engines/plugin.rst @@ -68,7 +68,7 @@ To build your plugin, your CMake should look something like: target_link_libraries(PluginEngineWrite adios2::cxx11 adios2::core) When using the Plugin Engine, ADIOS will check for your plugin at the path specified in the ``ADIOS2_PLUGIN_PATH`` environment variable. -If ``ADIOS2_PLUGIN_PATH`` is not set, ADIOS will warn you about it, and then look for your plugin in the current working directory. +If ``ADIOS2_PLUGIN_PATH`` is not set, and a path is not specified in the settings for the Plugin Engine (see below steps for using a plugin in your application), then the usual ``dlopen`` search is performed (see `dlopen man page `_). The following steps show how to use your engine plugin in your application. ``examplePluginEngine_write.cpp`` and ``examplePluginEngine_read.cpp`` are an example of how to use the engine plugins described above. @@ -88,10 +88,14 @@ The key steps to use your plugin are: io.SetParameters({{"PluginName", "WritePlugin"}}); io.SetParameters({{"PluginLibrary", "PluginEngineWrite"}}); + // also possible to use the path here instead of setting ADIOS2_PLUGIN_PATH + // io.SetParameters({{"PluginLibrary", "/path/to/libPluginEngineWrite.so"}}) .. note:: You don't need to add the ``lib`` prefix or the shared library ending (e.g., ``.so``, ``.dll``, etc.). ADIOS will add these when searching for your plugin library. + If you do at the prefix/suffix, ADIOS should still be able to find your plugin. + At this point you can open the engine and use it as you would any other ADIOS engine. You also shouldn't need to make any changes to your CMake files for your application. diff --git a/examples/plugins/engine/CMakeLists.txt b/examples/plugins/engine/CMakeLists.txt index a4f3e812c2..e49ccb6d55 100644 --- a/examples/plugins/engine/CMakeLists.txt +++ b/examples/plugins/engine/CMakeLists.txt @@ -3,14 +3,22 @@ # accompanying file Copyright.txt for details. #------------------------------------------------------------------------------# +add_library(PluginEngineWriteExample + ExampleWritePlugin.cpp +) +target_link_libraries(PluginEngineWriteExample adios2::cxx11 adios2_core) + +add_library(PluginEngineReadExample + ExampleReadPlugin.cpp +) +target_link_libraries(PluginEngineReadExample adios2::cxx11 adios2_core) + add_executable(examplePluginEngine_write examplePluginEngine_write.cpp - ExampleWritePlugin.h ExampleWritePlugin.cpp ) -target_link_libraries(examplePluginEngine_write adios2::cxx11 adios2_core) +target_link_libraries(examplePluginEngine_write adios2::cxx11) add_executable(examplePluginEngine_read examplePluginEngine_read.cpp - ExampleReadPlugin.h ExampleReadPlugin.cpp ) -target_link_libraries(examplePluginEngine_read adios2::cxx11 adios2_core) +target_link_libraries(examplePluginEngine_read adios2::cxx11) diff --git a/examples/plugins/engine/ExampleReadPlugin.cpp b/examples/plugins/engine/ExampleReadPlugin.cpp index cb29f28a29..e0a1d8b2a6 100644 --- a/examples/plugins/engine/ExampleReadPlugin.cpp +++ b/examples/plugins/engine/ExampleReadPlugin.cpp @@ -133,3 +133,17 @@ void ExampleReadPlugin::DoClose(const int transportIndex) {} } // end namespace engine } // end namespace core } // end namespace adios2 + +extern "C" { + +adios2::core::engine::ExampleReadPlugin *EngineCreate(adios2::core::IO &io, + const std::string &name, + const adios2::Mode mode, + adios2::helper::Comm comm) +{ + return new adios2::core::engine::ExampleReadPlugin(io, name, mode, + comm.Duplicate()); +} + +void EngineDestroy(adios2::core::engine::ExampleReadPlugin *obj) { delete obj; } +} diff --git a/examples/plugins/engine/ExampleReadPlugin.h b/examples/plugins/engine/ExampleReadPlugin.h index b40efa6ce0..620fc1486c 100644 --- a/examples/plugins/engine/ExampleReadPlugin.h +++ b/examples/plugins/engine/ExampleReadPlugin.h @@ -80,4 +80,13 @@ class ExampleReadPlugin : public PluginEngineInterface } // end namespace engine } // end namespace core } // end namespace adios2 + +extern "C" { + +adios2::core::engine::ExampleReadPlugin * +EngineCreate(adios2::core::IO &io, const std::string &name, + const adios2::Mode mode, adios2::helper::Comm comm); +void EngineDestroy(adios2::core::engine::ExampleReadPlugin *obj); +} + #endif /* EXAMPLEREADPLUGIN_H_ */ diff --git a/examples/plugins/engine/ExampleWritePlugin.cpp b/examples/plugins/engine/ExampleWritePlugin.cpp index 48649827b6..cc18042e7e 100644 --- a/examples/plugins/engine/ExampleWritePlugin.cpp +++ b/examples/plugins/engine/ExampleWritePlugin.cpp @@ -113,3 +113,19 @@ void ExampleWritePlugin::WriteVarsFromIO() } // end namespace engine } // end namespace core } // end namespace adios2 + +extern "C" { + +adios2::core::engine::ExampleWritePlugin * +EngineCreate(adios2::core::IO &io, const std::string &name, + const adios2::Mode mode, adios2::helper::Comm comm) +{ + return new adios2::core::engine::ExampleWritePlugin(io, name, mode, + comm.Duplicate()); +} + +void EngineDestroy(adios2::core::engine::ExampleWritePlugin *obj) +{ + delete obj; +} +} diff --git a/examples/plugins/engine/ExampleWritePlugin.h b/examples/plugins/engine/ExampleWritePlugin.h index 732130deaa..e13eecb184 100644 --- a/examples/plugins/engine/ExampleWritePlugin.h +++ b/examples/plugins/engine/ExampleWritePlugin.h @@ -81,4 +81,13 @@ class ExampleWritePlugin : public PluginEngineInterface } // end namespace engine } // end namespace core } // end namespace adios2 + +extern "C" { + +adios2::core::engine::ExampleWritePlugin * +EngineCreate(adios2::core::IO &io, const std::string &name, + const adios2::Mode mode, adios2::helper::Comm comm); +void EngineDestroy(adios2::core::engine::ExampleWritePlugin *obj); +} + #endif /* EXAMPLEWRITEPLUGIN_H_ */ diff --git a/examples/plugins/engine/examplePluginEngine_read.cpp b/examples/plugins/engine/examplePluginEngine_read.cpp index d9c6b95317..68e980c798 100644 --- a/examples/plugins/engine/examplePluginEngine_read.cpp +++ b/examples/plugins/engine/examplePluginEngine_read.cpp @@ -15,9 +15,6 @@ #include #include "adios2.h" -#include "adios2/engine/plugin/PluginEngine.h" - -#include "ExampleReadPlugin.h" void testStreaming(adios2::Engine &reader, std::vector &myFloats, adios2::Variable &var) @@ -58,10 +55,6 @@ int main(int argc, char *argv[]) std::vector myFloats = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; - /** Register Plugin with the PluginEngine **/ - adios2::core::engine::PluginEngine::RegisterPlugin< - adios2::core::engine::ExampleReadPlugin>("MyPlugin"); - try { /** ADIOS class factory of IO class objects */ @@ -73,7 +66,8 @@ int main(int argc, char *argv[]) /** Engine derived class, spawned to start IO operations */ io.SetEngine("Plugin"); - io.SetParameters({{"PluginName", "MyPlugin"}}); + io.SetParameters({{"PluginName", "ReadPlugin"}}); + io.SetParameters({{"PluginLibrary", "PluginEngineRead"}}); adios2::Engine reader = io.Open("TestPlugin", adios2::Mode::Read); auto var = io.InquireVariable("data"); diff --git a/examples/plugins/engine/examplePluginEngine_write.cpp b/examples/plugins/engine/examplePluginEngine_write.cpp index 5c87f4a7e1..9110995cd3 100644 --- a/examples/plugins/engine/examplePluginEngine_write.cpp +++ b/examples/plugins/engine/examplePluginEngine_write.cpp @@ -15,9 +15,6 @@ #include #include "adios2.h" -#include "adios2/engine/plugin/PluginEngine.h" - -#include "ExampleWritePlugin.h" void testStreaming(adios2::Engine &writer, std::vector &myFloats, adios2::Variable &var) @@ -49,10 +46,6 @@ int main(int argc, char *argv[]) std::vector myFloats = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; const std::size_t Nx = myFloats.size(); - /** Register Plugin with the PluginEngine **/ - adios2::core::engine::PluginEngine::RegisterPlugin< - adios2::core::engine::ExampleWritePlugin>("MyPlugin"); - try { /** ADIOS class factory of IO class objects */ @@ -69,7 +62,8 @@ int main(int argc, char *argv[]) /** Engine derived class, spawned to start IO operations */ io.SetEngine("Plugin"); - io.SetParameters({{"PluginName", "MyPlugin"}}); + io.SetParameters({{"PluginName", "WritePlugin"}}); + io.SetParameters({{"PluginLibrary", "PluginEngineWrite"}}); adios2::Engine writer = io.Open("TestPlugin", adios2::Mode::Write); if (streaming) diff --git a/source/adios2/engine/plugin/PluginEngine.cpp b/source/adios2/engine/plugin/PluginEngine.cpp index fe72c9ebd6..37395a9e7d 100644 --- a/source/adios2/engine/plugin/PluginEngine.cpp +++ b/source/adios2/engine/plugin/PluginEngine.cpp @@ -13,13 +13,14 @@ #include "PluginEngineInterface.h" #include -#include #include #include #include #include "adios2/helper/adiosDynamicBinder.h" +#include + namespace adios2 { namespace core @@ -31,27 +32,12 @@ namespace engine struct PluginEngine::Impl { - using Registry = - std::map>; - static Registry m_Registry; - - std::string m_PluginName; + std::string m_PluginName = "UserPlugin"; std::unique_ptr m_Binder; EngineCreateFun m_HandleCreate; EngineDestroyFun m_HandleDestroy; PluginEngineInterface *m_Plugin = nullptr; }; -PluginEngine::Impl::Registry PluginEngine::Impl::m_Registry; - -/******************************************************************************/ - -void PluginEngine::RegisterPlugin(const std::string pluginName, - EngineCreateFun create, - EngineDestroyFun destroy) -{ - PluginEngine::Impl::m_Registry.emplace(pluginName, - std::make_pair(create, destroy)); -} /******************************************************************************/ @@ -80,56 +66,43 @@ void PluginEngine::EndStep() { m_Impl->m_Plugin->EndStep(); } void PluginEngine::Init() { auto paramPluginNameIt = m_IO.m_Parameters.find("PluginName"); - if (paramPluginNameIt == m_IO.m_Parameters.end()) + if (paramPluginNameIt != m_IO.m_Parameters.end()) + { + m_Impl->m_PluginName = paramPluginNameIt->second; + } + + std::string pluginPath; + adios2sys::SystemTools::GetEnv("ADIOS2_PLUGIN_PATH", pluginPath); + + auto paramPluginLibraryIt = m_IO.m_Parameters.find("PluginLibrary"); + if (paramPluginLibraryIt == m_IO.m_Parameters.end()) { - throw std::invalid_argument("PluginEngine: PluginName must be " - "specified in engine parameters"); + throw std::invalid_argument( + "PluginEngine: PluginLibrary must be specified in " + "engine parameters if no PluginName " + "is specified"); } - m_Impl->m_PluginName = paramPluginNameIt->second; + std::string &pluginLibrary = paramPluginLibraryIt->second; - // First we check to see if we can find the plugin currently registerd - auto registryEntryIt = - PluginEngine::Impl::m_Registry.find(m_Impl->m_PluginName); + m_Impl->m_Binder.reset( + new helper::DynamicBinder(pluginLibrary, pluginPath)); - if (registryEntryIt != PluginEngine::Impl::m_Registry.end()) + m_Impl->m_HandleCreate = reinterpret_cast( + m_Impl->m_Binder->GetSymbol("EngineCreate")); + if (!m_Impl->m_HandleCreate) { - m_Impl->m_HandleCreate = registryEntryIt->second.first; - m_Impl->m_HandleDestroy = registryEntryIt->second.second; + throw std::runtime_error("PluginEngine: Unable to locate " + "EngineCreate symbol in specified plugin " + "library"); } - else + + m_Impl->m_HandleDestroy = reinterpret_cast( + m_Impl->m_Binder->GetSymbol("EngineDestroy")); + if (!m_Impl->m_HandleDestroy) { - // It's not currently registered so try to load it from a shared - // library - // - auto paramPluginLibraryIt = m_IO.m_Parameters.find("PluginLibrary"); - if (paramPluginLibraryIt == m_IO.m_Parameters.end()) - { - throw std::invalid_argument( - "PluginEngine: PluginLibrary must be specified in " - "engine parameters if no PluginName " - "is specified"); - } - std::string &pluginLibrary = paramPluginLibraryIt->second; - - m_Impl->m_Binder.reset(new helper::DynamicBinder(pluginLibrary)); - - m_Impl->m_HandleCreate = reinterpret_cast( - m_Impl->m_Binder->GetSymbol("EngineCreate")); - if (!m_Impl->m_HandleCreate) - { - throw std::runtime_error("PluginEngine: Unable to locate " - "EngineCreate symbol in specified plugin " - "library"); - } - - m_Impl->m_HandleDestroy = reinterpret_cast( - m_Impl->m_Binder->GetSymbol("EngineDestroy")); - if (!m_Impl->m_HandleDestroy) - { - throw std::runtime_error("PluginEngine: Unable to locate " - "EngineDestroy symbol in specified plugin " - "library"); - } + throw std::runtime_error("PluginEngine: Unable to locate " + "EngineDestroy symbol in specified plugin " + "library"); } } diff --git a/source/adios2/engine/plugin/PluginEngine.h b/source/adios2/engine/plugin/PluginEngine.h index 3e8c5a8172..74c74ce370 100644 --- a/source/adios2/engine/plugin/PluginEngine.h +++ b/source/adios2/engine/plugin/PluginEngine.h @@ -18,14 +18,12 @@ #include // for unique_ptr #include // for string #include // for add_pointer -#include // for vector #include "adios2/common/ADIOSMacros.h" #include "adios2/common/ADIOSTypes.h" #include "adios2/core/Engine.h" #include "adios2/core/IO.h" #include "adios2/core/Variable.h" -#include "adios2/core/VariableCompound.h" #include "adios2/helper/adiosComm.h" namespace adios2 @@ -49,23 +47,6 @@ class PluginEngine : public Engine using EngineDestroyFun = std::function::type>; - static void RegisterPlugin(const std::string pluginName, - EngineCreateFun create, - EngineDestroyFun destroy); - static void RegisterPlugin(const std::string pluginName, - EngineCreatePtr create, EngineDestroyPtr destroy) - { - RegisterPlugin(pluginName, EngineCreateFun(create), - EngineDestroyFun(destroy)); - } - - // This is just a shortcut method to handle the case where the class type is - // directly available to the caller so a simple new and delete call is - // sufficient to create and destroy the engine object - template - static void RegisterPlugin(const std::string name); - -public: PluginEngine(IO &io, const std::string &name, const Mode mode, helper::Comm comm); virtual ~PluginEngine(); @@ -99,6 +80,4 @@ class PluginEngine : public Engine } // end namespace core } // end namespace adios2 -#include "PluginEngine.inl" - #endif /* ADIOS2_ENGINE_PLUGIN_PLUGINENGINE_H_ */ diff --git a/source/adios2/engine/plugin/PluginEngine.inl b/source/adios2/engine/plugin/PluginEngine.inl deleted file mode 100644 index 3006e45c53..0000000000 --- a/source/adios2/engine/plugin/PluginEngine.inl +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Distributed under the OSI-approved Apache License, Version 2.0. See - * accompanying file Copyright.txt for details. - * - * PluginEngine.h Support for an engine implemented outside libadios2 - * - * Created on: July 5, 2021 - * Author: Chuck Atkins - * Caitlin Ross - */ - -#ifndef ADIOS2_ENGINE_PLUGIN_ENGINE_INL_ -#define ADIOS2_ENGINE_PLUGIN_ENGINE_INL_ -#ifndef ADIOS2_ENGINE_PLUGIN_PLUGINENGINE_H_ -#error "Inline file should only be included from it's header, never on it's own" -#endif - -#include "PluginEngine.h" - -namespace adios2 -{ -namespace core -{ -namespace engine -{ - -template -void PluginEngine::RegisterPlugin(const std::string name) -{ - EngineCreateFun createFun = - [](IO &io, const std::string &name, const Mode openMode, - helper::Comm comm) -> PluginEngineInterface * { - return new T(io, name, openMode, comm.Duplicate()); - }; - EngineDestroyFun destroyFun = [](Engine *obj) -> void { delete obj; }; - - RegisterPlugin(name, createFun, destroyFun); -} - -} // end namespace engine -} // end namespace core -} // end namespace adios2 - -#endif // ADIOS2_ENGINE_PLUGIN_ENGINE_INL_ diff --git a/source/adios2/helper/adiosDynamicBinder.cpp b/source/adios2/helper/adiosDynamicBinder.cpp index 42d7cfcc0b..79575e53e5 100644 --- a/source/adios2/helper/adiosDynamicBinder.cpp +++ b/source/adios2/helper/adiosDynamicBinder.cpp @@ -18,6 +18,7 @@ #include // for vector #include +#include namespace adios2 { @@ -30,6 +31,12 @@ struct DynamicBinder::Impl }; DynamicBinder::DynamicBinder(std::string libName) +{ + DynamicBinder(libName, ""); +} + +DynamicBinder::DynamicBinder(std::string libName, std::string libPath) +: m_Impl(new Impl) { std::vector libPrefixes; libPrefixes.emplace_back(""); @@ -39,6 +46,7 @@ DynamicBinder::DynamicBinder(std::string libName) #endif std::vector libSuffixes; + libSuffixes.emplace_back(""); #ifdef __APPLE__ libSuffixes.emplace_back(".dylib"); libSuffixes.emplace_back(".so"); @@ -61,7 +69,18 @@ DynamicBinder::DynamicBinder(std::string libName) { for (const std::string &suffix : libSuffixes) { - fileName = prefix + libName + suffix; + if (!libPath.empty()) + { + fileName = libPath + "/" + prefix + libName + suffix; + // Slashes in fileName is correct for unix-like systems + // ConvertToOutputPath() will change slashes if we're running on + // a Windows system + fileName = adios2sys::SystemTools::ConvertToOutputPath(fileName); + } + else + { + fileName = prefix + libName + suffix; + } m_Impl->m_LibraryHandle = adios2sys::DynamicLoader::OpenLibrary(fileName); searchedLibs.push_back(fileName); diff --git a/source/adios2/helper/adiosDynamicBinder.h b/source/adios2/helper/adiosDynamicBinder.h index 55247a0c9a..21f8f9d9de 100644 --- a/source/adios2/helper/adiosDynamicBinder.h +++ b/source/adios2/helper/adiosDynamicBinder.h @@ -27,6 +27,7 @@ class DynamicBinder public: DynamicBinder(std::string libName); + DynamicBinder(std::string libName, std::string libPath); ~DynamicBinder(); VoidSymbolPointer GetSymbol(std::string symbolName); diff --git a/testing/install/plugin/CMakeLists.txt b/testing/install/plugin/CMakeLists.txt index e566715800..2757112dba 100644 --- a/testing/install/plugin/CMakeLists.txt +++ b/testing/install/plugin/CMakeLists.txt @@ -9,20 +9,30 @@ enable_testing() find_package(adios2 REQUIRED) +option(BUILD_SHARED_LIBS "build shared libs" ON) + +add_library(PluginEngineWrite + ../../../examples/plugins/engine/ExampleWritePlugin.cpp +) +target_link_libraries(PluginEngineWrite adios2::cxx11 adios2::core) + +add_library(PluginEngineRead + ../../../examples/plugins/engine/ExampleReadPlugin.cpp +) +target_link_libraries(PluginEngineRead adios2::cxx11 adios2::core) + # add write test add_executable(adios_plugin_write_test ../../../examples/plugins/engine/examplePluginEngine_write.cpp - ../../../examples/plugins/engine/ExampleWritePlugin.cpp ) -target_link_libraries(adios_plugin_write_test adios2::cxx11 adios2::core) +target_link_libraries(adios_plugin_write_test adios2::cxx11) add_test(NAME adios_plugin_write_test COMMAND adios_plugin_write_test) # add read test add_executable(adios_plugin_read_test ../../../examples/plugins/engine/examplePluginEngine_read.cpp - ../../../examples/plugins/engine/ExampleReadPlugin.cpp ) -target_link_libraries(adios_plugin_read_test adios2::cxx11 adios2::core) +target_link_libraries(adios_plugin_read_test adios2::cxx11) add_test(NAME adios_plugin_read_test COMMAND adios_plugin_read_test) set_tests_properties(adios_plugin_read_test PROPERTIES DEPENDS adios_plugin_write_test)