1 # Copyright (C) 2009-2015 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, or (at your option) any later version.
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, cmake_src_compo_hxxpara
27 from module_generator import Service
29 from tempfile import mkstemp
30 from yacstypes import corba_rtn_type,moduleTypes
31 from gener import Library
33 class HXX2SALOMEParaComponent(Component):
34 def __init__(self, hxxfile , cpplib , cpp_path ):
35 # search a file within a directory tree
37 def search_file(pattern, root):
39 for path, dirs, files in os.walk(os.path.abspath(root)):
40 for filename in fnmatch.filter(files, pattern):
41 matches.append(os.path.join(path, filename))
44 hxxfileful=search_file(hxxfile,cpp_path)
45 cpplibful=search_file(cpplib,cpp_path)
46 assert len(hxxfileful) > 0 ,'Error in HXX2SALOMEComponent : file ' + hxxfile + ' not found in ' + cpp_path
47 assert len(cpplibful) > 0 ,'Error in HXX2SALOMEComponent : file ' + cpplib + ' not found in ' + cpp_path
49 self.hxxfile=hxxfile # to include it in servant implementation
51 # grab name of c++ component
52 from hxx_awk import parse01,parse1,parse2,parse3
53 cmd1="""awk '$1 == "class" && $0 !~ /;/ {print $2}' """ + hxxfileful[0] + """|awk -F: '{printf "%s",$1}' """
55 class_name=f.readlines()[0]
57 print "classname=",class_name
59 if cpplib[:3]=="lib" and cpplib[-3:]==".so":
60 cpplibname=cpplib[3:-3] # get rid of lib and .so, to use within makefile.am
62 cpplibname=class_name+"CXX" # the default name
66 # create temporary awk files
68 f01=os.fdopen(fd01,"w")
87 # awk parsing of hxx files - result written in file parse_type_result
88 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
95 # Retrieve the information which was generated in the file parse_type_result.
96 # The structure of the file is :
98 # Function return_type function_name
99 # [arg1_type arg1_name]
100 # [arg2_type arg2_name]
102 # The service names are stored in list_of_services
103 # The information relative to a service (called service_name) is stored in the dictionnary service_definition[service_name]
104 from hxx_awk import cpp2idl_mapping
105 from hxx_awk import cpp2yacs_mapping
106 cpp2yacs_mapping["const ParaMEDMEM::MEDCouplingFieldDouble*"]="SALOME_MED/MPIMEDCouplingFieldDoubleCorbaInterface"
107 cpp2yacs_mapping["const ParaMEDMEM::MEDCouplingFieldDouble&"]="SALOME_MED/MPIMEDCouplingFieldDoubleCorbaInterface"
108 cpp2yacs_mapping["ParaMEDMEM::MEDCouplingFieldDouble*&"]="SALOME_MED/MPIMEDCouplingFieldDoubleCorbaInterface"
109 cpp2yacs_mapping["ParaMEDMEM::MEDCouplingFieldDouble*"]="SALOME_MED/MPIMEDCouplingFieldDoubleCorbaInterface"
111 service_definition={}
112 result_parsing=open("parse_type_result","r")
113 for line in result_parsing.readlines():
114 line=line[0:-1] # get rid of trailing \n
115 words = string.split(line,';')
117 if len(words) >=3 and words[0] == "Function": # detect a new service
118 function_name=words[2]
119 if function_name != "getInputFieldTemplate":
120 list_of_services.append(function_name)
121 service_definition[function_name]={}
122 service_definition[function_name]["ret"]=words[1] # return type
123 service_definition[function_name]["inports"]=[]
124 service_definition[function_name]["outports"]=[]
125 service_definition[function_name]["ports"]=[]
126 service_definition[function_name]["impl"]=[]
127 service_definition[function_name]["thread_func_decl"]=[]
128 service_definition[function_name]["thread_str_decl"]=[]
130 if len(words) == 2 and function_name != "getInputFieldTemplate": # an argument type and argument name of a previous service
133 service_definition[list_of_services[-1]]["ports"].append( (argname,typename) ) # store in c++ order the arg names
135 # separate in from out parameters
136 inout=cpp2idl_mapping[typename][0:2]
137 assert inout=="in" or inout=="ou",'Error in table cpp2idl_mapping'
139 service_definition[list_of_services[-1]]["inports"].append( (argname,typename) )
141 service_definition[list_of_services[-1]]["outports"].append( (argname,typename) )
143 if service_definition.has_key('getInputFieldTemplate'):
144 del service_definition['getInputFieldTemplate']
146 # generate implementation of c++ servant
147 # store it in service_definition[serv]["impl"]
149 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
150 cpp_impl_b["ParaMEDMEM::MEDCouplingFieldDouble*"]="""\tParaMEDMEM::MPIMEDCouplingFieldDoubleServant * _rtn_field_i = new ParaMEDMEM::MPIMEDCouplingFieldDoubleServant(_orb,_poa,this,_rtn_cpp);
151 \t_rtn_cpp->decrRef();
152 \tSALOME_MED::MPIMEDCouplingFieldDoubleCorbaInterface_ptr _rtn_ior = _rtn_field_i->_this();\n"""
153 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"
155 from yacstypes import corbaTypes,corbaOutTypes
156 format_thread_signature="void * th_%s(void * st);" # this thread declaration will be included in servant's header
157 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
158 format_thread_create="""
159 // create threads to forward to other processes the service invocation
162 th = new pthread_t[_nbproc];
163 for(int ip=1;ip<_nbproc;ip++)
166 pthread_create(&(th[ip]),NULL,th_%(serv_name)s,(void*)st);
171 // waiting for all threads to complete
174 for(int ip=1;ip<_nbproc;ip++)
176 pthread_join(th[ip],&ret_th);
177 est = (except_st*)ret_th;
181 msg << "[" << ip << "] " << est->msg;
182 THROW_SALOME_CORBA_EXCEPTION(msg.str().c_str(),SALOME::INTERNAL_ERROR);
189 format_thread_impl="""
190 void *th_%(serv_name)s(void *s)
193 thread_%(serv_name)s_str *st = (thread_%(serv_name)s_str*)s;
194 except_st *est = new except_st;
195 est->exception = false;
199 %(module)s_ORB::%(component_name)s_Gen_var compo = %(module)s_ORB::%(component_name)s_Gen::_narrow((*(st->tior))[st->ip]);
200 compo->%(serv_name)s(%(arg_thread_invocation)s);
202 catch(const SALOME::SALOME_Exception &ex)
204 est->exception = true;
205 est->msg = ex.details.text;
207 catch(const CORBA::Exception &ex)
209 est->exception = true;
210 msg << "CORBA::Exception: " << ex;
211 est->msg = msg.str();
219 self.thread_impl="" # the implementation of the thread functions used to invoque services on slave processors
220 for serv in list_of_services:
222 print "service : ",serv
223 print " inports -> ",service_definition[serv]["inports"]
224 print " outports -> ",service_definition[serv]["outports"]
225 print " return -> ",service_definition[serv]["ret"]
227 # Part 0 : specific treatments for parallel components (call threads to forward the invocation to the service to all processes)
228 service_definition[serv]["thread_func_decl"]=format_thread_signature % serv
230 arg_thread_invocation=""
231 init_thread_str="thread_%s_str *st = new thread_%s_str;" % (serv,serv)
232 init_thread_str+="\n st->ip = ip;"
233 init_thread_str+="\n st->tior = _tior;"
234 for (argname,argtype) in service_definition[serv]["inports"]:
235 arg_declaration+=" "+corbaTypes[cpp2yacs_mapping[argtype]]+" "+argname+";\n"
236 init_thread_str+="\n st->"+argname+" = "+argname+";"
237 for (argname,argtype) in service_definition[serv]["outports"]:
238 arg_declaration+=" "+corbaOutTypes[cpp2yacs_mapping[argtype]]+" "+argname+";\n"
239 init_thread_str+="\n st->"+argname+" = "+argname+";"
240 for (argname,argtype) in service_definition[serv]["ports"]:
241 arg_thread_invocation+="st->"+argname+", "
242 if len(arg_thread_invocation)>0:
243 arg_thread_invocation=arg_thread_invocation[0:-2] # get rid of trailing comma
244 service_definition[serv]["thread_str_decl"]=format_thread_struct % { "serv_name" : serv, "arg_decl" : arg_declaration }
245 s_thread_call=format_thread_create % { "serv_name" : serv , "init_thread_str" : init_thread_str}
246 # within format_thread_impl the treatment of %(module) is postponed in makecxx() because we don't know here the module name
247 self.thread_impl+=format_thread_impl % {"serv_name" : serv , "arg_thread_invocation" : arg_thread_invocation , "component_name" : name, "module" : "%(module)s" }
249 # Part 1 : Argument pre-processing
250 s_argument_processing="//\tArguments processing\n"
251 for (argname,argtype) in service_definition[serv]["inports"] + service_definition[serv]["outports"]:
252 format=cpp_impl_a[argtype]
253 s_argument_processing += format % {"arg" : argname }
254 if s_argument_processing=="//\tArguments processing\n": # if there was no args
255 s_argument_processing=""
257 # if an argument called name is of type const char*, this argument is transmitted to getInputFieldTemplate()
258 # => we insert "name" between the bracket of getInputFieldTemplate()
259 indice_getInputFieldTemplate=s_argument_processing.find ("cppCompo_->getInputFieldTemplate();")
260 if s_argument_processing.find ("const std::string _name") != -1 and indice_getInputFieldTemplate != -1:
261 ind_insertion=indice_getInputFieldTemplate+33
262 s_argument_processing=s_argument_processing[:ind_insertion]+"name"+s_argument_processing[ind_insertion:-1]
264 # Part 2 : Call to the underlying c++ function
265 s_call_cpp_function="//\tCall cpp component\n\t"
266 rtn_type=service_definition[serv]["ret"]
267 if rtn_type == "void" : # if return type is void, the call syntax is different
268 s_call_cpp_function += "cppCompo_->%s(" % serv
270 s_call_cpp_function += "%s _rtn_cpp = cppCompo_->%s(" % (rtn_type ,serv )
272 for (argname,argtype) in service_definition[serv]["ports"]:
273 # special treatment for some arguments
276 if string.find(cpp_impl_a[argtype],"auto_ptr" ) != -1 :
277 post=".get()" # for auto_ptr argument, retrieve the raw pointer behind
278 if argtype == "const MEDMEM::MESH&" or argtype == "const MEDMEM::SUPPORT&" :
279 pre="*" # we cannot create MESHClient on the stack (private constructor), so we create it on the heap and dereference it
280 post+="," # separator between arguments
281 s_call_cpp_function += " %s_%s%s" % ( pre,argname,post)
282 if s_call_cpp_function[-1]==',':
283 s_call_cpp_function=s_call_cpp_function[0:-1] # get rid of trailing comma
284 s_call_cpp_function=s_call_cpp_function+');\n'
286 # Part 3.a : Out Argument Post-processing
287 s_argument_postprocessing="//\tPost-processing & return\n"
288 for (argname,argtype) in service_definition[serv]["outports"]:
289 format=cpp_impl_c[argtype]
290 s_argument_postprocessing += format % {"arg" : argname, "module" : "%(module)s" } # the treatment of %(module) is postponed in makecxx()
291 # because we don't know here the module name
292 # Part 3.b : In Argument Post-processing
293 for (argname,argtype) in service_definition[serv]["inports"]:
294 if cpp_impl_c.has_key(argtype): # not all in types require a treatment
295 format=cpp_impl_c[argtype]
296 s_argument_postprocessing += format % {"arg" : argname, "module" : "%(module)s" } # id : treatment of %(module) is postponed in makecxx
298 # Part 3.c : return processing
299 s_rtn_processing=cpp_impl_b[rtn_type]
300 if rtn_type != "void":
301 s_rtn_processing += "\treturn _rtn_ior;"
303 service_definition[serv]["impl"] = s_thread_call + s_argument_processing + s_call_cpp_function + s_thread_join + s_argument_postprocessing + s_rtn_processing
305 print "implementation :\n",service_definition[serv]["impl"]
308 # Create a list of Service objects (called services), and give it to Component constructor
311 self.use_medmem=False
312 self.use_medcoupling=False
313 self.thread_func_decl=[]
314 self.thread_str_decl=[]
315 for serv in list_of_services:
316 # for inports and outports, Service class expects a list of tuples, each tuple containing the name and the yacs type of the port
317 # thus we need to convert c++ types to yacs types (we use for that the cpp2yacs_mapping table
319 for i in range( len(service_definition[serv]["inports"]) ):
320 inports.append( [service_definition[serv]["inports"][i][0], cpp2yacs_mapping[service_definition[serv]["inports"][i][1]] ] )
322 for i in range( len(service_definition[serv]["outports"]) ):
323 outports.append( [service_definition[serv]["outports"][i][0], cpp2yacs_mapping[service_definition[serv]["outports"][i][1]] ] )
326 if service_definition[serv]["ret"] != "void":
327 Return=cpp2yacs_mapping[service_definition[serv]["ret"]]
329 # find out if component uses medmem types and/or medcoupling types
330 for (argname,argtype) in inports + outports + [("return",Return)]:
331 if moduleTypes[argtype]=="MED":
332 if argtype.count("Coupling")>0:
333 self.use_medcoupling=True
338 code=service_definition[serv]["impl"]
340 print "service : ",serv
341 print " inports -> ",service_definition[serv]["inports"]
342 print " converted inports -> ",inports
343 print " outports -> ",service_definition[serv]["outports"]
344 print " converted outports -> ",outports
345 print " Return -> ",service_definition[serv]["ret"]
346 print " converted Return -> ",Return
348 services.append(Service(serv,
355 self.thread_func_decl.append(service_definition[serv]["thread_func_decl"])
356 self.thread_str_decl.append(service_definition[serv]["thread_str_decl"])
357 # Includes="-I${"+name+"CPP_ROOT_DIR}/include"
358 Includes = os.path.join(cpp_path, "include")
359 # Libs="-L${"+name+"CPP_ROOT_DIR}/lib -l"+cpplibname
360 # Libs=[cpplibname+" PATH "+ os.path.join(cpp_path, "lib") ]
361 Libs = [ Library( name=cpplibname, path=os.path.join(cpp_path, "lib"))]
364 self.inheritedconstructor=""
366 #include CORBA_SERVER_HEADER(MEDCouplingCorbaServantTest)
367 #include "MPIMEDCouplingFieldDoubleServant.hxx"
370 Component.__init__(self, name, services, impl="CPP", libs=Libs,
371 rlibs="", includes=Includes, kind="lib",
372 sources=None,inheritedclass=Inheritedclass,
375 # -----------------------------------------------------------------------------
376 def libraryName(self):
377 """ Name of the target library
379 return self.name + "Engine"
381 # ------------------------------------------------------------------------------
382 def targetProperties(self):
383 """ define the rpath property of the target using self.rlibs
385 string containing the commands to add to cmake
388 if self.rlibs.strip() :
389 text="SET_TARGET_PROPERTIES( %sEngine PROPERTIES INSTALL_RPATH %s)\n" % (self.name, self.rlibs)
392 # ------------------------------------------------------------------------------
393 def makeCompo(self, gen):
394 """generate files for C++ component
396 return a dict where key is the file name and value is the content of the file
398 cxxfile = "%s_i.cxx" % self.name
399 hxxfile = "%s_i.hxx" % self.name
400 (cmake_text, cmake_vars) = self.additionalLibraries()
402 cmakelist_content = cmake_src_compo_hxxpara.substitute(
403 module = gen.module.name,
404 component = self.name,
405 componentlib = self.libraryName(),
406 includes = self.includes,
408 find_libs = cmake_text,
409 target_properties = self.targetProperties()
412 return {"CMakeLists.txt":cmakelist_content,
413 cxxfile:self.makecxx(gen),
414 hxxfile:self.makehxx(gen)
417 # def getMakefileItems(self,gen):
418 # makefileItems={"header":"""
419 #include $(top_srcdir)/adm_local/make_common_starter.am
422 # makefileItems["lib_LTLIBRARIES"]=["lib"+self.name+"Engine.la"]
423 # makefileItems["salomeinclude_HEADERS"]=["%s_i.hxx" % self.name]
424 # makefileItems["body"]=compoMakefile.substitute(module=gen.module.name,
425 # component=self.name,
427 # includes=self.includes)
428 # return makefileItems
430 def makehxx(self, gen):
431 """return a string that is the content of .hxx file
434 for serv in self.services:
435 service = " %s %s(" % (corba_rtn_type(serv.ret,gen.module.name),serv.name)
436 service = service+gen.makeArgs(serv)+") throw (SALOME::SALOME_Exception);"
437 services.append(service)
438 servicesdef = "\n".join(services)
440 inheritedclass=self.inheritedclass
441 thread_func_decl="\n".join(self.thread_func_decl)
442 thread_str_decl="\n".join(self.thread_str_decl)
444 print "thread_func_decl : "
445 print thread_func_decl
446 print "thread_str_decl : "
447 print thread_str_decl
449 if self.inheritedclass:
450 inheritedclass= " public virtual " + self.inheritedclass + ","
452 return hxxCompo.substitute(component=self.name, module=gen.module.name, thread_func_decl=thread_func_decl,
453 thread_str_decl=thread_str_decl, servicesdef=servicesdef, inheritedclass=inheritedclass,
454 compodefs=self.compodefs)
456 def makecxx(self, gen, exe=0):
457 """return a string that is the content of .cxx file
462 for serv in self.services:
463 defs.append(serv.defs)
464 service = cxxService.substitute(component=self.name, service=serv.name,ret=corba_rtn_type(serv.ret,gen.module.name),
465 parameters=gen.makeArgs(serv),
466 body=serv.body % {"module":gen.module.name+"_ORB"} )
467 services.append(service)
468 return cxxCompo.substitute(component=self.name, cxx_include_file=self.hxxfile,
469 inheritedconstructor=self.inheritedconstructor,
470 servicesdef="\n".join(defs),
471 servicesimpl="\n".join(services),
472 thread_impl=self.thread_impl % {"module":gen.module.name} )