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