Attachment 'fslinstaller.py'
Download 1 #!/usr/bin/python
2
3 from __future__ import print_function
4
5 import collections
6 import csv
7 import errno
8 import getpass
9 import itertools
10 import json
11 import locale
12 import os
13 import platform
14 import threading
15 import time
16 import shlex
17 import socket
18 import sys
19 import readline
20 import tempfile
21 import re
22 import fileinput
23
24 # py3
25 try:
26 import urllib.request as urlrequest
27 import urllib.error as urlerror
28
29 # py2
30 except ImportError:
31 import urllib2 as urlrequest
32 import urllib2 as urlerror
33
34 from optparse import OptionParser, OptionGroup, SUPPRESS_HELP
35 from re import compile, escape, sub
36 from subprocess import Popen, call, PIPE, STDOUT
37
38 try:
39 from subprocess import DEVNULL # py3
40 except ImportError:
41 DEVNULL = open(os.devnull, 'wb')
42
43 locale.setlocale(locale.LC_ALL, '')
44 code = locale.getpreferredencoding()
45
46 PYVER = sys.version_info[:2]
47
48
49 fsli_C_FAILED = 1
50 fsli_C_OK = 2
51 fsli_C_SKIP = 4
52 fsli_C_WARN = 3
53 CURRENT = 0
54 UPDATE = 1
55 UPGRADE = 2
56 BOURNE_SHELLS = ('sh', 'bash', 'zsh', 'ksh', 'dash', )
57 C_SHELLS = ('csh', 'tcsh', )
58
59
60 class Version(object):
61 def __init__(self, version_string):
62 if ':' in version_string:
63 version_string = version_string.split(':')[0]
64 v_vals = version_string.split('.')
65
66 for v in v_vals:
67 if not v.isdigit():
68 raise ValueError('Bad version string')
69 self.major = int(v_vals[0])
70 try:
71 self.minor = int(v_vals[1])
72 except IndexError:
73 self.minor = 0
74 try:
75 self.patch = int(v_vals[2])
76 except IndexError:
77 self.patch = 0
78 try:
79 self.hotfix = int(v_vals[3])
80 except IndexError:
81 self.hotfix = 0
82
83 def __repr__(self):
84 return "Version(%s,%s,%s,%s)" % (
85 self.major,
86 self.minor,
87 self.patch,
88 self.hotfix)
89
90 def __str__(self):
91 if self.hotfix == 0:
92 return "%s.%s.%s" % (self.major, self.minor, self.patch)
93 else:
94 return "%s.%s.%s.%s" % (
95 self.major,
96 self.minor,
97 self.patch,
98 self.hotfix)
99
100 def __ge__(self, other):
101 if not isinstance(other, Version):
102 return NotImplemented
103 if self > other or self == other:
104 return True
105 return False
106
107 def __le__(self, other):
108 if not isinstance(other, Version):
109 return NotImplemented
110 if self < other or self == other:
111 return True
112 return False
113
114 def __cmp__(self, other):
115 if not isinstance(other, Version):
116 return NotImplemented
117 if self.__lt__(other):
118 return -1
119 if self.__gt__(other):
120 return 1
121 return 0
122
123 def __lt__(self, other):
124 if not isinstance(other, Version):
125 return NotImplemented
126 if self.major < other.major:
127 return True
128 if self.major > other.major:
129 return False
130 if self.minor < other.minor:
131 return True
132 if self.minor > other.minor:
133 return False
134 if self.patch < other.patch:
135 return True
136 if self.patch > other.patch:
137 return False
138 if self.hotfix < other.hotfix:
139 return True
140 if self.hotfix > other.hotfix:
141 return False
142 # major, minor and patch all match so this is not less than
143 return False
144
145 def __gt__(self, other):
146 if not isinstance(other, Version):
147 return NotImplemented
148 if self.major > other.major:
149 return True
150 if self.major < other.major:
151 return False
152 if self.minor > other.minor:
153 return True
154 if self.minor < other.minor:
155 return False
156 if self.patch > other.patch:
157 return True
158 if self.patch < other.patch:
159 return False
160 if self.hotfix > other.hotfix:
161 return True
162 if self.hotfix < other.hotfix:
163 return False
164 # major, minor and patch all match so this is not less than
165 return False
166
167 def __eq__(self, other):
168 if not isinstance(other, Version):
169 return NotImplemented
170 if (
171 self.major == other.major and
172 self.minor == other.minor and
173 self.patch == other.patch and
174 self.hotfix == other.hotfix):
175 return True
176 return False
177
178 def __ne__(self, other):
179 if not isinstance(other, Version):
180 return NotImplemented
181 if self.__eq__(other):
182 return False
183 return True
184
185
186 version = Version('3.2.3')
187
188
189 def memoize(f):
190 cache = f.cache = {}
191
192 def g(*args, **kwargs):
193 key = (f, tuple(args), frozenset(list(kwargs.items())))
194 if key not in cache:
195 cache[key] = f(*args, **kwargs)
196 return cache[key]
197 return g
198
199
200 class InstallError(Exception):
201 pass
202
203
204 class shell_colours(object):
205 default = '\033[0m'
206 rfg_kbg = '\033[91m'
207 gfg_kbg = '\033[92m'
208 yfg_kbg = '\033[93m'
209 mfg_kbg = '\033[95m'
210 yfg_bbg = '\033[104;93m'
211 bfg_kbg = '\033[34m'
212 bold = '\033[1m'
213
214
215 class MsgUser(object):
216 __debug = False
217 __quiet = False
218
219 @classmethod
220 def debugOn(cls):
221 cls.__debug = True
222
223 @classmethod
224 def debugOff(cls):
225 cls.__debug = False
226
227 @classmethod
228 def quietOn(cls):
229 cls.__quiet = True
230
231 @classmethod
232 def quietOff(cls):
233 cls.__quiet = False
234
235 @classmethod
236 def isquiet(cls):
237 return cls.__quiet
238
239 @classmethod
240 def isdebug(cls):
241 return cls.__debug
242
243 @classmethod
244 def debug(cls, message, newline=True):
245 if cls.__debug:
246 mess = str(message)
247 if newline:
248 mess += "\n"
249 sys.stderr.write(mess)
250
251 @classmethod
252 def message(cls, msg):
253 if cls.__quiet:
254 return
255 print(msg)
256
257 @classmethod
258 def question(cls, msg):
259 print(msg, end=' ')
260
261 @classmethod
262 def skipped(cls, msg):
263 if cls.__quiet:
264 return
265 print("".join(
266 (shell_colours.mfg_kbg, "[Skipped] ", shell_colours.default, msg)))
267
268 @classmethod
269 def ok(cls, msg):
270 if cls.__quiet:
271 return
272 print("".join(
273 (shell_colours.gfg_kbg, "[OK] ", shell_colours.default, msg)))
274
275 @classmethod
276 def failed(cls, msg):
277 print("".join(
278 (shell_colours.rfg_kbg, "[FAILED] ", shell_colours.default, msg)))
279
280 @classmethod
281 def warning(cls, msg):
282 if cls.__quiet:
283 return
284 print("".join(
285 (shell_colours.bfg_kbg,
286 shell_colours.bold,
287 "[Warning]",
288 shell_colours.default, " ", msg)))
289
290
291 class Progress_bar(object):
292 def __init__(self, x=0, y=0, mx=1, numeric=False, percentage=False):
293 self.x = x
294 self.y = y
295 self.width = 50
296 self.current = 0
297 self.max = mx
298 self.numeric = numeric
299 self.percentage = percentage
300
301 def update(self, reading):
302 if MsgUser.isquiet():
303 return
304 percent = int(round(reading * 100.0 / self.max))
305 cr = '\r'
306 if not self.numeric and not self.percentage:
307 bar = '#' * int(percent)
308 elif self.numeric:
309 bar = "/".join(
310 (str(reading),
311 str(self.max))) + ' - ' + str(percent) + "%\033[K"
312 elif self.percentage:
313 bar = "%s%%" % (percent)
314 sys.stdout.write(cr)
315 sys.stdout.write(bar)
316 sys.stdout.flush()
317 self.current = percent
318 if percent == 100:
319 sys.stdout.write(cr)
320 if not self.numeric and not self.percentage:
321 sys.stdout.write(" " * int(percent))
322 sys.stdout.write(cr)
323 sys.stdout.flush()
324 elif self.numeric:
325 sys.stdout.write(" " * (len(str(self.max))*2 + 8))
326 sys.stdout.write(cr)
327 sys.stdout.flush()
328 elif self.percentage:
329 sys.stdout.write("100%")
330 sys.stdout.write(cr)
331 sys.stdout.flush()
332
333
334 def temp_file_name(mode='r', close=False):
335 '''Return a name for a temporary file - uses mkstemp to create the file and
336 returns a tuple (file object, file name).
337 Opens as read-only unless mode specifies otherwise. If close is set to True
338 will close the file before returning.
339 The file object is a fdopen file object so lacks a useable file name.'''
340 (tmpfile, fname) = tempfile.mkstemp()
341 file_obj = os.fdopen(tmpfile, mode)
342
343 if close:
344 file_obj.close()
345 return (file_obj, fname)
346
347
348 class RunCommandError(Exception):
349 pass
350
351
352 class Spinner(object):
353 spinner = itertools.cycle(('-', '\\', '|', '/', ))
354 busy = False
355 delay = 0.2
356
357 def __init__(self, delay=None, quiet=False):
358 if delay:
359 try:
360 self.delay = float(delay)
361 except ValueError:
362 pass
363 self.quiet = quiet
364
365 def spin_it(self):
366 while self.busy:
367 sys.stdout.write(next(self.spinner))
368 sys.stdout.flush()
369 time.sleep(self.delay)
370 sys.stdout.write('\b')
371 sys.stdout.flush()
372
373 def start(self):
374 if not self.quiet:
375 self.busy = True
376 threading.Thread(target=self.spin_it).start()
377
378 def stop(self):
379 self.busy = False
380 time.sleep(self.delay)
381
382
383 def run_cmd_dropstdout(command, as_root=False):
384 '''Run the command and return result.'''
385 command_line = shlex.split(command)
386
387 if as_root and os.getuid() != 0:
388 try:
389 sudo_pwd = get_sudo_pwd()
390 except SudoPasswordError:
391 raise RunCommandError(
392 "Unable to get valid administrator's password")
393 command_line.insert(0, '-S')
394 command_line.insert(0, 'sudo')
395 else:
396 sudo_pwd = ''
397 try:
398 my_spinner = Spinner(quiet=MsgUser.isquiet())
399 my_spinner.start()
400 cmd = Popen(command_line, stdin=PIPE, stdout=None, stderr=PIPE,
401 universal_newlines=True)
402 if sudo_pwd:
403 cmd.stdin.write(sudo_pwd + '\n')
404 cmd.stdin.flush()
405 (_, error) = cmd.communicate()
406 except Exception:
407 raise
408 finally:
409 my_spinner.stop()
410 if cmd.returncode:
411 MsgUser.debug("An error occured (%s, %s)" % (cmd.returncode, error))
412 raise RunCommandError(error)
413
414
415 def run_cmd(command, as_root=False):
416 '''Run the command and return result.'''
417 command_line = shlex.split(command)
418
419 if as_root and os.getuid() != 0:
420 try:
421 sudo_pwd = get_sudo_pwd()
422 except SudoPasswordError:
423 raise RunCommandError(
424 "Unable to get valid administrator's password")
425 command_line.insert(0, '-S')
426 command_line.insert(0, 'sudo')
427 else:
428 sudo_pwd = ''
429 MsgUser.debug("Will call %s" % (command_line))
430 try:
431 my_spinner = Spinner(quiet=MsgUser.isquiet())
432 my_spinner.start()
433 cmd = Popen(command_line, stdin=PIPE, stdout=PIPE, stderr=PIPE,
434 universal_newlines=True)
435 if sudo_pwd:
436 cmd.stdin.write(sudo_pwd + '\n')
437 cmd.stdin.flush()
438 (output, error) = cmd.communicate()
439 except Exception:
440 raise
441 finally:
442 my_spinner.stop()
443 if cmd.returncode:
444 MsgUser.debug("An error occured (%s, %s)" % (cmd.returncode, error))
445 raise RunCommandError(error)
446 MsgUser.debug("Command completed successfully (%s)" % (output))
447 return output
448
449
450 def run_cmd_displayoutput(command, as_root=False):
451 '''Run the command and display output.'''
452 command_line = shlex.split(command)
453
454 if as_root and os.getuid() != 0:
455 try:
456 sudo_pwd = get_sudo_pwd()
457 except SudoPasswordError:
458 raise RunCommandError(
459 "Unable to get valid administrator's password")
460
461 command_line.insert(0, '-S')
462 command_line.insert(0, 'sudo')
463 MsgUser.debug("Will call %s" % (command_line))
464 cmd = Popen(
465 command_line,
466 stdin=PIPE, stdout=sys.stdout, stderr=sys.stderr,
467 universal_newlines=True)
468 if sudo_pwd:
469 cmd.stdin.write(sudo_pwd + '\n')
470 cmd.stdin.flush()
471 cmd.communicate()
472 return_code = cmd.returncode
473 else:
474 return_code = call(command_line)
475
476 if return_code:
477 MsgUser.debug("An error occured (%s)" % (return_code))
478 raise RunCommandError(return_code)
479 MsgUser.debug("Command completed successfully")
480
481
482 def check_sudo(sudo_pwd):
483 command_line = ['sudo', '-S', 'true']
484 MsgUser.debug("Checking sudo password")
485 cmd = Popen(
486 command_line,
487 stdin=PIPE,
488 stdout=DEVNULL,
489 stderr=DEVNULL,
490 universal_newlines=True
491 )
492 cmd.stdin.write(sudo_pwd + '\n')
493 cmd.stdin.flush()
494 cmd.communicate()
495
496 if cmd.returncode != 0:
497 return False
498 else:
499 return True
500
501
502 class SudoPasswordError(Exception):
503 pass
504
505
506 @memoize
507 def get_sudo_pwd():
508 '''Get the sudo password from the user'''
509 MsgUser.message("We require your password to continue...")
510 attempts = 0
511 valid = False
512
513 while attempts < 3 and not valid:
514 sudo_pwd = getpass.getpass('password: ')
515 valid = check_sudo(sudo_pwd)
516 if not valid:
517 MsgUser.failed("Incorrect password")
518 attempts += 1
519 if not valid:
520 raise SudoPasswordError()
521 return sudo_pwd
522
523
524 class DeletionRefused(Exception):
525 pass
526
527
528 class SafeDeleteError(Exception):
529 pass
530
531
532 def safe_delete(fs_object, as_root=False):
533 '''Delete file/folder, becoming root if necessary.
534 Run some sanity checks on object'''
535
536 banned_items = ['/', '/usr', '/usr/bin', '/usr/local', '/bin',
537 '/sbin', '/opt', '/Library', '/System', '/System/Library',
538 '/var', '/tmp', '/var/tmp', '/lib', '/lib64', '/Users',
539 '/home', '/Applications', '/private', '/etc', '/dev',
540 '/Network', '/net', '/proc']
541 if os.path.isdir(fs_object):
542 del_opts = "-rf"
543 else:
544 del_opts = '-f'
545
546 if fs_object in banned_items:
547 raise DeletionRefused('Will not delete %s!' % (fs_object))
548
549 command_line = " ".join(('rm', del_opts, fs_object))
550 try:
551 result = run_cmd(command_line, as_root)
552 except RunCommandError as e:
553 raise SafeDeleteError(str(e))
554 return result
555
556
557 class MoveError(Exception):
558 pass
559
560
561 def move(source, target, as_root):
562 try:
563 run_cmd_dropstdout(" ".join(('mv', source, target)), as_root)
564 except RunCommandError as e:
565 raise MoveError(str(e))
566
567
568 class IsDirectoryError(Exception):
569 pass
570
571
572 class CopyFileError(Exception):
573 pass
574
575
576 def copy_file(fname, destination, as_root):
577 '''Copy a file using sudo if necessary'''
578 MsgUser.debug("Copying %s to %s (as root? %s)" % (
579 fname, destination, as_root))
580 if os.path.isdir(fname):
581 raise IsDirectoryError('Source (%s) is a directory!' % (fname))
582
583 if os.path.isdir(destination):
584 # Ensure that copying into a folder we have a terminating slash
585 destination = destination.rstrip('/') + "/"
586 copy_opts = '-p'
587 fname = '"%s"' % fname
588 destination = '"%s"' % destination
589 command_line = " ".join(('cp', copy_opts, fname, destination))
590 try:
591 result = run_cmd(command_line, as_root)
592 except RunCommandError as e:
593 raise CopyFileError(str(e))
594 return result
595
596
597 def file_contains(fname, search_for):
598 '''Equivalent of grep'''
599 regex = compile(escape(search_for))
600 found = False
601 MsgUser.debug("In file_contains.")
602 MsgUser.debug("Looking for %s in %s." % (search_for, fname))
603
604 f = open(fname, 'r')
605 for l in f:
606 if regex.search(l):
607 found = True
608 break
609 f.close()
610
611 return found
612
613
614 def file_contains_1stline(fname, search_for):
615 '''Equivalent of grep - returns first occurrence'''
616 regex = compile(escape(search_for))
617 found = ''
618 MsgUser.debug("In file_contains_1stline.")
619 MsgUser.debug("Looking for %s in %s." % (search_for, fname))
620 f = open(fname, 'r')
621 for l in f:
622 if regex.search(l):
623 found = l
624 break
625 f.close()
626
627 return found
628
629
630 def line_string_replace(line, search_for, replace_with):
631 return sub(escape(search_for), escape(replace_with), line)
632
633
634 def line_starts_replace(line, search_for, replace_with):
635 if line.startswith(search_for):
636 return replace_with + '\n'
637 return line
638
639
640 class MoveFileError(Exception):
641 pass
642
643
644 def move_file(from_file, to_file, requires_root=False):
645 '''Move a file, using /bin/cp via sudo if requested.
646 Will work around known bugs in python.'''
647
648 if requires_root:
649 try:
650 run_cmd_dropstdout(" ".join(
651 ("/bin/cp", from_file, to_file)), as_root=True)
652 except RunCommandError as e:
653 MsgUser.debug(e)
654 raise MoveFileError("Failed to move %s (%s)" % (from_file, str(e)))
655 os.remove(from_file)
656 else:
657 try:
658 move(from_file, to_file, requires_root)
659 except OSError as e:
660 # Handle bug in some python versions on OS X writing to NFS home
661 # folders, Python tries to preserve file flags but NFS can't do
662 # this. It fails to catch this error and ends up leaving the file
663 # in the original and new locations!
664 if e.errno == 45:
665 # Check if new file has been created:
666 if os.path.isfile(to_file):
667 # Check if original exists
668 if os.path.isfile(from_file):
669 # Destroy original and continue
670 os.remove(from_file)
671 else:
672 try:
673 run_cmd_dropstdout("/bin/cp %s %s" % (
674 from_file, to_file), as_root=False)
675 except RunCommandError as e:
676 MsgUser.debug(e)
677 raise MoveFileError("Failed to copy from %s (%s)" % (
678 from_file, str(e)))
679 os.remove(from_file)
680 else:
681 raise
682 except Exception:
683 raise
684
685
686 class EditFileError(Exception):
687 pass
688
689
690 def edit_file(fname, edit_function, search_for, replace_with, requires_root):
691 '''Search for a simple string in the file given and replace
692 it with the new text'''
693 try:
694 (tmpfile, tmpfname) = temp_file_name(mode='w')
695 src = open(fname)
696
697 for line in src:
698 line = edit_function(line, search_for, replace_with)
699 tmpfile.write(line)
700 src.close()
701 tmpfile.close()
702
703 try:
704 move_file(tmpfname, fname, requires_root)
705 except MoveFileError as e:
706 MsgUser.debug(e)
707 os.remove(tmpfname)
708 raise EditFileError("Failed to edit %s (%s)" % (fname, str(e)))
709 except IOError as e:
710 MsgUser.debug(e.strerror)
711 raise EditFileError("Failed to edit %s (%s)" % (fname, str(e)))
712 MsgUser.debug("Modified %s (search %s; replace %s)." % (
713 fname, search_for, replace_with))
714
715
716 class AddToFileError(Exception):
717 pass
718
719
720 def add_to_file(fname, add_lines, requires_root):
721 '''Add lines to end of a file'''
722 if isinstance(add_lines, str):
723 add_lines = add_lines.split('\n')
724 try:
725 (tmpfile, tmpfname) = temp_file_name(mode='w')
726 src = open(fname)
727
728 for line in src:
729 tmpfile.write(line)
730 src.close()
731 tmpfile.write('\n')
732 for line in add_lines:
733 tmpfile.write(line)
734 tmpfile.write('\n')
735 tmpfile.close()
736 try:
737 move_file(tmpfname, fname, requires_root)
738
739 except MoveFileError as e:
740 os.remove(tmpfname)
741 MsgUser.debug(e)
742 raise AddToFileError("Failed to add to file %s (%s)" % (
743 fname, str(e)))
744 except IOError as e:
745 MsgUser.debug(e.strerror + tmpfname + fname)
746 raise AddToFileError("Failed to add to file %s" % (fname))
747 MsgUser.debug("Modified %s (added %s)" % (fname, '\n'.join(add_lines)))
748
749
750 class CreateFileError(Exception):
751 pass
752
753
754 def create_file(fname, lines, requires_root):
755 '''Create a new file containing lines given'''
756 if isinstance(lines, str):
757 lines = lines.split('\n')
758 try:
759 (tmpfile, tmpfname) = temp_file_name(mode='w')
760
761 for line in lines:
762 tmpfile.write(line)
763 tmpfile.write('\n')
764 tmpfile.close()
765 try:
766 move_file(tmpfname, fname, requires_root)
767 except CreateFileError as e:
768 os.remove(tmpfname)
769 MsgUser.debug(e)
770 raise CreateFileError("Failed to edit %s (%s)" % (fname, str(e)))
771 except IOError as e:
772 MsgUser.debug(e.strerror)
773 raise CreateFileError("Failed to create %s" % (fname))
774 MsgUser.debug("Created %s (added %s)" % (fname, '\n'.join(lines)))
775
776
777 class UnsupportedOs(Exception):
778 pass
779
780
781 class Host(object):
782 '''Work out which platform we are running on'''
783 o_s = platform.system().lower()
784 arch = platform.machine()
785 applever = ''
786 os_type = os.name
787 supported = True
788
789 if o_s == 'darwin':
790 vendor = 'apple'
791 version = Version(platform.release())
792 (applever, _, _) = platform.mac_ver()
793 glibc = ''
794 elif o_s == 'linux':
795 # default to this if we can't detect the linux distro
796 fallback_vendor = 'centos'
797 fallback_version = '7.8.2003'
798
799 # python 2.6-3.7 has a linux_distribution function
800 if hasattr(platform, 'linux_distribution'):
801 (vendor, version, _) = platform.linux_distribution(
802 full_distribution_name=0)
803 # linux_distributiobn is not present in python >=3.8
804 else:
805 vendor, version = fallback_vendor, fallback_version
806 try:
807 vendor = vendor.lower()
808 version = Version(version)
809 except ValueError:
810 vendor = fallback_vendor
811 version = Version(fallback_version)
812 glibc = platform.libc_ver()[1]
813 else:
814 supported = False
815
816 if arch == 'x86_64':
817 bits = '64'
818 elif arch == 'i686':
819 bits = '32'
820 elif arch == 'Power Macintosh':
821 bits = ''
822
823
824 def is_writeable(location):
825 '''Check if we can write to the location given'''
826 writeable = True
827 try:
828 tfile = tempfile.NamedTemporaryFile(mode='w+b', dir=location)
829 tfile.close()
830 except OSError as e:
831 if e.errno == errno.EACCES or e.errno == errno.EPERM:
832 writeable = False
833 else:
834 raise
835 return writeable
836
837
838 def is_writeable_as_root(location):
839 '''Check if sudo can write to a given location'''
840 # This requires us to use sudo
841
842 (f, fname) = temp_file_name(mode='w')
843 f.write("FSL")
844 f.close()
845
846 result = False
847 tmptarget = '/'.join((location, os.path.basename(fname)))
848 MsgUser.debug(" ".join(('/bin/cp', fname, tmptarget)))
849 try:
850 run_cmd_dropstdout(" ".join(('/bin/cp',
851 fname, tmptarget)), as_root=True)
852 result = True
853 os.remove(fname)
854 run_cmd_dropstdout(" ".join(('/bin/rm',
855 '-f', tmptarget)), as_root=True)
856 except RunCommandError as e:
857 MsgUser.debug(e)
858 os.remove(fname)
859 result = False
860 MsgUser.debug("Writeable as root? %s" % (result))
861 return result
862
863
864 class ChecksumCalcError(Exception):
865 pass
866
867
868 def sha256File(filename, bs=1048576):
869 '''Returns the sha256 sum of the given file.'''
870 MsgUser.message("Checking FSL package")
871 try:
872 import hashlib
873 f = open(filename, 'rb')
874 pb = Progress_bar(mx=os.path.getsize(filename), percentage=True)
875 pb.position = 0
876 fhash = hashlib.sha256()
877 data = f.read(bs)
878 while len(data) == bs:
879 fhash.update(data)
880 data = f.read(bs)
881 pb.position += len(data)
882 pb.update(pb.position)
883 fhash.update(data)
884 f.close()
885 return fhash.hexdigest()
886 except ImportError:
887 # No SHA256 support on python pre-2.5 so call the OS to do it.
888 try:
889 result = run_cmd(" ".join(('sha256sum', '-b', filename)))
890 return parsesha256sumfile(result)
891 except RunCommandError as e:
892 MsgUser.debug("SHA256 calculation error %s" % (str(e)))
893 raise ChecksumCalcError
894
895
896 def parsesha256sumfile(sha256string):
897 '''Returns sha256 sum extracted from the output of sha256sum or shasum -a
898 256 from OS X/Linux platforms'''
899 (sha256, _) = sha256string.split("*")
900 return sha256.strip()
901
902
903 def md5File(filename, bs=1048576):
904 '''Returns the MD5 sum of the given file.'''
905 MsgUser.message("Checking FSL package")
906 try:
907 import hashlib
908 fhash = hashlib.md5()
909 except ImportError:
910 import md5
911 fhash = md5.new()
912 f = open(filename, 'rb')
913 pb = Progress_bar(mx=os.path.getsize(filename), percentage=True)
914 pb.position = 0
915 data = f.read(bs)
916 while len(data) == bs:
917 fhash.update(data)
918 data = f.read(bs)
919 pb.position += len(data)
920 pb.update(pb.position)
921 fhash.update(data)
922 f.close()
923 return fhash.hexdigest()
924
925
926 def file_checksum(filename, chktype='sha256'):
927 if chktype == 'sha256':
928 return sha256File(filename)
929 if chktype == 'md5':
930 return md5File(filename)
931 else:
932 raise ChecksumCalcError('Unrecognised checksum type')
933
934
935 class OpenUrlError(Exception):
936 pass
937
938
939 def open_url(url, start=0, timeout=20):
940 socket.setdefaulttimeout(timeout)
941 MsgUser.debug("Attempting to download %s." % (url))
942
943 try:
944 req = urlrequest.Request(url)
945 if start != 0:
946 req.headers['Range'] = 'bytes=%s-' % (start)
947 rf = urlrequest.urlopen(req)
948 except urlerror.HTTPError as e:
949 MsgUser.debug("%s %s" % (url, e.msg))
950 raise OpenUrlError("Cannot find file %s on server (%s). "
951 "Try again later." % (url, e.msg))
952 except urlerror.URLError as e:
953 if type(e.reason) != str:
954 errno = e.reason.args[0]
955 if len(e.reason.args) > 1:
956 message = e.reason.args[1]
957 # give up on trying to identify both the errno and message
958 else:
959 message = e.reason.args
960 if errno == 8:
961 # Bad host name
962 MsgUser.debug("%s %s" % (url,
963 "Unable to find FSL download "
964 "server in the DNS"))
965 else:
966 # Other error
967 MsgUser.debug("%s %s" % (url, message))
968 else:
969 message = str(e.reason)
970 raise OpenUrlError(
971 "Cannot find %s (%s). Try again later." % (url, message))
972 except socket.timeout as e:
973 MsgUser.debug(e)
974 raise OpenUrlError("Failed to contact FSL web site. Try again later.")
975 return rf
976
977
978 class DownloadFileError(Exception):
979 pass
980
981
982 def download_file(url, localf, timeout=20):
983 '''Get a file from the url given storing it in the local file specified'''
984
985 try:
986 rf = open_url(url, 0, timeout)
987 except OpenUrlError as e:
988 raise DownloadFileError(str(e))
989
990 metadata = rf.headers
991 rf_size = int(metadata.get("Content-Length"))
992
993 dl_size = 0
994 block = 16384
995 x = 0
996 y = 0
997 pb = Progress_bar(x, y, rf_size, numeric=True)
998
999 for attempt in range(1, 6):
1000 # Attempt download 5 times before giving up
1001 pause = timeout
1002 try:
1003 try:
1004 lf = open(localf, 'ab')
1005 except Exception:
1006 raise DownloadFileError("Failed to create temporary file.")
1007
1008 while True:
1009 buf = rf.read(block)
1010 if not buf:
1011 break
1012 dl_size += len(buf)
1013 lf.write(buf)
1014 pb.update(dl_size)
1015 lf.close()
1016 except (IOError, socket.timeout) as e:
1017 MsgUser.debug(e.strerror)
1018 MsgUser.message("\nDownload failed re-trying (%s)..." % attempt)
1019 pause = 0
1020 if dl_size != rf_size:
1021 time.sleep(pause)
1022 MsgUser.message("\nDownload failed re-trying (%s)..." % attempt)
1023 try:
1024 rf = open_url(url, dl_size, timeout)
1025 except OpenUrlError as e:
1026 MsgUser.debug(e)
1027 else:
1028 break
1029 if dl_size != rf_size:
1030 raise DownloadFileError("Failed to download file.")
1031
1032
1033 def build_url_with_protocol(protocol, base, parts):
1034 part_l = [protocol + '://' + base.strip('/')]
1035 part_l.extend([x.strip('/') for x in parts])
1036 return '/'.join(part_l)
1037
1038
1039 def build_url(parts):
1040 part_l = [parts[0].strip('/')]
1041 part_l.extend([x.strip('/') for x in parts[1:]])
1042 return '/'.join(part_l)
1043
1044
1045 class SiteNotResponding(Exception):
1046 pass
1047
1048
1049 def fastest_mirror(main_mirrors, mirrors_file, timeout=20):
1050 '''Find the fastest mirror for FSL downloads.'''
1051 MsgUser.debug("Calculating fastest mirror")
1052 socket.setdefaulttimeout(timeout)
1053
1054 # Get the mirror list from the url
1055 fastestmirrors = {}
1056 mirrorlist = []
1057 for m in main_mirrors:
1058 MsgUser.debug("Trying %s" % (m))
1059 m_url = '/'.join((m.strip('/'), mirrors_file))
1060 MsgUser.debug("Attempting to open %s" % (m_url))
1061 try:
1062 response = urlrequest.urlopen(url=m_url)
1063 except urlerror.HTTPError as e:
1064 MsgUser.debug("%s %s" % (m_url, e.msg))
1065 raise SiteNotResponding(e.msg)
1066 except urlerror.URLError as e:
1067 if isinstance(e.reason, socket.timeout):
1068 MsgUser.debug("Time out trying %s" % (m_url))
1069 raise SiteNotResponding(m)
1070 else:
1071 MsgUser.debug(str(e.reason))
1072 raise SiteNotResponding(str(e.reason))
1073 except socket.timeout as e:
1074 MsgUser.debug(e)
1075 raise SiteNotResponding(str(e))
1076 except Exception as e:
1077 MsgUser.debug("Unhandled exception %s" % (str(e)))
1078 raise
1079 else:
1080 mirrorlist = response.read().decode('utf-8').strip().split('\n')
1081 MsgUser.debug("Received the following "
1082 "mirror list %s" % (mirrorlist))
1083 continue
1084
1085 if len(mirrorlist) == 0:
1086 raise ServerFailure("Cannot find FSL download servers")
1087
1088 # Check timings from the urls specified
1089 if len(mirrorlist) > 1:
1090 for mirror in mirrorlist:
1091 MsgUser.debug("Trying %s" % (mirror))
1092 then = time.time()
1093 if mirror.startswith('http:'):
1094 serverport = 80
1095 elif mirror.startswith('https:'):
1096 serverport = 443
1097 else:
1098 raise ServerFailure("Unrecognised protocol")
1099 try:
1100 mysock = socket.create_connection((mirror, serverport),
1101 timeout)
1102 pingtime = time.time() - then
1103 mysock.close()
1104 fastestmirrors[pingtime] = mirror
1105 MsgUser.debug("Mirror responded in %s seconds" % (pingtime))
1106 except socket.gaierror as e:
1107 MsgUser.debug("%s can't be resolved" % (e))
1108 except socket.timeout as e:
1109 MsgUser.debug(e)
1110 if len(fastestmirrors) == 0:
1111 raise ServerFailure('Failed to contact any FSL download sites.')
1112 download_url = fastestmirrors[min(fastestmirrors.keys())]
1113 else:
1114 download_url = mirrorlist[0]
1115
1116 return download_url
1117
1118
1119 # Concept:
1120 # Web app creates the following files:
1121 # fslmirrorlist.txt - contains a list of mirror urls
1122 # fslreleases.json - contains the available maps for oses
1123 # mapping to a download url
1124 # {'installer' {
1125 # 'filename': 'fslinstaller.py',
1126 # 'version': '3.0.0',
1127 # 'date': '02/03/2017',
1128 # 'checksum_type', 'sha256',
1129 # 'checksum'},
1130 # 'linux' : {
1131 # 'centos' : {
1132 # 'x86_64': {
1133 # '6': {
1134 # '5.0.9': {
1135 # 'filename': 'fsl-5.0.9-centos6_64.tar.gz',
1136 # 'version': '5.0.9',
1137 # 'date': '01/02/2017',
1138 # 'checksum_type', 'sha256',
1139 # 'checksum': 'abf645662bcf4453235',
1140 # },
1141 # },
1142 # },
1143 # },
1144 # 'rhel' : {'alias': 'centos'}},
1145 # 'apple' : {
1146 # 'darwin' : {
1147 # 'x86_64': {
1148 # '11': {
1149 # ....
1150 # },
1151 # }
1152
1153 @memoize
1154 def get_web_manifest(download_url, timeout=20):
1155 '''Download the FSL manifest from download_url'''
1156 socket.setdefaulttimeout(timeout)
1157 MsgUser.debug("Looking for manifest at %s." % (download_url))
1158
1159 MsgUser.debug("Downloading JSON file")
1160 return get_json(download_url + Settings.manifest_json)
1161
1162
1163 class GetFslDirError(Exception):
1164 pass
1165
1166
1167 @memoize
1168 def get_fsldir(specified_dir=None, install=False):
1169 '''Find the installed version of FSL using FSLDIR
1170 or location of this script'''
1171
1172 def validate_fsldir(directory):
1173 parent = os.path.dirname(directory)
1174 if parent == directory:
1175 raise GetFslDirError(
1176 "%s appears to be the root folder" %
1177 parent)
1178 if not os.path.exists(parent):
1179 raise GetFslDirError(
1180 "%s doesn't exist" %
1181 parent)
1182 if not os.path.isdir(parent):
1183 raise GetFslDirError(
1184 "%s isn't a directory" %
1185 parent)
1186 if (os.path.exists(directory) and not
1187 os.path.exists(os.path.join(
1188 directory, 'etc', 'fslversion'
1189 ))):
1190 raise GetFslDirError(
1191 "%s exists and doesn't appear to be an installed FSL folder" %
1192 directory)
1193
1194 if specified_dir:
1195 specified_dir = os.path.abspath(specified_dir)
1196 if install is False:
1197 if not check_fsl_install(specified_dir):
1198 raise GetFslDirError(
1199 "%s isn't an 'fsl' folder" %
1200 specified_dir)
1201 else:
1202 validate_fsldir(specified_dir)
1203 return specified_dir
1204 try:
1205 fsldir = os.environ['FSLDIR']
1206 try:
1207 validate_fsldir(fsldir)
1208 except GetFslDirError:
1209 # FSLDIR environment variable is incorrect!
1210 MsgUser.warning('FSLDIR environment variable '
1211 'does not point at FSL install, ignoring...')
1212 MsgUser.debug('FSLDIR is set to %s - '
1213 'this folder does not appear to exist' % (fsldir))
1214 fsldir = None
1215 else:
1216 fsldir = fsldir.rstrip('/')
1217 if MsgUser.isquiet():
1218 return fsldir
1219 except KeyError:
1220 # Look to see if I'm in an FSL install
1221 try:
1222 my_parent = os.path.dirname(
1223 os.path.dirname(os.path.realpath(__file__)))
1224 except NameError:
1225 # Running in debugger - __file__ not set, assume it's cwd
1226 my_parent = os.path.dirname(
1227 os.path.dirname(os.getcwd()))
1228 try:
1229 validate_fsldir(my_parent)
1230 fsldir = my_parent
1231 except GetFslDirError:
1232 fsldir = None
1233
1234 if not install:
1235 MsgUser.debug("asking about %s" % (fsldir))
1236 valid_dir = False
1237 while not valid_dir:
1238 fsldir = Settings.inst_qus.ask_question(
1239 'inst_loc', default=fsldir)
1240 try:
1241 validate_fsldir(fsldir)
1242 valid_dir = True
1243 except GetFslDirError as e:
1244 MsgUser.falied(str(e))
1245 return fsldir
1246
1247 else:
1248 if not MsgUser.isquiet():
1249 valid_dir = False
1250 while not valid_dir:
1251 fsldir = Settings.inst_qus.ask_question(
1252 'location', default=fsldir)
1253 try:
1254 validate_fsldir(fsldir)
1255 valid_dir = True
1256 except GetFslDirError as e:
1257 MsgUser.failed(str(e))
1258 MsgUser.message(
1259 '''Hint - press Enter to select the default value '''
1260 '''given in the square brackets.
1261 If you are specifying a destination folder this needs to either be an existing
1262 FSL install folder or a folder that doesn't already exist.''')
1263 fsldir = None
1264 else:
1265 raise GetFslDirError(
1266 "I can't locate FSL, try again using '-d <FSLDIR>' "
1267 "to specify where to find the FSL install")
1268 return fsldir
1269
1270
1271 def archive_version(archive):
1272 '''Takes the path to a FSL install file
1273 and works out what version it is.'''
1274 if not os.path.isfile(archive):
1275 raise NotAFslVersion("%s is not a file" % (archive))
1276 else:
1277 # file is of form: fsl-V.V.V-platform.extensions
1278 (_, vstring, _) = archive.strip().split('-', 2)
1279 try:
1280 return Version(vstring)
1281 except ValueError:
1282 raise NotAFslVersion(
1283 "%s doesn't look like "
1284 "a version number" % (vstring))
1285
1286
1287 class NotAFslVersion(Exception):
1288 pass
1289
1290
1291 class GetInstalledVersionError(Exception):
1292 pass
1293
1294
1295 def get_installed_version(fsldir):
1296 '''Takes path to FSLDIR and finds installed version details'''
1297 MsgUser.debug("Looking for fsl in %s" % fsldir)
1298 v_file = os.path.join(fsldir, 'etc', 'fslversion')
1299 if os.path.exists(v_file):
1300 f = open(v_file)
1301 v_string = f.readline()
1302 f.close()
1303 try:
1304 version = Version(v_string.strip())
1305 except ValueError:
1306 raise NotAFslVersion(
1307 "%s not a valid "
1308 "version string" % (v_string.strip()))
1309 else:
1310 MsgUser.debug(
1311 "No version information found - "
1312 "is this actually an FSL dir?")
1313 raise GetInstalledVersionError(
1314 "Cannot find the version information - "
1315 "is this actually an FSL dir?")
1316 MsgUser.debug("Found version %s" % (version))
1317 return version
1318
1319
1320 def which_shell():
1321 return os.path.basename(os.getenv("SHELL"))
1322
1323
1324 class SelfUpdateError(Exception):
1325 pass
1326
1327
1328 def self_update(server_url):
1329 '''Check for and apply an update to myself'''
1330 # See if there is a newer version available
1331 if 'fslinstaller' in sys.argv[0]:
1332 try:
1333 installer = get_installer(server_url)
1334 except GetInstallerError as e:
1335 MsgUser.debug("Failed to get installer version %s." % (str(e)))
1336 raise SelfUpdateError('Failed to get installer version. '
1337 'Please try again later.')
1338
1339 MsgUser.debug("Server has version " + installer['version'])
1340 if Version(installer['version']) <= version:
1341 MsgUser.debug("Installer is up-to-date.")
1342 return
1343 # There is a new version available - download it
1344 MsgUser.message("There is a newer version (%s) of the installer "
1345 "(you have %s) updating..." % (
1346 installer['version'], version))
1347 (_, tmpfname) = temp_file_name(mode='w', close=True)
1348
1349 downloaded = False
1350 while downloaded is False:
1351 try:
1352 file_url = '/'.join(
1353 (Settings.mirror.rstrip('/'), installer['filename']))
1354 download_file(
1355 url=file_url,
1356 localf=tmpfname)
1357 if (
1358 file_checksum(tmpfname, installer['checksum_type']) !=
1359 installer['checksum']):
1360 raise SelfUpdateError(
1361 "Found update to installer but download "
1362 "was corrupt. Please try again later.")
1363 except DownloadFileError as e:
1364 if Settings.mirror != Settings.main_mirror:
1365 MsgUser.warning(
1366 "Download from mirror failed, re-trying from "
1367 "main FSL download site")
1368 Settings.mirror = Settings.main_mirror
1369 else:
1370 MsgUser.debug("Failed to update installer %s." % (str(e)))
1371 raise SelfUpdateError(
1372 'Found update to installer but unable to '
1373 'download the new version. Please try again.')
1374 else:
1375 downloaded = True
1376 # Now run the new installer
1377 # EXEC new script with the options we were given
1378 os.chmod(tmpfname, 0o755)
1379 c_args = [sys.executable, tmpfname, ]
1380 c_args.extend(sys.argv[1:])
1381 MsgUser.debug(
1382 "Calling %s %s" % (sys.executable, c_args))
1383 os.execv(sys.executable, c_args)
1384 else:
1385 # We are now running the newly downloaded installer
1386 MsgUser.ok('Installer updated to latest version %s' % (str(version)))
1387 MsgUser.ok("Installer self update successful.")
1388
1389
1390 class ServerFailure(Exception):
1391 pass
1392
1393
1394 class BadVersion(Exception):
1395 pass
1396
1397
1398 class GetInstallerError(Exception):
1399 pass
1400
1401
1402 def get_installer(server_url):
1403 MsgUser.debug("Checking %s for "
1404 "installer information" % (server_url))
1405 manifest = get_web_manifest(server_url)
1406 return manifest['installer']
1407
1408
1409 @memoize
1410 def get_releases(server_url):
1411 '''Return a hash with all information about available
1412 versions for this OS'''
1413 computer = Host
1414 MsgUser.debug("Getting web manifest")
1415 manifest = get_web_manifest(server_url)
1416 try:
1417 os_definition = manifest[computer.o_s][computer.vendor]
1418 except KeyError:
1419 raise UnsupportedOs("%s %s not supported by this installer" % (
1420 computer.o_s, computer.vendor
1421 ))
1422 t_version = computer.version.major
1423 alias_t = 'alias'
1424 if alias_t in list(os_definition.keys()):
1425 if str(t_version) in os_definition[alias_t]:
1426 os_parent = os_definition[alias_t][
1427 str(t_version)]['parent']
1428 t_version = os_definition[alias_t][
1429 str(t_version)]['version']
1430
1431 os_definition = manifest[computer.o_s][os_parent]
1432
1433 if computer.arch not in list(os_definition.keys()):
1434 raise UnsupportedOs("%s %s not supported" % (
1435 computer.vendor,
1436 computer.arch
1437 ))
1438
1439 os_def = os_definition[computer.arch]
1440 while t_version > 0:
1441 MsgUser.debug("Trying version %s" % (t_version))
1442 if str(t_version) not in list(os_def.keys()):
1443 MsgUser.debug("...not found")
1444 t_version -= 1
1445 else:
1446 break
1447 if t_version == 0:
1448 raise UnsupportedOs("%s %s not supported" % (
1449 computer.vendor,
1450 computer.version.major
1451 ))
1452 elif t_version != computer.version.major:
1453 MsgUser.warning(
1454 "%s %s not officially supported "
1455 "- trying to locate support for an earlier "
1456 "version - this may not work" % (
1457 computer.vendor, computer.version.major))
1458 return os_definition[computer.arch][str(t_version)]
1459
1460
1461 class ExtraDownloadError(Exception):
1462 pass
1463
1464
1465 @memoize
1466 def get_extra(server_url, extra_type):
1467 '''Return a hash with all information about available
1468 versions of source code'''
1469 MsgUser.debug("Getting web manifest")
1470 manifest = get_web_manifest(server_url)
1471 try:
1472 extra = manifest[extra_type]
1473 except KeyError:
1474 raise ExtraDownloadError("Unrecognised extra %s" % (extra_type))
1475 return extra
1476
1477
1478 class ImproperlyConfigured(Exception):
1479 pass
1480
1481
1482 def list_releases(url):
1483 releases = get_releases(url)
1484 MsgUser.message("Available FSL versions for this OS:")
1485 MsgUser.debug(releases)
1486
1487 rels = []
1488
1489 for v, release in list(releases.items()):
1490 if 'date' in release:
1491 rdate = release['date']
1492 else:
1493 rdate = "Third-party package"
1494 rels.append((v, rdate))
1495
1496 for v, rdate in sorted(rels, reverse=True):
1497 MsgUser.message("%s\t(%s)" % (v, rdate))
1498
1499
1500 def list_builds(url):
1501 '''Lists all available FSL builds. '''
1502 manifest = dict(get_web_manifest(url))
1503
1504 MsgUser.message("All available FSL builds:")
1505
1506 centos = manifest['linux']['centos']['x86_64']
1507 macos = manifest['darwin']['apple']['x86_64']
1508
1509 def get_platform(s):
1510 match = re.match(r'^fsl-(.+)-(.+).tar.gz$', s)
1511 plat = match.group(2)
1512 return plat
1513
1514 fslversions = collections.defaultdict(set)
1515
1516 for builds in itertools.chain(list(centos.values()), list(macos.values())):
1517 for fslversion, info in list(builds.items()):
1518 fslversions[fslversion].add(get_platform(info['filename']))
1519
1520 for fslversion, plats in list(fslversions.items()):
1521 MsgUser.message('%s - %s' % (fslversion, ', '.join(plats)))
1522
1523
1524 def latest_release(url):
1525 releases = get_releases(url)
1526 MsgUser.debug("Got version information: %s" % (releases))
1527 versions = [Version(x) for x in list(releases.keys())]
1528 MsgUser.debug("Versions: %s" % (versions))
1529 return releases[str(sorted(versions)[-1])]
1530
1531
1532 class InstallInstallerError(Exception):
1533 pass
1534
1535
1536 def install_installer(fsldir):
1537 '''Install this script into $FSLDIR/etc'''
1538 targetfolder = os.path.join(fsldir, 'etc')
1539 as_root = False
1540 installer = os.path.abspath(__file__)
1541 MsgUser.debug(
1542 "Copying fslinstaller (%s) to %s" % (
1543 installer,
1544 targetfolder))
1545 if not is_writeable(targetfolder):
1546 if not is_writeable_as_root(targetfolder):
1547 raise InstallInstallerError("Cannot write to folder as root user.")
1548 else:
1549 as_root = True
1550 copy_file(
1551 installer, os.path.join(targetfolder, "fslinstaller.py"),
1552 as_root)
1553
1554
1555 class InstallQuestions(object):
1556 def __init__(self):
1557 self.questions = {}
1558 self.validators = {}
1559 self.preprocs = {}
1560 self.type = {}
1561 self.default = {}
1562 self.defaults = False
1563
1564 def add_question(self, key, question, default, qtype, validation_f, preproc_f=None):
1565 self.questions[key] = question
1566 self.default[key] = default
1567 self.type[key] = qtype
1568 self.validators[key] = validation_f
1569 self.preprocs[key] = preproc_f
1570
1571 def ask_question(self, key, default=None):
1572 # Ask a question
1573 no_answer = True
1574 validator = self.validators[key]
1575 preproc = self.preprocs[key]
1576
1577 def parse_answer(q_type, answer):
1578 if q_type == 'bool':
1579 if answer.lower() == 'yes':
1580 return True
1581 else:
1582 return False
1583 else:
1584 return answer
1585
1586 if not default:
1587 default = self.default[key]
1588
1589 if self.defaults:
1590 MsgUser.debug(self.questions[key])
1591 MsgUser.debug("Automatically using the default %s" % (default))
1592 self.answers[key] = parse_answer(self.type[key], default)
1593 no_answer = False
1594
1595 while no_answer:
1596 MsgUser.question(
1597 "%s? %s:" % (
1598 self.questions[key],
1599 '[%s]' % (default)))
1600 if PYVER[0] == 2: your_answer = raw_input()
1601 else: your_answer = input()
1602
1603 MsgUser.debug("Your answer was %s" % (your_answer))
1604 if your_answer == '':
1605 MsgUser.debug("You want the default")
1606 your_answer = default
1607 elif preproc is not None:
1608 your_answer = preproc(your_answer)
1609 if validator(your_answer):
1610 answer = parse_answer(self.type[key], your_answer)
1611 no_answer = False
1612 MsgUser.debug("Returning the answer %s" % (answer))
1613 return answer
1614
1615
1616 def yes_no(answer):
1617 if answer.lower() == 'yes' or answer.lower() == 'no':
1618 return True
1619 else:
1620 MsgUser.message("Please enter yes or no.")
1621 return False
1622
1623
1624 def check_install_location(folder):
1625 '''Don't allow relative paths'''
1626 MsgUser.debug("Checking %s is an absolute path" % (folder))
1627 if (folder == '.' or
1628 folder == '..' or
1629 folder.startswith('./') or
1630 folder.startswith('../') or
1631 folder.startswith('~')):
1632 MsgUser.message("Please enter an absolute path.")
1633 return False
1634 return True
1635
1636
1637 def external_validate(what_to_check):
1638 '''We will validate elsewhere'''
1639 return True
1640
1641
1642 def check_fsl_install(fsldir):
1643 '''Check if this folder contains FSL install'''
1644 MsgUser.debug("Checking %s is an FSL install" % (fsldir))
1645 if os.path.isdir(fsldir):
1646 if os.path.exists(
1647 os.path.join(fsldir, 'etc', 'fslversion')
1648 ):
1649 return True
1650 return False
1651
1652
1653 def fsl_downloadname(suffix, version):
1654 return 'fsl-%s-%s' % (
1655 version, suffix)
1656
1657
1658 class Settings(object):
1659 version = version
1660 title = "--- FSL Installer - Version %s ---" % (version)
1661 main_server = 'fsl.fmrib.ox.ac.uk'
1662 mirrors = [build_url_with_protocol('https',
1663 main_server, ('fsldownloads',
1664 '')), ]
1665 mirrors_file = 'fslmirrorlist.txt'
1666 manifest_json = 'manifest.json'
1667 manifest_csv = 'manifest.csv'
1668 main_mirror = mirrors[0]
1669 mirror = main_mirror
1670
1671 applications = ['bin/fslview.app', 'bin/assistant.app']
1672 x11 = {'bad_versions': [],
1673 'download_url': "http://xquartz.macosforge.org/landing/",
1674 'apps': ['XQuartz.app', 'X11.app', ],
1675 'location': "/Applications/Utilities"}
1676 default_location = '/usr/local/fsl'
1677 post_inst_dir = "etc/fslconf"
1678
1679 inst_qus = InstallQuestions()
1680 inst_qus.add_question('version_match',
1681 "The requested version matches the installed "
1682 "version - do you wish to re-install FSL",
1683 'no', 'bool', yes_no)
1684 inst_qus.add_question('location',
1685 "Where would you like the FSL install to be "
1686 "(including the FSL folder name)",
1687 default_location, 'path', check_install_location, os.path.abspath)
1688 inst_qus.add_question('del_old',
1689 "FSL exists in the current location, "
1690 "would you like to keep a backup of the old "
1691 "version (N.B. You will not be able to use the old "
1692 "version)",
1693 'no', 'bool', yes_no)
1694 inst_qus.add_question('create',
1695 "Install location doesn't exist, should I create it",
1696 'yes', 'bool', yes_no)
1697 inst_qus.add_question('inst_loc',
1698 "Where is the FSL folder (e.g. /usr/local/fsl)",
1699 default_location, 'path', check_fsl_install)
1700 inst_qus.add_question('skipmd5',
1701 "I was unable to download the checksum of "
1702 "the install file so cannot confirm it is correct. "
1703 "Would you like to install anyway",
1704 'no', 'bool', yes_no)
1705 inst_qus.add_question('overwrite',
1706 "There is already a local copy of the file, would "
1707 "you like to overwrite it",
1708 "yes", 'bool', yes_no)
1709 inst_qus.add_question('upgrade',
1710 "Would you like to install upgrade",
1711 "yes", 'bool', yes_no)
1712 inst_qus.add_question('update',
1713 "Would you like to install update",
1714 "yes", 'bool', yes_no)
1715
1716
1717 def get_json(web_url):
1718 MsgUser.debug("Opening "+web_url)
1719 try:
1720 url = open_url(web_url)
1721 data = url.read().decode('utf-8')
1722 return json.loads(data)
1723 except OpenUrlError as e:
1724 raise ServerFailure(str(e))
1725
1726
1727 # [ linux, centos, x86_64, 6, filename, 'fname',
1728 # version, 'version', date, 'date', checksum_type, 'checksum_type',
1729 # checksum, 'checksum', supported, 'true/false', notes, 'notes',
1730 # instructions, 'instructions']
1731 # [ linux, redhat, alias, centos, supported, True/false, version, 'version' ]
1732 # [ 'installer', filename, 'fname', version, 'version', date, 'date',
1733 # checksum_type, 'checksum_type', checksum, 'checksum', supported,
1734 # 'true/false', notes, 'notes', instructions, 'instructions']
1735 # [ feeds, filename, 'fname', version, 'version',
1736 # date, 'date', checksum_type, 'checksum_type', checksum, 'checksum',
1737 # supported, 'true/false', notes, 'notes', instructions, 'instructions']
1738 # [ sources, filename, 'fname', version, 'version',
1739 # date, 'date', checksum_type, 'checksum_type', checksum, 'checksum',
1740 # supported, 'true/false', notes, 'notes', instructions, 'instructions']
1741
1742 class AutoDict(dict):
1743 '''Automatically create a nested dict'''
1744 def __getitem__(self, item):
1745 try:
1746 return dict.__getitem__(self, item)
1747 except KeyError:
1748 value = self[item] = type(self)()
1749 return value
1750
1751 def freeze(self):
1752 '''Returns a dict representation of an AutoDict'''
1753 frozen = {}
1754 for k, v in list(self.items()):
1755 if type(v) == type(self):
1756 frozen[k] = v.freeze()
1757 else:
1758 frozen[k] = v
1759 return frozen
1760
1761
1762 def get_csv_dict(web_url):
1763 MsgUser.debug("Opening "+web_url)
1764
1765 try:
1766 url = open_url(web_url)
1767 manifest_reader = csv.reader(
1768 url, delimiter=',', quoting=csv.QUOTE_MINIMAL)
1769 a_dict = AutoDict()
1770 for line in manifest_reader:
1771 MsgUser.debug(line)
1772 if line[0] == 'feeds':
1773 items = iter(line[1:])
1774 base_dict = dict(list(zip(items, items)))
1775 a_dict[line[0]] = base_dict
1776 elif line[0] == 'sources':
1777 items = iter(line[1:])
1778 base_dict = dict(list(zip(items, items)))
1779 a_dict[line[0]] = base_dict
1780 elif line[0] == 'installer':
1781 items = iter(line[1:])
1782 base_dict = dict(list(zip(items, items)))
1783 a_dict[line[0]] = base_dict
1784 else:
1785 # Install package or alias
1786 if line[2] == 'alias':
1787 items = iter(line[4:])
1788 base_dict = dict(list(zip(items, items)))
1789 a_dict[
1790 str(line[0])][
1791 str(line[1])][
1792 str(line[2])][
1793 str(line[3])] = base_dict
1794 else:
1795 items = iter(line[5:])
1796 base_dict = dict(list(zip(items, items)))
1797 MsgUser.debug(
1798 ",".join(
1799 (line[0], line[1], line[2], line[3], line[4])))
1800 a_dict[
1801 str(line[0])][
1802 str(line[1])][
1803 str(line[2])][
1804 str(line[3])][
1805 str(line[4])] = base_dict
1806 except OpenUrlError as e:
1807 raise ServerFailure(str(e))
1808 MsgUser.debug(a_dict)
1809 return a_dict.freeze()
1810
1811
1812 class InvalidVersion(Exception):
1813 pass
1814
1815
1816 def get_web_version_and_details(server_url, request_version=None):
1817 if request_version is None:
1818 details = latest_release(server_url)
1819 try:
1820 version = Version(details['version'])
1821 except KeyError:
1822 try:
1823 redirect = details['redirect']
1824 raise DownloadError(
1825 "Installer not supported on this platform."
1826 "Please visit %s for download instructions" % redirect)
1827 except KeyError:
1828 MsgUser.debug(
1829 "Can't find version or redirect - %s" % details)
1830 raise DownloadError(
1831 "Unsupported OS"
1832 )
1833 else:
1834 MsgUser.debug("Requested version %s" % request_version)
1835 releases = get_releases(server_url)
1836 try:
1837 version = Version(request_version)
1838 except ValueError:
1839 raise DownloadError(
1840 "%s doesn't look like a version" % request_version)
1841 if request_version not in list(releases.keys()):
1842 raise DownloadError(
1843 "%s isn't an available version" % request_version)
1844 details = releases[request_version]
1845 return (version, details)
1846
1847
1848 def download_release(
1849 server_url, to_temp=False,
1850 request_version=None, skip_verify=False,
1851 keep=False, source_code=False, feeds=False):
1852
1853 (version, details) = get_web_version_and_details(
1854 server_url, request_version)
1855 if request_version is None:
1856 request_version = str(version)
1857
1858 if source_code or feeds:
1859 if source_code:
1860 extra_type = 'sources'
1861 MsgUser.message("Downloading source code")
1862 else:
1863 extra_type = 'feeds'
1864 MsgUser.message("Downloading FEEDS")
1865
1866 try:
1867 releases = get_extra(server_url, extra_type)
1868 except ExtraDownloadError as e:
1869 raise DownloadError(
1870 "Unable to find details for %s" % (extra_type)
1871 )
1872 to_temp = False
1873 try:
1874 details = releases[request_version]
1875 except KeyError:
1876 raise DownloadError(
1877 "%s %s isn't available" % (request_version, extra_type)
1878 )
1879
1880 MsgUser.debug(details)
1881
1882 if to_temp:
1883 try:
1884 (_, local_filename) = temp_file_name(close=True)
1885 except Exception as e:
1886 MsgUser.debug("Error getting temporary file name %s" % (str(e)))
1887 raise DownloadError("Unable to begin download")
1888 else:
1889 local_filename = details['filename']
1890 if os.path.exists(local_filename):
1891 if os.path.isfile(local_filename):
1892 MsgUser.message("%s exists" % (local_filename))
1893 overwrite = Settings.inst_qus.ask_question('overwrite')
1894 if overwrite:
1895 MsgUser.warning(
1896 "Erasing existing file %s" % local_filename)
1897 try:
1898 os.remove(local_filename)
1899 except Exception:
1900 raise DownloadError(
1901 "Unabled to remove local file %s - remove"
1902 " it and try again" % local_filename)
1903 else:
1904 raise DownloadError("Aborting download")
1905 else:
1906 raise DownloadError(
1907 "There is a directory named %s "
1908 "- cannot overwrite" % local_filename)
1909
1910 MsgUser.debug(
1911 "Downloading to file %s "
1912 "(this may take some time)." % (local_filename))
1913 MsgUser.message(
1914 "Downloading...")
1915
1916 downloaded = False
1917 while downloaded is False:
1918 try:
1919 file_url = '/'.join(
1920 (Settings.mirror.rstrip('/'), details['filename']))
1921 download_file(
1922 url=file_url,
1923 localf=local_filename)
1924 if (not skip_verify and
1925 (details['checksum'] !=
1926 file_checksum(local_filename, details['checksum_type']))):
1927 raise DownloadError('Downloaded file fails checksum')
1928 MsgUser.ok("File downloaded")
1929 except DownloadFileError as e:
1930 MsgUser.debug(str(e))
1931 if Settings.mirror != Settings.main_mirror:
1932 MsgUser.warning(
1933 "Download from mirror failed, re-trying from "
1934 "main FSL download site")
1935 Settings.mirror = Settings.main_mirror
1936 else:
1937 raise DownloadError(str(e))
1938 else:
1939 downloaded = True
1940 return (local_filename, version, details)
1941
1942
1943 class DownloadError(Exception):
1944 pass
1945
1946
1947 def shell_config(shell, fsldir, skip_root=False):
1948 MsgUser.debug("Building environment for %s" % (shell))
1949 env_lines = ''
1950
1951 if shell in BOURNE_SHELLS:
1952 if skip_root:
1953 env_lines += '''if [ -x /usr/bin/id ]; then
1954 if [ -z "$EUID" ]; then
1955 # ksh and dash doesn't setup the EUID environment var
1956 EUID=`id -u`
1957 fi
1958 fi
1959 if [ "$EUID" != "0" ]; then
1960 '''
1961 env_lines += '''
1962 # FSL Setup
1963 FSLDIR=%s
1964 PATH=${FSLDIR}/bin:${PATH}
1965 export FSLDIR PATH
1966 . ${FSLDIR}/etc/fslconf/fsl.sh
1967 '''
1968 if skip_root:
1969 env_lines += '''fi'''
1970 match = "FSLDIR="
1971 replace = "FSLDIR=%s"
1972 elif shell in C_SHELLS:
1973 if skip_root:
1974 env_lines += '''if ( $uid != 0 ) then
1975 '''
1976 env_lines += '''
1977 # FSL Setup
1978 setenv FSLDIR %s
1979 setenv PATH ${FSLDIR}/bin:${PATH}
1980 source ${FSLDIR}/etc/fslconf/fsl.csh
1981 '''
1982 if skip_root:
1983 env_lines += '''
1984 endif'''
1985 match = "setenv FSLDIR"
1986 replace = "setenv FSLDIR %s"
1987 elif shell == 'matlab':
1988 env_lines = '''
1989 %% FSL Setup
1990 setenv( 'FSLDIR', '%s' );
1991 setenv('FSLOUTPUTTYPE', 'NIFTI_GZ');
1992 fsldir = getenv('FSLDIR');
1993 fsldirmpath = sprintf('%%s/etc/matlab',fsldir);
1994 path(path, fsldirmpath);
1995 clear fsldir fsldirmpath;
1996 '''
1997 match = "setenv( 'FSLDIR',"
1998 replace = "setenv( 'FSLDIR', '%s' );"
1999 else:
2000 raise ValueError("Unknown shell type %s" % shell)
2001 return (env_lines % (fsldir), match, replace % (fsldir))
2002
2003
2004 def get_profile(shell):
2005 home = os.path.expanduser("~")
2006
2007 dotprofile = os.path.join(home, '.profile')
2008 if shell == 'bash':
2009 profile = os.path.join(home, '.bash_profile')
2010 if not os.path.isfile(profile) and os.path.isfile(dotprofile):
2011 profile = dotprofile
2012 elif shell == 'zsh':
2013 profile = os.path.join(home, '.zprofile')
2014 # ZSH will never source .profile
2015 elif shell == 'sh':
2016 profile = dotprofile
2017 else:
2018 cshprofile = os.path.join(home, '.cshrc')
2019 if shell == 'csh':
2020 profile = cshprofile
2021 elif shell == 'tcsh':
2022 profile = os.path.join(home, '.tcshrc')
2023 if not os.path.isfile(profile) and os.path.isfile(cshprofile):
2024 profile = cshprofile
2025 else:
2026 raise ValueError("Unsupported shell")
2027 return profile
2028
2029
2030 class FixFslDirError(Exception):
2031 pass
2032
2033
2034 def fix_fsldir(shell, fsldir):
2035 (_, match, replace) = shell_config(shell, fsldir)
2036 profile = get_profile(shell)
2037 MsgUser.debug(
2038 "Editing %s, replacing line beginning:%s with %s." %
2039 (profile, match, replace))
2040 try:
2041 edit_file(profile, line_starts_replace, match, replace, False)
2042 except EditFileError as e:
2043 raise FixFslDirError(str(e))
2044
2045
2046 class AddFslDirError(Exception):
2047 pass
2048
2049
2050 def add_fsldir(shell, fsldir):
2051 (env_lines, _, _) = shell_config(shell, fsldir)
2052 profile = get_profile(shell)
2053 MsgUser.debug("Adding %s to %s" % (env_lines, profile))
2054 try:
2055 add_to_file(profile, env_lines, False)
2056 except AddToFileError as e:
2057 raise AddFslDirError(str(e))
2058
2059
2060 class ConfigureMatlabError(Exception):
2061 pass
2062
2063
2064 class ConfigureMatlabWarn(Exception):
2065 pass
2066
2067
2068 def configure_matlab(fsldir, m_startup='', c_file=True):
2069 '''Setup your startup.m file to enable FSL MATLAB functions to work'''
2070 (mlines, match, replace) = shell_config('matlab', fsldir)
2071 if m_startup == '':
2072 m_startup = os.path.join(
2073 os.path.expanduser('~'), 'Documents', 'MATLAB', 'startup.m')
2074 if os.path.exists(m_startup):
2075 # Check if already configured
2076 MsgUser.debug("Looking for %s in %s" % (match, m_startup))
2077 if file_contains(m_startup, match):
2078 try:
2079 MsgUser.debug('Updating MATLAB startup file.')
2080 edit_file(
2081 m_startup, line_starts_replace,
2082 match, replace, False)
2083 except EditFileError as e:
2084 raise ConfigureMatlabError(str(e))
2085 else:
2086 MsgUser.debug('Adding FSL settings to MATLAB.')
2087 try:
2088 add_to_file(m_startup, mlines, False)
2089 except AddToFileError as e:
2090 raise ConfigureMatlabError(str(e))
2091 elif c_file:
2092 # No startup.m file found. Create one
2093 try:
2094 MsgUser.debug('No MATLAB startup.m file found, creating one.')
2095 if not os.path.isdir(os.path.dirname(m_startup)):
2096 MsgUser.debug('No MATLAB startup.m file found, creating one.')
2097 os.mkdir(os.path.dirname(m_startup))
2098 create_file(m_startup, mlines, False)
2099 except (OSError, CreateFileError) as e:
2100 MsgUser.debug(
2101 'Unable to create ~/Documents/MATLAB/ folder or startup.m file,'
2102 ' cannot configure (%).' % (str(e)))
2103 raise ConfigureMatlabError(
2104 "Unable to create your ~/Documents/MATLAB/ folder or startup.m, "
2105 "so cannot configure MATLAB for FSL.")
2106 else:
2107 MsgUser.debug('MATLAB may not be installed, doing nothing.')
2108 raise ConfigureMatlabWarn("I can't tell if you have MATLAB installed.")
2109
2110
2111 class SetupEnvironmentError(Exception):
2112 pass
2113
2114
2115 class SetupEnvironmentSkip(Exception):
2116 pass
2117
2118
2119 def setup_system_environment(fsldir):
2120 '''Add a system-wide profile setting up FSL for all users.
2121 Only supported on Redhat/Centos'''
2122 profile_d = '/etc/profile.d'
2123 profile_files = ['fsl.sh', 'fsl.csh']
2124 exceptions = []
2125 skips = []
2126
2127 if os.getuid() != 0:
2128 sudo = True
2129 else:
2130 sudo = False
2131
2132 if os.path.isdir(profile_d):
2133 for profile in profile_files:
2134 pf = profile.split('.')[1]
2135 (lines, match, replace) = shell_config(pf, fsldir)
2136 this_profile = os.path.join(profile_d, profile)
2137 if os.path.exists(this_profile):
2138 # Already has a profile file
2139 # Does it contain an exact match for current FSLDIR?
2140 match = file_contains_1stline(this_profile, replace)
2141 if match != '':
2142 # If there is an fsl.(c)sh then just fix
2143 # the entry for FSLDIR
2144 MsgUser.debug(
2145 "Fixing %s for FSLDIR location." % (this_profile))
2146 try:
2147 edit_file(
2148 this_profile, line_starts_replace,
2149 match, replace, sudo)
2150 except EditFileError as e:
2151 exceptions.append(str(e))
2152 else:
2153 # No need to do anything
2154 MsgUser.debug(
2155 "%s already configured - skipping." %
2156 (this_profile))
2157 skips.append(profile)
2158 else:
2159 # Create the file
2160 try:
2161 create_file(this_profile, lines, sudo)
2162 except CreateFileError as e:
2163 exceptions.append(str(e))
2164
2165 else:
2166 raise SetupEnvironmentError(
2167 "No system-wide configuration folder found - Skipped")
2168 if exceptions:
2169 raise SetupEnvironmentError(".".join(exceptions))
2170 if skips:
2171 raise SetupEnvironmentSkip(".".join(skips))
2172
2173
2174 def setup_environment(fsldir=None, system=False, with_matlab=False):
2175 '''Setup the user's environment so that their
2176 terminal finds the FSL tools etc.'''
2177 # Check for presence of profile file:
2178 if fsldir is None:
2179 fsldir = get_fsldir()
2180
2181 user_shell = which_shell()
2182 MsgUser.debug("User's shell is %s" % (user_shell))
2183 try:
2184 (profile_lines, _, _) = shell_config(user_shell, fsldir)
2185 profile = get_profile(user_shell)
2186 except ValueError as e:
2187 raise SetupEnvironmentError(str(e))
2188
2189 cfile = False
2190 if not os.path.isfile(profile):
2191 MsgUser.debug("User is missing a shell setup file.")
2192 cfile = True
2193
2194 if cfile:
2195 MsgUser.debug("Creating file %s" % (profile))
2196 try:
2197 create_file(profile, profile_lines, False)
2198 except CreateFileError as e:
2199 raise SetupEnvironmentError(
2200 "Unable to create profile %s" % (profile))
2201 else:
2202 # Check if user already has FSLDIR set
2203 MsgUser.message("Setting up FSL software...")
2204 try:
2205 if file_contains(profile, "FSLDIR"):
2206 MsgUser.debug("Updating FSLDIR entry.")
2207 fix_fsldir(user_shell, fsldir)
2208 else:
2209 MsgUser.debug("Adding FSLDIR entry.")
2210 add_fsldir(user_shell, fsldir)
2211 except (AddFslDirError, FixFslDirError) as e:
2212 raise SetupEnvironmentError(
2213 "Unable to update your profile %s"
2214 " with FSL settings" % (profile))
2215
2216 if with_matlab:
2217 MsgUser.debug("Setting up MATLAB")
2218 try:
2219 configure_matlab(fsldir)
2220 except ConfigureMatlabError as e:
2221 MsgUser.debug(str(e))
2222 raise SetupEnvironmentError(str(e))
2223 except ConfigureMatlabWarn as e:
2224 MsgUser.skipped(str(e))
2225
2226
2227 class PostInstallError(Exception):
2228 pass
2229
2230
2231 class InstallArchiveError(Exception):
2232 pass
2233
2234
2235 class UnknownArchiveType(Exception):
2236 pass
2237
2238
2239 def archive_type(archive):
2240 '''Determine file type based on extension and check
2241 that file looks like this file type'''
2242 archive_types = {
2243 'gzip': ('tar', '-z'),
2244 'bzip2': ('tar', '-j'),
2245 'zip': ('zip', ''), }
2246
2247 try:
2248 file_type = run_cmd("file %s" % (archive))
2249 except RunCommandError as e:
2250 raise UnknownArchiveType(str(e))
2251 file_type = file_type.lower()
2252 for f_type in ('gzip', 'bzip2', 'zip', ):
2253 if f_type in file_type:
2254 return archive_types[f_type]
2255 raise UnknownArchiveType(archive)
2256
2257
2258 def asl_gui_604_patch(fsldir, as_root=False):
2259 '''
2260 fsl 6.0.4 shipped with a broken fsleyes preview in asl_gui.
2261
2262 This function applies the simple patch to any new installation
2263 that downloads FSL 6.0.4 using the fslinstaller.
2264
2265 1. parse fsl version
2266
2267 2. if version == 6.0.4 apply asl_gui patch, else do nothing and return
2268
2269 to test this patch with an existing fsl 6.0.4:
2270
2271 1. make a minimal $FSLDIR folder structure
2272 - cd ~
2273 - mkdir fsl_test
2274 - cd fsl_test
2275 - mkdir fsl
2276 - cp -r $FSLDIR/etc fsl/
2277 - cp -r $FSLDIR/python fsl/
2278 - mkdir fsl/bin
2279
2280 2. tar it up
2281 - tar -czf fsl-6.0.4-centos7_64.tar.gz fsl
2282 - rm -r fsl # remove the fsl folder after tar-ing
2283
2284 3. run a test python install from the tar file
2285 - be sure to use python 2.X (e.g. 2.7 works fine)
2286 - python fslinstaller.py -f ~/fsl_test/fsl-6.0.4-centos7_64.tar.gz -d ~/fsl_test/fsl -p -M -D
2287 '''
2288 asl_file = os.path.join(fsldir, 'python', 'oxford_asl', 'gui', 'preview_fsleyes.py') #$FSLDIR/python/oxford_asl/gui/preview_fsleyes.py
2289 vfile = os.path.join(fsldir, 'etc', 'fslversion')
2290 vstring = ''
2291 with open(vfile, 'r') as f:
2292 vstring = f.readline()
2293 v = vstring.split(':')[0] # e.g. 6.0.4:wkj2w3jh
2294 if v == '6.0.4':
2295 MsgUser.message("Patching asl_gui for fsl 6.0.4")
2296 tfile = os.path.join(tempfile.mkdtemp(), "preview_fsleyes.py")
2297 # backup asl_file
2298 run_cmd_displayoutput('cp {} {}.bkup'.format(asl_file, asl_file), as_root=as_root)
2299 # copy asl_file to tempfile
2300 run_cmd_displayoutput('cp {} {}'.format(asl_file, tfile), as_root=as_root)
2301 # ensure script can open temp file
2302 run_cmd_displayoutput('chmod 775 {}'.format(tfile), as_root=as_root)
2303
2304 for line in fileinput.input(files=tfile, inplace=True):
2305 line = re.sub('parent=parent, ready=ready', 'ready=ready, raiseErrors=True', line.rstrip())
2306 print(line)
2307
2308 run_cmd_displayoutput('cp {} {}'.format(tfile, asl_file), as_root=as_root)
2309 os.remove(tfile)
2310
2311
2312 def post_install(
2313 fsldir, settings, script="post_install.sh", quiet=False,
2314 app_links=False, x11=False):
2315 MsgUser.message("Performing post install tasks")
2316 if is_writeable(fsldir):
2317 as_root = False
2318 elif is_writeable_as_root(fsldir):
2319 as_root = True
2320 else:
2321 raise PostInstallError(
2322 "Unable to write to target folder (%s)" % (fsldir))
2323 install_installer(fsldir)
2324
2325 # apply asl_gui patch if fsl 6.0.4
2326 asl_gui_604_patch(fsldir, as_root=as_root)
2327 script_path = os.path.join(fsldir, Settings.post_inst_dir, script)
2328 if x11:
2329 try:
2330 check_X11(settings.x11)
2331 except CheckX11Warning as e:
2332 MsgUser.warning(str(e))
2333 else:
2334 MsgUser.ok("X11 (required for GUIs) found")
2335
2336 if os.path.exists(script_path):
2337 MsgUser.debug("Found post-install script %s" % (script_path))
2338 if not os.access(script_path, os.X_OK):
2339 raise PostInstallError(
2340 "Unable to run post install script %s" % (script_path)
2341 )
2342 script_opts = '-f "%s"' % (fsldir)
2343 if quiet:
2344 script_opts += " -q"
2345
2346 command_line = " ".join((script_path, script_opts))
2347 try:
2348 run_cmd_displayoutput(command_line, as_root=as_root)
2349 except RunCommandError as e:
2350 raise PostInstallError(
2351 "Error running post installation script (error %s)"
2352 " - check the install log" % (str(e))
2353 )
2354 # Work around for mistake in 5.0.10 post setup script
2355 mal = os.path.join(
2356 fsldir, Settings.post_inst_dir,
2357 'make_applications_links.sh')
2358 if (os.path.exists(mal) and
2359 not file_contains(script_path, "make_applications_links.sh")):
2360 MsgUser.debug(
2361 "Work around necessary for missing app link creation")
2362 else:
2363 app_links = False
2364 if app_links:
2365 try:
2366 make_applications_links(fsldir, settings.applications)
2367 except MakeApplicationLinksError as e:
2368 for message in list(e.app_messages.values()):
2369 MsgUser.warning(message)
2370 else:
2371 MsgUser.ok("/Applications links created/updated")
2372
2373 MsgUser.ok("Post installation setup complete")
2374
2375
2376 def install_archive(archive, fsldir=None):
2377 def clean_up_temp():
2378 try:
2379 safe_delete(tempfolder, as_root)
2380 except SafeDeleteError as sd_e:
2381 MsgUser.debug(
2382 "Unable to clean up temporary folder! "
2383 "%s" % (str(sd_e)))
2384 if not os.path.isfile(archive):
2385 raise InstallError("%s isn't a file" % (archive))
2386 if not fsldir:
2387 try:
2388 fsldir = get_fsldir(specified_dir=fsldir, install=True)
2389 except GetFslDirError as e:
2390 raise InstallError(str(e))
2391
2392 MsgUser.debug("Requested install of %s as %s" % (archive, fsldir))
2393 if os.path.exists(fsldir):
2394 # move old one out of way
2395 MsgUser.debug("FSL version already installed")
2396 keep_old = Settings.inst_qus.ask_question('del_old')
2397 else:
2398 keep_old = False
2399
2400 install_d = os.path.dirname(fsldir)
2401 MsgUser.debug("Checking %s is writeable." % (install_d))
2402 if is_writeable(install_d):
2403 as_root = False
2404 elif is_writeable_as_root(install_d):
2405 as_root = True
2406 else:
2407 raise InstallArchiveError(
2408 "Unable to write to target folder (%s), "
2409 "even as a super user." % (install_d))
2410 MsgUser.debug("Does %s require root for deletion? %s" % (
2411 install_d, as_root))
2412 try:
2413 unarchive, ua_option = archive_type(archive)
2414 except UnknownArchiveType as e:
2415 raise InstallArchiveError(str(e))
2416 # Generate a temporary name - eg fsl-<mypid>-date
2417 tempname = '-'.join(('fsl', str(os.getpid()), str(time.time())))
2418 tempfolder = os.path.join(install_d, tempname)
2419 try:
2420 run_cmd_dropstdout("mkdir %s" % (tempfolder), as_root=as_root)
2421 except RunCommandError as e:
2422 raise InstallArchiveError(
2423 "Unable to create folder to install into.")
2424 MsgUser.debug(
2425 "Unpacking %s into folder %s." % (archive, tempfolder))
2426 try:
2427 if unarchive == 'tar':
2428 unpack_cmd = 'tar -C %s -x %s -o -f %s' % (
2429 tempfolder, ua_option, archive)
2430 elif unarchive == 'zip':
2431 MsgUser.debug(
2432 "Calling unzip %s %s" % (ua_option, archive)
2433 )
2434 unpack_cmd = 'unzip %s %s' % (ua_option, archive)
2435
2436 try:
2437 run_cmd_dropstdout(unpack_cmd, as_root=as_root)
2438 except RunCommandError as e:
2439 raise InstallArchiveError("Unable to unpack FSL.")
2440
2441 new_fsl = os.path.join(tempfolder, 'fsl')
2442 if os.path.exists(fsldir):
2443 # move old one out of way
2444 try:
2445 old_version = get_installed_version(fsldir)
2446 except (NotAFslVersion, GetInstalledVersionError) as e:
2447 if keep_old:
2448 old_version = Version('0.0.0')
2449 MsgUser.warning(
2450 "The contents of %s doesn't look like an "
2451 "FSL installation! - "
2452 "moving to fsl-0.0.0" % (fsldir))
2453 old_fsl = '-'.join((fsldir, str(old_version)))
2454 if os.path.exists(old_fsl):
2455 MsgUser.debug(
2456 "Looks like there is another copy of the "
2457 "old version of FSL - deleting...")
2458 try:
2459 safe_delete(old_fsl, as_root)
2460 except SafeDeleteError as e:
2461 raise InstallError(
2462 ";".join((
2463 "Install location already has a "
2464 "%s - I've tried to delete it but"
2465 " failed" % (old_fsl), str(e))))
2466
2467 if keep_old:
2468 try:
2469 MsgUser.debug(
2470 "Moving %s to %s" % (fsldir, old_fsl))
2471 move(fsldir, old_fsl, as_root)
2472 MsgUser.message(
2473 '''You can find your archived version of FSL in %s.
2474 If you wish to restore it, remove %s and rename %s to %s''' % (
2475 old_fsl, fsldir, old_fsl, fsldir))
2476
2477 except MoveError as mv_e:
2478 # failed to move the old version
2479 MsgUser.debug(
2480 "Failed to move old version "
2481 "- %s" % (str(mv_e)))
2482 raise InstallError(
2483 "Failed to backup old version (%s)" % (str(mv_e)))
2484 else:
2485 MsgUser.debug("Removing existing FSL install")
2486 try:
2487 safe_delete(fsldir, as_root)
2488 MsgUser.debug("Deleted %s." % (fsldir))
2489 except SafeDeleteError as e:
2490 raise InstallError(
2491 "Failed to delete %s - %s." % (fsldir, str(e)))
2492 else:
2493 old_fsl = ''
2494 try:
2495 MsgUser.debug("Moving %s to %s" % (new_fsl, fsldir))
2496 move(new_fsl, fsldir, as_root)
2497 except MoveError as e:
2498 # Unable to move new install into place
2499 MsgUser.debug(
2500 "Move failed - %s." % (str(e)))
2501 raise InstallError(
2502 'Failed to move new version into place.')
2503
2504 except InstallError as e:
2505 clean_up_temp()
2506 raise InstallArchiveError(str(e))
2507
2508 clean_up_temp()
2509 MsgUser.debug("Install complete")
2510 MsgUser.ok("FSL software installed.")
2511 return fsldir
2512
2513
2514 def check_for_updates(url, fsldir, requested_v=None):
2515 # Start an update
2516 MsgUser.message("Looking for new version.")
2517 try:
2518 this_version = get_installed_version(fsldir)
2519 except GetInstalledVersionError as e:
2520 # We can't find an installed version of FSL!
2521 raise InstallError(str(e))
2522 else:
2523 MsgUser.debug("You have version %s" % (this_version))
2524 if not requested_v:
2525 version = Version(latest_release(url)['version'])
2526 else:
2527 try:
2528 version = Version(requested_v)
2529 except NotAFslVersion:
2530 raise InstallError(
2531 "%s doesn't look like a version" % requested_v)
2532
2533 if version > this_version:
2534 # Update Available
2535 if version.major > this_version.major:
2536 # We don't support patching between major
2537 # versions so download a fresh copy
2538 return (UPGRADE, version)
2539 else:
2540 return (UPDATE, version)
2541 else:
2542 return (CURRENT, None)
2543
2544
2545 class MakeApplicationLinksError(Exception):
2546 def __init__(self, *args):
2547 super(MakeApplicationLinksError, self).__init__(*args)
2548 try:
2549 self.app_messages = args[0]
2550 except IndexError:
2551 self.app_messages = []
2552
2553
2554 def make_applications_links(fsldir, apps):
2555 '''Create symlinks in /Applications'''
2556 MsgUser.message("Creating Application links...")
2557 results = {}
2558 for app in apps:
2559 app_location = os.path.join('/Applications', os.path.basename(app))
2560 app_target = os.path.join(fsldir, app)
2561 create_link = True
2562 MsgUser.debug("Looking for existing link %s" % (app_location))
2563 if os.path.lexists(app_location):
2564 MsgUser.debug(
2565 "Is a link: %s; realpath: %s" % (
2566 os.path.islink(app_location),
2567 os.path.realpath(app_location)))
2568 if os.path.islink(app_location):
2569 MsgUser.debug("A link already exists.")
2570 if os.path.realpath(app_location) != app_target:
2571 MsgUser.debug(
2572 "Deleting old (incorrect) link %s" % (app_location))
2573 try:
2574 run_cmd_dropstdout("rm " + app_location, as_root=True)
2575 except RunCommandError as e:
2576 MsgUser.debug(
2577 "Unable to remove broken"
2578 " link to %s (%s)." % (app_target, str(e)))
2579 results[app] = 'Unable to remove broken link to %s' % (
2580 app_target)
2581 create_link = False
2582 else:
2583 MsgUser.debug("Link is correct, skipping.")
2584 create_link = False
2585 else:
2586 MsgUser.debug(
2587 "%s doesn't look like a symlink, "
2588 "so let's not delete it." % (app_location))
2589 results[app] = (
2590 "%s is not a link so hasn't been updated to point at the "
2591 "new FSL install.") % (app_location)
2592 create_link = False
2593 if create_link:
2594 MsgUser.debug('Create a link for %s' % (app))
2595 if os.path.exists(app_target):
2596 try:
2597 run_cmd_dropstdout(
2598 "ln -s %s %s" % (app_target, app_location),
2599 as_root=True)
2600 except RunCommandError as e:
2601 MsgUser.debug(
2602 "Unable to create link to %s (%s)." % (
2603 app_target, str(e)))
2604 results[app] = (
2605 'Unable to create link to %s.') % (app_target)
2606 else:
2607 MsgUser.debug(
2608 'Unable to find application'
2609 ' %s to link to.') % (app_target)
2610 if results:
2611 raise MakeApplicationLinksError(results)
2612
2613
2614 class CheckX11Warning(Exception):
2615 pass
2616
2617
2618 def check_X11(x11):
2619 '''Function to find X11 install on Mac OS X and confirm it is compatible.
2620 Advise user to download Xquartz if necessary'''
2621
2622 MsgUser.message(
2623 "Checking for X11 windowing system (required for FSL GUIs).")
2624
2625 xbin = ''
2626
2627 for x in x11['apps']:
2628 if os.path.exists(os.path.join(x11['location'], x)):
2629 xbin = x
2630
2631 if xbin != '':
2632 # Find out what version is installed
2633 x_v_cmd = [
2634 '/usr/bin/mdls', '-name',
2635 'kMDItemVersion', os.path.join(x11['location'], xbin)]
2636 try:
2637 cmd = Popen(x_v_cmd, stdout=PIPE, stderr=STDOUT, universal_newlines=True)
2638 (vstring, _) = cmd.communicate()
2639 except Exception as e:
2640 raise CheckX11Warning(
2641 "Unable to check X11 version (%s)" % (str(e)))
2642 if cmd.returncode:
2643 MsgUser.debug("Error finding the version of X11 (%s)" % (vstring))
2644 # App found, but can't tell version, warn the user
2645 raise CheckX11Warning(
2646 "X11 (required for FSL GUIs) is installed but I"
2647 " can't tell what the version is.")
2648 else:
2649 # Returns:
2650 # kMDItemVersion = "2.3.6"\n
2651 (_, _, version) = vstring.strip().split()
2652 if version.startswith('"'):
2653 version = version[1:-1]
2654 if version in x11['bad_versions']:
2655 raise CheckX11Warning(
2656 "X11 (required for FSL GUIs) is a version that"
2657 " is known to cause problems. We suggest you"
2658 " upgrade to the latest XQuartz release from "
2659 "%s" % (x11['download_url']))
2660 else:
2661 MsgUser.debug(
2662 "X11 found and is not a bad version"
2663 " (%s: %s)." % (xbin, version))
2664 else:
2665 # No X11 found, warn the user
2666 raise CheckX11Warning(
2667 "The FSL GUIs require the X11 window system which I can't"
2668 " find in the usual places. You can download a copy from %s"
2669 " - you will need to install this before the GUIs will"
2670 " function" % (x11['download_url']))
2671
2672
2673 def do_install(options, settings):
2674 MsgUser.message(
2675 shell_colours.bold + settings.title + shell_colours.default)
2676
2677 if options.test_installer:
2678 settings.main_mirror = options.test_installer
2679
2680 this_computer = Host
2681 if not this_computer.supported:
2682 MsgUser.debug("Unsupported host %s %s %s" % (
2683 this_computer.o_s,
2684 this_computer.arch,
2685 this_computer.os_type))
2686 raise InstallError(
2687 "Unsupported host - you could try building from source")
2688
2689 if this_computer.o_s == "linux":
2690 system_environment = True
2691 with_matlab = False
2692 application_links = False
2693 x11 = False
2694 elif this_computer.o_s == "darwin":
2695 system_environment = False
2696 with_matlab = True
2697 application_links = True
2698 x11 = True
2699 else:
2700 MsgUser.debug("Unrecognised OS %s" % (this_computer.o_s))
2701 raise InstallError("Unrecognised OS")
2702
2703 my_uid = os.getuid()
2704
2705 def configure_environment(fsldir, env_all=False, skip=False, matlab=False):
2706 if skip:
2707 return
2708 if env_all:
2709 if system_environment:
2710 # Setup the system-wise environment
2711 try:
2712 setup_system_environment(fsldir)
2713 except SetupEnvironmentError as e:
2714 MsgUser.debug(str(e))
2715 MsgUser.failed(
2716 "Failed to configure system-wide profiles "
2717 "with FSL settings: %s" % (str(e)))
2718 except SetupEnvironmentSkip as e:
2719 MsgUser.skipped(
2720 "Some shells already configured: %s" % (str(e)))
2721 else:
2722 MsgUser.debug("System-wide profiles setup.")
2723 MsgUser.ok("System-wide FSL configuration complete.")
2724 else:
2725 MsgUser.skipped(
2726 "System-wide profiles not supported on this OS")
2727 elif my_uid != 0:
2728 # Setup the environment for the current user
2729 try:
2730 setup_environment(fsldir, with_matlab=matlab)
2731 except SetupEnvironmentError as e:
2732 MsgUser.debug(str(e))
2733 MsgUser.failed(str(e))
2734 else:
2735 MsgUser.ok(
2736 "User profile updated with FSL settings, you will need "
2737 "to log out and back in to use the FSL tools.")
2738
2739 if my_uid != 0:
2740 if options.quiet:
2741 settings.inst_qus.defaults = True
2742 print('''
2743 We may need administrator rights, but you have specified fully automated
2744 mode - you may still be asked for an admin password if required.''')
2745 print('''
2746 To install fully automatedly, either ensure this is running as the root
2747 user (use sudo) or that you can write to the folder you wish to install
2748 FSL in.''')
2749 elif (not options.download and
2750 not options.list_versions and
2751 not options.list_builds and
2752 not options.get_source and
2753 not options.get_feeds):
2754 MsgUser.warning(
2755 '''Some operations of the installer require administative rights,
2756 for example installing into the default folder of /usr/local.
2757 If your account is an 'Administrator' (you have 'sudo' rights)
2758 then you will be prompted for your administrator password
2759 when necessary.''')
2760 if not options.d_dir and options.quiet:
2761 raise InstallError(
2762 "Quiet mode requires you to specify the install location"
2763 " (e.g. /usr/local)")
2764 if not options.quiet and not (options.list_versions or options.list_builds):
2765 MsgUser.message(
2766 "When asked a question, the default answer is given in square "
2767 "brackets.\nHit the Enter key to accept this default answer.")
2768 if options.env_only and my_uid != 0:
2769 configure_environment(
2770 get_fsldir(specified_dir=options.d_dir),
2771 options.env_all)
2772 return
2773 if options.archive:
2774 if not options.skipchecksum:
2775 if not options.checksum:
2776 raise InstallError(
2777 "No checksum provided and checking not disabled")
2778 else:
2779 checksummer = globals()[options.checksum_type + 'File']
2780 if options.checksum != checksummer(options.archive):
2781 raise InstallError("FSL archive doesn't match checksum")
2782 else:
2783 MsgUser.ok("FSL Package looks good")
2784 arc_version = archive_version(options.archive)
2785 MsgUser.message(
2786 "Installing FSL software version %s..." % (arc_version))
2787
2788 fsldir = install_archive(
2789 archive=options.archive, fsldir=options.d_dir)
2790 try:
2791 post_install(fsldir=fsldir, settings=settings, quiet=options.quiet)
2792 except PostInstallError as e:
2793 raise InstallError(str(e))
2794 configure_environment(
2795 fsldir=fsldir, env_all=options.env_all,
2796 skip=options.skip_env, matlab=with_matlab)
2797 return
2798
2799 # All the following options require the Internet...
2800 try:
2801 settings.mirror = fastest_mirror(
2802 settings.mirrors, settings.mirrors_file)
2803 except SiteNotResponding as e:
2804 # We can't find the FSL site - possibly the internet is down
2805 raise InstallError(e)
2806
2807 try:
2808 self_update(settings.mirror)
2809 except SelfUpdateError as e:
2810 MsgUser.debug("Self update error: %s" % (str(e)))
2811 MsgUser.warning("Error checking for updates to installer - continuing")
2812 if options.list_versions:
2813 # Download a list of available downloads from the webserver
2814 list_releases(settings.mirror)
2815 return
2816 if options.list_builds:
2817 # List all available builds
2818 list_builds(settings.mirror)
2819 return
2820
2821 if options.download:
2822 MsgUser.debug("Attempting to download latest release")
2823 try:
2824 download_release(settings.mirror, request_version=options.requestversion,
2825 skip_verify=options.skipchecksum)
2826 except DownloadFileError as e:
2827 raise "Unable to download release %s"
2828 return
2829
2830 if options.update:
2831 fsldir = get_fsldir()
2832 status, new_v = check_for_updates(settings.mirror, fsldir=fsldir)
2833 if status == UPDATE:
2834 MsgUser.ok("Version %s available." % new_v)
2835 if not settings.inst_qus.ask_question('update'):
2836 return
2837 elif status == UPGRADE:
2838 MsgUser.ok("Version %s available." % new_v)
2839 if not settings.inst_qus.ask_question('upgrade'):
2840 return
2841 else:
2842 MsgUser.ok("FSL is up-to-date.")
2843 return
2844
2845 if options.get_source:
2846 MsgUser.debug("Attempting to download source")
2847 try:
2848 download_release(
2849 settings.mirror,
2850 request_version=options.requestversion,
2851 skip_verify=options.skipchecksum,
2852 source_code=True)
2853 except DownloadFileError as e:
2854 raise "Unable to download source code %s"
2855 return
2856
2857 if options.get_feeds:
2858 MsgUser.debug("Attempting to download FEEDS")
2859 try:
2860 download_release(
2861 settings.mirror,
2862 request_version=options.requestversion,
2863 skip_verify=options.skipchecksum,
2864 feeds=True)
2865 except DownloadFileError as e:
2866 raise "Unable to download FEEDS %s"
2867 return
2868
2869 try:
2870 (version, details) = get_web_version_and_details(
2871 Settings.mirror,
2872 request_version=options.requestversion)
2873 if 'redirect' in details:
2874 MsgUser.message("Please download FSL using the instructions here:")
2875 MsgUser.message("%s" % (details['redirect']))
2876 return
2877
2878 fsldir = get_fsldir(specified_dir=options.d_dir, install=True)
2879 reinstall = True
2880 if os.path.exists(fsldir):
2881 inst_version = get_installed_version(fsldir)
2882 if inst_version == version:
2883 reinstall = Settings.inst_qus.ask_question('version_match')
2884 if reinstall:
2885 (fname, version, details) = download_release(
2886 Settings.mirror,
2887 to_temp=True,
2888 request_version=options.requestversion,
2889 skip_verify=options.skipchecksum)
2890 if not details['supported']:
2891 MsgUser.debug(
2892 "This OS is not officially supported -"
2893 " you may experience issues"
2894 )
2895 MsgUser.debug(
2896 "Installing %s from %s (details: %s)" % (
2897 fname, version, details))
2898 MsgUser.message(
2899 "Installing FSL software version %s..." % (version))
2900 install_archive(
2901 archive=fname, fsldir=fsldir)
2902 try:
2903 safe_delete(fname)
2904 except SafeDeleteError as e:
2905 MsgUser.debug(
2906 "Unable to delete downloaded package %s ; %s" % (
2907 fname, str(e)))
2908 if details['notes']:
2909 MsgUser.message(details['notes'])
2910 try:
2911 post_install(
2912 fsldir=fsldir, settings=settings,
2913 quiet=options.quiet, x11=x11,
2914 app_links=application_links)
2915 except PostInstallError as e:
2916 raise InstallError(str(e))
2917
2918 except DownloadError as e:
2919 MsgUser.debug("Unable to download FSL %s" % (str(e)))
2920 raise InstallError("Unable to download FSL")
2921 except InstallArchiveError as e:
2922 MsgUser.debug("Unable to unpack FSL ; %s" % (str(e)))
2923 raise InstallError("Unable to unpack FSL - %s" % (str(e)))
2924
2925 configure_environment(
2926 fsldir=fsldir, env_all=options.env_all,
2927 skip=options.skip_env, matlab=with_matlab)
2928
2929 if details['notes']:
2930 MsgUser.message(details['notes'])
2931
2932
2933 def parse_options(args):
2934 usage = "usage: %prog [options]"
2935 ver = "%%prog %s" % (version)
2936 parser = OptionParser(usage=usage, version=ver)
2937 parser.add_option("-d", "--dest", dest="d_dir",
2938 help="Install into folder given by DESTDIR - "
2939 "e.g. /usr/local/fsl",
2940 metavar="DESTDIR", action="store",
2941 type="string")
2942 parser.add_option("-e", dest="env_only",
2943 help="Only setup/update your environment",
2944 action="store_true")
2945 parser.add_option("-E", dest="env_all",
2946 help="Setup/update the environment for ALL users",
2947 action="store_true")
2948 parser.add_option("-v", help="Print version number and exit",
2949 action="version")
2950 parser.add_option("-c", "--checkupdate", dest='update',
2951 help="Check for FSL updates -"
2952 " needs an internet connection",
2953 action="store_true")
2954 parser.add_option("-o", "--downloadonly", dest="download",
2955 help=SUPPRESS_HELP,
2956 action="store_true")
2957
2958 advanced_group = OptionGroup(
2959 parser, "Advanced Install Options",
2960 "These are advanced install options")
2961 advanced_group.add_option(
2962 "-l", "--listversions", dest="list_versions",
2963 help="List available versions of FSL",
2964 action="store_true")
2965 advanced_group.add_option(
2966 "-b", "--listbuilds", dest="list_builds",
2967 help="List available FSL builds",
2968 action="store_true")
2969 advanced_group.add_option(
2970 "-B", "--fslbuild", dest="requestbuild",
2971 help="Download the specific FSLBUILD of FSL",
2972 metavar="FSLBUILD", action="store",
2973 type="string")
2974 advanced_group.add_option(
2975 "-V", "--fslversion", dest="requestversion",
2976 help="Download the specific version FSLVERSION of FSL",
2977 metavar="FSLVERSION", action="store",
2978 type="string")
2979 advanced_group.add_option(
2980 "-s", "--source", dest="get_source",
2981 help="Download source code for FSL",
2982 action="store_true")
2983 advanced_group.add_option(
2984 "-F", "--feeds", dest="get_feeds",
2985 help="Download FEEDS",
2986 action="store_true")
2987 advanced_group.add_option(
2988 "-q", "--quiet", dest='quiet',
2989 help="Silence all messages - useful if scripting install",
2990 action="store_true")
2991 advanced_group.add_option(
2992 "-p", dest="skip_env",
2993 help="Don't setup the environment",
2994 action="store_true")
2995 parser.add_option_group(advanced_group)
2996
2997 debug_group = OptionGroup(
2998 parser, "Debugging Options",
2999 "These are for use if you have a problem running this installer.")
3000 debug_group.add_option(
3001 "-f", "--file", dest="archive",
3002 help="Install a pre-downloaded copy of the FSL archive",
3003 metavar="ARCHIVEFILE", action="store",
3004 type="string")
3005 debug_group.add_option(
3006 "-C", "--checksum", dest="checksum",
3007 help="Supply the expected checksum for the pre-downloaded FSL archive",
3008 metavar="CHECKSUM", action="store",
3009 type="string")
3010 debug_group.add_option(
3011 "-T", "--checksum-type", dest="checksum_type",
3012 default="sha256",
3013 help="Specify the type of checksum",
3014 action="store",
3015 type="string")
3016 debug_group.add_option(
3017 "-M", "--nochecksum", dest="skipchecksum",
3018 help="Don't check the pre-downloaded FSL archive",
3019 action="store_true")
3020 debug_group.add_option(
3021 "-D", dest="verbose",
3022 help="Switch on debug messages",
3023 action="store_true")
3024 debug_group.add_option(
3025 "-G", dest="test_installer",
3026 help=SUPPRESS_HELP,
3027 action="store",
3028 type="string")
3029 parser.add_option_group(debug_group)
3030 return parser.parse_args(args)
3031
3032
3033 def override_host(requestbuild):
3034 '''Overrides attributes of the Host class in the event that the user
3035 has requested a specific FSL build.
3036 '''
3037 if requestbuild == 'centos7_64':
3038 Host.o_s = 'linux'
3039 Host.arch = 'x86_64'
3040 Host.vendor = 'centos'
3041 Host.version = Version('7.8.2003')
3042 Host.glibc = '2.2.5'
3043 Host.supported = True
3044 Host.bits = '64'
3045 elif requestbuild == 'centos6_64':
3046 Host.o_s = 'linux'
3047 Host.arch = 'x86_64'
3048 Host.vendor = 'centos'
3049 Host.version = Version('6.10')
3050 Host.glibc = '2.2.5'
3051 Host.supported = True
3052 Host.bits = '64'
3053 elif requestbuild == 'macOS_64':
3054 Host.o_s = 'darwin'
3055 Host.arch = 'x86_64'
3056 Host.vendor = 'apple'
3057 Host.version = Version('19.6.0')
3058 Host.glibc = ''
3059 Host.supported = True
3060 Host.bits = '64'
3061 # Download x86 version if running on Apple
3062 # M1, as it runs just fine under emulation
3063 elif (requestbuild is None and
3064 Host.o_s == 'darwin' and
3065 Host.arch == 'arm64'):
3066 Host.arch = 'x86_64'
3067
3068
3069 def main(argv=None):
3070 if argv is None:
3071 argv = sys.argv[1:]
3072 (options, args) = parse_options(argv)
3073 if options.verbose:
3074 MsgUser.debugOn()
3075 print(options)
3076 if options.quiet:
3077 MsgUser.quietOn()
3078 override_host(options.requestbuild)
3079
3080 installer_settings = Settings()
3081 try:
3082 do_install(options, installer_settings)
3083 except BadVersion as e:
3084 MsgUser.debug(str(e))
3085 MsgUser.failed("Unable to find requested version!")
3086 sys.exit(1)
3087 except (InstallError, GetFslDirError, GetInstalledVersionError) as e:
3088 MsgUser.failed(str(e))
3089 sys.exit(1)
3090 except UnsupportedOs as e:
3091 MsgUser.failed(str(e))
3092 sys.exit(1)
3093 except KeyboardInterrupt as e:
3094 MsgUser.message('')
3095 MsgUser.failed("Install aborted.")
3096 sys.exit(1)
3097
3098
3099 if __name__ == '__main__':
3100 main()
Attached Files
To refer to attachments on a page, use attachment:filename, as shown below in the list of files. Do NOT use the URL of the [get] link, since this is subject to change and can break easily.You are not allowed to attach a file to this page.