From: vsr Date: Thu, 9 Mar 2006 09:03:07 +0000 (+0000) Subject: Improve Installation Wizard: X-Git-Tag: V_3_2_0b1~36 X-Git-Url: http://git.salome-platform.org/gitweb/?a=commitdiff_plain;h=f5cbaf142ab66ea52adb4a3303ee06dc830e5708;p=tools%2Finstall.git Improve Installation Wizard: - introduce new runInstall script (support command line options in POSIX standard); - provide version information - provide About dialog box - pass 'targe directory' and 'temp directory' parameters from command line to the GUI IW --- diff --git a/bin/SALOME_InstallWizard b/bin/SALOME_InstallWizard index dba449b..b110116 100755 Binary files a/bin/SALOME_InstallWizard and b/bin/SALOME_InstallWizard differ diff --git a/runInstall b/runInstall index d5cf914..8472253 100755 --- a/runInstall +++ b/runInstall @@ -1,362 +1,431 @@ #!/usr/bin/env python +""" +Installation Wizard launching script. +""" + +__version__ = "1.0.1" + +# --- avoid "deprecation" warnings --- # import warnings warnings.filterwarnings("ignore", "", DeprecationWarning) +# --- imports --- # import xmllib import sys, os, string, re +import types +import random -#============================================================== -# get_help_info -#============================================================== -def get_help_info() : - str = "\nSALOME Installation Wizard\n\n" - str = str + "\tUsage : \n\tInstall [-g|b] [-f ] [-t ] [-tmp ]\n" - str = str + "\n" - str = str + " -g Runs the Installation Wizard in the GUI mode.\n" - str = str + " In this case only is taken into account \n" - str = str + " from the parameters list. This key is used by 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 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_RedHat_8.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 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 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 this help information.\n" - return str - -#============================================================== -# message finction -#============================================================== -def message(msg): - print ">>>", msg - -#============================================================== -# 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 - -#============================================================== -# 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) : - - lenght = len(args) - if ( args is None or lenght == 0 ): - error_exit() - - list=[] - found = 0 - - 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 - - -#============================================================== -# Method find the $key in the list and return 1 if success -# and 0 otherwise. -#============================================================== -def find_key (key, argv) : +# --- global variables --- # +opt_parser = None +root_path = None - if (not is_key(key)) : return 0 +# --- XML tags definition --- # +__TAG__SOURCES__ = "install sources" +__TAG__BINARIES__ = "install binaries" +__TAG__NATIVE__ = "use native" +__TAG__PREINSTALL__ = "not install" - for simbol in argv : - if simbol == key: - return 1 - return 0 +#------------------------------------------------------------------# +# # +# COMMAND LINE ARGUMENTS PARSER # +# # +#------------------------------------------------------------------# -#============================================================== -# Parse the list of parameters -#============================================================== -def parse_parameters (args) : - - 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] - - -#================================================================= -# Checks boolean value: yes/no, true/false, 1/0 -#================================================================= -def check_bool(val): - return str(val).strip() in ["yes","true", "1"] +#=================================================================== +# 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 = "" + 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 - - products = copy.deepcopy(prods) + 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) - 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) + def print_usage(self): + """Print usage""" + print "usage: %s [options]" % os.path.basename(sys.argv[0]) + pass - #remove the products names from other products dependencies - for item in tmplist: - products.remove(item) + 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 - regexpr1 = "((^|,(\s+)?)"+item.name+"$|^"+item.name+"(\s+)?,(\s+)?)" - regexpr2 = ",(\s+)?"+item.name+"(\s+)?,(\s+)?" + 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 - for product in products: - product.dependencies = re.sub(r'\s+$', "", product.dependencies) - product.dependencies = re.sub(r'^\s+', "", 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 # +# # +#------------------------------------------------------------------# - product.dependencies = re.sub(regexpr1, "", product.dependencies) - product.dependencies = re.sub(regexpr2, ",", product.dependencies) +#=================================================================== +# 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) - return deps - -#================================================================= -# The third algorithm (same as SALOME_InstallWizard.cxx uses) -#================================================================= -def get_dependencies(prods) : - list = [] - for product in prods: - if check_bool(product.disable): continue +#=================================================================== +# 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) - 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 ) - #============================================================== -# Creates dir, returns the part of path that existed early. -# Access may be defined. +# class Product : pre-requisite product options #============================================================== -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 Product -#============================================================== - 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 + 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 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 - - -#=================================================================== -# 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 = [] @@ -368,36 +437,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']) + self.path = Path(attrs.get('targetdir', None), + attrs.get('tempdir', None)) + pass def end_path(self): pass @@ -408,84 +479,388 @@ 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 +# 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 "Invalid link " + dir + ". The directory " + native_dir + " a link points to 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 check_bool(product.disable) or product.install == "use native" or product.install == "not install": + 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 #=============================================================== -# Removes temporary directory +# remove_dir : removes temporary directory #=============================================================== -def remove_dir( rem_path = "" ): - if len( rem_path ) and os.path.exists( rem_path ): - os.system( "rm -rf " + rem_path ) +def remove_dir(path): + """ + Removes temporary directory. + """ + if path and os.path.exists(path): + os.system("rm -rf " + path) pass + +#============================================================== +# 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)) -#================================================================ -# main -#================================================================ +#------------------------------------------------------------------# +# # +# EXECUTION STARTS HERE # +# # +#------------------------------------------------------------------# if __name__ == "__main__": - - cur_dir = get_current_path(sys.argv[0]) - - [xml_file, target_dir, tmp_dir, is_gui] = parse_parameters(sys.argv) + # get program dir + cur_dir = get_program_path() + # parse command line + [xml_file, target_dir, tmp_dir, is_gui] = parse_parameters() - # define xml file ----------------- - if (xml_file is None) : + # define xml file to be used + if (xml_file is None): plt_name = "" plt_ver = "" xml_file_name = "config.xml" @@ -504,45 +879,47 @@ if __name__ == "__main__": plt_name = "Debian" plt_ver = open("/etc/debian_version").readline().strip() _xml_file_name = "config_%s_%s.xml"%(plt_name, plt_ver) - if plt_name and plt_ver and os.path.exists(cur_dir + _xml_file_name): + if plt_name and plt_ver and os.path.exists("%s/%s"%(cur_dir, _xml_file_name)): xml_file_name = _xml_file_name else: - print "" - print "Not supported Linux platform!" - print "Trying to use default configuration!" - print "" + msg = "Not supported Linux platform!\n" + msg += "Trying to use default configuration file!" + warning(msg) - xml_file = cur_dir + xml_file_name + 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 - msg += "\nTry to run with -f option." + 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 read access for %s file!"%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 - - message("Parsing xml config file: " + xml_file) + # parse XML file ----------- + message("Parsing XML configuration file: %s"%xml_file) filehandle = open(xml_file) data = filehandle.read() filehandle.close() @@ -550,103 +927,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", - "not install" : "try_preinstalled"} - # define tmp dir ----------- - if tmp_dir is None: - tmp_dir = parser.path.tmpdir - if tmp_dir is None or tmp_dir == "": - tmp_dir = "/tmp" - import random - tmp_dir = tmp_dir + "/INSTALLWORK" + str(random.randint(10000,100000)) - root_path = "" - if not os.path.exists(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 + ". Use -tmp key to set directory or correct xml file\n\n") - - if not os.access(tmp_dir, os.W_OK) : - str = "There is no write permissions for directory " + tmp_dir + ". Use -tmp key to set temporary 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): - message("Creating target directory: " + target_dir); create_dir(target_dir, 0755) - if not os.path.exists(target_dir): - error_exit("Invalid 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 = "There is no write permissions for directory " + target_dir + ". Use -t key to set target directory or correct xml file." - error_exit(str) - - # define products dir ------------ - source_dir = cur_dir + "Products" ; - if not check_dir(source_dir): - remove_dir(root_path) - sys.exit(1) - - subdir = {"install sources" : "SOURCES", - "install binaries" : "BINARIES/"+parser.config.os, - "use native" : "", - "not install" : ""} - - - # define scripts dir ------------ - scripts_dir = cur_dir + "config_files/" - if not check_dir(scripts_dir): - remove_dir(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) - list_of_dep = get_dependencies(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) - message("Checking available disk space") - 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) - message("Starting...") - # 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 check_bool(product.disable): continue + # change current directory ----------- + os.chdir(scripts_dir) - message("Processing " + product.name + "...") - 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; # try_preinstalled can return 1 - - # pickup environment - message("Creating environment files") - for product in parser.products : - - if check_bool(product.disable): continue - - if check_bool(product.pickupEnv): - 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) + # clean temporary directory ----------- message("Cleaning temporary directory") - remove_dir(root_path) + clean_all() + + # finishing ----------- message("Finished!") + pass diff --git a/src/InstallWizard.cpp b/src/InstallWizard.cpp index f2628e3..d4bd4da 100644 --- a/src/InstallWizard.cpp +++ b/src/InstallWizard.cpp @@ -39,6 +39,7 @@ #include #include +#include #include #include #include @@ -95,6 +96,7 @@ public: QPushButton * cancelButton; QPushButton * helpButton; QFrame * hbar1, * hbar2; + QToolButton * aboutButton; #ifndef QT_NO_ACCEL QAccel * accel; @@ -128,11 +130,13 @@ InstallWizard::InstallWizard( QWidget *parent, const char *name, bool modal, d->ws = new QWidgetStack( this, "qt_widgetstack" ); d->pages.setAutoDelete( TRUE ); d->titleBox = new QHBox( this, "title box" ); + d->aboutButton = new QToolButton( d->titleBox, "about button"); + d->aboutButton->setSizePolicy( QSizePolicy( QSizePolicy::Fixed, QSizePolicy::Fixed ) ); + d->aboutButton->setAutoRaise( true ); d->title = new QLabel( d->titleBox, "title label" ); d->logoBox = new QHBox( d->titleBox, "logo box" ); d->logoBox->setSpacing( 2 ); d->titleBox->setStretchFactor( d->title, 10 ); - // create in nice tab order d->nextButton = new QPushButton( this, "next" ); d->finishButton = new QPushButton( this, "finish" ); @@ -168,6 +172,8 @@ InstallWizard::InstallWizard( QWidget *parent, const char *name, bool modal, this, SLOT(reject()) ); connect( d->helpButton, SIGNAL(clicked()), this, SLOT(help()) ); + connect( d->aboutButton, SIGNAL(clicked()), + this, SIGNAL(aboutClicked()) ); #ifndef QT_NO_ACCEL d->accel = new QAccel( this, "arrow-key accel" ); @@ -176,6 +182,8 @@ InstallWizard::InstallWizard( QWidget *parent, const char *name, bool modal, d->nextAccel = d->accel->insertItem( Qt::ALT + Qt::Key_Right ); d->accel->connectItem( d->nextAccel, this, SIGNAL(nextClicked()) ); #endif + + showAboutBtn( false ); } @@ -416,7 +424,9 @@ void InstallWizard::help() emit helpClicked(); } - +/*! + Enables/disables button + */ void InstallWizard::setBackEnabled( bool enable ) { d->backButton->setEnabled( enable ); @@ -425,7 +435,9 @@ void InstallWizard::setBackEnabled( bool enable ) #endif } - +/*! + Enables/disables button + */ void InstallWizard::setNextEnabled( bool enable ) { d->nextButton->setEnabled( enable ); @@ -434,13 +446,14 @@ void InstallWizard::setNextEnabled( bool enable ) #endif } - +/*! + Enables/disables button + */ void InstallWizard::setHelpEnabled( bool enable ) { d->helpButton->setEnabled( enable ); } - /*! \fn void InstallWizard::setFinish( QWidget *, bool ) \obsolete @@ -914,6 +927,22 @@ void InstallWizard::removeLogos() delete children; } +/*! +Show/hide "About" button +*/ +void InstallWizard::showAboutBtn( bool show ) +{ + show ? d->aboutButton->show() : d->aboutButton->hide(); +} + +/*! +Set icon for "About" button +*/ +void InstallWizard::setAboutIcon( const QPixmap& px ) +{ + d->aboutButton->setIconSet( px ); +} + /*! Posts validation event */ diff --git a/src/InstallWizard.h b/src/InstallWizard.h index 8b36597..180ccab 100644 --- a/src/InstallWizard.h +++ b/src/InstallWizard.h @@ -78,13 +78,16 @@ public: QWidget * currentPage() const; QWidget* page( int ) const; - QWidget* page( const QString& title ) const; + QWidget* page( const QString& ) const; int pageCount() const; int indexOf( QWidget* ) const; - void addLogo( const QPixmap& pm ); + void addLogo( const QPixmap& ); void removeLogos(); + void showAboutBtn( bool ); + void setAboutIcon( const QPixmap& ); + virtual bool appropriate( QWidget * ) const; virtual void setAppropriate( QWidget *, bool ); @@ -115,6 +118,7 @@ signals: void nextClicked(); void backClicked(); void helpClicked(); + void aboutClicked(); void selected( const QString& ); protected: diff --git a/src/Makefile b/src/Makefile index b9c1406..078d0b8 100644 --- a/src/Makefile +++ b/src/Makefile @@ -1,6 +1,6 @@ ############################################################################# # Makefile for building: ../bin/SALOME_InstallWizard -# Generated by qmake (1.03a) on: Fri Jul 1 17:48:15 2005 +# Generated by qmake (1.03a) on: Tue Mar 7 14:46:50 2006 # Project: SALOME_INSTALL.pro # Template: app # Command: $(QMAKE) SALOME_INSTALL.pro @@ -12,8 +12,8 @@ CC = gcc CXX = g++ LEX = flex YACC = yacc -CFLAGS = -pipe -Wall -W -O2 -D_REENTRANT -DQT_NO_DEBUG -DQT_THREAD_SUPPORT -CXXFLAGS = -pipe -Wall -W -O2 -D_REENTRANT -DQT_NO_DEBUG -DQT_THREAD_SUPPORT +CFLAGS = -pipe -Wno-deprecated -Wall -W -O2 -D_REENTRANT -DQT_NO_DEBUG -DQT_THREAD_SUPPORT +CXXFLAGS = -pipe -Wno-deprecated -Wall -W -O2 -D_REENTRANT -DQT_NO_DEBUG -DQT_THREAD_SUPPORT LEXFLAGS = YACCFLAGS= -d INCPATH = -I$(QTDIR)/include -I$(QTDIR)/mkspecs/default diff --git a/src/SALOME_InstallWizard.cxx b/src/SALOME_InstallWizard.cxx index 3968afc..36737b4 100644 --- a/src/SALOME_InstallWizard.cxx +++ b/src/SALOME_InstallWizard.cxx @@ -317,6 +317,31 @@ static bool hasSpace( const QString& dir ) return false; } +// ================================================================ +/*! + * makeTitle + * Creates HTML-wrapped title text + */ +// ================================================================ +QString makeTitle( const QString& text, const QString& separator = " ", bool fl = true ) +{ + QStringList words = QStringList::split( separator, text ); + if ( fl ) { + for ( uint i = 0; i < words.count(); i++ ) + words[i] = QString( "%1" ).arg( words[i].left(1) ) + words[i].mid(1); + } + else { + if ( words.count() > 0 ) + words[0] = QString( "%1" ).arg( words[0] ); + if ( words.count() > 1 ) + words[words.count()-1] = QString( "%1" ).arg( words[words.count()-1] ); + } + QString res = words.join( separator ); + if ( !res.isEmpty() ) + res = QString( "%1" ).arg( res ); + return res; +} + // ================================================================ /*! * QMyCheckBox class : custom check box @@ -330,23 +355,117 @@ public: void setState ( ToggleState s ) { QCheckBox::setState( s ); } }; +// ================================================================ +/*! + * AboutDlg + * "About dialog box. + */ +// ================================================================ +class AboutDlg: public QDialog +{ +public: + AboutDlg( SALOME_InstallWizard* parent ) : QDialog( parent, "About dialog box", true ) + { + // caption + setCaption( QString( "About %1" ).arg( parent->getIWName() ) ); + // palette + QPalette pal = palette(); + QColorGroup cg = pal.active(); + cg.setColor( QColorGroup::Foreground, Qt::darkBlue ); + cg.setColor( QColorGroup::Background, Qt::white ); + pal.setActive( cg ); pal.setInactive( cg ); pal.setDisabled( cg ); + setPalette( pal ); + // layout + QGridLayout* main = new QGridLayout( this, 1, 1, 11, 6 ); + // image + QLabel* logo = new QLabel( this, "logo" ); + logo->setSizePolicy( QSizePolicy( QSizePolicy::Fixed, QSizePolicy::Fixed ) ); + logo->setMinimumSize( 32, 32 ); logo->setMaximumSize( 32, 32 ); + logo->setPaletteBackgroundColor( QColor( 234, 250, 234 ) ); + logo->setFrameStyle( QLabel::NoFrame | QLabel::Plain ); + logo->setPixmap( pixmap( pxAbout ) ); + logo->setScaledContents( false ); + logo->setAlignment( QLabel::AlignCenter ); + // decoration + QLabel* decorLeft = new QLabel( this, "decorLeft" ); + decorLeft->setSizePolicy( QSizePolicy( QSizePolicy::Fixed, QSizePolicy::Expanding ) ); + decorLeft->setMinimumWidth( 32 ); decorLeft->setMaximumWidth( 32 ); + decorLeft->setPaletteBackgroundColor( QColor( 234, 250, 234 ) ); + decorLeft->setScaledContents( false ); + QLabel* decorTop = new QLabel( this, "decorTop" ); + decorTop->setSizePolicy( QSizePolicy( QSizePolicy::Expanding, QSizePolicy::Fixed ) ); + decorTop->setMinimumHeight( 32 ); decorTop->setMaximumHeight( 32 ); + decorTop->setPaletteBackgroundColor( QColor( 234, 250, 234 ) ); + decorTop->setScaledContents( false ); + // contents + QLabel* title = new QLabel( this, "title" ); + QString tlt = parent->getIWName(); + title->setText( makeTitle( tlt ) ); + QLabel* version = new QLabel( this, "version" ); + version->setText( QString( "Version: %1.%1.%1" ).arg( __IW_VERSION_MAJOR__ ) \ + .arg( __IW_VERSION_MINOR__ ) \ + .arg( __IW_VERSION_PATCH__ ) ); + QLabel* copyright = new QLabel( this, "copyright" ); + copyright->setText( "Copyright © 2004-2006 CEA" ); + QFont font = title->font(); + font.setPointSize( (int)( font.pointSize() * 1.8 ) ); + title->setFont( font ); + QFrame* line = new QFrame( this, "line" ); + line->setFrameStyle( QFrame::HLine | QFrame::Sunken ); + QLabel* url = new QLabel( this, "url" ); + url->setText( makeTitle( "www.salome-platform.org", ".", false ) ); + url->setAlignment( AlignRight ); + font = version->font(); + font.setPointSize( (int)( font.pointSize() / 1.2 ) ); + version->setFont( font ); + copyright->setFont( font ); + url->setFont( font ); + // layout + main->addWidget( logo, 0, 0 ); + main->addMultiCellWidget( decorLeft, 1, 5, 0, 0 ); + main->addWidget( decorTop, 0, 1 ); + main->addWidget( title, 1, 1 ); + main->addWidget( version, 2, 1 ); + main->addWidget( copyright, 3, 1 ); + main->addWidget( line, 4, 1 ); + main->addWidget( url, 5, 1 ); + // resize + QFontMetrics fm( title->font() ); + int width = (int)( fm.width( tlt ) * 1.5 ); + title->setMinimumWidth( width ); + setMaximumSize( minimumSize() ); + } + void mousePressEvent( QMouseEvent* ) + { + accept(); + } +}; + // ================================================================ /*! * SALOME_InstallWizard::SALOME_InstallWizard * Constructor */ // ================================================================ -SALOME_InstallWizard::SALOME_InstallWizard(QString aXmlFileName) +SALOME_InstallWizard::SALOME_InstallWizard(const QString& aXmlFileName, + const QString& aTargetDir, + const QString& aTmpDir) : InstallWizard( qApp->desktop(), "SALOME_InstallWizard", false, 0 ), helpWindow( NULL ), moreMode( false ), previousPage( 0 ), exitConfirmed( false ) { - myIWName = tr( "Installation Wizard" ); - tmpCreated = QString::null; - xmlFileName = aXmlFileName; - QFont fnt = font(); fnt.setPointSize( 14 ); fnt.setBold( true ); + myIWName = tr( "Installation Wizard" ); + tmpCreated = QString::null; + xmlFileName = aXmlFileName; + targetDirPath = aTargetDir; + tmpDirPath = aTmpDir; + + // set application font + QFont fnt = font(); + fnt.setPointSize( 14 ); + fnt.setBold( true ); setTitleFont( fnt ); // set icon @@ -364,7 +483,9 @@ SALOME_InstallWizard::SALOME_InstallWizard(QString aXmlFileName) setLicense( tr( "All right reserved" ) ); setOS( "" ); - ___MESSAGE___( "Config. file : " << xmlFileName ); + ___MESSAGE___( "Configuration file : " << xmlFileName ); + ___MESSAGE___( "Target directory : " << targetDirPath ); + ___MESSAGE___( "Temporary directory: " << tmpDirPath ); // xml reader QFile xmlfile(xmlFileName); @@ -405,8 +526,10 @@ SALOME_InstallWizard::SALOME_InstallWizard(QString aXmlFileName) // common signals connections connect( this, SIGNAL( selected( const QString& ) ), - this, SLOT( pageChanged( const QString& ) ) ); - connect( this, SIGNAL( helpClicked() ), this, SLOT( helpClicked() ) ); + this, SLOT( pageChanged( const QString& ) ) ); + connect( this, SIGNAL( helpClicked() ), this, SLOT( helpClicked() ) ); + connect( this, SIGNAL( aboutClicked() ), this, SLOT( onAbout() ) ); + // catch signals from launched script connect(shellProcess, SIGNAL( readyReadStdout() ), this, SLOT( readFromStdout() ) ); connect(shellProcess, SIGNAL( readyReadStderr() ), this, SLOT( readFromStderr() ) ); @@ -415,6 +538,10 @@ SALOME_InstallWizard::SALOME_InstallWizard(QString aXmlFileName) // create validation thread myThread = new ProcessThread( this ); + + // show about button + setAboutIcon( pixmap( pxAbout ) ); + showAboutBtn( true ); } // ================================================================ /*! @@ -656,6 +783,12 @@ void SALOME_InstallWizard::setupProductsPage() reader.setContentHandler( handler ); reader.parse( source ); } + // take into account command line parameters + if ( !targetDirPath.isEmpty() ) + targetFolder->setText( targetDirPath ); + if ( !tmpDirPath.isEmpty() ) + tempFolder->setText( tmpDirPath ); + // set first item to be selected if ( productsView->childCount() > 0 ) { productsView->setSelected( productsView->firstChild(), true ); @@ -1486,6 +1619,19 @@ void SALOME_InstallWizard::onLaunchSalome() QMessageBox::NoButton, QMessageBox::NoButton ); } + +// ================================================================ +/*! + * SALOME_InstallWizard::onAbout + * button slot: shows dialog box + */ +// ================================================================ +void SALOME_InstallWizard::onAbout() +{ + AboutDlg d( this ); + d.exec(); +} + // ================================================================ /*! * SALOME_InstallWizard::findItem diff --git a/src/SALOME_InstallWizard.hxx b/src/SALOME_InstallWizard.hxx index 37800cd..ee7af23 100644 --- a/src/SALOME_InstallWizard.hxx +++ b/src/SALOME_InstallWizard.hxx @@ -114,7 +114,9 @@ class SALOME_InstallWizard: public InstallWizard public: // constructor - SALOME_InstallWizard(QString aXmlFileName); + SALOME_InstallWizard(const QString& aXmlFileName, + const QString& aTargetDir = QString::null, + const QString& aTmpDir = QString::null); // destructor virtual ~SALOME_InstallWizard( ); @@ -229,6 +231,8 @@ class SALOME_InstallWizard: public InstallWizard void onMoreBtn(); // button slot void onLaunchSalome(); + // button slot + void onAbout(); // QProcess slots: // -->something was written to stdin @@ -251,6 +255,8 @@ class SALOME_InstallWizard: public InstallWizard MapProducts productsMap; // products info (name, dependancies, disk space ) QStringList toInstall; // list of products being installed QString xmlFileName; // xml file + QString targetDirPath; // target directory + QString tmpDirPath; // temporary directory bool moreMode; // advanced mode flag QWidget* previousPage; // previous page QString tmpCreated; // created temporary directory diff --git a/src/globals.h b/src/globals.h index 160f26e..10fffb1 100644 --- a/src/globals.h +++ b/src/globals.h @@ -17,7 +17,7 @@ #define __IW_VERSION_MAJOR__ 1 #define __IW_VERSION_MINOR__ 0 -#define __IW_VERSION_PATCH__ 0 +#define __IW_VERSION_PATCH__ 1 #define __IW_VERSION__ (__IW_VERSION_MAJOR__*10000 + \ __IW_VERSION_MINOR__*100 + \ diff --git a/src/icons.cxx b/src/icons.cxx index 04409e1..560e4d4 100644 --- a/src/icons.cxx +++ b/src/icons.cxx @@ -30812,6 +30812,85 @@ static const char* const image_zoom_out[] = { "2 2 [.h+h+H. i+j+k+l+m+n+", " W o+p+|+q+"}; +static const char* const image_about[] = { +"16 16 5 1", +" c None", +". c #5151C1", +"+ c #1414C1", +"@ c #7C7CC1", +"# c #A5A5C3", +" ", +".+@ ", +".+@ ", +".+@ ", +".+@ ", +".+@@+@ @+@ @+@", +".+@.+@ .+@ .+@", +".+@.+@ .+@ .+@", +".+@.+@ #.+@ #.+@", +".+@.+@#.++@#.++@", +"###.+..+++..++.#", +" .++++@++++@# ", +" .++.@#++.@# ", +" .+@# +@# ", +" ", +" "}; + +static const char* const image_about1[] = { +"16 16 8 1", +" c None", +". c #C15151", +"+ c #C3A5A5", +"@ c #C11414", +"# c #5151C1", +"$ c #1414C1", +"% c #7C7CC1", +"& c #A5A5C3", +" .+@+. ", +" +.@.+ ", +"#$% @@@@@ ", +"#$% +.@.+ ", +"#$% .+@+. ", +"#$% ", +"#$%%$% %$% %$%", +"#$%#$% #$% #$%", +"#$%#$% #$% #$%", +"#$%#$% &#$% &#$%", +"#$%#$%&#$$%&#$$%", +"&&&#$##$$$##$$#&", +" #$$$$%$$$$%& ", +" #$$#%&$$#%& ", +" #$%& $%& ", +" "}; + +static const char* const image_about2[] = { +"16 16 9 1", +" c None", +". c #C15151", +"+ c #C11414", +"@ c #C17C7C", +"# c #C3A5A5", +"$ c #5151C1", +"% c #1414C1", +"& c #7C7CC1", +"* c #A5A5C3", +".+@ ", +".+@ ", +"### ", +"$%& ", +"$%& ", +"$%& ", +"$%& ", +"$%&&%& &%& &%&", +"$%&$%& $%& $%&", +"$%&$%& $%& $%&", +"$%&$%& *$%& *$%&", +"$%&$%&*$%%&*$%%&", +"***$%$$%%%$$%%$*", +" $%%%%&%%%%&* ", +" $%%$&*%%$&* ", +" $%&* %&* "}; + QPixmap pixmap( const int type ) { switch ( type ) { @@ -30819,6 +30898,8 @@ QPixmap pixmap( const int type ) return QPixmap( ( const char** )image_SALOME ); case pxLogo: // small logo return QPixmap( ( const char** )image_logo ); + case pxAbout: // about icon + return QPixmap( ( const char** )image_about2 ); case pxIcon: // title icon return QPixmap( ( const char** )image_icon ); case pxClose: // help window : close window diff --git a/src/icons.h b/src/icons.h index 8e5f201..4a62bb6 100644 --- a/src/icons.h +++ b/src/icons.h @@ -12,6 +12,7 @@ enum { pxBigLogo, // SALOME Logo pxLogo, // small logo + pxAbout, // about icon pxIcon, // title icon pxClose, // help window : close window pxHome, // help window : go home diff --git a/src/main.cxx b/src/main.cxx index 3e1697f..b508de0 100644 --- a/src/main.cxx +++ b/src/main.cxx @@ -50,6 +50,11 @@ int main( int argc, char **argv ) qInstallMsgHandler( MessageOutput ); QString xmlFileName; + QString targetDirPath; + QString tmpDirPath; + bool has_xml = false; + bool has_target = false; + bool has_tmp = false; for( int i = 1; i < argc; i++ ) { QString a = QString( argv[i] ); if ( a == "--version" || a == "-v" ) { @@ -63,9 +68,50 @@ int main( int argc, char **argv ) ( QT_VERSION ) & 0xFF ); return 0; } - if ( xmlFileName.isEmpty() ) - xmlFileName = a; + else if ( a == "--target" || a == "-d" ) { + has_target = true; + if ( i < argc-1 && !QString( argv[i+1] ).startsWith("-") ) { + targetDirPath = argv[i+1]; + i++; + } + else { + tmpDirPath = QString::null; + } + } + else if ( a == "--tmp" || a == "-t" ) { + has_tmp = true; + if ( i < argc-1 && !QString( argv[i+1] ).startsWith("-") ) { + tmpDirPath = argv[i+1]; + i++; + } + else { + tmpDirPath = QString::null; + } + } + else if ( a == "--file" || a == "-f" ) { + has_xml = true; + if ( i < argc-1 && !QString( argv[i+1] ).startsWith("-") ) { + xmlFileName = argv[i+1]; + i++; + } + else { + xmlFileName = QString::null; + } + } + } + if ( has_xml && xmlFileName.isEmpty() ) { + printf("Please specify the configuration XML file!\n"); + return 1; } + if ( has_target && targetDirPath.isEmpty() ) { + printf("Please specify the target directory path!\n"); + return 1; + } + if ( has_tmp && tmpDirPath.isEmpty() ) { + printf("Please specify the temprary directory path!\n"); + return 1; + } + if ( xmlFileName.isEmpty() ) xmlFileName = "config.xml"; @@ -75,7 +121,7 @@ int main( int argc, char **argv ) int result = -1; QFile xmlfile(xmlFileName); if ( xmlfile.exists() ) { - SALOME_InstallWizard wizard(xmlFileName); + SALOME_InstallWizard wizard(xmlFileName, targetDirPath, tmpDirPath); a.setMainWidget( &wizard ); wizard.show(); result = a.exec();