Salome HOME
Update installation sizes
[tools/install.git] / runInstall
1 #!/usr/bin/env python
2
3 import xmllib
4 import sys, os, string, re
5
6 #==============================================================
7 # get_help_info
8 #==============================================================
9 def get_help_info() :
10     str = "\nPAL/SALOME Installation Wizard\n\n"
11     str = str + "\tUsage : \n\tInstall [-g|b] [-f <xml-file>] [-t <target-dir>] [-tmp <tmp-dir>]\n"
12     str = str + "\n"
13     str = str + " -g              Runs the Installation Wizard in the GUI mode.\n"
14     str = str + "                 In this case only <xmlfile> is taken into account \n"
15     str = str + "                 from the parameters list. This key is default.\n"
16     str = str + "\n"
17     str = str + " -b              Runs the Installation Wizard in the batch mode.\n"
18     str = str + "                 All the found parameters are taken in to account.\n"
19     str = str + "\n"
20     str = str + " -f <xml-file>   The configuration file to be parsed by the Installation Wizard.\n" 
21     str = str + "                 If this parameter is missed then the script tries to define\n"
22     str = str + "                 the Red Hat version and use the corresponding xml. For example,\n"
23     str = str + "                 for Red Hat 8.0 config_RedHat8_0.xml file is supposed to be used\n"
24     str = str + "                 by default. If the appropriate xml file is not found, the config.xml\n"
25     str = str + "                 is used by default.\n"
26     str = str + "\n"
27     str = str + " -t <target-dir> The target directory the products to be installed to.\n"
28     str = str + "                 This parameter overloads the target directory described in the\n"
29     str = str + "                 configuration file.\n"
30     str = str + "\n"
31     str = str + " -tmp <tmp-dir>  The directory which should be used for the temporary files.\n"
32     str = str + "                 This parameter overloads the temporary directory described in the\n"
33     str = str + "                 configuration file.\n"
34     str = str + "\n"
35     str = str + " -h              Prints help information.\n" 
36     return str
37
38 #==============================================================
39 # error_exit
40 #==============================================================
41 def error_exit (str = ""):
42     import sys
43     if len(str): res = "\n" + str + "\n"
44     else : res = ""
45     print res + \
46           get_help_info() 
47     sys.exit(1);
48
49
50 #==============================================================
51 # Cheks whether the passed parameter is a key.
52 #==============================================================
53 def is_key ( val ):
54     import re
55     if val is not None : 
56         return re.match(r'^-[a-zA-Z]', val)
57     return 0
58
59 #==============================================================
60 # From the list of parameters extracts value following 'key' 
61 #==============================================================
62 def extract_parameter ( key, args ) :
63     import sys
64     length = len(args);
65     if ( length == 0 ) :  return None
66    
67     found = 0;
68
69     for i in range(0, length-1):
70         if  args[i] == key : 
71             if ( is_key ( args[i+1]) ) :
72                 print " No value after key ", key
73                 sys.exit(1);
74             
75             value = args[i+1]
76             if ( i < length - 2  and is_key ( args[i+2] ) == 0 ) : #control that only one value follows key 
77                                                               #(not a list). In this case params are correct.
78                 print "Too much values after key ", key
79                 sys.exit(1);
80             
81             found = 1; break;
82     
83     if (found) : 
84         return  value 
85     
86     return None
87
88
89 #===============================================================
90 # Extracts list of values following specified 'key' from 'args[]'
91 #===============================================================
92 def extract_list (key, args) : 
93
94     lenght = len(args)
95     if ( args is None or lenght == 0 ):
96         error_exit()
97
98     list=[]
99     found = 0
100
101     for i in  range(0, length) :
102         if args[i] == key  : 
103             if (is_key ( args[i+1]))  : 
104                  error_exit();
105             
106             for i in range (i+1, lenght):
107                 if is_key(args[i]) : break
108                 list.append(args[i])
109             found =1; break
110      
111     return list; #empty list is returned if no values after key
112
113
114 #==============================================================
115 # Method find the $key in the list and return 1 if success
116 # and 0 otherwise. 
117 #==============================================================
118 def find_key (key, argv) :
119
120     if (not is_key(key)) : return 0
121
122     for simbol in  argv :
123         if simbol == key:
124             return 1 
125     return 0
126
127 #==============================================================
128 # Parse the list of parameters
129 #==============================================================
130 def parse_parameters (args) :
131
132     if find_key('-h', args) :
133         print get_help_info();
134         import sys
135         sys.exit(0)
136         
137     xmlfile = extract_parameter("-f", args)
138     target_dir =  extract_parameter("-t", args)
139     tmp_dir = extract_parameter("-tmp", args)
140     if find_key('-b', args):
141         is_gui = 0
142     else : is_gui = 1
143     return [xmlfile, target_dir,  tmp_dir, is_gui]
144
145
146 #=================================================================
147 # The first algorithm to create the dependencies list by their level
148 #=================================================================
149 def get_next_level(list, products):
150     
151     import re
152     result = []
153     expr = "(" + list[0].name
154     for i in range(1, len(list)):
155         expr = expr + "|"+ list[i].name
156     
157     expr = expr + ")$"
158     #expr=re.compile(expr)
159
160     for product in products:
161         deps = re.sub(r'\s+', "", product.dependencies)
162         if re.search(expr,  deps):
163             result.append(product)
164
165     return result
166
167
168 def create_levels(prods):
169     import copy
170     
171     products = copy.deepcopy(prods)
172     
173     result = {}
174     import re
175     #1. find the products with empty lists of dependencies
176     list = []
177     for product in products:
178         if len(re.sub(r'\s', "", product.dependencies)) == 0 :
179             list.append(product)
180
181     if len(list) == 0 :
182         raise RuntimeError, "Products that depend on nothing are not found"
183
184     # remove the first level products from the common list of products
185     for product in list :
186         products.remove(product)
187
188     ind = 0; 
189     result[0] = list
190
191     while (len(products)) :
192         res = get_next_level(list, products)
193         if len(res) == 0 :
194             raise RuntimeError, "Empty list of products is found"
195
196         for product in res :
197             products.remove(product)
198
199         ind = ind +1
200         result[ind] =  res
201         list = res
202             
203     str = ""
204     for i in result.keys():
205         for product in  result[i]:
206             str = str + product.name + " "
207
208     return str;
209             
210 #=================================================================
211 # The second algorithm
212 #=================================================================
213 def get_dependencies_set(prods) :
214     import copy
215     import re
216
217     products = copy.deepcopy(prods)
218     deps = ""
219     list = []
220
221     while (len(products)) :
222
223         tmplist = []
224         #find the products with empty list of dependencies
225         for product in products:
226             product.dependencies = re.sub(r'\s+$', "", product.dependencies)
227             product.dependencies = re.sub(r'^\s+', "", product.dependencies)
228            
229             if len(product.dependencies) == 0 :
230                tmplist.append(product); 
231                deps = deps + " " + product.name
232
233         list.append(tmplist)
234
235         #remove the products names from other products dependencies
236         for item in tmplist:
237             products.remove(item)
238
239             regexpr1 = "((^|,(\s+)?)"+item.name+"$|^"+item.name+"(\s+)?,(\s+)?)"
240             regexpr2 = ",(\s+)?"+item.name+"(\s+)?,(\s+)?"
241
242             for product in products:
243                 product.dependencies = re.sub(r'\s+$', "", product.dependencies)
244                 product.dependencies = re.sub(r'^\s+', "", product.dependencies)
245
246                 product.dependencies = re.sub(regexpr1, "", product.dependencies)
247                 product.dependencies = re.sub(regexpr2, ",", product.dependencies)
248
249     return deps 
250     
251 #==============================================================
252 # Creates dir, returns the part of path that existed early.
253 # Access may be defined.
254 #==============================================================
255 def create_dir (directory, access = 0777):
256     import string, os
257     dirs = string.split(directory, "/")
258     existing = ""; dir = ""
259     root = ""
260     for item in dirs:
261         if len(item) == 0:  continue
262         dir = dir + "/"+item
263         if os.path.exists(dir):
264             existing = dir
265         else:
266             os.mkdir(dir, access )
267             #root= existing + "/"+item
268             if dir == existing + "/"+item :
269                 root = dir
270             #else : root = existing
271     
272     return root
273
274 #==============================================================
275 # class Product 
276 #==============================================================
277
278 class Product :
279     def __init__(self, theName,
280                  theVersion            = None,
281                  theInstall            = None,
282                  theSupportred         = None,
283                  theDisable            = None,
284                  theDependencies       = None,
285                  theInstalldiskspace   = None,
286                  theTemporarydiskspace = None,
287                  theScript             = None,
288                  thePickUpEnvironment  = None):
289         
290
291         self.name               = theName
292         self.version            = theVersion
293         self.install            = theInstall
294         self.supported          = theSupportred
295         self.disable            = theDisable
296         self.dependencies       = theDependencies
297         self.installdiskspace   = theInstalldiskspace
298         self.temporarydiskspace = theTemporarydiskspace
299         self.script             = theScript
300         self.pickupEnv          = thePickUpEnvironment
301
302 #===================================================================
303 # class Config
304 #===================================================================
305 class Config :
306     def __init__(self, theVersion='', theCaption='', theCopyright='', theLicense='', theOS=''):
307         self.version   = theVersion
308         self.caption   = theCaption
309         self.copyright = theCopyright
310         self.license   = theLicense
311         self.os        = theOS
312
313
314 #===================================================================
315 # class Path
316 #===================================================================
317 class Path :
318     def __init__(self, theTargetdir=".", theTmpdir="."):
319         self.targetdir = theTargetdir
320         self.tmpdir    = theTmpdir
321
322         
323 #===================================================================
324 # class ConfigParser
325 #===================================================================
326 class ConfigParser(xmllib.XMLParser):
327     def __init__(self):
328         xmllib.XMLParser.__init__(self)
329         self.products = []
330         self.currentdata = []
331         self.path = None
332         self.config = None
333         
334     def handle_data(self, data):
335         self.currentdata.append(data)
336         
337     def start_product(self, attrs):
338         aProduct = Product(attrs['name'],
339                            attrs['version'],
340                            attrs['install'],
341                            attrs['supported'],
342                            attrs['disable'],
343                            attrs['dependancies'],
344                            attrs['installdiskspace'],
345                            attrs['temporarydiskspace'],
346                            attrs['script'])
347
348         if attrs.has_key( 'pickupenv' ):
349             aProduct.pickupEnv = attrs['pickupenv']
350
351         self.products.append(aProduct)
352
353     def end_product(self):
354         pass
355
356     def start_config(self, attrs):
357         self.config = Config(attrs['version'],
358                              attrs['caption'],
359                              attrs['copyright'],
360                              attrs['license'],
361                              attrs['os'])
362     def end_config(self):
363         pass
364
365     def start_path (self, attrs):
366         self.path = Path(attrs['targetdir'],
367                          attrs['tempdir'])
368         print self.path.tmpdir
369         
370     def end_path(self):
371         pass
372
373     def getProduct(self, prod):
374         for product in self.products:
375             if product.name == prod:
376                 return product
377         return None
378
379 #================================================================
380 # get the path using file name
381 #================================================================
382 def get_current_path(file_name):
383     path = "."; where = string.rfind(file_name,'/');
384     if (where != -1):
385         path = (file_name)[: where]
386         os.chdir(path);
387     path = os.getcwd() + "/"
388     return path
389
390 #================================================================
391 # checks dir existing 
392 #================================================================
393 def check_dir(dir):
394     if (os.path.islink(dir)) :
395         native_dir = os.readlink(dir)
396         if not os.path.exists(native_dir) :
397             print "Bad link " + native_dir + " to directory " + native_dir + ". The last does not exist."
398             return 0 # problem
399     else :
400         if not os.path.exists(dir):
401             print "Directory " + dir + " does not exist"
402             return 0
403     return 1
404
405 #===============================================================
406 # Checks the disk space. Exit from interpreter if there is no
407 # enough disk space.
408 #===============================================================
409 def check_disk_space(products, script_dir, target_dir, tmp_dir):
410     import re, string, os
411     install_space = 0
412     temporary_space = 0
413     for product in products :
414         product.install = re.sub(r'^\s+', "", product.install)
415         product.install = re.sub(r'\s+$', "", product.install)
416         
417         if product.disable == "true" or product.install == "use native":
418             continue
419         spaces = string.split( product.installdiskspace,',')
420         prod_space = spaces[0]
421         if (len(spaces) == 2 ) and (product.install == "install binaries") :
422             prod_space = spaces[1]
423         install_space = install_space + string.atoi(prod_space)
424         temporary_space = temporary_space + string.atoi(product.temporarydiskspace)
425     res = os.system(scripts_dir + "checkSize.sh" + " " + target_dir + " " + str(install_space))
426     if res:
427         print "There is no enough space to install the products."
428         return 0
429     
430     res = os.system(scripts_dir + "checkSize.sh" + " " + tmp_dir + " " + str(temporary_space))
431     if res:
432         print "There is no enough space for tmp directory."
433         return 0
434     
435     return 1
436  
437
438 #================================================================
439 # main
440 #================================================================
441     
442 if __name__ == "__main__":
443     
444     cur_dir = get_current_path(sys.argv[0])
445    
446     [xml_file, target_dir, tmp_dir, is_gui]  = parse_parameters(sys.argv)
447
448     # define xml file -----------------
449     if (xml_file is None) :
450         xml_file_name = "config.xml"
451         if os.path.exists("/proc/version"):
452             data = open("/proc/version").readline()
453             res = re.search(r'Red\s+Hat\s+\w+(\s+)?(\d[.]\d)', data)
454             if res is not None:
455                 num = re.sub("[.]", "_", (res.groups())[1])
456                 filename = "config_RedHat" + num+ ".xml"
457                 if (os.path.exists(cur_dir + filename)):
458                     xml_file_name = filename
459                 
460         xml_file = cur_dir +  xml_file_name
461     if xml_file is None or not os.path.exists(xml_file):
462         error_exit("No xml file is found try to run with options -f <xmlfile>")
463
464     if not os.access(xml_file, os.R_OK):
465         print "There is no acceess to read "+ xml_file
466         sys.exit(1)
467
468     #---- GUI ----------------
469     if is_gui : 
470         env = os.environ
471         if not env.has_key("PATH") :
472             env["PATH"] = ""
473         if not env.has_key("LD_LIBRARY_PATH") :
474             env["LD_LIBRARY_PATH"]=  ""
475
476         env["LD_LIBRARY_PATH"] =  ".:" +  env["LD_LIBRARY_PATH"]
477         env["PATH"] = ".:"+ env["PATH"]
478
479         sys.exit(os.system("cd " + cur_dir + "; ./bin/SALOME_InstallWizard " + xml_file +"&"))
480         
481         
482
483     #-----  TUI ---------------------
484
485     #print xml_file, target_dir, tmp_dir, is_gui
486     
487     filehandle = open(xml_file)
488     data = filehandle.read()
489     filehandle.close()
490     parser = ConfigParser()
491     parser.feed(data)
492     parser.close()
493
494     # definitions :
495     # map
496     what_to_do = { "install sources":"install_source",
497                    "install binaries":"install_binary",
498                    "use native":"try_native"}
499     # define tmp dir  -----------
500     if tmp_dir is None:
501         tmp_dir = parser.path.tmpdir
502     root_path = ""
503     if not os.path.exists(tmp_dir):
504         print "Creating " + tmp_dir; root_path = create_dir(tmp_dir) ; 
505         import random
506         tmp_dir = tmp_dir + "/INSTALLWORK" + str(random.randint(10000,100000))
507         create_dir(tmp_dir,0755)
508         if not os.path.exists(tmp_dir):
509             error_exit("There is no tmp directory " + tmp_dir + ". Use -tmp key to set directory or correct xml file\n\n")
510
511     if not os.access(tmp_dir, os.W_OK) :
512         str = "We have no write permissions for directory " + tmp_dir + ". Use -tmp key to set directory or correct xml file"
513         error_exit(str)
514         
515     # define target dir  --------
516     if target_dir is None:
517         target_dir = parser.path.targetdir
518
519     if not os.path.exists(target_dir):
520         print "Creating " + target_dir; create_dir(target_dir, 0755)
521         if not os.path.exists(target_dir):
522             error_exit("There is no target directory " + target_dir + ". Use -t key to set directory or correct xml file\n\n")
523
524     if not os.access(target_dir, os.W_OK) :
525         str = "We have no write permissions for directory " + target_dir + ". Use -t key to set directory or correct xml file"
526         error_exit(str)
527     
528
529     # define products dir ------------
530     source_dir =  cur_dir + "Products" ; 
531     if not check_dir(source_dir):
532         if len(root_path) and os.path.exists(root_path):
533             os.system("rm -r -f "+ root_path)
534         sys.exit(1)
535        
536     subdir = {"install sources"  : "SOURCES",
537               "install binaries" : "BINARIES/"+parser.config.os,
538               "use native": ""}
539
540
541     #  define scripts dir ------------
542     scripts_dir = cur_dir + "config_files/"
543     if not check_dir(scripts_dir):
544         if len(root_path) and os.path.exists(root_path):
545             os.system("rm -r -f "+ root_path)
546         sys.exit(1)
547     os.chdir(scripts_dir)
548
549     list_of_dep =  create_levels(parser.products)
550     #list_of_dep =  get_dependencies_set(parser.products)
551
552     if check_disk_space(parser.products, scripts_dir, target_dir, tmp_dir) :
553
554         # install products
555         for product in parser.products :
556
557             if product.disable == "true": continue
558
559             cmd = scripts_dir +  product.script + " " + \
560                   what_to_do[product.install]+ " " + \
561                   tmp_dir + " " + \
562                   source_dir + "/" + subdir[product.install] + " " + \
563                   target_dir + " " + \
564                   '"' + list_of_dep + '"' + " " + \
565                   product.name
566
567             res = os.system(cmd)
568             if res : break;
569
570         # pickup environment
571         for product in parser.products :
572
573             if product.disable == "true": continue
574
575             if product.pickupEnv == "true":
576                 cmd = scripts_dir +  product.script + " " + \
577                       "pickup_env " + \
578                       tmp_dir + " " + \
579                       source_dir + "/" + subdir[product.install] + " " + \
580                       target_dir + " " + \
581                       '"' + list_of_dep + '"' + " " + \
582                       product.name
583                 
584                 res = os.system(cmd)
585
586     if len(root_path) and os.path.exists(root_path):
587         os.system("rm -r -f "+ root_path)