Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[runSofa] autoload plugins (2nd version) #301

Merged
merged 14 commits into from
Jul 5, 2017
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@
- Adds a new orientedBox dataField in BoxROI so that we can use it to either defined AABoxes or OrientedBox
- Minor improvement on the way warning/error message are presented to the users in runSofa. A single panel is now used instead of of two, it is always displayed, the Panel name also contains the number of message eg: "Messages(5)"
- The Graph view is now displaying the type of message they contains.
- [runSofa]
- Autoload plugins, described in the user-custom file 'plugin_list.conf' if present; else 'plugin_list.conf.default' containing all compiled plugins and generated automatically by CMake.

**For developpers**
- Add a Logger component that stores the history of messages into each sofa component.
Expand Down
5 changes: 3 additions & 2 deletions SofaKernel/SofaFramework/SofaMacros.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -181,10 +181,11 @@ macro(sofa_add_generic directory name type)
set_target_properties(${name} PROPERTIES FOLDER ${type}s) # IDE folder
set_target_properties(${name} PROPERTIES DEBUG_POSTFIX "_d")
endif()
else()

set_property(GLOBAL APPEND PROPERTY __GlobalTargetList__ ${name})
set_property(GLOBAL APPEND PROPERTY __GlobalTargetNameList__ ${option})
else()
message("${type} ${name} (${CMAKE_CURRENT_LIST_DIR}/${directory}) does not exist and will be ignored.")

endif()
endmacro()

Expand Down
15 changes: 11 additions & 4 deletions SofaKernel/framework/sofa/helper/system/PluginManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -87,12 +87,18 @@ PluginManager::~PluginManager()
void PluginManager::readFromIniFile(const std::string& path)
{
std::ifstream instream(path.c_str());
std::string pluginPath;

while(std::getline(instream,pluginPath))
std::string pluginPath, line, version;
while(std::getline(instream, line))
{
if (line.empty()) continue;

std::istringstream is(line);
is >> pluginPath;
is >> version; // information not used for now
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The old ini file does not have the version number.
Maybe you can implement a backward compatibility hook, I'm not sure this could work like that but
here is the idea:

   is >> pluginPath
   if( is.eof() ){
      msg_deprecated() << "You are using a deprecated version of the fiel.ini... blabblha"
   ...

Why not having the version number into the filename (as linux is doing libpng.1.0.so) ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually it already work with previous format (w/o version) but you're right about a deprecated message.
About the version thing in the filename, it is done already on Mac and Linux (e.g libFredPlugin.0.1.dylib is created and a symbolic link libFredPlugin points to it).
I guess you would like to load a specific version, e.g
CImgPlugin 0.5 then load CImgPlugin.0.5.dylib if not warning/error about the version, right ?
I think it should be the purpose of a future PR which will

  • make version consistent between (all?) plugin's CMakefile.txt version (set_target_properties(${PROJECT_NAME} PROPERTIES VERSION ${PROJECT_VERSION})) and getVersion() in the initPluginXXXX.cpp
  • add version to generated Windows libraries (it does not, and not really possible with symbolic link a la Unix)
  • add checks and a version parameter to PluginManager loading functions

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the quick answer,
I agree this could be for a future PR.

if(loadPlugin(pluginPath))
{
m_pluginMap[pluginPath].initExternalModule();
}
}
instream.close();
}
Expand All @@ -104,7 +110,8 @@ void PluginManager::writeToIniFile(const std::string& path)
for( iter = m_pluginMap.begin(); iter!=m_pluginMap.end(); ++iter)
{
const std::string& pluginPath = (iter->first);
outstream << pluginPath << "\n";
outstream << pluginPath << " ";
outstream << m_pluginMap[pluginPath].getModuleVersion() << "\n";
}
outstream.close();
}
Expand Down
27 changes: 27 additions & 0 deletions applications/projects/runSofa/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,19 @@ endif()

find_package(SofaFramework) # to get SOFA_HAVE_GLUT

include(cmake/GeneratePluginConfig.cmake)
# if MSVC then plugins are located in bin/ instead of lib/ on Mac and Linux
if(MSVC)
set(_pluginLocation "bin")
else()
set(_pluginLocation "lib")
endif()
set(_configPluginFileName plugin_list.conf)
set(_defaultConfigPluginFileName "${_configPluginFileName}.default")
set(_defaultConfigPluginFilePath "${CMAKE_BINARY_DIR}/${_pluginLocation}/${_defaultConfigPluginFileName}")
sofa_generate_plugin_config(${_defaultConfigPluginFilePath})
message("Write Plugin list at ${_defaultConfigPluginFilePath}")

if(APPLE)
set_source_files_properties(${RC_FILES} PROPERTIES MACOSX_PACKAGE_LOCATION "Resources")
endif()
Expand All @@ -25,6 +38,8 @@ if(APPLE)
set_target_properties( ${PROJECT_NAME} PROPERTIES MACOSX_BUNDLE_ICON_FILE "runSOFA.icns" )
endif()

target_compile_definitions(${PROJECT_NAME} PUBLIC "CONFIG_PLUGIN_FILENAME=${_configPluginFileName}")
target_compile_definitions(${PROJECT_NAME} PUBLIC "DEFAULT_CONFIG_PLUGIN_FILENAME=${_defaultConfigPluginFileName}")
target_link_libraries(${PROJECT_NAME} SofaComponentAdvanced SofaComponentMisc)
target_link_libraries(${PROJECT_NAME} SofaSimulationGraph)
target_link_libraries(${PROJECT_NAME} SofaGuiMain)
Expand All @@ -33,10 +48,22 @@ if(SOFA_HAVE_GLUT)
target_compile_definitions(${PROJECT_NAME} PUBLIC "SOFA_HAVE_GLUT_GUI")
endif()

if(SOFA_BUILD_TESTS)
find_package(SofaTest QUIET)
if(SofaTest_FOUND)
add_subdirectory(runSofa_test)
endif()
endif()

if(APPLE AND RUNSOFA_INSTALL_AS_BUNDLE)
# set(CPACK_COMPONENTS_ALL BundlePack)
set_property(GLOBAL PROPERTY RUNSOFA_CPACK_COMPONENTS_ALL BundlePack)
include(cmake/bundle.cmake)
else()
sofa_install_targets(SofaGui runSofa "")
endif()

# if MSVC then plugins are located in bin/ instead of lib/ on Mac and Linux

install(FILES "${_defaultConfigPluginFilePath}" DESTINATION ${_pluginLocation}/)

25 changes: 22 additions & 3 deletions applications/projects/runSofa/Main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,8 @@ using sofa::helper::logging::ClangMessageHandler ;
#include <sofa/helper/logging/ExceptionMessageHandler.h>
using sofa::helper::logging::ExceptionMessageHandler;

#define STRINGIFY(x) #x
#define TOSTRING(x) STRINGIFY(x)


void loadVerificationData(string& directory, string& filename, Node* node)
Expand Down Expand Up @@ -180,6 +182,7 @@ int main(int argc, char** argv)
bool loadRecent = false;
bool temporaryFile = false;
bool testMode = false;
bool noAutoloadPlugins = false;
int nbIterations = BatchGUI::DEFAULT_NUMBER_OF_ITERATIONS;
unsigned int nbMSSASamples = 1;
unsigned computationTimeSampling=0; ///< Frequency of display of the computation time statistics, in number of animation steps. 0 means never.
Expand Down Expand Up @@ -214,6 +217,7 @@ int main(int argc, char** argv)
.option(&computationTimeSampling,'c',"computationTimeSampling","Frequency of display of the computation time statistics, in number of animation steps. 0 means never.")
.option(&gui,'g',"gui",gui_help.c_str())
.option(&plugins,'l',"load","load given plugins")
.option(&noAutoloadPlugins, '0', "noautoload", "disable plugins autoloading")
.option(&nbMSSASamples, 'm', "msaa", "number of samples for MSAA (Multi Sampling Anti Aliasing ; value < 2 means disabled")
.option(&nbIterations,'n',"nb_iterations","(only batch) Number of iterations of the simulation")
.option(&printFactory,'p',"factory","print factory logs")
Expand Down Expand Up @@ -339,11 +343,26 @@ int main(int argc, char** argv)
for (unsigned int i=0; i<plugins.size(); i++)
PluginManager::getInstance().loadPlugin(plugins[i]);

// to force loading plugin SofaPython if existing
std::string configPluginPath = pluginDir + "/" + TOSTRING(CONFIG_PLUGIN_FILENAME);
std::string defaultConfigPluginPath = pluginDir + "/" + TOSTRING(DEFAULT_CONFIG_PLUGIN_FILENAME);

if (!noAutoloadPlugins)
{
std::ostringstream no_error_message; // no to get an error on the console if SofaPython does not exist
sofa::helper::system::PluginManager::getInstance().loadPlugin("SofaPython",&no_error_message);
if (sofa::helper::system::DataRepository.findFile(configPluginPath))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of repeating the full namespace, please use
using sofa::helper::system::DataRepository ;

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok (bis)

{
msg_info("runSofa") << "Loading automatically plugin list in " << configPluginPath;
sofa::helper::system::PluginManager::getInstance().readFromIniFile(configPluginPath);
}
else if (sofa::helper::system::DataRepository.findFile(defaultConfigPluginPath))
{
msg_info("runSofa") << "Loading automatically plugin list in " << defaultConfigPluginPath;
sofa::helper::system::PluginManager::getInstance().readFromIniFile(defaultConfigPluginPath);
}
else
msg_info("runSofa") << "No plugin list found. No plugin will be automatically loaded.";
}
else
msg_info("runSofa") << "Automatic plugin loading disabled.";

PluginManager::getInstance().init();

Expand Down
37 changes: 37 additions & 0 deletions applications/projects/runSofa/cmake/GeneratePluginConfig.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
cmake_minimum_required(VERSION 3.1)

macro(sofa_generate_plugin_config config_filename)
# Generate default list of plugins (according to the options)
get_property(_allTargets GLOBAL PROPERTY __GlobalTargetList__)
get_property(_allTargetNames GLOBAL PROPERTY __GlobalTargetNameList__)

list(LENGTH _allTargets nbTargets)
math(EXPR len "${nbTargets} - 1")

set(_pluginPrefix "PLUGIN")
foreach(counter RANGE ${len})
list(GET _allTargets ${counter} _target)
list(GET _allTargetNames ${counter} _targetName)

string(SUBSTRING "${_targetName}" 0 6 _testPlugin)
if(${_testPlugin} MATCHES "${_pluginPrefix}.*")
if(${${_targetName}})
get_target_property(_version ${_target} VERSION )
if(${_version} MATCHES ".*NOTFOUND")
set(_version "NO_VERSION")
endif()
string(CONCAT _pluginConfig "${_pluginConfig}\n${_target} ${_version}")
endif()
endif()
endforeach()
FILE(WRITE ${config_filename} ${_pluginConfig})

# only useful for devs working directly with a build version (not installed)
# With Win/MVSC, we can only know $CONFIG at build time
if (MSVC)
add_custom_target(do_always ALL
COMMAND "${CMAKE_COMMAND}" -E copy "${config_filename}" "${CMAKE_BINARY_DIR}/bin/$<CONFIG>/"
)
endif(MSVC)

endmacro()
12 changes: 12 additions & 0 deletions applications/projects/runSofa/runSofa_test/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
cmake_minimum_required(VERSION 3.1)
project(runSofa_test)


set(SOURCE_FILES
runSofa_test.cpp
)

add_executable(${PROJECT_NAME} ${SOURCE_FILES} )
target_link_libraries(${PROJECT_NAME} SofaTest SofaGTestMain)

add_test(NAME ${PROJECT_NAME} COMMAND ${PROJECT_NAME})
84 changes: 84 additions & 0 deletions applications/projects/runSofa/runSofa_test/runSofa_test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
/******************************************************************************
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1 for adding tests with your new features.

* SOFA, Simulation Open-Framework Architecture, development version *
* (c) 2006-2017 INRIA, USTL, UJF, CNRS, MGH *
* *
* This program is free software; you can redistribute it and/or modify it *
* under the terms of the GNU General Public License as published by the Free *
* Software Foundation; either version 2 of the License, or (at your option) *
* any later version. *
* *
* This program is distributed in the hope that it will be useful, but WITHOUT *
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or *
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for *
* more details. *
* *
* You should have received a copy of the GNU General Public License along *
* with this program. If not, see <http://www.gnu.org/licenses/>. *
*******************************************************************************
* Authors: The SOFA Team and external contributors (see Authors.txt) *
* *
* Contact information: contact@sofa-framework.org *
******************************************************************************/

#include <fstream>
#include <gtest/gtest.h>
#include <SofaTest/Sofa_test.h>

#include <sofa/helper/Utils.h>
#include <sofa/helper/Utils.h>
#include <sofa/helper/system/PluginManager.h>
#include <sofa/helper/system/FileRepository.h>

namespace sofa
{

class runSofa_test : public Sofa_test<>
{
protected:
std::string m_testConfigPluginName;
std::string m_testConfigPluginPath;
std::string m_testPluginName;

runSofa_test() {

}

void SetUp()
{
#ifdef WIN32
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we have a function in sofa::helper::Utils::getPluginDir() to avoid having #ifdef in the main application ?

const std::string pluginDir = helper::Utils::getExecutableDirectory();
#else
const std::string pluginDir = helper::Utils::getSofaPathPrefix() + "/lib";
#endif
m_testConfigPluginName = "test_plugin_list.conf";
m_testConfigPluginPath = pluginDir + "/" + m_testConfigPluginName;
m_testPluginName = "TestPlugin";

//generate on the fly test list
std::ofstream testPluginList;
testPluginList.open(m_testConfigPluginPath);
testPluginList << m_testPluginName << std::endl;
testPluginList.close();
}
void TearDown()
{

}

};

TEST_F(runSofa_test, runSofa_autoload)
{
sofa::helper::system::PluginManager& pm = sofa::helper::system::PluginManager::getInstance();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suggest you to use:
'using sofa::helper::system::PluginManager ; '
then 'PluginManager' to avoid repeating the long namespace everywhere which is less readable.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok


ASSERT_EQ(pm.getPluginMap().size(), 0U);
pm.readFromIniFile(m_testConfigPluginPath);
helper::system::PluginManager::getInstance().init();
ASSERT_GT(pm.getPluginMap().size(), 0U);
const std::string pluginPath = pm.findPlugin(m_testPluginName);
ASSERT_GT(pluginPath.size(), 0U);
sofa::helper::system::Plugin& p = pm.getPluginMap()[pluginPath];
ASSERT_EQ(0, std::string(p.getModuleName()).compare(m_testPluginName));
}

} // namespace sofa