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