Salome HOME
copyright notes
[tools/install.git] / runInstall
index 2f81342139fa2f57b02e0127b677b87a251bcb20..f5cf6d6032b76982715330b8977bd8add87b727d 100755 (executable)
 #!/usr/bin/env python
 
-import xmllib
-import sys, os, string, re
-
-#==============================================================
-# get_help_info
-#==============================================================
-def get_help_info() :
-    str = "\nPAL/SALOME Installation Wizard\n\n"
-    str = str + "\tUsage : \n\tInstall [-g|b] [-f <xml-file>] [-t <target-dir>] [-tmp <tmp-dir>]\n"
-    str = str + "\n"
-    str = str + " -g              Runs the Installation Wizard in the GUI mode.\n"
-    str = str + "                 In this case only <xmlfile> is taken into account \n"
-    str = str + "                 from the parameters list. This key is default.\n"
-    str = str + "\n"
-    str = str + " -b              Runs the Installation Wizard in the batch mode.\n"
-    str = str + "                 All the found parameters are taken in to account.\n"
-    str = str + "\n"
-    str = str + " -f <xml-file>   The configuration file to be parsed by the Installation Wizard.\n" 
-    str = str + "                 If this parameter is missed then the script tries to define\n"
-    str = str + "                 the Red Hat version and use the corresponding xml. For example,\n"
-    str = str + "                 for Red Hat 8.0 config_RedHat8_0.xml file is supposed to be used\n"
-    str = str + "                 by default. If the appropriate xml file is not found, the config.xml\n"
-    str = str + "                 is used by default.\n"
-    str = str + "\n"
-    str = str + " -t <target-dir> The target directory the products to be installed to.\n"
-    str = str + "                 This parameter overloads the target directory described in the\n"
-    str = str + "                 configuration file.\n"
-    str = str + "\n"
-    str = str + " -tmp <tmp-dir>  The directory which should be used for the temporary files.\n"
-    str = str + "                 This parameter overloads the temporary directory described in the\n"
-    str = str + "                 configuration file.\n"
-    str = str + "\n"
-    str = str + " -h              Prints help information.\n" 
-    return str
-
-#==============================================================
-# error_exit
-#==============================================================
-def error_exit (str = ""):
-    import sys
-    if len(str): res = "\n" + str + "\n"
-    else : res = ""
-    print res + \
-          get_help_info() 
-    sys.exit(1);
-
-
-#==============================================================
-# Cheks whether the passed parameter is a key.
-#==============================================================
-def is_key ( val ):
-    import re
-    if val is not None : 
-       return re.match(r'^-[a-zA-Z]', val)
-    return 0
+"""
+Installation Wizard launching script.
 
-#==============================================================
-# From the list of parameters extracts value following 'key' 
-#==============================================================
-def extract_parameter ( key, args ) :
-    import sys
-    length = len(args);
-    if ( length == 0 ) :  return None
-   
-    found = 0;
-
-    for i in range(0, length-1):
-        if  args[i] == key : 
-            if ( is_key ( args[i+1]) ) :
-               print " No value after key ", key
-                sys.exit(1);
-           
-           value = args[i+1]
-            if ( i < length - 2  and is_key ( args[i+2] ) == 0 ) : #control that only one value follows key 
-                                                              #(not a list). In this case params are correct.
-               print "Too much values after key ", key
-                sys.exit(1);
-           
-           found = 1; break;
-    
-    if (found) : 
-       return  value 
-    
-    return None
-
-
-#===============================================================
-# Extracts list of values following specified 'key' from 'args[]'
-#===============================================================
-def extract_list (key, args) : 
+This script is the part of the SALOME installation procedure.
+Author    : Vadim SANDLER, Open CASCADE SAS (vadim.sandler@opencascade.com)
+Created   : Thu Dec 18 12:01:00 2002
+Copyright : 2002-2006 CEA
 
-    lenght = len(args)
-    if ( args is None or lenght == 0 ):
-       error_exit()
+"""
 
-    list=[]
-    found = 0
+__version__ = "1.0.1"
 
-    for i in  range(0, length) :
-        if args[i] == key  : 
-            if (is_key ( args[i+1]))  : 
-                error_exit();
-           
-            for i in range (i+1, lenght):
-                if is_key(args[i]) : break
-                list.append(args[i])
-            found =1; break
-     
-    return list; #empty list is returned if no values after key
+# --- avoid "deprecation" warnings --- #
+import warnings
+warnings.filterwarnings("ignore", "", DeprecationWarning)
 
+# --- imports --- #
+import xmllib
+import sys, os, string, re
+import types
+import random
 
-#==============================================================
-# Method find the $key in the list and return 1 if success
-# and 0 otherwise. 
-#==============================================================
-def find_key (key, argv) :
-
-    if (not is_key(key)) : return 0
-
-    for simbol in  argv :
-        if simbol == key:
-            return 1 
-    return 0
+# --- global variables --- #
+opt_parser = None
+root_path  = None
 
-#==============================================================
-# Parse the list of parameters
-#==============================================================
-def parse_parameters (args) :
+# --- XML tags definition --- #
+__TAG__SOURCES__    = "install sources"
+__TAG__BINARIES__   = "install binaries"
+__TAG__NATIVE__     = "use native"
+__TAG__PREINSTALL__ = "not install"
 
-    if find_key('-h', args) :
-        print get_help_info();
-        import sys
-        sys.exit(0)
-        
-    xmlfile = extract_parameter("-f", args)
-    target_dir =  extract_parameter("-t", args)
-    tmp_dir = extract_parameter("-tmp", args)
-    if find_key('-b', args):
-        is_gui = 0
-    else : is_gui = 1
-    return [xmlfile, target_dir,  tmp_dir, is_gui]
+#------------------------------------------------------------------#
+#                                                                  #
+#                 COMMAND LINE ARGUMENTS PARSER                    #
+#                                                                  #
+#------------------------------------------------------------------#
 
+#===================================================================
+# class OptBaseError : base parse error
+#===================================================================
+class OptBaseError(Exception):
+    """
+    Base option parsing exception class
+    """
+    def __init__(self, msg):
+        self.msg = msg
+    def __str__ (self):
+        return self.msg
 
-#=================================================================
-# The first algorithm to create the dependencies list by their level
-#=================================================================
-def get_next_level(list, products):
-    
-    import re
-    result = []
-    expr = "(" + list[0].name
-    for i in range(1, len(list)):
-        expr = expr + "|"+ list[i].name
-    
-    expr = expr + ")$"
-    #expr=re.compile(expr)
+#===================================================================
+# class OptError : bad option error
+#===================================================================
+class OptError(OptBaseError):
+    """
+    Bad option exception class
+    """
+    def __init__ (self, msg, option):
+        self.msg = msg
+        self.option = option
+    def __str__ (self):
+        if self.option:
+            opt_prs = "<unknown>"
+            if self.option.short_opt and self.option.long_opt:
+                opt_prs = "%s/%s"%(self.option.short_opt,self.option.long_opt)
+            elif self.option.short_opt:
+                opt_prs = "%s"%(self.option.short_opt)
+            elif self.option.long_opt:
+                opt_prs = "%s"%(self.option.long_opt)
+            return "option %s: %s"%(opt_prs, self.msg)
+        return self.msg
 
-    for product in products:
-        deps = re.sub(r'\s+', "", product.dependencies)
-        if re.search(expr,  deps):
-            result.append(product)
+#===================================================================
+# class ArgError : bad option argument error
+#===================================================================
+class ArgError(OptBaseError):
+    """
+    Bad argument exception class
+    """
+    pass
 
-    return result
+#===================================================================
+# class ValError : bad command line parameter error
+#===================================================================
+class ValError(OptBaseError):
+    """
+    Bad parameter exception class
+    """
+    pass
 
+#===================================================================
+# class ArgOption : command line option
+#===================================================================
+class ArgOption:
+    """
+    Option class
+    """
+    attrs   = ["short_opt", "long_opt", "dest", "action", "type", "default", "metavar", "help"]
+    actions = ["store", "store_true", "store_false"]
+    types   = ["string", "int", "float", "bool"]
+    def __init__(self, *args, **kwargs):
+        # set defaults
+        for attr in self.attrs: setattr(self, attr, None)
+        # parse arguments
+        for i in range(len(args)):
+            if i > len(self.attrs)-1:
+                msg = "Wrong number of parameters is given (maximum is %d)" % len(self.attrs)
+                raise OptBaseError(msg)
+            setattr(self, self.attrs[i], args[i])
+        for arg in kwargs:
+            if arg not in self.attrs:
+                msg = "Invalid argument: %s" % arg
+                raise OptBaseError(msg)
+            setattr(self, arg, kwargs[arg])
+        # check short option key
+        if self.short_opt and \
+               not re.match("^-[a-zA-Z]$",self.short_opt):
+            msg  = "invalid short option key; "
+            msg += "should be of the form -x (x is letter)"
+            raise OptError(msg, self)
+        # check long option key
+        if self.long_opt and \
+               not re.match("^--[a-zA-Z][a-zA-Z0-9]*(-[a-zA-Z0-9]+)*$",self.long_opt):
+            msg  = "invalid long option key; "
+            msg += "should be of the form --word[[-word]...] "
+            msg += "(word is letters and digits sequence)"
+            raise OptError(msg, self)
+        # check that at least one option key is defined
+        if not self.short_opt and not self.long_opt:
+            msg  = "invalid option; neither short nor long option key is defined"
+            raise OptError(msg, self)
+        # destination
+        if not self.dest and self.long_opt:
+            self.dest = self.long_opt[2:].replace('-','_')
+        if not self.dest and self.short_opt:
+            self.dest = self.short_opt[1:]
+        # action
+        if not self.action:
+            self.action = "store"
+        if self.action not in self.actions:
+            msg  = "invalid action: %s" % self.action
+            raise OptError(msg, self)
+        # type
+        if not self.type:
+            if self.action in ["store_true","store_false"]: self.type = "bool"
+            else: self.type = "string"
+        if self.type not in self.types:
+            msg  = "invalid type: %s" % self.type
+            raise OptError(msg, self)
+        if self.action in ["store_true","store_false"] and self.type != "bool":
+            msg  = "invalid type: %s : should be 'bool' or None" % self.type
+            raise OptError(msg, self)
+        # default
+        if self.default:
+            try:
+                if self.type == "string": self.default = str(self.default)
+                if self.type == "int":    self.default = int(self.default)
+                if self.type == "float":  self.default = float(self.default)
+                if self.type == "bool":   self.default = boolean(self.default)
+            except :
+                msg  = "invalid default value type: should be %s" % self.type
+                raise OptError(msg, self)
+            pass
+        # metavar
+        if not self.metavar:
+            self.metavar = self.dest.upper()
+        # help
+        if not self.help:
+            self.help = ""
+        pass
 
-def create_levels(prods):
-    import copy
+    def to_string(self):
+        """
+        Returns string representation of the option
+        """
+        opts = []
+        opt = self.short_opt
+        if opt and self.action == "store" and self.metavar: opt += " %s" % self.metavar
+        if opt: opts.append(opt)
+        opt = self.long_opt
+        if opt and self.action == "store" and self.metavar: opt += "=%s" % self.metavar
+        if opt: opts.append(opt)
+        return (", ").join(opts)
     
-    products = copy.deepcopy(prods)
-    
-    result = {}
-    import re
-    #1. find the products with empty lists of dependencies
-    list = []
-    for product in products:
-        if len(re.sub(r'\s', "", product.dependencies)) == 0 :
-            list.append(product)
-
-    if len(list) == 0 :
-        raise RuntimeError, "Products that depend on nothing are not found"
-
-    # remove the first level products from the common list of products
-    for product in list :
-        products.remove(product)
-
-    ind = 0; 
-    result[0] = list
-
-    while (len(products)) :
-        res = get_next_level(list, products)
-        if len(res) == 0 :
-            raise RuntimeError, "Empty list of products is found"
-
-        for product in res :
-            products.remove(product)
+#===================================================================
+# class Values : resulting option values
+#===================================================================
+class Values:
+    """
+    Values class
+    """
+    def __init__(self):
+        pass
+        
+#===================================================================
+# class ArgParser : command line arguments parser
+#===================================================================
+class ArgParser:
+    """
+    Arguments parser class
+    """
+    def __init__(self):
+        self.options = []
+        pass
 
-        ind = ind +1
-        result[ind] =  res
-        list = res
-            
-    str = ""
-    for i in result.keys():
-        for product in  result[i]:
-            str = str + product.name + " "
+    def add_option(self, *args, **kwargs):
+        """Register an option"""
+        o = ArgOption(*args, **kwargs)
+        self._check_option(o)
+        self.options.append(o)
+        pass
 
-    return str;
+    def parse_args(self, args = None):
+        """Parse an arguments"""
+        if not args: args = sys.argv[1:]
+        values = Values()
+        for o in self.options:
+            if o.default:
+                setattr(values, o.dest, o.default)
+            elif not hasattr(values,o.dest):
+                setattr(values, o.dest, None)
+        try:
+            (values, args) = self._process_args(values, args)
+        except (ArgError, ValError), e:
+            self._error(e.msg)
+
+        return (values, args)
             
-#=================================================================
-# The second algorithm
-#=================================================================
-def get_dependencies_set(prods) :
-    import copy
-    import re
-
-    products = copy.deepcopy(prods)
-    deps = ""
-    list = []
-
-    while (len(products)) :
-
-        tmplist = []
-        #find the products with empty list of dependencies
-        for product in products:
-            product.dependencies = re.sub(r'\s+$', "", product.dependencies)
-            product.dependencies = re.sub(r'^\s+', "", product.dependencies)
-           
-            if len(product.dependencies) == 0 :
-               tmplist.append(product); 
-               deps = deps + " " + product.name
-
-        list.append(tmplist)
-
-        #remove the products names from other products dependencies
-        for item in tmplist:
-            products.remove(item)
+    def print_usage(self):
+        """Print usage"""
+        print "usage: %s [options]" % os.path.basename(sys.argv[0])
+        pass
 
-            regexpr1 = "((^|,(\s+)?)"+item.name+"$|^"+item.name+"(\s+)?,(\s+)?)"
-            regexpr2 = ",(\s+)?"+item.name+"(\s+)?,(\s+)?"
+    def print_help(self):
+        """Print help"""
+        self.print_usage()
+        print ""
+        olen = 0
+        _maxwidth, _indent = 79, 2
+        if len(self.options):
+            for option in self.options:
+                if olen < len(option.to_string()): olen = len(option.to_string())
+            print "options:"
+            for option in self.options:
+                strings = []
+                for hs in option.help.split("\n"):
+                    s = ""
+                    for w in hs.split():
+                        if len("%s %s" % (s,w)) > _maxwidth:
+                            strings.append(s.strip()); s = ""
+                        s = "%s %s" % (s,w)
+                    if s.strip(): strings.append(s.strip())
+                if not strings: strings[:0] = [""]
+                print "%s%s%s" % (option.to_string(), " "*(_indent+olen-len(option.to_string())), strings[0])
+                for i in range(1, len(strings)):
+                    print "%s%s" % (" "*(olen+_indent), strings[i])
+        pass
+    
+    def _check_option(self, option):
+        o = self._get_option(option.short_opt)
+        if not o: o = self._get_option(option.long_opt)
+        if o:
+            msg = "option conflicts with previously defined option(s)"
+            raise OptError(msg, option)
+        pass
 
-            for product in products:
-                product.dependencies = re.sub(r'\s+$', "", product.dependencies)
-                product.dependencies = re.sub(r'^\s+', "", product.dependencies)
+    def _get_option(self, opt_key):
+        if opt_key:
+            for o in self.options:
+                if opt_key in [o.short_opt, o.long_opt]: return o
+        return None
+        
+    def _error(self, msg):
+        self.print_usage()
+        sys.exit("\n%s: error: %s\n" % (os.path.basename(sys.argv[0]), msg))
+        pass
 
-                product.dependencies = re.sub(regexpr1, "", product.dependencies)
-                product.dependencies = re.sub(regexpr2, ",", product.dependencies)
+    def _check_value(self, option, value):
+        o = self._get_option(option)
+        try:
+            if o.type == "string": return str(value)
+            if o.type == "int":    return int(value)
+            if o.type == "float":  return float(value)
+            if o.type == "bool":   return boolean(value)
+        except:
+            msg  = "invalid value type for option %s: %s; " % (option, value)
+            msg += "should be %s" % o.type
+            raise ValError(msg)
+        raise OptBaseError("unknown error")
+
+    def _process_args(self, values, args):
+        res_args = []
+        cur_opt = None
+        rargs   = []
+        for index in range(len(args)):
+            a = args[index]
+            if cur_opt and cur_opt[1].action == "store":
+                setattr(values, cur_opt[1].dest, self._check_value(cur_opt[0], a))
+                cur_opt = None
+                continue
+            if a == "-":
+                rargs = args[index+1:]
+                break
+            elif re.match("^-[a-zA-Z].*", a):
+                for i in range(1,len(a)):
+                    if cur_opt and cur_opt[1].action == "store":
+                        setattr(values, cur_opt[1].dest, self._check_value(cur_opt[0], a[i:]))
+                        cur_opt = None
+                        break
+                    o = self._get_option("-%s"%a[i])
+                    if not o:
+                        raise ArgError("no such option: -%s"%a[i])
+                    if o.action == "store_true":
+                        setattr(values, o.dest, True)
+                    elif o.action == "store_false":
+                        setattr(values, o.dest, False)
+                    else:
+                        cur_opt = ("-%s"%a[i], o)
+                pass
+            elif re.match("^--[a-zA-Z][a-zA-Z0-9]*(-[a-zA-Z0-9]+)*", a):
+                oname  = ("%s="%a).split("=")[0]
+                ovalue = ("%s="%a).split("=")[1]
+                o = self._get_option(oname)
+                if not o:
+                    raise ArgError("no such option: %s" % oname)
+                if o.action == "store_true":
+                    if ovalue:
+                        raise ValError("option %s does not take a value" % oname)
+                    setattr(values, o.dest, True)
+                elif o.action == "store_false":
+                    if ovalue:
+                        raise ValError("option %s does not take a value" % oname)
+                    setattr(values, o.dest, False)
+                else:
+                    if ovalue:
+                        setattr(values, o.dest, self._check_value(oname, ovalue))
+                    else:
+                        cur_opt = (oname, o)
+                pass
+            elif a.startswith("-"):
+                raise ArgError("bad formatted option: %s" % a)
+            else:
+                rargs = args[index:]
+                break
+        if cur_opt and cur_opt[1].action == "store":
+            raise ValError("option %s requires value" % cur_opt[0])
+        return (values, rargs)
+
+#------------------------------------------------------------------#
+#                                                                  #
+#                 XML CONFIGURATION FILES PARSER                   #
+#                                                                  #
+#------------------------------------------------------------------#
 
-    return deps 
-    
-#==============================================================
-# Creates dir, returns the part of path that existed early.
-# Access may be defined.
-#==============================================================
-def create_dir (directory, access = 0777):
-    import string, os
-    dirs = string.split(directory, "/")
-    existing = ""; dir = ""
-    root = ""
-    for item in dirs:
-        if len(item) == 0:  continue
-        dir = dir + "/"+item
-        if os.path.exists(dir):
-            existing = dir
-        else:
-            os.mkdir(dir, access )
-            #root= existing + "/"+item
-            if dir == existing + "/"+item :
-                root = dir
-            #else : root = existing
-    
-    return root
+#===================================================================
+# class Config : general configuration options : version, OS, etc...
+#===================================================================
+class Config :
+    """
+    General configuration file options:
+    - Install Wizard window caption
+    - SALOME platform version
+    - Copyright and libcense info
+    - Target Linux OS version
+    """
+    def __init__(self,
+                 theVersion   = None,
+                 theCaption   = None,
+                 theCopyright = None,
+                 theLicense   = None,
+                 theOS        = None):
+        self.version   = strip(theVersion)
+        self.caption   = strip(theCaption)
+        self.copyright = strip(theCopyright)
+        self.license   = strip(theLicense)
+        self.os        = strip(theOS)
 
+#===================================================================
+# class Path : default target, temporary directories options
+#===================================================================
+class Path :
+    """
+    Path options:
+    - default target directory
+    - default temporary directory
+    """
+    def __init__(self,
+                 theTargetdir = None,
+                 theTmpdir    = None):
+        self.targetdir = strip(theTargetdir)
+        self.tmpdir    = strip(theTmpdir)
+        
 #==============================================================
-# class Product 
+# class Product : pre-requisite product options
 #==============================================================
-
 class Product :
-    def __init__(self, theName,
+    """
+    Product options:
+    - name, version
+    - supported installation modes and the default one
+    - dependencies
+    - required disk space
+    - installation script
+    - etc...
+    """
+    def __init__(self,
+                 theName,
                  theVersion            = None,
                  theInstall            = None,
                  theSupportred         = None,
-                 theDisable            = None,
                  theDependencies       = None,
                  theInstalldiskspace   = None,
                  theTemporarydiskspace = None,
                  theScript             = None,
                  thePickUpEnvironment  = None):
-        
-
-        self.name               = theName
-        self.version            = theVersion
-        self.install            = theInstall
-        self.supported          = theSupportred
-        self.disable            = theDisable
-        self.dependencies       = theDependencies
-        self.installdiskspace   = theInstalldiskspace
-        self.temporarydiskspace = theTemporarydiskspace
-        self.script             = theScript
-        self.pickupEnv          = thePickUpEnvironment
-
-#===================================================================
-# class Config
-#===================================================================
-class Config :
-    def __init__(self, theVersion='', theCaption='', theCopyright='', theLicense='', theOS=''):
-        self.version   = theVersion
-        self.caption   = theCaption
-        self.copyright = theCopyright
-        self.license   = theLicense
-        self.os        = theOS
-
-
-#===================================================================
-# class Path
-#===================================================================
-class Path :
-    def __init__(self, theTargetdir=".", theTmpdir="."):
-        self.targetdir = theTargetdir
-        self.tmpdir    = theTmpdir
+        self.name               = strip(theName)
+        self.version            = strip(theVersion)
+        self.install            = strip(theInstall)
+        self.supported          = strip(theSupportred)
+        self.dependencies       = strip(theDependencies)
+        self.installdiskspace   = strip(theInstalldiskspace)
+        self.temporarydiskspace = strip(theTemporarydiskspace)
+        self.script             = strip(theScript)
+        self.pickupEnv          = strip(thePickUpEnvironment)
 
-        
 #===================================================================
-# class ConfigParser
+# class ConfigParser : XML files parser implementation
 #===================================================================
 class ConfigParser(xmllib.XMLParser):
+    """
+    XML configuration files parser
+    """
     def __init__(self):
         xmllib.XMLParser.__init__(self)
         self.products = []
@@ -335,37 +443,38 @@ class ConfigParser(xmllib.XMLParser):
         self.currentdata.append(data)
         
     def start_product(self, attrs):
-        aProduct = Product(attrs['name'],
-                           attrs['version'],
-                           attrs['install'],
-                           attrs['supported'],
-                           attrs['disable'],
-                           attrs['dependancies'],
-                           attrs['installdiskspace'],
-                           attrs['temporarydiskspace'],
-                           attrs['script'])
-
-        if attrs.has_key( 'pickupenv' ):
-            aProduct.pickupEnv = attrs['pickupenv']
-
+        if not attrs.get('name', '').strip():         return
+        if check_bool(attrs.get('disable', 'false')): return
+        aProduct = Product(attrs.get('name'),
+                           attrs.get('version',            None),
+                           attrs.get('install',            None),
+                           attrs.get('supported',          None),
+                           attrs.get('dependancies',       None),
+                           attrs.get('installdiskspace',   None),
+                           attrs.get('temporarydiskspace', None),
+                           attrs.get('script',             None),
+                           attrs.get('pickupenv',          None))
         self.products.append(aProduct)
+        pass
 
     def end_product(self):
         pass
 
     def start_config(self, attrs):
-        self.config = Config(attrs['version'],
-                             attrs['caption'],
-                             attrs['copyright'],
-                             attrs['license'],
-                             attrs['os'])
+        self.config = Config(attrs.get('version',   None),
+                             attrs.get('caption',   None),
+                             attrs.get('copyright', None),
+                             attrs.get('license',   None),
+                             attrs.get('os',        None))
+        pass
+    
     def end_config(self):
         pass
 
     def start_path (self, attrs):
-        self.path = Path(attrs['targetdir'],
-                         attrs['tempdir'])
-        print self.path.tmpdir
+        self.path = Path(attrs.get('targetdir', None),
+                         attrs.get('tempdir',   None))
+        pass
         
     def end_path(self):
         pass
@@ -376,114 +485,461 @@ class ConfigParser(xmllib.XMLParser):
                 return product
         return None
 
+#------------------------------------------------------------------#
+#                                                                  #
+#                         SERVICE FUNCTIONS                        #
+#                                                                  #
+#------------------------------------------------------------------#
+
+#==============================================================
+# message: prints diagnostic information
+#==============================================================
+def message(msg):
+    """
+    Prints diagnostic information.
+    """
+    if msg.strip():
+        print ">>>", msg
+    pass
+
+#==============================================================
+# warning: prints warning
+#==============================================================
+def warning(msg):
+    """
+    Prints warning.
+    """
+    if msg.strip():
+        print ""
+        print msg
+        print ""
+    pass
+
+#==============================================================
+# error_exit : prints (optionally) error string, then prints
+#              help information and quits
+#==============================================================
+def error_exit(msg = "", print_help = True):
+    """
+    Prints (optionally) error string,
+    then prints help information and quits.
+    """
+    # print error message
+    if len(msg.strip()):
+        print ""
+        print msg
+        print ""
+    # print help information
+    if print_help:
+        global opt_parser
+        if opt_parser:
+            opt_parser.print_help() 
+            print ""
+    # cleaning 
+    clean_all()
+    # quit
+    sys.exit(1);
+    pass
+
+#==============================================================
+# boolean : Converts string to boolean value.
+#==============================================================
+def boolean(val):
+    """
+    Converts string to boolean value if possible.
+    Raises exception if wrong string is used.
+    """
+    if isinstance(val, types.StringType):
+        if val.strip().lower()   in ["true",  "yes", "ok"]     : return True
+        elif val.strip().lower() in ["false", "no",  "cancel"] : return False
+        else: raise TypeError("invalid boolean value")
+    return bool(val)
+
+#=================================================================
+# check_bool : checks boolean value: yes/no, true/false, 1/0
+#=================================================================
+def check_bool(val):
+    """
+    Checks boolean value.
+    """
+    try:
+        return boolean(val)
+    except:
+        pass
+    return False
+
+#==============================================================
+# clean_all : performs system cleaning before exiting
+#==============================================================
+def clean_all():
+    """
+    Performs system cleaning before exiting.
+    """
+    global root_path
+    remove_dir(root_path)
+    pass
+
+#==============================================================
+# parse_parameters : parses command line arguments
+#==============================================================
+def parse_parameters():
+    """
+    Parses command line arguments.
+    """
+    global opt_parser
+    opt_parser = ArgParser()
+    
+    help_str  = "Runs the Installation Wizard in the GUI mode [default].\n"
+    opt_parser.add_option("-g",
+                          "--gui",
+                          action="store_true",
+                          dest="gui",
+                          default=True,
+                          help=help_str)
+    help_str  = "Runs the Installation Wizard in the TUI mode."
+    opt_parser.add_option("-b",
+                          "--batch",
+                          action="store_false",
+                          dest="gui",
+                          help=help_str)
+    help_str  = "The configuration xml file.\n"
+    help_str += "If this parameter is missing, then the program tries to define the "
+    help_str += "Linux platform and use the corresponding xml file. For example, "
+    help_str += "for Red Hat 8.0 config_RedHat_8.0.xml file is used in this case. "
+    help_str += "If program fails to define target Linux platform or the corresponding "
+    help_str += "xml file is not provided with the Installation Wizard, then default "
+    help_str += "config.xml file is used."
+    opt_parser.add_option("-f",
+                          "--file",
+                          action="store",
+                          dest="xmlfile",
+                          metavar="FILE",
+                          help=help_str)
+    help_str  = "The target directory the products to be installed to.\n"
+    help_str += "When used this parameter overrides the default target directory "
+    help_str += "defined in the configuration xml file."
+    opt_parser.add_option("-d",
+                          "--target",
+                          action="store",
+                          dest="target_dir",
+                          metavar="DIR",
+                          help=help_str)
+    help_str  = "The directory to be used for temporary files.\n"
+    help_str += "When used this parameter overrides the default temporary directory "
+    help_str += "defined in the configuration xml file."
+    opt_parser.add_option("-t",
+                          "--tmp",
+                          action="store",
+                          dest="tmp_dir",
+                          metavar="DIR",
+                          help=help_str)
+    help_str  = "Prints version information and quits."
+    opt_parser.add_option("-v",
+                          "--version",
+                          action="store_true",
+                          help=help_str)
+    help_str  = "Prints this help and quits."
+    opt_parser.add_option("-h",
+                          "--help",
+                          action="store_true",
+                          help=help_str)
+    (options, args) = opt_parser.parse_args()
+    if options.help:
+        # print help info and quit
+        print "\nSALOME Installation Wizard\n"
+        opt_parser.print_help()
+        print ""
+        sys.exit(0)
+    if options.version:
+        # print version info and quit
+        print ""
+        cmd = "./bin/SALOME_InstallWizard --version"
+        os.system(cmd)
+        print ""
+        sys.exit(0)
+    return [options.xmlfile, options.target_dir, options.tmp_dir, options.gui]
+
+#=================================================================
+# strip : removes spaces at the beginning and at the end of the 
+#         <param> if it is of string type
+#=================================================================
+def strip(param):
+    """
+    Removes spaces at the beginning and at the end
+    of the given parameter.
+    """
+    if type(param) == types.StringType:
+        return param.strip()
+    return param
+    
+#=================================================================
+# get_dependencies : extract products dependencies
+#=================================================================
+def get_dependencies(prods):
+    """
+    Gets full list of pre-requisite products.
+    """
+    list = []
+    for product in prods:
+        deps = product.dependencies.split(",")
+        for dep in deps:
+            if dep and not dep in list:
+                list.append( dep )
+                
+        if product and not product in list:
+            list.append( product.name )
+            
+    return " ".join( list )
+
+#==============================================================
+# create_dir : creates a directory with (optional) permissions,
+#              returns the part of path that existed before
+#              directory creation; exits with error if access
+#              is denied
+#==============================================================
+def create_dir(directory, access = 0777):
+    """
+    Creates a directory with (optional) permissions,
+    returns the part of path that existed before
+    directory creation; exits with error if access
+    is denied.
+    """
+    dirs = string.split(directory, "/")
+    existing = "";
+    dir = ""
+    root = ""
+    for subdir in dirs:
+        if len(subdir) == 0:  continue
+        dir = "%s/%s"%(dir, subdir)
+        if os.path.exists(dir):
+            existing = dir
+        else:
+            try:
+                os.mkdir(dir, access)
+            except:
+                error_exit("Can't create directory: %s.\nAccess is denied."%directory)
+            if dir == "%s/%s"%(existing, subdir):
+                root = dir
+    return root
+
+#==============================================================
+# substituteVars : performes environment variables substistution
+#                  the given string; if varibale is not defined
+#                  it is substituted by the empty string
+#==============================================================
+def substituteVars(str):
+    """
+    Performes environment variables substistution.
+    """
+    str = os.path.expanduser(str)
+    str = os.path.expandvars(str)
+    return str
+
 #================================================================
-# get the path using file name
+# get_program_path : gets program's directory path
+#                    (and performs 'cd' command there) 
 #================================================================
-def get_current_path(file_name):
-    path = "."; where = string.rfind(file_name,'/');
-    if (where != -1):
-        path = (file_name)[: where]
-        os.chdir(path);
-    path = os.getcwd() + "/"
-    return path
+def get_program_path():
+    """
+    Returns the program directory path
+    (and make this directory current).
+    """
+    path = os.path.dirname(sys.argv[0])
+    if path:
+        os.chdir(path)
+    return os.getcwd()
 
 #================================================================
-# checks dir existing 
+# check_dir : checks directory existence
 #================================================================
 def check_dir(dir):
-    if (os.path.islink(dir)) :
-        native_dir = os.readlink(dir)
-        if not os.path.exists(native_dir) :
-            print "Bad link " + native_dir + " to directory " + native_dir + ". The last does not exist."
-            return 0 # problem
-    else :
+    """
+    Checks directory existence.
+    """
+    if (os.path.islink(dir)):
+        realpath = os.path.realpath(dir)
+        if not os.path.exists(realpath):
+            msg = "Invalid link %s.\nThe directory %s a link points to does not exist. Stopped..."%(dir,realpath)
+            error_exit(msg, False)
+    else:
         if not os.path.exists(dir):
-            print "Directory " + dir + " does not exist"
-            return 0
-    return 1
+            msg = "Directory %s does not exist. Stopped..."%dir
+            error_exit(msg, False)
+    pass
 
 #===============================================================
-# Checks the disk space. Exit from interpreter if there is no
-# enough disk space.
+# check_disk_space : checks the disk space;
+#                    quits if there is no enough disk space
 #===============================================================
-def check_disk_space(products, script_dir, target_dir, tmp_dir):
-    import re, string, os
+def check_disk_space(products, scripts_dir, target_dir, tmp_dir):
+    """
+    Checks if there is enough disk space to install products.
+    Quits with error if there is no enough disk space.
+    """
     install_space = 0
     temporary_space = 0
-    for product in products :
-        product.install = re.sub(r'^\s+', "", product.install)
-        product.install = re.sub(r'\s+$', "", product.install)
-        
-        if product.disable == "true" or product.install == "use native":
+    for product in products:
+        if product.install in [__TAG__NATIVE__, __TAG__PREINSTALL__]:
             continue
-        spaces = string.split( product.installdiskspace,',')
+        spaces = string.split(product.installdiskspace, ',')
         prod_space = spaces[0]
-        if (len(spaces) == 2 ) and (product.install == "install binaries") :
+        if (len(spaces) > 1 ) and (product.install == __TAG__SOURCES__):
             prod_space = spaces[1]
         install_space = install_space + string.atoi(prod_space)
-        temporary_space = temporary_space + string.atoi(product.temporarydiskspace)
-    res = os.system(scripts_dir + "checkSize.sh" + " " + target_dir + " " + str(install_space))
+        if product.install == __TAG__SOURCES__:
+            temporary_space = max(temporary_space, string.atoi(product.temporarydiskspace))
+
+    res = os.system("%s/%s %s %d"%(scripts_dir, "checkSize.sh", target_dir, install_space))
     if res:
-        print "There is no enough space to install the products."
-        return 0
+        msg = "There is no enough space to install the products. Stopped..."
+        error_exit(msg, False)
     
-    res = os.system(scripts_dir + "checkSize.sh" + " " + tmp_dir + " " + str(temporary_space))
+    res = os.system("%s/%s %s %d"%(scripts_dir, "checkSize.sh", tmp_dir, temporary_space))
     if res:
-        print "There is no enough space for tmp directory."
-        return 0
-    
-    return 1
+        msg = "There is no enough space for temporary directory. Stopped..."
+        error_exit(msg, False)
+    pass
  
+#===============================================================
+# remove_dir : removes temporary directory
+#===============================================================
+def remove_dir(path):
+    """
+    Removes temporary directory.
+    """
+    if path and os.path.exists(path):
+        os.system("rm -rf " + path)
+    pass
 
-#================================================================
-# main
-#================================================================
+#==============================================================
+# has_binaries : returns True if some product is installed from
+#                binaries
+#===============================================================
+def has_binaries(products):
+    """
+    Returns True if some product is installed in 'binaries' mode.
+    """
+    for product in products:
+        if product.install == __TAG__BINARIES__:
+            return True
+    return False
+
+#==============================================================
+# has_sources : returns True if some product is installed from
+#               sources
+#===============================================================
+def has_sources(products):
+    """
+    Returns True if some product is installed in 'sources' mode.
+    """
+    for product in products:
+        if product.install == __TAG__SOURCES__:
+            return True
+    return False
+
+#==============================================================
+# get_tmp_dir : gets temporary directory name
+#===============================================================
+def get_tmp_dir(dir):
+    """
+    Gets temporary directory path.
+    """
+    max_attempts = 100
+    dir_prefix="INSTALLWORK"
+    range_bottom = 0; range_top = 999999
+    for i in xrange(max_attempts):
+        tmp_dir = "%s/%s%d"%(dir, dir_prefix, random.randint(range_bottom,range_top))
+        if not os.path.exists( tmp_dir ):
+            return tmp_dir
+    return "%s/%s%d"%(dir, dir_prefix, random.randint(range_bottom,range_top))
     
-if __name__ == "__main__":
+#------------------------------------------------------------------#
+#                                                                  #
+#                    EXECUTION STARTS HERE                         #
+#                                                                  #
+#------------------------------------------------------------------#
     
-    cur_dir = get_current_path(sys.argv[0])
-   
-    [xml_file, target_dir, tmp_dir, is_gui]  = parse_parameters(sys.argv)
-
-    # define xml file -----------------
-    if (xml_file is None) :
+if __name__ == "__main__":
+    # get program dir
+    cur_dir = get_program_path()
+    # parse command line
+    [xml_file, target_dir, tmp_dir, is_gui] = parse_parameters()
+
+    # define xml file to be used
+    if (xml_file is None):
+        plt_name = ""
+        plt_ver  = ""
+        plt_bit  = ""
         xml_file_name = "config.xml"
-        if os.path.exists("/proc/version"):
-            data = open("/proc/version").readline()
-            res = re.search(r'Red\s+Hat\s+\w+(\s+)?(\d[.]\d)', data)
-            if res is not None:
-                num = re.sub("[.]", "_", (res.groups())[1])
-                filename = "config_RedHat" + num+ ".xml"
-                if (os.path.exists(cur_dir + filename)):
-                    xml_file_name = filename
-                
-        xml_file = cur_dir +  xml_file_name
-    if xml_file is None or not os.path.exists(xml_file):
-        error_exit("No xml file is found try to run with options -f <xmlfile>")
+        if os.path.exists("/etc/redhat-release"):
+            # - Red Hat Linux 8.0
+            # - Red Hat Linux 9
+            # - Mandrake Linux 10.1
+            # - Scientific Linux 3.0.5
+           # - Mandriva 2006.0 32bit/64bit
+            data = open("/etc/redhat-release").readline()
+           res = re.search(r'(.*)[L|l]inux.*release\s+([\d.]*)', data)
+           if res:
+                plt_name = "".join(res.group(1).split())
+                plt_ver  = res.group(2)
+               if re.search(r'x86_64', data):
+                    plt_bit  = "_64"
+        elif os.path.exists("/etc/debian_version"):
+            # - Debian 3.1
+            plt_name = "Debian"
+            plt_ver = open("/etc/debian_version").readline().strip()
+        elif os.path.exists("/etc/mandriva-release"):
+            # - Mandriva 2006 (an additional check if above check fails)
+            data = open("/etc/mandriva-release").readline()
+           res = re.search(r'(.*)[L|l]inux.*release\s+([\d.]*)', data)
+           if res:
+                plt_name = "".join(res.group(1).split())
+                plt_ver  = res.group(2)
+               if re.search(r'x86_64', data):
+                    plt_bit  = "_64"
+                pass
+        _xml_file_name = "config_%s_%s%s.xml"%(plt_name, plt_ver, plt_bit)
+        if plt_name and plt_ver and os.path.exists("%s/%s"%(cur_dir, _xml_file_name)):
+            xml_file_name = _xml_file_name
+        else:
+            msg  = "Not supported Linux platform!\n"
+            msg += "Trying to use default configuration file!"
+            warning(msg)
+
+        xml_file = "%s/%s"%(cur_dir, xml_file_name)
+
+    if not xml_file or not os.path.exists(xml_file):
+        msg = "Configuration file %s is not found!"%xml_file
+        error_exit(msg)
 
     if not os.access(xml_file, os.R_OK):
-        print "There is no acceess to read "+ xml_file
-        sys.exit(1)
+        msg = "There is no read access for %s file!"%xml_file
+        error_exit(msg)
 
     #---- GUI ----------------
+
     if is_gui : 
         env = os.environ
         if not env.has_key("PATH") :
             env["PATH"] = ""
         if not env.has_key("LD_LIBRARY_PATH") :
-            env["LD_LIBRARY_PATH"] ""
+            env["LD_LIBRARY_PATH"] = ""
 
-        env["LD_LIBRARY_PATH"] =  ".:" +  env["LD_LIBRARY_PATH"]
-        env["PATH"] = ".:"+ env["PATH"]
+        env["LD_LIBRARY_PATH"] =  ".:" + env["LD_LIBRARY_PATH"]
+        env["PATH"] = ".:" + env["PATH"]
 
-        sys.exit(os.system("cd " + cur_dir + "; ./bin/SALOME_InstallWizard " + xml_file +"&"))
-        
-        
+        cmd = "./bin/SALOME_InstallWizard --file %s"%xml_file
+        if target_dir is not None:
+            cmd += " --target %s"%target_dir
+        if tmp_dir is not None:
+            cmd += " --tmp %s"%tmp_dir
+        cmd += "&"
+        sys.exit(os.system(cmd))
 
     #-----  TUI ---------------------
 
-    #print xml_file, target_dir, tmp_dir, is_gui
-    
+    # parse XML file -----------
+    message("Parsing XML configuration file: %s"%xml_file)
     filehandle = open(xml_file)
     data = filehandle.read()
     filehandle.close()
@@ -491,97 +947,114 @@ if __name__ == "__main__":
     parser.feed(data)
     parser.close()
 
-    # definitions :
-    # map
-    what_to_do = { "install sources":"install_source",
-                   "install binaries":"install_binary",
-                   "use native":"try_native"}
-    # define tmp dir  -----------
-    if tmp_dir is None:
-        tmp_dir = parser.path.tmpdir
-    root_path = ""
-    if not os.path.exists(tmp_dir):
-        print "Creating " + tmp_dir; root_path = create_dir(tmp_dir) ; 
-        import random
-        tmp_dir = tmp_dir + "/INSTALLWORK" + str(random.randint(10000,100000))
-        create_dir(tmp_dir,0755)
-        if not os.path.exists(tmp_dir):
-            error_exit("There is no tmp directory " + tmp_dir + ". Use -tmp key to set directory or correct xml file\n\n")
-
-    if not os.access(tmp_dir, os.W_OK) :
-        str = "We have no write permissions for directory " + tmp_dir + ". Use -tmp key to set directory or correct xml file"
-        error_exit(str)
-        
-    # define target dir  --------
+    # actions map
+    what_to_do = { __TAG__SOURCES__    : "install_source",
+                   __TAG__BINARIES__   : "install_binary",
+                   __TAG__NATIVE__     : "try_native",
+                   __TAG__PREINSTALL__ : "try_preinstalled"}
+    # source directory map
+    bin_dir = ""
+    if parser.config.os:
+        bin_dir += "/%s"%parser.config.os
+    subdir = { __TAG__SOURCES__    : "SOURCES",
+               __TAG__BINARIES__   : "BINARIES" + bin_dir,
+               __TAG__NATIVE__     : "",
+               __TAG__PREINSTALL__ : ""}
+
+    # check scripts directory -----------
+    scripts_dir = "%s/%s"%(cur_dir, "config_files")
+    check_dir(scripts_dir)
+
+    # check products archives directories -----------
+    has_bin = has_binaries(parser.products)
+    has_src = has_sources(parser.products)
+    source_dir = "%s/%s"%(cur_dir, "Products")
+
+    if has_src or has_bin:
+        check_dir(source_dir)
+
+    if has_src:
+        check_dir("%s/%s"%(source_dir,subdir[__TAG__SOURCES__]))
+
+    if has_bin:
+        check_dir("%s/%s"%(source_dir,subdir[__TAG__BINARIES__]))
+
+    # check/create target dir -----------
     if target_dir is None:
         target_dir = parser.path.targetdir
+    target_dir = substituteVars(target_dir)
+
+    message("Creating target directory: " + target_dir)
+    create_dir(target_dir, 0755)
 
     if not os.path.exists(target_dir):
-        print "Creating " + target_dir; create_dir(target_dir, 0755)
-        if not os.path.exists(target_dir):
-            error_exit("There is no target directory " + target_dir + ". Use -t key to set directory or correct xml file\n\n")
+        error_exit("Invalid target directory: " + target_dir)
 
     if not os.access(target_dir, os.W_OK) :
-        str = "We have no write permissions for directory " + target_dir + ". Use -t key to set directory or correct xml file"
-        error_exit(str)
-    
-
-    # define products dir ------------
-    source_dir =  cur_dir + "Products" ; 
-    if not check_dir(source_dir):
-        if len(root_path) and os.path.exists(root_path):
-            os.system("rm -r -f "+ root_path)
-        sys.exit(1)
-       
-    subdir = {"install sources"  : "SOURCES",
-              "install binaries" : "BINARIES/"+parser.config.os,
-              "use native": ""}
-
-
-    #  define scripts dir ------------
-    scripts_dir = cur_dir + "config_files/"
-    if not check_dir(scripts_dir):
-        if len(root_path) and os.path.exists(root_path):
-            os.system("rm -r -f "+ root_path)
-        sys.exit(1)
-    os.chdir(scripts_dir)
+        error_exit("There is no write permissions for the directory: " + target_dir)
 
-    list_of_dep =  create_levels(parser.products)
-    #list_of_dep =  get_dependencies_set(parser.products)
+    # check/create temporary dir -----------
+    if tmp_dir is None:
+        tmp_dir = parser.path.tmpdir
+    if not tmp_dir:
+        tmp_dir = "/tmp"
+    tmp_dir = substituteVars(tmp_dir)
+    tmp_dir = get_tmp_dir(tmp_dir)
 
-    if check_disk_space(parser.products, scripts_dir, target_dir, tmp_dir) :
+    message("Creating temporary directory: " + tmp_dir)
+    root_path = create_dir(tmp_dir, 0755)
+   
+    if not os.path.exists(tmp_dir):
+        error_exit("Invalid temporary directory: " + tmp_dir)
 
-        # install products
-        for product in parser.products :
+    if not os.access(tmp_dir, os.W_OK) :
+        error_exit("There is no write permissions for the directory: " + tmp_dir)
+        
+    # check available disk space -----------
+    message("Checking available disk space")
+    check_disk_space(parser.products, scripts_dir, target_dir, tmp_dir)
 
-            if product.disable == "true": continue
+    # change current directory -----------
+    os.chdir(scripts_dir)
 
-            cmd = scripts_dir +  product.script + " " + \
-                  what_to_do[product.install]+ " " + \
-                  tmp_dir + " " + \
-                  source_dir + "/" + subdir[product.install] + " " + \
-                  target_dir + " " + \
-                  '"' + list_of_dep + '"' + " " + \
-                  product.name
+    # get dependencies list -----------
+    list_of_dep = get_dependencies(parser.products)
 
+    # starting -----------
+    message("Starting ...")
+    
+    # install products -----------
+    for product in parser.products:
+        message("... processing %s ..."%product.name)
+        cmd = '%s/%s %s %s %s/%s %s "%s" %s'%(scripts_dir,
+                                              product.script,
+                                              what_to_do[product.install],
+                                              tmp_dir,
+                                              source_dir,
+                                              subdir[product.install],
+                                              target_dir,
+                                              list_of_dep,
+                                              product.name)
+        res = os.system(cmd)
+
+    # pickup environment -----------
+    message("Creating environment files")
+    for product in parser.products :
+        if check_bool(product.pickupEnv):
+            cmd = '%s/%s pickup_env %s %s/%s %s "%s" %s'%(scripts_dir,
+                                                          product.script,
+                                                          tmp_dir,
+                                                          source_dir,
+                                                          subdir[product.install],
+                                                          target_dir,
+                                                          list_of_dep,
+                                                          product.name)
             res = os.system(cmd)
-            if res : break;
-
-        # pickup environment
-        for product in parser.products :
-
-            if product.disable == "true": continue
 
-            if product.pickupEnv == "true":
-                cmd = scripts_dir +  product.script + " " + \
-                      "pickup_env " + \
-                      tmp_dir + " " + \
-                      source_dir + "/" + subdir[product.install] + " " + \
-                      target_dir + " " + \
-                      '"' + list_of_dep + '"' + " " + \
-                      product.name
-                
-                res = os.system(cmd)
-
-    if len(root_path) and os.path.exists(root_path):
-        os.system("rm -r -f "+ root_path)
+    # clean temporary directory -----------
+    message("Cleaning temporary directory")
+    clean_all()
+    
+    # finishing -----------
+    message("Finished!")
+    pass