From: Serge Rehbinder Date: Wed, 1 Jun 2016 13:23:02 +0000 (+0200) Subject: Add the launcher command X-Git-Tag: 5.0.0a0~40 X-Git-Url: http://git.salome-platform.org/gitweb/?a=commitdiff_plain;h=7f20ea98ea0ed549cf45175d6f1edb5124829220;p=tools%2Fsat.git Add the launcher command --- diff --git a/commands/config.py b/commands/config.py index 79169e0..c494212 100644 --- a/commands/config.py +++ b/commands/config.py @@ -273,7 +273,12 @@ class ConfigManager: for rule in self.get_command_line_overrides(options, ["APPLICATION"]): # this cannot be factorized because of the exec - exec('cfg.' + rule) + exec('cfg.' + rule) + + # default launcher name ('salome') + if ('profile' in cfg.APPLICATION and + 'launcher_name' not in cfg.APPLICATION.profile): + cfg.APPLICATION.profile.launcher_name = 'salome' # ===================================================================== # Load product config files in PRODUCTS section @@ -296,6 +301,10 @@ class ConfigManager: except IOError as error: e = str(error) raise src.SatException( e ); + except Exception as e: + raise src.SatException(_( + "Error in configuration file: %(prod)s\n %(error)s") % \ + {'prod' : fName, 'error': str(e) }) merger.merge(cfg.PRODUCTS, prod_cfg) @@ -314,7 +323,7 @@ class ConfigManager: # apply overwrite from command line if needed for rule in self.get_command_line_overrides(options, ["USER"]): exec('cfg.' + rule) # this cannot be factorize because of the exec - + return cfg def set_user_config_file(self, config): diff --git a/commands/environ.py b/commands/environ.py index 5796537..777efa6 100644 --- a/commands/environ.py +++ b/commands/environ.py @@ -47,7 +47,21 @@ def write_all_source_files(config, shells=["bash"], prefix="env", env_info=None): + '''Generates the environment files. + :param config Config: The global configuration + :param logger Logger: The logger instance to use for the display + and logging + :param out_dir str: The path to the directory where the files will be put + :param src_root str: The path to the directory where the sources are + :param silent boolean: If True, do not print anything in the terminal + :param shells list: The list of shells to generate + :param prefix str: The prefix to add to the file names. + :param env_info str: The list of products to add in the files. + :return: The list of the generated files. + :rtype: List + ''' + if not out_dir: out_dir = config.APPLICATION.workdir diff --git a/commands/launcher.py b/commands/launcher.py new file mode 100644 index 0000000..a353a67 --- /dev/null +++ b/commands/launcher.py @@ -0,0 +1,396 @@ +#!/usr/bin/env python +#-*- coding:utf-8 -*- +# Copyright (C) 2010-2013 CEA/DEN +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +import os +import platform +import shutil +import getpass +import subprocess +import stat + +import src + +parser = src.options.Options() + +parser.add_option('n', 'name', 'string', 'name', _('The name of the launcher ' + '(default is APPLICATION.profile.launcher_name)')) +parser.add_option('c', 'catalog', 'string', 'catalog', + _('The resources catalog to use')) +parser.add_option('', 'gencat', 'string', 'gencat', + _("Create a resources catalog for the specified machines " + "(separated with ',') \n\tNOTICE: this command will ssh to retrieve" + " information to each machine in the list")) + +def generate_launch_file(config, + logger, + env_info=None, + pathlauncher=None, + display=True, + additional_env={}): + '''Generates the launcher file. + + :param config Config: The global configuration + :param logger Logger: The logger instance to use for the display + and logging + :param env_info str: The list of products to add in the files. + :param pathlauncher str: The path to the launcher to generate + :param src_root str: The path to the directory where the sources are + :param display boolean: If False, do not print anything in the terminal + :param additional_env dict: The dict giving additional + environment variables + :return: The launcher file path. + :rtype: str + ''' + # Get the application directory and the profile directory + out_dir = config.APPLICATION.workdir + profile = config.APPLICATION.profile + profile_install_dir = get_profile_dir(config) + + # Compute the default launcher path if it is not provided in pathlauncher + # parameter + if pathlauncher is None: + filepath = os.path.join( os.path.join( profile_install_dir, + 'bin', + 'salome' ), + profile['launcher_name'] ) + else: + filepath = os.path.join(pathlauncher, profile['launcher_name']) + + # Remove the file if it exists in order to replace it + if os.path.exists(filepath): + os.remove(filepath) + + # Add the APPLI variable + additional_env['APPLI'] = filepath + + # Get the launcher template + withProfile = src.fileEnviron.withProfile.replace( "PROFILE_INSTALL_DIR", + profile_install_dir ) + before, after = withProfile.split( + "# here your local standalone environment\n") + + # create an environment file writer + writer = src.environment.FileEnvWriter(config, + logger, + out_dir, + src_root=None, + env_info=env_info) + + # Display some information + if display: + # Write the launcher file + logger.write(_("Generating launcher for %s :\n") % + src.printcolors.printcLabel(config.VARS.application), 1) + logger.write(" %s\n" %src.printcolors.printcLabel(filepath), 1) + + # open the file and write into it + launch_file = open(filepath, "w") + launch_file.write(before) + # Write + writer.write_cfgForPy_file(launch_file, additional_env=additional_env) + launch_file.write(after) + launch_file.close() + + # change the rights in order to make the file executable for everybody + os.chmod(filepath, + stat.S_IRUSR | + stat.S_IRGRP | + stat.S_IROTH | + stat.S_IWUSR | + stat.S_IXUSR | + stat.S_IXGRP | + stat.S_IXOTH) + return filepath + +def generate_launch_link(config, + logger, + launcherPath, + pathlauncher=None, + display=True, + packageLauncher=False): + '''Generates the launcher link that sources Python + and call the actual launcher. + + :param config Config: The global configuration + :param logger Logger: The logger instance to use for the display + and logging + :param launcherPath str: The path to the launcher to call + :param pathlauncher str: The path to the launcher (link) to generate + :param display boolean: If False, do not print anything in the terminal + :param packageLauncher boolean: if True, use a relative path (for package) + :return: The launcher link file path. + :rtype: str + ''' + if pathlauncher is None: + # Make an executable file that sources python, then launch the launcher + # produced by generate_launch_file method + sourceLauncher = os.path.join(config.APPLICATION.workdir, + config.APPLICATION.profile.launcher_name) + else: + sourceLauncher = os.path.join(pathlauncher, + config.APPLICATION.profile.launcher_name) + + # Change the extension for the windows case + if platform.system() == "Windows" : + sourceLauncher += '.bat' + + # display some information + if display: + logger.write(_("\nGenerating the executable that sources" + " python and runs the launcher :\n") , 1) + logger.write(" %s\n" %src.printcolors.printcLabel(sourceLauncher), 1) + + # open the file to write + f = open(sourceLauncher, "w") + + # Write the set up of the environment + if platform.system() == "Windows" : + shell = 'bat' + else: + shell = 'bash' + + # Write the Python environment files + env = src.environment.SalomeEnviron( config, + src.fileEnviron.get_file_environ( f, shell, config ) ) + env.set_a_product( "Python", logger) + + # Write the call to the original launcher + f.write( "\n\n") + if packageLauncher: + cmd = os.path.join('${out_dir_Path}', launcherPath) + else: + cmd = launcherPath + + if platform.system() == "Windows" : + cmd = 'python ' + cmd + ' %*' + else: + cmd = cmd + ' $*' + + f.write( cmd ) + f.write( "\n\n") + + # Write the cleaning of the environment + env.finish(True) + + # Close new launcher + f.close() + os.chmod(sourceLauncher, + stat.S_IRUSR | + stat.S_IRGRP | + stat.S_IROTH | + stat.S_IWUSR | + stat.S_IWGRP | + stat.S_IWOTH | + stat.S_IXUSR | + stat.S_IXGRP | + stat.S_IXOTH) + return sourceLauncher + +def generate_catalog(machines, config, logger): + """Generates an xml catalog file from a list of machines. + + :param machines List: The list of machines to add in the catalog + :param config Config: The global configuration + :param logger Logger: The logger instance to use for the display + and logging + :return: The catalog file path. + :rtype: str + """ + # remove empty machines + machines = map(lambda l: l.strip(), machines) + machines = filter(lambda l: len(l) > 0, machines) + + # log something + src.printcolors.print_value(logger, _("Generate Resources Catalog"), + ", ".join(machines), 4) + + # The command to execute on each machine in order to get some information + cmd = '"cat /proc/cpuinfo | grep MHz ; cat /proc/meminfo | grep MemTotal"' + user = getpass.getuser() + + # Create the catalog path + catfile = src.get_tmp_filename(config, "CatalogResources.xml") + catalog = file(catfile, "w") + + # Write into it + catalog.write("\n\n") + for k in machines: + logger.write(" ssh %s " % (k + " ").ljust(20, '.'), 4) + logger.flush() + + # Verify that the machine is accessible + ssh_cmd = 'ssh -o "StrictHostKeyChecking no" %s %s' % (k, cmd) + p = subprocess.Popen(ssh_cmd, shell=True, + stdin=subprocess.PIPE, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE) + p.wait() + + if p.returncode != 0: # The machine is not accessible + logger.write(src.printcolors.printc(src.KO_STATUS) + "\n", 4) + logger.write(" " + + src.printcolors.printcWarning(p.stderr.read()), 2) + else: + # The machine is accessible, write the corresponding section on + # the xml file + logger.write(src.printcolors.printc(src.OK_STATUS) + "\n", 4) + lines = p.stdout.readlines() + freq = lines[0][:-1].split(':')[-1].split('.')[0].strip() + nb_proc = len(lines) -1 + memory = lines[-1].split(':')[-1].split()[0].strip() + memory = int(memory) / 1000 + + catalog.write(" \n") + catalog.write(" \n") + + catalog.write("\n") + catalog.close() + return catfile + +def copy_catalog(config, catalog_path): + """Copy the xml catalog file into the right location + + :param config Config: The global configuration + :param catalog_path str: the catalog file path + :return: The environment dictionary corresponding to the file path. + :rtype: Dict + """ + # Verify the existence of the file + if not os.path.exists(catalog_path): + raise IOError(_("Catalog not found: %s") % catalog_path) + # Compute the location where to copy the file + profile_dir = get_profile_dir(config) + new_catalog_path = os.path.join(profile_dir, "CatalogResources.xml") + # Do the copy + shutil.copy(catalog_path, new_catalog_path) + additional_environ = {'USER_CATALOG_RESOURCES_FILE' : new_catalog_path} + return additional_environ + +def get_profile_dir(config): + """Get the profile directory from the config + + :param config Config: The global configuration + :return: The profile install directory + :rtype: Str + """ + profile_name = config.APPLICATION.profile.product + profile_info = src.product.get_product_config(config, profile_name) + return profile_info.install_dir + +def finish_profile_install(config, launcherPath): + """Add some symlinks required for SALOME + + :param config Config: The global configuration + :param launcherPath str: the launcher file path + """ + # Create a USERS directory + profile_dir = get_profile_dir(config) + user_dir = os.path.join(profile_dir, 'USERS') + if not os.path.exists(user_dir): + os.makedirs(user_dir) + # change rights of USERS directory + os.chmod(user_dir, + stat.S_IRUSR | + stat.S_IRGRP | + stat.S_IROTH | + stat.S_IWUSR | + stat.S_IWGRP | + stat.S_IWOTH | + stat.S_IXUSR | + stat.S_IXGRP | + stat.S_IXOTH) + + # create a link in root directory to the launcher + if platform.system() != "Windows" : + link_path = os.path.join(config.APPLICATION.workdir, 'salome') + if not os.path.exists(link_path): + try: + os.symlink(launcherPath, link_path) + except OSError: + os.remove(link_path) + os.symlink(launcherPath, link_path) + + link_path = os.path.join(profile_dir, 'salome') + if not os.path.exists(link_path): + try: + os.symlink(launcherPath, link_path) + except OSError: + os.remove(link_path) + os.symlink(launcherPath, link_path) + +################################################## + +## +# Describes the command +def description(): + return _("The launcher command generates a salome launcher.") + +## +# Runs the command. +def run(args, runner, logger): + + # check for product + (options, args) = parser.parse_args(args) + + # Verify that the command was called with an application + src.check_config_has_application( runner.cfg ) + + # Verify that the APPLICATION section has a profile section + src.check_config_has_profile( runner.cfg ) + + # Change the name of the file to create + # if the corresponding option was called + if options.name: + runner.cfg.APPLICATION.profile['launcher_name'] = options.name + + # Copy a catalog if the option is called + additional_environ = {} + if options.catalog: + additional_environ = copy_catalog(runner.cfg, options.catalog) + + # Generate a catalog of resources if the corresponding option was called + if options.gencat: + catalog_path = generate_catalog(options.gencat.split(","), + runner.cfg, + logger) + additional_environ = copy_catalog(runner.cfg, catalog_path) + + # Generate the launcher + launcherPath = generate_launch_file( runner.cfg, + logger, + additional_env = additional_environ ) + + # Create the link (bash file that sources python and then call + # the actual launcher) to the launcher + generate_launch_link( runner.cfg, logger, launcherPath) + + # Add some links + finish_profile_install(runner.cfg, launcherPath) + + return 0 diff --git a/complete_sat.sh b/complete_sat.sh index c258f90..ed6d121 100755 --- a/complete_sat.sh +++ b/complete_sat.sh @@ -80,7 +80,7 @@ _salomeTools_complete() # first argument => show available commands if [[ ${argc} == 1 ]] then - opts="config log testcommand source patch prepare environ clean configure make makeinstall compile --help" + opts="config log testcommand source patch prepare environ clean configure make makeinstall compile launcher --help" COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) ) return 0 fi @@ -184,6 +184,11 @@ _salomeTools_complete() COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) ) return 0 ;; + launcher) + opts="--name --catalog --gencat" + COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) ) + return 0 + ;; *) return 0 ;; esac diff --git a/src/__init__.py b/src/__init__.py index 0b61594..45da5cc 100644 --- a/src/__init__.py +++ b/src/__init__.py @@ -62,6 +62,19 @@ def check_config_has_application( config, details = None ): details.append(message) raise SatException( message ) +def check_config_has_profile( config, details = None ): + '''check that the config has the key APPLICATION.profile. + Else, raise an exception. + + :param config class 'common.pyconf.Config': The config. + ''' + check_config_has_application(config) + if 'profile' not in config.APPLICATION: + message = _("A profile section is required in your application.\n") + if details : + details.append(message) + raise SatException( message ) + def config_has_application( config ): return 'APPLICATION' in config @@ -109,6 +122,23 @@ def get_base_path(config): base_path = config.USER.bases.base return base_path +def only_numbers(str_num): + return ''.join([nb for nb in str_num if nb in '0123456789'] or '0') + +def read_config_from_a_file(filePath): + try: + cfg_file = pyconf.Config(filePath) + except pyconf.ConfigError as e: + raise SatException(_("Error in configuration file: %(file)s\n %(error)s") % \ + { 'file': filePath, 'error': str(e) }) + return cfg_file + +def get_tmp_filename(cfg, name): + if not os.path.exists(cfg.VARS.tmp_root): + os.makedirs(cfg.VARS.tmp_root) + + return os.path.join(cfg.VARS.tmp_root, name) + ## # Utils class to simplify path manipulations. class Path: diff --git a/src/environment.py b/src/environment.py index 7059350..7868ed4 100644 --- a/src/environment.py +++ b/src/environment.py @@ -303,9 +303,9 @@ class SalomeEnviron: :param lProducts list: List of products to potentially add """ - lProdHasGui = [p for p in lProducts if 'has_gui' in + lProdHasGui = [p for p in lProducts if 'type ' in src.product.get_product_config(self.cfg, p) and - src.product.get_product_config(self.cfg, p).has_gui=='yes'] + src.product.get_product_config(self.cfg, p).type=='salome'] lProdName = [] for ProdName in lProdHasGui: pi = src.product.get_product_config(self.cfg, ProdName) @@ -435,6 +435,11 @@ class SalomeEnviron: # Loop on cfg_env values for env_def in cfg_env: val = cfg_env[env_def] + + # if it is env_script, do not do anything (reserved keyword) + if env_def == "env_script": + continue + # if it is a dict, do not do anything if isinstance(val, src.pyconf.Mapping): continue @@ -490,16 +495,15 @@ class SalomeEnviron: self.load_cfg_environment(pi.environ.build) if not self.forBuild and "launch" in pi.environ: self.load_cfg_environment(pi.environ.launch) + # if product_info defines a env_scripts, load it + if 'env_script' in pi.environ: + self.run_env_script(pi, logger) # Set an additional environment for SALOME products if src.product.product_is_salome(pi): # set environment using definition of the product self.set_salome_minimal_product_env(pi, logger) self.set_salome_generic_product_env(product) - - # if product_info defines a env_scripts, load it - if 'env_script' in pi: - self.run_env_script(pi, logger) def run_env_script(self, product_info, logger=None): @@ -508,14 +512,14 @@ class SalomeEnviron: :param product_info Config: The product description :param logger Logger: The logger instance to display messages """ - env_script = product_info.env_script + env_script = product_info.environ.env_script # Check that the script exists - if not os.path.exists(product_info.env_script): + if not os.path.exists(env_script): raise src.SatException(_("Environment script not found: %s") % env_script) if not self.silent and logger is not None: - logger.write(" ** load %s\n" % product_info.env_script, 4) + logger.write(" ** load %s\n" % env_script, 4) # import the script and run the set_env function try: @@ -531,6 +535,38 @@ 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 + """ + # 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. @@ -569,13 +605,9 @@ class SalomeEnviron: # set product environ self.set_application_env(logger) - # set products - install_root = os.path.join(self.cfg.APPLICATION.workdir, "INSTALL") - source_root = os.path.join(self.cfg.APPLICATION.workdir, "SOURCES") - self.set('INSTALL_ROOT', install_root) - self.set('SRC_ROOT', source_root) self.set_python_libdirs() - + + # set products for product in env_info: self.set_a_product(product, logger) @@ -589,7 +621,7 @@ class FileEnvWriter: :param logger Logger: The logger instance to display messages :param out_dir str: The directory path where t put the output files :param src_root str: The application working directory - :param env_info str: + :param env_info str: The list of products to add in the files. ''' self.config = config self.logger = logger @@ -660,8 +692,7 @@ class FileEnvWriter: else: # set env from PRODUCT env.set_application_env(self.logger) - # set the prerequisites - env.set_prerequisites(self.logger) + # set the products env.set_products(self.logger, src_root=self.src_root) diff --git a/src/fileEnviron.py b/src/fileEnviron.py index d149ca3..aac1488 100644 --- a/src/fileEnviron.py +++ b/src/fileEnviron.py @@ -364,15 +364,15 @@ class LauncherFileEnviron: else: self.environ = os.environ # Initialize some variables - if not self.environ.has_key("PATH"): + if not "PATH" in self.environ.keys(): self.environ["PATH"]="" - if not self.environ.has_key("LD_LIBRARY_PATH"): + if not "LD_LIBRARY_PATH" in self.environ.keys(): self.environ["LD_LIBRARY_PATH"]="" - if not self.environ.has_key("PYTHONPATH"): + if not "PYTHONPATH" in self.environ.keys(): self.environ["PYTHONPATH"]="" - if not self.environ.has_key("TCLLIBPATH"): + if not "TCLLIBPATH" in self.environ.keys(): self.environ["TCLLIBPATH"]="" - if not self.environ.has_key("TKLIBPATH"): + if not "TKLIBPATH" in self.environ.keys(): self.environ["TKLIBPATH"]="" # four whitespaces for first indentation in a python script @@ -466,7 +466,7 @@ class LauncherFileEnviron: :param key str: the environment variable to check ''' - return self.environ.has_key(key) + return key in self.environ.keys() def get(self, key): '''Get the value of the environment variable "key" @@ -566,4 +566,108 @@ class LauncherFileEnviron: :param required bool: Do nothing if required is False """ - return \ No newline at end of file + return + +# The SALOME launcher template +withProfile = """#! /usr/bin/env python + +################################################################ +# WARNING: this file is automatically generated by SalomeTools # +# WARNING: and so could be overwritten at any time. # +################################################################ + +import os +import sys + + +# Add the pwdPath to able to run the launcher after unpacking a package +# Used only in case of a salomeTools package +out_dir_Path=os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname((os.path.abspath(os.path.dirname(__file__))))))) +prereq_install_Path=os.path.join(out_dir_Path , 'PREREQUISITES', 'INSTALL') +prereq_build_Path=os.path.join(out_dir_Path , 'PREREQUISITES', 'BUILD') + +# Preliminary work to initialize path to SALOME Python modules +def __initialize(): + + sys.path[:0] = [ os.path.join( 'PROFILE_INSTALL_DIR', 'bin', 'salome' ) ] + os.environ['ABSOLUTE_APPLI_PATH'] = 'PROFILE_INSTALL_DIR' + + # define folder to store omniorb config (initially in virtual application folder) + try: + from salomeContextUtils import setOmniOrbUserPath + setOmniOrbUserPath() + except Exception, e: + print e + sys.exit(1) +# End of preliminary work + +def main(args): + # Identify application path then locate configuration files + __initialize() + + if args == ['--help']: + from salomeContext import usage + usage() + sys.exit(0) + + #from salomeContextUtils import getConfigFileNames + #configFileNames, args, unexisting = getConfigFileNames( args, checkExistence=True ) + #if len(unexisting) > 0: + # print "ERROR: unexisting configuration file(s): " + ', '.join(unexisting) + # sys.exit(1) + + # Create a SalomeContext which parses configFileNames to initialize environment + try: + from salomeContext import SalomeContext, SalomeContextException + SalomeContext.addToSpecial=addToSpecial + context = SalomeContext(None) + + # Here set specific variables, if needed + # context.addToPath('mypath') + # context.addToLdLibraryPath('myldlibrarypath') + # context.addToPythonPath('mypythonpath') + # context.setVariable('myvarname', 'value') + + context.setVariable(r"PRODUCT_ROOT_DIR", out_dir_Path, overwrite=True) + # here your local standalone environment + + # Start SALOME, parsing command line arguments + context.runSalome(args) + #print 'Thank you for using SALOME!' + + except SalomeContextException, e: + import logging + logging.getLogger("salome").error(e) + sys.exit(1) +# +def addToSpecial(self, name, value, pathSep=None): + "add special dangerous cases: TCLLIBPATH PV_PLUGIN_PATH etc..." + #http://computer-programming-forum.com/57-tcl/1dfddc136afccb94.htm + #TCLLIBPATH: Tcl treats the contents of that variable as a list. Be happy, for you can now use drive letters on windows. + if value == '': + return + + specialBlanksKeys=["TCLLIBPATH", "TKLIBPATH"] + specialSemicolonKeys=["PV_PLUGIN_PATH"] + res=os.pathsep + if name in specialBlanksKeys: res=" " + if name in specialSemicolonKeys: res=";" + + if pathSep==None: + sep=res + else: + sep=pathSep + value = os.path.expandvars(value) # expand environment variables + self.getLogger().debug("Add to %s: %s", name, value) + env = os.getenv(name, None) + if env is None: + os.environ[name] = value + else: + os.environ[name] = value + sep + env #explicitely or not special path separator ?whitespace, semicolon? + +if __name__ == "__main__": + args = sys.argv[1:] + main(args) +# +""" + \ No newline at end of file