diff --git a/.travis.yml b/.travis.yml index d1f71944887d..3bf77333954e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -12,10 +12,10 @@ matrix: dist: trusty script: - ./mach build -d --verbose + - ./mach test-compiletest + - ./mach test-unit - ./mach build-geckolib - ./mach test-geckolib - - ./mach test-unit - - ./mach test-compiletest - bash etc/ci/check_no_unwrap.sh - bash etc/ci/lockfile_changed.sh - bash etc/ci/manifest_changed.sh @@ -28,18 +28,7 @@ matrix: addons: apt: packages: - - cmake - - freeglut3-dev - - gperf - - libosmesa6-dev - - libgles2-mesa-dev - - python-virtualenv - - xorg-dev - ccache - - libdbus-glib-1-dev - - libavformat-dev - - libavcodec-dev - - libavutil-dev branches: only: diff --git a/appveyor.yml b/appveyor.yml index f75c29e6ed32..a313b572d5bd 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -2,7 +2,6 @@ version: 1.0.{build} environment: RUST_BACKTRACE: 1 - HOME: '%APPVEYOR_BUILD_FOLDER%' # The appveyor image we use has a pretty huge set of things installed... we make the # initial PATH something sane so we know what to expect PATH: "C:\\windows\\system32;\ @@ -69,6 +68,6 @@ install: build_script: - if %BUILD_ENV%==msvc mach build -d -v && mach test-unit - - if %BUILD_ENV%==gnu bash -lc "./mach build -d -v && ./mach test-unit" + - if %BUILD_ENV%==gnu bash -lc "cd $APPVEYOR_BUILD_FOLDER; ./mach build -d -v && ./mach test-unit" test: off diff --git a/python/mach_bootstrap.py b/python/mach_bootstrap.py index b5b94a726605..03960a26b969 100644 --- a/python/mach_bootstrap.py +++ b/python/mach_bootstrap.py @@ -106,6 +106,16 @@ def _activate_virtualenv(topdir): script_dir = _get_virtualenv_script_dir() activate_path = os.path.join(virtualenv_path, script_dir, "activate_this.py") if not (os.path.exists(virtualenv_path) and os.path.exists(activate_path)): + # Install missing pip and/or virtualenv packages before creating virtual environment + if not _get_exec_path(PIP_NAMES) or not _get_exec_path(VIRTUALENV_NAMES): + try: + from servo.bootstrapper.bootstrap import Bootstrapper + + bootstrapper = Bootstrapper() + bootstrapper.virtualenv() + except: + pass + virtualenv = _get_exec_path(VIRTUALENV_NAMES) if not virtualenv: sys.exit("Python virtualenv is not installed. Please install it prior to running mach.") diff --git a/python/servo/bootstrap_commands.py b/python/servo/bootstrap_commands.py index 78d151607368..16cec1b52374 100644 --- a/python/servo/bootstrap_commands.py +++ b/python/servo/bootstrap_commands.py @@ -142,11 +142,14 @@ def env(self): @CommandArgument('--force', '-f', action='store_true', help='Force reinstall packages') - def bootstrap(self, android=False, interactive=False, force=False): + @CommandArgument('--silent', '-s', + action='store_true', + help='Run bootstrap without output') + def bootstrap(self, android=False, interactive=False, force=False, silent=False): from servo.bootstrapper.bootstrap import Bootstrapper bootstrapper = Bootstrapper() - bootstrapper.bootstrap(android=android, interactive=interactive, force=force) + bootstrapper.bootstrap(android=android, interactive=interactive, force=force, silent=silent) @Command('bootstrap-rust', description='Download the Rust compiler', diff --git a/python/servo/bootstrapper/base.py b/python/servo/bootstrapper/base.py index 40f3d1f5ebc7..669aa62cc6be 100644 --- a/python/servo/bootstrapper/base.py +++ b/python/servo/bootstrapper/base.py @@ -4,6 +4,8 @@ from __future__ import print_function, unicode_literals +import os +import sys import distutils import subprocess @@ -11,9 +13,10 @@ class BaseBootstrapper(object): """Base class for system bootstrappers.""" - def __init__(self, interactive=False): + def __init__(self, interactive=False, silent=False): self.package_manager_updated = False self.interactive = interactive + self.silent = silent def ensure_system_packages(self): ''' @@ -44,6 +47,27 @@ def which(self, name): """ return distutils.spawn.find_executable(name) + def run(self, command): + if self.silent: + FNULL = open(os.devnull, 'w') + subprocess.check_call(command, stdout=FNULL, stderr=subprocess.STDOUT) + else: + subprocess.check_call(command, stdin=sys.stdin) + + def run_check(self, command): + return subprocess.call(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + + def run_as_root(self, command): + if os.geteuid() != 0: + if self.which('sudo'): + command.insert(0, 'sudo') + else: + command = ['su', 'root', '-c', ' '.join(command)] + + print('Executing as root:', subprocess.list2cmdline(command)) + + self.run(command) + def check_output(self, *args, **kwargs): """Run subprocess.check_output.""" return subprocess.check_output(*args, **kwargs) @@ -60,3 +84,6 @@ def _update_package_manager(self): This should be defined in child classes. """ + + def install_virtualenv(self): + """Install virtualenv and pip packages""" diff --git a/python/servo/bootstrapper/bootstrap.py b/python/servo/bootstrapper/bootstrap.py index c6ac9d196eb0..e822584899cb 100644 --- a/python/servo/bootstrapper/bootstrap.py +++ b/python/servo/bootstrapper/bootstrap.py @@ -2,14 +2,36 @@ # 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/. +# Based on Mozilla's Mozboot: https://dxr.mozilla.org/mozilla-central/source/python/mozboot + from __future__ import print_function +import platform import sys +from centosfedora import CentOSFedoraBootstrapper +from debian import DebianBootstrapper +from osx import OSXBootstrapper from windows_gnu import WindowsGnuBootstrapper from windows_msvc import WindowsMsvcBootstrapper +DEBIAN_DISTROS = ( + 'Debian', + 'debian', + 'Ubuntu', + # Most Linux Mint editions are based on Ubuntu. One is based on Debian. + # The difference is reported in dist_id from platform.linux_distribution. + # But it doesn't matter since we share a bootstrapper between Debian and + # Ubuntu. + 'Mint', + 'LinuxMint', + 'Elementary OS', + 'Elementary', + '"elementary OS"', +) + + class Bootstrapper(object): """Main class that performs system bootstrap.""" @@ -18,7 +40,27 @@ def __init__(self): cls = None args = {} - if sys.platform.startswith('msys'): + if sys.platform.startswith('linux'): + distro, version, dist_id = platform.linux_distribution() + + if distro in ('CentOS', 'CentOS Linux', 'Fedora'): + cls = CentOSFedoraBootstrapper + args['distro'] = distro + elif distro in DEBIAN_DISTROS: + cls = DebianBootstrapper + else: + sys.exit('Bootstrap support for this Linux distro not yet available.') + + args['version'] = version + args['dist_id'] = dist_id + + elif sys.platform.startswith('darwin'): + osx_version = platform.mac_ver()[0] + + cls = OSXBootstrapper + args['version'] = osx_version + + elif sys.platform.startswith('msys'): cls = WindowsGnuBootstrapper elif sys.platform.startswith('win32'): @@ -29,13 +71,22 @@ def __init__(self): self.instance = cls(**args) - def bootstrap(self, android=False, interactive=False, force=False): + def bootstrap(self, android, interactive, force, silent): self.instance.interactive = interactive self.instance.force = force + self.instance.silent = silent - if android: - self.instance.install_mobile_android_packages() - elif force: + if force: self.instance.install_system_packages() else: self.instance.ensure_system_packages() + + if android: + self.instance.install_mobile_android_packages() + + print + + def virtualenv(self): + self.instance.install_virtualenv() + + print diff --git a/python/servo/bootstrapper/centosfedora.py b/python/servo/bootstrapper/centosfedora.py new file mode 100644 index 000000000000..96001afb26bc --- /dev/null +++ b/python/servo/bootstrapper/centosfedora.py @@ -0,0 +1,48 @@ +# 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/. + +from base import BaseBootstrapper + + +class CentOSFedoraBootstrapper(BaseBootstrapper): + from packages import CENTOSFEDORA as desktop_deps + + def __init__(self, distro, version, dist_id, **kwargs): + BaseBootstrapper.__init__(self, **kwargs) + + self.distro = distro + self.version = version + self.dist_id = dist_id + + def ensure_system_packages(self): + install_packages = [] + installed_list = str(self.check_output(['rpm', '-qa'])).replace('\n', '|') + install_packages = [] + for p in self.desktop_deps: + if not "|" + p in installed_list: + install_packages += [p] + if install_packages: + print "Installing missing packages..." + self.install_system_packages(install_packages) + + def install_system_packages(self, packages=desktop_deps): + self.dnf_install(*packages) + + def install_mobile_android_packages(self): + raise NotImplementedError('Bootstrap support for Android not yet available.') + + def install_virtualenv(self): + self.dnf_install(*["python-pip", "python-virtualenv"]) + + def dnf_install(self, *packages): + if self.which('dnf'): + command = ['dnf', 'reinstall' if self.force else 'install'] + else: + command = ['yum', 'install'] + + if not self.interactive: + command.append('-y') + command.extend(packages) + + self.run_as_root(command) diff --git a/python/servo/bootstrapper/debian.py b/python/servo/bootstrapper/debian.py new file mode 100644 index 000000000000..fa9a678b7ed7 --- /dev/null +++ b/python/servo/bootstrapper/debian.py @@ -0,0 +1,65 @@ +# 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/. + +import subprocess + +from base import BaseBootstrapper + + +class DebianBootstrapper(BaseBootstrapper): + '''Bootstrapper for Debian-based distributions.''' + + from packages import DEBIAN as desktop_deps + + def __init__(self, version, dist_id, **kwargs): + BaseBootstrapper.__init__(self, **kwargs) + # Find Python virtualenv package name + venv = subprocess.Popen(['apt-cache', 'policy', 'virtualenv'], stdout=subprocess.PIPE) + self.virtualenv = 'virtualenv' if "virtualenv:" in venv.stdout.read() else 'python-virtualenv' + self.desktop_deps += [self.virtualenv] + + self.version = version + self.dist_id = dist_id + + def ensure_system_packages(self): + printed = False + + for package in self.desktop_deps: + if self.run_check(['dpkg', '-s', package]): + if not printed: + print "Updating package manager..." + self._ensure_package_manager_updated() + print "Installing missing packages..." + printed = True + + print "Installing %s..." % package + self.apt_install(package) + + def install_system_packages(self, packages=desktop_deps): + self._ensure_package_manager_updated() + self.apt_install(*packages) + + def install_mobile_android_packages(self): + raise NotImplementedError('Bootstrap support for Android not yet available.') + + def install_virtualenv(self): + self.apt_install(*["python-pip", self.virtualenv]) + + def _update_package_manager(self): + self.apt_update() + + def apt_install(self, *packages): + command = ['apt-get', 'install'] + if not self.interactive: + command.append('-y') + if self.force: + command.append('--reinstall') + command.extend(packages) + self.run_as_root(command) + + def apt_update(self): + command = ['apt-get', 'update'] + if not self.interactive: + command.append('-y') + self.run_as_root(command) diff --git a/python/servo/bootstrapper/osx.py b/python/servo/bootstrapper/osx.py new file mode 100644 index 000000000000..9af842d0ed62 --- /dev/null +++ b/python/servo/bootstrapper/osx.py @@ -0,0 +1,464 @@ +# 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/. + +from __future__ import print_function, unicode_literals + +import os +import re +import subprocess +import sys +import tempfile +import traceback +try: + from urllib2 import urlopen +except ImportError: + from urllib.request import urlopen + +from distutils.version import StrictVersion + +from base import BaseBootstrapper +from packages import OSX_BREW, OSX_PORT + +XCODE_APP_STORE = 'macappstore://itunes.apple.com/app/id497799835?mt=12' + +HOMEBREW_BOOTSTRAP = 'https://raw.githubusercontent.com/Homebrew/install/master/install' + +MACPORTS_URL = {'11': 'https://distfiles.macports.org/MacPorts/MacPorts-2.3.4-10.11-ElCapitan.pkg', + '10': 'https://distfiles.macports.org/MacPorts/MacPorts-2.3.4-10.10-Yosemite.pkg', + '9': 'https://distfiles.macports.org/MacPorts/MacPorts-2.3.4-10.9-Mavericks.pkg', + '8': 'https://distfiles.macports.org/MacPorts/MacPorts-2.3.4-10.8-MountainLion.pkg', + '7': 'https://distfiles.macports.org/MacPorts/MacPorts-2.3.4-10.7-Lion.pkg', } + +MACPORTS_CLANG_PACKAGE = 'clang-3.3' + +RE_CLANG_VERSION = re.compile('Apple (?:clang|LLVM) version (\d+\.\d+)') + +APPLE_CLANG_MINIMUM_VERSION = StrictVersion('4.2') + +XCODE_REQUIRED = ''' +Xcode is required to build Servo. Please complete the install of Xcode +through the App Store. + +It's possible Xcode is already installed on this machine but it isn't being +detected. This is possible with developer preview releases of Xcode, for +example. To correct this problem, run: + + `xcode-select --switch /path/to/Xcode.app`. + +e.g. `sudo xcode-select --switch /Applications/Xcode.app`. +''' + +XCODE_REQUIRED_LEGACY = ''' +You will need to download and install Xcode to build Servo. + +Please complete the Xcode download and then relaunch this script. +''' + +XCODE_NO_DEVELOPER_DIRECTORY = ''' +xcode-select says you don't have a developer directory configured. We think +this is due to you not having Xcode installed (properly). We're going to +attempt to install Xcode through the App Store. If the App Store thinks you +have Xcode installed, please run xcode-select by hand until it stops +complaining and then re-run this script. +''' + +XCODE_COMMAND_LINE_TOOLS_MISSING = ''' +The Xcode command line tools are required to build Servo. +''' + +INSTALL_XCODE_COMMAND_LINE_TOOLS_STEPS = ''' +Perform the following steps to install the Xcode command line tools: + + 1) Open Xcode.app + 2) Click through any first-run prompts + 3) From the main Xcode menu, select Preferences (Command ,) + 4) Go to the Download tab (near the right) + 5) Install the "Command Line Tools" + +When that has finished installing, please relaunch this script. +''' + +UPGRADE_XCODE_COMMAND_LINE_TOOLS = ''' +An old version of the Xcode command line tools is installed. You will need to +install a newer version in order to compile Servo. If Xcode itself is old, +its command line tools may be too old even if it claims there are no updates +available, so if you are seeing this message multiple times, please update +Xcode first. +''' + +PACKAGE_MANAGER_INSTALL = ''' +We will install the %s package manager to install required packages. + +You will be prompted to install %s with its default settings. If you +would prefer to do this manually, hit CTRL+c, install %s yourself, ensure +"%s" is in your $PATH, and relaunch bootstrap. +''' + +PACKAGE_MANAGER_PACKAGES = ''' +We are now installing all required packages via %s. You will see a lot of +output as packages are built. +''' + +PACKAGE_MANAGER_OLD_CLANG = ''' +We require a newer compiler than what is provided by your version of Xcode. + +We will install a modern version of Clang through %s. +''' + +PACKAGE_MANAGER_CHOICE = ''' +Please choose a package manager you'd like: +1. Homebrew +2. MacPorts +Your choice: +''' + +NO_PACKAGE_MANAGER_WARNING = ''' +It seems you don't have any supported package manager installed. +''' + +PACKAGE_MANAGER_EXISTS = ''' +Looks like you have %s installed. We will install all required packages via %s. +''' + +MULTI_PACKAGE_MANAGER_EXISTS = ''' +It looks like you have multiple package managers installed. +''' + +# May add support for other package manager on os x. +PACKAGE_MANAGER = {'Homebrew': 'brew', + 'MacPorts': 'port'} + +PACKAGE_MANAGER_CHOICES = ['Homebrew', 'MacPorts'] + +PACKAGE_MANAGER_BIN_MISSING = ''' +A package manager is installed. However, your current shell does +not know where to find '%s' yet. You'll need to start a new shell +to pick up the environment changes so it can be found. + +Please start a new shell or terminal window and run this +bootstrapper again. + +If this problem persists, you will likely want to adjust your +shell's init script (e.g. ~/.bash_profile) to export a PATH +environment variable containing the location of your package +manager binary. e.g. + + export PATH=/usr/local/bin:$PATH +''' + +BAD_PATH_ORDER = ''' +Your environment's PATH variable lists a system path directory (%s) +before the path to your package manager's binaries (%s). +This means that the package manager's binaries likely won't be +detected properly. + +Modify your shell's configuration (e.g. ~/.profile or +~/.bash_profile) to have %s appear in $PATH before %s. e.g. + + export PATH=%s:$PATH + +Once this is done, start a new shell (likely Command+T) and run +this bootstrap again. +''' + + +class OSXBootstrapper(BaseBootstrapper): + def __init__(self, version, **kwargs): + BaseBootstrapper.__init__(self, **kwargs) + + self.os_version = StrictVersion(version) + + self.packages_port = OSX_PORT + self.packages_brew = OSX_BREW + if self.os_version >= StrictVersion('10.11'): + self.packages_brew += ["openssl"] + + print(self.packages_brew) + + if self.os_version < StrictVersion('10.7'): + raise Exception('OS X 10.7 or above is required.') + + self.minor_version = version.split('.')[1] + + self.package_manager = self.ensure_package_manager() + + if self.package_manager == "homebrew": + self.brew = self.which('brew') + elif self.package_manager == "macports": + self.port = self.which('port') + else: + sys.exit("No selected package manager") + + def ensure_system_packages(self): + self.install_system_packages() + + def install_system_packages(self): + self.ensure_xcode() + self._update_package_manager() + + getattr(self, 'ensure_%s_system_packages' % self.package_manager)() + + def install_mobile_android_packages(self): + raise NotImplementedError('Bootstrap support for Android not yet available.') + + def install_virtualenv(self): + self.install_system_packages() + + if self.package_manager == "homebrew": + self.run(['pip', 'install', 'virtualenv']) + + def ensure_xcode(self): + # OS X 10.7 have Xcode come from the app store. However, users can + # still install Xcode into any arbitrary location. We honor the + # location of Xcode as set by xcode-select. This should also pick up + # developer preview releases of Xcode, which can be installed into + # paths like /Applications/Xcode5-DP6.app. + select = self.which('xcode-select') + try: + output = self.check_output([select, '--print-path'], + stderr=subprocess.STDOUT) + except subprocess.CalledProcessError as e: + # This seems to appear on fresh OS X machines before any Xcode + # has been installed. It may only occur on OS X 10.9 and later. + if b'unable to get active developer directory' in e.output: + print(XCODE_NO_DEVELOPER_DIRECTORY) + self._install_xcode_app_store() + assert False # Above should exit. + + output = e.output + + # This isn't the most robust check in the world. It relies on the + # default value not being in an application bundle, which seems to + # hold on at least Mavericks. + if b'.app/' not in output: + print(XCODE_REQUIRED) + self._install_xcode_app_store() + assert False # Above should exit. + + # Once Xcode is installed, you need to agree to the license before you can + # use it. + try: + output = self.check_output(['/usr/bin/xcrun', 'clang'], + stderr=subprocess.STDOUT) + except subprocess.CalledProcessError as e: + if b'license' in e.output: + xcodebuild = self.which('xcodebuild') + try: + subprocess.check_call([xcodebuild, '-license'], + stderr=subprocess.STDOUT) + except subprocess.CalledProcessError as e: + if b'requires admin privileges' in e.output: + self.run_as_root([xcodebuild, '-license']) + + # Even then we're not done! We need to install the Xcode command line tools. + # As of Mountain Lion, apparently the only way to do this is to go through a + # menu dialog inside Xcode itself. We're not making this up. + if not os.path.exists('/usr/bin/clang'): + print(XCODE_COMMAND_LINE_TOOLS_MISSING) + print(INSTALL_XCODE_COMMAND_LINE_TOOLS_STEPS) + sys.exit(1) + + output = self.check_output(['/usr/bin/clang', '--version']) + match = RE_CLANG_VERSION.search(output) + if match is None: + raise Exception('Could not determine Clang version.') + + version = StrictVersion(match.group(1)) + + if version < APPLE_CLANG_MINIMUM_VERSION: + print(UPGRADE_XCODE_COMMAND_LINE_TOOLS) + print(INSTALL_XCODE_COMMAND_LINE_TOOLS_STEPS) + sys.exit(1) + + def _install_xcode_app_store(self): + subprocess.check_call(['open', XCODE_APP_STORE]) + print('Once the install has finished, please relaunch this script.') + sys.exit(1) + + def _ensure_homebrew_packages(self, packages, extra_brew_args=[]): + self.brew = self.which('brew') + assert self.brew is not None + cmd = [self.brew] + extra_brew_args + + installed = self.check_output(cmd + ['list']).split() + + printed = False + + for package in packages: + if package in installed: + continue + + if not printed: + print("\nInstalling required packages via Homebrew...\n") + printed = True + + print("Installing %s..." % package, end="") + self.run(cmd + ['reinstall' if self.force else 'install', package]) + if package == "openssl": + print("Linking %s..." % package, end="") + self.run(cmd + ['link', '--force', package]) + + return printed + + def ensure_homebrew_system_packages(self): + packages = self.packages_brew + + self._ensure_homebrew_packages(packages) + + def _ensure_macports_packages(self, packages): + self.port = self.which('port') + assert self.port is not None + + installed = set(self.check_output([self.port, 'installed']).split()) + + missing = [package for package in packages if package not in installed] + if missing: + print("\nInstalling required packages via MacPorts...\n") + self.run_as_root([self.port, '-v', 'install'] + missing) + + def ensure_macports_system_packages(self): + packages = self.packages_port + + self._ensure_macports_packages(packages) + self.run_as_root([self.port, 'select', '--set', 'python', 'python27']) + + def ensure_package_manager(self): + ''' + Search package mgr in sys.path, if none is found, prompt the user to install one. + If only one is found, use that one. If both are found, prompt the user to choose + one. + ''' + # Read file with selected default package manager + default_manager_file = os.path.join(os.path.abspath(os.curdir), ".servo", "default_package_manager") + print(default_manager_file) + default_manager = "" + if os.path.exists(default_manager_file): + default_manager = open(default_manager_file).read().strip() + + print(default_manager) + + installed = [] + for name, cmd in PACKAGE_MANAGER.iteritems(): + if default_manager == cmd and self.which(cmd) is not None: + installed = [name] + break + elif self.which(cmd) is not None: + installed.append(name) + + active_name, active_cmd = None, None + + if not installed: + print(NO_PACKAGE_MANAGER_WARNING) + choice = self.prompt_int(prompt=PACKAGE_MANAGER_CHOICE, low=1, high=2) + active_name = PACKAGE_MANAGER_CHOICES[choice - 1] + active_cmd = PACKAGE_MANAGER[active_name] + getattr(self, 'install_%s' % active_name.lower())() + elif len(installed) == 1: + print(PACKAGE_MANAGER_EXISTS % (installed[0], installed[0])) + active_name = installed[0] + active_cmd = PACKAGE_MANAGER[active_name] + else: + print(MULTI_PACKAGE_MANAGER_EXISTS) + choice = self.prompt_int(prompt=PACKAGE_MANAGER_CHOICE, low=1, high=2) + + active_name = PACKAGE_MANAGER_CHOICES[choice - 1] + active_cmd = PACKAGE_MANAGER[active_name] + + # Ensure the active package manager is in $PATH and it comes before + # /usr/bin. If it doesn't come before /usr/bin, we'll pick up system + # packages before package manager installed packages and the build may + # break. + p = self.which(active_cmd) + if not p: + print(PACKAGE_MANAGER_BIN_MISSING % active_cmd) + sys.exit(1) + + p_dir = os.path.dirname(p) + for path in os.environ['PATH'].split(os.pathsep): + if path == p_dir: + break + + for check in ('/bin', '/usr/bin'): + if path == check: + print(BAD_PATH_ORDER % (check, p_dir, p_dir, check, p_dir)) + sys.exit(1) + + # Write default package manager to file + try: + if not os.path.exists(".servo"): + os.makedirs(".servo") + + with open(default_manager_file, 'w') as dm: + dm.write(active_cmd) + except: + traceback.print_exc() + pass + + return active_name.lower() + + def install_homebrew(self): + print(PACKAGE_MANAGER_INSTALL % ('Homebrew', 'Homebrew', 'Homebrew', 'brew')) + bootstrap = urlopen(url=HOMEBREW_BOOTSTRAP, timeout=20).read() + with tempfile.NamedTemporaryFile() as tf: + tf.write(bootstrap) + tf.flush() + + self.run(['ruby', tf.name]) + + def install_macports(self): + url = MACPORTS_URL.get(self.minor_version, None) + if not url: + raise Exception('We do not have a MacPorts install URL for your ' + 'OS X version. You will need to install MacPorts manually.') + + print(PACKAGE_MANAGER_INSTALL % ('MacPorts', 'MacPorts', 'MacPorts', 'port')) + pkg = urlopen(url=url, timeout=300).read() + with tempfile.NamedTemporaryFile(suffix='.pkg') as tf: + tf.write(pkg) + tf.flush() + + self.run_as_root(['installer', '-pkg', tf.name, '-target', '/']) + + def _update_package_manager(self): + print("Updating package manager...") + if self.package_manager == 'homebrew': + self.run([self.brew, '-v', 'update']) + else: + assert self.package_manager == 'macports' + self.run_as_root([self.port, 'selfupdate']) + + def _upgrade_package(self, package): + self._ensure_package_manager_updated() + + if self.package_manager == 'homebrew': + try: + subprocess.check_output([self.brew, '-v', 'upgrade', package], + stderr=subprocess.STDOUT) + except subprocess.CalledProcessError as e: + if b'already installed' not in e.output: + raise + else: + assert self.package_manager == 'macports' + + self.run_as_root([self.port, 'upgrade', package]) + + def prompt_int(self, prompt, low, high, limit=5): + ''' Prompts the user with prompt and requires an integer between low and high. ''' + valid = False + while not valid and limit > 0: + try: + choice = int(raw_input(prompt)) + if not low <= choice <= high: + print("ERROR! Please enter a valid option!") + limit -= 1 + else: + valid = True + except ValueError: + print("ERROR! Please enter a valid option!") + limit -= 1 + + if limit > 0: + return choice + else: + raise Exception("Error! Reached max attempts of entering option.") diff --git a/python/servo/bootstrapper/packages.py b/python/servo/bootstrapper/packages.py index 7e9a7cd9c0e5..015857fa6e63 100644 --- a/python/servo/bootstrapper/packages.py +++ b/python/servo/bootstrapper/packages.py @@ -4,6 +4,74 @@ # Listed all packages for different platforms in one file +CENTOSFEDORA = [ + "curl", + "freeglut-devel", + "libtool", + "gcc-c++", + "libXi-devel", + "freetype-devel", + "mesa-libGL-devel", + "mesa-libEGL-devel", + "glib2-devel", + "libX11-devel", + "libXrandr-devel", + "gperf", + "fontconfig-devel", + "cabextract", + "ttmkfdir", + "python", + "python2-virtualenv", + "python-pip", + "expat-devel", + "rpm-build", + "openssl-devel", + "cmake", + "bzip2-devel", + "libXcursor-devel", + "libXmu-devel", + "mesa-libOSMesa-devel", + "dbus-devel", +] + +DEBIAN = [ + "git", + "curl", + "freeglut3-dev", + "autoconf", + "libfreetype6-dev", + "libgl1-mesa-dri", + "libglib2.0-dev", + "xorg-dev", + "gperf", + "g++", + "build-essential", + "cmake", + "python-pip", + "libssl-dev", + "libbz2-dev", + "libosmesa6-dev", + "libxmu6", + "libxmu-dev", + "libglu1-mesa-dev", + "libgles2-mesa-dev", + "libegl1-mesa-dev", + "libdbus-1-dev", +] + +OSX_BREW = [ + "automake", + "pkg-config", + "python", + "cmake", +] + +OSX_PORT = [ + "python27", + "py27-virtualenv", + "cmake", +] + WINDOWS_GNU = [ "mingw-w64-x86_64-toolchain", "mingw-w64-x86_64-freetype", diff --git a/python/servo/bootstrapper/windows_gnu.py b/python/servo/bootstrapper/windows_gnu.py index 794f1790cd12..5d3d0c51a031 100644 --- a/python/servo/bootstrapper/windows_gnu.py +++ b/python/servo/bootstrapper/windows_gnu.py @@ -3,15 +3,15 @@ # file, You can obtain one at http://mozilla.org/MPL/2.0/. import sys -import subprocess from base import BaseBootstrapper -from packages import WINDOWS_GNU as deps class WindowsGnuBootstrapper(BaseBootstrapper): '''Bootstrapper for msys2 based environments for building in Windows.''' + from packages import WINDOWS_GNU as deps + def __init__(self, **kwargs): BaseBootstrapper.__init__(self, **kwargs) @@ -21,12 +21,13 @@ def __init__(self, **kwargs): def ensure_system_packages(self): install_packages = [] - for p in deps: + for p in self.deps: command = ['pacman', '-Qs', p] if self.run_check(command): install_packages += [p] if install_packages: - install_packages(install_packages) + print "Installing missing packages..." + self.install_system_packages(install_packages) def install_system_packages(self, packages=deps): self._ensure_package_manager_updated() @@ -38,12 +39,6 @@ def install_mobile_android_packages(self): def _update_package_manager(self): self.pacman_update() - def run(self, command): - subprocess.check_call(command, stdin=sys.stdin) - - def run_check(self, command): - return subprocess.call(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE) - def pacman_update(self): command = ['pacman', '--sync', '--refresh'] self.run(command) diff --git a/python/servo/bootstrapper/windows_msvc.py b/python/servo/bootstrapper/windows_msvc.py index f4926276549f..2cebb72c0e03 100644 --- a/python/servo/bootstrapper/windows_msvc.py +++ b/python/servo/bootstrapper/windows_msvc.py @@ -8,12 +8,13 @@ from distutils import spawn from base import BaseBootstrapper -from packages import WINDOWS_MSVC as deps class WindowsMsvcBootstrapper(BaseBootstrapper): '''Bootstrapper for MSVC building on Windows.''' + from packages import WINDOWS_MSVC as deps + def __init__(self, **kwargs): BaseBootstrapper.__init__(self, **kwargs) @@ -84,3 +85,6 @@ def install_system_packages(self, packages=deps): def install_mobile_android_packages(self): sys.exit('We do not support building Android on Windows. Sorry!') + + def install_virtualenv(self): + self.run(['pip', 'install', 'virtualenv']) diff --git a/python/servo/command_base.py b/python/servo/command_base.py index 7c4827f8b62d..af7738b2ecfc 100644 --- a/python/servo/command_base.py +++ b/python/servo/command_base.py @@ -518,9 +518,11 @@ def ensure_bootstrapped(self, target=None): target_path = path.join(base_target_path, target_platform) target_exists = path.exists(target_path) - # Always check if all needed MSVC dependencies are installed - if "msvc" in target_platform: - Registrar.dispatch("bootstrap", context=self.context) + # Check for missing packages on supported platforms + try: + Registrar.dispatch("bootstrap", context=self.context, silent=True) + except: + pass if not (self.config['tools']['system-rust'] or (rustc_binary_exists and target_exists)): print("looking for rustc at %s" % (rustc_path))