Salome HOME
sat #17766 et sat #17848 : traitement des modules par ordre de dépendance, correction...
[tools/sat.git] / src / environment.py
1 #!/usr/bin/env python
2 #-*- coding:utf-8 -*-
3 #  Copyright (C) 2010-2013  CEA/DEN
4 #
5 #  This library is free software; you can redistribute it and/or
6 #  modify it under the terms of the GNU Lesser General Public
7 #  License as published by the Free Software Foundation; either
8 #  version 2.1 of the License.
9 #
10 #  This library is distributed in the hope that it will be useful,
11 #  but WITHOUT ANY WARRANTY; without even the implied warranty of
12 #  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13 #  Lesser General Public License for more details.
14 #
15 #  You should have received a copy of the GNU Lesser General Public
16 #  License along with this library; if not, write to the Free Software
17 #  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
18
19 import os
20 import subprocess
21 import string
22 import sys
23 import copy
24
25 import src
26 import src.debug as DBG
27 import pprint as PP
28
29
30 class Environ:
31     """\
32     Class to manage the environment context
33     """
34     def __init__(self, environ=None):
35         """Initialization. If the environ argument is passed, the environment
36            will be add to it, else it is the external environment.
37            
38         :param environ dict:  
39         """
40         if environ is not None:
41             self.environ = environ
42         else:
43             self.environ = os.environ
44
45     def __repr__(self):
46         """easy non exhaustive quick resume for debug print"""
47         return "%s(\n%s\n)" % (self.__class__.__name__, PP.pformat(self.environ))
48
49     def _expandvars(self, value):
50         """\
51         replace some $VARIABLE into its actual value in the environment
52         
53         :param value str: the string to be replaced
54         :return: the replaced variable
55         :rtype: str
56         """
57         if "$" in value:
58             # The string.Template class is a string class 
59             # for supporting $-substitutions
60             zt = string.Template(value)
61             try:
62                 value = zt.substitute(self.environ)
63             except KeyError as exc:
64                 pass
65                 #raise src.SatException(_("Missing definition "
66                 #                         "in environment: %s") % str(exc))
67         return value
68
69     def append_value(self, key, value, sep=os.pathsep):
70         """\
71         append value to key using sep,
72         if value contains ":" or ";" then raise error
73
74         :param key str: the environment variable to append
75         :param value str: the value to append to key
76         :param sep str: the separator string
77         """
78         # check that value so no contain the system separator
79         separator=os.pathsep
80         if separator in value:
81             raise Exception("Environ append key '%s' value '%s' contains forbidden character '%s'" % (key, value, separator))
82
83         # check if the key is already in the environment
84         if key in self.environ:
85             value_list = self.environ[key].split(sep)
86             # Check if the value is already in the key value or not
87             if not value in value_list:
88                 value_list.append(value)
89             else:
90                 value_list.append(value_list.pop(value_list.index(value)))
91             self.set(key, sep.join(value_list))
92         else:
93             self.set(key, value)
94
95     def append(self, key, value, sep=os.pathsep):
96         """\
97         Same as append_value but the value argument can be a list
98         
99         :param key str: the environment variable to append
100         :param value str or list: the value(s) to append to key
101         :param sep str: the separator string
102         """
103         if isinstance(value, list):
104             for v in value:
105                 self.append_value(key, v, sep)
106         else:
107             self.append_value(key, value, sep)
108
109     def prepend_value(self, key, value, sep=os.pathsep):
110         """\
111         prepend value to key using sep,
112         if value contains ":" or ";" then raise error
113         
114         :param key str: the environment variable to prepend
115         :param value str: the value to prepend to key
116         :param sep str: the separator string
117         """
118         # check that value so no contain the system separator
119         separator=os.pathsep
120         if separator in value:
121             raise Exception("Environ append key '%s' value '%s' contains forbidden character '%s'" % (key, value, separator))
122
123         # check if the key is already in the environment
124         if key in self.environ:
125             value_list = self.environ[key].split(sep)
126             if not value in value_list:
127                 value_list.insert(0, value)
128             else:
129                 value_list.insert(0, value_list.pop(value_list.index(value)))
130             self.set(key, sep.join(value_list))
131         else:
132             self.set(key, value)
133
134     def prepend(self, key, value, sep=os.pathsep):
135         """\
136         Same as prepend_value but the value argument can be a list
137         
138         :param key str: the environment variable to prepend
139         :param value str or list: the value(s) to prepend to key
140         :param sep str: the separator string
141         """
142         if isinstance(value, list):
143             for v in reversed(value): # prepend list, first item at last to stay first
144                 self.prepend_value(key, v, sep)
145         else:
146             self.prepend_value(key, value, sep)
147
148     def is_defined(self, key):
149         """\
150         Check if the key exists in the environment
151         
152         :param key str: the environment variable to check
153         """
154         return key in self.environ.keys()
155
156     def set(self, key, value):
157         """\
158         Set the environment variable "key" to value "value"
159         
160         :param key str: the environment variable to set
161         :param value str: the value
162         """
163         self.environ[key] = self._expandvars(value)
164
165     def get(self, key):
166         """\
167         Get the value of the environment variable "key"
168         
169         :param key str: the environment variable
170         """
171         if key in self.environ:
172             return self.environ[key]
173         else:
174             return ""
175
176     def get_value(self, key):
177         """\
178         Get the value of the environment variable "key"
179         This method is added for API compatibility with FileEnviron class
180         
181         :param key str: the environment variable
182         """
183         return self.get(key)
184
185
186
187 class SalomeEnviron:
188     """\
189     Class to manage the environment of SALOME.
190     """
191     def __init__(self,
192                  cfg,
193                  environ,
194                  forBuild=False,
195                  for_package=None,
196                  enable_simple_env_script = True):
197         """\
198         Initialization.
199
200         :param cfg Config: the global config
201         :param environ Environ: the Environ instance where 
202                                 to store the environment variables
203         :param forBuild bool: If true, it is a launch environment, 
204                               else a build one
205         :param for_package str: If not None, produce a relative environment 
206                                 designed for a package. 
207         """
208         self.environ = environ
209         self.cfg = cfg
210         self.forBuild = forBuild
211         self.for_package = for_package
212         self.enable_simple_env_script = enable_simple_env_script
213         self.silent = False
214         self.has_python = False
215         self.__set_sorted_products_list()
216
217     def __repr__(self):
218         """easy almost exhaustive quick resume for debug print"""
219         res = {
220           "environ" : self.environ,
221           "forBuild" : self.forBuild,
222           "for_package" : self.for_package,
223         }
224         return "%s(\n%s\n)" % (self.__class__.__name__, PP.pformat(res))
225
226     def __set_sorted_products_list(self):
227         from compile import get_dependencies_graph, depth_first_topo_graph
228         all_products_infos = src.product.get_products_infos(
229                                  self.cfg.APPLICATION.products,
230                                  self.cfg)
231         
232         all_products_graph=get_dependencies_graph(all_products_infos)
233         visited_nodes=[]
234         sorted_nodes=[]
235         for n in all_products_graph:
236             if n not in visited_nodes:
237                 visited_nodes,sorted_nodes=depth_first_topo_graph(
238                                                all_products_graph, 
239                                                n, 
240                                                visited_nodes,
241                                                sorted_nodes)
242         self.sorted_product_list=sorted_nodes
243
244
245     def append(self, key, value, sep=os.pathsep):
246         """\
247         append value to key using sep
248         
249         :param key str: the environment variable to append
250         :param value str: the value to append to key
251         :param sep str: the separator string
252         """
253         return self.environ.append(key, value, sep)
254
255     def prepend(self, key, value, sep=os.pathsep):
256         """\
257         prepend value to key using sep
258         
259         :param key str: the environment variable to prepend
260         :param value str: the value to prepend to key
261         :param sep str: the separator string
262         """
263         return self.environ.prepend(key, value, sep)
264
265     def is_defined(self, key):
266         """\
267         Check if the key exists in the environment
268         
269         :param key str: the environment variable to check
270         """
271         return self.environ.is_defined(key)
272
273     def get(self, key):
274         """\
275         Get the value of the environment variable "key"
276         
277         :param key str: the environment variable
278         """
279         return self.environ.get(key)
280
281     def get_value(self, key):
282         """\
283         Get the real value of the environment variable "key"
284         This method is added for API compatibility with FileEnviron class
285         
286         :param key str: the environment variable
287         """
288         if key in self.environ:
289             return self.environ[key]
290         else:
291             return ""
292
293     def set(self, key, value):
294         """\
295         Set the environment variable "key" to value "value"
296         
297         :param key str: the environment variable to set
298         :param value str: the value
299         """
300         # check if value needs to be evaluated
301         if value is not None and value.startswith("`") and value.endswith("`"):
302             res = subprocess.Popen("echo %s" % value,
303                                    shell=True,
304                                    stdout=subprocess.PIPE).communicate()
305             value = res[0].strip()
306
307         return self.environ.set(key, value)
308
309     def dump(self, out):
310         """\
311         Write the environment to out
312         
313         :param out file: the stream where to write the environment
314         """
315         for k in self.environ.environ.keys():
316             try:
317                 value = self.get(k)
318             except:
319                 value = "?"
320             out.write("%s=%s\n" % (k, value))
321
322     def add_line(self, nb_line):
323         """\
324         Add empty lines to the out stream (in case of file generation)
325         
326         :param nb_line int: the number of empty lines to add
327         """
328         if 'add_line' in dir(self.environ):
329             self.environ.add_line(nb_line)
330
331     def add_comment(self, comment):
332         """\
333         Add a commentary to the out stream (in case of file generation)
334         
335         :param comment str: the commentary to add
336         """
337         if 'add_comment' in dir(self.environ):
338             self.environ.add_comment(comment)
339
340     def add_warning(self, warning):
341         """\
342         Add a warning to the out stream (in case of file generation)
343         
344         :param warning str: the warning to add
345         """
346         if 'add_warning' in dir(self.environ):
347             self.environ.add_warning(warning)
348
349     def finish(self):
350         """\
351         Add a final instruction in the out file (in case of file generation)
352         
353         :param required bool: Do nothing if required is False
354         """
355         if 'finish' in dir(self.environ):
356             self.environ.add_line(1)
357             # what for ?
358             # self.environ.add_comment("clean all the path")
359             self.environ.finish()
360
361     def set_python_libdirs(self):
362         """Set some generic variables for python library paths"""
363         ver = self.get('PYTHON_VERSION')
364         self.set('PYTHON_LIBDIR', os.path.join('lib',
365                                                 'python' + ver,
366                                                 'site-packages'))
367         self.python_lib = self.get('PYTHON_LIBDIR')
368         self.has_python = True
369
370     def set_application_env(self, logger):
371         """\
372         Sets the environment defined in the APPLICATION file.
373         
374         :param logger Logger: The logger instance to display messages
375         """
376         
377         if self.for_package:
378            self.set("PRODUCT_ROOT_DIR", "out_dir_Path")
379         else:
380            self.cfg.APPLICATION.environ.PRODUCT_ROOT_DIR = src.pyconf.Reference(self.cfg, src.pyconf.DOLLAR, "workdir")
381
382         # these sensitive variables are reset to avoid bad environment interactions
383         self.add_line(1)
384         self.add_comment("reset these sensitive variables to avoid bad environment interactions")
385         self.add_comment("comment these to lines if you wish a different behaviour")
386         self.set("LD_LIBRARY_PATH", "")
387         self.set("PYTHONPATH", "")
388         self.add_line(1)
389
390         # Set the variables defined in the "environ" section
391         if 'environ' in self.cfg.APPLICATION:
392             # we write PRODUCT environment it in order to conform to 
393             # parseConfigFile.py
394             self.add_comment("PRODUCT environment") 
395             self.load_cfg_environment(self.cfg.APPLICATION.environ)
396             if self.forBuild and "build" in self.cfg.APPLICATION.environ:
397                 self.load_cfg_environment(self.cfg.APPLICATION.environ.build)
398             if not self.forBuild and "launch" in self.cfg.APPLICATION.environ:
399                 self.load_cfg_environment(self.cfg.APPLICATION.environ.launch)
400             self.add_line(1)
401
402
403     def set_salome_minimal_product_env(self, product_info, logger):
404         """\
405         Sets the minimal environment for a SALOME product.
406         xxx_ROOT_DIR and xxx_SRC_DIR
407         
408         :param product_info Config: The product description
409         :param logger Logger: The logger instance to display messages        
410         """
411         # set root dir
412         DBG.write("set_salome_minimal_product_env", product_info)
413         root_dir = product_info.name + "_ROOT_DIR"
414         if 'install_dir' in product_info and product_info.install_dir:
415             self.set(root_dir, product_info.install_dir)
416         elif not self.silent:
417             logger.write("  " + _("No install_dir for product %s\n") %
418                           product_info.name, 5)
419     
420         source_in_package = src.get_property_in_product_cfg(product_info,
421                                                            "sources_in_package")
422         if not self.for_package or source_in_package == "yes":
423             # set source dir, unless no source dir
424             if not src.product.product_is_fixed(product_info):
425                 src_dir = product_info.name + "_SRC_DIR"
426                 if not self.for_package:
427                     self.set(src_dir, product_info.source_dir)
428                 else:
429                     self.set(src_dir, os.path.join("out_dir_Path",
430                              "SOURCES",
431                              os.path.basename(product_info.source_dir)))
432
433     def expand_salome_modules(self, pi):
434         if 'component_name' in pi:
435             compo_name = pi.component_name
436         else:
437             compo_name = pi.name
438         self.append('SALOME_MODULES', compo_name, ',')
439         
440         
441     def set_salome_generic_product_env(self, pi):
442         """\
443         Sets the generic environment for a SALOME product.
444         
445         :param pi Config: The product description
446         """
447         # Construct XXX_ROOT_DIR
448         env_root_dir = self.get(pi.name + "_ROOT_DIR")
449         l_binpath_libpath = []
450
451         # create additional ROOT_DIR for CPP components
452         if 'component_name' in pi:
453             compo_name = pi.component_name
454             if compo_name + "CPP" == pi.name:
455                 compo_root_dir = compo_name + "_ROOT_DIR"
456                 envcompo_root_dir = os.path.join(
457                             self.cfg.TOOLS.common.install_root, compo_name )
458                 self.set(compo_root_dir ,  envcompo_root_dir)
459                 bin_path = os.path.join(envcompo_root_dir, 'bin', 'salome')
460                 lib_path = os.path.join(envcompo_root_dir, 'lib', 'salome')
461                 l_binpath_libpath.append( (bin_path, lib_path) )
462
463
464         if src.get_property_in_product_cfg(pi, "fhs"):
465             lib_path = os.path.join(env_root_dir, 'lib')
466             bin_path = os.path.join(env_root_dir, 'bin')
467             if self.has_python:
468             # if the application doesn't include python, we don't need these two lines
469                 pylib_path = os.path.join(env_root_dir, self.python_lib)
470         else:
471             lib_path = os.path.join(env_root_dir, 'lib', 'salome')
472             bin_path = os.path.join(env_root_dir, 'bin', 'salome')
473             if self.has_python:
474             # if the application doesn't include python, we don't need these two lines
475                 pylib_path = os.path.join(env_root_dir, self.python_lib, 'salome')
476
477         # Construct the paths to prepend to PATH and LD_LIBRARY_PATH and 
478         # PYTHONPATH
479         l_binpath_libpath.append( (bin_path, lib_path) )
480
481         for bin_path, lib_path in l_binpath_libpath:
482             if not self.forBuild:
483                 self.prepend('PATH', bin_path)
484                 if src.architecture.is_windows():
485                     self.prepend('PATH', lib_path)
486                 else :
487                     self.prepend('LD_LIBRARY_PATH', lib_path)
488
489             l = [ bin_path, lib_path ]
490             if not src.product.product_is_wheel(pi):
491                 if self.has_python:
492                     l.append(pylib_path)
493                 self.prepend('PYTHONPATH', l)
494
495     def set_cpp_env(self, product_info):
496         """\
497         Sets the generic environment for a SALOME cpp product.
498         
499         :param product_info Config: The product description
500         """
501         # Construct XXX_ROOT_DIR
502         env_root_dir = self.get(product_info.name + "_ROOT_DIR")
503         l_binpath_libpath = []
504
505         # Construct the paths to prepend to PATH and LD_LIBRARY_PATH and 
506         # PYTHONPATH
507         bin_path = os.path.join(env_root_dir, 'bin')
508         lib_path = os.path.join(env_root_dir, 'lib')
509         l_binpath_libpath.append( (bin_path, lib_path) )
510
511         for bin_path, lib_path in l_binpath_libpath:
512             if not self.forBuild:
513                 self.prepend('PATH', bin_path)
514                 if src.architecture.is_windows():
515                     self.prepend('PATH', lib_path)
516                 else :
517                     self.prepend('LD_LIBRARY_PATH', lib_path)
518
519             l = [ bin_path, lib_path ]
520             if self.has_python:
521                 l.append(os.path.join(env_root_dir, self.python_lib))
522             self.prepend('PYTHONPATH', l)
523
524     def load_cfg_environment(self, cfg_env):
525         """\
526         Loads environment defined in cfg_env 
527         
528         :param cfg_env Config: A config containing an environment    
529         """
530         # Loop on cfg_env values
531         for env_def in cfg_env:
532             val = cfg_env[env_def]
533             
534             # if it is env_script, do not do anything (reserved keyword)
535             if env_def == "env_script":
536                 continue
537             
538             # if it is a dict, do not do anything
539             if isinstance(val, src.pyconf.Mapping):
540                 continue
541
542             # if it is a list, loop on its values
543             if isinstance(val, src.pyconf.Sequence):
544                 # transform into list of strings
545                 l_val = []
546                 for item in val:
547                     l_val.append(item)
548                 val = l_val
549
550             # "_" means that the value must be prepended
551             if env_def.startswith("_"):
552                 # separator exception for PV_PLUGIN_PATH
553                 if env_def[1:] == 'PV_PLUGIN_PATH':
554                     self.prepend(env_def[1:], val, ';')
555                 else:
556                     self.prepend(env_def[1:], val)
557             elif env_def.endswith("_"):
558                 # separator exception for PV_PLUGIN_PATH
559                 if env_def[:-1] == 'PV_PLUGIN_PATH':
560                     self.append(env_def[:-1], val, ';')
561                 else:
562                     self.append(env_def[:-1], val)
563             else:
564                 self.set(env_def, val)
565
566     def set_a_product(self, product, logger):
567         """\
568         Sets the environment of a product. 
569         
570         :param product str: The product name
571         :param logger Logger: The logger instance to display messages
572         """
573
574         # Get the informations corresponding to the product
575         pi = src.product.get_product_config(self.cfg, product)
576         
577         # skip compile time products at run time 
578         if not self.forBuild:
579             if src.product.product_is_compile_time(pi):
580                 return
581
582         # skip pip products when pip is activated and installation is done in python 
583         #if (src.appli_test_property(self.cfg,"pip", "yes") and 
584         #    src.product.product_test_property(pi,"pip", "yes") and
585         #    src.appli_test_property(self.cfg,"pip_install_dir", "python") ):
586         #        return
587
588         # skip mesa products (if any) at run time, 
589         # unless use_mesa property was activated
590         if not self.forBuild:
591             if not ("APPLICATION" in self.cfg  and
592                     "properties" in self.cfg.APPLICATION  and
593                     "use_mesa" in self.cfg.APPLICATION.properties  and
594                     self.cfg.APPLICATION.properties.use_mesa == "yes") :
595                 if ("properties" in pi and
596                     "is_mesa" in pi.properties  and
597                     pi.properties.is_mesa == "yes") :
598                     logger.write(_("Skip mesa product %s\n") % pi.name, 4)
599                     return
600                
601         
602         if self.for_package:
603             pi.install_dir = os.path.join(
604                                  "out_dir_Path",
605                                  self.for_package,
606                                  os.path.basename(pi.install_dir))
607
608         if not self.silent:
609             logger.write(_("Setting environment for %s\n") % product, 4)
610
611         self.add_line(1)
612         self.add_comment('setting environ for ' + product)
613             
614         # Do not define environment if the product is native
615         if src.product.product_is_native(pi):
616             if src.product.product_has_env_script(pi):
617                 self.run_env_script(pi, native=True)
618             return
619                
620         # Set an additional environment for SALOME products
621         if src.product.product_is_salome(pi):
622             # set environment using definition of the product
623             self.set_salome_minimal_product_env(pi, logger)
624             self.set_salome_generic_product_env(pi)
625            
626         
627         # Expand SALOME_MODULES variable for products which have a salome gui
628         if src.product.product_has_salome_gui(pi):
629             self.expand_salome_modules(pi)
630
631         # use variable LICENCE_FILE to communicate the licence file name to the environment script
632         licence_file_name = src.product.product_has_licence(pi, self.cfg.PATHS.LICENCEPATH)
633         if licence_file_name:
634             logger.write("licence file found for product %s : %s\n" % (pi.name, licence_file_name), 5) 
635             self.set("LICENCE_FILE", licence_file_name)
636
637         if src.product.product_is_cpp(pi):
638             # set a specific environment for cpp modules
639             self.set_salome_minimal_product_env(pi, logger)
640             self.set_cpp_env(pi)
641             
642             if src.product.product_is_generated(pi):
643                 if "component_name" in pi:
644                     # hack the source and install directories in order to point  
645                     # on the generated product source install directories
646                     install_dir_save = pi.install_dir
647                     source_dir_save = pi.source_dir
648                     name_save = pi.name
649                     pi.install_dir = os.path.join(self.cfg.APPLICATION.workdir,
650                                                   self.cfg.INTERNAL.config.install_dir,
651                                                   pi.component_name)
652                     if self.for_package:
653                         pi.install_dir = os.path.join("out_dir_Path",
654                                                       self.for_package,
655                                                       pi.component_name)
656                     pi.source_dir = os.path.join(self.cfg.APPLICATION.workdir,
657                                                   "GENERATED",
658                                                   pi.component_name)
659                     pi.name = pi.component_name
660                     self.set_salome_minimal_product_env(pi, logger)
661                     self.set_salome_generic_product_env(pi)
662                     
663                     # Put original values
664                     pi.install_dir = install_dir_save
665                     pi.source_dir = source_dir_save
666                     pi.name = name_save
667         
668         # Put the environment define in the configuration of the product
669         if "environ" in pi:
670             self.load_cfg_environment(pi.environ)
671             if self.forBuild and "build" in pi.environ:
672                 self.load_cfg_environment(pi.environ.build)
673             if not self.forBuild and "launch" in pi.environ:
674                 self.load_cfg_environment(pi.environ.launch)
675             # if product_info defines a env_scripts, load it
676             if 'env_script' in pi.environ:
677                 self.run_env_script(pi, logger)
678
679         
680             
681
682     def run_env_script(self, product_info, logger=None, native=False):
683         """\
684         Runs an environment script. 
685         
686         :param product_info Config: The product description
687         :param logger Logger: The logger instance to display messages
688         :param native Boolean: if True load set_native_env instead of set_env
689         """
690         env_script = product_info.environ.env_script
691         # Check that the script exists
692         if not os.path.exists(env_script):
693             raise src.SatException(_("Environment script not found: %s") % 
694                                    env_script)
695
696         if not self.silent and logger is not None:
697             logger.write("  ** load %s\n" % env_script, 4)
698
699         # import the script and run the set_env function
700         try:
701             import imp
702             pyproduct = imp.load_source(product_info.name + "_env_script",
703                                         env_script)
704             if not native:
705                 if self.forBuild and "set_env_build" in dir(pyproduct):
706                     pyproduct.set_env_build(self,
707                                             product_info.install_dir,
708                                             product_info.version)
709                 elif (not self.forBuild) and "set_env_launch" in dir(pyproduct):
710                     pyproduct.set_env_launch(self,
711                                             product_info.install_dir,
712                                             product_info.version)
713                 else:
714                     # at least this one is mandatory,
715                     # if set_env_build and set_env_build are not defined
716                     pyproduct.set_env(self,
717                                       product_info.install_dir,
718                                       product_info.version)
719             else:
720                 # not mandatory, if set_nativ_env not defined, we do nothing
721                 if "set_nativ_env" in dir(pyproduct):
722                     pyproduct.set_nativ_env(self)
723         except:
724             __, exceptionValue, exceptionTraceback = sys.exc_info()
725             print(exceptionValue)
726             import traceback
727             traceback.print_tb(exceptionTraceback)
728             traceback.print_exc()
729
730     def set_products(self, logger, src_root=None):
731         """\
732         Sets the environment for all the products. 
733         
734         :param logger Logger: The logger instance to display messages
735         :param src_root src: the application working directory
736         """
737         self.add_line(1)
738         self.add_comment('setting environ for all products')
739
740         # Make sure that the python lib dirs are set after python
741         if "Python" in self.sorted_product_list:
742             self.set_a_product("Python", logger)
743             self.set_python_libdirs()
744
745         # The loop on the products
746         for product in self.sorted_product_list:
747             if product == "Python":
748                 continue
749             self.set_a_product(product, logger)
750  
751     def set_full_environ(self, logger, env_info):
752         """\
753         Sets the full environment for products 
754         specified in env_info dictionary. 
755         
756         :param logger Logger: The logger instance to display messages
757         :param env_info list: the list of products
758         """
759         DBG.write("set_full_environ for", env_info)
760         # DBG.write("set_full_environ config", self.cfg.APPLICATION.environ, True)
761
762         # set product environ
763         self.set_application_env(logger)
764
765         # use the sorted list of all products to sort the list of products 
766         # we have to set
767         sorted_product_list=[]
768         for n in self.sorted_nodes:
769             if n in env_info:
770                 sorted_product_list.append(n)
771
772         if "Python" in sorted_product_list:
773             self.set_a_product("Python", logger)
774             self.set_python_libdirs()
775
776         # set products
777         for product in sorted_product_list:
778             if product == "Python":
779                 continue
780             self.set_a_product(product, logger)
781
782 class FileEnvWriter:
783     """\
784     Class to dump the environment to a file.
785     """
786     def __init__(self, config, logger, out_dir, src_root, env_info=None):
787         """\
788         Initialization.
789
790         :param cfg Config: the global config
791         :param logger Logger: The logger instance to display messages
792         :param out_dir str: The directory path where t put the output files
793         :param src_root str: The application working directory
794         :param env_info str: The list of products to add in the files.
795         """
796         self.config = config
797         self.logger = logger
798         self.out_dir = out_dir
799         self.src_root= src_root
800         self.silent = True
801         self.env_info = env_info
802
803     def write_tcl_files(self,
804                         forBuild, 
805                         shell, 
806                         for_package = None,
807                         no_path_init=False,
808                         additional_env = {}):
809         """\
810         Create tcl environment files for environment module.
811         
812         :param forBuild bool: if true, the build environment
813         :param shell str: the type of file wanted (.sh, .bat)
814         :param for_package bool: if true do specific stuff for required for packages
815         :param no_path_init bool: if true generate a environ file that do not reinitialise paths
816         :param additional_env dict: contains sat_ prefixed variables to help the génération, 
817                                     and also variables to add in the environment.
818         :return: The path to the generated file
819         :rtype: str
820         """
821
822         # get the products informations
823         all_products=self.config.APPLICATION.products
824         products_infos = src.product.get_products_infos(all_products, self.config) 
825
826         # set a global environment (we need it to resolve variable references
827         # between dependent products
828         global_environ = src.environment.SalomeEnviron(self.config,
829                                   src.environment.Environ(additional_env),
830                                   False)
831         global_environ.set_products(self.logger)
832         
833         # The loop on the products
834         for product in all_products:
835             # create one file per product
836             pi = src.product.get_product_config(self.config, product)
837             if "base" not in pi:  # we write tcl files only for products in base
838                 continue
839
840             # get the global environment, and complete it with sat_ prefixed 
841             # prefixed variables which are used to transfer info to 
842             # TclFileEnviron class  
843             product_env = copy.deepcopy(global_environ.environ)
844             product_env.environ["sat_product_name"] = pi.name
845             product_env.environ["sat_product_version"] = pi.version
846             product_env.environ["sat_product_base_path"] = src.get_base_path(self.config)
847             product_env.environ["sat_product_base_name"] = pi.base
848    
849             # store infos in sat_product_load_depend to set dependencies in tcl file
850             sat_product_load_depend=""
851             for p_name,p_info in products_infos:
852                 if p_name in pi.depend:
853                     sat_product_load_depend+="module load %s/%s/%s;" % (pi.base, 
854                                                                         p_info.name, 
855                                                                         p_info.version)
856             if len(sat_product_load_depend)>0:
857                 # if there are dependencies, store the module to load (get rid of trailing ;)
858                 product_env.environ["sat_product_load_depend"]=sat_product_load_depend[0:-1]
859
860
861             env_file_name = os.path.join(product_env.environ["sat_product_base_path"], 
862                                          "modulefiles", 
863                                          product_env.environ["sat_product_base_name"],
864                                          product_env.environ["sat_product_name"], 
865                                          product_env.environ["sat_product_version"])
866             prod_dir_name=os.path.dirname(env_file_name)
867             if not os.path.isdir(prod_dir_name):
868                 os.makedirs(prod_dir_name)
869
870             env_file = open(env_file_name, "w")
871             file_environ = src.fileEnviron.get_file_environ(env_file,
872                                            "tcl", product_env)
873             env = SalomeEnviron(self.config, 
874                                 file_environ, 
875                                 False, 
876                                 for_package=for_package)
877             if "Python" in pi.depend:
878                 # short cut, env.python_lib is required by set_a_product for salome modules
879                 env.has_python="True"
880                 env.python_lib=global_environ.get("PYTHON_LIBDIR")
881             env.set_a_product(product, self.logger)
882             env_file.close()
883             if not self.silent:
884                 self.logger.write(_("    Create tcl module environment file %s\n") % 
885                                   src.printcolors.printcLabel(env_file_name), 3)
886
887
888     def write_env_file(self,
889                        filename,
890                        forBuild, 
891                        shell, 
892                        for_package = None,
893                        no_path_init=False,
894                        additional_env = {}):
895         """\
896         Create an environment file.
897         
898         :param filename str: the file path
899         :param forBuild bool: if true, the build environment
900         :param shell str: the type of file wanted (.sh, .bat)
901         :param for_package bool: if true do specific stuff for required for packages
902         :param no_path_init bool: if true generate a environ file that do not reinitialise paths
903         :param additional_env dict: contains sat_ prefixed variables to help the génération, 
904                                     and also variables to add in the environment.
905         :return: The path to the generated file
906         :rtype: str
907         """
908         if not self.silent:
909             self.logger.write(_("Create environment file %s\n") % 
910                               src.printcolors.printcLabel(filename), 3)
911
912         # create then env object
913         env_file = open(os.path.join(self.out_dir, filename), "w")
914
915         # we duplicate additional_env, and transmit it to fileEnviron, which will use its sat_ prefixed variables.
916         # the other variables of additional_env are added to the environement file at the end of this function.
917         salome_env = copy.deepcopy(additional_env)
918         file_environ = src.fileEnviron.get_file_environ(env_file,
919                                                shell,
920                                                src.environment.Environ(salome_env))
921         if no_path_init:
922             # specify we don't want to reinitialise paths
923             # path will keep the inherited value, which will be appended with new values.
924             file_environ.set_no_init_path()
925
926         env = SalomeEnviron(self.config, 
927                             file_environ, 
928                             forBuild, 
929                             for_package=for_package)
930
931         env.silent = self.silent
932
933         # Set the environment
934         if self.env_info is not None:
935             env.set_full_environ(self.logger, self.env_info)
936         else:
937             # set env from the APPLICATION
938             env.set_application_env(self.logger)
939             
940             # set the products
941             env.set_products(self.logger,
942                             src_root=self.src_root)
943
944         # Add the additional environment if it is not empty
945         if len(additional_env) != 0:
946             env.add_line(1)
947             env.add_comment("[APPLI variables]")
948             for variable in additional_env:
949                 if not variable.startswith("sat_"):
950                     # by convention variables starting with sat_ are used to transfer information, 
951                     # not to be written in env
952                     env.set(variable, additional_env[variable])
953
954         # finalise the writing and close the file
955         env.finish()
956         env_file.close()
957
958         return env_file.name
959    
960
961 class Shell:
962     """\
963     Definition of a Shell.
964     """
965     def __init__(self, name, extension):
966         """\
967         Initialization.
968
969         :param name str: the shell name
970         :param extension str: the shell extension
971         """
972         self.name = name
973         self.extension = extension
974
975 def load_environment(config, build, logger):
976     """\
977     Loads the environment (used to run the tests, for example).
978     
979     :param config Config: the global config
980     :param build bool: build environement if True
981     :param logger Logger: The logger instance to display messages
982     """
983     environ = SalomeEnviron(config, Environ(os.environ), build)
984     environ.set_application_env(logger)
985     environ.set_products(logger)