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