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