1 # Copyright (C) 2009-2016 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
21 (its header and its shares library)
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
44 # ------------------------------------------------------------------------------
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):
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))
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]
64 # grab name of c++ component
65 cmd1="""awk '$1 == "class" && $0 !~ /;/ {print $2}' """ + hxxfile +\
66 """|awk -F: '{printf "%s",$1}' """
68 class_name=f.readlines()[0]
70 print("classname=",class_name)
73 # create temporary awk files for the parsing
75 f01=os.fdopen(fd01,"w")
94 # awk parsing of hxx files -
95 # result written in file parse_type_result
99 "sed 's/virtual //g'",
100 "sed 's/MEDMEM_EXPORT//g'",
101 "sed 's/throw.*;/;/g'",
104 "awk -v class_name=%s -f %s" % (class_name, p3n) ]
105 cmd2 = ' | '.join(cmd2)
108 import subprocess, sys
109 subprocess.call(cmd2, shell=True, stdout=sys.stdout, stderr=subprocess.STDOUT)
115 # Retrieve the information which was generated in
116 # the file parse_type_result.
117 # The structure of the file is :
119 # Function return_type function_name
120 # [arg1_type arg1_name]
121 # [arg2_type arg2_name]
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]
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(';')
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"]=[]
145 # an argument type and argument name of the current service
147 current_service=list_of_services[-1]
148 current_service_dict=service_definition[current_service]
151 # store in c++ order the arg names
152 current_service_dict["ports"].append( (argname,typename) )
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'
158 current_service_dict["inports"].append((argname, typename) )
160 current_service_dict["outports"].append((argname, typename) )
163 # - generate implementation of c++ servant
164 # - store it in service_definition[serv]["impl"]
165 for serv in list_of_services:
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"])
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 }
180 # if there was no args
181 if s_argument_processing=="//\tArguments processing\n":
182 s_argument_processing=""
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"]
189 # if return type is void, the call syntax is different
190 if rtn_type == "void" :
191 s_call_cpp_function += "cppCompo_->%s(" % serv
193 s_call_cpp_function +=\
194 "%s _rtn_cpp = cppCompo_->%s(" % (rtn_type ,serv )
196 for (argname,argtype) in service_definition[serv]["ports"]:
197 # special treatment for some arguments
201 if cpp_impl_a[argtype].find("auto_ptr" ) != -1 :
202 # for auto_ptr argument, retrieve the raw pointer behind
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
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]
217 s_call_cpp_function=s_call_cpp_function+');\n'
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" }
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" }
237 # Part 3.c : return processing
238 s_rtn_processing=cpp_impl_b[rtn_type]
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 }
245 if rtn_type != "void":
246 s_rtn_processing += "\treturn _rtn_ior;"
248 service_definition[serv]["impl"] = s_argument_processing + \
249 s_call_cpp_function + \
250 s_argument_postprocessing + \
253 print("implementation :\n",service_definition[serv]["impl"])
256 # Create a list of Service objects (called services),
257 # and give it to Component constructor
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)
268 for op in service_definition[serv]["inports"]:
269 inports.append([op[0], cpp2yacs_mapping[op[1]] ] )
272 for op in service_definition[serv]["outports"]:
273 outports.append([op[0], cpp2yacs_mapping[op[1]] ] )
276 if service_definition[serv]["ret"] != "void":
277 Return=cpp2yacs_mapping[service_definition[serv]["ret"]]
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
288 code=service_definition[serv]["impl"]
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)
298 services.append(Service(serv,
306 Includes = os.path.join(cpp_path, "include")
307 Libs = [ Library( name=name+"CXX", path=os.path.join(cpp_path, "lib"))]
310 self.inheritedconstructor=""
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"
322 Inheritedclass="Med_Gen_Driver_i, public SALOMEMultiComm"
323 self.inheritedconstructor="Med_Gen_Driver_i(orb),"
325 if self.use_medcoupling:
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"
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)
345 # -----------------------------------------------------------------------------
346 def libraryName(self):
347 """ Name of the target library
349 return self.name + "Engine"
351 # ------------------------------------------------------------------------------
352 def targetProperties(self):
353 """ define the rpath property of the target using self.rlibs
355 string containing the commands to add to cmake
358 if self.rlibs.strip() :
359 text="SET_TARGET_PROPERTIES( %sEngine PROPERTIES INSTALL_RPATH %s)\n" % (self.name, self.rlibs)
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
368 cxxfile = "%s_i.cxx" % self.name
369 hxxfile = "%s_i.hxx" % self.name
370 (cmake_text, cmake_vars) = self.additionalLibraries()
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,
378 find_libs = cmake_text,
379 target_properties = self.targetProperties()
382 return {"CMakeLists.txt":cmakelist_content,
383 cxxfile:self.makecxx(gen),
384 hxxfile:self.makehxx(gen)
387 # ------------------------------------------------------------------------------
388 # def getMakefileItems(self,gen):
389 # makefileItems={"header":"""
390 #include $(top_srcdir)/adm_local/make_common_starter.am
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,
398 # includes=self.includes)
399 # return makefileItems
401 # ------------------------------------------------------------------------------
402 def makehxx(self, gen):
403 """return a string that is the content of .hxx file
406 for serv in self.services:
407 service = " %s %s(" % (corba_rtn_type(serv.ret,gen.module.name),
409 service = service+gen.makeArgs(serv)+") throw (SALOME::SALOME_Exception);"
410 services.append(service)
411 servicesdef = "\n".join(services)
413 inheritedclass=self.inheritedclass
414 if self.inheritedclass:
415 inheritedclass= " public virtual " + self.inheritedclass + ","
417 return hxxCompo.substitute(component=self.name,
418 module=gen.module.name,
419 servicesdef=servicesdef,
420 inheritedclass=inheritedclass,
421 compodefs=self.compodefs)
423 # ------------------------------------------------------------------------------
424 def makecxx(self, gen, exe=0):
425 """return a string that is the content of .cxx file
430 for serv in self.services:
431 defs.append(serv.defs)
432 print("CNC bug : ",serv.body)
433 service = cxxService.substitute(
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))
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
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(),
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(),
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)
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")
484 gui_cxx_file=open(gui_cxx_file_name,"w")
485 gui_cxx_file.write(gui_cxx)
487 list_of_gui_names.append(gui_cxx_file_name)
489 gui_h_file=open(gui_h_file_name,"w")
490 gui_h_file.write(gui_h)
492 list_of_gui_names.append(gui_h_file_name)
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)
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)
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)
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)
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)
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)
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
530 def getIdlInterfaces(self):
531 services = self.getIdlServices()
532 from .hxx_tmpl import interfaceidlhxx
534 if self.use_medmem==True:
535 Inherited="Engines::EngineComponent,SALOME::MultiCommClass,SALOME_MED::MED_Gen_Driver"
537 Inherited="Engines::EngineComponent"
538 return interfaceidlhxx.substitute(component=self.name,inherited=Inherited, services="\n".join(services))