Salome HOME
Update for IW salome 5.X
[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.5"
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, pltname=None):
436         self.docElem = None
437         self.products = []
438         self.full_prods_list = []
439         self.config = None
440         self.is_force_src = is_force_src
441         self.pltname = pltname
442         pass
443         
444     def parse_config(self):
445         # Parse 'config' part of the XML file
446         configElem = self.docElem.getElementsByTagName('config')[0]
447         
448         self.config = Config(configElem.getAttribute('version').strip(),
449                              configElem.getAttribute('caption').strip(),
450                              configElem.getAttribute('copyright').strip(),
451                              configElem.getAttribute('license').strip(),
452                              configElem.getAttribute('platforms').strip(),
453                              configElem.getAttribute('targetdir').strip(),
454                              configElem.getAttribute('tempdir').strip())
455         if not self.pltname and self.config.platforms:
456             self.pltname = self.config.platforms.split(",")[0].strip()
457         pass
458     
459     def parse_dependencies(self):
460         # Parse 'dependencies' part of the XML file
461         depsMap = {}
462         depsElem = self.docElem.getElementsByTagName('dependencies')[0]
463         for prodElem in depsElem.getElementsByTagName('product'):
464             prodName = prodElem.getAttribute('name').strip()
465             if not prodName: continue
466             depsList = []
467             for depElem in prodElem.getElementsByTagName('dep'):
468                 depsList.append(depElem.firstChild.data)
469                 pass
470             depsMap[prodName] = depsList
471             pass
472         return depsMap
473     
474     def parse_product(self):
475         # Parse 'products' part of the XML file
476         depsMap = self.parse_dependencies()
477         prodsElem = self.docElem.getElementsByTagName('products')[0]
478         sal_prods_list = []; req_prods_list = []
479         modules_list = []; prereqs_list = []
480         for prodElem in prodsElem.getElementsByTagName('product'):
481             prodName = prodElem.getAttribute('name').strip()
482             if not prodName: continue
483             instElems = prodElem.getElementsByTagName('installation')
484             instElem = None
485             for node in instElems:
486                 if not self.pltname or self.pltname == node.getAttribute('os').strip():
487                     instElem = node
488                     break
489                 pass
490             if not instElem: continue
491             if check_bool(str(instElem.getAttribute('disable').strip())): continue
492             depsList = []
493             if prodName in depsMap: depsList = depsMap[prodName]
494             aProduct = Product(prodName,
495                                prodElem.getAttribute('type').strip(),
496                                instElem.getAttribute('os').strip(),
497                                instElem.getAttribute('version').strip(),
498                                depsList,
499                                instElem.getAttribute('woguimode').strip(),
500                                instElem.getAttribute('installdiskspace').strip(),
501                                instElem.getAttribute('script').strip(),
502                                instElem.getAttribute('pickupenv').strip())
503             if self.is_force_src:
504                 aProduct.setMode(__BUILDSRC__)
505                 pass
506             if prodElem.getAttribute('type').strip() == "component":
507                 sal_prods_list.append(aProduct)
508                 # fill an ordered modules list -----------
509                 modules_list.append(prodName)
510                 modules_list.append(prodName + "_src")
511                 pass
512             else: #prerequisite
513                 req_prods_list.append(aProduct)
514                 # fill an ordered prerequisites list -----------
515                 prereqs_list.append(prodName)
516                 #AKL: prerequisite sources and temp files are removed, by default.
517                 #     So, there is no need to make sources environment
518                 #if aProduct.whattodo == __BUILDSRC__: prereqs_list.append(prodName + "_src")
519                 pass
520             pass
521         self.products.extend( req_prods_list )
522         self.products.extend( sal_prods_list )
523         if len(self.products) != 0:
524             gcc_product = Product("gcc",
525                                   __CTX__PREREQUISITE__,
526                                   self.products[0].os,
527                                   "",
528                                   [],
529                                   None,
530                                   "0,0,0",
531                                   "gcc-common.sh",
532                                   "")
533             gcc_product.setMode(__PREINSTALL__)
534             self.products.insert(0, gcc_product)
535             prereqs_list.insert(0, gcc_product.name)
536             pass
537         self.full_prods_list.extend( prereqs_list )
538         self.full_prods_list.extend( modules_list )
539         pass
540
541     def parse(self, xml_file):
542         filehandle = open(xml_file)
543         sax_parser = xml.sax.make_parser()
544         doc = xml.dom.minidom.parse(filehandle, sax_parser)
545         filehandle.close()
546
547         self.docElem = doc.documentElement
548         self.parse_config()
549         self.parse_product()
550         pass
551
552     def getProduct(self, prod):
553         for product in self.products:
554             if product.name == prod:
555                 return product
556         return None
557
558
559 #------------------------------------------------------------------#
560 #                                                                  #
561 #                         SERVICE FUNCTIONS                        #
562 #                                                                  #
563 #------------------------------------------------------------------#
564
565 #==============================================================
566 # message: prints diagnostic information
567 #==============================================================
568 def message(msg):
569     """
570     Prints diagnostic information.
571     """
572     if msg.strip():
573         print ">>>", msg
574     pass
575
576 #==============================================================
577 # warning: prints warning
578 #==============================================================
579 def warning(msg):
580     """
581     Prints warning.
582     """
583     if msg.strip():
584         print ""
585         print msg
586         print ""
587     pass
588
589 #==============================================================
590 # error_exit : prints (optionally) error string, then prints
591 #              help information and quits
592 #==============================================================
593 def error_exit(msg = "", print_help = True):
594     """
595     Prints (optionally) error string,
596     then prints help information and quits.
597     """
598     # print error message
599     if len(msg.strip()):
600         print ""
601         print msg
602         print ""
603     # print help information
604     if print_help:
605         global opt_parser
606         if opt_parser:
607             opt_parser.print_help() 
608             print ""
609     # cleaning 
610     clean_all()
611     # quit
612     sys.exit(1);
613     pass
614
615 #==============================================================
616 # boolean : Converts string to boolean value.
617 #==============================================================
618 def boolean(val):
619     """
620     Converts string to boolean value if possible.
621     Raises exception if wrong string is used.
622     """
623     if isinstance(val, types.StringType):
624         if val.strip().lower()   in ["true",  "yes", "ok"]     : return True
625         elif val.strip().lower() in ["false", "no",  "cancel"] : return False
626         else: raise TypeError("invalid boolean value")
627     return bool(val)
628
629 #=================================================================
630 # check_bool : checks boolean value: yes/no, true/false, 1/0
631 #=================================================================
632 def check_bool(val):
633     """
634     Checks boolean value.
635     """
636     try:
637         return boolean(val)
638     except:
639         pass
640     return False
641
642 #==============================================================
643 # clean_all : performs system cleaning before exiting
644 #==============================================================
645 def clean_all():
646     """
647     Performs system cleaning before exiting.
648     """
649     global root_path
650     remove_dir(root_path)
651     pass
652
653 #==============================================================
654 # parse_parameters : parses command line arguments
655 #==============================================================
656 def parse_parameters():
657     """
658     Parses command line arguments.
659     """
660     global opt_parser
661     opt_parser = ArgParser()
662     
663     help_str  = "Runs the Installation Wizard in the GUI mode [default].\n"
664     opt_parser.add_option("-g",
665                           "--gui",
666                           action="store_true",
667                           dest="gui",
668                           default=True,
669                           help=help_str)
670     help_str  = "Runs the Installation Wizard in the TUI mode."
671     opt_parser.add_option("-b",
672                           "--batch",
673                           action="store_false",
674                           dest="gui",
675                           help=help_str)
676     help_str  = "The configuration xml file.\n"
677     help_str += "If this parameter is missing, then the program tries to define the "
678     help_str += "Linux platform and use the corresponding xml file. For example, "
679     help_str += "for Red Hat 8.0 config_RedHat_8.0.xml file is used in this case. "
680     opt_parser.add_option("-f",
681                           "--file",
682                           action="store",
683                           dest="xmlfile",
684                           metavar="FILE",
685                           help=help_str)
686     help_str  = "The platform specification.\n"
687     help_str += "This option can be used in conjunction with --file option in order"
688     help_str += "to specify Linux platform name when XML file contains installation"
689     help_str += "options for several platforms."
690     opt_parser.add_option("-p",
691                           "--platform",
692                           action="store",
693                           dest="platform",
694                           metavar="PLT",
695                           help=help_str)
696     help_str  = "The target directory the products to be installed to.\n"
697     help_str += "When used this parameter overrides the default target directory "
698     help_str += "defined in the configuration xml file."
699     opt_parser.add_option("-d",
700                           "--target",
701                           action="store",
702                           dest="target_dir",
703                           metavar="DIR",
704                           help=help_str)
705     help_str  = "The directory to be used for temporary files.\n"
706     help_str += "When used this parameter overrides the default temporary directory "
707     help_str += "defined in the configuration xml file."
708     opt_parser.add_option("-t",
709                           "--tmp",
710                           action="store",
711                           dest="tmp_dir",
712                           metavar="DIR",
713                           help=help_str)
714     help_str  = "Force all products to be installed from sources \n"
715     help_str += "including SALOME modules.\n"
716     help_str += "If this option is used all the default installation modes are ignored."
717     opt_parser.add_option("-a",
718                           "--all-from-sources",
719                           action="store_true",
720                           dest="force_sources",
721                           default=False,
722                           help=help_str)
723     help_str  = "Install all SALOME binaries packages to one directory.\n"
724     help_str += "This option is ignored when --all-from-sources (-a) option is used."
725     opt_parser.add_option("-s",
726                           "--single-directory",
727                           action="store_true",
728                           dest="single_dir",
729                           default=False,
730                           help=help_str)
731     help_str  = "Prints version information and quits."
732     opt_parser.add_option("-v",
733                           "--version",
734                           action="store_true",
735                           help=help_str)
736     help_str  = "Prints this help and quits."
737     opt_parser.add_option("-h",
738                           "--help",
739                           action="store_true",
740                           help=help_str)
741     (options, args) = opt_parser.parse_args()
742     if options.help:
743         # print help info and quit
744         print "\nSALOME Installation Wizard\n"
745         opt_parser.print_help()
746         print ""
747         sys.exit(0)
748     if options.version:
749         # print version info and quit
750         print ""
751         cmd = "./bin/SALOME_InstallWizard --version"
752         os.system(cmd)
753         print ""
754         sys.exit(0)
755     return [options.xmlfile, options.target_dir, options.tmp_dir, options.gui, options.force_sources, options.single_dir, options.platform]
756
757 #=================================================================
758 # strip : removes spaces at the beginning and at the end of the 
759 #         <param> if it is of string type
760 #=================================================================
761 def strip(param):
762     """
763     Removes spaces at the beginning and at the end
764     of the given parameter.
765     """
766     if type(param) == types.StringType:
767         return param.strip()
768     return param
769     
770 #=================================================================
771 # get_dependencies : extract products dependencies
772 #=================================================================
773 def get_dependencies(prods):
774     """
775     Gets a list of installed and required products.
776     """
777     prods_list = []
778     for product in prods:
779         for dep in product.dependencies:
780             if dep and dep not in prods_list:
781                 prods_list.append( dep )
782                 dep_name = dep
783                 if product.whattodo == __BUILDSRC__:
784                     dep_name = dep + "_src"
785                 if dep_name not in parser.full_prods_list:
786                     msg = "Prerequisite '%s' is required for '%s' product,\n"%(dep, product.name)
787                     msg += "but the first one is absent in the list of products to be installed!\n"
788                     msg += "Please check your XML file."
789                     warning(msg)
790
791         if product.name and product.name not in prods_list:
792             prods_list.append( product.name )
793             
794     return " ".join( prods_list )
795
796 #==============================================================
797 # create_dir : creates a directory with (optional) permissions,
798 #              returns the part of path that existed before
799 #              directory creation; exits with error if access
800 #              is denied
801 #==============================================================
802 def create_dir(directory, access = 0777):
803     """
804     Creates a directory with (optional) permissions,
805     returns the part of path that existed before
806     directory creation; exits with error if access
807     is denied.
808     """
809     dirs = directory.split("/")
810     existing = "";
811     dir = ""
812     root = ""
813     for subdir in dirs:
814         if len(subdir) == 0:  continue
815         dir = "%s/%s"%(dir, subdir)
816         if os.path.exists(dir):
817             existing = dir
818         else:
819             try:
820                 os.mkdir(dir, access)
821             except:
822                 error_exit("Can't create directory: %s.\nAccess is denied."%directory)
823             if dir == "%s/%s"%(existing, subdir):
824                 root = dir
825     return root
826
827 #==============================================================
828 # substituteVars : performes environment variables substistution
829 #                  the given string; if varibale is not defined
830 #                  it is substituted by the empty string
831 #==============================================================
832 def substituteVars(str):
833     """
834     Performes environment variables substistution.
835     """
836     str = os.path.expanduser(str)
837     str = os.path.expandvars(str)
838     return str
839
840 #================================================================
841 # get_program_path : gets program's directory path
842 #                    (and performs 'cd' command there) 
843 #================================================================
844 def get_program_path():
845     """
846     Returns the program directory path
847     (and make this directory current).
848     """
849     path = os.path.dirname(sys.argv[0])
850     if path:
851         os.chdir(path)
852     return os.getcwd()
853
854 #================================================================
855 # check_dir : checks directory existence
856 #================================================================
857 def check_dir(dir):
858     """
859     Checks directory existence.
860     """
861     if (os.path.islink(dir)):
862         realpath = os.path.realpath(dir)
863         if not os.path.exists(realpath):
864             msg = "Invalid link %s.\nThe directory %s a link points to does not exist. Stopped..."%(dir,realpath)
865             error_exit(msg, False)
866     else:
867         if not os.path.exists(dir):
868             msg = "Directory %s does not exist. Stopped..."%dir
869             error_exit(msg, False)
870     pass
871
872 #===============================================================
873 # check_disk_space : checks the disk space;
874 #                    quits if there is no enough disk space
875 #===============================================================
876 def check_disk_space(products, scripts_dir, target_dir, tmp_dir, is_force_src=False):
877     """
878     Checks if there is enough disk space to install products.
879     Quits with error if there is no enough disk space.
880     """
881     install_space = 0
882     temporary_space = 0
883     for product in products:
884         prod_space = 0
885         try:
886             spaces = product.installdiskspace.split(',')
887             prod_space = int( spaces[0] )
888             if product.whattodo == __BINARIES__:
889                 prod_space = int( spaces[0] )
890                 if product.type == __CTX__COMPONENT__:
891                     prod_space += int( spaces[1] )
892             else:
893                 if product.type == __CTX__PREREQUISITE__:
894                     prod_space = int( spaces[0] )
895                 else:
896                     prod_space = int( spaces[2] )
897         except:
898             pass
899         install_space = install_space + prod_space
900         pass
901
902     res = os.system("%s/%s %s %d"%(scripts_dir, "checkSize.sh", target_dir, install_space))
903     if res:
904         msg = "There is no enough space to install the products. Stopped..."
905         error_exit(msg, False)
906     pass
907  
908 #===============================================================
909 # remove_dir : removes temporary directory
910 #===============================================================
911 def remove_dir(path):
912     """
913     Removes temporary directory.
914     """
915     if path and os.path.exists(path):
916         os.system("rm -rf " + path)
917     pass
918
919 #==============================================================
920 # has_binaries : returns True if some product is installed from
921 #                binaries
922 #===============================================================
923 def has_binaries(products):
924     """
925     Returns True if some product is installed in 'binaries' mode.
926     """
927     for product in products:
928         if product.whattodo == __BINARIES__:
929             return True
930     return False
931
932 #==============================================================
933 # has_sources : returns True if some product is installed from
934 #               sources
935 #===============================================================
936 def has_sources(products):
937     """
938     Returns True if some product is installed in 'sources' mode.
939     """
940     for product in products:
941         if product.whattodo == __BUILDSRC__:
942             return True
943     return False
944
945 #==============================================================
946 # get_tmp_dir : gets temporary directory name
947 #===============================================================
948 def get_tmp_dir(dir):
949     """
950     Gets temporary directory path.
951     """
952     max_attempts = 100
953     dir_prefix="INSTALLWORK"
954     range_bottom = 0; range_top = 999999
955     for i in xrange(max_attempts):
956         tmp_dir = "%s/%s%d"%(dir, dir_prefix, random.randint(range_bottom,range_top))
957         if not os.path.exists( tmp_dir ):
958             return tmp_dir
959     return "%s/%s%d"%(dir, dir_prefix, random.randint(range_bottom,range_top))
960
961 #==============================================================
962 # get_os_release : gets OS release; the OS name, version and
963 #                  architecture
964 #                  For example:
965 #                  RedHat, 8.0; Mandriva, 2006.0, 64
966 #===============================================================
967 def get_os_release():
968     filename = "/etc/issue"
969     # ---
970     plt_name = "unknown"
971     plt_ver  = ""
972     plt_arch = ""   
973     if os.path.exists(filename):
974         # ---
975         f = open(filename)
976         lines = f.readlines()
977         f.close()
978         # ---
979         regvar  = re.compile("(.*)\s+[^\s]*[R|r]elease[^\s]*\s+([\d.]*)")
980         regvar1 = re.compile("(.*)\s+[^\s]*[L|l][I|i][N|n][U|u][X|x][^\s]*(.*)\s+([\d.]*)\s+")
981         for l in lines:
982             res = re.search(regvar, l)
983             if not res:
984                 res = re.search(regvar1, l)
985             if res:
986                 plt_name = " ".join(" ".join(res.groups()[:len(res.groups())-1]).split())
987                 # workaround for Mandrake and other platforms
988                 plt_name = plt_name.replace("Linux", "").replace("linux", "").replace("LINUX", "").strip()
989                 # workaround for SuSe
990                 plt_name = plt_name.replace("Welcome to", "").strip()
991                 # ---
992                 plt_name = " ".join(plt_name.split())
993                 plt_ver  = res.group(len(res.groups()))
994                 if re.search(r'x86_64', l):
995                     plt_arch = "64bit"
996                     pass
997                 # workaround for Red Hat Enterprise
998                 if not plt_arch:
999                     try:
1000                         import platform
1001                         if platform.machine() == "x86_64":
1002                             plt_arch = "64bit"
1003                             pass
1004                         pass
1005                     except:
1006                         pass
1007                     pass
1008                 break
1009             pass
1010         pass
1011
1012     return plt_name, plt_ver, plt_arch
1013
1014 #==============================================================
1015 # check_xml_file : checks XML file existence and readability
1016 #===============================================================
1017 def check_xml_file(xml_file):
1018     """
1019     Checks XML file existence and readability.
1020     """
1021     if not os.path.isfile(xml_file):
1022         msg = "Configuration file %s is not found!"%xml_file
1023         error_exit(msg, False)
1024
1025     if not os.access(xml_file, os.R_OK):
1026         msg = "There is no read access for %s file!"%xml_file
1027         error_exit(msg, False)
1028     pass
1029
1030 #==============================================================
1031 # get_supported_plts : gets map of supported Linux platforms and
1032 #                      corresponding configuration files
1033 #===============================================================
1034 def get_supported_platforms(xml_file=None):
1035     """
1036     Gets map of supported Linux platforms and
1037     corresponding configuration files.
1038     """
1039     platforms_map = {}
1040     if xml_file:
1041         xml_file_list = [xml_file]
1042         pass
1043     else:
1044         xml_file_list = filter(lambda i: i.endswith(".xml"), os.listdir(get_program_path()))
1045         while 'config.xml' in xml_file_list: xml_file_list.remove('config.xml')
1046         if os.path.exists(os.path.join(get_program_path(), 'config.xml')):
1047             xml_file_list.append('config.xml')
1048         xml_file_list = [os.path.abspath(i) for i in xml_file_list]
1049         pass
1050     for an_xml_file in xml_file_list: # XML files parsing
1051         check_xml_file(an_xml_file)
1052         parser = ConfigParser()
1053         parser.parse(an_xml_file)
1054         if parser.config.platforms is not None:
1055             for plt in parser.config.platforms.split(","):
1056                 if not plt or plt in platforms_map.keys(): continue
1057                 platforms_map[strip(plt)] = an_xml_file
1058                 pass
1059             pass
1060         pass
1061     return platforms_map
1062
1063 #==============================================================
1064 # Print menu with list of supported platform
1065 # and return user choice
1066 #===============================================================
1067 def select_platform(all_platforms):
1068     platforms = all_platforms.keys()
1069     platforms.sort()
1070     pltname = None
1071     while not pltname:
1072         print "Please, select any platform from the list below."
1073         print "--------------------------"
1074         for idx in range(len(platforms)):
1075             print " %2d. %s" % (idx+1, " ".join(platforms[idx].split("_")))
1076         print "  0. Exit"
1077         print "--------------------------"
1078         print "Type your choice (%d-%d) and press <Enter>:" % (0, len(platforms)),
1079         try:
1080             idx = raw_input()
1081         except:
1082             sys.exit(1)
1083         try:
1084             idx = int(idx)
1085         except:
1086             warning("Invalid input!")
1087             pass
1088         if idx == 0: sys.exit(0)
1089         if idx > 0 and idx <= len(platforms):
1090             pltname = platforms[idx-1]
1091         else:
1092             warning("Invalid input!")
1093         pass
1094     return pltname, all_platforms[pltname]
1095
1096 #------------------------------------------------------------------#
1097 #                                                                  #
1098 #                    EXECUTION STARTS HERE                         #
1099 #                                                                  #
1100 #------------------------------------------------------------------#
1101
1102 if __name__ == "__main__":
1103     # parse command line
1104     [xml_file, target_dir, tmp_dir, is_gui, is_force_src, is_single_dir, pltname] = parse_parameters()
1105     if xml_file:   xml_file   = os.path.abspath(xml_file)
1106     if target_dir: target_dir = os.path.abspath(target_dir)
1107     if tmp_dir:    tmp_dir    = os.path.abspath(tmp_dir)
1108     # get program dir
1109     cur_dir = get_program_path()
1110
1111     #---- GUI ----------------
1112
1113     if is_gui : 
1114         env = os.environ
1115         if not env.has_key("PATH") :
1116             env["PATH"] = ""
1117         if not env.has_key("LD_LIBRARY_PATH") :
1118             env["LD_LIBRARY_PATH"] = ""
1119
1120         env["LD_LIBRARY_PATH"] =  "./bin/lib:" + ".:" + env["LD_LIBRARY_PATH"]
1121         env["PATH"] = ".:" + env["PATH"]
1122
1123         cmd = "./bin/SALOME_InstallWizard"
1124         if xml_file is not None:
1125             cmd += " --file %s"%xml_file
1126         if pltname is not None:
1127             cmd += " --platform %s"%pltname
1128         if target_dir is not None:
1129             cmd += " --target %s"%target_dir
1130         if tmp_dir is not None:
1131             cmd += " --tmp %s"%tmp_dir
1132         if is_force_src:
1133             cmd += " --all-from-sources"
1134         if is_single_dir:
1135             cmd += " --single-directory"
1136         cmd += "&"
1137         sys.exit(os.system(cmd))
1138
1139     #-----  TUI ---------------------
1140     #
1141     # define xml file to be used
1142     #
1143     # get current Linux platform
1144     plt_name, plt_ver, plt_arch = get_os_release()
1145     data = []
1146     for i in plt_name, plt_ver, plt_arch:
1147         if i: data.append(i)
1148     full_plt_name = " ".join(data)
1149     # get all supported platforms
1150     all_platforms = get_supported_platforms(xml_file)
1151     if all_platforms:
1152         if pltname:
1153             # platform name is specified in the command line
1154             if pltname in all_platforms:
1155                 # if specified platform is supported, choose the corresponding XML file for use
1156                 xml_file = all_platforms[pltname]
1157             else:
1158                 # if specified platform is NOT supported, print warning message
1159                 # and prompt user to choose another platform
1160                 msg = "Specified platform is not supported: %s" % (pltname)
1161                 warning(msg)
1162                 pltname, xml_file = select_platform(all_platforms)
1163                 pass
1164             pass
1165         elif full_plt_name in all_platforms:
1166             # if current platform is supported, choose the corresponding XML file for use
1167             pltname  = full_plt_name
1168             xml_file = all_platforms[pltname]
1169         else:
1170             if xml_file and len(all_platforms) == 1:
1171                 # XML file is specified and contains only one platform definition
1172                 xml_file = all_platforms.values()[0]
1173                 pltname  = all_platforms.keys()[0]
1174             else:
1175                 # current Linux platform is not supported, print warning message
1176                 # and prompt user to choose platform from the list
1177                 warning("Not supported Linux platform: %s."%" ".join(data))
1178                 pltname, xml_file = select_platform(all_platforms)
1179             pass
1180         pass
1181     else:
1182         # current Linux platform is not supported, exit
1183         if pltname:
1184             msg = "Not supported Linux platform: %s."%pltname
1185         else:
1186             msg = "Not supported Linux platform: %s."%" ".join(data)
1187         error_exit(msg, False)
1188         pass
1189
1190     # parse XML file -----------
1191     message("Parsing XML configuration file: %s"%xml_file)
1192     parser = ConfigParser(is_force_src, pltname)
1193     parser.parse(xml_file)
1194
1195     # source directory map
1196     bin_dir = ""
1197     if parser.config.platforms:
1198         bin_dir += "/%s"%"_".join( parser.pltname.split() )
1199     subdir = { __BINARIES__   : "BINARIES" + bin_dir,
1200                __BUILDSRC__   : "SOURCES",
1201                __PREINSTALL__ : ""}
1202
1203     # check scripts directory -----------
1204     scripts_dir = "%s/%s"%(cur_dir, "config_files")
1205     check_dir(scripts_dir)
1206
1207     # check products archives directories -----------
1208     has_bin = has_binaries(parser.products)
1209     has_src = has_sources(parser.products)
1210     source_dir = "%s/%s"%(cur_dir, "Products")
1211
1212     if has_src or has_bin:
1213         check_dir(source_dir)
1214
1215     if has_src:
1216         check_dir("%s/%s"%(source_dir,subdir[__BUILDSRC__]))
1217
1218     if has_bin:
1219         check_dir("%s/%s"%(source_dir,subdir[__BINARIES__]))
1220
1221     # check/create target dir -----------
1222     if target_dir is None:
1223         target_dir = parser.config.targetdir
1224     target_dir = substituteVars(target_dir)
1225
1226     message("Creating target directory: " + target_dir)
1227     create_dir(target_dir, 0755)
1228
1229     if not os.path.exists(target_dir):
1230         error_exit("Invalid target directory: " + target_dir)
1231
1232     if not os.access(target_dir, os.W_OK) :
1233         error_exit("There is no write permissions for the directory: " + target_dir)
1234
1235     # check/create temporary dir -----------
1236     if tmp_dir is None:
1237         tmp_dir = parser.config.tmpdir
1238     if not tmp_dir:
1239         tmp_dir = "/tmp"
1240     tmp_dir = substituteVars(tmp_dir)
1241     tmp_dir = get_tmp_dir(tmp_dir)
1242
1243     message("Creating temporary directory: " + tmp_dir)
1244     root_path = create_dir(tmp_dir, 0755)
1245    
1246     if not os.path.exists(tmp_dir):
1247         error_exit("Invalid temporary directory: " + tmp_dir)
1248
1249     if not os.access(tmp_dir, os.W_OK) :
1250         error_exit("There is no write permissions for the directory: " + tmp_dir)
1251         
1252     # check available disk space -----------
1253     message("Checking available disk space")
1254     check_disk_space(parser.products, scripts_dir, target_dir, tmp_dir, is_force_src)
1255
1256     # change current directory -----------
1257     os.chdir(scripts_dir)
1258
1259     # get dependencies list -----------
1260     list_of_dep = get_dependencies(parser.products)
1261     products_string = " ".join(parser.full_prods_list)
1262
1263     # don't remove sources and tmp files, by default -----------
1264     rm_src_tmp = "FALSE"
1265
1266     # starting -----------
1267     message("Starting ...")
1268     
1269     # install products -----------
1270     for product in parser.products:
1271         # remove only prerequisites temporary files
1272         if product.type == __CTX__PREREQUISITE__ or \
1273            (product.type == __CTX__COMPONENT__ and product.whattodo == __BUILDSRC__):
1274             rm_src_tmp = "TRUE"
1275         message("... processing %s ..."%product.name)
1276         cmd = '%s/%s %s %s %s/%s %s "%s" %s "%s" %s/%s %s %s/%s' % (
1277             scripts_dir, product.script,
1278             product.whattodo,
1279             tmp_dir,
1280             source_dir, subdir[product.whattodo],
1281             target_dir,
1282             products_string,
1283             product.name,
1284             products_string,
1285             source_dir, subdir[__BUILDSRC__],
1286             rm_src_tmp,
1287             source_dir, subdir[__BINARIES__]
1288             )
1289         # install all modules with GUI
1290         if product.woguiinst is not None and product.woguiinst != "":
1291             cmd += ' TRUE'
1292         # use single directory or not
1293         if product.whattodo == __BINARIES__ and product.type == __CTX__COMPONENT__ and is_single_dir:
1294             cmd += ' TRUE'
1295         res = os.system(cmd)
1296         rm_src_tmp = "FALSE"
1297         pass
1298
1299     # modify *.la files, if --single-directory option was  -----------
1300     if is_single_dir:
1301         message("Modifying of *.la files of SALOME modules...")
1302         cmd = '%s/modifyLaFiles.sh modify_la_files %s' % (scripts_dir, target_dir)
1303         res = os.system(cmd)
1304
1305     # pickup environment -----------
1306     message("Creating environment files")
1307     for product in parser.products :
1308         if check_bool(product.pickupEnv):
1309             cmd = '%s/%s pickup_env %s %s/%s %s "%s" %s "%s" %s/%s %s %s/%s' % (
1310                 scripts_dir, product.script,
1311                 tmp_dir,
1312                 source_dir, subdir[product.whattodo],
1313                 target_dir,
1314                 products_string,
1315                 product.name,
1316                 products_string,
1317                 source_dir, subdir[__BUILDSRC__],
1318                 rm_src_tmp,
1319                 source_dir, subdir[__BINARIES__]
1320                 )
1321             # install all modules with GUI
1322             if product.woguiinst is not None and product.woguiinst != "":
1323                 cmd += ' TRUE'
1324             # use single directory or not
1325             if product.whattodo == __BINARIES__ and product.type == __CTX__COMPONENT__ and is_single_dir:
1326                 cmd += ' TRUE'
1327             res = os.system(cmd)
1328             pass
1329         pass
1330
1331     # clean temporary directory -----------
1332     message("Cleaning temporary directory")
1333     clean_all()
1334     
1335     # finishing -----------
1336     message("Finished!")
1337     pass