Bug 1286799 - mozboot: Install or upgrade rust. r?gps
Download and run a known-good rustup-init installer for the host
system. Once installed, use it to upgrade the latest toolchain.
NB: I expect the MozillaBuildBootstrapper to run its installer
first, but this will take care of Mac, Linux, and FreeBSD.
MozReview-Commit-ID: BKDm1UcLxQS
--- a/python/mozboot/mozboot/base.py
+++ b/python/mozboot/mozboot/base.py
@@ -7,17 +7,17 @@ from __future__ import print_function, u
import hashlib
import os
import re
import subprocess
import sys
import urllib2
from distutils.version import LooseVersion
-
+from mozboot import rust
NO_MERCURIAL = '''
Could not find Mercurial (hg) in the current shell's path. Try starting a new
shell and running the bootstrapper again.
'''
MERCURIAL_UNABLE_UPGRADE = '''
You are currently running Mercurial %s. Running %s or newer is
@@ -92,16 +92,34 @@ and upgrade Rust programming language su
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.
'''
+RUST_UPGRADE_FAILED = '''
+We attempted to upgrade Rust to a modern version (%s or newer).
+However, you appear to still have version %s.
+
+It's possible rustup failed. It's also possible the new Rust is not being
+installed in the search path for this shell. Try creating a new shell and
+run this bootstrapper again.
+
+If this continues to fail and you are sure you have a modern Rust on your
+system, ensure it is on the $PATH and try again. If that fails, you'll need to
+install Rust manually and ensure the path with the rustc and cargo binaries
+are listed in the $PATH environment variable.
+
+We recommend the installer from https://rustup.rs/ for installing Rust,
+but you may be able to get a recent enough version from a software install
+tool or package manager on your system, or directly from https://rust-lang.org/
+'''
+
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
>>>
'''
@@ -476,19 +494,19 @@ 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.')
+ rustc = self.which('rustc')
+ if not rustc:
+ print('Could not find a Rust compiler.')
return False, None
cargo = self.which('cargo')
our = self._parse_version(rust)
if not our:
return False, None
@@ -497,46 +515,91 @@ class BaseBootstrapper(object):
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.'''
+ # Rust wasn't in PATH. Try the standard path.
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.
+ rustup = self.which('rustup')
+ if rustup:
+ rustup_version = self._parse_version(rustup)
+ if not rustup_version:
+ print(RUSTUP_OLD)
+ sys.exit(1)
+ if not version:
+ # We have rustup but no rustc.
+ # Try running rustup; maybe it will fix things.
+ print('Found rustup. Will try to upgrade.')
+ else:
+ # We have both rustup and rustc.
+ print('Your version of Rust (%s) is too old. Will try to upgrade.' %
+ version)
+ self.upgrade_rust(rustup)
+ else:
+ # No rustc or rustup.
+ print('Will try to install Rust.')
+ self.install_rust()
modern, after = self.is_rust_modern()
if not modern:
print(RUST_UPGRADE_FAILED % (MODERN_RUST_VERSION, after))
sys.exit(1)
+ def upgrade_rust(self, rustup):
+ """Upgrade Rust.
+
+ Invoke rustup from the given path to update the rust install."""
+ subprocess.check_call([rustup, 'update'])
+
+ def install_rust(self):
+ """Download and run the rustup installer."""
+ import errno
+ import stat
+ import tempfile
+ platform = rust.platform()
+ url = rust.rustup_url(platform)
+ checksum = rust.rustup_hash(platform)
+ if not url or not checksum:
+ print('ERROR: Could not download installer.')
+ sys.exit(1)
+ print('Downloading rustup-init... ', end='')
+ fd, rustup_init = tempfile.mkstemp(prefix=os.path.basename(url))
+ os.close(fd)
+ try:
+ self.http_download_and_save(url, rustup_init, checksum)
+ mode = os.stat(rustup_init).st_mode
+ os.chmod(rustup_init, mode | stat.S_IRWXU)
+ print('Ok')
+ print('Running rustup-init...')
+ subprocess.check_call([rustup_init, '-y',
+ '--default-toolchain', 'stable',
+ '--default-host', platform,
+ ])
+ print('Rust installation complete.')
+ finally:
+ try:
+ os.remove(rustup_init)
+ except OSError as e:
+ if e.errno != errno.ENOENT:
+ raise
+
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)
new file mode 100644
--- /dev/null
+++ b/python/mozboot/mozboot/rust.py
@@ -0,0 +1,69 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this,
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+# If we add unicode_literals, Python 2.6.1 (required for OS X 10.6) breaks.
+from __future__ import print_function
+
+import errno
+import os
+import stat
+import subprocess
+import sys
+
+# Base url for pulling the rustup installer.
+RUSTUP_URL_BASE = 'https://static.rust-lang.org/rustup'
+
+# Pull this to get the lastest stable version number.
+RUSTUP_MANIFEST = os.path.join(RUSTUP_URL_BASE, 'release-stable.toml')
+
+# We bake in a known version number so we can verify a checksum.
+RUSTUP_VERSION = '0.6.5'
+
+# SHA-256 checksums of the installers, per platform.
+RUSTUP_HASHES = {
+ 'x86_64-apple-darwin':
+ '6404ab0a92c1559bac279a20d31be9166c91434f8e7ff8d1a97bcbe4dbd3cadc',
+ 'x86_64-pc-windows-msvc':
+ '772579edcbc9a480a61fb19ace49527839e7f919e1041bcc2dee2a4ff82d3ca2',
+ 'x86_64-unknown-linux-gnu':
+ 'e901e23ee48c3a24470d997c4376d8835cecca51bf2636dcd419821d4056d823',
+ 'x86_64-unknown-freebsd':
+ '63b7c0f35a811993c94af85b96abdd3dcca847d260af284f888e91c2ffdb374e',
+}
+
+NO_PLATFORM = '''
+Sorry, we have no installer configured for your platform.
+
+Please try installing rust for your system from https://rustup.rs/
+or from https://rust-lang.org/ or from your package manager.
+'''
+
+def rustup_url(host, version=RUSTUP_VERSION):
+ '''Download url for a particular version of the installer.'''
+ ext = ''
+ if 'windows' in host:
+ ext = '.exe'
+ return os.path.join(RUSTUP_URL_BASE,
+ 'archive/%(version)s/%(host)s/rustup-init%(ext)s') % {
+ 'version': version,
+ 'host': host,
+ 'ext': ext}
+
+def rustup_hash(host):
+ '''Look up the checksum for the given installer.'''
+ return RUSTUP_HASHES.get(host, None)
+
+def platform():
+ '''Determine the appropriate rust platform string for the current host'''
+ if sys.platform.startswith('darwin'):
+ return 'x86_64-apple-darwin'
+ elif sys.platform.startswith(('win32', 'msys')):
+ # Bravely assume we'll be building 64-bit Firefox.
+ return 'x86_64-pc-windows-msvc'
+ elif sys.platform.startswith('linux'):
+ return 'x86_64-unknown-linux-gnu'
+ elif sys.platform.startswith('freebsd'):
+ return 'x86_64-unknown-freebsd'
+
+ return None