python/mozboot/mozboot/base.py

Thu, 15 Jan 2015 21:13:52 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 15 Jan 2015 21:13:52 +0100
branch
TOR_BUG_9701
changeset 12
7540298fafa1
permissions
-rw-r--r--

Remove forgotten relic of ABI crash risk averse overloaded method change.

     1 # This Source Code Form is subject to the terms of the Mozilla Public
     2 # License, v. 2.0. If a copy of the MPL was not distributed with this file,
     3 # You can obtain one at http://mozilla.org/MPL/2.0/.
     5 from __future__ import print_function, unicode_literals
     7 import os
     8 import re
     9 import subprocess
    10 import sys
    12 from distutils.version import StrictVersion
    15 NO_MERCURIAL = '''
    16 Could not find Mercurial (hg) in the current shell's path. Try starting a new
    17 shell and running the bootstrapper again.
    18 '''
    20 MERCURIAL_UNABLE_UPGRADE = '''
    21 You are currently running Mercurial %s. Running %s or newer is
    22 recommended for performance and stability reasons.
    24 Unfortunately, this bootstrapper currently does not know how to automatically
    25 upgrade Mercurial on your machine.
    27 You can usually install Mercurial through your package manager or by
    28 downloading a package from http://mercurial.selenic.com/.
    29 '''
    31 MERCURIAL_UPGRADE_FAILED = '''
    32 We attempted to upgrade Mercurial to a modern version (%s or newer).
    33 However, you appear to have version %s still.
    35 It's possible your package manager doesn't support a modern version of
    36 Mercurial. It's also possible Mercurial is not being installed in the search
    37 path for this shell. Try creating a new shell and run this bootstrapper again.
    39 If it continues to fail, consider installing Mercurial by following the
    40 instructions at http://mercurial.selenic.com/.
    41 '''
    43 PYTHON_UNABLE_UPGRADE = '''
    44 You are currently running Python %s. Running %s or newer (but
    45 not 3.x) is required.
    47 Unfortunately, this bootstrapper does not currently know how to automatically
    48 upgrade Python on your machine.
    50 Please search the Internet for how to upgrade your Python and try running this
    51 bootstrapper again to ensure your machine is up to date.
    52 '''
    54 PYTHON_UPGRADE_FAILED = '''
    55 We attempted to upgrade Python to a modern version (%s or newer).
    56 However, you appear to still have version %s.
    58 It's possible your package manager doesn't yet expose a modern version of
    59 Python. It's also possible Python is not being installed in the search path for
    60 this shell. Try creating a new shell and run this bootstrapper again.
    62 If this continues to fail and you are sure you have a modern Python on your
    63 system, ensure it is on the $PATH and try again. If that fails, you'll need to
    64 install Python manually and ensure the path with the python binary is listed in
    65 the $PATH environment variable.
    67 We recommend the following tools for installing Python:
    69     pyenv   -- https://github.com/yyuu/pyenv)
    70     pythonz -- https://github.com/saghul/pythonz
    71     official installers -- http://www.python.org/
    72 '''
    75 # Upgrade Mercurial older than this.
    76 MODERN_MERCURIAL_VERSION = StrictVersion('2.5')
    78 # Upgrade Python older than this.
    79 MODERN_PYTHON_VERSION = StrictVersion('2.7.3')
    82 class BaseBootstrapper(object):
    83     """Base class for system bootstrappers."""
    85     def __init__(self):
    86         self.package_manager_updated = False
    88     def install_system_packages(self):
    89         raise NotImplemented('%s must implement install_system_packages()' %
    90             __name__)
    92     def which(self, name):
    93         """Python implementation of which.
    95         It returns the path of an executable or None if it couldn't be found.
    96         """
    97         for path in os.environ['PATH'].split(os.pathsep):
    98             test = os.path.join(path, name)
    99             if os.path.exists(test) and os.access(test, os.X_OK):
   100                 return test
   102         return None
   104     def run_as_root(self, command):
   105         if os.geteuid() != 0:
   106             if self.which('sudo'):
   107                 command.insert(0, 'sudo')
   108             else:
   109                 command = ['su', 'root', '-c', ' '.join(command)]
   111         print('Executing as root:', subprocess.list2cmdline(command))
   113         subprocess.check_call(command, stdin=sys.stdin)
   115     def yum_install(self, *packages):
   116         command = ['yum', 'install']
   117         command.extend(packages)
   119         self.run_as_root(command)
   121     def yum_groupinstall(self, *packages):
   122         command = ['yum', 'groupinstall']
   123         command.extend(packages)
   125         self.run_as_root(command)
   127     def yum_update(self, *packages):
   128         command = ['yum', 'update']
   129         command.extend(packages)
   131         self.run_as_root(command)
   133     def apt_install(self, *packages):
   134         command = ['apt-get', 'install']
   135         command.extend(packages)
   137         self.run_as_root(command)
   139     def apt_update(self):
   140         command = ['apt-get', 'update']
   142         self.run_as_root(command)
   144     def apt_add_architecture(self, arch):
   145         command = ['dpkg', '--add-architecture']
   146         command.extend(arch)
   148         self.run_as_root(command)
   150     def check_output(self, *args, **kwargs):
   151         """Run subprocess.check_output even if Python doesn't provide it."""
   152         fn = getattr(subprocess, 'check_output', BaseBootstrapper._check_output)
   154         return fn(*args, **kwargs)
   156     @staticmethod
   157     def _check_output(*args, **kwargs):
   158         """Python 2.6 compatible implementation of subprocess.check_output."""
   159         proc = subprocess.Popen(stdout=subprocess.PIPE, *args, **kwargs)
   160         output, unused_err = proc.communicate()
   161         retcode = proc.poll()
   162         if retcode:
   163             cmd = kwargs.get('args', args[0])
   164             e = subprocess.CalledProcessError(retcode, cmd)
   165             e.output = output
   166             raise e
   168         return output
   170     def prompt_int(self, prompt, low, high, limit=5):
   171         ''' Prompts the user with prompt and requires an integer between low and high. '''
   172         valid = False
   173         while not valid and limit > 0:
   174             try:
   175                 choice = int(raw_input(prompt))
   176                 if not low <= choice <= high:
   177                     print("ERROR! Please enter a valid option!")
   178                     limit -= 1
   179                 else:
   180                     valid = True
   181             except ValueError:
   182                 print("ERROR! Please enter a valid option!")
   183                 limit -= 1
   185         if limit > 0:
   186             return choice
   187         else:
   188             raise Exception("Error! Reached max attempts of entering option.")
   190     def _ensure_package_manager_updated(self):
   191         if self.package_manager_updated:
   192             return
   194         self._update_package_manager()
   195         self.package_manager_updated = True
   197     def _update_package_manager(self):
   198         """Updates the package manager's manifests/package list.
   200         This should be defined in child classes.
   201         """
   203     def _hgplain_env(self):
   204         """ Returns a copy of the current environment updated with the HGPLAIN
   205         environment variable.
   207         HGPLAIN prevents Mercurial from applying locale variations to the output
   208         making it suitable for use in scripts.
   209         """
   210         env = os.environ.copy()
   211         env['HGPLAIN'] = '1'
   212         return env
   214     def is_mercurial_modern(self):
   215         hg = self.which('hg')
   216         if not hg:
   217             print(NO_MERCURIAL)
   218             return False, False, None
   220         info = self.check_output([hg, '--version'], env=self._hgplain_env()).splitlines()[0]
   222         match = re.search('version ([^\+\)]+)', info)
   223         if not match:
   224             print('ERROR: Unable to identify Mercurial version.')
   225             return True, False, None
   227         our = StrictVersion(match.group(1))
   229         return True, our >= MODERN_MERCURIAL_VERSION, our
   231     def ensure_mercurial_modern(self):
   232         installed, modern, version = self.is_mercurial_modern()
   234         if not installed or modern:
   235             print('Your version of Mercurial (%s) is sufficiently modern.' %
   236                 version)
   237             return
   239         self._ensure_package_manager_updated()
   240         self.upgrade_mercurial(version)
   242         installed, modern, after = self.is_mercurial_modern()
   244         if installed and not modern:
   245             print(MERCURIAL_UPGRADE_FAILED % (MODERN_MERCURIAL_VERSION, after))
   247     def upgrade_mercurial(self, current):
   248         """Upgrade Mercurial.
   250         Child classes should reimplement this.
   251         """
   252         print(MERCURIAL_UNABLE_UPGRADE % (current, MODERN_MERCURIAL_VERSION))
   254     def is_python_modern(self):
   255         python = None
   257         for test in ['python2.7', 'python']:
   258             python = self.which(test)
   259             if python:
   260                 break
   262         assert python
   264         info = self.check_output([python, '--version'],
   265             stderr=subprocess.STDOUT)
   266         match = re.search('Python ([a-z0-9\.]+)', info)
   267         if not match:
   268             print('ERROR Unable to identify Python version.')
   269             return False, None
   271         our = StrictVersion(match.group(1))
   273         return our >= MODERN_PYTHON_VERSION, our
   275     def ensure_python_modern(self):
   276         modern, version = self.is_python_modern()
   278         if modern:
   279             print('Your version of Python (%s) is new enough.' % version)
   280             return
   282         print('Your version of Python (%s) is too old. Will try to upgrade.' %
   283             version)
   285         self._ensure_package_manager_updated()
   286         self.upgrade_python(version)
   288         modern, after = self.is_python_modern()
   290         if not modern:
   291             print(PYTHON_UPGRADE_FAILED % (MODERN_PYTHON_VERSION, after))
   292             sys.exit(1)
   294     def upgrade_python(self, current):
   295         """Upgrade Python.
   297         Child classes should reimplement this.
   298         """
   299         print(PYTHON_UNABLE_UPGRADE % (current, MODERN_PYTHON_VERSION))

mercurial