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