diff --git a/src/utils/CMakeLists.txt b/src/utils/CMakeLists.txt index a13986a..1cd5557 100644 --- a/src/utils/CMakeLists.txt +++ b/src/utils/CMakeLists.txt @@ -1,128 +1,137 @@ # headers only module # ------------------- add_custom_target(utils_inc SOURCES #log.hpp # sparse solvers # -------------- sparse_solvers/sparse_solver_base.hpp sparse_solvers/sparse_solver.hpp sparse_solvers/sparse_solver_structs.hpp sparse_solvers/sparse_qr.hpp sparse_solvers/sparse_lu.hpp sparse_solvers/sparse_bicgstab.hpp sparse_solvers/sparse_gmres.hpp options_handler.hpp perfs_handler.hpp compat.hpp value_checker.hpp ) set(SPECMICP_COMMON_LIBRARY_FILES log.cpp dateandtime.cpp timer.cpp moving_average.cpp + plugins/dynamic_library.cpp + ../physics/laws.cpp ../physics/units.cpp ../physics/io/units.cpp io/format.cpp io/csv_formatter.cpp io/yaml.cpp ) # hdf5 # ----- if (HDF5_FOUND) list(APPEND SPECMICP_COMMON_LIBRARY_FILES io/specmicp_hdf5.cpp ) include_directories(${HDF5_INCLUDE_DIRS}) set_source_files_properties(io/specmicp_hdf5.cpp PROPERTIES COMPILE_DEFINITIONS HDF5_DEFINITIONS) add_custom_target(utils_io_hdf5_inc SOURCES io/hdf5_eigen.hpp io/hdf5_eigen.inl ) endif() set_pgo_flag(${SPECMICP_COMMON_LIBRARY_FILES}) add_library(objspecmicp_common OBJECT ${SPECMICP_COMMON_LIBRARY_FILES}) set_property(TARGET objspecmicp_common PROPERTY POSITION_INDEPENDENT_CODE 1) add_library(specmicp_common SHARED $) + +if (UNIX) + set(SPECMICP_COMMON_LINK dl) +else() + message(FATAL_ERROR "Plugin system only for POSIX at this time !") +endif() + if (HDF5_FOUND) - target_link_libraries(specmicp_common ${HDF5_LIBRARIES}) + set(SPECMICP_COMMON_LINK "${HDF5_LIBRARIES};${SPECMICP_COMMON_LINK}") endif() +target_link_libraries(specmicp_common ${SPECMICP_COMMON_LINK}) + install(TARGETS specmicp_common LIBRARY DESTINATION ${LIBRARY_INSTALL_DIR} ) set(UTILS_INCLUDE_LIST log.hpp options_handler.hpp perfs_handler.hpp moving_average.hpp timer.hpp dateandtime.hpp compat.hpp ) set(UTILS_SPARSE_INCLUDE_LIST # sparse solvers # -------------- sparse_solvers/sparse_solver.hpp sparse_solvers/sparse_solver_base.hpp sparse_solvers/sparse_solver_structs.hpp sparse_solvers/sparse_qr.hpp sparse_solvers/sparse_lu.hpp sparse_solvers/sparse_bicgstab.hpp sparse_solvers/sparse_gmres.hpp ) set(UTILS_IO_INCLUDE_LIST io/format.hpp io/csv_formatter.hpp io/yaml.hpp ) install(FILES ${UTILS_INCLUDE_LIST} DESTINATION ${INCLUDE_INSTALL_DIR}/utils ) install(FILES ${UTILS_SPARSE_INCLUDE_LIST} DESTINATION ${INCLUDE_INSTALL_DIR}/utils/sparse_solvers ) install(FILES ${UTILS_IO_INCLUDE_LIST} DESTINATION ${INCLUDE_INSTALL_DIR}/utils/io ) # static libraries # ---------------- if(SPECMICP_BUILD_STATIC) add_library(specmicp_common_static STATIC $) install(TARGETS specmicp_common_static ARCHIVE DESTINATION ${STATIC_LIBRARY_INSTALL_DIR} ) else() add_library(specmicp_common_static EXCLUDE_FROM_ALL STATIC $) - endif() - set_target_properties(specmicp_common_static PROPERTIES OUTPUT_NAME specmicp_common) -if (HDF5_FOUND) - target_link_libraries(specmicp_common_static ${HDF5_LIBRARIES}) -endif() +target_link_libraries(specmicp_common_static ${SPECMICP_COMMON_LINK}) +set_target_properties(specmicp_common_static PROPERTIES OUTPUT_NAME specmicp_common) + diff --git a/src/utils/plugins/dynamic_library.cpp b/src/utils/plugins/dynamic_library.cpp new file mode 100644 index 0000000..ce94aa7 --- /dev/null +++ b/src/utils/plugins/dynamic_library.cpp @@ -0,0 +1,105 @@ +/*------------------------------------------------------------------------------- + +Copyright (c) 2015 F. Georget , Princeton University +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this +list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, +this list of conditions and the following disclaimer in the documentation and/or +other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its contributors +may be used to endorse or promote products derived from this software without +specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +-----------------------------------------------------------------------------*/ + +#include "dynamic_library.hpp" + +#include + +#include "../compat.hpp" + +#include "../log.hpp" + +namespace specmicp { +namespace plugins { + +DynamicLibraryPtr DynamicLibrary::load(const std::string& path, std::string& error) +{ + void* handle = dlopen(path.c_str(), RTLD_LAZY); + + if (not handle) + { + error = "Error when loading dynamic library : " + path + ".\n"; + const char* str_error = dlerror(); + if (str_error != NULL) + { + error += str_error; + } + ERROR << error; + return nullptr; + } + + return std::unique_ptr(new DynamicLibrary(handle)); +} + +DynamicLibrary::DynamicLibrary(void *handle): + m_handle(handle) +{ + +} + +DynamicLibrary::~DynamicLibrary() +{ + if (m_handle) + { + int ret = dlclose(m_handle); + if (ret > 0) + { + ERROR << dlerror(); + } + } +} + + +void* DynamicLibrary::get_symbol( + const std::string& name, + std::string& error) +{ + if (not m_handle) + { + error = "The library is not open !"; + return nullptr; + } + void* symbol = dlsym(m_handle, name.c_str()); + const char* str_error = dlerror(); + if (str_error != NULL) + { + error = "Failed to load symbol '" + name + "'\n"; + error += str_error; + + return nullptr; + } + return symbol; + +} + +} //end namespace plugins +} //end namespace specmicp diff --git a/src/utils/plugins/dynamic_library.hpp b/src/utils/plugins/dynamic_library.hpp new file mode 100644 index 0000000..30c0fb5 --- /dev/null +++ b/src/utils/plugins/dynamic_library.hpp @@ -0,0 +1,89 @@ +/*------------------------------------------------------------------------------- + +Copyright (c) 2014,2015 F. Georget , Princeton University +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this +list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, +this list of conditions and the following disclaimer in the documentation and/or +other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its contributors +may be used to endorse or promote products derived from this software without +specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +-----------------------------------------------------------------------------*/ + +#ifndef SPECMICP_COMMON_PLUGIN_DYNAMICLIBRARY_HPP +#define SPECMICP_COMMON_PLUGIN_DYNAMICLIBRARY_HPP + +#include +#include + +//! \file dynamic_library.hpp +//! \brief Dynamic library loader +//! +//! Load the libraries and the symbols within. +//! +//! \warning Only implemented for POSIX platforms + +namespace specmicp { +//! \namespace plugins +//! \brief Plugin framework +namespace plugins { + +class DynamicLibrary; +//! \brief Pointer to a dynamic library +//! +//! All this stuff should be managed in only one place +//! +//! \internal +using DynamicLibraryPtr = std::unique_ptr; + +//! \brief A dynamic library +//! +//! \internal +class DynamicLibrary +{ +public: + + //! \brief Create a dynamic library + static DynamicLibraryPtr load(const std::string& path, std::string& error); + + ~DynamicLibrary(); + + //! \brief Return a symbol + void* get_symbol(const std::string& name, std::string& error); + +private: + // Cannot be created directly + DynamicLibrary(void* handle); + + // Non copyable + DynamicLibrary(const DynamicLibrary& other) = delete; + DynamicLibrary& operator=(const DynamicLibrary& other) = delete; + + void* m_handle; //!< The library handle +}; + + +} //end namespace plugins +} //end namespace specmicp + +#endif // SPECMICP_COMMON_PLUGIN_DYNAMICLIBRARY_HPP diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index a688f39..f1c9a79 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -1,82 +1,88 @@ ################## Tests ######################################## include(CatchTest) set(PROJECT_TEST_DIR ${CMAKE_CURRENT_SOURCE_DIR}) include_directories(${PROJECT_TEST_DIR}) add_custom_target(check COMMAND ${CMAKE_CTEST_COMMAND}) # make check is also valid # Catch test #=========== #common set( COMMON_TEST_DIR common ) +# mock test library +add_library(test_to_load SHARED ${COMMON_TEST_DIR}/test_to_load.cpp) +set_target_properties(test_to_load PROPERTIES PREFIX "") + set(SPECMICP_COMMON_TEST_FILES ${COMMON_TEST_DIR}/test_common.cpp ${COMMON_TEST_DIR}/units.cpp ${COMMON_TEST_DIR}/laws.cpp ${COMMON_TEST_DIR}/misc.cpp ${COMMON_TEST_DIR}/value_checker.cpp ${COMMON_TEST_DIR}/sparse_solvers.cpp + ${COMMON_TEST_DIR}/dynamic_loader.cpp ) if (HDF5_FOUND) list(APPEND SPECMICP_COMMON_TEST_FILES ${COMMON_TEST_DIR}/hdf5_eigen.cpp ) - include_directories(${HDF5_INCLUDE_DIRS}) set_source_files_properties(${COMMON_TEST_DIR}/hdf5_eigen.cpp PROPERTIES COMPILE_DEFINITIONS HDF5_DEFINITIONS) endif() add_catch_test(NAME common SOURCES ${SPECMICP_COMMON_TEST_FILES} LINK_LIBRARIES specmicp_common_static + DEPENDS test_to_load ) + # MiCPSolver # ---------- set(MICPSOLVER_TEST_DIR micpsolver) add_catch_test(NAME micpsolver SOURCES ${MICPSOLVER_TEST_DIR}/test_micpsolver.cpp ${MICPSOLVER_TEST_DIR}/condition_number.cpp ${MICPSOLVER_TEST_DIR}/ncp_functions.cpp ${MICPSOLVER_TEST_DIR}/micpsolver.cpp LINK_LIBRARIES specmicp_common_static ) # ODEInt # ---------- set(ODEINT_TEST_DIR odeint) add_catch_test(NAME odeint SOURCES ${ODEINT_TEST_DIR}/test_odeint.cpp ${ODEINT_TEST_DIR}/butchertableau.cpp ${ODEINT_TEST_DIR}/embeddedrungekutta.cpp ) # Database # -------- add_subdirectory(database) # Specmicp # -------- add_subdirectory( specmicp ) # Reactmicp # --------- add_subdirectory( reactmicp ) diff --git a/tests/common/dynamic_loader.cpp b/tests/common/dynamic_loader.cpp new file mode 100644 index 0000000..964fe1a --- /dev/null +++ b/tests/common/dynamic_loader.cpp @@ -0,0 +1,56 @@ +#include + +#include "utils/plugins/dynamic_library.hpp" +#include + +using namespace specmicp::plugins; + +TEST_CASE("Dynamic Loader", "[plugin]") +{ + SECTION("Loading") + { + std::string error; + DynamicLibraryPtr lib = DynamicLibrary::load("test_to_load.so", error); + + CHECK(lib != nullptr); + if (lib == nullptr) + { + std::cerr << error << std::endl; + REQUIRE(false); + } + + int (*add_f)(int, int) = (int (*)(int, int)) lib->get_symbol("add", error); + CHECK(add_f != nullptr); + if (add_f == nullptr) + { + std::cerr << error << std::endl; + REQUIRE(false); + } + + CHECK(add_f(1, 2) == 3); + + } + + SECTION("Unsuccessful loading") + { + std::string error; + DynamicLibraryPtr lib = DynamicLibrary::load("platyput_with_7_legs", error); + + CHECK(lib == nullptr); + } + + SECTION("Unsuccessful symbol loading") + { + std::string error; + DynamicLibraryPtr lib = DynamicLibrary::load("test_to_load.so", error); + + CHECK(lib != nullptr); + if (lib == nullptr) + { + std::cerr << error << std::endl; + REQUIRE(false); + } + void * hop = lib->get_symbol("latypus_with_7_legs", error); + CHECK(hop == nullptr); + } +} diff --git a/tests/common/test_to_load.cpp b/tests/common/test_to_load.cpp new file mode 100644 index 0000000..b6b0b87 --- /dev/null +++ b/tests/common/test_to_load.cpp @@ -0,0 +1,9 @@ +extern "C" { + + +int add(int a, int b) +{ + return a + b; +} + +}