Salome HOME
Update for 4.1.3
[tools/install.git] / runInstall
1 #!/usr/bin/env python
2
3 """
4 Installation Wizard launching script.
5
6 This script is the part of the SALOME installation procedure.
7 Author    : Vadim SANDLER, Open CASCADE SAS (vadim.sandler@opencascade.com)
8 Created   : Thu Dec 18 12:01:00 2002
9 Copyright : 2002-2008 CEA
10
11 """
12
13 __version__ = "1.1.4"
14
15 # --- avoid "deprecation" warnings --- #
16 import warnings
17 warnings.filterwarnings("ignore", "", DeprecationWarning)
18
19 # --- imports --- #
20 import xml.sax, xml.dom.minidom
21 import sys, os, re
22 import types
23 import random
24
25 # --- global variables --- #
26 opt_parser = None
27 root_path  = None
28
29 # --- actions definition --- #
30 __BINARIES__   = "install_binary"
31 __BUILDSRC__   = "install_source_and_build"
32 __PREINSTALL__ = "try_preinstalled"
33
34 # --- product type definition --- #
35 __CTX__COMPONENT__    = "component"
36 __CTX__PREREQUISITE__ = "prerequisite"
37
38 #------------------------------------------------------------------#
39 #                                                                  #
40 #                 COMMAND LINE ARGUMENTS PARSER                    #
41 #                                                                  #
42 #------------------------------------------------------------------#
43
44 #===================================================================
45 # class OptBaseError : base parse error
46 #===================================================================
47 class OptBaseError(Exception):
48     """
49     Base option parsing exception class
50     """
51     def __init__(self, msg):
52         self.msg = msg
53     def __str__ (self):
54         return self.msg
55
56 #===================================================================
57 # class OptError : bad option error
58 #===================================================================
59 class OptError(OptBaseError):
60     """
61     Bad option exception class
62     """
63     def __init__ (self, msg, option):
64         self.msg = msg
65         self.option = option
66     def __str__ (self):
67         if self.option:
68             opt_prs = "<unknown>"
69             if self.option.short_opt and self.option.long_opt:
70                 opt_prs = "%s/%s"%(self.option.short_opt,self.option.long_opt)
71             elif self.option.short_opt:
72                 opt_prs = "%s"%(self.option.short_opt)
73             elif self.option.long_opt:
74                 opt_prs = "%s"%(self.option.long_opt)
75             return "option %s: %s"%(opt_prs, self.msg)
76         return self.msg
77
78 #===================================================================
79 # class ArgError : bad option argument error
80 #===================================================================
81 class ArgError(OptBaseError):
82     """
83     Bad argument exception class
84     """
85     pass
86
87 #===================================================================
88 # class ValError : bad command line parameter error
89 #===================================================================
90 class ValError(OptBaseError):
91     """
92     Bad parameter exception class
93     """
94     pass
95
96 #===================================================================
97 # class ArgOption : command line option
98 #===================================================================
99 class ArgOption:
100     """
101     Option class
102     """
103     attrs   = ["short_opt", "long_opt", "dest", "action", "type", "default", "metavar", "help"]
104     actions = ["store", "store_true", "store_false"]
105     types   = ["string", "int", "float", "bool"]
106     def __init__(self, *args, **kwargs):
107         # set defaults
108         for attr in self.attrs: setattr(self, attr, None)
109         # parse arguments
110         for i in range(len(args)):
111             if i > len(self.attrs)-1:
112                 msg = "Wrong number of parameters is given (maximum is %d)" % len(self.attrs)
113                 raise OptBaseError(msg)
114             setattr(self, self.attrs[i], args[i])
115         for arg in kwargs:
116             if arg not in self.attrs:
117                 msg = "Invalid argument: %s" % arg
118                 raise OptBaseError(msg)
119             setattr(self, arg, kwargs[arg])
120         # check short option key
121         if self.short_opt and \
122                not re.match("^-[a-zA-Z]$",self.short_opt):
123             msg  = "invalid short option key; "
124             msg += "should be of the form -x (x is letter)"
125             raise OptError(msg, self)
126         # check long option key
127         if self.long_opt and \
128                not re.match("^--[a-zA-Z][a-zA-Z0-9]*(-[a-zA-Z0-9]+)*$",self.long_opt):
129             msg  = "invalid long option key; "
130             msg += "should be of the form --word[[-word]...] "
131             msg += "(word is letters and digits sequence)"
132             raise OptError(msg, self)
133         # check that at least one option key is defined
134         if not self.short_opt and not self.long_opt:
135             msg  = "invalid option; neither short nor long option key is defined"
136             raise OptError(msg, self)
137         # destination
138         if not self.dest and self.long_opt:
139             self.dest = self.long_opt[2:].replace('-','_')
140         if not self.dest and self.short_opt:
141             self.dest = self.short_opt[1:]
142         # action
143         if not self.action:
144             self.action = "store"
145         if self.action not in self.actions:
146             msg  = "invalid action: %s" % self.action
147             raise OptError(msg, self)
148         # type
149         if not self.type:
150             if self.action in ["store_true","store_false"]: self.type = "bool"
151             else: self.type = "string"
152         if self.type not in self.types:
153             msg  = "invalid type: %s" % self.type
154             raise OptError(msg, self)
155         if self.action in ["store_true","store_false"] and self.type != "bool":
156             msg  = "invalid type: %s : should be 'bool' or None" % self.type
157             raise OptError(msg, self)
158         # default
159         if self.default is not None:
160             try:
161                 if self.type == "string": self.default = str(self.default)
162                 if self.type == "int":    self.default = int(self.default)
163                 if self.type == "float":  self.default = float(self.default)
164                 if self.type == "bool":   self.default = boolean(self.default)
165             except :
166                 msg  = "invalid default value type: should be %s" % self.type
167                 raise OptError(msg, self)
168             pass
169         # metavar
170         if not self.metavar:
171             self.metavar = self.dest.upper()
172         # help
173         if not self.help:
174             self.help = ""
175         pass
176
177     def to_string(self):
178         """
179         Returns string representation of the option
180         """
181         opts = []
182         opt = self.short_opt
183         if opt and self.action == "store" and self.metavar: opt += " %s" % self.metavar
184         if opt: opts.append(opt)
185         opt = self.long_opt
186         if opt and self.action == "store" and self.metavar: opt += "=%s" % self.metavar
187         if opt: opts.append(opt)
188         return (", ").join(opts)
189     
190 #===================================================================
191 # class Values : resulting option values
192 #===================================================================
193 class Values:
194     """
195     Values class
196     """
197     def __init__(self):
198         pass
199         
200 #===================================================================
201 # class ArgParser : command line arguments parser
202 #===================================================================
203 class ArgParser:
204     """
205     Arguments parser class
206     """
207     def __init__(self):
208         self.options = []
209         pass
210
211     def add_option(self, *args, **kwargs):
212         """Register an option"""
213         o = ArgOption(*args, **kwargs)
214         self._check_option(o)
215         self.options.append(o)
216         pass
217
218     def parse_args(self, args = None):
219         """Parse an arguments"""
220         if not args: args = sys.argv[1:]
221         values = Values()
222         for o in self.options:
223             if o.default is not None:
224                 setattr(values, o.dest, o.default)
225             elif not hasattr(values,o.dest):
226                 setattr(values, o.dest, None)
227         try:
228             (values, args) = self._process_args(values, args)
229         except (ArgError, ValError), e:
230             self._error(e.msg)
231
232         return (values, args)
233             
234     def print_usage(self):
235         """Print usage"""
236         print "usage: %s [options]" % os.path.basename(sys.argv[0])
237         pass
238
239     def print_help(self):
240         """Print help"""
241         self.print_usage()
242         print ""
243         olen = 0
244         _maxwidth, _indent = 79, 2
245         if len(self.options):
246             for option in self.options:
247                 if olen < len(option.to_string()): olen = len(option.to_string())
248             print "options:"
249             for option in self.options:
250                 strings = []
251                 for hs in option.help.split("\n"):
252                     s = ""
253                     for w in hs.split():
254                         if len("%s %s" % (s,w)) > _maxwidth:
255                             strings.append(s.strip()); s = ""
256                         s = "%s %s" % (s,w)
257                     if s.strip(): strings.append(s.strip())
258                 if not strings: strings[:0] = [""]
259                 print "%s%s%s" % (option.to_string(), " "*(_indent+olen-len(option.to_string())), strings[0])
260                 for i in range(1, len(strings)):
261                     print "%s%s" % (" "*(olen+_indent), strings[i])
262         pass
263     
264     def _check_option(self, option):
265         o = self._get_option(option.short_opt)
266         if not o: o = self._get_option(option.long_opt)
267         if o:
268             msg = "option conflicts with previously defined option(s)"
269             raise OptError(msg, option)
270         pass
271
272     def _get_option(self, opt_key):
273         if opt_key:
274             for o in self.options:
275                 if opt_key in [o.short_opt, o.long_opt]: return o
276         return None
277         
278     def _error(self, msg):
279         self.print_usage()
280         sys.exit("\n%s: error: %s\n" % (os.path.basename(sys.argv[0]), msg))
281         pass
282
283     def _check_value(self, option, value):
284         o = self._get_option(option)
285         try:
286             if o.type == "string": return str(value)
287             if o.type == "int":    return int(value)
288             if o.type == "float":  return float(value)
289             if o.type == "bool":   return boolean(value)
290         except:
291             msg  = "invalid value type for option %s: %s; " % (option, value)
292             msg += "should be %s" % o.type
293             raise ValError(msg)
294         raise OptBaseError("unknown error")
295
296     def _process_args(self, values, args):
297         res_args = []
298         cur_opt = None
299         rargs   = []
300         for index in range(len(args)):
301             a = args[index]
302             if cur_opt and cur_opt[1].action == "store":
303                 setattr(values, cur_opt[1].dest, self._check_value(cur_opt[0], a))
304                 cur_opt = None
305                 continue
306             if a == "-":
307                 rargs = args[index+1:]
308                 break
309             elif re.match("^-[a-zA-Z].*", a):
310                 for i in range(1,len(a)):
311                     if cur_opt and cur_opt[1].action == "store":
312                         setattr(values, cur_opt[1].dest, self._check_value(cur_opt[0], a[i:]))
313                         cur_opt = None
314                         break
315                     o = self._get_option("-%s"%a[i])
316                     if not o:
317                         raise ArgError("no such option: -%s"%a[i])
318                     if o.action == "store_true":
319                         setattr(values, o.dest, True)
320                     elif o.action == "store_false":
321                         setattr(values, o.dest, False)
322                     else:
323                         cur_opt = ("-%s"%a[i], o)
324                 pass
325             elif re.match("^--[a-zA-Z][a-zA-Z0-9]*(-[a-zA-Z0-9]+)*", a):
326                 oname  = ("%s="%a).split("=")[0]
327                 ovalue = ("%s="%a).split("=")[1]
328                 o = self._get_option(oname)
329                 if not o:
330                     raise ArgError("no such option: %s" % oname)
331                 if o.action == "store_true":
332                     if ovalue:
333                         raise ValError("option %s does not take a value" % oname)
334                     setattr(values, o.dest, True)
335                 elif o.action == "store_false":
336                     if ovalue:
337                         raise ValError("option %s does not take a value" % oname)
338                     setattr(values, o.dest, False)
339                 else:
340                     if ovalue:
341                         setattr(values, o.dest, self._check_value(oname, ovalue))
342                     else:
343                         cur_opt = (oname, o)
344                 pass
345             elif a.startswith("-"):
346                 raise ArgError("bad formatted option: %s" % a)
347             else:
348                 rargs = args[index:]
349                 break
350         if cur_opt and cur_opt[1].action == "store":
351             raise ValError("option %s requires value" % cur_opt[0])
352         return (values, rargs)
353
354 #------------------------------------------------------------------#
355 #                                                                  #
356 #                 XML CONFIGURATION FILES PARSER                   #
357 #                                                                  #
358 #------------------------------------------------------------------#
359
360 #===================================================================
361 # class Config : general configuration options : version, target and
362 #                temporary directories, etc...
363 #===================================================================
364 class Config :
365     """
366     General configuration file options:
367     - Install Wizard window caption
368     - SALOME platform version
369     - Copyright and license info
370     - Default target and temporary directories
371     """
372     def __init__(self,
373                  theVersion   = None,
374                  theCaption   = None,
375                  theCopyright = None,
376                  theLicense   = None,
377                  thePlatforms = None,
378                  theTargetdir = None,
379                  theTmpdir    = None):
380         self.version   = strip(theVersion)
381         self.caption   = strip(theCaption)
382         self.copyright = strip(theCopyright)
383         self.license   = strip(theLicense)
384         self.platforms = strip(thePlatforms)
385         self.targetdir = strip(theTargetdir)
386         self.tmpdir    = strip(theTmpdir)
387
388 #==============================================================
389 # class Product : pre-requisite product options
390 #==============================================================
391 class Product :
392     """
393     Product options:
394     - name, version
395     - target Linux OS version
396     - dependencies
397     - required disk space
398     - installation script
399     - etc...
400     """
401     def __init__(self,
402                  theName,
403                  theType               = None,
404                  theOS                 = None,
405                  theVersion            = None,
406                  theDependencies       = None,
407                  theWoGuiInstallation  = None,
408                  theInstalldiskspace   = None,
409                  theScript             = None,
410                  thePickUpEnvironment  = None):
411         self.name               = strip(theName)
412         self.type               = strip(theType)
413         self.os                 = strip(theOS)
414         self.version            = strip(theVersion)
415         self.dependencies       = strip(theDependencies)
416         self.woguiinst          = strip(theWoGuiInstallation)
417         self.installdiskspace   = strip(theInstalldiskspace)
418         self.script             = strip(theScript)
419         self.pickupEnv          = strip(thePickUpEnvironment)
420         self.whattodo           = __BINARIES__
421         
422     def setMode(self, mode):
423         if mode not in [__BINARIES__, __BUILDSRC__, __PREINSTALL__]:
424             return
425         self.whattodo = mode
426         return
427         
428 #===================================================================
429 # class ConfigParser : XML files parser implementation
430 #===================================================================
431 class ConfigParser:
432     """
433     XML configuration files parser
434     """
435     def __init__(self, is_force_src=False):
436         self.docElem = None
437         self.products = list()
438         self.full_prods_list = list()
439         self.config = None
440         self.is_force_src = is_force_src
441         pass
442         
443     def parse_config(self):
444         # Parse 'config' part of the XML file
445         configElem = self.docElem.getElementsByTagName('config')[0]
446         
447         self.config = Config(configElem.getAttribute('version').strip(),
448                              configElem.getAttribute('caption').strip(),
449                              configElem.getAttribute('copyright').strip(),
450                              configElem.getAttribute('license').strip(),
451                              configElem.getAttribute('platforms').strip(),
452                              configElem.getAttribute('targetdir').strip(),
453                              configElem.getAttribute('tempdir').strip())
454         pass
455     
456     def parse_dependencies(self):
457         # Parse 'dependencies' part of the XML file
458         depsMap = {}
459         depsElem = self.docElem.getElementsByTagName('dependencies')[0]
460         for prodElem in depsElem.getElementsByTagName('product'):
461             prodName = prodElem.getAttribute('name').strip()
462             if not prodName: continue
463             depsList = list()
464             for depElem in prodElem.getElementsByTagName('dep'):
465                 depsList.append(depElem.firstChild.data)
466                 pass
467             depsMap[prodName] = depsList
468             pass
469         return depsMap
470     
471     def parse_product(self):
472         # Parse 'products' part of the XML file
473         depsMap = self.parse_dependencies()
474         prodsElem = self.docElem.getElementsByTagName('products')[0]
475         sal_prods_list = list(); req_prods_list = list()
476         modules_list = list(); prereqs_list = list()
477         for prodElem in prodsElem.getElementsByTagName('product'):
478             prodName = prodElem.getAttribute('name').strip()
479             if not prodName: continue
480             instElem = prodElem.getElementsByTagName('installation')[0]
481             if check_bool(str(instElem.getAttribute('disable').strip())): continue
482             depsList = list()
483             if prodName in depsMap: depsList = depsMap[prodName]
484             aProduct = Product(prodName,
485                                prodElem.getAttribute('type').strip(),
486                                instElem.getAttribute('os').strip(),
487                                instElem.getAttribute('version').strip(),
488                                depsList,
489                                instElem.getAttribute('woguimode').strip(),
490                                instElem.getAttribute('installdiskspace').strip(),
491                                instElem.getAttribute('script').strip(),
492                                instElem.getAttribute('pickupenv').strip())
493             if self.is_force_src:
494                 aProduct.setMode(__BUILDSRC__)
495                 pass
496             if prodElem.getAttribute('type').strip() == "component":
497                 sal_prods_list.append(aProduct)
498                 # fill an ordered modules list -----------
499                 modules_list.append(prodName)
500                 modules_list.append(prodName + "_src")
501                 pass
502             else: #prerequisite
503                 req_prods_list.append(aProduct)
504                 # fill an ordered prerequisites list -----------
505                 prereqs_list.append(prodName)
506                 #AKL: prerequisite sources and temp files are removed, by default.
507                 #     So, there is no need to make sources environment
508                 #if aProduct.whattodo == __BUILDSRC__: prereqs_list.append(prodName + "_src")
509                 pass
510             pass
511         self.products.extend( req_prods_list )
512         self.products.extend( sal_prods_list )
513         if len(self.products) != 0:
514             gcc_product = Product("gcc",
515                                   __CTX__PREREQUISITE__,
516                                   self.products[0].os,
517                                   "",
518                                   list(),
519                                   None,
520                                   "0,0,0",
521                                   "gcc-common.sh",
522                                   "")
523             gcc_product.setMode(__PREINSTALL__)
524             self.products.insert(0, gcc_product)
525             prereqs_list.insert(0, gcc_product.name)
526             pass
527         self.full_prods_list.extend( prereqs_list )
528         self.full_prods_list.extend( modules_list )
529         pass
530
531     def parse(self, xml_file):
532         filehandle = open(xml_file)
533         sax_parser = xml.sax.make_parser()
534         doc = xml.dom.minidom.parse(filehandle, sax_parser)
535         filehandle.close()
536
537         self.docElem = doc.documentElement
538         self.parse_config()
539         self.parse_product()
540         pass
541
542     def getProduct(self, prod):
543         for product in self.products:
544             if product.name == prod:
545                 return product
546         return None
547
548
549 #------------------------------------------------------------------#
550 #                                                                  #
551 #                         SERVICE FUNCTIONS                        #
552 #                                                                  #
553 #------------------------------------------------------------------#
554
555 #==============================================================
556 # message: prints diagnostic information
557 #==============================================================
558 def message(msg):
559     """
560     Prints diagnostic information.
561     """
562     if msg.strip():
563         print ">>>", msg
564     pass
565
566 #==============================================================
567 # warning: prints warning
568 #==============================================================
569 def warning(msg):
570     """
571     Prints warning.
572     """
573     if msg.strip():
574         print ""
575         print msg
576         print ""
577     pass
578
579 #==============================================================
580 # error_exit : prints (optionally) error string, then prints
581 #              help information and quits
582 #==============================================================
583 def error_exit(msg = "", print_help = True):
584     """
585     Prints (optionally) error string,
586     then prints help information and quits.
587     """
588     # print error message
589     if len(msg.strip()):
590         print ""
591         print msg
592         print ""
593     # print help information
594     if print_help:
595         global opt_parser
596         if opt_parser:
597             opt_parser.print_help() 
598             print ""
599     # cleaning 
600     clean_all()
601     # quit
602     sys.exit(1);
603     pass
604
605 #==============================================================
606 # boolean : Converts string to boolean value.
607 #==============================================================
608 def boolean(val):
609     """
610     Converts string to boolean value if possible.
611     Raises exception if wrong string is used.
612     """
613     if isinstance(val, types.StringType):
614         if val.strip().lower()   in ["true",  "yes", "ok"]     : return True
615         elif val.strip().lower() in ["false", "no",  "cancel"] : return False
616         else: raise TypeError("invalid boolean value")
617     return bool(val)
618
619 #=================================================================
620 # check_bool : checks boolean value: yes/no, true/false, 1/0
621 #=================================================================
622 def check_bool(val):
623     """
624     Checks boolean value.
625     """
626     try:
627         return boolean(val)
628     except:
629         pass
630     return False
631
632 #==============================================================
633 # clean_all : performs system cleaning before exiting
634 #==============================================================
635 def clean_all():
636     """
637     Performs system cleaning before exiting.
638     """
639     global root_path
640     remove_dir(root_path)
641     pass
642
643 #==============================================================
644 # parse_parameters : parses command line arguments
645 #==============================================================
646 def parse_parameters():
647     """
648     Parses command line arguments.
649     """
650     global opt_parser
651     opt_parser = ArgParser()
652     
653     help_str  = "Runs the Installation Wizard in the GUI mode [default].\n"
654     opt_parser.add_option("-g",
655                           "--gui",
656                           action="store_true",
657                           dest="gui",
658                           default=True,
659                           help=help_str)
660     help_str  = "Runs the Installation Wizard in the TUI mode."
661     opt_parser.add_option("-b",
662                           "--batch",
663                           action="store_false",
664                           dest="gui",
665                           help=help_str)
666     help_str  = "The configuration xml file.\n"
667     help_str += "If this parameter is missing, then the program tries to define the "
668     help_str += "Linux platform and use the corresponding xml file. For example, "
669     help_str += "for Red Hat 8.0 config_RedHat_8.0.xml file is used in this case. "
670     opt_parser.add_option("-f",
671                           "--file",
672                           action="store",
673                           dest="xmlfile",
674                           metavar="FILE",
675                           help=help_str)
676     help_str  = "The target directory the products to be installed to.\n"
677     help_str += "When used this parameter overrides the default target directory "
678     help_str += "defined in the configuration xml file."
679     opt_parser.add_option("-d",
680                           "--target",
681                           action="store",
682                           dest="target_dir",
683                           metavar="DIR",
684                           help=help_str)
685     help_str  = "The directory to be used for temporary files.\n"
686     help_str += "When used this parameter overrides the default temporary directory "
687     help_str += "defined in the configuration xml file."
688     opt_parser.add_option("-t",
689                           "--tmp",
690                           action="store",
691                           dest="tmp_dir",
692                           metavar="DIR",
693                           help=help_str)
694     help_str  = "Force all products to be installed from sources \n"
695     help_str += "including SALOME modules.\n"
696     help_str += "If this option is used all the default installation modes are ignored."
697     opt_parser.add_option("-a",
698                           "--all-from-sources",
699                           action="store_true",
700                           dest="force_sources",
701                           default=False,
702                           help=help_str)
703     help_str  = "Prints version information and quits."
704     opt_parser.add_option("-v",
705                           "--version",
706                           action="store_true",
707                           help=help_str)
708     help_str  = "Prints this help and quits."
709     opt_parser.add_option("-h",
710                           "--help",
711                           action="store_true",
712                           help=help_str)
713     (options, args) = opt_parser.parse_args()
714     if options.help:
715         # print help info and quit
716         print "\nSALOME Installation Wizard\n"
717         opt_parser.print_help()
718         print ""
719         sys.exit(0)
720     if options.version:
721         # print version info and quit
722         print ""
723         cmd = "./bin/SALOME_InstallWizard --version"
724         os.system(cmd)
725         print ""
726         sys.exit(0)
727     return [options.xmlfile, options.target_dir, options.tmp_dir, options.gui, options.force_sources]
728
729 #=================================================================
730 # strip : removes spaces at the beginning and at the end of the 
731 #         <param> if it is of string type
732 #=================================================================
733 def strip(param):
734     """
735     Removes spaces at the beginning and at the end
736     of the given parameter.
737     """
738     if type(param) == types.StringType:
739         return param.strip()
740     return param
741     
742 #=================================================================
743 # get_dependencies : extract products dependencies
744 #=================================================================
745 def get_dependencies(prods):
746     """
747     Gets a list of installed and required products.
748     """
749     prods_list = list()
750     for product in prods:
751         for dep in product.dependencies:
752             if dep and dep not in prods_list:
753                 prods_list.append( dep )
754                 dep_name = dep
755                 if product.whattodo == __BUILDSRC__:
756                     dep_name = dep + "_src"
757                 if dep_name not in parser.full_prods_list:
758                     msg = "Prerequisite '%s' is required for '%s' product,\n"%(dep, product.name)
759                     msg += "but the first one is absent in the list of products to be installed!\n"
760                     msg += "Please check your XML file."
761                     warning(msg)
762
763         if product.name and product.name not in prods_list:
764             prods_list.append( product.name )
765             
766     return " ".join( prods_list )
767
768 #==============================================================
769 # create_dir : creates a directory with (optional) permissions,
770 #              returns the part of path that existed before
771 #              directory creation; exits with error if access
772 #              is denied
773 #==============================================================
774 def create_dir(directory, access = 0777):
775     """
776     Creates a directory with (optional) permissions,
777     returns the part of path that existed before
778     directory creation; exits with error if access
779     is denied.
780     """
781     dirs = directory.split("/")
782     existing = "";
783     dir = ""
784     root = ""
785     for subdir in dirs:
786         if len(subdir) == 0:  continue
787         dir = "%s/%s"%(dir, subdir)
788         if os.path.exists(dir):
789             existing = dir
790         else:
791             try:
792                 os.mkdir(dir, access)
793             except:
794                 error_exit("Can't create directory: %s.\nAccess is denied."%directory)
795             if dir == "%s/%s"%(existing, subdir):
796                 root = dir
797     return root
798
799 #==============================================================
800 # substituteVars : performes environment variables substistution
801 #                  the given string; if varibale is not defined
802 #                  it is substituted by the empty string
803 #==============================================================
804 def substituteVars(str):
805     """
806     Performes environment variables substistution.
807     """
808     str = os.path.expanduser(str)
809     str = os.path.expandvars(str)
810     return str
811
812 #================================================================
813 # get_program_path : gets program's directory path
814 #                    (and performs 'cd' command there) 
815 #================================================================
816 def get_program_path():
817     """
818     Returns the program directory path
819     (and make this directory current).
820     """
821     path = os.path.dirname(sys.argv[0])
822     if path:
823         os.chdir(path)
824     return os.getcwd()
825
826 #================================================================
827 # check_dir : checks directory existence
828 #================================================================
829 def check_dir(dir):
830     """
831     Checks directory existence.
832     """
833     if (os.path.islink(dir)):
834         realpath = os.path.realpath(dir)
835         if not os.path.exists(realpath):
836             msg = "Invalid link %s.\nThe directory %s a link points to does not exist. Stopped..."%(dir,realpath)
837             error_exit(msg, False)
838     else:
839         if not os.path.exists(dir):
840             msg = "Directory %s does not exist. Stopped..."%dir
841             error_exit(msg, False)
842     pass
843
844 #===============================================================
845 # check_disk_space : checks the disk space;
846 #                    quits if there is no enough disk space
847 #===============================================================
848 def check_disk_space(products, scripts_dir, target_dir, tmp_dir, is_force_src=False):
849     """
850     Checks if there is enough disk space to install products.
851     Quits with error if there is no enough disk space.
852     """
853     install_space = 0
854     temporary_space = 0
855     for product in products:
856         prod_space = 0
857         try:
858             spaces = product.installdiskspace.split(',')
859             prod_space = int( spaces[0] )
860             if product.whattodo == __BINARIES__:
861                 prod_space = int( spaces[0] )
862                 if product.type == __CTX__COMPONENT__:
863                     prod_space += int( spaces[1] )
864             else:
865                 if product.type == __CTX__PREREQUISITE__:
866                     prod_space = int( spaces[0] )
867                 else:
868                     prod_space = int( spaces[2] )
869         except:
870             pass
871         install_space = install_space + prod_space
872         pass
873
874     res = os.system("%s/%s %s %d"%(scripts_dir, "checkSize.sh", target_dir, install_space))
875     if res:
876         msg = "There is no enough space to install the products. Stopped..."
877         error_exit(msg, False)
878     pass
879  
880 #===============================================================
881 # remove_dir : removes temporary directory
882 #===============================================================
883 def remove_dir(path):
884     """
885     Removes temporary directory.
886     """
887     if path and os.path.exists(path):
888         os.system("rm -rf " + path)
889     pass
890
891 #==============================================================
892 # has_binaries : returns True if some product is installed from
893 #                binaries
894 #===============================================================
895 def has_binaries(products):
896     """
897     Returns True if some product is installed in 'binaries' mode.
898     """
899     for product in products:
900         if product.whattodo == __BINARIES__:
901             return True
902     return False
903
904 #==============================================================
905 # has_sources : returns True if some product is installed from
906 #               sources
907 #===============================================================
908 def has_sources(products):
909     """
910     Returns True if some product is installed in 'sources' mode.
911     """
912     for product in products:
913         if product.whattodo == __BUILDSRC__:
914             return True
915     return False
916
917 #==============================================================
918 # get_tmp_dir : gets temporary directory name
919 #===============================================================
920 def get_tmp_dir(dir):
921     """
922     Gets temporary directory path.
923     """
924     max_attempts = 100
925     dir_prefix="INSTALLWORK"
926     range_bottom = 0; range_top = 999999
927     for i in xrange(max_attempts):
928         tmp_dir = "%s/%s%d"%(dir, dir_prefix, random.randint(range_bottom,range_top))
929         if not os.path.exists( tmp_dir ):
930             return tmp_dir
931     return "%s/%s%d"%(dir, dir_prefix, random.randint(range_bottom,range_top))
932
933 #==============================================================
934 # get_os_release : gets OS release; the OS name, version and
935 #                  architecture
936 #                  For example:
937 #                  RedHat, 8.0; Mandriva, 2006.0, 64
938 #===============================================================
939 def get_os_release():
940     filename = "/etc/issue"
941     # ---
942     plt_name = "unknown"
943     plt_ver  = ""
944     plt_arch = ""   
945     if os.path.exists(filename):
946         # ---
947         f = open(filename)
948         lines = f.readlines()
949         f.close()
950         # ---
951         regvar  = re.compile("(.*)\s+[^\s]*[R|r]elease[^\s]*\s+([\d.]*)")
952         regvar1 = re.compile("(.*)\s+[^\s]*[L|l][I|i][N|n][U|u][X|x][^\s]*(.*)\s+([\d.]*)\s+")
953         for l in lines:
954             res = re.search(regvar, l)
955             if not res:
956                 res = re.search(regvar1, l)
957             if res:
958                 plt_name = "".join("".join(res.groups()[:len(res.groups())-1]).split())
959                 # workaround for Mandrake and other platforms
960                 plt_name = plt_name.replace("Linux", "").replace("linux", "").replace("LINUX", "").strip()
961                 # workaround for SuSe
962                 plt_name = plt_name.replace("Welcometo", "").strip()
963                 # ---
964                 plt_ver  = res.group(len(res.groups()))
965                 if re.search(r'x86_64', l):
966                     plt_arch = "64"
967                     pass
968                 # workaround for Red Hat Enterprise
969                 if not plt_arch:
970                     try:
971                         import platform
972                         if platform.machine() == "x86_64":
973                             plt_arch = "64"
974                             pass
975                         pass
976                     except:
977                         pass
978                     pass
979                 break
980             pass
981         pass
982
983     return plt_name, plt_ver, plt_arch
984
985 #==============================================================
986 # check_xml_file : checks XML file existence and readability
987 #===============================================================
988 def check_xml_file(xml_file):
989     """
990     Checks XML file existence and readability.
991     """
992     if not os.path.isfile(xml_file):
993         msg = "Configuration file %s is not found!"%xml_file
994         error_exit(msg, False)
995
996     if not os.access(xml_file, os.R_OK):
997         msg = "There is no read access for %s file!"%xml_file
998         error_exit(msg, False)
999     pass
1000
1001 #==============================================================
1002 # get_supported_plts : gets map of supported Linux platforms and
1003 #                      corresponding configuration files
1004 #===============================================================
1005 def get_supported_platforms(xml_file=None):
1006     """
1007     Gets map of supported Linux platforms and
1008     corresponding configuration files.
1009     """
1010     platforms_map = {}
1011     xml_file_list = list()
1012     if xml_file:
1013         xml_file_list = [ xml_file ]
1014     else:
1015         entry_list = os.listdir(get_program_path())
1016         for entry in entry_list:
1017             if re.search(r'(.*).xml', entry):
1018                 xml_file_list.append(os.path.abspath(entry))
1019     for an_xml_file in xml_file_list: # XML files parsing
1020         check_xml_file(an_xml_file)
1021         filehandle = open(an_xml_file)
1022         data = filehandle.read()
1023         filehandle.close()
1024         parser = ConfigParser()
1025         parser.feed(data)
1026         parser.close()
1027         if parser.config.platforms is not None:
1028             for plt in parser.config.platforms.split(","):
1029                 if not plt or plt in platforms_map.keys(): continue
1030 ##                 print "plt = %s, xml = %s"%(strip(plt), an_xml_file)
1031                 platforms_map[strip(plt)] = an_xml_file
1032     return platforms_map
1033
1034 #------------------------------------------------------------------#
1035 #                                                                  #
1036 #                    EXECUTION STARTS HERE                         #
1037 #                                                                  #
1038 #------------------------------------------------------------------#
1039
1040 if __name__ == "__main__":
1041     # parse command line
1042     [xml_file, target_dir, tmp_dir, is_gui, is_force_src] = parse_parameters()
1043     if xml_file:   xml_file   = os.path.abspath(xml_file)
1044     if target_dir: target_dir = os.path.abspath(target_dir)
1045     if tmp_dir:    tmp_dir    = os.path.abspath(tmp_dir)
1046     # get program dir
1047     cur_dir = get_program_path()
1048
1049     #---- GUI ----------------
1050
1051     if is_gui : 
1052         env = os.environ
1053         if not env.has_key("PATH") :
1054             env["PATH"] = ""
1055         if not env.has_key("LD_LIBRARY_PATH") :
1056             env["LD_LIBRARY_PATH"] = ""
1057
1058         env["LD_LIBRARY_PATH"] =  "./bin/lib:" + ".:" + env["LD_LIBRARY_PATH"]
1059         env["PATH"] = ".:" + env["PATH"]
1060
1061         cmd = "./bin/SALOME_InstallWizard"
1062         if xml_file is not None:
1063             cmd += " --file %s"%xml_file
1064         if target_dir is not None:
1065             cmd += " --target %s"%target_dir
1066         if tmp_dir is not None:
1067             cmd += " --tmp %s"%tmp_dir
1068         if is_force_src:
1069             cmd += " --all-from-sources"
1070         cmd += "&"
1071         sys.exit(os.system(cmd))
1072
1073     #-----  TUI ---------------------
1074
1075     # define xml file to be used
1076     if not xml_file:
1077         # get current Linux platform
1078         plt_name = ""; plt_ver  = ""; plt_arch  = ""
1079         plt_name, plt_ver, plt_arch = get_os_release()
1080         data = []
1081         for i in plt_name, plt_ver, plt_arch:
1082             if i: data.append(i)
1083         full_plt_name = "_".join(data)
1084         xml_file_name = "config_%s.xml" % full_plt_name
1085         if not plt_name or not plt_ver or not os.path.exists("%s/%s"%(cur_dir, xml_file_name)):
1086             msg  = "Not supported Linux platform: %s!" % full_plt_name
1087             error_exit(msg)
1088             pass
1089
1090         xml_file= "%s/%s"%(cur_dir, xml_file_name)
1091         pass
1092
1093     check_xml_file(xml_file)
1094     
1095     # parse XML file -----------
1096     message("Parsing XML configuration file: %s"%xml_file)
1097     parser = ConfigParser(is_force_src)
1098     parser.parse(xml_file)
1099
1100     # source directory map
1101     bin_dir = ""
1102     if parser.config.platforms:
1103         bin_dir += "/%s"%parser.config.platforms
1104     subdir = { __BINARIES__   : "BINARIES" + bin_dir,
1105                __BUILDSRC__   : "SOURCES",
1106                __PREINSTALL__ : ""}
1107
1108     # check scripts directory -----------
1109     scripts_dir = "%s/%s"%(cur_dir, "config_files")
1110     check_dir(scripts_dir)
1111
1112     # check products archives directories -----------
1113     has_bin = has_binaries(parser.products)
1114     has_src = has_sources(parser.products)
1115     source_dir = "%s/%s"%(cur_dir, "Products")
1116
1117     if has_src or has_bin:
1118         check_dir(source_dir)
1119
1120     if has_src:
1121         check_dir("%s/%s"%(source_dir,subdir[__BUILDSRC__]))
1122
1123     if has_bin:
1124         check_dir("%s/%s"%(source_dir,subdir[__BINARIES__]))
1125
1126     # check/create target dir -----------
1127     if target_dir is None:
1128         target_dir = parser.config.targetdir
1129     target_dir = substituteVars(target_dir)
1130
1131     message("Creating target directory: " + target_dir)
1132     create_dir(target_dir, 0755)
1133
1134     if not os.path.exists(target_dir):
1135         error_exit("Invalid target directory: " + target_dir)
1136
1137     if not os.access(target_dir, os.W_OK) :
1138         error_exit("There is no write permissions for the directory: " + target_dir)
1139
1140     # check/create temporary dir -----------
1141     if tmp_dir is None:
1142         tmp_dir = parser.config.tmpdir
1143     if not tmp_dir:
1144         tmp_dir = "/tmp"
1145     tmp_dir = substituteVars(tmp_dir)
1146     tmp_dir = get_tmp_dir(tmp_dir)
1147
1148     message("Creating temporary directory: " + tmp_dir)
1149     root_path = create_dir(tmp_dir, 0755)
1150    
1151     if not os.path.exists(tmp_dir):
1152         error_exit("Invalid temporary directory: " + tmp_dir)
1153
1154     if not os.access(tmp_dir, os.W_OK) :
1155         error_exit("There is no write permissions for the directory: " + tmp_dir)
1156         
1157     # check available disk space -----------
1158     message("Checking available disk space")
1159     check_disk_space(parser.products, scripts_dir, target_dir, tmp_dir, is_force_src)
1160
1161     # change current directory -----------
1162     os.chdir(scripts_dir)
1163
1164     # get dependencies list -----------
1165     list_of_dep = get_dependencies(parser.products)
1166     products_string = " ".join(parser.full_prods_list)
1167
1168     # don't remove sources and tmp files, by default -----------
1169     rm_src_tmp = "FALSE"
1170
1171     # starting -----------
1172     message("Starting ...")
1173     
1174     # install products -----------
1175     for product in parser.products:
1176         # remove only prerequisites temporary files
1177         if product.type == __CTX__PREREQUISITE__ or \
1178            (product.type == __CTX__COMPONENT__ and product.whattodo == __BUILDSRC__):
1179             rm_src_tmp = "TRUE"
1180         message("... processing %s ..."%product.name)
1181         cmd = '%s/%s %s %s %s/%s %s "%s" %s "%s" %s/%s %s %s/%s' % (
1182             scripts_dir, product.script,
1183             product.whattodo,
1184             tmp_dir,
1185             source_dir, subdir[product.whattodo],
1186             target_dir,
1187             products_string,
1188             product.name,
1189             products_string,
1190             source_dir, subdir[__BUILDSRC__],
1191             rm_src_tmp,
1192             source_dir, subdir[__BINARIES__]
1193             )
1194         if product.woguiinst is not None and product.woguiinst != "": cmd += ' TRUE'
1195         res = os.system(cmd)
1196         rm_src_tmp = "FALSE"
1197         pass
1198
1199     # pickup environment -----------
1200     message("Creating environment files")
1201     for product in parser.products :
1202         if check_bool(product.pickupEnv):
1203             cmd = '%s/%s pickup_env %s %s/%s %s "%s" %s "%s" %s/%s %s %s/%s' % (
1204                 scripts_dir, product.script,
1205                 tmp_dir,
1206                 source_dir, subdir[product.whattodo],
1207                 target_dir,
1208                 products_string,
1209                 product.name,
1210                 products_string,
1211                 source_dir, subdir[__BUILDSRC__],
1212                 rm_src_tmp,
1213                 source_dir, subdir[__BINARIES__]
1214                 )
1215             if product.woguiinst is not None and product.woguiinst != "": cmd += ' TRUE'
1216             res = os.system(cmd)
1217             pass
1218         pass
1219
1220     # clean temporary directory -----------
1221     message("Cleaning temporary directory")
1222     clean_all()
1223     
1224     # finishing -----------
1225     message("Finished!")
1226     pass