aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--python/mach_bootstrap.py10
-rw-r--r--python/requirements-salt.txt6
-rw-r--r--python/requirements.txt3
-rw-r--r--python/servo/bootstrap.py229
-rw-r--r--python/servo/bootstrap_commands.py111
-rw-r--r--python/servo/bootstrapper/__init__.py3
-rw-r--r--python/servo/bootstrapper/base.py62
-rw-r--r--python/servo/bootstrapper/bootstrap.py42
-rw-r--r--python/servo/bootstrapper/windows_gnu.py75
-rw-r--r--python/servo/bootstrapper/windows_msvc.py86
-rw-r--r--python/servo/build_commands.py3
-rw-r--r--python/servo/command_base.py64
-rw-r--r--python/servo/packages.py (renamed from python/servo/bootstrapper/packages.py)24
-rw-r--r--python/servo/testing_commands.py3
-rw-r--r--python/servo/util.py151
15 files changed, 430 insertions, 442 deletions
diff --git a/python/mach_bootstrap.py b/python/mach_bootstrap.py
index d41bb1f5099..fa612ddd868 100644
--- a/python/mach_bootstrap.py
+++ b/python/mach_bootstrap.py
@@ -118,7 +118,11 @@ def _activate_virtualenv(topdir):
if not virtualenv:
sys.exit("Python virtualenv is not installed. Please install it prior to running mach.")
- process = Popen([virtualenv, "-p", python, virtualenv_path], stdout=PIPE, stderr=PIPE)
+ process = Popen(
+ [virtualenv, "-p", python, "--system-site-packages", virtualenv_path],
+ stdout=PIPE,
+ stderr=PIPE
+ )
process.wait()
if process.returncode:
out, err = process.communicate()
@@ -153,7 +157,7 @@ def _activate_virtualenv(topdir):
if not pip:
sys.exit("Python pip is either not installed or not found in virtualenv.")
- process = Popen([pip, "install", "-q", "-U", "pip"], stdout=PIPE, stderr=PIPE)
+ process = Popen([pip, "install", "-q", "-I", "-U", "pip"], stdout=PIPE, stderr=PIPE)
process.wait()
if process.returncode:
out, err = process.communicate()
@@ -175,7 +179,7 @@ def _activate_virtualenv(topdir):
if not pip:
sys.exit("Python pip is either not installed or not found in virtualenv.")
- process = Popen([pip, "install", "-q", "-r", req_path], stdout=PIPE, stderr=PIPE)
+ process = Popen([pip, "install", "-q", "-I", "-r", req_path], stdout=PIPE, stderr=PIPE)
process.wait()
if process.returncode:
out, err = process.communicate()
diff --git a/python/requirements-salt.txt b/python/requirements-salt.txt
new file mode 100644
index 00000000000..1486b590053
--- /dev/null
+++ b/python/requirements-salt.txt
@@ -0,0 +1,6 @@
+# Ensure all versions are pinned for repeatability,
+# since `--system-site-packages` is enabled
+
+# For boostrapping, make sure versions match those in saltfs
+salt == 2016.3.4
+GitPython == 0.3.2
diff --git a/python/requirements.txt b/python/requirements.txt
index 8248a21e930..41b25ccdb5a 100644
--- a/python/requirements.txt
+++ b/python/requirements.txt
@@ -1,3 +1,6 @@
+# Ensure all versions are pinned for repeatability,
+# since `--system-site-packages` is enabled
+
blessings == 1.6
mach == 0.6.0
mozdebug == 0.1
diff --git a/python/servo/bootstrap.py b/python/servo/bootstrap.py
new file mode 100644
index 00000000000..54862a885bd
--- /dev/null
+++ b/python/servo/bootstrap.py
@@ -0,0 +1,229 @@
+# 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 absolute_import, print_function
+
+from distutils.spawn import find_executable
+import json
+import os
+import platform
+import shutil
+import subprocess
+
+import servo.packages as packages
+from servo.util import extract, download_file, host_triple
+
+
+def salt(context, force=False):
+ # Ensure Salt is installed in the virtualenv
+ # It's not instaled globally because it's a large, non-required dependency,
+ # and the installation fails on Windows
+ print("Checking Salt installation...", end='')
+ reqs_path = os.path.join(context.topdir, 'python', 'requirements-salt.txt')
+ process = subprocess.Popen(
+ ["pip", "install", "-q", "-I", "-r", reqs_path],
+ stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE
+ )
+ process.wait()
+ if process.returncode:
+ out, err = process.communicate()
+ print('failed to install Salt via pip:')
+ print('Output: {}\nError: {}'.format(out, err))
+ return 1
+ print("done")
+
+ salt_root = os.path.join(context.sharedir, 'salt')
+ config_dir = os.path.join(salt_root, 'etc', 'salt')
+ pillar_dir = os.path.join(config_dir, 'pillars')
+
+ # In order to allow `mach bootstrap` to work from any CWD,
+ # the `root_dir` must be an absolute path.
+ # We place it under `context.sharedir` because
+ # Salt caches data (e.g. gitfs files) in its `var` subdirectory.
+ # Hence, dynamically generate the config with an appropriate `root_dir`
+ # and serialize it as JSON (which is valid YAML).
+ config = {
+ 'fileserver_backend': ['git'],
+ 'gitfs_env_whitelist': 'base',
+ 'gitfs_provider': 'gitpython',
+ 'gitfs_remotes': [
+ 'https://github.com/servo/saltfs.git',
+ ],
+ 'hash_type': 'sha384',
+ 'master': 'localhost',
+ 'root_dir': salt_root,
+ 'state_output': 'changes',
+ 'state_tabular': True,
+ }
+
+ if not os.path.exists(config_dir):
+ os.makedirs(config_dir, mode=0o700)
+ with open(os.path.join(config_dir, 'minion'), 'w') as config_file:
+ config_file.write(json.dumps(config) + '\n')
+
+ # Similarly, the pillar data is created dynamically
+ # and temporarily serialized to disk.
+ # This dynamism is not yet used, but will be in the future
+ # to enable Android bootstrapping by using
+ # context.sharedir as a location for Android packages.
+ pillar = {
+ 'top.sls': {
+ 'base': {
+ '*': ['bootstrap'],
+ },
+ },
+ 'bootstrap.sls': {
+ 'fully_managed': False,
+ },
+ }
+ if os.path.exists(pillar_dir):
+ shutil.rmtree(pillar_dir)
+ os.makedirs(pillar_dir, mode=0o700)
+ for filename in pillar:
+ with open(os.path.join(pillar_dir, filename), 'w') as pillar_file:
+ pillar_file.write(json.dumps(pillar[filename]) + '\n')
+
+ cmd = [
+ 'sudo',
+ # sudo escapes from the venv, need to use full path
+ find_executable('salt-call'),
+ '--local',
+ '--config-dir={}'.format(config_dir),
+ '--pillar-root={}'.format(pillar_dir),
+ 'state.apply',
+ 'servo-build-dependencies',
+ ]
+
+ if not force:
+ print('Running bootstrap in dry-run mode to show changes')
+ # Because `test=True` mode runs each state individually without
+ # considering how required/previous states affect the system,
+ # it will often report states with requisites as failing due
+ # to the requisites not actually being run,
+ # even though these are spurious and will succeed during
+ # the actual highstate.
+ # Hence `--retcode-passthrough` is not helpful in dry-run mode,
+ # so only detect failures of the actual salt-call binary itself.
+ retcode = subprocess.call(cmd + ['test=True'])
+ if retcode != 0:
+ print('Something went wrong while bootstrapping')
+ return retcode
+
+ proceed = raw_input(
+ 'Proposed changes are above, proceed with bootstrap? [y/N]: '
+ )
+ if proceed.lower() not in ['y', 'yes']:
+ return 0
+
+ print('')
+
+ print('Running Salt bootstrap')
+ retcode = subprocess.call(cmd + ['--retcode-passthrough'])
+ if retcode == 0:
+ print('Salt bootstrapping complete')
+ else:
+ print('Salt bootstrapping encountered errors')
+ return retcode
+
+
+def windows_gnu(context, force=False):
+ '''Bootstrapper for msys2 based environments for building in Windows.'''
+
+ if not find_executable('pacman'):
+ print(
+ 'The Windows GNU bootstrapper only works with msys2 with pacman. '
+ 'Get msys2 at http://msys2.github.io/'
+ )
+ return 1
+
+ # Ensure repositories are up to date
+ command = ['pacman', '--sync', '--refresh']
+ subprocess.check_call(command)
+
+ # Install packages
+ command = ['pacman', '--sync', '--needed']
+ if force:
+ command.append('--noconfirm')
+ subprocess.check_call(command + list(packages.WINDOWS_GNU))
+
+ # Downgrade GCC to 5.4.0-1
+ gcc_pkgs = ["gcc", "gcc-ada", "gcc-fortran", "gcc-libgfortran", "gcc-libs", "gcc-objc"]
+ gcc_version = "5.4.0-1"
+ mingw_url = "http://repo.msys2.org/mingw/x86_64/mingw-w64-x86_64-{}-{}-any.pkg.tar.xz"
+ gcc_list = [mingw_url.format(gcc, gcc_version) for gcc in gcc_pkgs]
+
+ # Note: `--upgrade` also does downgrades
+ downgrade_command = ['pacman', '--upgrade']
+ if force:
+ downgrade_command.append('--noconfirm')
+ subprocess.check_call(downgrade_command + gcc_list)
+
+
+def windows_msvc(context, force=False):
+ '''Bootstrapper for MSVC building on Windows.'''
+
+ deps_dir = os.path.join(context.sharedir, "msvc-dependencies")
+ deps_url = "https://servo-rust.s3.amazonaws.com/msvc-deps/"
+
+ def version(package):
+ return packages.WINDOWS_MSVC[package]
+
+ def package_dir(package):
+ return os.path.join(deps_dir, package, version(package))
+
+ to_install = {}
+ for package in packages.WINDOWS_MSVC:
+ # Don't install CMake if it already exists in PATH
+ if package == "cmake" and find_executable(package):
+ continue
+
+ if not os.path.isdir(package_dir(package)):
+ to_install[package] = version(package)
+
+ if not to_install:
+ return 0
+
+ print("Installing missing MSVC dependencies...")
+ for package in to_install:
+ full_spec = '{}-{}'.format(package, version(package))
+
+ parent_dir = os.path.dirname(package_dir(package))
+ if not os.path.isdir(parent_dir):
+ os.makedirs(parent_dir)
+
+ zip_path = package_dir(package) + ".zip"
+ if not os.path.isfile(zip_path):
+ zip_url = "{}{}.zip".format(deps_url, full_spec)
+ download_file(full_spec, zip_url, zip_path)
+
+ print("Extracting {}...".format(full_spec), end='')
+ extract(zip_path, deps_dir)
+ print("done")
+
+ extracted_path = os.path.join(deps_dir, full_spec)
+ os.rename(extracted_path, package_dir(package))
+
+ return 0
+
+
+def bootstrap(context, force=False):
+ '''Dispatches to the right bootstrapping function for the OS.'''
+
+ bootstrapper = None
+
+ if "windows-gnu" in host_triple():
+ bootstrapper = windows_gnu
+ elif "windows-msvc" in host_triple():
+ bootstrapper = windows_msvc
+ elif "linux-gnu" in host_triple():
+ distro, version, _ = platform.linux_distribution()
+ if distro == 'Ubuntu' and version == '14.04':
+ bootstrapper = salt
+
+ if bootstrapper is None:
+ print('Bootstrap support is not yet available for your OS.')
+ return 1
+
+ return bootstrapper(context, force=force)
diff --git a/python/servo/bootstrap_commands.py b/python/servo/bootstrap_commands.py
index 9bc7c60fd4a..8975f174d84 100644
--- a/python/servo/bootstrap_commands.py
+++ b/python/servo/bootstrap_commands.py
@@ -7,8 +7,7 @@
# option. This file may not be copied, modified, or distributed
# except according to those terms.
-from __future__ import print_function, unicode_literals
-from socket import error as socket_error
+from __future__ import absolute_import, print_function, unicode_literals
import base64
import json
@@ -17,9 +16,6 @@ import os.path as path
import re
import shutil
import sys
-import StringIO
-import tarfile
-import zipfile
import urllib2
from mach.decorators import (
@@ -28,93 +24,9 @@ from mach.decorators import (
Command,
)
-from servo.command_base import CommandBase, host_triple, BIN_SUFFIX
-
-
-def download(desc, src, writer, start_byte=0):
- if start_byte:
- print("Resuming download of %s..." % desc)
- else:
- print("Downloading %s..." % desc)
- dumb = (os.environ.get("TERM") == "dumb") or (not sys.stdout.isatty())
-
- try:
- req = urllib2.Request(src)
- if start_byte:
- req = urllib2.Request(src, headers={'Range': 'bytes={}-'.format(start_byte)})
- resp = urllib2.urlopen(req)
-
- fsize = None
- if resp.info().getheader('Content-Length'):
- fsize = int(resp.info().getheader('Content-Length').strip()) + start_byte
-
- recved = start_byte
- chunk_size = 8192
-
- while True:
- chunk = resp.read(chunk_size)
- if not chunk:
- break
- recved += len(chunk)
- if not dumb:
- if fsize is not None:
- pct = recved * 100.0 / fsize
- print("\rDownloading %s: %5.1f%%" % (desc, pct), end="")
-
- sys.stdout.flush()
- writer.write(chunk)
-
- if not dumb:
- print()
- except urllib2.HTTPError, e:
- print("Download failed (%d): %s - %s" % (e.code, e.reason, src))
- if e.code == 403:
- print("No Rust compiler binary available for this platform. "
- "Please see https://github.com/servo/servo/#prerequisites")
- sys.exit(1)
- except urllib2.URLError, e:
- print("Error downloading Rust compiler: %s. The failing URL was: %s" % (e.reason, src))
- sys.exit(1)
- except socket_error, e:
- print("Looks like there's a connectivity issue, check your Internet connection. %s" % (e))
- sys.exit(1)
- except KeyboardInterrupt:
- writer.flush()
- raise
-
-
-def download_file(desc, src, dst):
- tmp_path = dst + ".part"
- try:
- start_byte = os.path.getsize(tmp_path)
- with open(tmp_path, 'ab') as fd:
- download(desc, src, fd, start_byte=start_byte)
- except os.error:
- with open(tmp_path, 'wb') as fd:
- download(desc, src, fd)
- os.rename(tmp_path, dst)
-
-
-def download_bytes(desc, src):
- content_writer = StringIO.StringIO()
- download(desc, src, content_writer)
- return content_writer.getvalue()
-
-
-def extract(src, dst, movedir=None):
- if src.endswith(".zip"):
- zipfile.ZipFile(src).extractall(dst)
- else:
- tarfile.open(src).extractall(dst)
-
- if movedir:
- for f in os.listdir(movedir):
- frm = path.join(movedir, f)
- to = path.join(dst, f)
- os.rename(frm, to)
- os.rmdir(movedir)
-
- os.remove(src)
+import servo.bootstrap as bootstrap
+from servo.command_base import CommandBase, BIN_SUFFIX
+from servo.util import download_bytes, download_file, extract, host_triple
@CommandProvider
@@ -133,20 +45,11 @@ class MachCommands(CommandBase):
@Command('bootstrap',
description='Install required packages for building.',
category='bootstrap')
- @CommandArgument('--interactive', "-i",
- action='store_true',
- help='Need to answer any (Y/n) interactive prompts.')
- @CommandArgument('--android',
- action='store_true',
- help='Install required packages for Android')
@CommandArgument('--force', '-f',
action='store_true',
- help='Force reinstall packages')
- def bootstrap(self, android=False, interactive=False, force=False):
- from servo.bootstrapper.bootstrap import Bootstrapper
-
- bootstrapper = Bootstrapper(self.context)
- bootstrapper.bootstrap(android=android, interactive=interactive, force=force)
+ help='Boostrap without confirmation')
+ def bootstrap(self, force=False):
+ return bootstrap.bootstrap(self.context, force=force)
@Command('bootstrap-rust',
description='Download the Rust compiler',
diff --git a/python/servo/bootstrapper/__init__.py b/python/servo/bootstrapper/__init__.py
deleted file mode 100644
index 6fbe8159b2d..00000000000
--- a/python/servo/bootstrapper/__init__.py
+++ /dev/null
@@ -1,3 +0,0 @@
-# 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/.
diff --git a/python/servo/bootstrapper/base.py b/python/servo/bootstrapper/base.py
deleted file mode 100644
index 40f3d1f5ebc..00000000000
--- a/python/servo/bootstrapper/base.py
+++ /dev/null
@@ -1,62 +0,0 @@
-# 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 distutils
-import subprocess
-
-
-class BaseBootstrapper(object):
- """Base class for system bootstrappers."""
-
- def __init__(self, interactive=False):
- self.package_manager_updated = False
- self.interactive = interactive
-
- def ensure_system_packages(self):
- '''
- Check for missing packages.
- '''
- raise NotImplementedError('%s must implement ensure_system_packages()' %
- __name__)
-
- def install_system_packages(self):
- '''
- Install packages required to build Servo.
- '''
- raise NotImplementedError('%s must implement install_system_packages()' %
- __name__)
-
- def install_mobile_android_packages(self):
- '''
- Install packages required to build Servo for Android.
- '''
- raise NotImplementedError('Cannot bootstrap Servo for Android: '
- '%s does not yet implement install_mobile_android_packages()'
- % __name__)
-
- def which(self, name):
- """Python implementation of which.
-
- It returns the path of an executable or None if it couldn't be found.
- """
- return distutils.spawn.find_executable(name)
-
- def check_output(self, *args, **kwargs):
- """Run subprocess.check_output."""
- return subprocess.check_output(*args, **kwargs)
-
- def _ensure_package_manager_updated(self):
- if self.package_manager_updated:
- return
-
- self._update_package_manager()
- self.package_manager_updated = True
-
- def _update_package_manager(self):
- """Updates the package manager's manifests/package list.
-
- This should be defined in child classes.
- """
diff --git a/python/servo/bootstrapper/bootstrap.py b/python/servo/bootstrapper/bootstrap.py
deleted file mode 100644
index d07dc12fc3c..00000000000
--- a/python/servo/bootstrapper/bootstrap.py
+++ /dev/null
@@ -1,42 +0,0 @@
-# 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
-
-import sys
-
-from windows_gnu import WindowsGnuBootstrapper
-from windows_msvc import WindowsMsvcBootstrapper
-
-
-class Bootstrapper(object):
- """Main class that performs system bootstrap."""
-
- def __init__(self, context):
- self.instance = None
- cls = None
- args = {}
-
- if sys.platform.startswith('msys'):
- cls = WindowsGnuBootstrapper
-
- elif sys.platform.startswith('win32'):
- cls = WindowsMsvcBootstrapper
-
- if cls is None:
- sys.exit('Bootstrap support is not yet available for your OS.')
-
- self.instance = cls(**args)
- self.instance.context = context
-
- def bootstrap(self, android=False, interactive=False, force=False):
- self.instance.interactive = interactive
- self.instance.force = force
-
- if android:
- self.instance.install_mobile_android_packages()
- elif force:
- self.instance.install_system_packages()
- else:
- self.instance.ensure_system_packages()
diff --git a/python/servo/bootstrapper/windows_gnu.py b/python/servo/bootstrapper/windows_gnu.py
deleted file mode 100644
index 794f1790cd1..00000000000
--- a/python/servo/bootstrapper/windows_gnu.py
+++ /dev/null
@@ -1,75 +0,0 @@
-# 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 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.'''
-
- def __init__(self, **kwargs):
- BaseBootstrapper.__init__(self, **kwargs)
-
- if not self.which('pacman'):
- raise NotImplementedError('The Windows bootstrapper only works with msys2 with pacman. Get msys2 at '
- 'http://msys2.github.io/')
-
- def ensure_system_packages(self):
- install_packages = []
- for p in deps:
- command = ['pacman', '-Qs', p]
- if self.run_check(command):
- install_packages += [p]
- if install_packages:
- install_packages(install_packages)
-
- def install_system_packages(self, packages=deps):
- self._ensure_package_manager_updated()
- self.pacman_install(*packages)
-
- def install_mobile_android_packages(self):
- sys.exit('We do not support building Android on Windows. Sorry!')
-
- 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)
-
- def pacman_upgrade(self):
- command = ['pacman', '--sync', '--refresh', '--sysupgrade']
- self.run(command)
-
- def pacman_install(self, *packages):
- command = ['pacman', '--sync']
- if not self.force:
- command.append('--needed')
- if not self.interactive:
- command.append('--noconfirm')
- command.extend(packages)
- self.run(command)
-
- # downgrade GCC to 5.4.0-1
- gcc_type = ["gcc", "gcc-ada", "gcc-fortran", "gcc-libgfortran", "gcc-libs", "gcc-objc"]
- gcc_version = "5.4.0-1"
- mingw_url = "http://repo.msys2.org/mingw/x86_64/mingw-w64-x86_64-{}-{}-any.pkg.tar.xz"
- gcc_list = []
- for gcc in gcc_type:
- gcc_list += [mingw_url.format(gcc, gcc_version)]
- downgrade_command = ['pacman', '-U']
- if not self.interactive:
- downgrade_command.append('--noconfirm')
- downgrade_command.extend(gcc_list)
- self.run(downgrade_command)
diff --git a/python/servo/bootstrapper/windows_msvc.py b/python/servo/bootstrapper/windows_msvc.py
deleted file mode 100644
index 11ff765c99d..00000000000
--- a/python/servo/bootstrapper/windows_msvc.py
+++ /dev/null
@@ -1,86 +0,0 @@
-# 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 os
-import sys
-import shutil
-from distutils import spawn
-
-from base import BaseBootstrapper
-from packages import WINDOWS_MSVC as deps
-
-
-class WindowsMsvcBootstrapper(BaseBootstrapper):
- '''Bootstrapper for MSVC building on Windows.'''
-
- def __init__(self, **kwargs):
- BaseBootstrapper.__init__(self, **kwargs)
-
- def ensure_system_packages(self):
- self.install_system_packages()
-
- def install_system_packages(self, packages=deps):
- from servo.bootstrap_commands import extract, download_file
-
- deps_dir = os.path.join(self.context.sharedir, "msvc-dependencies")
- deps_url = "https://servo-rust.s3.amazonaws.com/msvc-deps/"
- first_run = True
-
- if self.force:
- if os.path.isdir(deps_dir):
- shutil.rmtree(deps_dir)
-
- if not os.path.isdir(deps_dir):
- os.makedirs(deps_dir)
-
- # Read file with installed dependencies, if exist
- installed_deps_file = os.path.join(deps_dir, "installed-dependencies.txt")
- if os.path.exists(installed_deps_file):
- installed_deps = [l.strip() for l in open(installed_deps_file)]
- else:
- installed_deps = []
-
- # list of dependencies that need to be updated
- update_deps = list(set(packages) - set(installed_deps))
-
- for dep in packages:
- dep_name = dep.split("-")[0]
-
- # Don't download CMake if already exists in PATH
- if dep_name == "cmake":
- if spawn.find_executable(dep_name):
- continue
-
- dep_dir = os.path.join(deps_dir, dep_name)
- # if not installed or need to be updated
- if not os.path.exists(dep_dir) or dep in update_deps:
- if first_run:
- print "Installing missing MSVC dependencies..."
- first_run = False
-
- dep_version_dir = os.path.join(deps_dir, dep)
-
- if os.path.exists(dep_version_dir):
- shutil.rmtree(dep_version_dir)
-
- dep_zip = dep_version_dir + ".zip"
- if not os.path.isfile(dep_zip):
- download_file(dep, "%s%s.zip" % (deps_url, dep), dep_zip)
-
- print "Extracting %s..." % dep,
- extract(dep_zip, deps_dir)
- print "done"
-
- # Delete directory if exist
- if os.path.exists(dep_dir):
- shutil.rmtree(dep_dir)
- os.rename(dep_version_dir, dep_dir)
-
- # Write in installed-dependencies.txt file
- with open(installed_deps_file, 'w') as installed_file:
- for line in packages:
- installed_file.write(line + "\n")
-
- def install_mobile_android_packages(self):
- sys.exit('We do not support building Android on Windows. Sorry!')
diff --git a/python/servo/build_commands.py b/python/servo/build_commands.py
index 6e7ec8d24c5..5fbb7ba164d 100644
--- a/python/servo/build_commands.py
+++ b/python/servo/build_commands.py
@@ -24,7 +24,8 @@ from mach.decorators import (
Command,
)
-from servo.command_base import CommandBase, cd, call, BIN_SUFFIX, host_triple, find_dep_path_newest
+from servo.command_base import CommandBase, cd, call, BIN_SUFFIX, find_dep_path_newest
+from servo.util import host_triple
def format_duration(seconds):
diff --git a/python/servo/command_base.py b/python/servo/command_base.py
index 80fe675da1c..e80c9cb018b 100644
--- a/python/servo/command_base.py
+++ b/python/servo/command_base.py
@@ -18,11 +18,12 @@ import subprocess
from subprocess import PIPE
import sys
import tarfile
-import platform
+from mach.registrar import Registrar
import toml
-from mach.registrar import Registrar
+from servo.packages import WINDOWS_MSVC as msvc_deps
+from servo.util import host_triple
BIN_SUFFIX = ".exe" if sys.platform == "win32" else ""
@@ -107,51 +108,6 @@ def archive_deterministically(dir_to_archive, dest_archive, prepend_path=None):
os.rename(temp_file, dest_archive)
-def host_triple():
- os_type = platform.system().lower()
- if os_type == "linux":
- os_type = "unknown-linux-gnu"
- elif os_type == "darwin":
- os_type = "apple-darwin"
- elif os_type == "android":
- os_type = "linux-androideabi"
- elif os_type == "windows":
- # If we are in a Visual Studio environment, use msvc
- if os.getenv("PLATFORM") is not None:
- os_type = "pc-windows-msvc"
- elif os.getenv("MSYSTEM") is not None:
- os_type = "pc-windows-gnu"
- else:
- os_type = "unknown"
- elif os_type.startswith("mingw64_nt-") or os_type.startswith("cygwin_nt-"):
- os_type = "pc-windows-gnu"
- elif os_type == "freebsd":
- os_type = "unknown-freebsd"
- else:
- os_type = "unknown"
-
- cpu_type = platform.machine().lower()
- if os_type.endswith("-msvc"):
- # vcvars*.bat should set it properly
- platform_env = os.environ.get("PLATFORM")
- if platform_env == "X86":
- cpu_type = "i686"
- elif platform_env == "X64":
- cpu_type = "x86_64"
- else:
- cpu_type = "unknown"
- elif cpu_type in ["i386", "i486", "i686", "i768", "x86"]:
- cpu_type = "i686"
- elif cpu_type in ["x86_64", "x86-64", "x64", "amd64"]:
- cpu_type = "x86_64"
- elif cpu_type == "arm":
- cpu_type = "arm"
- else:
- cpu_type = "unknown"
-
- return "%s-%s" % (cpu_type, os_type)
-
-
def normalize_env(env):
# There is a bug in subprocess where it doesn't like unicode types in
# environment variables. Here, ensure all unicode are converted to
@@ -428,14 +384,18 @@ class CommandBase(object):
if "msvc" in (target or host_triple()):
msvc_x64 = "64" if "x86_64" in (target or host_triple()) else ""
msvc_deps_dir = path.join(self.context.sharedir, "msvc-dependencies")
- extra_path += [path.join(msvc_deps_dir, "cmake", "bin")]
- extra_path += [path.join(msvc_deps_dir, "ninja", "bin")]
+
+ def package_dir(package):
+ return path.join(msvc_deps_dir, package, msvc_deps[package])
+
+ extra_path += [path.join(package_dir("cmake"), "bin")]
+ extra_path += [path.join(package_dir("ninja"), "bin")]
# Link openssl
- env["OPENSSL_INCLUDE_DIR"] = path.join(msvc_deps_dir, "openssl", "include")
- env["OPENSSL_LIB_DIR"] = path.join(msvc_deps_dir, "openssl", "lib" + msvc_x64)
+ env["OPENSSL_INCLUDE_DIR"] = path.join(package_dir("openssl"), "include")
+ env["OPENSSL_LIB_DIR"] = path.join(package_dir("openssl"), "lib" + msvc_x64)
env["OPENSSL_LIBS"] = "ssleay32MD:libeay32MD"
# Link moztools
- env["MOZTOOLS_PATH"] = path.join(msvc_deps_dir, "moztools", "bin")
+ env["MOZTOOLS_PATH"] = path.join(package_dir("moztools"), "bin")
if is_windows():
if not os.environ.get("NATIVE_WIN32_PYTHON"):
diff --git a/python/servo/bootstrapper/packages.py b/python/servo/packages.py
index 7e9a7cd9c0e..9e4906dc5cf 100644
--- a/python/servo/bootstrapper/packages.py
+++ b/python/servo/packages.py
@@ -2,9 +2,9 @@
# 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/.
-# Listed all packages for different platforms in one file
-
-WINDOWS_GNU = [
+WINDOWS_GNU = set([
+ "diffutils",
+ "make",
"mingw-w64-x86_64-toolchain",
"mingw-w64-x86_64-freetype",
"mingw-w64-x86_64-icu",
@@ -12,17 +12,15 @@ WINDOWS_GNU = [
"mingw-w64-x86_64-ca-certificates",
"mingw-w64-x86_64-expat",
"mingw-w64-x86_64-cmake",
- "tar",
- "diffutils",
"patch",
"patchutils",
- "make",
"python2-setuptools",
-]
+ "tar",
+])
-WINDOWS_MSVC = [
- "cmake-3.6.1",
- "ninja-1.7.1",
- "openssl-1.0.1t-vs2015",
- "moztools-0.0.1-5",
-]
+WINDOWS_MSVC = {
+ "cmake": "3.6.1",
+ "moztools": "0.0.1-5",
+ "ninja": "1.7.1",
+ "openssl": "1.0.1t-vs2015",
+}
diff --git a/python/servo/testing_commands.py b/python/servo/testing_commands.py
index 04b0542a079..185e7420a47 100644
--- a/python/servo/testing_commands.py
+++ b/python/servo/testing_commands.py
@@ -30,8 +30,9 @@ from mach.decorators import (
from servo.command_base import (
BuildNotFound, CommandBase,
- call, cd, check_call, host_triple, set_osmesa_env,
+ call, cd, check_call, set_osmesa_env,
)
+from servo.util import host_triple
from wptrunner import wptcommandline
from update import updatecommandline
diff --git a/python/servo/util.py b/python/servo/util.py
new file mode 100644
index 00000000000..03f993b3bf0
--- /dev/null
+++ b/python/servo/util.py
@@ -0,0 +1,151 @@
+# Copyright 2013 The Servo Project Developers. See the COPYRIGHT
+# file at the top-level directory of this distribution.
+#
+# Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+# http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+# <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+# option. This file may not be copied, modified, or distributed
+# except according to those terms.
+
+from __future__ import absolute_import, print_function, unicode_literals
+
+import os
+import os.path as path
+import platform
+import sys
+from socket import error as socket_error
+import StringIO
+import tarfile
+import zipfile
+import urllib2
+
+
+def host_triple():
+ os_type = platform.system().lower()
+ if os_type == "linux":
+ os_type = "unknown-linux-gnu"
+ elif os_type == "darwin":
+ os_type = "apple-darwin"
+ elif os_type == "android":
+ os_type = "linux-androideabi"
+ elif os_type == "windows":
+ # If we are in a Visual Studio environment, use msvc
+ if os.getenv("PLATFORM") is not None:
+ os_type = "pc-windows-msvc"
+ elif os.getenv("MSYSTEM") is not None:
+ os_type = "pc-windows-gnu"
+ else:
+ os_type = "unknown"
+ elif os_type.startswith("mingw64_nt-") or os_type.startswith("cygwin_nt-"):
+ os_type = "pc-windows-gnu"
+ elif os_type == "freebsd":
+ os_type = "unknown-freebsd"
+ else:
+ os_type = "unknown"
+
+ cpu_type = platform.machine().lower()
+ if os_type.endswith("-msvc"):
+ # vcvars*.bat should set it properly
+ platform_env = os.environ.get("PLATFORM")
+ if platform_env == "X86":
+ cpu_type = "i686"
+ elif platform_env == "X64":
+ cpu_type = "x86_64"
+ else:
+ cpu_type = "unknown"
+ elif cpu_type in ["i386", "i486", "i686", "i768", "x86"]:
+ cpu_type = "i686"
+ elif cpu_type in ["x86_64", "x86-64", "x64", "amd64"]:
+ cpu_type = "x86_64"
+ elif cpu_type == "arm":
+ cpu_type = "arm"
+ else:
+ cpu_type = "unknown"
+
+ return "{}-{}".format(cpu_type, os_type)
+
+
+def download(desc, src, writer, start_byte=0):
+ if start_byte:
+ print("Resuming download of {}...".format(desc))
+ else:
+ print("Downloading {}...".format(desc))
+ dumb = (os.environ.get("TERM") == "dumb") or (not sys.stdout.isatty())
+
+ try:
+ req = urllib2.Request(src)
+ if start_byte:
+ req = urllib2.Request(src, headers={'Range': 'bytes={}-'.format(start_byte)})
+ resp = urllib2.urlopen(req)
+
+ fsize = None
+ if resp.info().getheader('Content-Length'):
+ fsize = int(resp.info().getheader('Content-Length').strip()) + start_byte
+
+ recved = start_byte
+ chunk_size = 8192
+
+ while True:
+ chunk = resp.read(chunk_size)
+ if not chunk:
+ break
+ recved += len(chunk)
+ if not dumb:
+ if fsize is not None:
+ pct = recved * 100.0 / fsize
+ print("\rDownloading %s: %5.1f%%" % (desc, pct), end="")
+
+ sys.stdout.flush()
+ writer.write(chunk)
+
+ if not dumb:
+ print()
+ except urllib2.HTTPError, e:
+ print("Download failed ({}): {} - {}".format(e.code, e.reason, src))
+ if e.code == 403:
+ print("No Rust compiler binary available for this platform. "
+ "Please see https://github.com/servo/servo/#prerequisites")
+ sys.exit(1)
+ except urllib2.URLError, e:
+ print("Error downloading {}: {}. The failing URL was: {}".format(desc, e.reason, src))
+ sys.exit(1)
+ except socket_error, e:
+ print("Looks like there's a connectivity issue, check your Internet connection. {}".format(e))
+ sys.exit(1)
+ except KeyboardInterrupt:
+ writer.flush()
+ raise
+
+
+def download_bytes(desc, src):
+ content_writer = StringIO.StringIO()
+ download(desc, src, content_writer)
+ return content_writer.getvalue()
+
+
+def download_file(desc, src, dst):
+ tmp_path = dst + ".part"
+ try:
+ start_byte = path.getsize(tmp_path)
+ with open(tmp_path, 'ab') as fd:
+ download(desc, src, fd, start_byte=start_byte)
+ except os.error:
+ with open(tmp_path, 'wb') as fd:
+ download(desc, src, fd)
+ os.rename(tmp_path, dst)
+
+
+def extract(src, dst, movedir=None):
+ if src.endswith(".zip"):
+ zipfile.ZipFile(src).extractall(dst)
+ else:
+ tarfile.open(src).extractall(dst)
+
+ if movedir:
+ for f in os.listdir(movedir):
+ frm = path.join(movedir, f)
+ to = path.join(dst, f)
+ os.rename(frm, to)
+ os.rmdir(movedir)
+
+ os.remove(src)