Salome HOME
Aster changed some paths in version 11.4
[tools/yacsgen.git] / module_generator / astcompo.py
1 # Copyright (C) 2009-2013  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.
7 #
8 # This library is distributed in the hope that it will be useful,
9 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
11 # Lesser General Public License for more details.
12 #
13 # You should have received a copy of the GNU Lesser General Public
14 # License along with this library; if not, write to the Free Software
15 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
16 #
17 # See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
18 #
19
20 """
21   This module defines the ASTERComponent class for ASTER component generation
22   An ASTER component comes in 3 flavors :
23    - implemented as a dynamic library (kind='lib')
24    - implemented as a standalone component (kind='exe')
25    - implemented as a specific container (kind='cexe')
26 """
27 import re, os, sys
28
29 from gener import Component, Invalid, makedirs
30
31 from pyth_tmpl import pyinitEXEService, pyinitCEXEService, pyinitService
32 import aster_tmpl
33 from aster_tmpl import asterCEXEService, asterEXEService
34 from aster_tmpl import asterService, asterEXECompo, asterCEXECompo, asterCompo
35 from aster_tmpl import comm, make_etude, cexe, exeaster
36 from aster_tmpl import container, component
37
38 class ASTERComponent(Component):
39   """
40    A :class:`ASTERComponent` instance represents an ASTER SALOME component (special component for Code_Aster that is a mix of
41    Fortran and Python code) with services given as a list of :class:`Service` instances with the parameter *services*.
42
43    :param name: gives the name of the component.
44    :type name: str
45    :param services: the list of services (:class:`Service`) of the component.
46    :param kind: If it is given and has the value "exe", the component will be built as a standalone
47       component (executable or shell script). The default is to build the component as a dynamic library.
48    :param libs: gives all the libraries options to add when linking the generated component (-L...).
49    :param rlibs: gives all the runtime libraries options to add when linking the generated component (-R...).
50    :param exe_path: is only used when kind is "exe" and gives the path to the standalone component.
51    :param aster_dir: gives the Code_Aster installation directory.
52    :param python_path: If it is given (as a list of paths), all the paths are added to the python path (sys.path).
53    :param argv: is a list of strings that gives the command line parameters for Code_Aster. This parameter is only useful when
54       kind is "lib".
55
56    For example, the following call defines a Code_Aster component named "mycompo" with one service s1 (it must have been defined before).
57    This standalone component takes some command line arguments::
58
59       >>> c1 = module_generator.ASTERComponent('mycompo', services=[s1,], kind="exe",
60                                                           exe_path="launch.sh",
61                                                           argv=["-memjeveux","4"])
62   """
63   def __init__(self, name, services=None, libs="", rlibs="", aster_dir="", 
64                      python_path=None, argv=None, kind="lib", exe_path=None):
65     """initialise component attributes"""
66     self.aster_dir = aster_dir
67     self.python_path = python_path or []
68     self.argv = argv or []
69     self.exe_path = exe_path
70     Component.__init__(self, name, services, impl="ASTER", libs=libs, 
71                              rlibs=rlibs, kind=kind)
72
73   def validate(self):
74     """validate the component definition"""
75     Component.validate(self)
76     if not self.aster_dir:
77       raise Invalid("aster_dir must be defined for component %s" % self.name)
78
79     kinds = ("lib", "cexe", "exe")
80     if self.kind not in kinds:
81       raise Invalid("kind must be one of %s for component %s" % (kinds,self.name))
82     if self.kind == "lib" and not self.python_path:
83       raise Invalid("python_path must be defined for component %s" % self.name)
84     if self.kind == "cexe" :
85       if not self.exe_path:
86         raise Invalid("exe_path must be defined for component %s" % self.name)
87     if self.kind == "exe" :
88       if not self.exe_path:
89         raise Invalid("exe_path must be defined for component %s" % self.name)
90
91     #Si un port de nom jdc n'est pas defini dans la liste des inports du service,
92     #on en ajoute un de type string en premiere position
93     for serv in self.services:
94       found=False
95       for port_name,port_type in serv.inport:
96         if port_name == "jdc":
97           found=True
98           break
99       if not found:
100         serv.inport.insert(0, ("jdc", "string"))
101
102   def getAsterPythonPath(self):
103     """Directory of aster python modules
104     """
105     python_version_dir = 'python%s.%s' % (sys.version_info[0], sys.version_info[1])
106     aster_python_path = os.path.join(self.aster_dir, "lib", python_version_dir, "site-packages")
107
108     if not os.path.exists(aster_python_path) :
109       aster_python_path = os.path.join(self.aster_dir, "bibpyt")
110       
111     return aster_python_path
112
113   def getConfig(self, gen):
114     """Content of the config.txt file
115     """
116     path_compo=os.path.join(os.path.abspath(gen.module.prefix),'lib',
117                       'python%s.%s' % (sys.version_info[0], sys.version_info[1]),
118                       'site-packages','salome','%s_component.py'%self.name)
119     conf="""ENV_SH         | env     | -     | $ASTER_VERSION_DIR/share/aster/profile.sh
120 BINELE         | bin     | -     | $ASTER_VERSION_DIR/share/aster/elements
121 SRCPY          | src     | -     | %s
122 ARGPYT         | exec    | -     | %s
123 """ % (self.getAsterPythonPath(), path_compo)
124     
125     return conf
126
127   def makeCompo(self, gen):
128     """drive the generation of SALOME module files and code files
129        depending on the choosen component kind
130     """
131     filename = "%s.py" % self.name
132     #on suppose que les composants ASTER sont homogenes (utilisent meme install)
133     gen.aster = self.aster_dir
134
135     #get ASTER version
136     f = os.path.join(self.aster_dir, self.getAsterPythonPath(), 'Accas', 'properties.py')
137     self.version=(0,0,0)
138     if os.path.isfile(f):
139       mydict = {}
140       execfile(f, mydict)
141       v,r,p = mydict['version'].split('.')
142       self.version=(int(v),int(r),int(p))
143
144     if self.kind == "lib":
145       return {"Makefile.am":gen.makeMakefile(self.getMakefileItems(gen)),
146               filename:self.makeaster(gen)}
147     elif self.kind == "cexe":
148       fdict=self.makecexepath(gen)
149       d= {"Makefile.am":gen.makeMakefile(self.getMakefileItems(gen)),
150            self.name+".exe":cexe.substitute(compoexe=self.exe_path),
151            filename:self.makecexeaster(gen)
152          }
153       d.update(fdict)
154       return d
155     elif self.kind == "exe":
156       fdict=self.makeexepath(gen)
157       d= {"Makefile.am":gen.makeMakefile(self.getMakefileItems(gen)),
158            self.name+".exe":exeaster.substitute(compoexe=self.exe_path),
159            self.name+"_module.py":self.makeexeaster(gen)
160          }
161       d.update(fdict)
162       return d
163
164   def getMakefileItems(self,gen):
165     makefileItems={"header":"include $(top_srcdir)/adm_local/make_common_starter.am"}
166     if self.kind == "lib":
167       makefileItems["salomepython_PYTHON"]=[self.name+".py"]
168     elif self.kind == "exe":
169       makefileItems["salomepython_PYTHON"]=[self.name+"_module.py",self.name+"_component.py"]
170       if self.version < (10,1,2):
171         makefileItems["salomepython_PYTHON"].append("E_SUPERV.py")
172       makefileItems["dist_salomescript_SCRIPTS"]=[self.name+".exe"]
173       makefileItems["salomeres_DATA"]=[self.name+"_config.txt"]
174     elif self.kind == "cexe":
175       makefileItems["salomepython_PYTHON"]=[self.name+".py",self.name+"_container.py"]
176       if self.version < (10,1,2):
177         makefileItems["salomepython_PYTHON"].append("E_SUPERV.py")
178       makefileItems["dist_salomescript_SCRIPTS"]=[self.name+".exe"]
179       makefileItems["salomeres_DATA"]=[self.name+"_config.txt"]
180     return makefileItems
181
182
183   def makeexepath(self, gen):
184     """standalone component: generate files for calculation code"""
185
186     fdict={}
187
188     if self.version < (10,1,2):
189       #patch to E_SUPERV.py
190       fil = open(os.path.join(self.aster_dir, "bibpyt", "Execution", "E_SUPERV.py"))
191       esuperv = fil.read()
192       fil.close()
193       esuperv = re.sub("def Execute\(self\)", "def Execute(self, params)", esuperv)
194       esuperv = re.sub("j=self.JdC", "self.jdc=j=self.JdC", esuperv)
195       esuperv = re.sub("\*\*args", "context_ini=params, **args", esuperv)
196       esuperv = re.sub("def main\(self\)", "def main(self,params={})", esuperv)
197       esuperv = re.sub("return self.Execute\(\)", "return self.Execute(params)", esuperv)
198       fdict["E_SUPERV.py"]=esuperv
199
200     #use a specific main program (modification of config.txt file)
201     config = ""
202     path_config = os.path.join(self.aster_dir, "config.txt")
203     if os.path.exists(path_config) :
204       # old aster version - old mechanism kept for compatibility
205       fil = open(path_config)
206       config = fil.read()
207       fil.close()
208       config = re.sub(" profile.sh", os.path.join(self.aster_dir, "profile.sh"), config)
209       path=os.path.join(os.path.abspath(gen.module.prefix),'lib',
210                       'python%s.%s' % (sys.version_info[0], sys.version_info[1]),
211                       'site-packages','salome','%s_component.py'%self.name)
212       config = re.sub("Execution\/E_SUPERV.py", path, config)
213     else :
214       # getConfig doesn't work with older versions of aster
215       config = self.getConfig(gen)
216
217     fdict["%s_config.txt" % self.name] = config
218     fdict["%s_component.py" % self.name] = component.substitute(component=self.name)
219
220     return fdict
221
222   def makecexepath(self, gen):
223     """specific container: generate files"""
224
225     fdict={}
226
227     if self.version < (10,1,2):
228       #patch to E_SUPERV.py
229       fil = open(os.path.join(self.aster_dir, "bibpyt", "Execution", "E_SUPERV.py"))
230       esuperv = fil.read()
231       fil.close()
232       esuperv = re.sub("def Execute\(self\)", "def Execute(self, params)", esuperv)
233       esuperv = re.sub("j=self.JdC", "self.jdc=j=self.JdC", esuperv)
234       esuperv = re.sub("\*\*args", "context_ini=params, **args", esuperv)
235       esuperv = re.sub("def main\(self\)", "def main(self,params={})", esuperv)
236       esuperv = re.sub("return self.Execute\(\)", "return self.Execute(params)", esuperv)
237       fdict["E_SUPERV.py"]=esuperv
238
239     #use a specific main program
240     config = ""
241     path_config = os.path.join(self.aster_dir, "config.txt")
242     if os.path.exists(path_config) :
243       # old aster version - old mechanism kept for compatibility
244       fil = open(path_config)
245       config = fil.read()
246       fil.close()
247       config = re.sub(" profile.sh", os.path.join(self.aster_dir, "profile.sh"), config)
248       path=os.path.join(os.path.abspath(gen.module.prefix),'lib',
249                       'python%s.%s' % (sys.version_info[0], sys.version_info[1]),
250                       'site-packages','salome','%s_container.py' % self.name)
251       config = re.sub("Execution\/E_SUPERV.py", path, config)
252     else :
253       # getConfig doesn't work with older versions of aster
254       config = self.getConfig(gen)
255
256     fdict["%s_container.py" % self.name] = container
257     fdict["%s_config.txt" % self.name] = config
258
259     return fdict
260
261   def getImportESuperv(self):
262     importesuperv=""
263     if self.version < (10,1,2):
264       importesuperv="from E_SUPERV import SUPERV"
265     else :
266       importesuperv="""sys.path=["%s"]+sys.path
267 from Execution.E_SUPERV import SUPERV
268 """ % self.getAsterPythonPath()
269     return importesuperv
270
271
272   def makeexeaster(self, gen):
273     """standalone component: generate SALOME component source"""
274     services = []
275     inits = []
276     defs = []
277     for serv in self.services:
278       defs.append(serv.defs)
279       params = []
280       datas = []
281       for name, typ in serv.inport:
282         if typ=="file":continue #files are not passed through service interface
283         params.append(name)
284         if typ == "pyobj":
285           datas.append('"%s":cPickle.loads(%s)' % (name, name))
286         else:
287           datas.append('"%s":%s' % (name, name))
288       #ajout de l'adresse du composant
289       datas.append('"component":self.proxy.ptr()')
290       dvars = "{"+','.join(datas)+"}"
291       inparams = ",".join(params)
292
293       params = []
294       datas = []
295       for name, typ in serv.outport:
296         if typ=="file":continue #files are not passed through service interface
297         params.append(name)
298         if typ == "pyobj":
299           datas.append('cPickle.dumps(j.g_context["%s"],-1)'%name)
300         else:
301           datas.append('j.g_context["%s"]'%name)
302       outparams = ",".join(params)
303       rvars = ",".join(datas)
304
305       service = asterEXEService.substitute(component=self.name,
306                                            service=serv.name,
307                                            inparams=inparams,
308                                            outparams=outparams,
309                                            body=serv.body,
310                                            dvars=dvars, rvars=rvars)
311       streams = []
312       for name, typ, dep in serv.instream:
313         streams.append('       calcium.create_calcium_port(self.proxy,"%s","%s","IN","%s")'% (name, typ, dep))
314       instream = "\n".join(streams)
315       streams = []
316       for name, typ, dep in serv.outstream:
317         streams.append('       calcium.create_calcium_port(self.proxy,"%s","%s","OUT","%s")'% (name, typ, dep))
318       outstream = "\n".join(streams)
319
320       init = pyinitEXEService.substitute(component=self.name, service=serv.name,
321                                          instream=instream, outstream=outstream)
322       services.append(service)
323       inits.append(init)
324
325     importesuperv = self.getImportESuperv()
326
327     return asterEXECompo.substitute(component=self.name, module=gen.module.name,
328                                     servicesdef="\n".join(defs),
329                                     servicesimpl="\n".join(services),
330                                     initservice='\n'.join(inits),
331                                     aster_dir=self.aster_dir,
332                                     importesuperv=importesuperv,
333                                     )
334
335   def makecexeaster(self, gen):
336     """specific container: generate SALOME component source"""
337     services = []
338     inits = []
339     defs = []
340     for serv in self.services:
341       defs.append(serv.defs)
342       params = []
343       datas = []
344       for name, typ in serv.inport:
345         if typ=="file":continue #files are not passed through service interface
346         params.append(name)
347         if typ == "pyobj":
348           datas.append('"%s":cPickle.loads(%s)' % (name, name))
349         else:
350           datas.append('"%s":%s' % (name, name))
351       #ajout de l'adresse du composant
352       datas.append('"component":self.proxy.ptr()')
353       dvars = "{"+','.join(datas)+"}"
354       inparams = ",".join(params)
355
356       params = []
357       datas = []
358       for name, typ in serv.outport:
359         params.append(name)
360         if typ == "pyobj":
361           datas.append('cPickle.dumps(j.g_context["%s"],-1)'%name)
362         else:
363           datas.append('j.g_context["%s"]'%name)
364       outparams = ",".join(params)
365       rvars = ",".join(datas)
366
367       service = asterCEXEService.substitute(component=self.name,
368                                             service=serv.name,
369                                             inparams=inparams,
370                                             outparams=outparams,
371                                             body=serv.body,
372                                             dvars=dvars, rvars=rvars)
373       streams = []
374       for name, typ, dep in serv.instream:
375         streams.append('       calcium.create_calcium_port(self.proxy,"%s","%s","IN","%s")'% (name, typ, dep))
376       instream = "\n".join(streams)
377       streams = []
378       for name, typ, dep in serv.outstream:
379         streams.append('       calcium.create_calcium_port(self.proxy,"%s","%s","OUT","%s")'% (name, typ, dep))
380       outstream = "\n".join(streams)
381
382       init = pyinitCEXEService.substitute(component=self.name, 
383                                           service=serv.name,
384                                           instream=instream, 
385                                           outstream=outstream)
386       services.append(service)
387       inits.append(init)
388
389     importesuperv = self.getImportESuperv()
390
391     return asterCEXECompo.substitute(component=self.name, 
392                                      module=gen.module.name,
393                                      servicesdef="\n".join(defs), 
394                                      servicesimpl="\n".join(services), 
395                                      initservice='\n'.join(inits),
396                                      aster_dir=self.aster_dir,
397                                      importesuperv=importesuperv,
398                                      )
399
400   def getImpl(self):
401     if self.kind == "cexe":
402       return "CEXE", self.name+".exe"
403     else:
404       return "SO", ""
405
406   def makeaster(self, gen):
407     """library component: generate SALOME component source"""
408     services = []
409     inits = []
410     defs = []
411     for serv in self.services:
412       defs.append(serv.defs)
413       params = []
414       datas = []
415       for name, typ in serv.inport:
416         if typ=="file":continue #files are not passed through service interface
417         params.append(name)
418         if typ == "pyobj":
419           datas.append('"%s":cPickle.loads(%s)' % (name, name))
420         else:
421           datas.append('"%s":%s' % (name, name))
422       #ajout de l'adresse du composant
423       datas.append('"component":self.proxy.ptr()')
424       dvars = "{"+','.join(datas)+"}"
425       inparams = ",".join(params)
426
427       params = []
428       datas = []
429       for name, typ in serv.outport:
430         params.append(name)
431         if typ == "pyobj":
432           datas.append('cPickle.dumps(j.g_context["%s"],-1)'%name)
433         else:
434           datas.append('j.g_context["%s"]'%name)
435       outparams = ",".join(params)
436       rvars = ",".join(datas)
437
438       service = asterService.substitute(component=self.name, service=serv.name, 
439                                         inparams=inparams, outparams=outparams, 
440                                         body=serv.body, 
441                                         dvars=dvars, rvars=rvars)
442       streams = []
443       for name, typ, dep in serv.instream:
444         streams.append('       calcium.create_calcium_port(self.proxy,"%s","%s","IN","%s")'% (name, typ, dep))
445       instream = "\n".join(streams)
446       streams = []
447       for name, typ, dep in serv.outstream:
448         streams.append('       calcium.create_calcium_port(self.proxy,"%s","%s","OUT","%s")'% (name, typ, dep))
449       outstream = "\n".join(streams)
450
451       init = pyinitService.substitute(component=self.name, service=serv.name,
452                                       instream=instream, outstream=outstream)
453       services.append(service)
454       inits.append(init)
455
456     python_path = ",".join([repr(p) for p in self.python_path])
457     argv = ",".join([repr(p) for p in self.argv])
458     return asterCompo.substitute(component=self.name, module=gen.module.name,
459                                  servicesdef="\n".join(defs), 
460                                  servicesimpl="\n".join(services), 
461                                  initservice='\n'.join(inits),
462                                  aster_dir=self.aster_dir, 
463                                  python_path=python_path, argv=argv)
464