1 # Copyright (C) 2009-2013 EDF R&D
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.
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.
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
17 # See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
20 Module that generates SALOME c++ Component from a non SALOME c++ component (its header and its shares library)
25 from gener import Component, Invalid
26 from hxx_para_tmpl import cxxService, hxxCompo, cxxCompo, compoMakefile
27 from module_generator import Service
29 from tempfile import mkstemp
30 from yacstypes import corba_rtn_type,moduleTypes
32 class HXX2SALOMEParaComponent(Component):
33 def __init__(self, hxxfile , cpplib , cpp_path ):
34 # search a file within a directory tree
36 def search_file(pattern, root):
38 for path, dirs, files in os.walk(os.path.abspath(root)):
39 for filename in fnmatch.filter(files, pattern):
40 matches.append(os.path.join(path, filename))
43 hxxfileful=search_file(hxxfile,cpp_path)
44 cpplibful=search_file(cpplib,cpp_path)
45 assert len(hxxfileful) > 0 ,'Error in HXX2SALOMEComponent : file ' + hxxfile + ' not found in ' + cpp_path
46 assert len(cpplibful) > 0 ,'Error in HXX2SALOMEComponent : file ' + cpplib + ' not found in ' + cpp_path
48 self.hxxfile=hxxfile # to include it in servant implementation
50 # grab name of c++ component
51 from hxx_awk import parse01,parse1,parse2,parse3
52 cmd1="""awk '$1 == "class" && $0 !~ /;/ {print $2}' """ + hxxfileful[0] + """|awk -F: '{printf "%s",$1}' """
54 class_name=f.readlines()[0]
56 print "classname=",class_name
58 if cpplib[:3]=="lib" and cpplib[-3:]==".so":
59 cpplibname=cpplib[3:-3] # get rid of lib and .so, to use within makefile.am
61 cpplibname=class_name+"CXX" # the default name
65 # create temporary awk files
67 f01=os.fdopen(fd01,"w")
86 # awk parsing of hxx files - result written in file parse_type_result
87 cmd2="cat " + hxxfileful[0] + " | awk -f " + p01n + """ | sed 's/virtual //g' | sed 's/MEDMEM_EXPORT//g' | sed 's/throw.*;/;/g' | awk -f """ + p1n + " | awk -f " + p2n + " | awk -v class_name=" + class_name + " -f " + p3n
94 # Retrieve the information which was generated in the file parse_type_result.
95 # The structure of the file is :
97 # Function return_type function_name
98 # [arg1_type arg1_name]
99 # [arg2_type arg2_name]
101 # The service names are stored in list_of_services
102 # The information relative to a service (called service_name) is stored in the dictionnary service_definition[service_name]
103 from hxx_awk import cpp2idl_mapping
104 from hxx_awk import cpp2yacs_mapping
105 cpp2yacs_mapping["const ParaMEDMEM::MEDCouplingFieldDouble*"]="SALOME_MED/MPIMEDCouplingFieldDoubleCorbaInterface"
106 cpp2yacs_mapping["const ParaMEDMEM::MEDCouplingFieldDouble&"]="SALOME_MED/MPIMEDCouplingFieldDoubleCorbaInterface"
107 cpp2yacs_mapping["ParaMEDMEM::MEDCouplingFieldDouble*&"]="SALOME_MED/MPIMEDCouplingFieldDoubleCorbaInterface"
108 cpp2yacs_mapping["ParaMEDMEM::MEDCouplingFieldDouble*"]="SALOME_MED/MPIMEDCouplingFieldDoubleCorbaInterface"
110 service_definition={}
111 result_parsing=open("parse_type_result","r")
112 for line in result_parsing.readlines():
113 line=line[0:-1] # get rid of trailing \n
114 words = string.split(line,';')
116 if len(words) >=3 and words[0] == "Function": # detect a new service
117 function_name=words[2]
118 if function_name != "getInputFieldTemplate":
119 list_of_services.append(function_name)
120 service_definition[function_name]={}
121 service_definition[function_name]["ret"]=words[1] # return type
122 service_definition[function_name]["inports"]=[]
123 service_definition[function_name]["outports"]=[]
124 service_definition[function_name]["ports"]=[]
125 service_definition[function_name]["impl"]=[]
126 service_definition[function_name]["thread_func_decl"]=[]
127 service_definition[function_name]["thread_str_decl"]=[]
129 if len(words) == 2 and function_name != "getInputFieldTemplate": # an argument type and argument name of a previous service
132 service_definition[list_of_services[-1]]["ports"].append( (argname,typename) ) # store in c++ order the arg names
134 # separate in from out parameters
135 inout=cpp2idl_mapping[typename][0:2]
136 assert inout=="in" or inout=="ou",'Error in table cpp2idl_mapping'
138 service_definition[list_of_services[-1]]["inports"].append( (argname,typename) )
140 service_definition[list_of_services[-1]]["outports"].append( (argname,typename) )
142 if service_definition.has_key('getInputFieldTemplate'):
143 del service_definition['getInputFieldTemplate']
145 # generate implementation of c++ servant
146 # store it in service_definition[serv]["impl"]
148 from hxx_awk import cpp_impl_a,cpp_impl_b,cpp_impl_c # these tables contain the part of code which depends upon c++ types
149 cpp_impl_b["ParaMEDMEM::MEDCouplingFieldDouble*"]="""\tParaMEDMEM::MPIMEDCouplingFieldDoubleServant * _rtn_field_i = new ParaMEDMEM::MPIMEDCouplingFieldDoubleServant(_orb,_poa,this,_rtn_cpp);
150 \t_rtn_cpp->decrRef();
151 \tSALOME_MED::MPIMEDCouplingFieldDoubleCorbaInterface_ptr _rtn_ior = _rtn_field_i->_this();\n"""
152 cpp_impl_a["const ParaMEDMEM::MEDCouplingFieldDouble*"]="\tParaMEDMEM::MEDCouplingFieldDouble* _%(arg)s=cppCompo_->getInputFieldTemplate();\n\t_setInputField(%(arg)s,_%(arg)s);\n\t_initializeCoupling(%(arg)s);\n"
154 from yacstypes import corbaTypes,corbaOutTypes
155 format_thread_signature="void * th_%s(void * st);" # this thread declaration will be included in servant's header
156 format_thread_struct="typedef struct {\n int ip;\n Engines::IORTab* tior;\n%(arg_decl)s} thread_%(serv_name)s_str;" # this thread declaration will be included in servant's header
157 format_thread_create="""
158 // create threads to forward to other processes the service invocation
161 th = new pthread_t[_nbproc];
162 for(int ip=1;ip<_nbproc;ip++)
165 pthread_create(&(th[ip]),NULL,th_%(serv_name)s,(void*)st);
170 // waiting for all threads to complete
173 for(int ip=1;ip<_nbproc;ip++)
175 pthread_join(th[ip],&ret_th);
176 est = (except_st*)ret_th;
180 msg << "[" << ip << "] " << est->msg;
181 THROW_SALOME_CORBA_EXCEPTION(msg.str().c_str(),SALOME::INTERNAL_ERROR);
188 format_thread_impl="""
189 void *th_%(serv_name)s(void *s)
192 thread_%(serv_name)s_str *st = (thread_%(serv_name)s_str*)s;
193 except_st *est = new except_st;
194 est->exception = false;
198 %(module)s_ORB::%(component_name)s_Gen_var compo = %(module)s_ORB::%(component_name)s_Gen::_narrow((*(st->tior))[st->ip]);
199 compo->%(serv_name)s(%(arg_thread_invocation)s);
201 catch(const SALOME::SALOME_Exception &ex)
203 est->exception = true;
204 est->msg = ex.details.text;
206 catch(const CORBA::Exception &ex)
208 est->exception = true;
209 msg << "CORBA::Exception: " << ex;
210 est->msg = msg.str();
218 self.thread_impl="" # the implementation of the thread functions used to invoque services on slave processors
219 for serv in list_of_services:
221 print "service : ",serv
222 print " inports -> ",service_definition[serv]["inports"]
223 print " outports -> ",service_definition[serv]["outports"]
224 print " return -> ",service_definition[serv]["ret"]
226 # Part 0 : specific treatments for parallel components (call threads to forward the invocation to the service to all processes)
227 service_definition[serv]["thread_func_decl"]=format_thread_signature % serv
229 arg_thread_invocation=""
230 init_thread_str="thread_%s_str *st = new thread_%s_str;" % (serv,serv)
231 init_thread_str+="\n st->ip = ip;"
232 init_thread_str+="\n st->tior = _tior;"
233 for (argname,argtype) in service_definition[serv]["inports"]:
234 arg_declaration+=" "+corbaTypes[cpp2yacs_mapping[argtype]]+" "+argname+";\n"
235 init_thread_str+="\n st->"+argname+" = "+argname+";"
236 for (argname,argtype) in service_definition[serv]["outports"]:
237 arg_declaration+=" "+corbaOutTypes[cpp2yacs_mapping[argtype]]+" "+argname+";\n"
238 init_thread_str+="\n st->"+argname+" = "+argname+";"
239 for (argname,argtype) in service_definition[serv]["ports"]:
240 arg_thread_invocation+="st->"+argname+", "
241 if len(arg_thread_invocation)>0:
242 arg_thread_invocation=arg_thread_invocation[0:-2] # get rid of trailing comma
243 service_definition[serv]["thread_str_decl"]=format_thread_struct % { "serv_name" : serv, "arg_decl" : arg_declaration }
244 s_thread_call=format_thread_create % { "serv_name" : serv , "init_thread_str" : init_thread_str}
245 # within format_thread_impl the treatment of %(module) is postponed in makecxx() because we don't know here the module name
246 self.thread_impl+=format_thread_impl % {"serv_name" : serv , "arg_thread_invocation" : arg_thread_invocation , "component_name" : name, "module" : "%(module)s" }
248 # Part 1 : Argument pre-processing
249 s_argument_processing="//\tArguments processing\n"
250 for (argname,argtype) in service_definition[serv]["inports"] + service_definition[serv]["outports"]:
251 format=cpp_impl_a[argtype]
252 s_argument_processing += format % {"arg" : argname }
253 if s_argument_processing=="//\tArguments processing\n": # if there was no args
254 s_argument_processing=""
256 # if an argument called name is of type const char*, this argument is transmitted to getInputFieldTemplate()
257 # => we insert "name" between the bracket of getInputFieldTemplate()
258 indice_getInputFieldTemplate=s_argument_processing.find ("cppCompo_->getInputFieldTemplate();")
259 if s_argument_processing.find ("const std::string _name") != -1 and indice_getInputFieldTemplate != -1:
260 ind_insertion=indice_getInputFieldTemplate+33
261 s_argument_processing=s_argument_processing[:ind_insertion]+"name"+s_argument_processing[ind_insertion:-1]
263 # Part 2 : Call to the underlying c++ function
264 s_call_cpp_function="//\tCall cpp component\n\t"
265 rtn_type=service_definition[serv]["ret"]
266 if rtn_type == "void" : # if return type is void, the call syntax is different
267 s_call_cpp_function += "cppCompo_->%s(" % serv
269 s_call_cpp_function += "%s _rtn_cpp = cppCompo_->%s(" % (rtn_type ,serv )
271 for (argname,argtype) in service_definition[serv]["ports"]:
272 # special treatment for some arguments
275 if string.find(cpp_impl_a[argtype],"auto_ptr" ) != -1 :
276 post=".get()" # for auto_ptr argument, retrieve the raw pointer behind
277 if argtype == "const MEDMEM::MESH&" or argtype == "const MEDMEM::SUPPORT&" :
278 pre="*" # we cannot create MESHClient on the stack (private constructor), so we create it on the heap and dereference it
279 post+="," # separator between arguments
280 s_call_cpp_function += " %s_%s%s" % ( pre,argname,post)
281 if s_call_cpp_function[-1]==',':
282 s_call_cpp_function=s_call_cpp_function[0:-1] # get rid of trailing comma
283 s_call_cpp_function=s_call_cpp_function+');\n'
285 # Part 3.a : Out Argument Post-processing
286 s_argument_postprocessing="//\tPost-processing & return\n"
287 for (argname,argtype) in service_definition[serv]["outports"]:
288 format=cpp_impl_c[argtype]
289 s_argument_postprocessing += format % {"arg" : argname, "module" : "%(module)s" } # the treatment of %(module) is postponed in makecxx()
290 # because we don't know here the module name
291 # Part 3.b : In Argument Post-processing
292 for (argname,argtype) in service_definition[serv]["inports"]:
293 if cpp_impl_c.has_key(argtype): # not all in types require a treatment
294 format=cpp_impl_c[argtype]
295 s_argument_postprocessing += format % {"arg" : argname, "module" : "%(module)s" } # id : treatment of %(module) is postponed in makecxx
297 # Part 3.c : return processing
298 s_rtn_processing=cpp_impl_b[rtn_type]
299 if rtn_type != "void":
300 s_rtn_processing += "\treturn _rtn_ior;"
302 service_definition[serv]["impl"] = s_thread_call + s_argument_processing + s_call_cpp_function + s_thread_join + s_argument_postprocessing + s_rtn_processing
304 print "implementation :\n",service_definition[serv]["impl"]
307 # Create a list of Service objects (called services), and give it to Component constructor
310 self.use_medmem=False
311 self.use_medcoupling=False
312 self.thread_func_decl=[]
313 self.thread_str_decl=[]
314 for serv in list_of_services:
315 # for inports and outports, Service class expects a list of tuples, each tuple containing the name and the yacs type of the port
316 # thus we need to convert c++ types to yacs types (we use for that the cpp2yacs_mapping table
318 for i in range( len(service_definition[serv]["inports"]) ):
319 inports.append( [service_definition[serv]["inports"][i][0], cpp2yacs_mapping[service_definition[serv]["inports"][i][1]] ] )
321 for i in range( len(service_definition[serv]["outports"]) ):
322 outports.append( [service_definition[serv]["outports"][i][0], cpp2yacs_mapping[service_definition[serv]["outports"][i][1]] ] )
325 if service_definition[serv]["ret"] != "void":
326 Return=cpp2yacs_mapping[service_definition[serv]["ret"]]
328 # find out if component uses medmem types and/or medcoupling types
329 for (argname,argtype) in inports + outports + [("return",Return)]:
330 if moduleTypes[argtype]=="MED":
331 if argtype.count("Coupling")>0:
332 self.use_medcoupling=True
337 code=service_definition[serv]["impl"]
339 print "service : ",serv
340 print " inports -> ",service_definition[serv]["inports"]
341 print " converted inports -> ",inports
342 print " outports -> ",service_definition[serv]["outports"]
343 print " converted outports -> ",outports
344 print " Return -> ",service_definition[serv]["ret"]
345 print " converted Return -> ",Return
347 services.append(Service(serv,
354 self.thread_func_decl.append(service_definition[serv]["thread_func_decl"])
355 self.thread_str_decl.append(service_definition[serv]["thread_str_decl"])
356 Includes="-I${"+name+"CPP_ROOT_DIR}/include"
357 Libs="-L${"+name+"CPP_ROOT_DIR}/lib -l"+cpplibname
360 self.inheritedconstructor=""
362 #include CORBA_SERVER_HEADER(MEDCouplingCorbaServantTest)
363 #include "MPIMEDCouplingFieldDoubleServant.hxx"
366 Component.__init__(self, name, services, impl="CPP", libs=Libs,
367 rlibs="", includes=Includes, kind="lib",
368 sources=None,inheritedclass=Inheritedclass,
371 def makeCompo(self, gen):
372 """generate files for C++ component
374 return a dict where key is the file name and value is the content of the file
376 cxxfile = "%s_i.cxx" % self.name
377 hxxfile = "%s_i.hxx" % self.name
378 return {"Makefile.am":gen.makeMakefile(self.getMakefileItems(gen)),
379 cxxfile:self.makecxx(gen),
380 hxxfile:self.makehxx(gen)
383 def getMakefileItems(self,gen):
384 makefileItems={"header":"""
385 include $(top_srcdir)/adm_local/make_common_starter.am
388 makefileItems["lib_LTLIBRARIES"]=["lib"+self.name+"Engine.la"]
389 makefileItems["salomeinclude_HEADERS"]=["%s_i.hxx" % self.name]
390 makefileItems["body"]=compoMakefile.substitute(module=gen.module.name,
393 includes=self.includes)
396 def makehxx(self, gen):
397 """return a string that is the content of .hxx file
400 for serv in self.services:
401 service = " %s %s(" % (corba_rtn_type(serv.ret,gen.module.name),serv.name)
402 service = service+gen.makeArgs(serv)+") throw (SALOME::SALOME_Exception);"
403 services.append(service)
404 servicesdef = "\n".join(services)
406 inheritedclass=self.inheritedclass
407 thread_func_decl="\n".join(self.thread_func_decl)
408 thread_str_decl="\n".join(self.thread_str_decl)
410 print "thread_func_decl : "
411 print thread_func_decl
412 print "thread_str_decl : "
413 print thread_str_decl
415 if self.inheritedclass:
416 inheritedclass= " public virtual " + self.inheritedclass + ","
418 return hxxCompo.substitute(component=self.name, module=gen.module.name, thread_func_decl=thread_func_decl,
419 thread_str_decl=thread_str_decl, servicesdef=servicesdef, inheritedclass=inheritedclass,
420 compodefs=self.compodefs)
422 def makecxx(self, gen, exe=0):
423 """return a string that is the content of .cxx file
428 for serv in self.services:
429 defs.append(serv.defs)
430 service = cxxService.substitute(component=self.name, service=serv.name,ret=corba_rtn_type(serv.ret,gen.module.name),
431 parameters=gen.makeArgs(serv),
432 body=serv.body % {"module":gen.module.name+"_ORB"} )
433 services.append(service)
434 return cxxCompo.substitute(component=self.name, cxx_include_file=self.hxxfile,
435 inheritedconstructor=self.inheritedconstructor,
436 servicesdef="\n".join(defs),
437 servicesimpl="\n".join(services),
438 thread_impl=self.thread_impl % {"module":gen.module.name} )