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