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