Salome HOME
0023227: [CEA 1718] Correction of the script hxx2salome.py in YACSGEN
[tools/yacsgen.git] / module_generator / gener.py
1 # Copyright (C) 2009-2015  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 import os, shutil, glob, socket
21 import traceback
22 import warnings
23
24 try:
25   from string import Template
26 except:
27   from compat import Template, set
28
29 class Invalid(Exception):
30   pass
31
32 debug=0
33
34 from mod_tmpl import *
35 from cata_tmpl import catalog, interface, idl, parallel_interface
36 from cata_tmpl import xml, xml_interface, xml_service
37 from cata_tmpl import idlMakefilePaCO_BUILT_SOURCES, idlMakefilePaCO_nodist_salomeinclude_HEADERS
38 from cata_tmpl import idlMakefilePACO_salomepython_DATA, idlMakefilePACO_salomeidl_DATA
39 from cata_tmpl import idlMakefilePACO_INCLUDES
40 from cata_tmpl import cataOutStream, cataInStream, cataOutparam, cataInparam
41 from cata_tmpl import cataOutParallelStream, cataInParallelStream
42 from cata_tmpl import cataService, cataCompo
43 #from aster_tmpl import check_aster
44 from salomemodules import salome_modules
45 from yacstypes import corbaTypes, corbaOutTypes, moduleTypes, idlTypes, corba_in_type, corba_out_type
46 from yacstypes import ValidTypes, PyValidTypes, calciumTypes, DatastreamParallelTypes
47 from yacstypes import ValidImpl, ValidImplTypes, ValidStreamTypes, ValidParallelStreamTypes, ValidDependencies
48 from gui_tmpl import cmake_py_gui, pysalomeapp, cmake_cpp_gui, cppsalomeapp
49 from doc_tmpl import docmakefile, docconf, docsalomeapp
50 import yacsgen_version
51
52 def makedirs(namedir):
53   """Create a new directory named namedir. If a directory already exists copy it to namedir.bak"""
54   if os.path.exists(namedir):
55     dirbak = namedir+".bak"
56     if os.path.exists(dirbak):
57       shutil.rmtree(dirbak)
58     os.rename(namedir, dirbak)
59     os.listdir(dirbak) #needed to update filesystem on special machines (cluster with NFS, for example)
60   os.makedirs(namedir)
61
62 class Module(object):
63   """
64    A :class:`Module` instance represents a SALOME module that contains components given as a list of
65    component instances (:class:`CPPComponent` or :class:`PYComponent` or :class:`F77Component` or :class:`ASTERComponent`)
66    with the parameter *components*.
67
68    :param name: gives the name of the module. The SALOME source module
69       will be located in the <name_SRC> directory.
70    :type name: str
71    :param components: gives the list of components of the module.
72    :param prefix: is the path of the installation directory.
73    :param doc: can be used to add an online documentation to the module. It must be a list of file names (sources, images, ...) that will be
74       used to build a sphinx documentation (see http://sphinx.pocoo.org, for more information). If not given, the Makefile.am
75       and the conf.py (sphinx configuration) files are generated. In this case, the file name extension of source files must be .rst.
76       See small examples in Examples/pygui1 and Examples/cppgui1.
77    :param gui: can be used to add a GUI to the module. It must be a list of file names (sources, images, qt designer files, ...).
78       If not given, the CMakeLists.txt and SalomeApp.xml are generated. All image files are put in the resources directory of the module.
79       The GUI can be implemented in C++ (file name extension '.cxx') or in Python (file name extension '.py').
80       See small examples in Examples/pygui1 and Examples/cppgui1.
81
82    For example, the following call defines a module named "mymodule" with 2 components c1 and c2  (they must have been
83    defined before) that will be installed in the "install" directory::
84
85       >>> m = module_generator.Module('mymodule', components=[c1,c2],
86                                                   prefix="./install")
87
88   """
89   def __init__(self, name, components=None, prefix="", doc=None, gui=None):
90     self.name = name
91     self.components = components or []
92     self.prefix = prefix or "%s_INSTALL" % name
93     self.doc = doc
94     self.gui = gui
95     try:
96       self.validate()
97     except Invalid,e:
98       if debug:
99         traceback.print_exc()
100       print "Error in module %s: %s" % (name,e)
101       raise SystemExit
102
103   def validate(self):
104     # Test Module name, canot have a "-" in the name
105     if self.name.find("-") != -1:
106       raise Invalid("Module name %s is not valid, remove character - in the module name" % self.name)
107     lcompo = set()
108     for compo in self.components:
109       if compo.name in lcompo:
110         raise Invalid("%s is already defined as a component of the module" % compo.name)
111       lcompo.add(compo.name)
112       compo.validate()
113
114 class Library(object):
115   """
116      A :class:'Library' instance contains the informations of a user library.
117      
118      :param name: name of the library (exemple: "cppunit", "calcul")
119      :param path: path where to find the library (exemple: "/home/user/libs")
120   """
121   
122   def __init__(self, name, path):
123     self.name=name
124     self.path=path
125
126   def findLibrary(self):
127     """
128     return : text for the FIND_LIBRARY command for cmake.
129     Feel free to overload this function for your own needs.
130     """
131     return "FIND_LIBRARY( "+self.cmakeVarName()+" "+self.name+" PATH "+self.path + ")\n"
132     
133   def cmakeVarName(self):
134     """
135     return : name of the cmake variable used by FIND_LIBRARY
136     """
137     return "_userlib_" + self.name.split()[0]
138
139 class Component(object):
140   def __init__(self, name, services=None, impl="PY", libs=[], rlibs="",
141                      includes="", kind="lib", sources=None,
142                      inheritedclass="",compodefs="",
143                      idls=None,interfacedefs="",inheritedinterface="",addedmethods=""):
144     self.name = name
145     self.impl = impl
146     self.kind = kind
147     self.services = services or []
148     self.libs = libs
149     self.rlibs = rlibs
150     self.includes = includes
151     self.sources = sources or []
152     self.inheritedclass=inheritedclass
153     self.compodefs=compodefs
154     self.idls=idls
155     self.interfacedefs=interfacedefs
156     self.inheritedinterface=inheritedinterface
157     self.addedmethods=addedmethods
158
159   def additionalLibraries(self):
160     """ generate the cmake code for finding the additional libraries
161     return
162       string containing a list of "find_library"
163       string containing a list of cmake variables defined
164     """
165     cmake_text=""
166     cmake_vars=""
167     
168     for lib in self.libs:
169       cmake_text = cmake_text + lib.findLibrary()
170       cmake_vars = cmake_vars + "${" + lib.cmakeVarName() + "}\n  "
171     
172     var_template = Template("$${${name}_SalomeIDL${name}}")
173     for mod in self.depend_modules:
174       if salome_modules[mod]["linklibs"]:
175         cmake_vars = cmake_vars + salome_modules[mod]["linklibs"]
176       else:
177         default_lib = var_template.substitute(name=mod)
178         print "Unknown libraries for module " + mod
179         print "Using default library name " + default_lib
180         cmake_vars = cmake_vars + default_lib + "\n  "
181     
182     return cmake_text, cmake_vars
183
184   def validate(self):
185     if self.impl not in ValidImpl:
186       raise Invalid("%s is not a valid implementation. It should be one of %s" % (self.impl, ValidImpl))
187
188     lnames = set()
189     for serv in self.services:
190       serv.impl = self.impl
191       if serv.name in lnames:
192         raise Invalid("%s is already defined as a service of the module" % serv.name)
193       lnames.add(serv.name)
194       serv.validate()
195
196     for src in self.sources:
197       if not os.path.exists(src):
198         raise Invalid("Source file %s does not exist" % src)
199
200   def getImpl(self):
201     return "SO", ""
202
203   def getMakefileItems(self,gen):
204     return {}
205
206   def setPrerequisites(self, prerequisites_file):
207     self.prerequisites = prerequisites_file
208
209 class Service(object):
210   """
211    A :class:`Service` instance represents a component service with dataflow and datastream ports.
212
213    :param name: gives the name of the service.
214    :type name: str
215    :param inport: gives the list of input dataflow ports.
216    :param outport: gives the list of output dataflow ports. An input or output dataflow port is defined
217       by a 2-tuple (port name, data type name). The list of supported basic data types is: "double", "long", "string",
218       "dblevec", "stringvec", "intvec", "file" and "pyobj" only for Python services. Depending on the implementation
219       language, it is also possible to use some types from SALOME modules (see :ref:`yacstypes`).
220    :param ret: gives the type of the return parameter
221    :param instream: gives the list of input datastream ports.
222    :param outstream: gives the list of output datastream ports. An input or output datastream port is defined
223       by a 3-tuple (port name, data type name, mode name). The list of possible data types is: "CALCIUM_double", "CALCIUM_integer",
224       "CALCIUM_real", "CALCIUM_string", "CALCIUM_complex", "CALCIUM_logical", "CALCIUM_long". The mode can be "I" (iterative mode)
225       or "T" (temporal mode).
226    :param defs: gives the source code to insert in the definition section of the component. It can be C++ includes
227       or Python imports
228    :type defs: str
229    :param body: gives the source code to insert in the service call. It can be any C++
230       or Python code that fits well in the body of the service method.
231    :type body: str
232
233    For example, the following call defines a minimal Python service with one input dataflow port (name "a", type double)
234    and one input datastream port::
235
236       >>> s1 = module_generator.Service('myservice', inport=[("a","double"),],
237                                         instream=[("aa","CALCIUM_double","I")],
238                                         body="print a")
239
240
241   """
242   def __init__(self, name, inport=None, outport=None, ret="void", instream=None, outstream=None,
243                      parallel_instream=None, parallel_outstream=None, defs="", body="", impl_type="sequential"):
244     self.name = name
245     self.inport = inport or []
246     self.outport = outport or []
247     self.ret = ret
248     self.instream = instream or []
249     self.outstream = outstream or []
250     self.parallel_instream = parallel_instream or []
251     self.parallel_outstream = parallel_outstream or []
252     self.defs = defs
253     self.body = body
254     self.impl = ""
255     self.impl_type = impl_type
256
257   def validate(self):
258     lports = set()
259     for port in self.inport:
260       name, typ = self.validatePort(port)
261       if name in lports:
262         raise Invalid("%s is already defined as a service parameter" % name)
263       lports.add(name)
264
265     for port in self.outport:
266       name, typ = self.validatePort(port)
267       if name in lports:
268         raise Invalid("%s is already defined as a service parameter" % name)
269       lports.add(name)
270
271     lports = set()
272     for port in self.instream:
273       name, typ, dep = self.validateStream(port)
274       if name in lports:
275         raise Invalid("%s is already defined as a stream port" % name)
276       lports.add(name)
277
278     for port in self.outstream:
279       name, typ, dep = self.validateStream(port)
280       if name in lports:
281         raise Invalid("%s is already defined as a stream port" % name)
282       lports.add(name)
283
284     for port in self.parallel_instream:
285       name, typ = self.validateParallelStream(port)
286       if name in lports:
287         raise Invalid("%s is already defined as a stream port" % name)
288       lports.add(name)
289
290     for port in self.parallel_outstream:
291       name, typ = self.validateParallelStream(port)
292       if name in lports:
293         raise Invalid("%s is already defined as a stream port" % name)
294       lports.add(name)
295
296     self.validateImplType()
297
298   def validatePort(self, port):
299     try:
300       name, typ = port
301     except:
302       raise Invalid("%s is not a valid definition of an data port (name,type)" % (port,))
303
304     if self.impl in ("PY", "ASTER"):
305       validtypes = PyValidTypes
306     else:
307       validtypes = ValidTypes
308
309     if typ not in validtypes:
310       raise Invalid("%s is not a valid type. It should be one of %s" % (typ, validtypes))
311     return name, typ
312
313   def validateImplType(self):
314     if self.impl_type not in ValidImplTypes:
315       raise Invalid("%s is not a valid impl type. It should be one of %s" % (self.impl_type, ValidImplTypes))
316
317   def validateStream(self, port):
318     try:
319       name, typ, dep = port
320     except:
321       raise Invalid("%s is not a valid definition of a stream port (name,type,dependency)" % (port,))
322     if typ not in ValidStreamTypes:
323       raise Invalid("%s is not a valid type. It should be one of %s" % (typ, ValidStreamTypes))
324     if dep not in ValidDependencies:
325       raise Invalid("%s is not a valid dependency. It should be one of %s" % (dep, ValidDependencies))
326     return name, typ, dep
327
328   def validateParallelStream(self, port):
329     try:
330       name, typ = port
331     except:
332       raise Invalid("%s is not a valid definition of a parallel stream port (name,type)" % (port,))
333     if typ not in ValidParallelStreamTypes:
334       raise Invalid("%s is not a valid type. It should be one of %s" % (typ, ValidParallelStreamTypes))
335     return name, typ
336
337 class Generator(object):
338   """
339    A :class:`Generator` instance take a :class:`Module` instance as its first parameter and can be used to generate the
340    SALOME source module, builds it, installs it and includes it in a SALOME application.
341
342    :param module: gives the :class:`Module` instance that will be used for the generation.
343    :param context: If given , its content is used to specify the prerequisites
344       environment file (key *"prerequisites"*) and the SALOME KERNEL installation directory (key *"kernel"*).
345    :type context: dict
346
347    For example, the following call creates a generator for the module m::
348
349       >>> g = module_generator.Generator(m,context)
350   """
351   def __init__(self, module, context=None):
352     self.module = module
353     self.context = context or {}
354     self.kernel = self.context["kernel"]
355     self.gui = self.context.get("gui")
356     self.makeflags = self.context.get("makeflags")
357     self.aster = ""
358     if self.module.gui and not self.gui:
359       raise Invalid("To generate a module with GUI, you need to set the 'gui' parameter in the context dictionnary")
360     for component in self.module.components:
361       component.setPrerequisites(self.context.get("prerequisites"))
362
363   def sourceDir(self):
364     """ get the name of the source directory"""
365     return self.module.name+"_SRC"
366
367   def generate(self):
368     """Generate a SALOME source module"""
369     module = self.module
370     namedir = self.sourceDir()
371     force = self.context.get("force")
372     update = self.context.get("update")
373     paco = self.context.get("paco")
374     if os.path.exists(namedir):
375       if force:
376         shutil.rmtree(namedir)
377       elif not update:
378         raise Invalid("The directory %s already exists" % namedir)
379     if update:
380       makedirs(namedir)
381     else:
382       os.makedirs(namedir)
383
384     srcs = {}
385
386     #get the list of SALOME modules used and put it in used_modules attribute
387     def get_dependent_modules(mod,modules):
388       modules[mod]=1
389       if not salome_modules[mod].has_key("depends"):return
390       for m in salome_modules[mod]["depends"]:
391         if modules.has_key(m):continue
392         get_dependent_modules(m,modules)
393
394     modules = {}
395     for compo in module.components:
396       compo.depend_modules = set()
397       for serv in compo.services:
398         for name, typ in serv.inport + serv.outport + [ ("return",serv.ret) ] :
399           mod = moduleTypes[typ]
400           if mod:
401             get_dependent_modules(mod,modules)
402             compo.depend_modules.add(mod)
403
404     self.used_modules = modules.keys()
405
406     for compo in module.components:
407       #for components files
408       fdict=compo.makeCompo(self)
409       srcs[compo.name] = fdict
410
411     cmakecontent = ""
412     components_string = "".join(map(lambda x: x.name+" ", module.components))
413
414     if self.module.gui:
415       GUIname=module.name+"GUI"
416       fdict=self.makeGui(namedir)
417       srcs[GUIname] = fdict
418       components_string = components_string + "\n  " + GUIname
419       
420     cmakecontent = cmake_src.substitute(components=components_string)
421     srcs["CMakeLists.txt"] = cmakecontent
422
423     docsubdir=""
424     if module.doc:
425       docsubdir="doc"
426       cmake_doc="ON"
427     else:
428       cmake_doc="OFF"
429
430     #for catalog files
431     catalogfile = "%sCatalog.xml" % module.name
432
433     if module.gui:
434       cmake_gui="ON"
435     else:
436       cmake_gui="OFF"
437       
438     prefix = os.path.abspath(self.module.prefix)
439     component_libs = "".join(map(lambda x: x.libraryName()+" ",
440                                            module.components))
441     add_modules = "".join(map(lambda x:cmake_find_module.substitute(module=x),
442                                        self.used_modules))
443     self.makeFiles({"CMakeLists.txt":cmake_root_cpp.substitute(
444                                                  module=self.module.name,
445                                                  module_min=self.module.name.lower(),
446                                                  compolibs=component_libs,
447                                                  with_doc=cmake_doc,
448                                                  with_gui=cmake_gui,
449                                                  add_modules=add_modules,
450                                                  major_version=yacsgen_version.major_version,
451                                                  minor_version=yacsgen_version.minor_version,
452                                                  patch_version=yacsgen_version.patch_version),
453                     "README":"", "NEWS":"", "AUTHORS":"", "ChangeLog":"",
454                     "src":srcs,
455                     "resources":{"CMakeLists.txt":cmake_ressources.substitute(
456                                                         module=self.module.name),
457                                  catalogfile:self.makeCatalog()},
458                     }, namedir)
459
460     files={}
461     #for idl files
462     idlfile = "%s.idl" % module.name
463
464     #if components have other idls
465     other_idls=""
466 #    other_sks=""
467     for compo in module.components:
468       if compo.idls:
469         for idl in compo.idls:
470           for fidl in glob.glob(idl):
471             other_idls=other_idls+os.path.basename(fidl) +" "
472 #            other_sks=other_sks+os.path.splitext(os.path.basename(fidl))[0]+"SK.cc "
473
474     include_template=Template("$${${module}_ROOT_DIR}/idl/salome")
475     opt_inc="".join(map(lambda x:include_template.substitute(module=x)+"\n  ",
476                                        self.used_modules))
477     link_template=Template("$${${module}_SalomeIDL${module}}")
478     opt_link="".join(map(lambda x:link_template.substitute(module=x)+"\n  ",
479                                        self.used_modules))
480     
481     idlfiles={"CMakeLists.txt":cmake_idl.substitute(module=module.name,
482                                                     extra_idl=other_idls,
483                                                     extra_include=opt_inc,
484                                                     extra_link=opt_link),
485               idlfile         :self.makeidl(),
486              }
487
488     files["idl"]=idlfiles
489
490     self.makeFiles(files,namedir)
491
492     #copy source files if any in created tree
493     for compo in module.components:
494       for src in compo.sources:
495         shutil.copyfile(src, os.path.join(namedir, "src", compo.name, os.path.basename(src)))
496
497       if compo.idls:
498         #copy provided idl files in idl directory
499         for idl in compo.idls:
500           for fidl in glob.glob(idl):
501             shutil.copyfile(fidl, os.path.join(namedir, "idl", os.path.basename(fidl)))
502
503     self.makeDoc(namedir)
504     return
505
506   def makeDoc(self,namedir):
507     if not self.module.doc:
508       return
509     rep=os.path.join(namedir,"doc")
510     os.makedirs(rep)
511     doc_files=""
512     for docs in self.module.doc:
513       for doc in glob.glob(docs):
514         name = os.path.basename(doc)
515         doc_files = doc_files + name + "\n  "
516         shutil.copyfile(doc, os.path.join(rep, name))
517
518     d={}
519
520     if not self.module.gui:
521        #without gui but with doc: create a small SalomeApp.xml in doc directory
522        if not os.path.exists(os.path.join(namedir, "doc", "SalomeApp.xml")):
523          #create a minimal SalomeApp.xml
524          salomeapp=docsalomeapp.substitute(module=self.module.name,
525                                            lmodule=self.module.name.lower(),
526                                            version=yacsgen_version.complete_version)
527          d["SalomeApp.xml"]=salomeapp
528
529     if not os.path.exists(os.path.join(namedir, "doc", "CMakeLists.txt")):
530       #create a minimal CMakeLists.txt
531       makefile_txt=docmakefile.substitute(module=self.module.name,
532                                           files=doc_files)
533       if not self.module.gui:
534         txt = 'INSTALL(FILES SalomeApp.xml DESTINATION \
535 "${SALOME_%s_INSTALL_RES_DATA}")\n' % self.module.name
536         makefile_txt = makefile_txt + txt
537         pass
538       
539       d["CMakeLists.txt"]=makefile_txt
540       pass
541
542     if not os.path.exists(os.path.join(namedir, "doc", "conf.py")):
543       #create a minimal conf.py
544       d["conf.py"]=docconf.substitute(module=self.module.name)
545
546     self.makeFiles(d,os.path.join(namedir,"doc"))
547
548   def makeGui(self,namedir):
549     if not self.module.gui:
550       return
551     ispython=False
552     iscpp=False
553     #Force creation of intermediate directories
554     os.makedirs(os.path.join(namedir, "src", self.module.name+"GUI"))
555
556     for srcs in self.module.gui:
557       for src in glob.glob(srcs):
558         shutil.copyfile(src, os.path.join(namedir, "src", self.module.name+"GUI", os.path.basename(src)))
559         if src[-3:]==".py":ispython=True
560         if src[-4:]==".cxx":iscpp=True
561     if ispython and iscpp:
562       raise Invalid("Module GUI must be pure python or pure C++ but not mixed")
563     if ispython:
564       return self.makePyGUI(namedir)
565     if iscpp:
566       return self.makeCPPGUI(namedir)
567     raise Invalid("Module GUI must be in python or C++ but it is none of them")
568
569   def makePyGUI(self,namedir):
570     d={}
571     if not os.path.exists(os.path.join(namedir, "src", self.module.name+"GUI", "CMakeLists.txt")):
572       #create a minimal CMakeLists.txt
573       sources=""
574       other=""
575       ui_files=""
576       ts_files=""
577       for srcs in self.module.gui:
578         for src in glob.glob(srcs):
579           if src[-3:]==".py":
580             sources=sources+os.path.basename(src)+"\n  "
581           elif src[-3:]==".ts":
582             ts_files=ts_files+os.path.basename(src)+"\n  "
583           else:
584             other=other+os.path.basename(src)+"\n  "
585       makefile=cmake_py_gui.substitute(module=self.module.name,
586                                        scripts=sources,
587                                        ts_resources=ts_files,
588                                        resources=other)
589       d["CMakeLists.txt"]=makefile
590
591     if not os.path.exists(os.path.join(namedir, "src", self.module.name+"GUI", "SalomeApp.xml")):
592       #create a minimal SalomeApp.xml
593       salomeapp=pysalomeapp.substitute(module=self.module.name,
594                                        lmodule=self.module.name.lower(),
595                                        version=yacsgen_version.complete_version)
596       d["SalomeApp.xml"]=salomeapp
597
598     return d
599
600   def makeCPPGUI(self,namedir):
601     d={}
602     if not os.path.exists(os.path.join(namedir, "src", self.module.name+"GUI", "CMakeLists.txt")):
603       #create a minimal CMakeLists.txt
604       sources=""
605       headers=""
606       other=""
607       ui_files=""
608       ts_files=""
609       for srcs in self.module.gui:
610         for src in glob.glob(srcs):
611           if src[-4:]==".cxx" or src[-4:]==".cpp":
612             sources=sources+os.path.basename(src)+"\n  "
613           elif src[-2:]==".h" or src[-4:]==".hxx":
614             headers=headers+os.path.basename(src)+"\n  "
615           elif src[-3:]==".ui":
616             ui_files=ui_files+os.path.basename(src)+"\n  "
617           elif src[-3:]==".ts":
618             ts_files=ts_files+os.path.basename(src)+"\n  "
619           else:
620             other=other+os.path.basename(src)+"\n  "
621
622       compo_dirs = "".join(map(lambda x: 
623                                  "${PROJECT_SOURCE_DIR}/src/"+x.name+"\n  ",
624                                  self.module.components))
625       compo_dirs = compo_dirs + "${PROJECT_BINARY_DIR}/src/" + self.module.name + "GUI\n"
626       component_libs = "".join(map(lambda x:
627                               x.libraryName()+" ", self.module.components))
628       makefile=cmake_cpp_gui.substitute(module=self.module.name,
629                                     include_dirs=compo_dirs,
630                                     libs=component_libs,
631                                     uic_files=ui_files,
632                                     moc_headers=headers,
633                                     sources=sources,
634                                     resources=other,
635                                     ts_resources=ts_files)
636       d["CMakeLists.txt"]=makefile
637
638     if not os.path.exists(os.path.join(namedir, "src", self.module.name+"GUI", "SalomeApp.xml")):
639       #create a minimal SalomeApp.xml
640       salomeapp=cppsalomeapp.substitute(module=self.module.name,
641                                         lmodule=self.module.name.lower(),
642                                         version=yacsgen_version.complete_version)
643       d["SalomeApp.xml"]=salomeapp
644
645     return d
646
647   def makeMakefile(self,makefileItems):
648     makefile=""
649     if makefileItems.has_key("header"):
650       makefile=makefile + makefileItems["header"]+'\n'
651     if makefileItems.has_key("lib_LTLIBRARIES"):
652       makefile=makefile+"lib_LTLIBRARIES= "+" ".join(makefileItems["lib_LTLIBRARIES"])+'\n'
653     if makefileItems.has_key("salomepython_PYTHON"):
654       makefile=makefile+"salomepython_PYTHON= "+" ".join(makefileItems["salomepython_PYTHON"])+'\n'
655     if makefileItems.has_key("dist_salomescript_SCRIPTS"):
656       makefile=makefile+"dist_salomescript_SCRIPTS= "+" ".join(makefileItems["dist_salomescript_SCRIPTS"])+'\n'
657     if makefileItems.has_key("salomeres_DATA"):
658       makefile=makefile+"salomeres_DATA= "+" ".join(makefileItems["salomeres_DATA"])+'\n'
659     if makefileItems.has_key("salomeinclude_HEADERS"):
660       makefile=makefile+"salomeinclude_HEADERS= "+" ".join(makefileItems["salomeinclude_HEADERS"])+'\n'
661     if makefileItems.has_key("body"):
662       makefile=makefile+makefileItems["body"]+'\n'
663     return makefile
664
665   def makeArgs(self, service):
666     """generate source service for arguments"""
667     params = []
668     for name, typ in service.inport:
669       if typ=="file":continue #files are not passed through service interface
670       params.append("%s %s" % (corba_in_type(typ, self.module.name), name))
671     for name, typ in service.outport:
672       if typ=="file":continue #files are not passed through service interface
673       params.append("%s %s" % (corba_out_type(typ, self.module.name), name))
674     return ",".join(params)
675
676   def makeCatalog(self):
677     """generate SALOME components catalog source"""
678     components = []
679     for compo in self.module.components:
680       services = []
681       for serv in compo.services:
682         params = []
683         for name, typ in serv.inport:
684           params.append(cataInparam.substitute(name=name, type=typ))
685         inparams = "\n".join(params)
686         params = []
687         for name, typ in serv.outport:
688           params.append(cataOutparam.substitute(name=name, type=typ))
689         if serv.ret != "void" :
690           params.append(cataOutparam.substitute(name="return", type=serv.ret))
691         outparams = "\n".join(params)
692         streams = []
693         for name, typ, dep in serv.instream:
694           streams.append(cataInStream.substitute(name=name, type=calciumTypes[typ], dep=dep))
695         for name, typ, dep in serv.outstream:
696           streams.append(cataOutStream.substitute(name=name, type=calciumTypes[typ], dep=dep))
697         for name, typ in serv.parallel_instream:
698           streams.append(cataInParallelStream.substitute(name=name, type=DatastreamParallelTypes[typ]))
699         for name, typ in serv.parallel_outstream:
700           streams.append(cataOutParallelStream.substitute(name=name, type=DatastreamParallelTypes[typ]))
701         datastreams = "\n".join(streams)
702         services.append(cataService.substitute(service=serv.name, author="EDF-RD",
703                                                inparams=inparams, outparams=outparams, datastreams=datastreams))
704       impltype, implname = compo.getImpl()
705       components.append(cataCompo.substitute(component=compo.name, author="EDF-RD", impltype=impltype, implname=implname,
706                                              services='\n'.join(services)))
707     return catalog.substitute(components='\n'.join(components))
708
709   def makeidl(self):
710     """generate module IDL file source (CORBA interface)"""
711     from pacocompo import PACOComponent
712     interfaces = []
713     idldefs=""
714     for compo in self.module.components:
715       if isinstance(compo, PACOComponent):
716         services = []
717         for serv in compo.services:
718           params = []
719           for name, typ in serv.inport:
720             if typ == "file":continue #files are not passed through IDL interface
721             params.append("in %s %s" % (idlTypes[typ], name))
722           for name, typ in serv.outport:
723             if typ == "file":continue #files are not passed through IDL interface
724             params.append("out %s %s" % (idlTypes[typ], name))
725           service = "    void %s(" % serv.name
726           service = service+",".join(params)+");"
727           services.append(service)
728
729         interfaces.append(parallel_interface.substitute(component=compo.name, services="\n".join(services)))
730
731       else:
732         services = []
733         for serv in compo.services:
734           params = []
735           for name, typ in serv.inport:
736             if typ == "file":continue #files are not passed through IDL interface
737             if compo.impl in ("PY", "ASTER") and typ == "pyobj":
738               typ = "Engines::fileBlock"
739             else:
740               typ=idlTypes[typ]
741             params.append("in %s %s" % (typ, name))
742           for name, typ in serv.outport:
743             if typ == "file":continue #files are not passed through IDL interface
744             if compo.impl in ("PY", "ASTER") and typ == "pyobj":
745               typ = "Engines::fileBlock"
746             else:
747               typ=idlTypes[typ]
748             params.append("out %s %s" % (typ, name))
749           service = "    %s %s(" % (idlTypes[serv.ret],serv.name)
750           service = service+",".join(params)+") raises (SALOME::SALOME_Exception);"
751           services.append(service)
752
753         from hxxcompo import HXX2SALOMEComponent
754         from hxxparacompo import HXX2SALOMEParaComponent
755         if isinstance(compo,HXX2SALOMEComponent) or isinstance(compo,HXX2SALOMEParaComponent):
756           from hxx_tmpl import interfaceidlhxx
757           Inherited=""
758           if isinstance(compo,HXX2SALOMEParaComponent):
759               Inherited="SALOME_MED::ParaMEDMEMComponent"
760               idldefs="""#include "ParaMEDMEMComponent.idl"\n"""
761           else:
762               if compo.use_medmem==True:
763                   Inherited="Engines::EngineComponent,SALOME::MultiCommClass,SALOME_MED::MED_Gen_Driver"
764               else:
765                   Inherited="Engines::EngineComponent"
766           interfaces.append(interfaceidlhxx.substitute(component=compo.name,inherited=Inherited, services="\n".join(services)))
767         else:
768           inheritedinterface=""
769           if compo.inheritedinterface:
770             inheritedinterface=compo.inheritedinterface+","
771           interfaces.append(interface.substitute(component=compo.name, services="\n".join(services),inheritedinterface=inheritedinterface))
772
773     #build idl includes for SALOME modules
774     for mod in self.used_modules:
775       idldefs = idldefs + salome_modules[mod]["idldefs"]
776
777     for compo in self.module.components:
778       if compo.interfacedefs:
779         idldefs = idldefs + compo.interfacedefs
780
781     return idl.substitute(module=self.module.name, interfaces='\n'.join(interfaces),idldefs=idldefs)
782
783   # For PaCO++
784   def makexml(self):
785     from pacocompo import PACOComponent
786     interfaces = []
787     for compo in self.module.components:
788       if isinstance(compo, PACOComponent):
789         services = []
790         for serv in compo.services:
791           if serv.impl_type == "parallel":
792             service = xml_service.substitute(service_name=serv.name)
793             services.append(service)
794         interfaces.append(xml_interface.substitute(component=compo.name, xml_services="\n".join(services)))
795     return xml.substitute(module=self.module.name, interfaces='\n'.join(interfaces))
796
797   def makeFiles(self, dic, basedir):
798     """create files and directories defined in dictionary dic in basedir directory
799        dic key = file name to create
800        dic value = file content or dictionary defining the content of a sub directory
801     """
802     for name, content in dic.items():
803       filename = os.path.join(basedir, name)
804       if isinstance(content, str):
805         fil =  open(filename, 'w')
806         fil.write(content)
807         fil.close()
808       else:
809         if not os.path.exists(filename):
810           os.makedirs(filename)
811         self.makeFiles(content, filename)
812
813   def configure(self):
814     """Execute the second build step (configure) with installation prefix as given by the prefix attribute of module"""
815     prefix = os.path.abspath(self.module.prefix)
816
817     self.build_dir = "%s_build" % self.module.name
818     makedirs(self.build_dir)
819     
820     build_sh = "cd %s; cmake ../%s -DCMAKE_INSTALL_PREFIX:PATH=%s"%(self.build_dir, self.sourceDir(), prefix) 
821     ier = os.system(build_sh)
822     if ier != 0:
823       raise Invalid("configure has ended in error")
824
825   def make(self):
826     """Execute the third build step (compile and link) : make"""
827     make_command = "cd %s; make " % self.build_dir
828     if self.makeflags:
829       make_command += self.makeflags
830     ier = os.system(make_command)
831     if ier != 0:
832       raise Invalid("make has ended in error")
833
834   def install(self):
835     """Execute the installation step : make install """
836     make_command = "cd %s; make install" % self.build_dir
837     ier = os.system(make_command)
838     if ier != 0:
839       raise Invalid("install has ended in error")
840
841   def make_appli(self, appliname, restrict=None, altmodules=None, resources=""):
842     """
843    Create a SALOME application containing the module and preexisting SALOME modules.
844
845    :param appliname: is a string that gives the name of the application (directory path where the application
846       will be installed).
847    :type appliname: str
848    :param restrict: If given (a list of module names), only those SALOME modules will be included in the
849       application. The default is to include all modules that are located in the same directory as the KERNEL module and have
850       the same suffix (for example, if KERNEL directory is KERNEL_V5 and GEOM directory is GEOM_V5, GEOM module is automatically
851       included, except if restrict is used).
852    :param altmodules: can be used to add SALOME modules that cannot be managed with the precedent rule. This parameter
853       is a dict with a module name as the key and the installation path as the value.
854    :param resources: can be used to define an alternative resources catalog (path of the file).
855
856    For example, the following calls create a SALOME application with external modules and resources catalog in "appli" directory::
857
858      >>> g=Generator(m,context)
859      >>> g.generate()
860      >>> g.configure()
861      >>> g.make()
862      >>> g.install()
863      >>> g.make_appli("appli", restrict=["KERNEL"], altmodules={"GUI":GUI_ROOT_DIR, "YACS":YACS_ROOT_DIR},
864                       resources="myresources.xml")
865
866     """
867     makedirs(appliname)
868
869     rootdir, kerdir = os.path.split(self.kernel)
870
871     #collect modules besides KERNEL module with the same suffix if any
872     modules_dict = {}
873     if kerdir[:6] == "KERNEL":
874       suffix = kerdir[6:]
875       for mod in os.listdir(rootdir):
876         if mod[-len(suffix):] == suffix:
877           module = mod[:-len(suffix)]
878           path = os.path.join(rootdir, mod)
879           #try to find catalog files
880           lcata = glob.glob(os.path.join(path, "share", "salome", "resources", "*", "*Catalog.xml"))
881           if not lcata:
882             #catalogs have not been found : try the upper level
883             lcata = glob.glob(os.path.join(path, "share", "salome", "resources", "*Catalog.xml"))
884           if lcata:
885             #catalogs have been found : add the corresponding entries in the application
886             for cata in lcata:
887               catadir, catafile = os.path.split(cata)
888               name = catafile[:-11]
889               modules_dict[name] = '  <module name="%s" path="%s"/>' % (name, path)
890           else:
891             modules_dict[module] = '  <module name="%s" path="%s"/>' % (module, path)
892
893     modules_dict["KERNEL"] = '  <module name="KERNEL" path="%s"/>' % self.kernel
894
895     #keep only the modules which names are in restrict if given
896     modules = []
897     if restrict:
898       for mod in restrict:
899         if modules_dict.has_key(mod):
900           modules.append(modules_dict[mod])
901     else:
902       modules = modules_dict.values()
903
904     #add the alternate modules if given
905     if altmodules:
906       for module, path in altmodules.items():
907         modules.append('  <module name="%s" path="%s"/>' % (module, path))
908
909     #add the generated module
910     modules.append('  <module name="%s" path="%s"/>' % (self.module.name, os.path.abspath(self.module.prefix)))
911
912
913     #try to find a prerequisites file
914     prerequisites = self.context.get("prerequisites")
915     if not prerequisites:
916       #try to find one in rootdir
917       prerequisites = os.path.join(rootdir, "profile%s.sh" % suffix)
918     if not os.path.exists(prerequisites):
919       raise Invalid("Can not create an application : prerequisites file not defined or does not exist")
920
921     #add resources catalog if it exists
922     resources_spec=""
923     if os.path.isfile(resources):
924       resources_spec='<resources path="%s" />' % os.path.abspath(resources)
925
926     #create config_appli.xml file
927     appli = application.substitute(prerequisites=prerequisites,
928                                    modules="\n".join(modules),
929                                    resources=resources_spec)
930     fil = open(os.path.join(appliname, "config_appli.xml"), 'w')
931     fil.write(appli)
932     fil.close()
933
934     #execute appli_gen.py script
935     appligen = os.path.join(self.kernel, "bin", "salome", "appli_gen.py")
936     ier = os.system("cd %s;%s" % (appliname, appligen))
937     if ier != 0:
938       raise Invalid("make_appli has ended in error")
939
940     #add CatalogResources.xml if not created by appli_gen.py
941     if not os.path.exists(os.path.join(appliname, "CatalogResources.xml")):
942       #CatalogResources.xml does not exist create a minimal one
943       fil  = open(os.path.join(appliname, 'CatalogResources.xml'), 'w')
944       command = """<!DOCTYPE ResourcesCatalog>
945 <resources>
946     <machine hostname="%s" protocol="ssh" mode="interactive" />
947 </resources>
948 """
949       host = socket.gethostname().split('.')[0]
950       fil.write(command % host)
951       fil.close()
952