Bug 1286799 - mozboot: Ensure an up-to-date rust toolchain. r=gps draft
authorRalph Giles <giles@mozilla.com>
Wed, 16 Nov 2016 09:27:32 -0800
changeset 442122 7e3b93919bbd859d6c6ca63aaf659c8f1f4844ba
parent 442121 006d347050789dd4fdd370a1267d17067365cd66
child 442123 c434b103a699b5a9b1e3ae42c91efa45d5e928ca
child 442188 75cf121d2dff55c21de7a4ee5e08efecbb6c2c1a
push id36589
push userbmo:giles@thaumas.net
push dateMon, 21 Nov 2016 19:39:40 +0000
reviewersgps
bugs1286799
milestone53.0a1
Bug 1286799 - mozboot: Ensure an up-to-date rust toolchain. r=gps Add a check to `mach bootstrap` that a compatible version of the rust toolchain is installed and in path. Note that we use a separate minimum version from the one checked at configure time, defined in build/moz.configure/rust.configure, because this script may be running stand-alone. If we don't find `rustc` in PATH, we check for it in CARGO_HOME and suggest adding that to PATH. If we don't find `rustc` but find a `rustup`, we call it with the --version switch and report if that fails. This will detect the older `rustup.rs` script. MozReview-Commit-ID: EPfQhLz4Dvo
python/mozboot/mozboot/base.py
python/mozboot/mozboot/bootstrap.py
--- a/python/mozboot/mozboot/base.py
+++ b/python/mozboot/mozboot/base.py
@@ -68,16 +68,40 @@ the $PATH environment variable.
 
 We recommend the following tools for installing Python:
 
     pyenv   -- https://github.com/yyuu/pyenv)
     pythonz -- https://github.com/saghul/pythonz
     official installers -- http://www.python.org/
 '''
 
+RUST_NOT_IN_PATH = '''
+You have some rust files in %(cargo_bin)s, but they're not part of the
+standard PATH.
+
+To make these available, please add this directory to the PATH variable
+in your shell initialization script, which may be called ~/.bashrc or
+~/.bash_profile or ~/.profile. Edit this and add the following line:
+
+    source %(cargo_home)s/env
+
+Then restart your shell and run the bootstrap script again.
+'''
+
+RUSTUP_OLD = '''
+We found an executable called `rustup` which we normally use to install
+and upgrade Rust programming language support, but we didn't understand
+its output. It may be an old version, or not be the installer from
+https://rustup.rs/
+
+Please move it out of the way and run the bootstrap script again.
+Or if you prefer and know how, use the current rustup to install
+a compatible version of the Rust programming language yourself.
+'''
+
 BROWSER_ARTIFACT_MODE_MOZCONFIG = '''
 Paste the lines between the chevrons (>>> and <<<) into your mozconfig file:
 
 <<<
 # Automatically download and use compiled C++ components:
 ac_add_options --enable-artifact-builds
 >>>
 '''
@@ -85,16 +109,18 @@ ac_add_options --enable-artifact-builds
 # Upgrade Mercurial older than this.
 # This should match OLDEST_NON_LEGACY_VERSION from
 # the hg setup wizard in version-control-tools.
 MODERN_MERCURIAL_VERSION = LooseVersion('3.7.3')
 
 # Upgrade Python older than this.
 MODERN_PYTHON_VERSION = LooseVersion('2.7.3')
 
+# Upgrade rust older than this.
+MODERN_RUST_VERSION = LooseVersion('1.13.0')
 
 class BaseBootstrapper(object):
     """Base class for system bootstrappers."""
 
     def __init__(self, no_interactive=False):
         self.package_manager_updated = False
         self.no_interactive = no_interactive
 
@@ -449,16 +475,68 @@ class BaseBootstrapper(object):
 
     def upgrade_python(self, current):
         """Upgrade Python.
 
         Child classes should reimplement this.
         """
         print(PYTHON_UNABLE_UPGRADE % (current, MODERN_PYTHON_VERSION))
 
+    def is_rust_modern(self):
+        rust = self.which('rustc')
+        if not rust:
+            print('Could not find rust compiler.')
+            return False, None
+
+        cargo = self.which('cargo')
+
+        our = self._parse_version(rust)
+        if not our:
+            return False, None
+
+        return our >= MODERN_RUST_VERSION, our
+
+    def ensure_rust_modern(self):
+        modern, version = self.is_rust_modern()
+
+        if modern:
+            print('Your version of Rust (%s) is new enough.' % version)
+            return
+
+        if not version:
+            '''Rust wasn't in PATH. Try a few things.'''
+            cargo_home = os.environ.get('CARGO_HOME',
+                    os.path.expanduser(os.path.join('~', '.cargo')))
+            cargo_bin = os.path.join(cargo_home, 'bin')
+            have_rustc = os.path.exists(os.path.join(cargo_bin, 'rustc'))
+            have_cargo = os.path.exists(os.path.join(cargo_bin, 'cargo'))
+            if have_rustc or have_cargo:
+                print(RUST_NOT_IN_PATH % { 'cargo_bin': cargo_bin,
+                                           'cargo_home': cargo_home })
+                sys.exit(1)
+
+            rustup = self.which('rustup')
+            if rustup:
+                print('Found rustup.')
+                version = self._parse_version(rustup)
+                if not version:
+                    print(RUSTUP_OLD)
+                    sys.exit(1)
+
+            print('Please download and run the installer from https://rustup.rs/')
+            sys.exit(1)
+
+        # TODO: Upgrade rust.
+
+        modern, after = self.is_rust_modern()
+
+        if not modern:
+            print(RUST_UPGRADE_FAILED % (MODERN_RUST_VERSION, after))
+            sys.exit(1)
+
     def http_download_and_save(self, url, dest, sha256hexhash):
         f = urllib2.urlopen(url)
         h = hashlib.sha256()
         with open(dest, 'wb') as out:
             while True:
                 data = f.read(4096)
                 if data:
                     out.write(data)
--- a/python/mozboot/mozboot/bootstrap.py
+++ b/python/mozboot/mozboot/bootstrap.py
@@ -224,16 +224,17 @@ class Bootstrapper(object):
 
         self.instance.install_system_packages()
 
         # Like 'install_browser_packages' or 'install_mobile_android_packages'.
         getattr(self.instance, 'install_%s_packages' % application)()
 
         hg_installed, hg_modern = self.instance.ensure_mercurial_modern()
         self.instance.ensure_python_modern()
+        self.instance.ensure_rust_modern()
 
         # The state directory code is largely duplicated from mach_bootstrap.py.
         # We can't easily import mach_bootstrap.py because the bootstrapper may
         # run in self-contained mode and only the files in this directory will
         # be available. We /could/ refactor parts of mach_bootstrap.py to be
         # part of this directory to avoid the code duplication.
         state_dir, _ = get_state_dir()