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