X-Git-Url: http://git.salome-platform.org/gitweb/?a=blobdiff_plain;f=src%2Fenvironment.py;h=8da1705504bb0f7cc5ee867891c2a4e1370b236a;hb=c076d3c094c90d169c67176998ffc0191b43aa78;hp=ed7970680ab6ef7399ad05c8560b035b29998acb;hpb=ec43f51274f7a1bd62f02a613090118e78402af6;p=tools%2Fsat.git diff --git a/src/environment.py b/src/environment.py index ed79706..8da1705 100644 --- a/src/environment.py +++ b/src/environment.py @@ -20,6 +20,7 @@ import os import subprocess import string import sys +import copy import src import src.debug as DBG @@ -53,15 +54,21 @@ class Environ: :return: the replaced variable :rtype: str """ - if "$" in value: + if src.architecture.is_windows(): + delim = "%" + else: + delim = "$" + if delim in value: # The string.Template class is a string class # for supporting $-substitutions zt = string.Template(value) + zt.delimiter = delim try: value = zt.substitute(self.environ) except KeyError as exc: - raise src.SatException(_("Missing definition " - "in environment: %s") % str(exc)) + pass + #raise src.SatException(_("Missing definition " + # "in environment: %s") % str(exc)) return value def append_value(self, key, value, sep=os.pathsep): @@ -73,9 +80,11 @@ class Environ: :param value str: the value to append to key :param sep str: the separator string """ - for c in [";", ":"]: # windows or linux path separators - if c in value: - raise Exception("Environ append key '%s' value '%s' contains forbidden character '%s'" % (key, value, c)) + # check that value so no contain the system separator + separator=os.pathsep + if separator in value: + raise Exception("Environ append key '%s' value '%s' contains forbidden character '%s'" % (key, value, separator)) + # check if the key is already in the environment if key in self.environ: value_list = self.environ[key].split(sep) @@ -111,9 +120,11 @@ class Environ: :param value str: the value to prepend to key :param sep str: the separator string """ - for c in [";", ":"]: # windows or linux path separators - if c in value: - raise Exception("Environ prepend key '%s' value '%s' contains forbidden character '%s'" % (key, value, c)) + # check that value so no contain the system separator + separator=os.pathsep + if separator in value: + raise Exception("Environ append key '%s' value '%s' contains forbidden character '%s'" % (key, value, separator)) + # check if the key is already in the environment if key in self.environ: value_list = self.environ[key].split(sep) @@ -176,19 +187,6 @@ class Environ: """ return self.get(key) - def command_value(self, key, command): - """\ - Get the value given by the system command "command" - and put it in the environment variable key - - :param key str: the environment variable - :param command str: the command to execute - """ - value = subprocess.Popen(command, - shell=True, - stdout=subprocess.PIPE, - env=self.environ).communicate()[0] - self.environ[key] = value class SalomeEnviron: @@ -219,6 +217,7 @@ class SalomeEnviron: self.enable_simple_env_script = enable_simple_env_script self.silent = False self.has_python = False + self.__set_sorted_products_list() def __repr__(self): """easy almost exhaustive quick resume for debug print""" @@ -229,6 +228,26 @@ class SalomeEnviron: } return "%s(\n%s\n)" % (self.__class__.__name__, PP.pformat(res)) + def __set_sorted_products_list(self): + all_products_infos = src.product.get_products_infos( + self.cfg.APPLICATION.products, + self.cfg) + + from compile import get_dependencies_graph,depth_first_topo_graph + all_products_graph=get_dependencies_graph(all_products_infos, self.forBuild) + visited_nodes=[] + sorted_nodes=[] + for n in all_products_graph: + if n not in visited_nodes: + visited_nodes,sorted_nodes=depth_first_topo_graph( + all_products_graph, + n, + visited_nodes, + sorted_nodes) + self.sorted_product_list=sorted_nodes + self.all_products_graph=all_products_graph + + def append(self, key, value, sep=os.pathsep): """\ append value to key using sep @@ -333,7 +352,7 @@ class SalomeEnviron: if 'add_warning' in dir(self.environ): self.environ.add_warning(warning) - def finish(self, required): + def finish(self): """\ Add a final instruction in the out file (in case of file generation) @@ -343,70 +362,33 @@ class SalomeEnviron: self.environ.add_line(1) # what for ? # self.environ.add_comment("clean all the path") - self.environ.finish(required) + self.environ.finish() def set_python_libdirs(self): """Set some generic variables for python library paths""" ver = self.get('PYTHON_VERSION') - self.set('PYTHON_LIBDIR0', os.path.join('lib', - 'python' + ver, - 'site-packages')) - self.set('PYTHON_LIBDIR1', os.path.join('lib64', + self.set('PYTHON_LIBDIR', os.path.join('lib', 'python' + ver, 'site-packages')) - - self.python_lib0 = self.get('PYTHON_LIBDIR0') - self.python_lib1 = self.get('PYTHON_LIBDIR1') + self.python_lib = self.get('PYTHON_LIBDIR') self.has_python = True - def get_names(self, lProducts): - """\ - Get the products name to add in SALOME_MODULES environment variable - It is the name of the product, except in the case where the is a - component name. And it has to be in SALOME_MODULES variable only - if the product has the property has_salome_hui = "yes" - - :param lProducts list: List of products to potentially add - """ - lProdHasGui = [p for p in lProducts if 'properties' in - src.product.get_product_config(self.cfg, p) and - 'has_salome_gui' in - src.product.get_product_config(self.cfg, p).properties and - src.product.get_product_config(self.cfg, - p).properties.has_salome_gui=='yes'] - lProdName = [] - for ProdName in lProdHasGui: - pi = src.product.get_product_config(self.cfg, ProdName) - if 'component_name' in pi: - lProdName.append(pi.component_name) - else: - lProdName.append(ProdName) - return lProdName - - def set_application_env(self, logger, no_PRODUCT_ROOT_DIR=False): + def set_application_env(self, logger): """\ Sets the environment defined in the APPLICATION file. :param logger Logger: The logger instance to display messages """ - # add variable PRODUCT_ROOT_DIR as $workdir in APPLICATION.environ section if not present - # but if sat launcher or sat package do not duplicate line context.setVariable(r"PRODUCT_ROOT_DIR", ... - # no_PRODUCT_ROOT_DIR used only for write_cfgForPy_file - if not no_PRODUCT_ROOT_DIR: # do not duplicate context.setVariable(r"PRODUCT_ROOT_DIR" - try: - tmp = self.cfg.APPLICATION.environ.PRODUCT_ROOT_DIR - except: - self.cfg.APPLICATION.environ.PRODUCT_ROOT_DIR = src.pyconf.Reference(self.cfg, src.pyconf.DOLLAR, "workdir") - DBG.write("set_application_env: add APPLICATION.environ.PRODUCT_ROOT_DIR", self.cfg.APPLICATION.environ) - - # these sensitive variables are reset to avoid bad environment interactions - self.add_line(1) - self.add_comment("reset these sensitive variables to avoid bad environment interactions") - self.add_comment("comment these to lines if you wish a different behaviour") - self.set("LD_LIBRARY_PATH", "") - self.set("PYTHONPATH", "") - self.add_line(1) + if self.for_package: + if src.architecture.is_windows(): + self.set("PRODUCT_ROOT_DIR", "%out_dir_Path%") + else: + self.set("PRODUCT_ROOT_DIR", "out_dir_Path") + + else: + self.cfg.APPLICATION.environ.PRODUCT_ROOT_DIR = src.pyconf.Reference(self.cfg, src.pyconf.DOLLAR, "workdir") + # Set the variables defined in the "environ" section if 'environ' in self.cfg.APPLICATION: @@ -432,27 +414,33 @@ class SalomeEnviron: # set root dir DBG.write("set_salome_minimal_product_env", product_info) root_dir = product_info.name + "_ROOT_DIR" - if not self.is_defined(root_dir): - if 'install_dir' in product_info and product_info.install_dir: - self.set(root_dir, product_info.install_dir) - elif not self.silent: - logger.write(" " + _("No install_dir for product %s\n") % - product_info.name, 5) - + if 'install_dir' in product_info and product_info.install_dir: + self.set(root_dir, product_info.install_dir) + elif not self.silent: + logger.write(" " + _("No install_dir for product %s\n") % + product_info.name, 5) + source_in_package = src.get_property_in_product_cfg(product_info, "sources_in_package") if not self.for_package or source_in_package == "yes": # set source dir, unless no source dir if not src.product.product_is_fixed(product_info): src_dir = product_info.name + "_SRC_DIR" - if not self.is_defined(src_dir): - if not self.for_package: - self.set(src_dir, product_info.source_dir) - else: - self.set(src_dir, os.path.join("out_dir_Path", - "SOURCES", - product_info.name)) + if not self.for_package: + self.set(src_dir, product_info.source_dir) + else: + self.set(src_dir, os.path.join("out_dir_Path", + "SOURCES", + os.path.basename(product_info.source_dir))) + def expand_salome_modules(self, pi): + if 'component_name' in pi: + compo_name = pi.component_name + else: + compo_name = pi.name + self.append('SALOME_MODULES', compo_name, ',') + + def set_salome_generic_product_env(self, pi): """\ Sets the generic environment for a SALOME product. @@ -462,7 +450,6 @@ class SalomeEnviron: # Construct XXX_ROOT_DIR env_root_dir = self.get(pi.name + "_ROOT_DIR") l_binpath_libpath = [] - # create additional ROOT_DIR for CPP components if 'component_name' in pi: compo_name = pi.component_name @@ -481,15 +468,13 @@ class SalomeEnviron: bin_path = os.path.join(env_root_dir, 'bin') if self.has_python: # if the application doesn't include python, we don't need these two lines - pylib1_path = os.path.join(env_root_dir, self.python_lib0) - pylib2_path = os.path.join(env_root_dir, self.python_lib1) + pylib_path = os.path.join(env_root_dir, self.python_lib) else: lib_path = os.path.join(env_root_dir, 'lib', 'salome') bin_path = os.path.join(env_root_dir, 'bin', 'salome') if self.has_python: # if the application doesn't include python, we don't need these two lines - pylib1_path = os.path.join(env_root_dir, self.python_lib0, 'salome') - pylib2_path = os.path.join(env_root_dir, self.python_lib1, 'salome') + pylib_path = os.path.join(env_root_dir, self.python_lib, 'salome') # Construct the paths to prepend to PATH and LD_LIBRARY_PATH and # PYTHONPATH @@ -504,11 +489,10 @@ class SalomeEnviron: self.prepend('LD_LIBRARY_PATH', lib_path) l = [ bin_path, lib_path ] - if self.has_python: - l.append(pylib1_path) - l.append(pylib2_path) - - self.prepend('PYTHONPATH', l) + if not src.product.product_is_wheel(pi): + if self.has_python: + l.append(pylib_path) + self.prepend('PYTHONPATH', l) def set_cpp_env(self, product_info): """\ @@ -536,8 +520,7 @@ class SalomeEnviron: l = [ bin_path, lib_path ] if self.has_python: - l.append(os.path.join(env_root_dir, self.python_lib0)) - l.append(os.path.join(env_root_dir, self.python_lib1)) + l.append(os.path.join(env_root_dir, self.python_lib)) self.prepend('PYTHONPATH', l) def load_cfg_environment(self, cfg_env): @@ -592,6 +575,16 @@ class SalomeEnviron: # Get the informations corresponding to the product pi = src.product.get_product_config(self.cfg, product) + # skip compile time products at run time + if not self.forBuild: + if src.product.product_is_compile_time(pi): + return + + # skip pip products when pip is activated and installation is done in python + #if (src.appli_test_property(self.cfg,"pip", "yes") and + # src.product.product_test_property(pi,"pip", "yes") and + # src.appli_test_property(self.cfg,"pip_install_dir", "python") ): + # return # skip mesa products (if any) at run time, # unless use_mesa property was activated @@ -608,9 +601,14 @@ class SalomeEnviron: if self.for_package: - pi.install_dir = os.path.join("out_dir_Path", - self.for_package, - pi.name) + prod_base_name=os.path.basename(pi.install_dir) + if prod_base_name.startswith("config"): + # case of a products installed in base. We remove "config-i" + prod_base_name=os.path.basename(os.path.dirname(pi.install_dir)) + pi.install_dir = os.path.join( + "out_dir_Path", + self.for_package, + prod_base_name) if not self.silent: logger.write(_("Setting environment for %s\n") % product, 4) @@ -629,13 +627,24 @@ class SalomeEnviron: # set environment using definition of the product self.set_salome_minimal_product_env(pi, logger) self.set_salome_generic_product_env(pi) + + # Expand SALOME_MODULES variable for products which have a salome gui + if src.product.product_has_salome_gui(pi): + self.expand_salome_modules(pi) + # use variable LICENCE_FILE to communicate the licence file name to the environment script licence_file_name = src.product.product_has_licence(pi, self.cfg.PATHS.LICENCEPATH) if licence_file_name: logger.write("licence file found for product %s : %s\n" % (pi.name, licence_file_name), 5) self.set("LICENCE_FILE", licence_file_name) + # these infos may be needed for the environment of some products + if "debug" in pi and pi.debug == "yes": + self.set("SAT_DEBUG", "1") + if "verbose" in pi and pi.verbose == "yes": + self.set("SAT_VERBOSE", "1") + if src.product.product_is_cpp(pi): # set a specific environment for cpp modules self.set_salome_minimal_product_env(pi, logger) @@ -649,7 +658,7 @@ class SalomeEnviron: source_dir_save = pi.source_dir name_save = pi.name pi.install_dir = os.path.join(self.cfg.APPLICATION.workdir, - "INSTALL", + self.cfg.INTERNAL.config.install_dir, pi.component_name) if self.for_package: pi.install_dir = os.path.join("out_dir_Path", @@ -729,41 +738,6 @@ class SalomeEnviron: traceback.print_tb(exceptionTraceback) traceback.print_exc() - def run_simple_env_script(self, script_path, logger=None): - """\ - Runs an environment script. Same as run_env_script, but with a - script path as parameter. - - :param script_path str: a path to an environment script - :param logger Logger: The logger instance to display messages - """ - if not self.enable_simple_env_script: - return - # Check that the script exists - if not os.path.exists(script_path): - raise src.SatException(_("Environment script not found: %s") % - script_path) - - if not self.silent and logger is not None: - logger.write(" ** load %s\n" % script_path, 4) - - script_basename = os.path.basename(script_path) - if script_basename.endswith(".py"): - script_basename = script_basename[:-len(".py")] - - # import the script and run the set_env function - try: - import imp - pyproduct = imp.load_source(script_basename + "_env_script", - script_path) - pyproduct.load_env(self) - except: - __, exceptionValue, exceptionTraceback = sys.exc_info() - print(exceptionValue) - import traceback - traceback.print_tb(exceptionTraceback) - traceback.print_exc() - def set_products(self, logger, src_root=None): """\ Sets the environment for all the products. @@ -775,20 +749,19 @@ class SalomeEnviron: self.add_comment('setting environ for all products') # Make sure that the python lib dirs are set after python - if "Python" in self.cfg.APPLICATION.products: + if "Python" in self.sorted_product_list: self.set_a_product("Python", logger) self.set_python_libdirs() # The loop on the products - for product in self.cfg.APPLICATION.products.keys(): + for product in self.sorted_product_list: if product == "Python": continue self.set_a_product(product, logger) - self.finish(False) def set_full_environ(self, logger, env_info): """\ - Sets the full environment for products + Sets the full environment for products, with their dependencies specified in env_info dictionary. :param logger Logger: The logger instance to display messages @@ -796,16 +769,26 @@ class SalomeEnviron: """ DBG.write("set_full_environ for", env_info) # DBG.write("set_full_environ config", self.cfg.APPLICATION.environ, True) - # set product environ self.set_application_env(logger) - if "Python" in env_info: + # use the sorted list of all products to sort the list of products + # we have to set + visited=[] + from compile import depth_search_graph # to get the dependencies + for p_name in env_info: + visited=depth_search_graph(self.all_products_graph, p_name, visited) + sorted_product_list=[] + for n in self.sorted_product_list: + if n in visited: + sorted_product_list.append(n) + + if "Python" in sorted_product_list: self.set_a_product("Python", logger) self.set_python_libdirs() # set products - for product in env_info: + for product in sorted_product_list: if product == "Python": continue self.set_a_product(product, logger) @@ -831,26 +814,134 @@ class FileEnvWriter: self.silent = True self.env_info = env_info - def write_env_file(self, filename, forBuild, shell, for_package = None): + def write_tcl_files(self, + forBuild, + shell, + for_package = None, + no_path_init=False, + additional_env = {}): + """\ + Create tcl environment files for environment module. + + :param forBuild bool: if true, the build environment + :param shell str: the type of file wanted (.sh, .bat) + :param for_package bool: if true do specific stuff for required for packages + :param no_path_init bool: if true generate a environ file that do not reinitialise paths + :param additional_env dict: contains sat_ prefixed variables to help the génération, + and also variables to add in the environment. + :return: The path to the generated file + :rtype: str + """ + + # get the products informations + all_products=self.config.APPLICATION.products + products_infos = src.product.get_products_infos(all_products, self.config) + + # set a global environment (we need it to resolve variable references + # between dependent products + global_environ = src.environment.SalomeEnviron(self.config, + src.environment.Environ(additional_env), + False) + global_environ.set_products(self.logger) + + # The loop on the products + for product in all_products: + # create one file per product + pi = src.product.get_product_config(self.config, product) + if "base" not in pi: # we write tcl files only for products in base + continue + + # get the global environment, and complete it with sat_ prefixed + # prefixed variables which are used to transfer info to + # TclFileEnviron class + product_env = copy.deepcopy(global_environ.environ) + product_env.environ["sat_product_name"] = pi.name + product_env.environ["sat_product_version"] = pi.version + product_env.environ["sat_product_base_path"] = src.get_base_path(self.config) + product_env.environ["sat_product_base_name"] = pi.base + + # store infos in sat_product_load_depend to set dependencies in tcl file + sat_product_load_depend="" + for p_name,p_info in products_infos: + if p_name in pi.depend: + sat_product_load_depend+="module load %s/%s/%s;" % (pi.base, + p_info.name, + p_info.version) + if len(sat_product_load_depend)>0: + # if there are dependencies, store the module to load (get rid of trailing ;) + product_env.environ["sat_product_load_depend"]=sat_product_load_depend[0:-1] + + + env_file_name = os.path.join(product_env.environ["sat_product_base_path"], + "modulefiles", + product_env.environ["sat_product_base_name"], + product_env.environ["sat_product_name"], + product_env.environ["sat_product_version"]) + prod_dir_name=os.path.dirname(env_file_name) + if not os.path.isdir(prod_dir_name): + os.makedirs(prod_dir_name) + + env_file = open(env_file_name, "w") + file_environ = src.fileEnviron.get_file_environ(env_file, + "tcl", product_env) + env = SalomeEnviron(self.config, + file_environ, + False, + for_package=for_package) + if "Python" in pi.depend: + # short cut, env.python_lib is required by set_a_product for salome modules + env.has_python="True" + env.python_lib=global_environ.get("PYTHON_LIBDIR") + env.set_a_product(product, self.logger) + env_file.close() + if not self.silent: + self.logger.write(_(" Create tcl module environment file %s\n") % + src.printcolors.printcLabel(env_file_name), 3) + + + def write_env_file(self, + filename, + forBuild, + shell, + for_package = None, + no_path_init=False, + additional_env = {}): """\ Create an environment file. :param filename str: the file path :param forBuild bool: if true, the build environment :param shell str: the type of file wanted (.sh, .bat) + :param for_package bool: if true do specific stuff for required for packages + :param no_path_init bool: if true generate a environ file that do not reinitialise paths + :param additional_env dict: contains sat_ prefixed variables to help the génération, + and also variables to add in the environment. :return: The path to the generated file :rtype: str """ + additional_env["sat_dist"]=self.config.VARS.dist if not self.silent: self.logger.write(_("Create environment file %s\n") % src.printcolors.printcLabel(filename), 3) - # create then env object env_file = open(os.path.join(self.out_dir, filename), "w") - tmp = src.fileEnviron.get_file_environ(env_file, + + # we duplicate additional_env, and transmit it to fileEnviron, which will use its sat_ prefixed variables. + # the other variables of additional_env are added to the environement file at the end of this function. + salome_env = copy.deepcopy(additional_env) + file_environ = src.fileEnviron.get_file_environ(env_file, shell, - {}) - env = SalomeEnviron(self.config, tmp, forBuild, for_package=for_package) + src.environment.Environ(salome_env)) + if no_path_init: + # specify we don't want to reinitialise paths + # path will keep the inherited value, which will be appended with new values. + file_environ.set_no_init_path() + + env = SalomeEnviron(self.config, + file_environ, + forBuild, + for_package=for_package) + env.silent = self.silent # Set the environment @@ -859,77 +950,25 @@ class FileEnvWriter: else: # set env from the APPLICATION env.set_application_env(self.logger) - - # The list of products to launch - lProductsName = env.get_names(self.config.APPLICATION.products.keys()) - env.set( "SALOME_MODULES", ','.join(lProductsName)) - # set the products env.set_products(self.logger, src_root=self.src_root) + # Add the additional environment if it is not empty + if len(additional_env) != 0: + env.add_line(1) + env.add_comment("[APPLI variables]") + for variable in additional_env: + if not variable.startswith("sat_"): + # by convention variables starting with sat_ are used to transfer information, + # not to be written in env + env.set(variable, additional_env[variable]) - # add cleanup and close - env.finish(True) + # finalise the writing and close the file + env.finish() env_file.close() return env_file.name - def write_cfgForPy_file(self, - filename, - additional_env = {}, - for_package = None, - with_commercial = True): - """\ - Append to current opened aFile a cfgForPy - environment (SALOME python launcher). - - :param filename str: the file path - :param additional_env dict: a dictionary of additional variables - to add to the environment - :param for_package str: If not None, produce a relative environment - designed for a package. - """ - if not self.silent: - self.logger.write(_("Create configuration file %s\n") % - src.printcolors.printcLabel(filename.name), 3) - - # create then env object - tmp = src.fileEnviron.get_file_environ(filename, "cfgForPy", {}) - # DBG.write("fileEnviron.get_file_environ %s" % filename, tmp, True) - - # environment for launch - env = SalomeEnviron(self.config, - tmp, - forBuild=False, - for_package=for_package, - enable_simple_env_script = with_commercial) - env.silent = self.silent - - DBG.write("write_cfgForPy_file", self.config.APPLICATION.environ) - - if self.env_info is not None: - env.set_full_environ(self.logger, self.env_info) - DBG.write("set_full_environ", self.env_info) - - else: - # set env from PRODUCT - env.set_application_env(self.logger, no_PRODUCT_ROOT_DIR=True) - - # The list of products to launch - lProductsName = env.get_names(self.config.APPLICATION.products.keys()) - env.set("SALOME_MODULES", ','.join(lProductsName)) - - # set the products - env.set_products(self.logger, src_root=self.src_root) - DBG.write("set_application_env without PRODUCT_ROOT_DIR", self.config.APPLICATION.environ) - - # Add the additional environment if it is not empty - if len(additional_env) != 0: - for variable in additional_env: - env.set(variable, additional_env[variable]) - - # add cleanup and close - env.finish(True) class Shell: """\ @@ -956,4 +995,3 @@ def load_environment(config, build, logger): environ = SalomeEnviron(config, Environ(os.environ), build) environ.set_application_env(logger) environ.set_products(logger) - environ.finish(True)