Salome HOME
Merge from V6_main 01/04/2013
[tools/yacsgen.git] / module_generator / hxxcompo.py
1 # Copyright (C) 2009-2013  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.
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 
21   (its header and its shares library)
22 """
23
24 debug=1
25 import os
26 import string
27 import fnmatch
28 from tempfile import mkstemp
29 from gener import Component, Invalid
30 from hxx_tmpl import cxxService, hxxCompo, cxxCompo, compoMakefile
31 from module_generator import Service
32 from yacstypes import corba_rtn_type,moduleTypes
33 from hxx_awk import parse01,parse1,parse2,parse3
34 from hxx_awk import cpp2idl_mapping
35 # these tables contain the part of code which depends upon c++ types
36 from hxx_awk import cpp_impl_a,cpp_impl_b,cpp_impl_c  
37 from hxx_awk import cpp2yacs_mapping
38 from tempfile import mkdtemp
39 from hxx_tmpl_gui import hxxgui_cxx, hxxgui_h, hxxgui_icon_ts
40 from hxx_tmpl_gui import hxxgui_message_en, hxxgui_message_fr
41 from hxx_tmpl_gui import hxxgui_config, hxxgui_xml_fr, hxxgui_xml_en
42
43 # ------------------------------------------------------------------------------
44
45 class HXX2SALOMEComponent(Component):
46   def __init__(self, hxxfile , cpplib , cpp_path ):
47     # search a file within a directory tree
48     def search_file(pattern, root):
49         matches = []
50         for path, dirs, files in os.walk(os.path.abspath(root)):
51             for filename in fnmatch.filter(files, pattern):
52                  matches.append(os.path.join(path, filename))
53         return matches
54
55     hxxfileful = search_file(hxxfile,cpp_path)
56     cpplibful = search_file(cpplib,cpp_path)
57     format_error = 'Error in HXX2SALOMEComponent : file %s ot found in %s'
58     assert len(hxxfileful) > 0, format_error %  (hxxfile, cpp_path)
59     assert len(cpplibful) > 0, format_error % (cpplib, cpp_path)
60     hxxfile = hxxfileful[0]
61     cpplib = cpplibful[0]
62
63     # grab name of c++ component
64     cmd1="""awk '$1 == "class" && $0 !~ /;/ {print $2}' """ + hxxfile +\
65          """|awk -F: '{printf "%s",$1}' """
66     f=os.popen(cmd1)
67     class_name=f.readlines()[0]
68     name=class_name
69     print "classname=",class_name
70     f.close()
71
72     # create temporary awk files for the parsing
73     (fd01,p01n)=mkstemp()
74     f01=os.fdopen(fd01,"w")
75     f01.write(parse01)
76     f01.close()
77
78     (fd1,p1n)=mkstemp()
79     f1=os.fdopen(fd1,"w")
80     f1.write(parse1)
81     f1.close()
82
83     (fd2,p2n)=mkstemp()
84     f2=os.fdopen(fd2,"w")
85     f2.write(parse2)
86     f2.close()
87
88     (fd3,p3n)=mkstemp()
89     f3=os.fdopen(fd3,"w")
90     f3.write(parse3)
91     f3.close()
92
93     # awk parsing of hxx files - 
94     # result written in file parse_type_result
95     cmd2 = [
96         "cat %s" % hxxfile,
97         "awk -f %s" % p01n,
98         "sed 's/virtual //g'",
99         "sed 's/MEDMEM_EXPORT//g'",
100         "sed 's/throw.*;/;/g'",
101         "awk -f %s" % p1n,
102         "awk -f %s" % p2n,
103         "awk -v class_name=%s -f %s" % (class_name, p3n) ]
104     cmd2 = ' | '.join(cmd2)
105
106     os.system(cmd2)
107     os.remove(p01n)
108     os.remove(p1n)
109     os.remove(p2n)
110     os.remove(p3n)
111
112     # Retrieve the information which was generated in 
113     # the file parse_type_result.
114     # The structure of the file is :
115     #
116     #   Function  return_type   function_name
117     #   [arg1_type  arg1_name]
118     #   [arg2_type  arg2_name]
119     #   ...
120     # The service names are stored in list_of_services
121     # The information relative to a service (called service_name) is stored in
122     # the dictionnary service_definition[service_name]
123     list_of_services=[]
124     service_definition={}
125     result_parsing=open("parse_type_result","r")
126     for line in result_parsing.readlines():
127         line=line[0:-1] # get rid of trailing \n
128         words = string.split(line,';')
129
130         if len(words) >=3 and words[0] == "Function": # detect a new service
131             function_name=words[2]
132             # store the name of new service
133             list_of_services.append(function_name) 
134             # create a dict to store informations relative to this service
135             service_definition[function_name]={} 
136             service_definition[function_name]["ret"]=words[1]  # return type
137             service_definition[function_name]["inports"]=[]
138             service_definition[function_name]["outports"]=[]
139             service_definition[function_name]["ports"]=[]
140             service_definition[function_name]["impl"]=[]
141
142         # an argument type and argument name of the current service
143         if len(words) == 2:  
144             current_service=list_of_services[-1]
145             current_service_dict=service_definition[current_service]
146             typename=words[0]
147             argname=words[1]
148             # store in c++ order the arg names
149             current_service_dict["ports"].append( (argname,typename) ) 
150
151             # separate in from out parameters
152             inout=cpp2idl_mapping[typename][0:2]
153             assert inout=="in" or inout=="ou",'Error in table cpp2idl_mapping'
154             if inout == "in":
155                 current_service_dict["inports"].append((argname, typename) )
156             else:
157                 current_service_dict["outports"].append((argname, typename) )
158     #
159     # For each service : 
160     #  - generate implementation of c++ servant
161     #  - store it in service_definition[serv]["impl"]
162     for serv in list_of_services:
163         if debug:
164             print "service : ",serv
165             print "  inports  -> ",service_definition[serv]["inports"]
166             print "  outports -> ",service_definition[serv]["outports"]
167             print "  return   -> ",service_definition[serv]["ret"]
168
169
170         # Part 1 : Argument pre-processing
171         s_argument_processing="//\tArguments processing\n"
172         for (argname,argtype) in service_definition[serv]["inports"] + \
173                                  service_definition[serv]["outports"]:
174             format=cpp_impl_a[argtype]
175             s_argument_processing += format % {"arg" : argname }
176
177         # if there was no args
178         if s_argument_processing=="//\tArguments processing\n": 
179             s_argument_processing=""
180
181
182         # Part 2 : Call to the underlying c++ function
183         s_call_cpp_function="//\tCall cpp component\n\t"
184         rtn_type=service_definition[serv]["ret"]
185
186         # if return type is void, the call syntax is different
187         if rtn_type == "void" : 
188             s_call_cpp_function += "cppCompo_->%s(" % serv
189         else:
190             s_call_cpp_function +=\
191                 "%s _rtn_cpp = cppCompo_->%s(" % (rtn_type ,serv )
192
193         for (argname,argtype) in service_definition[serv]["ports"]:
194               # special treatment for some arguments
195               post=""
196               pre=""
197
198               if string.find(cpp_impl_a[argtype],"auto_ptr" ) != -1 :
199                   # for auto_ptr argument, retrieve the raw pointer behind
200                   post=".get()" 
201               if  argtype == "const MEDMEM::MESH&"  or  \
202                   argtype == "const MEDMEM::SUPPORT&" : 
203                   # we cannot create MESHClient on the stack 
204                   # (private constructor!), 
205                   # so we create it on the heap and dereference it
206                   pre="*"  
207
208               post+="," # separator between arguments
209               s_call_cpp_function += " %s_%s%s" % ( pre,argname,post)
210         if s_call_cpp_function[-1]==',':
211             # get rid of trailing comma
212             s_call_cpp_function=s_call_cpp_function[0:-1] 
213
214         s_call_cpp_function=s_call_cpp_function+');\n'
215
216         # Part 3.a : Out Argument Post-processing
217         s_argument_postprocessing="//\tPost-processing & return\n"
218         for (argname,argtype) in service_definition[serv]["outports"]:
219             format=cpp_impl_c[argtype]
220             # the treatment of %(module) is postponed in makecxx() 
221             # because we don't know here the module name
222             s_argument_postprocessing += \
223                 format % {"arg" : argname, "module" : "%(module)s" } 
224
225         # Part 3.b : In Argument Post-processing
226         for (argname,argtype) in service_definition[serv]["inports"]:
227             # not all in types require a treatment
228             if cpp_impl_c.has_key(argtype): 
229                 format=cpp_impl_c[argtype]
230                 # id : treatment of %(module) is postponed in makecxx
231                 s_argument_postprocessing += \
232                         format % {"arg" : argname, "module" : "%(module)s" } 
233
234         # Part 3.c : return processing
235         s_rtn_processing=cpp_impl_b[rtn_type]
236
237         format_end_serv = "\tendService(\"%(class_name)s_i::%(serv_name)s\");"
238         format_end_serv += "\n\tEND_OF(\"%(class_name)s_i::%(serv_name)s\");\n"
239         s_rtn_processing += format_end_serv %\
240                 { "serv_name" : serv, "class_name" : class_name }
241
242         if  rtn_type != "void":
243             s_rtn_processing += "\treturn _rtn_ior;"
244
245         service_definition[serv]["impl"] = s_argument_processing + \
246                                            s_call_cpp_function + \
247                                            s_argument_postprocessing + \
248                                            s_rtn_processing
249         if debug:
250             print "implementation :\n",service_definition[serv]["impl"]
251
252     #
253     # Create a list of Service objects (called services), 
254     # and give it to Component constructor
255     #
256     services=[]
257     self.use_medmem=False
258     self.use_medcoupling=False
259     for serv in list_of_services:
260         # for inports and outports, Service class expects a list of tuples, 
261         # each tuple containing the name and the yacs type of the port
262         # thus we need to convert c++ types to yacs types  
263         # (we use for that the cpp2yacs_mapping table)
264         inports=[]
265         for op in service_definition[serv]["inports"]:
266             inports.append([op[0], cpp2yacs_mapping[op[1]] ] )
267
268         outports = []
269         for op in service_definition[serv]["outports"]:
270             outports.append([op[0], cpp2yacs_mapping[op[1]] ] )
271
272         Return="void"
273         if service_definition[serv]["ret"] != "void":
274             Return=cpp2yacs_mapping[service_definition[serv]["ret"]]
275
276         # find out if component uses medmem types and/or medcoupling types
277         for (argname,argtype) in inports + outports + [("return",Return)]:
278             if moduleTypes[argtype]=="MED":
279                 if argtype.count("Coupling")>0:
280                     self.use_medcoupling=True
281                 else:
282                     self.use_medmem=True
283                 break
284
285         code=service_definition[serv]["impl"]
286         if debug:
287             print "service : ",serv
288             print "  inports  -> ",service_definition[serv]["inports"]
289             print "  converted inports  -> ",inports
290             print "  outports -> ",service_definition[serv]["outports"]
291             print "  converted outports  -> ",outports
292             print "  Return  -> ",service_definition[serv]["ret"]
293             print "  converted Return  -> ",Return
294
295         services.append(Service(serv, 
296            inport=inports, 
297            outport=outports,
298            ret=Return, 
299            defs="", 
300            body=code,
301            ) )
302
303     Includes="-I${"+name+"CPP_ROOT_DIR}/include"
304     Libs="-L${"+name+"CPP_ROOT_DIR}/lib -l"+name+"CXX"
305     Compodefs=""
306     Inheritedclass=""
307     self.inheritedconstructor=""
308     if self.use_medmem:
309         Compodefs="""
310 #include CORBA_CLIENT_HEADER(MED)
311 #include CORBA_CLIENT_HEADER(MED_Gen)
312 #include "FIELDClient.hxx"
313 #include "MESHClient.hxx"
314 #include "MEDMEM_Support_i.hxx"
315 #include "MEDMEM_Mesh_i.hxx"
316 #include "MEDMEM_FieldTemplate_i.hxx"
317 #include "Med_Gen_Driver_i.hxx"
318 """
319         Inheritedclass="Med_Gen_Driver_i, public SALOMEMultiComm"
320         self.inheritedconstructor="Med_Gen_Driver_i(orb),"
321
322     if self.use_medcoupling:
323         Compodefs+="""
324 #include CORBA_CLIENT_HEADER(MEDCouplingCorbaServant)
325 #include CORBA_CLIENT_HEADER(MED_Gen)
326 #include "MEDCouplingFieldDoubleServant.hxx"
327 #include "MEDCouplingUMeshServant.hxx"
328 #include "MEDCouplingFieldDouble.hxx"
329 #include "MEDCouplingUMesh.hxx"
330 #include "MEDCouplingUMeshClient.hxx"
331 #include "MEDCouplingFieldDouble.hxx"
332 #include "MEDCouplingFieldDoubleClient.hxx"
333 """
334
335     Component.__init__(self, name, services, impl="CPP", libs=Libs,
336                              rlibs="", includes=Includes, kind="lib",
337                              sources=None,inheritedclass=Inheritedclass,
338                              compodefs=Compodefs)
339
340 # ------------------------------------------------------------------------------
341   def makeCompo(self, gen):
342     """generate files for C++ component
343        return a dict where key is the file name and 
344        value is the content of the file
345     """
346     cxxfile = "%s_i.cxx" % self.name
347     hxxfile = "%s_i.hxx" % self.name
348     return {"Makefile.am":gen.makeMakefile(self.getMakefileItems(gen)),
349             cxxfile:self.makecxx(gen),
350             hxxfile:self.makehxx(gen)
351            }
352
353 # ------------------------------------------------------------------------------
354   def getMakefileItems(self,gen):
355       makefileItems={"header":"""
356 include $(top_srcdir)/adm_local/make_common_starter.am
357
358 """}
359       makefileItems["lib_LTLIBRARIES"]=["lib"+self.name+"Engine.la"]
360       makefileItems["salomeinclude_HEADERS"]=["%s_i.hxx" % self.name]
361       makefileItems["body"]=compoMakefile.substitute(module=gen.module.name,
362                                                      component=self.name,
363                                                      libs=self.libs,
364                                                      includes=self.includes)
365       return makefileItems
366
367 # ------------------------------------------------------------------------------
368   def makehxx(self, gen):
369     """return a string that is the content of .hxx file
370     """
371     services = []
372     for serv in self.services:
373       service = "    %s %s(" % (corba_rtn_type(serv.ret,gen.module.name),
374                                 serv.name)
375       service = service+gen.makeArgs(serv)+") throw (SALOME::SALOME_Exception);"
376       services.append(service)
377     servicesdef = "\n".join(services)
378
379     inheritedclass=self.inheritedclass
380     if self.inheritedclass:
381       inheritedclass= " public virtual " + self.inheritedclass + ","
382
383     return hxxCompo.substitute(component=self.name, 
384                                module=gen.module.name,
385                                servicesdef=servicesdef, 
386                                inheritedclass=inheritedclass,
387                                compodefs=self.compodefs)
388
389 # ------------------------------------------------------------------------------
390   def makecxx(self, gen, exe=0):
391     """return a string that is the content of .cxx file
392     """
393     services = []
394     inits = []
395     defs = []
396     for serv in self.services:
397       defs.append(serv.defs)
398       print "CNC bug : ",serv.body
399       service = cxxService.substitute(
400                            component=self.name, 
401                            service=serv.name,
402                            ret=corba_rtn_type(serv.ret,gen.module.name),
403                            parameters=gen.makeArgs(serv),
404                            body=serv.body % {"module":gen.module.name+"_ORB"} )
405       services.append(service)
406     return cxxCompo.substitute(component=self.name, 
407                                inheritedconstructor=self.inheritedconstructor,
408                                servicesdef="\n".join(defs),
409                                servicesimpl="\n".join(services))
410
411 # ------------------------------------------------------------------------------
412   def getGUIfilesTemplate(self):
413       """generate in a temporary directory files for a generic GUI, 
414          and return a list with file names.
415          it is the responsability of the user to get rid 
416          of the temporary directory when finished
417       """
418       gui_cxx=hxxgui_cxx.substitute(component_name=self.name)
419       gui_h=hxxgui_h.substitute(component_name=self.name)
420       gui_icon_ts=hxxgui_icon_ts.substitute(component_name=self.name)
421       gui_message_en=hxxgui_message_en.substitute(component_name=self.name)
422       gui_message_fr=hxxgui_message_fr.substitute(component_name=self.name)
423       gui_config=hxxgui_config.substitute(component_name=self.name)
424       gui_xml_fr=hxxgui_xml_fr.substitute(component_name=self.name)
425       gui_xml_en=hxxgui_xml_en.substitute(component_name=self.name)
426       temp_dir=mkdtemp()
427       gui_cxx_file_name=os.path.join(temp_dir,self.name+"GUI.cxx")
428       gui_h_file_name=os.path.join(temp_dir,self.name+"GUI.h")
429       gui_icon_ts_file_name=os.path.join(temp_dir,self.name+"_icons.ts")
430       gui_message_en_file_name=os.path.join(temp_dir,self.name+"_msg_en.ts")
431       gui_message_fr_file_name=os.path.join(temp_dir,self.name+"_msg_fr.ts")
432       gui_config_file_name=os.path.join(temp_dir,"config")
433       gui_xml_fr_file_name=os.path.join(temp_dir,self.name+"_en.xml")
434       gui_xml_en_file_name=os.path.join(temp_dir,self.name+"_fr.xml")
435
436       list_of_gui_names=[]
437
438       gui_cxx_file=open(gui_cxx_file_name,"w")
439       gui_cxx_file.write(gui_cxx)
440       gui_cxx_file.close()
441       list_of_gui_names.append(gui_cxx_file_name)
442
443       gui_h_file=open(gui_h_file_name,"w")
444       gui_h_file.write(gui_h)
445       gui_h_file.close()
446       list_of_gui_names.append(gui_h_file_name)
447
448       gui_icon_ts_file=open(gui_icon_ts_file_name,"w")
449       gui_icon_ts_file.write(gui_icon_ts)
450       gui_icon_ts_file.close()
451       list_of_gui_names.append(gui_icon_ts_file_name)
452
453       gui_message_en_file=open(gui_message_en_file_name,"w")
454       gui_message_en_file.write(gui_message_en)
455       gui_message_en_file.close()
456       list_of_gui_names.append(gui_message_en_file_name)
457
458       gui_message_fr_file=open(gui_message_fr_file_name,"w")
459       gui_message_fr_file.write(gui_message_fr)
460       gui_message_fr_file.close()
461       list_of_gui_names.append(gui_message_fr_file_name)
462
463       gui_config_file=open(gui_config_file_name,"w")
464       gui_config_file.write(gui_config)
465       gui_config_file.close()
466       list_of_gui_names.append(gui_config_file_name)
467
468       gui_xml_fr_file=open(gui_xml_fr_file_name,"w")
469       gui_xml_fr_file.write(gui_xml_fr)
470       gui_xml_fr_file.close()
471       list_of_gui_names.append(gui_xml_fr_file_name)
472
473       gui_xml_en_file=open(gui_xml_en_file_name,"w")
474       gui_xml_en_file.write(gui_xml_en)
475       gui_xml_en_file.close()
476       list_of_gui_names.append(gui_xml_en_file_name)
477
478
479       return list_of_gui_names