3 # Copyright (C) 2010-2013 CEA/DEN
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.
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.
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
25 import src.debug as DBG
29 '''Class to manage the environment context
31 def __init__(self, environ=None):
32 '''Initialization. If the environ argument is passed, the environment
33 will be add to it, else it is the external environment.
37 if environ is not None:
38 self.environ = environ
40 self.environ = os.environ
43 """easy non exhaustive quick resume for debug print
45 return "%s(\n%s\n)" % (self.__class__.__name__, PP.pformat(self.environ))
47 def _expandvars(self, value):
48 '''replace some $VARIABLE into its actual value in the environment
50 :param value str: the string to be replaced
51 :return: the replaced variable
55 # The string.Template class is a string class
56 # for supporting $-substitutions
57 zt = string.Template(value)
59 value = zt.substitute(self.environ)
60 except KeyError as exc:
61 raise src.SatException(_("Missing definition "
62 "in environment: %s") % str(exc))
65 def append_value(self, key, value, sep=os.pathsep):
66 '''append value to key using sep
68 :param key str: the environment variable to append
69 :param value str: the value to append to key
70 :param sep str: the separator string
72 # check if the key is already in the environment
73 if key in self.environ:
74 value_list = self.environ[key].split(sep)
75 # Check if the value is already in the key value or not
76 if not value in value_list:
77 value_list.append(value)
79 value_list.append(value_list.pop(value_list.index(value)))
80 self.set(key, sep.join(value_list))
84 def append(self, key, value, sep=os.pathsep):
85 '''Same as append_value but the value argument can be a list
87 :param key str: the environment variable to append
88 :param value str or list: the value(s) to append to key
89 :param sep str: the separator string
91 if isinstance(value, list):
93 self.append_value(key, v, sep)
95 self.append_value(key, value, sep)
97 def prepend_value(self, key, value, sep=os.pathsep):
98 '''prepend value to key using sep
100 :param key str: the environment variable to prepend
101 :param value str: the value to prepend to key
102 :param sep str: the separator string
104 if key in self.environ:
105 value_list = self.environ[key].split(sep)
106 if not value in value_list:
107 value_list.insert(0, value)
109 value_list.insert(0, value_list.pop(value_list.index(value)))
110 self.set(key, sep.join(value_list))
114 def prepend(self, key, value, sep=os.pathsep):
115 '''Same as prepend_value but the value argument can be a list
117 :param key str: the environment variable to prepend
118 :param value str or list: the value(s) to prepend to key
119 :param sep str: the separator string
121 if isinstance(value, list):
123 self.prepend_value(key, v, sep)
125 self.prepend_value(key, value, sep)
127 def is_defined(self, key):
128 '''Check if the key exists in the environment
130 :param key str: the environment variable to check
132 return key in self.environ.keys()
134 def set(self, key, value):
135 '''Set the environment variable "key" to value "value"
137 :param key str: the environment variable to set
138 :param value str: the value
140 self.environ[key] = self._expandvars(value)
143 '''Get the value of the environment variable "key"
145 :param key str: the environment variable
147 if key in self.environ:
148 return self.environ[key]
152 def command_value(self, key, command):
153 '''Get the value given by the system command "command"
154 and put it in the environment variable key
156 :param key str: the environment variable
157 :param command str: the command to execute
159 value = subprocess.Popen(command,
161 stdout=subprocess.PIPE,
162 env=self.environ).communicate()[0]
163 self.environ[key] = value
167 """Class to manage the environment of SALOME.
175 enable_simple_env_script = True):
178 :param cfg Config: the global config
179 :param environ Environ: the Environ instance where
180 to store the environment variables
181 :param forBuild bool: If true, it is a launch environment,
183 :param for_package str: If not None, produce a relative environment
184 designed for a package.
186 self.environ = environ
188 self.forBuild = forBuild
189 self.for_package = for_package
190 self.enable_simple_env_script = enable_simple_env_script
194 """easy almost exhaustive quick resume for debug print"""
196 "environ" : self.environ,
197 "forBuild" : self.forBuild,
198 "for_package" : self.for_package,
200 return "%s(\n%s\n)" % (self.__class__.__name__, PP.pformat(res))
202 def append(self, key, value, sep=os.pathsep):
203 '''append value to key using sep
205 :param key str: the environment variable to append
206 :param value str: the value to append to key
207 :param sep str: the separator string
209 return self.environ.append(key, value, sep)
211 def prepend(self, key, value, sep=os.pathsep):
212 '''prepend value to key using sep
214 :param key str: the environment variable to prepend
215 :param value str: the value to prepend to key
216 :param sep str: the separator string
218 return self.environ.prepend(key, value, sep)
220 def is_defined(self, key):
221 '''Check if the key exists in the environment
223 :param key str: the environment variable to check
225 return self.environ.is_defined(key)
228 '''Get the value of the environment variable "key"
230 :param key str: the environment variable
232 return self.environ.get(key)
234 def set(self, key, value):
235 '''Set the environment variable "key" to value "value"
237 :param key str: the environment variable to set
238 :param value str: the value
240 # check if value needs to be evaluated
241 if value is not None and value.startswith("`") and value.endswith("`"):
242 res = subprocess.Popen("echo %s" % value,
244 stdout=subprocess.PIPE).communicate()
245 value = res[0].strip()
247 return self.environ.set(key, value)
250 """Write the environment to out
252 :param out file: the stream where to write the environment
254 for k in self.environ.environ.keys():
259 out.write("%s=%s\n" % (k, value))
261 def add_line(self, nb_line):
262 """Add empty lines to the out stream (in case of file generation)
264 :param nb_line int: the number of empty lines to add
266 if 'add_line' in dir(self.environ):
267 self.environ.add_line(nb_line)
269 def add_comment(self, comment):
270 """Add a commentary to the out stream (in case of file generation)
272 :param comment str: the commentary to add
274 if 'add_comment' in dir(self.environ):
275 self.environ.add_comment(comment)
277 def add_warning(self, warning):
278 """Add a warning to the out stream (in case of file generation)
280 :param warning str: the warning to add
282 if 'add_warning' in dir(self.environ):
283 self.environ.add_warning(warning)
285 def finish(self, required):
286 """Add a final instruction in the out file (in case of file generation)
288 :param required bool: Do nothing if required is False
290 if 'finish' in dir(self.environ):
291 self.environ.add_line(1)
292 self.environ.add_comment("clean all the path")
293 self.environ.finish(required)
295 def set_python_libdirs(self):
296 """Set some generic variables for python library paths
298 ver = self.get('PYTHON_VERSION')
299 self.set('PYTHON_LIBDIR0', os.path.join('lib',
302 self.set('PYTHON_LIBDIR1', os.path.join('lib64',
306 self.python_lib0 = self.get('PYTHON_LIBDIR0')
307 self.python_lib1 = self.get('PYTHON_LIBDIR1')
309 def get_names(self, lProducts):
310 """Get the products name to add in SALOME_MODULES environment variable
311 It is the name of the product, except in the case where the is a
312 component name. And it has to be in SALOME_MODULES variable only
313 if the product has the property has_salome_hui = "yes"
315 :param lProducts list: List of products to potentially add
317 lProdHasGui = [p for p in lProducts if 'properties' in
318 src.product.get_product_config(self.cfg, p) and
320 src.product.get_product_config(self.cfg, p).properties and
321 src.product.get_product_config(self.cfg,
322 p).properties.has_salome_gui=='yes']
324 for ProdName in lProdHasGui:
325 pi = src.product.get_product_config(self.cfg, ProdName)
326 if 'component_name' in pi:
327 lProdName.append(pi.component_name)
329 lProdName.append(ProdName)
332 def set_application_env(self, logger):
333 """Sets the environment defined in the APPLICATION file.
335 :param logger Logger: The logger instance to display messages
338 # add variable PRODUCT_ROOT_DIR as $workdir in APPLICATION.environ section if not present
340 tmp = self.cfg.APPLICATION.environ.PRODUCT_ROOT_DIR
342 self.cfg.APPLICATION.environ.PRODUCT_ROOT_DIR = src.pyconf.Reference(self.cfg, src.pyconf.DOLLAR, "workdir")
343 DBG.write("set_application_env add default Config.APPLICATION.environ.PRODUCT_ROOT_DIR", self.cfg.APPLICATION.environ)
345 # Set the variables defined in the "environ" section
346 if 'environ' in self.cfg.APPLICATION:
347 # we write PRODUCT environment it in order to conform to
349 self.add_comment("PRODUCT environment")
350 self.load_cfg_environment(self.cfg.APPLICATION.environ)
351 if self.forBuild and "build" in self.cfg.APPLICATION.environ:
352 self.load_cfg_environment(self.cfg.APPLICATION.environ.build)
353 if not self.forBuild and "launch" in self.cfg.APPLICATION.environ:
354 self.load_cfg_environment(self.cfg.APPLICATION.environ.launch)
357 # If there is an "environ_script" section, load the scripts
358 if 'environ_script' in self.cfg.APPLICATION:
359 for pscript in self.cfg.APPLICATION.environ_script:
360 self.add_comment("script %s" % pscript)
361 sname = pscript.replace(" ", "_")
362 self.run_env_script("APPLICATION_%s" % sname,
363 self.cfg.APPLICATION.environ_script[pscript],
367 def set_salome_minimal_product_env(self, product_info, logger):
368 """Sets the minimal environment for a SALOME product.
369 xxx_ROOT_DIR and xxx_SRC_DIR
371 :param product_info Config: The product description
372 :param logger Logger: The logger instance to display messages
375 DBG.write("set_salome_minimal_product_env", product_info)
376 root_dir = product_info.name + "_ROOT_DIR"
377 if not self.is_defined(root_dir):
378 if 'install_dir' in product_info and product_info.install_dir:
379 self.set(root_dir, product_info.install_dir)
380 elif not self.silent:
381 logger.write(" " + _("No install_dir for product %s\n") %
382 product_info.name, 5)
384 source_in_package = src.get_property_in_product_cfg(product_info,
385 "sources_in_package")
386 if not self.for_package or source_in_package == "yes":
387 # set source dir, unless no source dir
388 if not src.product.product_is_fixed(product_info):
389 src_dir = product_info.name + "_SRC_DIR"
390 if not self.is_defined(src_dir):
391 if not self.for_package:
392 self.set(src_dir, product_info.source_dir)
394 self.set(src_dir, os.path.join("out_dir_Path",
398 def set_salome_generic_product_env(self, pi):
399 """Sets the generic environment for a SALOME product.
401 :param pi Config: The product description
403 # Construct XXX_ROOT_DIR
404 env_root_dir = self.get(pi.name + "_ROOT_DIR")
405 l_binpath_libpath = []
407 # create additional ROOT_DIR for CPP components
408 if 'component_name' in pi:
409 compo_name = pi.component_name
410 if compo_name + "CPP" == pi.name:
411 compo_root_dir = compo_name + "_ROOT_DIR"
412 envcompo_root_dir = os.path.join(
413 self.cfg.TOOLS.common.install_root, compo_name )
414 self.set(compo_root_dir , envcompo_root_dir)
415 bin_path = os.path.join(envcompo_root_dir, 'bin', 'salome')
416 lib_path = os.path.join(envcompo_root_dir, 'lib', 'salome')
417 l_binpath_libpath.append( (bin_path, lib_path) )
419 if src.get_property_in_product_cfg(pi, "fhs"):
420 lib_path = os.path.join(env_root_dir, 'lib')
421 pylib1_path = os.path.join(env_root_dir, self.python_lib0)
422 pylib2_path = os.path.join(env_root_dir, self.python_lib1)
423 bin_path = os.path.join(env_root_dir, 'bin')
425 lib_path = os.path.join(env_root_dir, 'lib', 'salome')
426 pylib1_path = os.path.join(env_root_dir, self.python_lib0, 'salome')
427 pylib2_path = os.path.join(env_root_dir, self.python_lib1, 'salome')
428 bin_path = os.path.join(env_root_dir, 'bin', 'salome')
430 # Construct the paths to prepend to PATH and LD_LIBRARY_PATH and
432 l_binpath_libpath.append( (bin_path, lib_path) )
434 for bin_path, lib_path in l_binpath_libpath:
435 if not self.forBuild:
436 self.prepend('PATH', bin_path)
437 if src.architecture.is_windows():
438 self.prepend('PATH', lib_path)
440 self.prepend('LD_LIBRARY_PATH', lib_path)
442 l = [ bin_path, lib_path, pylib1_path, pylib2_path ]
443 self.prepend('PYTHONPATH', l)
445 def set_cpp_env(self, product_info):
446 """Sets the generic environment for a SALOME cpp product.
448 :param product_info Config: The product description
450 # Construct XXX_ROOT_DIR
451 env_root_dir = self.get(product_info.name + "_ROOT_DIR")
452 l_binpath_libpath = []
454 # Construct the paths to prepend to PATH and LD_LIBRARY_PATH and
456 bin_path = os.path.join(env_root_dir, 'bin')
457 lib_path = os.path.join(env_root_dir, 'lib')
458 l_binpath_libpath.append( (bin_path, lib_path) )
460 for bin_path, lib_path in l_binpath_libpath:
461 if not self.forBuild:
462 self.prepend('PATH', bin_path)
463 if src.architecture.is_windows():
464 self.prepend('PATH', lib_path)
466 self.prepend('LD_LIBRARY_PATH', lib_path)
468 l = [ bin_path, lib_path,
469 os.path.join(env_root_dir, self.python_lib0),
470 os.path.join(env_root_dir, self.python_lib1)
472 self.prepend('PYTHONPATH', l)
474 def load_cfg_environment(self, cfg_env):
475 """Loads environment defined in cfg_env
477 :param cfg_env Config: A config containing an environment
479 # Loop on cfg_env values
480 for env_def in cfg_env:
481 val = cfg_env[env_def]
483 # if it is env_script, do not do anything (reserved keyword)
484 if env_def == "env_script":
487 # if it is a dict, do not do anything
488 if isinstance(val, src.pyconf.Mapping):
491 # if it is a list, loop on its values
492 if isinstance(val, src.pyconf.Sequence):
493 # transform into list of strings
499 # "_" means that the value must be prepended
500 if env_def.startswith("_"):
501 # separator exception for PV_PLUGIN_PATH
502 if env_def[1:] == 'PV_PLUGIN_PATH':
503 self.prepend(env_def[1:], val, ';')
505 self.prepend(env_def[1:], val)
506 elif env_def.endswith("_"):
507 # separator exception for PV_PLUGIN_PATH
508 if env_def[:-1] == 'PV_PLUGIN_PATH':
509 self.append(env_def[:-1], val, ';')
511 self.append(env_def[:-1], val)
513 self.set(env_def, val)
515 def set_a_product(self, product, logger):
516 """Sets the environment of a product.
518 :param product str: The product name
519 :param logger Logger: The logger instance to display messages
522 # Get the informations corresponding to the product
523 pi = src.product.get_product_config(self.cfg, product)
525 # skip mesa product, unless use_mesa property was activated
527 if not ("APPLICATION" in self.cfg and
528 "properties" in self.cfg.APPLICATION and
529 "use_mesa" in self.cfg.APPLICATION.properties and
530 self.cfg.APPLICATION.properties.use_mesa == "yes") :
531 if ("properties" in pi and
532 "is_mesa" in pi.properties and
533 pi.properties.is_mesa == "yes") :
534 logger.write(_("Skip mesa product %s\n") % pi.name, 4)
539 pi.install_dir = os.path.join("out_dir_Path",
544 logger.write(_("Setting environment for %s\n") % product, 4)
547 self.add_comment('setting environ for ' + product)
549 # Do not define environment if the product is native
550 if src.product.product_is_native(pi):
551 if src.product.product_has_env_script(pi):
552 self.run_env_script(pi, native=True)
555 # Set an additional environment for SALOME products
556 if src.product.product_is_salome(pi):
557 # set environment using definition of the product
558 self.set_salome_minimal_product_env(pi, logger)
559 self.set_salome_generic_product_env(pi)
561 if src.product.product_is_cpp(pi):
562 # set a specific environment for cpp modules
563 self.set_salome_minimal_product_env(pi, logger)
566 if src.product.product_is_generated(pi):
567 if "component_name" in pi:
568 # hack the source and install directories in order to point
569 # on the generated product source install directories
570 install_dir_save = pi.install_dir
571 source_dir_save = pi.source_dir
573 pi.install_dir = os.path.join(self.cfg.APPLICATION.workdir,
577 pi.install_dir = os.path.join("out_dir_Path",
580 pi.source_dir = os.path.join(self.cfg.APPLICATION.workdir,
583 pi.name = pi.component_name
584 self.set_salome_minimal_product_env(pi, logger)
585 self.set_salome_generic_product_env(pi)
587 # Put original values
588 pi.install_dir = install_dir_save
589 pi.source_dir = source_dir_save
592 # Put the environment define in the configuration of the product
594 self.load_cfg_environment(pi.environ)
595 if self.forBuild and "build" in pi.environ:
596 self.load_cfg_environment(pi.environ.build)
597 if not self.forBuild and "launch" in pi.environ:
598 self.load_cfg_environment(pi.environ.launch)
599 # if product_info defines a env_scripts, load it
600 if 'env_script' in pi.environ:
601 self.run_env_script(pi, logger)
606 def run_env_script(self, product_info, logger=None, native=False):
607 """Runs an environment script.
609 :param product_info Config: The product description
610 :param logger Logger: The logger instance to display messages
611 :param native Boolean: if True load set_native_env instead of set_env
613 env_script = product_info.environ.env_script
614 # Check that the script exists
615 if not os.path.exists(env_script):
616 raise src.SatException(_("Environment script not found: %s") %
619 if not self.silent and logger is not None:
620 logger.write(" ** load %s\n" % env_script, 4)
622 # import the script and run the set_env function
625 pyproduct = imp.load_source(product_info.name + "_env_script",
628 pyproduct.set_env(self,
629 product_info.install_dir,
630 product_info.version)
632 if "set_nativ_env" in dir(pyproduct):
633 pyproduct.set_nativ_env(self)
635 __, exceptionValue, exceptionTraceback = sys.exc_info()
636 print(exceptionValue)
638 traceback.print_tb(exceptionTraceback)
639 traceback.print_exc()
641 def run_simple_env_script(self, script_path, logger=None):
642 """Runs an environment script. Same as run_env_script, but with a
643 script path as parameter.
645 :param script_path str: a path to an environment script
646 :param logger Logger: The logger instance to display messages
648 if not self.enable_simple_env_script:
650 # Check that the script exists
651 if not os.path.exists(script_path):
652 raise src.SatException(_("Environment script not found: %s") %
655 if not self.silent and logger is not None:
656 logger.write(" ** load %s\n" % script_path, 4)
658 script_basename = os.path.basename(script_path)
659 if script_basename.endswith(".py"):
660 script_basename = script_basename[:-len(".py")]
662 # import the script and run the set_env function
665 pyproduct = imp.load_source(script_basename + "_env_script",
667 pyproduct.load_env(self)
669 __, exceptionValue, exceptionTraceback = sys.exc_info()
670 print(exceptionValue)
672 traceback.print_tb(exceptionTraceback)
673 traceback.print_exc()
675 def set_products(self, logger, src_root=None):
676 """Sets the environment for all the products.
678 :param logger Logger: The logger instance to display messages
679 :param src_root src: the application working directory
682 self.add_comment('setting environ for all products')
684 # Make sure that the python lib dirs are set after python
685 if "Python" in self.cfg.APPLICATION.products:
686 self.set_a_product("Python", logger)
687 self.set_python_libdirs()
689 # The loop on the products
690 for product in self.cfg.APPLICATION.products.keys():
691 if product == "Python":
693 self.set_a_product(product, logger)
696 def set_full_environ(self, logger, env_info):
697 """Sets the full environment for products
698 specified in env_info dictionary.
700 :param logger Logger: The logger instance to display messages
701 :param env_info list: the list of products
703 DBG.write("set_full_environ for", env_info)
704 # DBG.write("set_full_environ config", self.cfg.APPLICATION.environ, True)
705 # set product environ
706 self.set_application_env(logger)
708 self.set_python_libdirs()
711 for product in env_info:
712 self.set_a_product(product, logger)
715 """Class to dump the environment to a file.
717 def __init__(self, config, logger, out_dir, src_root, env_info=None):
720 :param cfg Config: the global config
721 :param logger Logger: The logger instance to display messages
722 :param out_dir str: The directory path where t put the output files
723 :param src_root str: The application working directory
724 :param env_info str: The list of products to add in the files.
728 self.out_dir = out_dir
729 self.src_root= src_root
731 self.env_info = env_info
733 def write_env_file(self, filename, forBuild, shell, for_package = None):
734 """Create an environment file.
736 :param filename str: the file path
737 :param forBuild bool: if true, the build environment
738 :param shell str: the type of file wanted (.sh, .bat)
739 :return: The path to the generated file
743 self.logger.write(_("Create environment file %s\n") %
744 src.printcolors.printcLabel(filename), 3)
746 # create then env object
747 env_file = open(os.path.join(self.out_dir, filename), "w")
748 tmp = src.fileEnviron.get_file_environ(env_file,
751 env = SalomeEnviron(self.config, tmp, forBuild, for_package=for_package)
752 env.silent = self.silent
754 # Set the environment
755 if self.env_info is not None:
756 env.set_full_environ(self.logger, self.env_info)
758 # set env from the APPLICATION
759 env.set_application_env(self.logger)
761 # The list of products to launch
762 lProductsName = env.get_names(self.config.APPLICATION.products.keys())
763 env.set( "SALOME_MODULES", ','.join(lProductsName))
766 env.set_products(self.logger,
767 src_root=self.src_root)
769 # add cleanup and close
775 def write_cfgForPy_file(self,
779 with_commercial = True):
780 """Append to current opened aFile a cfgForPy
781 environment (SALOME python launcher).
783 :param filename str: the file path
784 :param additional_env dict: a dictionary of additional variables
785 to add to the environment
786 :param for_package str: If not None, produce a relative environment
787 designed for a package.
790 self.logger.write(_("Create configuration file %s\n") %
791 src.printcolors.printcLabel(filename.name), 3)
793 # create then env object
794 tmp = src.fileEnviron.get_file_environ(filename,
797 # environment for launch
798 env = SalomeEnviron(self.config,
801 for_package=for_package,
802 enable_simple_env_script = with_commercial)
803 env.silent = self.silent
805 if self.env_info is not None:
806 env.set_full_environ(self.logger, self.env_info)
808 # set env from PRODUCT
809 env.set_application_env(self.logger)
811 # The list of products to launch
812 lProductsName = env.get_names(self.config.APPLICATION.products.keys())
813 env.set( "SALOME_MODULES", ','.join(lProductsName))
816 env.set_products(self.logger,
817 src_root=self.src_root)
819 # Add the additional environment if it is not empty
820 if len(additional_env) != 0:
821 for variable in additional_env:
822 env.set(variable, additional_env[variable])
824 # add cleanup and close
828 """Definition of a Shell.
830 def __init__(self, name, extension):
833 :param name str: the shell name
834 :param extension str: the shell extension
837 self.extension = extension
839 def load_environment(config, build, logger):
840 """Loads the environment (used to run the tests, for example).
842 :param config Config: the global config
843 :param build bool: build environement if True
844 :param logger Logger: The logger instance to display messages
846 environ = SalomeEnviron(config, Environ(os.environ), build)
847 environ.set_application_env(logger)
848 environ.set_products(logger)