Salome HOME
add the possibility to bypass the reinitialisation of paths with a dedicated variable...
[tools/sat.git] / src / fileEnviron.py
index 4c0e12923da292a080db33a5722af5f462fcd47c..dbf0938545537fbe2e056f8434032a343a34c19e 100644 (file)
 import os
 import pprint as PP
 import src.debug as DBG
-
-bat_header="""@echo off
-
-rem The following variables are used only in case of a sat package
-set out_dir_Path=%~dp0
-set PRODUCT_OUT_DIR=%out_dir_Path%
-set prereq_install_Path=%out_dir_Path%\PREREQUISITES\INSTALL
-set prereq_build_Path=%out_dir_Path%\PREREQUISITES\BUILD
-"""
-
-
-bash_header="""#!/bin/bash
-##########################################################################
-#
-#### cleandup ###
-# cleanup a path (first parameter) from duplicated entries;
-# second parameter is the separator
-cleandup() {
-out_var=`echo $1 | awk -v sep=$2 '{                      \\
-     na = split($1,a,sep);                               \\
-     k=0;                                                \\
-     for(i=0;i<=na;i++) {                                \\
-       found=0;                                          \\
-       for(j=0;j<k;j++) {                                \\
-         if(a[i]==aa[j])                                 \\
-         {                                               \\
-           found=1;                                      \\
-           break;                                        \\
-         };                                              \\
-       };                                                \\
-       if(found==0) {                                    \\
-         aa[k++]=a[i];                                   \\
-       };                                                \\
-     };                                                  \\
-     ORS=sep;                                            \\
-     for(i=0;i<k;i++) {                                  \\
-       print aa[i];                                      \\
-     }                                                   \\
-   }' | sed -e 's|\\(.*\\)$1|\\1|g' -e 's|^[:;]||' -e 's|[:;]$||'`
-echo $out_var
-}
-### clean ###
-clean ()
-{
-xenv=`printenv $1`
-out_var=`cleandup $xenv $2`
-export $1=$out_var
-}
-
-# The 3 following variables are used only in case of a sat package
-export out_dir_Path=$(cd $(dirname ${BASH_SOURCE[0]});pwd)
-export PRODUCT_OUT_DIR=${out_dir_Path}
-export PRODUCT_ROOT_DIR=${PRODUCT_OUT_DIR}
-
-###########################################################################
-"""
-
-cfg_header='''[SALOME Configuration]
-'''
-
-Launcher_header='''# a generated SALOME Configuration file using python syntax
-'''
+import src.architecture
+import src.environment
 
 def get_file_environ(output, shell, environ=None):
     """Instantiate correct FileEnvironment sub-class.
@@ -89,8 +29,12 @@ def get_file_environ(output, shell, environ=None):
     :param shell str: the type of shell syntax to use.
     :param environ dict: a potential additional environment.
     """
+    if environ == None:
+        environ=src.environment.Environ({})
     if shell == "bash":
         return BashFileEnviron(output, environ)
+    if shell == "tcl":
+        return TclFileEnviron(output, environ)
     if shell == "bat":
         return BatFileEnviron(output, environ)
     if shell == "cfgForPy":
@@ -100,18 +44,21 @@ def get_file_environ(output, shell, environ=None):
     raise Exception("FileEnviron: Unknown shell = %s" % shell)
 
 class FileEnviron(object):
-    """Base class for shell environment
+    """\
+    Base class for shell environment
     """
     def __init__(self, output, environ=None):
-        """Initialization
+        """\
+        Initialization
         
         :param output file: the output file stream.
-        :param environ dict: a potential additional environment.
+        :param environ dict: SalomeEnviron.
         """
         self._do_init(output, environ)
 
     def __repr__(self):
-        """easy non exhaustive quick resume for debug print"""
+        """\
+        easy non exhaustive quick resume for debug print"""
         res = {
           "output" : self.output,
           "environ" : self.environ,
@@ -120,141 +67,185 @@ class FileEnviron(object):
         
 
     def _do_init(self, output, environ=None):
-        """Initialization
+        """\
+        Initialization
         
         :param output file: the output file stream.
         :param environ dict: a potential additional environment.
         """
         self.output = output
-        self.toclean = []
+        self.init_path=True # by default we initialise all paths, except PATH
         if environ is not None:
-            #if str(type(environ)) == "<type 'instance'>":
-            if id(environ) == id(os.environ):
-               DBG.tofix("set %s environ as python os.environ, are you sure it is safe ?" % self.__class__.__name__, True)
             self.environ = environ
         else:
-            DBG.tofix("set %s environ as COPY of python os.environ, are you sure it is safe ?" % self.__class__.__name__, True)
-            self.environ = dict(os.environ) #make a copy cvw 180320
+            self.environ = src.environment.Environ({})
 
     def add_line(self, number):
-        """Add some empty lines in the shell file
+        """\
+        Add some empty lines in the shell file
         
         :param number int: the number of lines to add
         """
         self.output.write("\n" * number)
 
     def add_comment(self, comment):
-        """Add a comment in the shell file
+        """\
+        Add a comment in the shell file
         
         :param comment str: the comment to add
         """
         self.output.write("# %s\n" % comment)
 
     def add_echo(self, text):
-        """Add a "echo" in the shell file
+        """\
+        Add a "echo" in the shell file
         
         :param text str: the text to echo
         """
         self.output.write('echo %s"\n' % text)
 
     def add_warning(self, warning):
-        """Add a warning "echo" in the shell file
+        """\
+        Add a warning "echo" in the shell file
         
         :param warning str: the text to echo
         """
         self.output.write('echo "WARNING %s"\n' % warning)
 
     def append_value(self, key, value, sep=os.pathsep):
-        '''append value to key using sep
-        
+        """\
+        append value to key using sep,
+        if value contains ":" or ";" then raise error
+
         :param key str: the environment variable to append
         :param value str: the value to append to key
         :param sep str: the separator string
-        '''
-        self.set(key, self.get(key) + sep + value)
-        if (key, sep) not in self.toclean:
-            self.toclean.append((key, sep))
+        """
+        # check that value so no contain the system separator
+        separator=os.pathsep
+        if separator in value:
+            raise Exception("FileEnviron append key '%s' value '%s' contains forbidden character '%s'" % (key, value, separator))
+        do_append=True
+        if self.environ.is_defined(key):
+            value_list = self.environ.get(key).split(sep)
+            if self.environ._expandvars(value) in value_list:
+                do_append=False  # value is already in key path : we don't append it again
+            
+        if do_append:
+            self.environ.append_value(key, value,sep)
+            self.set(key, self.get(key) + sep + value)
 
     def append(self, key, value, sep=os.pathsep):
-        '''Same as append_value but the value argument can be a list
+        """\
+        Same as append_value but the value argument can be a list
         
         :param key str: the environment variable to append
         :param value str or list: the value(s) to append to key
         :param sep str: the separator string
-        '''
+        """
         if isinstance(value, list):
-            self.append_value(key, sep.join(value), sep)
+            for v in value:
+                self.append_value(key, v, sep)
         else:
             self.append_value(key, value, sep)
 
     def prepend_value(self, key, value, sep=os.pathsep):
-        '''prepend value to key using sep
+        """\
+        prepend value to key using sep,
+        if value contains ":" or ";" then raise error
         
         :param key str: the environment variable to prepend
         :param value str: the value to prepend to key
         :param sep str: the separator string
-        '''
-        self.set(key, value + sep + self.get(key))
-        if (key, sep) not in self.toclean:
-            self.toclean.append((key, sep))
+        """
+        # check that value so no contain the system separator
+        separator=os.pathsep
+        if separator in value:
+            raise Exception("FileEnviron append key '%s' value '%s' contains forbidden character '%s'" % (key, value, separator))
+
+        do_not_prepend=False
+        if self.environ.is_defined(key):
+            value_list = self.environ.get(key).split(sep)
+            exp_val=self.environ._expandvars(value)
+            if exp_val in value_list:
+                do_not_prepend=True
+        if not do_not_prepend:
+            self.environ.prepend_value(key, value,sep)
+            self.set(key, value + sep + self.get(key))
 
     def prepend(self, key, value, sep=os.pathsep):
-        '''Same as prepend_value but the value argument can be a list
+        """\
+        Same as prepend_value but the value argument can be a list
         
         :param key str: the environment variable to prepend
         :param value str or list: the value(s) to prepend to key
         :param sep str: the separator string
-        '''
+        """
         if isinstance(value, list):
-            self.prepend_value(key, sep.join(value), sep)
+            for v in reversed(value): # prepend list, first item at last to stay first
+                self.prepend_value(key, v, sep)
         else:
             self.prepend_value(key, value, sep)
 
     def is_defined(self, key):
-        '''Check if the key exists in the environment
+        """\
+        Check if the key exists in the environment
         
         :param key str: the environment variable to check
-        '''
-        return (key in self.environ)
+        """
+        return self.environ.is_defined(key)
 
     def set(self, key, value):
-        '''Set the environment variable "key" to value "value"
+        """\
+        Set the environment variable 'key' to value 'value'
         
         :param key str: the environment variable to set
         :param value str: the value
-        '''
+        """
         raise NotImplementedError("set is not implement for this shell!")
 
     def get(self, key):
-        '''Get the value of the environment variable "key"
+        """\
+        Get the value of the environment variable "key"
         
         :param key str: the environment variable
-        '''
-        return '${%s}' % key
+        """
+        if src.architecture.is_windows():
+            return '%' + key + '%'
+        else:
+            return '${%s}' % key
 
-    def command_value(self, key, command):
-        '''Get the value given by the system command "command" 
-           and put it in the environment variable key.
-           Has to be overwritten in the derived classes
-           This can be seen as a virtual method
-        
+    def get_value(self, key):
+        """Get the real value of the environment variable "key"
+        It can help env scripts
         :param key str: the environment variable
-        :param command str: the command to execute
-        '''
-        raise NotImplementedError("command_value is not implement "
-                                  "for this shell!")
+        """
+        return self.environ.get_value(key)
 
-    def finish(self, required=True):
+    def finish(self):
         """Add a final instruction in the out file (in case of file generation)
         
         :param required bool: Do nothing if required is False
         """
-        for (key, sep) in self.toclean:
-            if sep != ' ':
-                self.output.write('clean %s "%s"\n' % (key, sep))
+        return
 
-class BashFileEnviron(FileEnviron):
-    """Class for bash shell.
+    def set_no_init_path(self):
+        """Set the no initialisation mode for all paths.
+           By default only PATH is not reinitialised. All others paths are
+           (LD_LIBRARY_PATH, PYTHONPATH, ...)
+           After the call to these function ALL PATHS ARE NOT REINITIALISED.
+           There initial value is inherited from the environment
+        """
+        self.init_path=False
+
+    def value_filter(self, value):
+        res=value
+        return res
+
+
+class TclFileEnviron(FileEnviron):
+    """\
+    Class for tcl shell.
     """
     def __init__(self, output, environ=None):
         """Initialization
@@ -263,39 +254,92 @@ class BashFileEnviron(FileEnviron):
         :param environ dict: a potential additional environment.
         """
         self._do_init(output, environ)
-        self.output.write(bash_header)
+        self.output.write(tcl_header.replace("<module_name>",
+                                             self.environ.get("sat_product_name")))
+        self.output.write("\nset software %s\n" % self.environ.get("sat_product_name") )
+        self.output.write("set version %s\n" % self.environ.get("sat_product_version") )
+        root=os.path.join(self.environ.get("sat_product_base_path"),  
+                                  "apps", 
+                                  self.environ.get("sat_product_base_name"), 
+                                  "$software", 
+                                  "$version")
+        self.output.write("set root %s\n" % root) 
+        modules_to_load=self.environ.get("sat_product_load_depend")
+        if len(modules_to_load)>0:
+            # write module load commands for product dependencies
+            self.output.write("\n")
+            for module_to_load in modules_to_load.split(";"):
+                self.output.write(module_to_load+"\n")
 
     def set(self, key, value):
-        '''Set the environment variable "key" to value "value"
+        """Set the environment variable "key" to value "value"
         
         :param key str: the environment variable to set
         :param value str: the value
-        '''
-        self.output.write('export %s="%s"\n' % (key, value))
-        self.environ[key] = value
-
-    def command_value(self, key, command):
-        '''Get the value given by the system command "command" 
-           and put it in the environment variable key.
-           Has to be overwritten in the derived classes
-           This can be seen as a virtual method
+        """
+        self.output.write('setenv  %s "%s"\n' % (key, value))
+        self.environ.set(key, value)
+        
+    def get(self, key):
+        """\
+        Get the value of the environment variable "key"
         
         :param key str: the environment variable
-        :param command str: the command to execute
-        '''
-        self.output.write('export %s=$(%s)\n' % (key, command))
+        """
+        return self.environ.get(key)
 
-    def finish(self, required=True):
-        """Add a final instruction in the out file (in case of file generation)
+    def append_value(self, key, value, sep=os.pathsep):
+        """append value to key using sep
         
-        :param required bool: Do nothing if required is False
+        :param key str: the environment variable to append
+        :param value str: the value to append to key
+        :param sep str: the separator string
         """
-        if not required:
-            return
-        FileEnviron.finish(self, required)
+        if sep==os.pathsep:
+            self.output.write('append-path  %s   %s\n' % (key, value))
+        else:
+            self.output.write('append-path --delim=\%c %s   %s\n' % (sep, key, value))
+
+    def prepend_value(self, key, value, sep=os.pathsep):
+        """prepend value to key using sep
+        
+        :param key str: the environment variable to prepend
+        :param value str: the value to prepend to key
+        :param sep str: the separator string
+        """
+        if sep==os.pathsep:
+            self.output.write('prepend-path  %s   %s\n' % (key, value))
+        else:
+            self.output.write('prepend-path --delim=\%c %s   %s\n' % (sep, key, value))
+
+        
+class BashFileEnviron(FileEnviron):
+    """\
+    Class for bash shell.
+    """
+    def __init__(self, output, environ=None):
+        """Initialization
+        
+        :param output file: the output file stream.
+        :param environ dict: a potential additional environment.
+        """
+        self._do_init(output, environ)
+        self.output.write(bash_header)
+
+    def set(self, key, value):
+        """Set the environment variable "key" to value "value"
+        
+        :param key str: the environment variable to set
+        :param value str: the value
+        """
+        self.output.write('export %s="%s"\n' % (key, value))
+        self.environ.set(key, value)
+        
+
         
 class BatFileEnviron(FileEnviron):
-    """for Windows batch shell.
+    """\
+    for Windows batch shell.
     """
     def __init__(self, output, environ=None):
         """Initialization
@@ -314,40 +358,21 @@ class BatFileEnviron(FileEnviron):
         self.output.write("rem %s\n" % comment)
     
     def get(self, key):
-        '''Get the value of the environment variable "key"
+        """Get the value of the environment variable "key"
         
         :param key str: the environment variable
-        '''
+        """
         return '%%%s%%' % key
     
     def set(self, key, value):
-        '''Set the environment variable "key" to value "value"
+        """Set the environment variable "key" to value "value"
         
         :param key str: the environment variable to set
         :param value str: the value
-        '''
-        self.output.write('set %s=%s\n' % (key, value))
-        self.environ[key] = value
-
-    def command_value(self, key, command):
-        '''Get the value given by the system command "command" 
-           and put it in the environment variable key.
-           Has to be overwritten in the derived classes
-           This can be seen as a virtual method
-        
-        :param key str: the environment variable
-        :param command str: the command to execute
-        '''
-        self.output.write('%s > tmp.txt\n' % (command))
-        self.output.write('set /p %s =< tmp.txt\n' % (key))
-
-    def finish(self, required=True):
-        """Add a final instruction in the out file (in case of file generation)
-           In the particular windows case, do nothing
-        
-        :param required bool: Do nothing if required is False
         """
-        return
+        self.output.write('set %s=%s\n' % (key, self.value_filter(value)))
+        self.environ.set(key, value)
+
 
 class ContextFileEnviron(FileEnviron):
     """Class for a salome context configuration file.
@@ -362,33 +387,21 @@ class ContextFileEnviron(FileEnviron):
         self.output.write(cfg_header)
 
     def set(self, key, value):
-        '''Set the environment variable "key" to value "value"
+        """Set the environment variable "key" to value "value"
         
         :param key str: the environment variable to set
         :param value str: the value
-        '''
+        """
         self.output.write('%s="%s"\n' % (key, value))
-        self.environ[key] = value
+        self.environ.set(key, value)
 
     def get(self, key):
-        '''Get the value of the environment variable "key"
+        """Get the value of the environment variable "key"
         
         :param key str: the environment variable
-        '''
+        """
         return '%({0})s'.format(key)
 
-    def command_value(self, key, command):
-        '''Get the value given by the system command "command" 
-           and put it in the environment variable key.
-           Has to be overwritten in the derived classes
-           This can be seen as a virtual method
-        
-        :param key str: the environment variable
-        :param command str: the command to execute
-        '''
-        raise NotImplementedError("command_value is not implement "
-                                  "for salome context files!")
-
     def add_echo(self, text):
         """Add a comment
         
@@ -404,48 +417,37 @@ class ContextFileEnviron(FileEnviron):
         self.add_comment("WARNING %s"  % warning)
 
     def prepend_value(self, key, value, sep=os.pathsep):
-        '''prepend value to key using sep
+        """prepend value to key using sep
         
         :param key str: the environment variable to prepend
         :param value str: the value to prepend to key
         :param sep str: the separator string
-        '''
-        self.output.write('ADD_TO_%s: %s\n' % (key, value))
+        """
+        do_append=True
+        if self.environ.is_defined(key):
+            value_list = self.environ.get(key).split(sep)
+            #value cannot be expanded (unlike bash/bat case) - but it doesn't matter.
+            if value in value_list:
+                do_append=False  # value is already in key path : we don't append it again
+            
+        if do_append:
+            self.environ.append_value(key, value,sep)
+            self.output.write('ADD_TO_%s: %s\n' % (key, value))
 
     def append_value(self, key, value, sep=os.pathsep):
-        '''append value to key using sep
+        """append value to key using sep
         
         :param key str: the environment variable to append
         :param value str: the value to append to key
         :param sep str: the separator string
-        '''
+        """
         self.prepend_value(key, value)
 
-    def finish(self, required=True):
-        """Add a final instruction in the out file (in case of file generation)
-        
-        :param required bool: Do nothing if required is False
-        """
-        return
 
-def special_path_separator(name):
-    """TCLLIBPATH, TKLIBPATH, PV_PLUGIN_PATH environments variables need
-       some exotic path separator.
-       This function gives the separator regarding the name of the variable
-       to append or prepend.
-       
-    :param name str: The name of the variable to find the separator
-    """
-    special_blanks_keys=["TCLLIBPATH", "TKLIBPATH"]
-    special_semicolon_keys=["PV_PLUGIN_PATH"]
-    res=os.pathsep
-    if name in special_blanks_keys: res=" "
-    if name in special_semicolon_keys: res=";"
-    return res
-
-class LauncherFileEnviron:
-    """Class to generate a launcher file script 
-       (in python syntax) SalomeContext API
+class LauncherFileEnviron(FileEnviron):
+    """\
+    Class to generate a launcher file script 
+    (in python syntax) SalomeContext API
     """
     def __init__(self, output, environ=None):
         """Initialization
@@ -453,47 +455,44 @@ class LauncherFileEnviron:
         :param output file: the output file stream.
         :param environ dict: a potential additional environment.
         """
-        self.output = output
-        self.toclean = []
-        if environ is not None:
-            self.environ = environ
-        else:
-            self.environ = os.environ
-        # Initialize some variables
-        if not "PATH" in self.environ.keys():
-            self.environ["PATH"]=""
-        if not "LD_LIBRARY_PATH" in self.environ.keys():
-            self.environ["LD_LIBRARY_PATH"]=""
-        if not "PYTHONPATH" in self.environ.keys():
-            self.environ["PYTHONPATH"]=""
-        if not "TCLLIBPATH" in self.environ.keys():
-            self.environ["TCLLIBPATH"]=""
-        if not "TKLIBPATH" in self.environ.keys():
-            self.environ["TKLIBPATH"]=""
+        self._do_init(output, environ)
+        self.python_version=self.environ.get("sat_python_version")
+        self.bin_kernel_root_dir=self.environ.get("sat_bin_kernel_install_dir")
 
         # four whitespaces for first indentation in a python script
         self.indent="    "
         self.prefix="context."
         self.setVarEnv="setVariable"
-        
         self.begin=self.indent+self.prefix
-        self.output.write(Launcher_header)
+
+        # write the begining of launcher file.
+        # choose the template version corresponding to python version 
+        # and substitute BIN_KERNEL_INSTALL_DIR (the path to salomeContext.py)
+        if self.python_version == 2:
+            launcher_header=launcher_header2
+        else:
+            launcher_header=launcher_header3
+        # in case of Windows OS, Python scripts are not executable.  PyExe ?
+        if src.architecture.is_windows():
+            launcher_header = launcher_header.replace("#! /usr/bin/env python3",'')
+        self.output.write(launcher_header\
+                          .replace("BIN_KERNEL_INSTALL_DIR", self.bin_kernel_root_dir))
+
+        # for these path, we use specialired functions in salomeContext api
         self.specialKeys={"PATH": "Path",
                           "LD_LIBRARY_PATH": "LdLibraryPath",
                           "PYTHONPATH": "PythonPath"}
 
-    def change_to_launcher(self, value):
-        """
-        """
-        res=value
-        return res
+        # we do not want to reinitialise PATH.
+        # for that we make sure PATH is in self.environ
+        # and therefore we will not use setVariable for PATH
+        if not self.environ.is_defined("PATH"):
+            self.environ.set("PATH","")
 
-    def add_line(self, number):
-        """Add some empty lines in the launcher file
-        
-        :param number int: the number of lines to add
-        """
-        self.output.write("\n" * number)
+        if self.init_path:
+            self.output.write('\n'+self.indent)
+            self.add_echo("Modifier cette variable pour ne pas réinitialiser les PATHS")
+            self.output.write(self.indent+'reinitialise_paths=True\n\n')
 
     def add_echo(self, text):
         """Add a comment
@@ -509,137 +508,151 @@ class LauncherFileEnviron:
         """
         self.output.write('# "WARNING %s"\n' % warning)
 
-    def append_value(self, key, value, sep=":"):
-        '''append value to key using sep
+    def append_value(self, key, value, sep=os.pathsep):
+        """append value to key using sep,
+        if value contains ":" or ";" then raise error
         
-        :param key str: the environment variable to append
-        :param value str: the value to append to key
+        :param key str: the environment variable to prepend
+        :param value str: the value to prepend to key
         :param sep str: the separator string
-        '''
-        if self.is_defined(key) :
-            self.add(key, value)
-        else :
+        """
+        # check that value so no contain the system separator
+        separator=os.pathsep
+        msg="LauncherFileEnviron append key '%s' value '%s' contains forbidden character '%s'"
+        if separator in value:
+            raise Exception(msg % (key, value, separator))
+
+        is_key_defined=self.environ.is_defined(key)
+        conditional_reinit=False
+        if (self.init_path and (not is_key_defined)):
+            # reinitialisation mode set to true (the default)
+            # for the first occurrence of key, we set it.
+            # therefore key will not be inherited from environment
+            self.output.write(self.indent+'if reinitialise_paths:\n'+self.indent)
             self.set(key, value)
+            self.output.write(self.indent+'else:\n'+self.indent)
+            conditional_reinit=True # in this case do not register value in self.environ a second time
+
+        # in all other cases we use append (except if value is already the key
+        do_append=True
+        if is_key_defined:
+            value_list = self.environ.get(key).split(sep)
+            # rem : value cannot be expanded (unlike bash/bat case) - but it doesn't matter.
+            if value in value_list:
+                do_append=False  # value is already in key path : we don't append it again
+            
+        if do_append:
+            if not conditional_reinit:
+                self.environ.append_value(key, value,sep) # register value in self.environ
+            if key in self.specialKeys.keys():
+                #for these special keys we use the specific salomeContext function
+                self.output.write(self.begin+'addTo%s(r"%s")\n' % 
+                                  (self.specialKeys[key], self.value_filter(value)))
+            else:
+                # else we use the general salomeContext addToVariable function
+                self.output.write(self.indent+'appendPath(r"%s", r"%s",separator="%s")\n' 
+                                  % (key, self.value_filter(value), sep))
 
     def append(self, key, value, sep=":"):
-        '''Same as append_value but the value argument can be a list
+        """Same as append_value but the value argument can be a list
         
         :param key str: the environment variable to append
         :param value str or list: the value(s) to append to key
         :param sep str: the separator string
-        '''
+        """
         if isinstance(value, list):
-            self.append_value(key, sep.join(value), sep)
+            for v in value:
+                self.append_value(key, v, sep)
         else:
             self.append_value(key, value, sep)
 
-    def prepend_value(self, key, value, sep=":"):
-        '''prepend value to key using sep
+    def prepend_value(self, key, value, sep=os.pathsep):
+        """prepend value to key using sep,
+        if value contains ":" or ";" then raise error
         
         :param key str: the environment variable to prepend
         :param value str: the value to prepend to key
         :param sep str: the separator string
-        '''
-        if self.is_defined(key) :
-            self.add(key, value)
-        else :
+        """
+        # check that value so no contain the system separator
+        separator=os.pathsep
+        msg="LauncherFileEnviron append key '%s' value '%s' contains forbidden character '%s'"
+        if separator in value:
+            raise Exception(msg % (key, value, separator))
+
+        is_key_defined=self.environ.is_defined(key)
+        conditional_reinit=False
+        if (self.init_path and (not is_key_defined)):
+            # reinitialisation mode set to true (the default)
+            # for the first occurrence of key, we set it.
+            # therefore key will not be inherited from environment
+            self.output.write(self.indent+'if reinitialise_paths:\n'+self.indent)
             self.set(key, value)
+            self.output.write(self.indent+'else:\n'+self.indent)
+            conditional_reinit=True # in this case do not register value in self.environ a second time
+
+        # in all other cases we use append (except if value is already the key
+        do_append=True
+        if is_key_defined:
+            value_list = self.environ.get(key).split(sep)
+            # rem : value cannot be expanded (unlike bash/bat case) - but it doesn't matter.
+            if value in value_list:
+                do_append=False  # value is already in key path : we don't append it again
+            
+        if do_append:
+            if not conditional_reinit:
+                self.environ.append_value(key, value,sep) # register value in self.environ
+            if key in self.specialKeys.keys():
+                #for these special keys we use the specific salomeContext function
+                self.output.write(self.begin+'addTo%s(r"%s")\n' % 
+                                  (self.specialKeys[key], self.value_filter(value)))
+            else:
+                # else we use the general salomeContext addToVariable function
+                self.output.write(self.begin+'addToVariable(r"%s", r"%s",separator="%s")\n' 
+                                  % (key, self.value_filter(value), sep))
+            
 
     def prepend(self, key, value, sep=":"):
-        '''Same as prepend_value but the value argument can be a list
+        """Same as prepend_value but the value argument can be a list
         
         :param key str: the environment variable to prepend
         :param value str or list: the value(s) to prepend to key
         :param sep str: the separator string
-        '''
+        """
         if isinstance(value, list):
-            self.prepend_value(key, sep.join(value), sep)
+            for v in value:
+                self.prepend_value(key, v, sep)
         else:
             self.prepend_value(key, value, sep)
 
-    def is_defined(self, key):
-        '''Check if the key exists in the environment
-        
-        :param key str: the environment variable to check
-        '''
-        return key in self.environ.keys()
-
-    def get(self, key):
-        '''Get the value of the environment variable "key"
-        
-        :param key str: the environment variable
-        '''
-        return '${%s}' % key
 
     def set(self, key, value):
-        '''Set the environment variable "key" to value "value"
+        """Set the environment variable "key" to value "value"
         
         :param key str: the environment variable to set
         :param value str: the value
-        '''
+        """
         self.output.write(self.begin+self.setVarEnv+
                           '(r"%s", r"%s", overwrite=True)\n' % 
-                          (key, self.change_to_launcher(value)))
-        self.environ[key] = value
+                          (key, self.value_filter(value)))
+        self.environ.set(key,value)
     
-    def add(self, key, value):
-        '''prepend value to key using sep
-        
-        :param key str: the environment variable to prepend
-        :param value str: the value to prepend to key
-        '''     
-        if key in self.specialKeys.keys():
-            self.output.write(self.begin+'addTo%s(r"%s")\n' % 
-                              (self.specialKeys[key],
-                               self.change_to_launcher(value)))
-            self.environ[key]+=":"+value
-            return
-        sep=special_path_separator(key)
-        self.output.write(self.indent+
-                          '#temporary solution!!! have to be defined in API a '
-                          '?dangerous? addToSpecial(r"%s", r"%s")\n' % 
-                          (key, value))
-        #pathsep not precised because do not know future os launch?
-        self.output.write(self.begin+'addToSpecial(r"%s", r"%s")\n' 
-                          % (key, self.change_to_launcher(value)))
-        self.environ[key]+=sep+value #here yes we know os for current execution
-
-    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
-        '''
-        self.output.write(self.indent+'#`%s`\n' % command)
-
-        import shlex, subprocess
-        args = shlex.split(command)
-        res=subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
-        out, __ = res.communicate()
-        self.output.write(self.begin+
-                          self.setVarEnv+
-                          '(r"%s", r"%s", overwrite=True)\n' % (key, out))
 
     def add_comment(self, comment):
-        # Special comment in case of the distène licence
+        # Special comment in case of the DISTENE licence
         if comment=="DISTENE license":
             self.output.write(self.indent+
                               "#"+
                               self.prefix+
                               self.setVarEnv+
                               '(r"%s", r"%s", overwrite=True)\n' % 
-                              ('DISTENE_LICENSE_FILE', 
-                               self.change_to_launcher(
-                                            'Use global envvar: DLIM8VAR')))
+                              ('DISTENE_LICENSE_FILE', 'Use global envvar: DLIM8VAR'))
             self.output.write(self.indent+
                               "#"+
                               self.prefix+
                               self.setVarEnv+
                               '(r"%s", r"%s", overwrite=True)\n' % 
-                              ('DLIM8VAR', 
-                               self.change_to_launcher(
-                                                '<your licence>')))
+                              ('DLIM8VAR', '<your licence>'))
             return
         if "setting environ for" in comment:
             self.output.write(self.indent+"#[%s]\n" % 
@@ -648,12 +661,18 @@ class LauncherFileEnviron:
 
         self.output.write(self.indent+"# %s\n" % comment)
 
-    def finish(self, required=True):
-        """Add a final instruction in the out file (in case of file generation)
-           In the particular launcher case, do nothing
+    def finish(self):
+        """\
+        Add a final instruction in the out file (in case of file generation)
+        In the particular launcher case, do nothing
         
         :param required bool: Do nothing if required is False
         """
+        if self.python_version == 2:
+            launcher_tail=launcher_tail_py2
+        else:
+            launcher_tail=launcher_tail_py3
+        self.output.write(launcher_tail)
         return
 
 class ScreenEnviron(FileEnviron):
@@ -681,7 +700,7 @@ class ScreenEnviron(FileEnviron):
              src.printcolors.printcInfo(name), sign, value))
 
     def is_defined(self, name):
-        return self.defined.has_key(name)
+        return name in self.defined
 
     def get(self, name):
         return "${%s}" % name
@@ -702,14 +721,52 @@ class ScreenEnviron(FileEnviron):
         value = self.get(name) + sep + value
         self.write("append", name, value)
 
-    def command_value(self, key, command):
-        pass
-
     def run_env_script(self, module, script):
         self.write("load", script, "", sign="")
 
-# The SALOME launcher template 
-withProfile =  """#! /usr/bin/env python
+
+#
+#  Headers
+#
+bat_header="""\
+@echo off
+
+rem The following variables are used only in case of a sat package
+set out_dir_Path=%~dp0
+"""
+
+tcl_header="""\
+#%Module -*- tcl -*-
+#
+# <module_name> module for use with 'environment-modules' package
+#
+"""
+
+bash_header="""\
+#!/bin/bash
+if [ "$BASH" = "" ]
+then
+  # check that the user is not using another shell
+  echo
+  echo "Warning! SALOME environment not initialized"
+  echo "You must run this script in a bash shell."
+  echo "As you are using another shell. Please first run: bash"
+  echo
+fi
+##########################################################################
+#
+# This line is used only in case of a sat package
+export out_dir_Path=$(cd $(dirname ${BASH_SOURCE[0]});pwd)
+
+###########################################################################
+"""
+
+cfg_header="""\
+[SALOME Configuration]
+"""
+
+launcher_header2="""\
+#! /usr/bin/env python
 
 ################################################################
 # WARNING: this file is automatically generated by SalomeTools #
@@ -719,24 +776,97 @@ withProfile =  """#! /usr/bin/env python
 import os
 import sys
 import subprocess
+import os.path
+
+# 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.realpath(__file__))
+
+# Preliminary work to initialize path to SALOME Python modules
+def __initialize():
+
+  sys.path[:0] = [ r'BIN_KERNEL_INSTALL_DIR' ]  # to get salomeContext
+  
+  # define folder to store omniorb config (initially in virtual application folder)
+  try:
+    from salomeContextUtils import setOmniOrbUserPath
+    setOmniOrbUserPath()
+  except Exception as e:
+    print(e)
+    sys.exit(1)
+# End of preliminary work
 
+# salome doc only works for virtual applications. Therefore we overwrite it with this function
+def _showDoc(modules):
+    for module in modules:
+      modulePath = os.getenv(module+"_ROOT_DIR")
+      if modulePath != None:
+        baseDir = os.path.join(modulePath, "share", "doc", "salome")
+        docfile = os.path.join(baseDir, "gui", module.upper(), "index.html")
+        if not os.path.isfile(docfile):
+          docfile = os.path.join(baseDir, "tui", module.upper(), "index.html")
+        if not os.path.isfile(docfile):
+          docfile = os.path.join(baseDir, "dev", module.upper(), "index.html")
+        if os.path.isfile(docfile):
+          out, err = subprocess.Popen(["xdg-open", docfile]).communicate()
+        else:
+          print ("Online documentation is not accessible for module:", module)
+      else:
+        print (module+"_ROOT_DIR not found!")
+
+def main(args):
+  # Identify application path then locate configuration files
+  __initialize()
+
+  if args == ['--help']:
+    from salomeContext import usage
+    usage()
+    sys.exit(0)
+
+
+  # Create a SalomeContext which parses configFileNames to initialize environment
+  try:
+    from salomeContext import SalomeContext, SalomeContextException
+    context = SalomeContext(None)
+    
+    # Here set specific variables, if needed
+    # context.addToPath('mypath')
+    # context.addToLdLibraryPath('myldlibrarypath')
+    # context.addToPythonPath('mypythonpath')
+    # context.setVariable('myvarname', 'value')
+
+    # Logger level error
+    context.getLogger().setLevel(40)
+"""
+
+launcher_header3="""\
+#! /usr/bin/env python3
+
+################################################################
+# WARNING: this file is automatically generated by SalomeTools #
+# WARNING: and so could be overwritten at any time.            #
+################################################################
+
+import os
+import sys
+import subprocess
+import os.path
 
 # 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.abspath(os.path.dirname(__file__))
+out_dir_Path=os.path.dirname(os.path.realpath(__file__))
 
 # Preliminary work to initialize path to SALOME Python modules
 def __initialize():
 
-  sys.path[:0] = [ 'BIN_KERNEL_INSTALL_DIR' ]
-  os.environ['ABSOLUTE_APPLI_PATH'] = 'KERNEL_INSTALL_DIR'
+  sys.path[:0] = [ r'BIN_KERNEL_INSTALL_DIR' ]
   
   # define folder to store omniorb config (initially in virtual application folder)
   try:
     from salomeContextUtils import setOmniOrbUserPath
     setOmniOrbUserPath()
-  except Exception, e:
-    print e
+  except Exception as e:
+    print(e)
     sys.exit(1)
 # End of preliminary work
 
@@ -754,9 +884,9 @@ def _showDoc(modules):
         if os.path.isfile(docfile):
           out, err = subprocess.Popen(["xdg-open", docfile]).communicate()
         else:
-          print "Online documentation is not accessible for module:", module
+          print("Online documentation is not accessible for module:", module)
       else:
-        print module+"_ROOT_DIR not found!"
+        print(module+"_ROOT_DIR not found!")
 
 def main(args):
   # Identify application path then locate configuration files
@@ -770,13 +900,12 @@ def main(args):
   #from salomeContextUtils import getConfigFileNames
   #configFileNames, args, unexisting = getConfigFileNames( args, checkExistence=True )
   #if len(unexisting) > 0:
-  #  print "ERROR: unexisting configuration file(s): " + ', '.join(unexisting)
+  #  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
@@ -787,54 +916,114 @@ def main(args):
 
     # Logger level error
     context.getLogger().setLevel(40)
+"""
 
-    context.setVariable(r"PRODUCT_ROOT_DIR", out_dir_Path, overwrite=True)
-    # here your local standalone environment
+launcher_tail_py2="""\
+    #[hook to integrate in launcher additionnal user modules]
+    
+    # Load all files extra.env.d/*.py and call the module's init routine]
+
+    extradir=out_dir_Path + r"/extra.env.d"
+
+    if os.path.exists(extradir):
+        import imp
+        sys.path.insert(0, os.path.join(os.getcwd(), extradir))
+        for filename in sorted(
+            filter(lambda x: os.path.isfile(os.path.join(extradir, x)),
+                   os.listdir(extradir))):
 
+            if filename.endswith(".py"):
+                f = os.path.join(extradir, filename)
+                module_name = os.path.splitext(os.path.basename(f))[0]
+                fp, path, desc = imp.find_module(module_name)
+                module = imp.load_module(module_name, fp, path, desc)
+                module.init(context, out_dir_Path)
+
+    #[manage salome doc command]
     if len(args) >1 and args[0]=='doc':
         _showDoc(args[1:])
         return
 
     # Start SALOME, parsing command line arguments
-    context.runSalome(args)
-    #print 'Thank you for using SALOME!'
-
-    # Logger level info
-    context.getLogger().setLevel(20)
+    out, err, status = context.runSalome(args)
+    sys.exit(status)
 
   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?
+# salomeContext only prepend variables, we use our own appendPath when required
+def appendPath(name, value, separator=os.pathsep):
+    if value == '':
+      return
+
+    value = os.path.expandvars(value) # expand environment variables
+    env = os.getenv(name, None)
+    if env is None:
+      os.environ[name] = value
+    else:
+      os.environ[name] = env + separator + value
+
 
 if __name__ == "__main__":
   args = sys.argv[1:]
   main(args)
 #
 """
+
+launcher_tail_py3="""\
+    #[hook to integrate in launcher additionnal user modules]
     
+    # Load all files extra.env.d/*.py and call the module's init routine]
+
+    extradir=out_dir_Path + r"/extra.env.d"
+
+    if os.path.exists(extradir):
+        import imp
+        sys.path.insert(0, os.path.join(os.getcwd(), extradir))
+        for filename in sorted(
+            filter(lambda x: os.path.isfile(os.path.join(extradir, x)),
+                   os.listdir(extradir))):
+
+            if filename.endswith(".py"):
+                f = os.path.join(extradir, filename)
+                module_name = os.path.splitext(os.path.basename(f))[0]
+                fp, path, desc = imp.find_module(module_name)
+                module = imp.load_module(module_name, fp, path, desc)
+                module.init(context, out_dir_Path)
+
+    #[manage salome doc command]
+    if len(args) >1 and args[0]=='doc':
+        _showDoc(args[1:])
+        return
+
+    # Start SALOME, parsing command line arguments
+    out, err, status = context.runSalome(args)
+    sys.exit(status)
+
+  except SalomeContextException as e:
+    import logging
+    logging.getLogger("salome").error(e)
+    sys.exit(1)
+# salomeContext only prepend variables, we use our own appendPath when required
+def appendPath(name, value, separator=os.pathsep):
+    if value == '':
+      return
+
+    value = os.path.expandvars(value) # expand environment variables
+    env = os.getenv(name, None)
+    if env is None:
+      os.environ[name] = value
+    else:
+      os.environ[name] = env + separator + value
+
+
+if __name__ == "__main__":
+  args = sys.argv[1:]
+  main(args)
+#
+"""
+    
+