Windows bootstrap support

This commit is contained in:
Martin Robinson 2023-06-23 11:07:18 +02:00 committed by Martin Robinson
parent 35ab311635
commit 633d9b0eb9
10 changed files with 69 additions and 74 deletions

View file

@ -241,13 +241,8 @@ def bootstrap_command_only(topdir):
import servo.platform
import servo.util
# We are not set up yet, so we always use the default cache directory
# for the initial bootstrap.
# TODO(mrobinson): Why not just run the bootstrap command in this case?
try:
servo.platform.get().bootstrap(
servo.util.get_default_cache_dir(topdir), '-f' in sys.argv)
servo.platform.get().bootstrap('-f' in sys.argv or '--force' in sys.argv)
except NotImplementedError as exception:
print(exception)
return 1

View file

@ -46,7 +46,7 @@ class MachCommands(CommandBase):
# ./mach bootstrap calls mach_bootstrap.bootstrap_command_only so that
# it can install dependencies without needing mach's dependencies
try:
servo.platform.get().bootstrap(self.context.sharedir, force)
servo.platform.get().bootstrap(force)
except NotImplementedError as exception:
print(exception)
return 1
@ -60,7 +60,7 @@ class MachCommands(CommandBase):
help='Boostrap without confirmation')
def bootstrap_gstreamer(self, force=False):
try:
servo.platform.get().bootstrap_gstreamer(self.context.sharedir, force)
servo.platform.get().bootstrap_gstreamer(force)
except NotImplementedError as exception:
print(exception)
return 1

View file

@ -178,11 +178,11 @@ class MachCommands(CommandBase):
# Override any existing GStreamer installation with the vendored libraries.
env["GSTREAMER_1_0_ROOT_" + arch['gst']] = path.join(
self.msvc_package_dir("gstreamer-uwp"), arch['gst_root']
servo.platform.windows.get_dependency_dir("gstreamer-uwp"), arch['gst_root']
)
env["PKG_CONFIG_PATH"] = path.join(
self.msvc_package_dir("gstreamer-uwp"), arch['gst_root'],
"lib", "pkgconfig"
servo.platform.windows.get_dependency_dir("gstreamer-uwp"),
arch['gst_root'], "lib", "pkgconfig"
)
if 'windows' in host:

View file

@ -476,8 +476,7 @@ class CommandBase(object):
return self.get_executable(destination_folder)
def msvc_package_dir(self, package):
return path.join(self.context.sharedir, "msvc-dependencies", package,
servo.platform.windows.DEPENDENCIES[package])
return servo.platform.windows.get_dependency_dir(package)
def vs_dirs(self):
assert 'windows' in servo.platform.host_triple()
@ -508,11 +507,7 @@ class CommandBase(object):
extra_path = []
effective_target = self.cross_compile_target or servo.platform.host_triple()
if "msvc" in effective_target:
extra_path += [path.join(self.msvc_package_dir("cmake"), "bin")]
extra_path += [path.join(self.msvc_package_dir("llvm"), "bin")]
extra_path += [path.join(self.msvc_package_dir("ninja"), "bin")]
extra_path += [self.msvc_package_dir("nuget")]
env.setdefault("CC", "clang-cl.exe")
env.setdefault("CXX", "clang-cl.exe")
if self.is_uwp_build:
@ -911,10 +906,7 @@ class CommandBase(object):
if self.context.bootstrapped:
return
# Always check if all needed MSVC dependencies are installed
target_platform = self.cross_compile_target or servo.platform.host_triple()
if "msvc" in target_platform:
Registrar.dispatch("bootstrap", context=self.context)
servo.platform.get().passive_bootstrap()
if self.config["tools"]["use-rustup"]:
self.ensure_rustup_version()

View file

@ -75,7 +75,7 @@ class Base:
def executable_suffix(self):
return ""
def _platform_bootstrap(self, _cache_dir: str, _force: bool) -> bool:
def _platform_bootstrap(self, _force: bool) -> bool:
raise NotImplementedError("Bootstrap installation detection not yet available.")
def _platform_bootstrap_gstreamer(self, _force: bool) -> bool:
@ -97,11 +97,17 @@ class Base:
== 0
)
def bootstrap(self, cache_dir: str, force: bool):
if not self._platform_bootstrap(cache_dir, force):
def bootstrap(self, force: bool):
if not self._platform_bootstrap(force):
print("Dependencies were already installed!")
def bootstrap_gstreamer(self, _cache_dir: str, force: bool):
def passive_bootstrap(self) -> bool:
"""A bootstrap method that is called without explicitly invoking `./mach bootstrap`
but that is executed in the process of other `./mach` commands. This should be
as fast as possible."""
return False
def bootstrap_gstreamer(self, force: bool):
if not self._platform_bootstrap_gstreamer(force):
root = self.gstreamer_root(None)
if root:

View file

@ -94,7 +94,7 @@ class Linux(Base):
return (distrib, version)
def _platform_bootstrap(self, _cache_dir: str, force: bool) -> bool:
def _platform_bootstrap(self, force: bool) -> bool:
if self.distro.lower() == 'nixos':
print('NixOS does not need bootstrap, it will automatically enter a nix-shell')
print('Just run ./mach build')

View file

@ -54,7 +54,7 @@ class MacOS(Base):
return False
return True
def _platform_bootstrap(self, _cache_dir: str, force: bool) -> bool:
def _platform_bootstrap(self, _force: bool) -> bool:
installed_something = False
try:
brewfile = os.path.join(util.SERVO_ROOT, "etc", "homebrew", "Brewfile")

View file

@ -8,25 +8,19 @@
# except according to those terms.
import os
import shutil
import subprocess
import tempfile
from typing import Optional
import urllib
import zipfile
from distutils.version import LooseVersion
import six
from .. import util
from .base import Base
DEPS_URL = "https://github.com/servo/servo-build-deps/releases/download/msvc-deps/"
DEPENDENCIES = {
"cmake": "3.14.3",
"llvm": "15.0.5",
"moztools": "3.2",
"ninja": "1.7.1",
"nuget": "08-08-2019",
"openssl": "111.3.0+1.1.1c-vs2017-2019-09-18",
"gstreamer-uwp": "1.16.0.5",
"openxr-loader-uwp": "1.0",
@ -38,6 +32,11 @@ GSTREAMER_DEVEL_URL = f"{URL_BASE}/gstreamer-1.0-devel-msvc-x86_64-1.16.0.msi"
DEPENDENCIES_DIR = os.path.join(util.get_target_dir(), "dependencies")
def get_dependency_dir(package):
"""Get the directory that a given Windows dependency should extract to."""
return os.path.join(DEPENDENCIES_DIR, package, DEPENDENCIES[package])
class Windows(Base):
def __init__(self, triple: str):
super().__init__(triple)
@ -49,64 +48,65 @@ class Windows(Base):
def library_path_variable_name(self):
return "LIB"
@staticmethod
def cmake_already_installed(required_version: str) -> bool:
cmake_path = shutil.which("cmake")
if not cmake_path:
return False
output = subprocess.check_output([cmake_path, "--version"])
cmake_version_output = six.ensure_str(output).splitlines()[0]
installed_version = cmake_version_output.replace("cmake version ", "")
return LooseVersion(installed_version) >= LooseVersion(required_version)
@classmethod
def prepare_file(cls, deps_dir: str, zip_path: str, full_spec: str):
def download_and_extract_dependency(cls, zip_path: str, full_spec: str):
if not os.path.isfile(zip_path):
zip_url = "{}{}.zip".format(DEPS_URL, urllib.parse.quote(full_spec))
zip_url = f"{DEPS_URL}{urllib.parse.quote(full_spec)}.zip"
util.download_file(full_spec, zip_url, zip_path)
print("Extracting {}...".format(full_spec), end="")
zip_dir = os.path.dirname(zip_path)
print(f"Extracting {full_spec} to {zip_dir}...", end="")
try:
util.extract(zip_path, deps_dir)
util.extract(zip_path, zip_dir)
except zipfile.BadZipfile:
print("\nError: %s.zip is not a valid zip file, redownload..." % full_spec)
print(f"\nError: {full_spec}.zip is not a valid zip file, redownload...")
os.remove(zip_path)
cls.prepare_file(deps_dir, zip_path, full_spec)
cls.download_and_extract_dependency(zip_path, full_spec)
else:
print("done")
def _platform_bootstrap(self, cache_dir: str, _force: bool = False) -> bool:
deps_dir = os.path.join(cache_dir, "msvc-dependencies")
def _platform_bootstrap(self, force: bool = False) -> bool:
installed_something = self.passive_bootstrap()
def get_package_dir(package, version) -> str:
return os.path.join(deps_dir, package, version)
try:
choco_config = os.path.join(util.SERVO_ROOT, "support", "windows", "chocolatey.config")
to_install = {}
for package, version in DEPENDENCIES.items():
# Don't install CMake if it already exists in PATH
if package == "cmake" and self.cmake_already_installed(version):
continue
# This is the format that PowerShell wants arguments passed to it.
cmd_exe_args = f"'/K','choco','install','-y','{choco_config}'"
if force:
cmd_exe_args += ",'-f'"
if not os.path.isdir(get_package_dir(package, version)):
to_install[package] = version
print(cmd_exe_args)
subprocess.check_output([
"powershell", "Start-Process", "-Wait", "-verb", "runAs",
"cmd.exe", "-ArgumentList", f"@({cmd_exe_args})"
]).decode("utf-8")
except subprocess.CalledProcessError as e:
print("Could not run chocolatey. Follow manual build setup instructions.")
raise e
return installed_something
def passive_bootstrap(self) -> bool:
"""A bootstrap method that is called without explicitly invoking `./mach bootstrap`
but that is executed in the process of other `./mach` commands. This should be
as fast as possible."""
to_install = [package for package in DEPENDENCIES if
not os.path.isdir(get_dependency_dir(package))]
if not to_install:
return False
print("Installing missing MSVC dependencies...")
for package, version in to_install.items():
full_spec = "{}-{}".format(package, version)
for package in to_install:
full_spec = "{}-{}".format(package, DEPENDENCIES[package])
package_dir = get_package_dir(package, version)
package_dir = get_dependency_dir(package)
parent_dir = os.path.dirname(package_dir)
if not os.path.isdir(parent_dir):
os.makedirs(parent_dir)
self.prepare_file(deps_dir, package_dir + ".zip", full_spec)
extracted_path = os.path.join(deps_dir, full_spec)
os.rename(extracted_path, package_dir)
self.download_and_extract_dependency(package_dir + ".zip", full_spec)
os.rename(os.path.join(parent_dir, full_spec), package_dir)
return True