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