Salome HOME
Update copyrights 2014.
[tools/yacsgen.git] / module_generator / hxxparacompo.py
1 # Copyright (C) 2009-2014  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 email : webmaster.salome@opencascade.com
18 #
19 """
20   Module that generates SALOME c++ Component from a non SALOME c++ component (its header and its shares library)
21 """
22
23 debug=1
24 import os
25 from gener import Component, Invalid
26 from hxx_para_tmpl import cxxService, hxxCompo, cxxCompo, compoMakefile
27 from module_generator import Service
28 import string
29 from tempfile import mkstemp
30 from yacstypes import corba_rtn_type,moduleTypes
31
32 class HXX2SALOMEParaComponent(Component):
33   def __init__(self, hxxfile , cpplib , cpp_path ):
34     # search a file within a directory tree
35     import fnmatch
36     def search_file(pattern, root):
37         matches = []
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))
41         return matches
42
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
47
48     self.hxxfile=hxxfile  # to include it in servant implementation
49
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}' """
53     f=os.popen(cmd1)
54     class_name=f.readlines()[0]
55     name=class_name
56     print "classname=",class_name
57
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
60     else:
61         cpplibname=class_name+"CXX"  # the default name
62
63     f.close()
64
65     # create temporary awk files
66     (fd01,p01n)=mkstemp()
67     f01=os.fdopen(fd01,"w")
68     f01.write(parse01)
69     f01.close()
70
71     (fd1,p1n)=mkstemp()
72     f1=os.fdopen(fd1,"w")
73     f1.write(parse1)
74     f1.close()
75
76     (fd2,p2n)=mkstemp()
77     f2=os.fdopen(fd2,"w")
78     f2.write(parse2)
79     f2.close()
80
81     (fd3,p3n)=mkstemp()
82     f3=os.fdopen(fd3,"w")
83     f3.write(parse3)
84     f3.close()
85
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
88     os.system(cmd2)
89     os.remove(p01n)
90     os.remove(p1n)
91     os.remove(p2n)
92     os.remove(p3n)
93
94     # Retrieve the information which was generated in the file parse_type_result.
95     # The structure of the file is :
96     #
97     #   Function  return_type   function_name
98     #   [arg1_type  arg1_name]
99     #   [arg2_type  arg2_name]
100     #   ...
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"
109     list_of_services=[]
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,';')
115
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"]=[]
128
129         if len(words) == 2 and function_name != "getInputFieldTemplate":  # an argument type and argument name of a previous service
130             typename=words[0]
131             argname=words[1]
132             service_definition[list_of_services[-1]]["ports"].append( (argname,typename) ) # store in c++ order the arg names
133
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'
137             if inout == "in":
138                 service_definition[list_of_services[-1]]["inports"].append( (argname,typename) )
139             else:
140                 service_definition[list_of_services[-1]]["outports"].append( (argname,typename) )
141
142     if service_definition.has_key('getInputFieldTemplate'):
143         del service_definition['getInputFieldTemplate']
144     #
145     # generate implementation of c++ servant
146     # store it in service_definition[serv]["impl"]
147     #
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"
153
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
159         if(_numproc == 0)
160         {
161             th = new pthread_t[_nbproc];
162             for(int ip=1;ip<_nbproc;ip++)
163             {
164                 %(init_thread_str)s
165                 pthread_create(&(th[ip]),NULL,th_%(serv_name)s,(void*)st);
166             }
167         }
168 """
169     s_thread_join="""
170 //      waiting for all threads to complete
171         if(_numproc == 0)
172         {
173             for(int ip=1;ip<_nbproc;ip++)
174             {
175                 pthread_join(th[ip],&ret_th);
176                 est = (except_st*)ret_th;
177                 if(est->exception)
178                 {
179                     ostringstream msg;
180                     msg << "[" << ip << "] " << est->msg;
181                     THROW_SALOME_CORBA_EXCEPTION(msg.str().c_str(),SALOME::INTERNAL_ERROR);
182                 }
183                 delete est;
184             }
185           delete[] th;
186         }
187 """
188     format_thread_impl="""
189 void *th_%(serv_name)s(void *s)
190 {
191   ostringstream msg;
192   thread_%(serv_name)s_str *st = (thread_%(serv_name)s_str*)s;
193   except_st *est = new except_st;
194   est->exception = false;
195
196   try
197     {
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);
200     }
201   catch(const SALOME::SALOME_Exception &ex)
202     {
203       est->exception = true;
204       est->msg = ex.details.text;
205     }
206   catch(const CORBA::Exception &ex)
207     {
208       est->exception = true;
209       msg << "CORBA::Exception: " << ex;
210       est->msg = msg.str();
211     }
212   delete st;
213   return((void*)est);
214 }
215
216 """
217
218     self.thread_impl=""  # the implementation of the thread functions used to invoque services on slave processors 
219     for serv in list_of_services:
220         if debug:
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"]
225
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
228         arg_declaration=""
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" } 
247
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=""
255
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]
262
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
268         else:
269             s_call_cpp_function += "%s _rtn_cpp = cppCompo_->%s(" % (rtn_type ,serv )
270
271         for (argname,argtype) in service_definition[serv]["ports"]:
272               # special treatment for some arguments
273               post=""
274               pre=""
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'
284
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
296
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;"
301
302         service_definition[serv]["impl"] = s_thread_call + s_argument_processing + s_call_cpp_function + s_thread_join  + s_argument_postprocessing + s_rtn_processing
303         if debug:
304             print "implementation :\n",service_definition[serv]["impl"]
305
306     #
307     # Create a list of Service objects (called services), and give it to Component constructor
308     #
309     services=[]
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
317         inports=[]
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]] ] )
320         outports=[]
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]] ] )
323
324         Return="void"
325         if service_definition[serv]["ret"] != "void":
326             Return=cpp2yacs_mapping[service_definition[serv]["ret"]]
327
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
333                 else:
334                     self.use_medmem=True
335                 break
336
337         code=service_definition[serv]["impl"]
338         if debug:
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
346
347         services.append(Service(serv, 
348            inport=inports, 
349            outport=outports,
350            ret=Return, 
351            defs="", 
352            body=code,
353            ) )
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
358     Compodefs=""
359     Inheritedclass=""
360     self.inheritedconstructor=""
361     Compodefs="""
362 #include CORBA_SERVER_HEADER(MEDCouplingCorbaServantTest)
363 #include "MPIMEDCouplingFieldDoubleServant.hxx"
364 """
365
366     Component.__init__(self, name, services, impl="CPP", libs=Libs,
367                              rlibs="", includes=Includes, kind="lib",
368                              sources=None,inheritedclass=Inheritedclass,
369                              compodefs=Compodefs)
370
371   def makeCompo(self, gen):
372     """generate files for C++ component
373
374        return a dict where key is the file name and value is the content of the file
375     """
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)
381            }
382
383   def getMakefileItems(self,gen):
384       makefileItems={"header":"""
385 include $(top_srcdir)/adm_local/make_common_starter.am
386
387 """}
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,
391                                                      component=self.name,
392                                                      libs=self.libs,
393                                                      includes=self.includes)
394       return makefileItems
395
396   def makehxx(self, gen):
397     """return a string that is the content of .hxx file
398     """
399     services = []
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)
405
406     inheritedclass=self.inheritedclass
407     thread_func_decl="\n".join(self.thread_func_decl)
408     thread_str_decl="\n".join(self.thread_str_decl)
409     if debug:
410         print "thread_func_decl : "
411         print thread_func_decl
412         print "thread_str_decl : "
413         print thread_str_decl
414
415     if self.inheritedclass:
416       inheritedclass= " public virtual " + self.inheritedclass + ","
417
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)
421
422   def makecxx(self, gen, exe=0):
423     """return a string that is the content of .cxx file
424     """
425     services = []
426     inits = []
427     defs = []
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} )
439