1 # Copyright (C) 2009-2019 EDF R&D
3 # This library is free software; you can redistribute it and/or
4 # modify it under the terms of the GNU Lesser General Public
5 # License as published by the Free Software Foundation; either
6 # version 2.1 of the License, or (at your option) any later version.
8 # This library is distributed in the hope that it will be useful,
9 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 # Lesser General Public License for more details.
13 # You should have received a copy of the GNU Lesser General Public
14 # License along with this library; if not, write to the Free Software
15 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17 # See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
20 import os, shutil, glob, socket
25 from string import Template
27 from module_generator.compat import Template, set
29 class Invalid(Exception):
34 from module_generator.mod_tmpl import *
35 from module_generator.cata_tmpl import catalog, interface, idl
36 from module_generator.cata_tmpl import xml, xml_interface, xml_service
37 from module_generator.cata_tmpl import idlMakefilePaCO_BUILT_SOURCES, idlMakefilePaCO_nodist_salomeinclude_HEADERS
38 from module_generator.cata_tmpl import idlMakefilePACO_salomepython_DATA, idlMakefilePACO_salomeidl_DATA
39 from module_generator.cata_tmpl import idlMakefilePACO_INCLUDES
40 from module_generator.cata_tmpl import cataOutStream, cataInStream, cataOutparam, cataInparam
41 from module_generator.cata_tmpl import cataOutParallelStream, cataInParallelStream
42 from module_generator.cata_tmpl import cataService, cataCompo
43 #from aster_tmpl import check_aster
44 from module_generator.salomemodules import salome_modules
45 from module_generator.yacstypes import corbaTypes, corbaOutTypes, moduleTypes, idlTypes, corba_in_type, corba_out_type
46 from module_generator.yacstypes import ValidTypes, PyValidTypes, calciumTypes, DatastreamParallelTypes
47 from module_generator.yacstypes import ValidImpl, ValidImplTypes, ValidStreamTypes, ValidParallelStreamTypes, ValidDependencies
48 from module_generator.gui_tmpl import cmake_py_gui, pysalomeapp, cmake_cpp_gui, cppsalomeapp
49 from module_generator.doc_tmpl import docmakefile, docconf, docsalomeapp
50 from module_generator import yacsgen_version
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):
58 os.rename(namedir, dirbak)
59 os.listdir(dirbak) #needed to update filesystem on special machines (cluster with NFS, for example)
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*.
68 :param name: gives the name of the module. The SALOME source module
69 will be located in the <name_SRC> directory.
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.
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::
85 >>> m = module_generator.Module('mymodule', components=[c1,c2],
89 def __init__(self, name, components=None, prefix="", doc=None, gui=None):
91 self.components = components or []
92 self.prefix = prefix or "%s_INSTALL" % name
100 print("Error in module %s: %s" % (name,e))
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)
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)
114 class Library(object):
116 A :class:'Library' instance contains the informations of a user library.
118 :param name: name of the library (exemple: "cppunit", "calcul")
119 :param path: path where to find the library (exemple: "/home/user/libs")
122 def __init__(self, name, path):
126 def findLibrary(self):
128 return : text for the FIND_LIBRARY command for cmake.
129 Feel free to overload this function for your own needs.
131 return "FIND_LIBRARY( "+self.cmakeVarName()+" "+self.name+" PATH "+self.path + ")\n"
133 def cmakeVarName(self):
135 return : name of the cmake variable used by FIND_LIBRARY
137 return "_userlib_" + self.name.split()[0]
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=""):
147 self.services = services or []
150 self.includes = includes
151 self.sources = sources or []
152 self.inheritedclass=inheritedclass
153 self.compodefs=compodefs
155 self.interfacedefs=interfacedefs
156 self.inheritedinterface=inheritedinterface
157 self.addedmethods=addedmethods
159 def additionalLibraries(self):
160 """ generate the cmake code for finding the additional libraries
162 string containing a list of "find_library"
163 string containing a list of cmake variables defined
168 for lib in self.libs:
169 cmake_text = cmake_text + lib.findLibrary()
170 cmake_vars = cmake_vars + "${" + lib.cmakeVarName() + "}\n "
172 var_template = Template("$${${name}_SalomeIDL${name}}")
173 for mod in self.getDependentModules():
174 if salome_modules[mod]["linklibs"]:
175 cmake_vars = cmake_vars + salome_modules[mod]["linklibs"]
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 "
182 return cmake_text, cmake_vars
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))
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)
196 for src in self.sources:
197 if not os.path.exists(src):
198 raise Invalid("Source file %s does not exist" % src)
203 def getMakefileItems(self,gen):
206 def setPrerequisites(self, prerequisites_file):
207 self.prerequisites = prerequisites_file
209 def getIdlInterfaces(self):
210 services = self.getIdlServices()
211 inheritedinterface=""
212 if self.inheritedinterface:
213 inheritedinterface=self.inheritedinterface+","
214 return interface.substitute(component=self.name,
215 services="\n".join(services),
216 inheritedinterface=inheritedinterface)
218 def getIdlServices(self):
220 for serv in self.services:
222 for name, typ in serv.inport:
223 if typ == "file":continue #files are not passed through IDL interface
224 if self.impl in ("PY", "ASTER") and typ == "pyobj":
225 typ = "Engines::fileBlock"
228 params.append("in %s %s" % (typ, name))
229 for name, typ in serv.outport:
230 if typ == "file":continue #files are not passed through IDL interface
231 if self.impl in ("PY", "ASTER") and typ == "pyobj":
232 typ = "Engines::fileBlock"
235 params.append("out %s %s" % (typ, name))
236 service = " %s %s(" % (idlTypes[serv.ret],serv.name)
237 service = service+",".join(params)+") raises (SALOME::SALOME_Exception);"
238 services.append(service)
241 def getIdlDefs(self):
243 #include "DSC_Engines.idl"
244 #include "SALOME_Parametric.idl"
246 if self.interfacedefs:
247 idldefs = idldefs + self.interfacedefs
250 def getDependentModules(self):
251 """get the list of SALOME modules used by the component
253 def get_dependent_modules(mod,modules):
255 if "depends" in salome_modules[mod]:
256 for m in salome_modules[mod]["depends"]:
258 get_dependent_modules(m,modules)
260 depend_modules = set()
261 for serv in self.services:
262 for name, typ in serv.inport + serv.outport + [ ("return",serv.ret) ] :
263 mod = moduleTypes[typ]
265 get_dependent_modules(mod,depend_modules)
266 return depend_modules
268 class Service(object):
270 A :class:`Service` instance represents a component service with dataflow and datastream ports.
272 :param name: gives the name of the service.
274 :param inport: gives the list of input dataflow ports.
275 :param outport: gives the list of output dataflow ports. An input or output dataflow port is defined
276 by a 2-tuple (port name, data type name). The list of supported basic data types is: "double", "long", "string",
277 "dblevec", "stringvec", "intvec", "file" and "pyobj" only for Python services. Depending on the implementation
278 language, it is also possible to use some types from SALOME modules (see :ref:`yacstypes`).
279 :param ret: gives the type of the return parameter
280 :param instream: gives the list of input datastream ports.
281 :param outstream: gives the list of output datastream ports. An input or output datastream port is defined
282 by a 3-tuple (port name, data type name, mode name). The list of possible data types is: "CALCIUM_double", "CALCIUM_integer",
283 "CALCIUM_real", "CALCIUM_string", "CALCIUM_complex", "CALCIUM_logical", "CALCIUM_long". The mode can be "I" (iterative mode)
284 or "T" (temporal mode).
285 :param defs: gives the source code to insert in the definition section of the component. It can be C++ includes
288 :param body: gives the source code to insert in the service call. It can be any C++
289 or Python code that fits well in the body of the service method.
292 For example, the following call defines a minimal Python service with one input dataflow port (name "a", type double)
293 and one input datastream port::
295 >>> s1 = module_generator.Service('myservice', inport=[("a","double"),],
296 instream=[("aa","CALCIUM_double","I")],
301 def __init__(self, name, inport=None, outport=None, ret="void", instream=None, outstream=None,
302 parallel_instream=None, parallel_outstream=None, defs="", body="", impl_type="sequential"):
304 self.inport = inport or []
305 self.outport = outport or []
307 self.instream = instream or []
308 self.outstream = outstream or []
309 self.parallel_instream = parallel_instream or []
310 self.parallel_outstream = parallel_outstream or []
314 self.impl_type = impl_type
318 for port in self.inport:
319 name, typ = self.validatePort(port)
321 raise Invalid("%s is already defined as a service parameter" % name)
324 for port in self.outport:
325 name, typ = self.validatePort(port)
327 raise Invalid("%s is already defined as a service parameter" % name)
331 for port in self.instream:
332 name, typ, dep = self.validateStream(port)
334 raise Invalid("%s is already defined as a stream port" % name)
337 for port in self.outstream:
338 name, typ, dep = self.validateStream(port)
340 raise Invalid("%s is already defined as a stream port" % name)
343 for port in self.parallel_instream:
344 name, typ = self.validateParallelStream(port)
346 raise Invalid("%s is already defined as a stream port" % name)
349 for port in self.parallel_outstream:
350 name, typ = self.validateParallelStream(port)
352 raise Invalid("%s is already defined as a stream port" % name)
355 self.validateImplType()
357 def validatePort(self, port):
361 raise Invalid("%s is not a valid definition of an data port (name,type)" % (port,))
363 if self.impl in ("PY", "ASTER"):
364 validtypes = PyValidTypes
366 validtypes = ValidTypes
368 if typ not in validtypes:
369 raise Invalid("%s is not a valid type. It should be one of %s" % (typ, validtypes))
372 def validateImplType(self):
373 if self.impl_type not in ValidImplTypes:
374 raise Invalid("%s is not a valid impl type. It should be one of %s" % (self.impl_type, ValidImplTypes))
376 def validateStream(self, port):
378 name, typ, dep = port
380 raise Invalid("%s is not a valid definition of a stream port (name,type,dependency)" % (port,))
381 if typ not in ValidStreamTypes:
382 raise Invalid("%s is not a valid type. It should be one of %s" % (typ, ValidStreamTypes))
383 if dep not in ValidDependencies:
384 raise Invalid("%s is not a valid dependency. It should be one of %s" % (dep, ValidDependencies))
385 return name, typ, dep
387 def validateParallelStream(self, port):
391 raise Invalid("%s is not a valid definition of a parallel stream port (name,type)" % (port,))
392 if typ not in ValidParallelStreamTypes:
393 raise Invalid("%s is not a valid type. It should be one of %s" % (typ, ValidParallelStreamTypes))
396 class Generator(object):
398 A :class:`Generator` instance take a :class:`Module` instance as its first parameter and can be used to generate the
399 SALOME source module, builds it, installs it and includes it in a SALOME application.
401 :param module: gives the :class:`Module` instance that will be used for the generation.
402 :param context: If given , its content is used to specify the prerequisites
403 environment file (key *"prerequisites"*) and the SALOME KERNEL installation directory (key *"kernel"*).
406 For example, the following call creates a generator for the module m::
408 >>> g = module_generator.Generator(m,context)
410 def __init__(self, module, context=None):
412 self.context = context or {}
413 self.kernel = self.context["kernel"]
414 self.gui = self.context.get("gui")
415 self.makeflags = self.context.get("makeflags")
417 if self.module.gui and not self.gui:
418 raise Invalid("To generate a module with GUI, you need to set the 'gui' parameter in the context dictionnary")
419 for component in self.module.components:
420 component.setPrerequisites(self.context.get("prerequisites"))
423 """ get the name of the source directory"""
424 return self.module.name+"_SRC"
427 """Generate a SALOME source module"""
429 namedir = self.sourceDir()
430 force = self.context.get("force")
431 update = self.context.get("update")
432 paco = self.context.get("paco")
433 if os.path.exists(namedir):
435 shutil.rmtree(namedir)
437 raise Invalid("The directory %s already exists" % namedir)
445 #get the list of SALOME modules used and put it in used_modules attribute
447 for compo in module.components:
448 modules |= compo.getDependentModules()
450 self.used_modules = modules
452 for compo in module.components:
453 #for components files
454 fdict=compo.makeCompo(self)
455 srcs[compo.name] = fdict
458 components_string = "".join([x.name+" " for x in module.components])
461 GUIname=module.name+"GUI"
462 fdict=self.makeGui(namedir)
463 srcs[GUIname] = fdict
464 components_string = components_string + "\n " + GUIname
466 cmakecontent = cmake_src.substitute(components=components_string)
467 srcs["CMakeLists.txt"] = cmakecontent
477 catalogfile = "%sCatalog.xml" % module.name
484 prefix = os.path.abspath(self.module.prefix)
485 component_libs = "".join([x.libraryName()+" " for x in module.components])
487 for x in self.used_modules:
489 cmake_text = cmake_find_module.substitute(module="FIELDS")
490 cmake_text = cmake_text + """
491 #####################################
493 #####################################
494 SET(MEDCOUPLING_ROOT_DIR $ENV{MEDCOUPLING_ROOT_DIR} CACHE PATH "Path to MEDCOUPLING module")
495 IF(EXISTS ${MEDCOUPLING_ROOT_DIR})
496 LIST(APPEND CMAKE_MODULE_PATH "${MEDCOUPLING_ROOT_DIR}/cmake_files")
497 FIND_PACKAGE(SalomeMEDCoupling REQUIRED)
498 ADD_DEFINITIONS(${MEDCOUPLING_DEFINITIONS})
499 INCLUDE_DIRECTORIES(${MEDCOUPLING_INCLUDE_DIRS})
500 ELSE(EXISTS ${MEDCOUPLING_ROOT_DIR})
501 MESSAGE(FATAL_ERROR "We absolutely need MEDCOUPLING module, please define MEDCOUPLING_ROOT_DIR")
502 ENDIF(EXISTS ${MEDCOUPLING_ROOT_DIR})
503 #####################################
507 cmake_text = cmake_find_module.substitute(module=x)
509 add_modules = add_modules + cmake_text
512 self.makeFiles({"CMakeLists.txt":cmake_root_cpp.substitute(
513 module=self.module.name,
514 module_min=self.module.name.lower(),
515 compolibs=component_libs,
518 add_modules=add_modules,
519 major_version=yacsgen_version.major_version,
520 minor_version=yacsgen_version.minor_version,
521 patch_version=yacsgen_version.patch_version),
522 "README":"", "NEWS":"", "AUTHORS":"", "ChangeLog":"",
524 "resources":{"CMakeLists.txt":cmake_ressources.substitute(
525 module=self.module.name),
526 catalogfile:self.makeCatalog()},
531 idlfile = "%s.idl" % module.name
533 #if components have other idls
536 for compo in module.components:
538 for idl in compo.idls:
539 for fidl in glob.glob(idl):
540 other_idls=other_idls+os.path.basename(fidl) +" "
541 # other_sks=other_sks+os.path.splitext(os.path.basename(fidl))[0]+"SK.cc "
543 include_template=Template("$${${module}_ROOT_DIR}/idl/salome")
544 link_template=Template("$${${module}_SalomeIDL${module}}")
547 for x in self.used_modules:
549 # here template cannot be used as we mix FIELDS and MED
550 opt_inc+="${FIELDS_ROOT_DIR}/idl/salome\n "
551 opt_link+="${FIELDS_SalomeIDLMED}\n "
553 opt_inc+=include_template.substitute(module=x)+"\n "
554 opt_link+=link_template.substitute(module=x)+"\n "
557 idlfiles={"CMakeLists.txt":cmake_idl.substitute(module=module.name,
558 extra_idl=other_idls,
559 extra_include=opt_inc,
560 extra_link=opt_link),
561 idlfile :self.makeidl(),
564 files["idl"]=idlfiles
566 self.makeFiles(files,namedir)
568 #copy source files if any in created tree
569 for compo in module.components:
570 for src in compo.sources:
571 shutil.copyfile(src, os.path.join(namedir, "src", compo.name, os.path.basename(src)))
574 #copy provided idl files in idl directory
575 for idl in compo.idls:
576 for fidl in glob.glob(idl):
577 shutil.copyfile(fidl, os.path.join(namedir, "idl", os.path.basename(fidl)))
579 self.makeDoc(namedir)
582 def makeDoc(self,namedir):
583 if not self.module.doc:
585 rep=os.path.join(namedir,"doc")
588 for docs in self.module.doc:
589 for doc in glob.glob(docs):
590 name = os.path.basename(doc)
591 doc_files = doc_files + name + "\n "
592 shutil.copyfile(doc, os.path.join(rep, name))
596 if not self.module.gui:
597 #without gui but with doc: create a small SalomeApp.xml in doc directory
598 if not os.path.exists(os.path.join(namedir, "doc", "SalomeApp.xml")):
599 #create a minimal SalomeApp.xml
600 salomeapp=docsalomeapp.substitute(module=self.module.name,
601 lmodule=self.module.name.lower(),
602 version=yacsgen_version.complete_version)
603 d["SalomeApp.xml"]=salomeapp
605 if not os.path.exists(os.path.join(namedir, "doc", "CMakeLists.txt")):
606 #create a minimal CMakeLists.txt
607 makefile_txt=docmakefile.substitute(module=self.module.name,
609 if not self.module.gui:
610 txt = 'INSTALL(FILES SalomeApp.xml DESTINATION \
611 "${SALOME_%s_INSTALL_RES_DATA}")\n' % self.module.name
612 makefile_txt = makefile_txt + txt
615 d["CMakeLists.txt"]=makefile_txt
618 if not os.path.exists(os.path.join(namedir, "doc", "conf.py")):
619 #create a minimal conf.py
620 d["conf.py"]=docconf.substitute(module=self.module.name)
622 self.makeFiles(d,os.path.join(namedir,"doc"))
624 def makeGui(self,namedir):
625 if not self.module.gui:
629 #Force creation of intermediate directories
630 os.makedirs(os.path.join(namedir, "src", self.module.name+"GUI"))
632 for srcs in self.module.gui:
633 for src in glob.glob(srcs):
634 shutil.copyfile(src, os.path.join(namedir, "src", self.module.name+"GUI", os.path.basename(src)))
635 if src[-3:]==".py":ispython=True
636 if src[-4:]==".cxx":iscpp=True
637 if ispython and iscpp:
638 raise Invalid("Module GUI must be pure python or pure C++ but not mixed")
640 return self.makePyGUI(namedir)
642 return self.makeCPPGUI(namedir)
643 raise Invalid("Module GUI must be in python or C++ but it is none of them")
645 def makePyGUI(self,namedir):
647 if not os.path.exists(os.path.join(namedir, "src", self.module.name+"GUI", "CMakeLists.txt")):
648 #create a minimal CMakeLists.txt
653 for srcs in self.module.gui:
654 for src in glob.glob(srcs):
656 sources=sources+os.path.basename(src)+"\n "
657 elif src[-3:]==".ts":
658 ts_files=ts_files+os.path.basename(src)+"\n "
660 other=other+os.path.basename(src)+"\n "
661 makefile=cmake_py_gui.substitute(module=self.module.name,
663 ts_resources=ts_files,
665 d["CMakeLists.txt"]=makefile
667 if not os.path.exists(os.path.join(namedir, "src", self.module.name+"GUI", "SalomeApp.xml")):
668 #create a minimal SalomeApp.xml
669 salomeapp=pysalomeapp.substitute(module=self.module.name,
670 lmodule=self.module.name.lower(),
671 version=yacsgen_version.complete_version)
672 d["SalomeApp.xml"]=salomeapp
676 def makeCPPGUI(self,namedir):
678 if not os.path.exists(os.path.join(namedir, "src", self.module.name+"GUI", "CMakeLists.txt")):
679 #create a minimal CMakeLists.txt
685 for srcs in self.module.gui:
686 for src in glob.glob(srcs):
687 if src[-4:]==".cxx" or src[-4:]==".cpp":
688 sources=sources+os.path.basename(src)+"\n "
689 elif src[-2:]==".h" or src[-4:]==".hxx":
690 headers=headers+os.path.basename(src)+"\n "
691 elif src[-3:]==".ui":
692 ui_files=ui_files+os.path.basename(src)+"\n "
693 elif src[-3:]==".ts":
694 ts_files = ts_files + os.path.basename(src) + "\n "
696 other=other+os.path.basename(src)+"\n "
698 compo_dirs = "".join(["${PROJECT_SOURCE_DIR}/src/"+x.name+"\n " for x in self.module.components])
699 compo_dirs = compo_dirs + "${PROJECT_BINARY_DIR}/src/" + self.module.name + "GUI\n"
700 component_libs = "".join([x.libraryName()+" " for x in self.module.components])
701 makefile=cmake_cpp_gui.substitute(module=self.module.name,
702 include_dirs=compo_dirs,
708 ts_resources=ts_files)
709 d["CMakeLists.txt"]=makefile
711 if not os.path.exists(os.path.join(namedir, "src", self.module.name+"GUI", "SalomeApp.xml")):
712 #create a minimal SalomeApp.xml
713 salomeapp=cppsalomeapp.substitute(module=self.module.name,
714 lmodule=self.module.name.lower(),
715 version=yacsgen_version.complete_version)
716 d["SalomeApp.xml"]=salomeapp
720 def makeMakefile(self,makefileItems):
722 if "header" in makefileItems:
723 makefile=makefile + makefileItems["header"]+'\n'
724 if "lib_LTLIBRARIES" in makefileItems:
725 makefile=makefile+"lib_LTLIBRARIES= "+" ".join(makefileItems["lib_LTLIBRARIES"])+'\n'
726 if "salomepython_PYTHON" in makefileItems:
727 makefile=makefile+"salomepython_PYTHON= "+" ".join(makefileItems["salomepython_PYTHON"])+'\n'
728 if "dist_salomescript_SCRIPTS" in makefileItems:
729 makefile=makefile+"dist_salomescript_SCRIPTS= "+" ".join(makefileItems["dist_salomescript_SCRIPTS"])+'\n'
730 if "salomeres_DATA" in makefileItems:
731 makefile=makefile+"salomeres_DATA= "+" ".join(makefileItems["salomeres_DATA"])+'\n'
732 if "salomeinclude_HEADERS" in makefileItems:
733 makefile=makefile+"salomeinclude_HEADERS= "+" ".join(makefileItems["salomeinclude_HEADERS"])+'\n'
734 if "body" in makefileItems:
735 makefile=makefile+makefileItems["body"]+'\n'
738 def makeArgs(self, service):
739 """generate source service for arguments"""
741 for name, typ in service.inport:
742 if typ=="file":continue #files are not passed through service interface
743 params.append("%s %s" % (corba_in_type(typ, self.module.name), name))
744 for name, typ in service.outport:
745 if typ=="file":continue #files are not passed through service interface
746 params.append("%s %s" % (corba_out_type(typ, self.module.name), name))
747 return ",".join(params)
749 def makeCatalog(self):
750 """generate SALOME components catalog source"""
752 for compo in self.module.components:
754 for serv in compo.services:
756 for name, typ in serv.inport:
757 params.append(cataInparam.substitute(name=name, type=typ))
758 inparams = "\n".join(params)
760 for name, typ in serv.outport:
761 params.append(cataOutparam.substitute(name=name, type=typ))
762 if serv.ret != "void" :
763 params.append(cataOutparam.substitute(name="return", type=serv.ret))
764 outparams = "\n".join(params)
766 for name, typ, dep in serv.instream:
767 streams.append(cataInStream.substitute(name=name, type=calciumTypes[typ], dep=dep))
768 for name, typ, dep in serv.outstream:
769 streams.append(cataOutStream.substitute(name=name, type=calciumTypes[typ], dep=dep))
770 for name, typ in serv.parallel_instream:
771 streams.append(cataInParallelStream.substitute(name=name, type=DatastreamParallelTypes[typ]))
772 for name, typ in serv.parallel_outstream:
773 streams.append(cataOutParallelStream.substitute(name=name, type=DatastreamParallelTypes[typ]))
774 datastreams = "\n".join(streams)
775 services.append(cataService.substitute(service=serv.name, author="EDF-RD",
776 inparams=inparams, outparams=outparams, datastreams=datastreams))
777 impltype, implname = compo.getImpl()
778 components.append(cataCompo.substitute(component=compo.name, author="EDF-RD", impltype=impltype, implname=implname,
779 services='\n'.join(services)))
780 return catalog.substitute(components='\n'.join(components))
783 """generate module IDL file source (CORBA interface)"""
786 for compo in self.module.components:
787 interfaces.append(compo.getIdlInterfaces())
789 #build idl includes for SALOME modules
790 for mod in self.used_modules:
791 idldefs = idldefs + salome_modules[mod]["idldefs"]
793 for compo in self.module.components:
794 idldefs = idldefs + compo.getIdlDefs()
797 for defLine in idldefs.split('\n'):
798 if defLine not in filteredDefs:
799 filteredDefs.append(defLine)
801 return idl.substitute(module=self.module.name,
802 interfaces='\n'.join(interfaces),
803 idldefs='\n'.join(filteredDefs) )
807 from .pacocompo import PACOComponent
809 for compo in self.module.components:
810 if isinstance(compo, PACOComponent):
812 for serv in compo.services:
813 if serv.impl_type == "parallel":
814 service = xml_service.substitute(service_name=serv.name)
815 services.append(service)
816 interfaces.append(xml_interface.substitute(component=compo.name, xml_services="\n".join(services)))
817 return xml.substitute(module=self.module.name, interfaces='\n'.join(interfaces))
819 def makeFiles(self, dic, basedir):
820 """create files and directories defined in dictionary dic in basedir directory
821 dic key = file name to create
822 dic value = file content or dictionary defining the content of a sub directory
824 for name, content in list(dic.items()):
825 filename = os.path.join(basedir, name)
826 if isinstance(content, str):
827 # encodage to utf-8 if unicode string / on python3 str are unicode
828 with open(filename, 'w') as fil:
831 if not os.path.exists(filename):
832 os.makedirs(filename)
833 self.makeFiles(content, filename)
836 """Execute the second build step (configure) with installation prefix as given by the prefix attribute of module"""
837 prefix = os.path.abspath(self.module.prefix)
839 self.build_dir = "%s_build" % self.module.name
840 makedirs(self.build_dir)
842 build_sh = "cd %s; cmake ../%s -DCMAKE_INSTALL_PREFIX:PATH=%s"%(self.build_dir, self.sourceDir(), prefix)
843 ier = os.system(build_sh)
845 raise Invalid("configure has ended in error")
848 """Execute the third build step (compile and link) : make"""
849 make_command = "cd %s; make " % self.build_dir
851 make_command += self.makeflags
852 ier = os.system(make_command)
854 raise Invalid("make has ended in error")
857 """Execute the installation step : make install """
858 make_command = "cd %s; make install" % self.build_dir
859 ier = os.system(make_command)
861 raise Invalid("install has ended in error")
863 def make_appli(self, appliname, restrict=None, altmodules=None, resources=""):
865 Create a SALOME application containing the module and preexisting SALOME modules.
867 :param appliname: is a string that gives the name of the application (directory path where the application
870 :param restrict: If given (a list of module names), only those SALOME modules will be included in the
871 application. The default is to include all modules that are located in the same directory as the KERNEL module and have
872 the same suffix (for example, if KERNEL directory is KERNEL_V5 and GEOM directory is GEOM_V5, GEOM module is automatically
873 included, except if restrict is used).
874 :param altmodules: can be used to add SALOME modules that cannot be managed with the precedent rule. This parameter
875 is a dict with a module name as the key and the installation path as the value.
876 :param resources: can be used to define an alternative resources catalog (path of the file).
878 For example, the following calls create a SALOME application with external modules and resources catalog in "appli" directory::
880 >>> g=Generator(m,context)
885 >>> g.make_appli("appli", restrict=["KERNEL"], altmodules={"GUI":GUI_ROOT_DIR, "YACS":YACS_ROOT_DIR},
886 resources="myresources.xml")
891 rootdir, kerdir = os.path.split(self.kernel)
893 #collect modules besides KERNEL module with the same suffix if any
895 if kerdir[:6] == "KERNEL":
897 for mod in os.listdir(rootdir):
898 if mod[-len(suffix):] == suffix:
899 module = mod[:-len(suffix)]
900 path = os.path.join(rootdir, mod)
901 #try to find catalog files
902 lcata = glob.glob(os.path.join(path, "share", "salome", "resources", "*", "*Catalog.xml"))
904 #catalogs have not been found : try the upper level
905 lcata = glob.glob(os.path.join(path, "share", "salome", "resources", "*Catalog.xml"))
907 #catalogs have been found : add the corresponding entries in the application
909 catadir, catafile = os.path.split(cata)
910 name = catafile[:-11]
911 modules_dict[name] = ' <module name="%s" path="%s"/>' % (name, path)
913 modules_dict[module] = ' <module name="%s" path="%s"/>' % (module, path)
915 modules_dict["KERNEL"] = ' <module name="KERNEL" path="%s"/>' % self.kernel
917 #keep only the modules which names are in restrict if given
921 if mod in modules_dict:
922 modules.append(modules_dict[mod])
924 modules = list(modules_dict.values())
926 #add the alternate modules if given
928 for module, path in list(altmodules.items()):
929 modules.append(' <module name="%s" path="%s"/>' % (module, path))
931 #add the generated module
932 modules.append(' <module name="%s" path="%s"/>' % (self.module.name, os.path.abspath(self.module.prefix)))
934 ROOT_SALOME=os.getenv("ROOT_SALOME")
935 #try to find a prerequisites file
936 prerequisites = self.context.get("prerequisites")
937 if not prerequisites:
938 #try to find one in rootdir
939 prerequisites = os.path.join(ROOT_SALOME, "salome_prerequisites.sh")
940 if not os.path.exists(prerequisites):
941 raise Invalid("Can not create an application : prerequisites file not defined or does not exist")
943 salome_context = self.context.get("salome_context")
944 if not salome_context:
945 #try to find one in rootdir
946 salome_context = os.path.join(ROOT_SALOME, "salome_context.cfg")
947 if not os.path.exists(salome_context):
948 raise Invalid("Can not create an application : salome_context file not defined or does not exist")
950 #add resources catalog if it exists
952 if os.path.isfile(resources):
953 resources_spec='<resources path="%s" />' % os.path.abspath(resources)
955 #create config_appli.xml file
956 appli = application.substitute(prerequisites=prerequisites,
957 context=salome_context,
958 modules="\n".join(modules),
959 resources=resources_spec)
960 fil = open(os.path.join(appliname, "config_appli.xml"), 'w')
964 #execute appli_gen.py script
965 appligen = os.path.join(self.kernel, "bin", "salome", "appli_gen.py")
966 ier = os.system("cd %s;%s" % (appliname, appligen))
968 raise Invalid("make_appli has ended in error")
970 #add CatalogResources.xml if not created by appli_gen.py
971 if not os.path.exists(os.path.join(appliname, "CatalogResources.xml")):
972 #CatalogResources.xml does not exist create a minimal one
973 fil = open(os.path.join(appliname, 'CatalogResources.xml'), 'w')
974 command = """<!DOCTYPE ResourcesCatalog>
976 <machine hostname="%s" protocol="ssh" mode="interactive" />
979 host = socket.gethostname().split('.')[0]
980 fil.write(command % host)