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