Salome HOME
updated copyright message
[tools/yacsgen.git] / module_generator / cppcompo.py
1 # Copyright (C) 2009-2023  EDF
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 """
21   Module that defines CPPComponent for SALOME components implemented in C++
22 """
23
24 import os
25 from module_generator.gener import Component, Invalid
26 from module_generator.cpp_tmpl import initService, cxxService, hxxCompo, cxxCompo
27 from module_generator.cpp_tmpl import exeCPP, cmake_src_compo_cpp
28 from module_generator.yacstypes import corba_rtn_type
29
30 try:
31   from string import Template
32 except:
33   from module_generator.compat import Template,set
34
35 class CPPComponent(Component):
36   """
37    A :class:`CPPComponent` instance represents a C++ SALOME component with services given as a list of :class:`Service`
38    instances with the parameter *services*.
39
40    :param name: gives the name of the component.
41    :type name: str
42    :param services: the list of services (:class:`Service`) of the component.
43    :param kind: If it is given and has the value "exe", the component will be built as a standalone
44       component (executable or shell script). The default is to build the component as a dynamic library.
45    :param libs: list of the additional libraries. see *Library* class.
46       If you want to add "libmylib.so", installed in "/path/to/lib" you should use:
47          libs=[Library(name="mylib", path="/path/to/lib")]
48       For more advanced features, see the documentation of cmake / FIND_LIBRARY
49    :param rlibs: space-separated list specifying the rpath to use in installed targets
50    :param includes: additional include directories, separated by spaces.
51    :param sources: gives all the external source files to add in the compilation step (list of paths).
52    :param exe_path: is only used when kind is "exe" and gives the path to the standalone component.
53    :param compodefs: can be used to add extra definition code in the component for example when using a base class
54       to define the component class by deriving it (see *inheritedclass* parameter)
55    :param inheritedclass: can be used to define a base class for the component. The base class can be defined in external
56       source or with the *compodefs* parameter. The value of the *inheritedclass* parameter is the name of the base class.
57    :param idls: can be used to add extra idl CORBA interfaces to the component. This parameter must gives a list of idl file
58       names that are added into the generated module (idl directory) and compiled with the generated idl of the module.
59    :param interfacedefs: can be used to add idl definitions (or includes of idl files) into the generated idl of the module.
60    :param inheritedinterface: can be used to make the component inherit an extra idl interface that has been included through
61       the *idls* and *interfacedefs* parameters. See the cppgui1 example for how to use these last parameters.
62    :param addedmethods: is a C++ specific parameter that can be used to redefine a component method (DumpPython for example). This
63       parameter is a string that must contain the definition and implementation code of the method. See the cppgui1 example
64       for how to use it.
65    :param calciumextendedinterface: if you want to use the Calcium extended interface for C++ as defined by the header CalciumInterface.hxx
66       set this parameter to 1. By default its value is 0 so to not use extended interface. The extended interface requires boost as a dependency.
67
68    For example, the following call defines a standalone component named "mycompo" with one service s1 (it must have been defined before)::
69
70       >>> c1 = module_generator.CPPComponent('mycompo', services=[s1,], kind="exe",
71                                              exe_path="./launch.sh")
72   """
73   def __init__(self, name, services=None, libs=[], rlibs="", includes="", kind="lib",
74                      exe_path=None, sources=None, inheritedclass="", compodefs="",
75                      idls=None,interfacedefs="",inheritedinterface="",addedmethods="",
76                      calciumextendedinterface=0):
77     self.exe_path = exe_path
78     self.calciumextendedinterface=calciumextendedinterface
79     Component.__init__(self, name, services, impl="CPP", libs=libs, rlibs=rlibs,
80                              includes=includes, kind=kind, sources=sources,
81                              inheritedclass=inheritedclass, compodefs=compodefs, idls=idls,
82                              interfacedefs=interfacedefs, inheritedinterface=inheritedinterface,
83                              addedmethods=addedmethods)
84
85   def validate(self):
86     """ validate component definition parameters"""
87     Component.validate(self)
88     kinds = ("lib", "exe")
89     if self.kind not in kinds:
90       raise Invalid("kind must be one of %s for component %s" % (kinds,self.name))
91
92     if self.kind == "exe" :
93       if not self.exe_path:
94         raise Invalid("exe_path must be defined for component %s" % self.name)
95
96   def targetProperties(self):
97     """ define the rpath property of the target using self.rlibs
98     return
99       string containing the commands to add to cmake
100     """
101     text=""
102     if self.rlibs.strip() :
103       text="SET_TARGET_PROPERTIES( %sEngine PROPERTIES INSTALL_RPATH %s)\n" % (self.name, self.rlibs)
104     return text
105
106   def libraryName(self):
107     """ Name of the target library
108     """
109     ret=""
110     if self.kind == "lib":
111       ret = self.name + "Engine"
112     elif self.kind == "exe":
113       ret = self.name + "Exelib"
114     else:
115       raise Invalid("Invalid kind of component: %s. Supported kinds are 'lib' and 'exe'" % self.name)
116     return ret
117     
118   def makeCompo(self, gen):
119     """generate files for C++ component
120
121        return a dict where key is the file name and value is the file content
122     """
123     (cmake_text, cmake_vars) = self.additionalLibraries()
124     # DSC_libs are needed for datastream ports only
125     DSC_libs = """${KERNEL_SalomeDSCContainer}
126   ${KERNEL_SalomeDSCSuperv}
127   ${KERNEL_SalomeDatastream}
128   ${KERNEL_SalomeDSCSupervBasic}
129   ${KERNEL_CalciumC}
130   """
131     cmake_vars = DSC_libs + cmake_vars
132     cxxfile = "%s.cxx" % self.name
133     hxxfile = "%s.hxx" % self.name
134     if self.kind == "exe":
135       exe_opt = 1
136     else:
137       exe_opt = 0
138     ret = { cxxfile:self.makecxx(gen, exe_opt),
139             hxxfile:self.makehxx(gen)
140           }
141     sources = " ".join(map(os.path.basename,self.sources))
142     cmakelist_content = cmake_src_compo_cpp.substitute(
143                         module = gen.module.name,
144                         component = self.name,
145                         componentlib = self.libraryName(),
146                         includes = self.includes,
147                         sources = sources,
148                         libs = cmake_vars,
149                         find_libs = cmake_text,
150                         target_properties = self.targetProperties())
151     if self.kind == "exe":
152       exe_file = self.name+".exe"
153       install_commande = "\nINSTALL(PROGRAMS %s DESTINATION ${SALOME_INSTALL_BINS})\n" % exe_file
154       cmakelist_content = cmakelist_content + install_commande
155       ret[exe_file] = exeCPP.substitute(compoexe=self.exe_path)
156     pass
157     
158     ret["CMakeLists.txt"] = cmakelist_content
159     
160     return ret
161
162   def makehxx(self, gen):
163     """return a string that is the content of .hxx file
164     """
165     services = []
166     for serv in self.services:
167       service = "    %s %s(" % (corba_rtn_type(serv.ret,gen.module.name),serv.name)
168       service = service+gen.makeArgs(serv)+");"
169       services.append(service)
170
171     if self.addedmethods:
172       services.append(self.addedmethods)
173     servicesdef = "\n".join(services)
174
175     inheritedclass=self.inheritedclass
176     if self.inheritedclass:
177       inheritedclass= " public virtual " + self.inheritedclass + ","
178
179     return hxxCompo.substitute(component=self.name, module=gen.module.name,
180                                servicesdef=servicesdef, inheritedclass=inheritedclass,
181                                compodefs=self.compodefs)
182
183   def makecxx(self, gen, exe=0):
184     """return a string that is the content of .cxx file
185     """
186     services = []
187     inits = []
188     defs = []
189     for serv in self.services:
190       defs.append(serv.defs)
191       service = cxxService.substitute(component=self.name, service=serv.name,
192                                       parameters=gen.makeArgs(serv),
193                                       body=serv.body, exe=exe)
194       streams = []
195       for name, typ, dep in serv.instream:
196         streams.append('          create_calcium_port(this,(char *)"%s",(char *)"%s",(char *)"IN",(char *)"%s");'% (name, typ, dep))
197       instream = "\n".join(streams)
198       streams = []
199       for name, typ, dep in serv.outstream:
200         streams.append('          create_calcium_port(this,(char *)"%s",(char *)"%s",(char *)"OUT",(char *)"%s");'% (name, typ, dep))
201       outstream = "\n".join(streams)
202
203       init = initService.substitute(component=self.name, service=serv.name,
204                                     instream=instream, outstream=outstream)
205       services.append(service)
206       inits.append(init)
207
208     CalciumInterface=""
209     if self.calciumextendedinterface:
210       CalciumInterface="#include <CalciumInterface.hxx>"
211
212     return cxxCompo.substitute(component=self.name, module=gen.module.name,
213                                exe=exe, exe_path=self.exe_path,
214                                servicesdef="\n".join(defs),
215                                servicesimpl="\n".join(services),
216                                initservice='\n'.join(inits),
217                                CalciumInterface=CalciumInterface)
218