Salome HOME
Porting to SALOME 9.1.0.
[modules/shaper.git] / src / Config / Config_ModuleReader.cpp
1 // Copyright (C) 2014-2017  CEA/DEN, EDF R&D
2 //
3 // This library is free software; you can redistribute it and/or
4 // modify it under the terms of the GNU Lesser General Public
5 // License as published by the Free Software Foundation; either
6 // version 2.1 of the License, or (at your option) any later version.
7 //
8 // This library is distributed in the hope that it will be useful,
9 // but WITHOUT ANY WARRANTY; without even the implied warranty of
10 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
11 // Lesser General Public License for more details.
12 //
13 // You should have received a copy of the GNU Lesser General Public
14 // License along with this library; if not, write to the Free Software
15 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
16 //
17 // See http://www.salome-platform.org/ or
18 // email : webmaster.salome@opencascade.com<mailto:webmaster.salome@opencascade.com>
19 //
20
21 #include <pyconfig.h>
22
23 #include <Config_Keywords.h>
24 #include <Config_Common.h>
25 #include <Config_ModuleReader.h>
26 #include <Config_FeatureReader.h>
27 #include <Config_PluginMessage.h>
28 #include <Events_InfoMessage.h>
29
30 #include <libxml/parser.h>
31 #include <libxml/tree.h>
32
33 // Have to be included before std headers
34 #include <Python.h>
35
36 //Necessary for cerr
37 #include <iostream>
38 #include <algorithm>
39
40 #ifdef WIN32
41 #include <windows.h>
42 #pragma warning(disable : 4996) // for getenv
43 #else
44 #include <dlfcn.h>
45 #endif
46
47 std::map<std::string, Config_ModuleReader::PluginType> Config_ModuleReader::myPluginTypes;
48 std::set<std::string> Config_ModuleReader::myDependencyModules;
49
50 Config_ModuleReader::Config_ModuleReader(const char* theEventGenerated)
51     : Config_XMLReader(PLUGIN_FILE),
52       myEventGenerated(theEventGenerated)
53 {
54 }
55
56 Config_ModuleReader::~Config_ModuleReader()
57 {
58 }
59
60 const std::map<std::string, std::string>& Config_ModuleReader::featuresInFiles() const
61 {
62   return myFeaturesInFiles;
63 }
64
65 const std::set<std::string>& Config_ModuleReader::modulePluginFiles() const
66 {
67   return myPluginFiles;
68 }
69
70 /*!
71  * Get module name from plugins.xml
72  * (property "module")
73  */
74 std::string Config_ModuleReader::getModuleName()
75 {
76   xmlNodePtr aRoot = findRoot();
77   return getProperty(aRoot, PLUGINS_MODULE);
78 }
79
80
81 void Config_ModuleReader::addFeature(const std::string& theFeatureName,
82                                      const std::string& thePluginConfig)
83 {
84   if (myFeaturesInFiles.count(theFeatureName)) {
85     std::string anErrorMsg = "Can not register feature '%1' in plugin '%2'."
86       " There is a feature with the same ID.";
87     Events_InfoMessage("Config_ModuleReader", anErrorMsg)
88       .arg(theFeatureName).arg(thePluginConfig).send();
89     return;
90   }
91
92   myFeaturesInFiles[theFeatureName] = thePluginConfig;
93 }
94
95 void Config_ModuleReader::processNode(xmlNodePtr theNode)
96 {
97   if (isNode(theNode, NODE_PLUGIN, NULL)) {
98     if (!hasRequiredModules(theNode))
99       return;
100     std::string aPluginConf = getProperty(theNode, PLUGIN_CONFIG);
101     if (!aPluginConf.empty())
102       myPluginFiles.insert(aPluginConf);
103     std::string aPluginLibrary = getProperty(theNode, PLUGIN_LIBRARY);
104     std::string aPluginScript = getProperty(theNode, PLUGIN_SCRIPT);
105     std::string aPluginName = addPlugin(aPluginLibrary, aPluginScript, aPluginConf);
106     std::string aUsesPlugin = getProperty(theNode, PLUGIN_USES);
107     if (!aUsesPlugin.empty()) { // send information about the plugin dependencies
108       std::shared_ptr<Config_PluginMessage> aMess(new Config_PluginMessage(
109         Events_Loop::loop()->eventByName(Config_PluginMessage::EVENT_ID()), aPluginName));
110       aMess->setUses(aUsesPlugin);
111       Events_Loop::loop()->send(aMess);
112     }
113
114     std::list<std::string> aFeatures = importPlugin(aPluginName, aPluginConf);
115     std::list<std::string>::iterator it = aFeatures.begin();
116     for (; it != aFeatures.end(); it++) {
117       addFeature(*it, aPluginConf);
118     }
119   }
120 }
121
122 bool Config_ModuleReader::processChildren(xmlNodePtr theNode)
123 {
124   return isNode(theNode, NODE_PLUGINS, NULL);
125 }
126
127 bool Config_ModuleReader::hasRequiredModules(xmlNodePtr theNode) const
128 {
129   std::string aRequiredModule = normalize(getProperty(theNode, PLUGIN_DEPENDENCY));
130   if(aRequiredModule.empty())
131     return true;
132   std::set<std::string>::iterator it = myDependencyModules.begin();
133   for ( ; it != myDependencyModules.end(); it++ ) {
134     if (*it == aRequiredModule) return true;
135   }
136   return false;
137 }
138
139 std::list<std::string> Config_ModuleReader::importPlugin(const std::string& thePluginLibrary,
140                                                          const std::string& thePluginXmlConf)
141 {
142   if (thePluginXmlConf.empty()) {  //probably a third party library
143     loadLibrary(thePluginLibrary);
144     return std::list<std::string>();
145   }
146
147   Config_FeatureReader aReader = Config_FeatureReader(thePluginXmlConf,
148                                                       thePluginLibrary,
149                                                       myEventGenerated);
150   aReader.readAll();
151   return aReader.features();
152 }
153
154 std::string Config_ModuleReader::addPlugin(const std::string& aPluginLibrary,
155                                            const std::string& aPluginScript,
156                                            const std::string& aPluginConf)
157 {
158   PluginType aType = Config_ModuleReader::Binary;
159   std::string aPluginName;
160   if (!aPluginLibrary.empty()) {
161     aPluginName = aPluginLibrary;
162     if (aPluginConf.empty()) {
163       aType = Config_ModuleReader::Intrenal;
164     }
165   } else if (!aPluginScript.empty()) {
166     aPluginName = aPluginScript;
167     aType = Config_ModuleReader::Python;
168   }
169   if(!aPluginName.empty()) {
170     myPluginTypes[aPluginName] = aType;
171   }
172   addDependencyModule(aPluginName);
173   return aPluginName;
174 }
175
176 void Config_ModuleReader::loadPlugin(const std::string& thePluginName)
177 {
178   // informs model that plugin loading is started
179   static const Events_ID kEVENT_ID =
180     Events_Loop::loop()->eventByName(Config_PluginMessage::EVENT_ID());
181   std::shared_ptr<Config_PluginMessage> aMess(new Config_PluginMessage(kEVENT_ID, thePluginName));
182   Events_Loop::loop()->send(aMess);
183
184   PluginType aType = Config_ModuleReader::Binary;
185   if(myPluginTypes.find(thePluginName) != myPluginTypes.end()) {
186     aType = myPluginTypes.at(thePluginName);
187   }
188   switch (aType) {
189     case Config_ModuleReader::Python:
190       loadScript(thePluginName);
191       break;
192     case Config_ModuleReader::Binary:
193     case Config_ModuleReader::Intrenal:
194     default:
195       loadLibrary(thePluginName);
196       break;
197   }
198 }
199
200 void Config_ModuleReader::loadScript(const std::string& theFileName, bool theSendErr)
201 {
202   /* acquire python thread */
203   PyGILState_STATE gstate = PyGILState_Ensure();
204
205   PyObject* module = PyImport_ImportModule(theFileName.c_str());
206   if (!module) {
207     std::string anErrorMsg = "An error occurred while importing " + theFileName;
208     //Get detailed error message:
209     if (PyErr_Occurred()) {
210       PyObject *pstr, *ptype, *pvalue, *ptraceback;
211       PyErr_Fetch(&ptype, &pvalue, &ptraceback);
212       PyErr_NormalizeException(&ptype, &pvalue, &ptraceback);
213       pstr = PyObject_Str(pvalue);
214       std::string aPyError = std::string(PyUnicode_AsUTF8(pstr));
215       if (!aPyError.empty()) {
216         anErrorMsg += ":\n" + aPyError;
217       }
218       Py_XDECREF(pstr);
219       Py_XDECREF(ptype);
220       Py_XDECREF(pvalue);
221       Py_XDECREF(ptraceback);
222     }
223     if (theSendErr)
224       Events_InfoMessage("Config_ModuleReader", anErrorMsg).send();
225   }
226
227   /* release python thread */
228   PyGILState_Release(gstate);
229 }
230
231 void Config_ModuleReader::loadLibrary(const std::string& theLibName)
232 {
233   std::string aFileName = library(theLibName);
234   if (aFileName.empty())
235     return;
236
237   #ifdef WIN32
238   HINSTANCE aModLib = ::LoadLibrary(aFileName.c_str());
239   #else
240   void* aModLib = dlopen( aFileName.c_str(), RTLD_LAZY | RTLD_GLOBAL );
241   #endif
242   if(!aModLib && theLibName != "DFBrowser") { // don't show error for internal debugging tool
243     std::string anErrorMsg = "Failed to load " + aFileName;
244     #ifdef WIN32
245     DWORD   dwLastError = ::GetLastError();
246     LPSTR messageBuffer = NULL;
247     size_t size = ::FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER |
248                                  FORMAT_MESSAGE_FROM_SYSTEM |
249                                  FORMAT_MESSAGE_IGNORE_INSERTS,
250                                  NULL,
251                                  dwLastError,
252                                  MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
253                                  (LPSTR)&messageBuffer, 0, NULL);
254     anErrorMsg += ": " +  std::string(messageBuffer, size);
255     #else
256     anErrorMsg += ": " + std::string(dlerror());
257     #endif
258     std::cerr << anErrorMsg << std::endl;
259     Events_InfoMessage("Config_ModuleReader", anErrorMsg).send();
260   }
261 }
262
263 void Config_ModuleReader::addDependencyModule(const std::string& theModuleName)
264 {
265   myDependencyModules.insert(normalize(theModuleName));
266 }
267