diff --git a/Framework/Kernel/CMakeLists.txt b/Framework/Kernel/CMakeLists.txt index 71e9145385b723e2ccd53d9fefc9d7fafcb31e7c..75152affa181cc44d3d8fb8f74eb20ad055b8ab0 100644 --- a/Framework/Kernel/CMakeLists.txt +++ b/Framework/Kernel/CMakeLists.txt @@ -559,18 +559,18 @@ if ( ${CMAKE_PROJECT_NAME} STREQUAL "MantidFramework" ) set ( MANTID_ROOT ${CMAKE_SOURCE_DIR}/.. ) endif () -set ( PLUGINS "." ) -set ( QTPLUGINS "." ) +set ( FRAMEWORK_PLUGINS_DIR "." ) +set ( QT_PLUGINS_DIR "." ) # %V will be replaced with the major version of Qt at runtime if ( MAKE_VATES ) - set ( PV_PLUGINS "./plugins/paraview/qt%V" ) + set ( PV_PLUGINS_DIR "./plugins/paraview/qt%V" ) if ( MSVC ) set (PARAVIEW_PYTHON_PATHS "${ParaView_DIR}/bin/$<$<CONFIG:Release>:Release>$<$<CONFIG:Debug>:Debug>;${ParaView_DIR}/lib/$<$<CONFIG:Release>:Release>$<$<CONFIG:Debug>:Debug>;${ParaView_DIR}/lib/site-packages;${ParaView_DIR}/lib/site-packages/vtk") else() set ( PARAVIEW_PYTHON_PATHS "${ParaView_DIR}/lib;${ParaView_DIR}/lib/site-packages;${ParaView_DIR}/lib/site-packages/vtk") endif() else () - set ( PV_PLUGINS "" ) + set ( PV_PLUGINS_DIR "" ) set ( PARAVIEW_PYTHON_PATHS "") endif () set ( IGNORE_PARAVIEW "0" ) @@ -618,7 +618,7 @@ endforeach() # These settings carry down to the installation configuration below if ( MPI_BUILD ) set ( CONSOLEPATTERN "%H:%M:%S,%i [%N:%P] %p %s - %t") - set ( PV_PLUGINS "" ) + set ( PV_PLUGINS_DIR "" ) set ( IGNORE_PARAVIEW "1" ) else () option ( ENABLE_FILE_LOGGING "Enable logging to file for development builds. It is always enabled for packages" ON ) @@ -671,7 +671,7 @@ if ( NOT MPI_BUILD ) set ( FILELOGGER_CHANNEL fileFilterChannel ) endif () set ( ENABLE_WEB_UPDATES 1 ) -set ( PLUGINS ${MANTID_ROOT}/plugins ) +set ( FRAMEWORK_PLUGINS_DIR ${MANTID_ROOT}/plugins ) set ( PYTHONPLUGIN_DIRS "${PLUGINS}/python" ) if(MAKE_VATES) if (APPLE) @@ -723,13 +723,13 @@ set ( PYTHONSCRIPT_DIRS "${WITH_SEMICOLONS}" ) # For a framework-only (e.g. MPI) build some of these are not relevant and should # be left empty to avoid warnings on starting Mantid if ( ${CMAKE_PROJECT_NAME} MATCHES "MantidFramework" ) - set ( QTPLUGINS "" ) + set ( QT_PLUGINS_DIR "" ) set ( COLORMAPS_FOLDER "" ) - set ( PV_PLUGINS "" ) + set ( PV_PLUGINS_DIR "" ) set ( IGNORE_PARAVIEW "1" ) else () - set ( QTPLUGINS "${MANTID_ROOT}/plugins/qt%V" ) - set ( PV_PLUGINS "${MANTID_ROOT}/${PLUGINS_DIR}/${PVPLUGINS_SUBDIR}/qt%V" ) + set ( QT_PLUGINS_DIR "${MANTID_ROOT}/plugins/qt%V" ) + set ( PV_PLUGINS_DIR "${MANTID_ROOT}/${PLUGINS_DIR}/${PVPLUGINS_SUBDIR}/qt%V" ) set ( COLORMAPS_FOLDER ${MANTID_ROOT}/colormaps ) endif () diff --git a/Framework/Kernel/inc/MantidKernel/LibraryManager.h b/Framework/Kernel/inc/MantidKernel/LibraryManager.h index ae95398fc06fc474f4788e348bdd699d4d528111..447863e16de4f77f4b1a5f373cb9d89d4fd0da64 100644 --- a/Framework/Kernel/inc/MantidKernel/LibraryManager.h +++ b/Framework/Kernel/inc/MantidKernel/LibraryManager.h @@ -5,18 +5,19 @@ // Includes //---------------------------------------------------------------------- #include <string> -#include <map> -#ifndef Q_MOC_RUN -#include <boost/shared_ptr.hpp> -#endif +#include <unordered_map> -#include "MantidKernel/SingletonHolder.h" #include "MantidKernel/DllConfig.h" +#include "MantidKernel/LibraryWrapper.h" +#include "MantidKernel/SingletonHolder.h" + +namespace Poco { +class File; +class Path; +} namespace Mantid { namespace Kernel { -class LibraryWrapper; - /** Class for opening shared libraries. @@ -46,8 +47,9 @@ Code Documentation is available at: <http://doxygen.mantidproject.org> */ class MANTID_KERNEL_DLL LibraryManagerImpl { public: - // opens all suitable libraries on a given path - int OpenAllLibraries(const std::string &, bool isRecursive = false); + enum LoadLibraries { Recursive, NonRecursive }; + int openLibraries(const std::string &, LoadLibraries loadingBehaviour, + const std::vector<std::string> &excludes); LibraryManagerImpl(const LibraryManagerImpl &) = delete; LibraryManagerImpl &operator=(const LibraryManagerImpl &) = delete; @@ -56,17 +58,20 @@ private: /// Private Constructor LibraryManagerImpl(); - /// Private Destructor - virtual ~LibraryManagerImpl() = default; + ~LibraryManagerImpl() = default; + /// Load libraries from the given Poco::File path + /// Private so Poco::File doesn't leak to the public interface + int openLibraries(const Poco::File &, LoadLibraries loadingBehaviour, + const std::vector<std::string> &excludes); /// Load a given library - bool loadLibrary(const std::string &filepath); + bool loadLibrary(Poco::Path filepath); /// Returns true if the library is to be loaded - bool skip(const std::string &filename); + bool skipLibrary(const std::string &filename, + const std::vector<std::string> &excludes); /// Storage for the LibraryWrappers. - std::map<const std::string, boost::shared_ptr<Mantid::Kernel::LibraryWrapper>> - OpenLibs; + std::unordered_map<std::string, LibraryWrapper> m_openedLibs; }; EXTERN_MANTID_KERNEL template class MANTID_KERNEL_DLL diff --git a/Framework/Kernel/src/ConfigService.cpp b/Framework/Kernel/src/ConfigService.cpp index d4fc884102b1eb15937a6fe245937b6ad3ec09f1..7510ee3036c4d67dd08601002f81389b6d078b22 100644 --- a/Framework/Kernel/src/ConfigService.cpp +++ b/Framework/Kernel/src/ConfigService.cpp @@ -194,7 +194,7 @@ ConfigServiceImpl::ConfigServiceImpl() // Fill the list of possible relative path keys that may require conversion to // absolute paths m_ConfigPaths.emplace("mantidqt.python_interfaces_directory", true); - m_ConfigPaths.emplace("plugins.directory", true); + m_ConfigPaths.emplace("framework.plugins.directory", true); m_ConfigPaths.emplace("pvplugins.directory", false); m_ConfigPaths.emplace("mantidqt.plugins.directory", false); m_ConfigPaths.emplace("instrumentDefinition.directory", true); diff --git a/Framework/Kernel/src/LibraryManager.cpp b/Framework/Kernel/src/LibraryManager.cpp index 3158e80ccb2fa13d51a45db1f3418a6f12e2e219..5cb0941f32c03051947df0aa8c07f415388af174 100644 --- a/Framework/Kernel/src/LibraryManager.cpp +++ b/Framework/Kernel/src/LibraryManager.cpp @@ -1,4 +1,3 @@ - #include "MantidKernel/ConfigService.h" #include "MantidKernel/DllOpen.h" #include "MantidKernel/LibraryManager.h" @@ -9,8 +8,6 @@ #include <Poco/File.h> #include <Poco/DirectoryIterator.h> #include <boost/algorithm/string.hpp> -#include <boost/make_shared.hpp> -#include <unordered_set> namespace Mantid { namespace Kernel { @@ -20,73 +17,89 @@ Logger g_log("LibraryManager"); } /// Constructor -LibraryManagerImpl::LibraryManagerImpl() { +LibraryManagerImpl::LibraryManagerImpl() : m_openedLibs() { g_log.debug() << "LibraryManager created.\n"; } -/** Opens all suitable DLLs on a given path. -* @param filePath :: The filepath to the directory where the libraries are. -* @param isRecursive :: Whether to search subdirectories. -* @return The number of libraries opened. -*/ -int LibraryManagerImpl::OpenAllLibraries(const std::string &filePath, - bool isRecursive) { +/** + * Opens suitable DLLs on a given path. + * @param filePath The filepath to the directory where the libraries are. + * @param loadingBehaviour Control how libraries are searched for + * @param excludes If not empty then each string is considered as a substring + * to search within each library to be opened. If the substring is found then + * the library is not opened. + * @return The number of libraries opened. + */ +int LibraryManagerImpl::openLibraries( + const std::string &filePath, LoadLibraries loadingBehaviour, + const std::vector<std::string> &excludes) { g_log.debug() << "Opening all libraries in " << filePath << "\n"; - int libCount = 0; - // validate inputs - Poco::File libPath; try { - libPath = Poco::File(filePath); + return openLibraries(Poco::File(filePath), loadingBehaviour, excludes); + } catch (std::exception &exc) { + g_log.debug() << "Error occurred while opening libraries: " << exc.what() + << "\n"; + return 0; } catch (...) { - return libCount; + g_log.error() << "An unknown error occurred while opening libraries."; + return 0; } +} + +//------------------------------------------------------------------------- +// Private members +//------------------------------------------------------------------------- +/** + * Opens suitable DLLs on a given path. + * @param filePath A Poco::File object pointing to a directory where the + * libraries are. + * @param loadingBehaviour Control how libraries are searched for + * @param excludes If not empty then each string is considered as a substring + * to search within each library to be opened. If the substring is found then + * the library is not opened. + * @return The number of libraries opened. + */ +int LibraryManagerImpl::openLibraries( + const Poco::File &libPath, + LibraryManagerImpl::LoadLibraries loadingBehaviour, + const std::vector<std::string> &excludes) { + int libCount(0); if (libPath.exists() && libPath.isDirectory()) { - DllOpen::addSearchDirectory(filePath); - // Iteratate over the available files + DllOpen::addSearchDirectory(libPath.path()); + // Iterate over the available files Poco::DirectoryIterator end_itr; for (Poco::DirectoryIterator itr(libPath); itr != end_itr; ++itr) { - const Poco::Path &item = itr.path(); - if (item.isDirectory()) { - if (isRecursive) { - libCount += OpenAllLibraries(item.toString()); - } - } else { - if (skip(item.toString())) + const Poco::File &item = *itr; + if (item.isFile()) { + if (skipLibrary(itr.path().getFileName(), excludes)) continue; - if (loadLibrary(item.toString())) { + if (loadLibrary(itr.path())) { ++libCount; } + } else if (loadingBehaviour == LoadLibraries::Recursive) { + // it must be a directory + libCount += openLibraries(item, LoadLibraries::Recursive, excludes); } } } else { - g_log.error("In OpenAllLibraries: " + filePath + " must be a directory."); + g_log.error("In OpenAllLibraries: " + libPath.path() + + " must be a directory."); } - return libCount; } -//------------------------------------------------------------------------- -// Private members -//------------------------------------------------------------------------- /** * Returns true if the name contains one of the strings given in the - * 'plugins.exclude' variable. Each string from the variable is + * exclude list. Each string from the variable is * searched for with the filename so an exact match is not necessary. This * avoids having to specify prefixes and suffixes for different platforms, * i.e. 'plugins.exclude = MantidKernel' will exclude libMantidKernel.so - * @param filename :: A string giving the filename/file path + * @param filename A string giving the filename/file path + * @param excludes A list of substrings to exclude library from loading * @return True if the library should be skipped */ -bool LibraryManagerImpl::skip(const std::string &filename) { - static std::unordered_set<std::string> excludes; - static bool initialized(false); - if (!initialized) { - std::string excludeStr = - ConfigService::Instance().getString("plugins.exclude"); - boost::split(excludes, excludeStr, boost::is_any_of(":;"), - boost::token_compress_on); - initialized = true; - } +bool LibraryManagerImpl::skipLibrary(const std::string &filename, + const std::vector<std::string> &excludes) { bool skipme(false); for (const auto &exclude : excludes) { if (filename.find(exclude) != std::string::npos) { @@ -99,31 +112,26 @@ bool LibraryManagerImpl::skip(const std::string &filename) { /** * Load a library -* @param filepath :: The full path to a library as a string +* @param filepath :: A Poco::File The full path to a library as a string */ -bool LibraryManagerImpl::loadLibrary(const std::string &filepath) { +bool LibraryManagerImpl::loadLibrary(Poco::Path filepath) { // Get the name of the library. - std::string libName = - DllOpen::ConvertToLibName(Poco::Path(filepath).getFileName()); + std::string libName = DllOpen::convertToLibName(filepath.getFileName()); if (libName.empty()) return false; - // The wrapper will unload the library when it is deleted - auto dlwrap = boost::make_shared<LibraryWrapper>(); - std::string libNameLower = boost::algorithm::to_lower_copy(libName); - - // Check that a libray with this name has not already been loaded - if (OpenLibs.find(libNameLower) == OpenLibs.end()) { - Poco::Path directory(filepath); - directory.makeParent(); - if (g_log.is(Poco::Message::PRIO_DEBUG)) { - g_log.debug() << "Trying to open library: " << libName << " from " - << directory.toString() << " ..."; - } - // Try to open the library - if (dlwrap->OpenLibrary(libName, directory.toString())) { + // Check that a library with this name has not already been loaded + if (m_openedLibs.find(boost::algorithm::to_lower_copy(libName)) == + m_openedLibs.end()) { + filepath.makeParent(); + // Try to open the library. The wrapper will unload the library when it + // is deleted + LibraryWrapper dlwrap; + if (dlwrap.openLibrary(libName, filepath.toString())) { // Successfully opened, so add to map - g_log.debug("Opened library: " + libName + ".\n"); - OpenLibs.emplace(libName, dlwrap); + if (g_log.is(Poco::Message::PRIO_DEBUG)) { + g_log.debug("Opened library: " + libName + ".\n"); + } + m_openedLibs.emplace(libName, std::move(dlwrap)); return true; } else { return false; diff --git a/Framework/Properties/Mantid.properties.template b/Framework/Properties/Mantid.properties.template index 6047e8cd6bab77859338131c5978b34fe8d85e12..10a988d98ad88bac3179e771dc096cdbdaada489 100644 --- a/Framework/Properties/Mantid.properties.template +++ b/Framework/Properties/Mantid.properties.template @@ -22,20 +22,20 @@ Q.convention = Inelastic # Set of PyQt interfaces to add to the Interfaces menu, separated by a space. Interfaces are seperated from their respective categories by a "/". mantidqt.python_interfaces = Direct/DGS_Reduction.py Direct/DGSPlanner.py Direct/PyChop.py SANS/ORNL_SANS.py Utility/TofConverter.py Reflectometry/ISIS_Reflectometry_Old.py Diffraction/Powder_Diffraction_Reduction.py Utility/FilterEvents.py Diffraction/HFIR_Powder_Diffraction_Reduction.py Diffraction/HFIR_4Circle_Reduction.py Utility/QECoverage.py SANS/ISIS_SANS_v2_experimental.py Muon/Frequency_Domain_Analysis.py - +# Directory containing the above startup scripts mantidqt.python_interfaces_directory = @MANTID_ROOT@/scripts -# Where to find mantid plugin libraries -plugins.directory = @PLUGINS@ +# Where to find mantid framework plugins +framework.plugins.directory = @FRAMEWORK_PLUGINS_DIR@ # Libraries to skip. The strings are searched for when loading libraries so they don't need to be exact -plugins.exclude = Qt5 +framework.plugins.exclude = Qt4;Qt5 # Where to find mantid paraview plugin libraries -pvplugins.directory = @PV_PLUGINS@ +pvplugins.directory = @PV_PLUGINS_DIR@ # Where to find Mantid Qt plugin libraries -mantidqt.plugins.directory = @QTPLUGINS@ +mantidqt.plugins.directory = @QT_PLUGINS_DIR@ # Where to find python plugins python.plugins.directories = @PYTHONPLUGIN_DIRS@ diff --git a/docs/source/concepts/PropertiesFile.rst b/docs/source/concepts/PropertiesFile.rst index 248392c77f592343f8402cac2eb41745d2e0c6b8..9001719d45b56465ce35ea0810685142126ca3c4 100644 --- a/docs/source/concepts/PropertiesFile.rst +++ b/docs/source/concepts/PropertiesFile.rst @@ -87,9 +87,15 @@ Directory Properties | | that Mantid requires to function correctly. | | | | **WARNING:** Do not alter the default value. | | +--------------------------------------+---------------------------------------------------+-------------------------------------+ -| ``plugins.directory`` | The path to the directory that contains the | ``../Plugins`` | +| ``framework.plugins.directory`` | The path to the directory that contains the | ``../plugins`` | | | Mantid plugin libraries | | +--------------------------------------+---------------------------------------------------+-------------------------------------+ +| ``framework.plugins.exclude`` | A list of substrings to allow libraries to be | ``Qt4;Qt5`` | +| | skipped | | ++--------------------------------------+---------------------------------------------------+-------------------------------------+ +| ``mantidqt.plugins.directory`` | The path to the directory containing the | ``../plugins/qtX`` | +| | Mantid Qt-based plugin libraries | | ++--------------------------------------+---------------------------------------------------+-------------------------------------+ | ``requiredpythonscript.directories`` | A list of directories containing Python scripts | N/A | | | that Mantid requires to function correctly. | | | | **WARNING:** Do not alter the default value. | |