mirror of
https://github.com/servo/servo.git
synced 2025-07-22 23:03:42 +01:00
Windows bootstrap support
This commit is contained in:
parent
35ab311635
commit
633d9b0eb9
10 changed files with 69 additions and 74 deletions
11
README.md
11
README.md
|
@ -43,9 +43,12 @@ manually, try the [manual build setup][manual-build].
|
||||||
|
|
||||||
- Download and run [`rustup-init.exe`](https://win.rustup.rs/) then follow the onscreen instructions.
|
- Download and run [`rustup-init.exe`](https://win.rustup.rs/) then follow the onscreen instructions.
|
||||||
- Install [chocolatey](https://chocolatey.org/)
|
- Install [chocolatey](https://chocolatey.org/)
|
||||||
- Run `choco install support\windows\chocolatey.config`
|
- Run `mach bootstrap`
|
||||||
*This will install CMake, Git, LLVM, Ninja, NuGet, Python and the Visual Studio 2019 Build Tools.*
|
- *This will install CMake, Git, Ninja, NuGet, Python and the Visual Studio 2019 Build Tools
|
||||||
- Run `mach bootstrap-gstreamer`
|
via choco in an Administrator console. It can take quite a while.*
|
||||||
|
- *If you already have Visual Studio 2019 installed, this may not install all necessary components.
|
||||||
|
Please follow the Visual Studio 2019 installation instructions in the [manual setup][manual-build].*
|
||||||
|
- Run `refreshenv`
|
||||||
|
|
||||||
See also [Windows Troubleshooting Tips][windows-tips].
|
See also [Windows Troubleshooting Tips][windows-tips].
|
||||||
|
|
||||||
|
@ -174,4 +177,4 @@ There are lots of mach commands you can use. You can list them with `./mach
|
||||||
The generated documentation can be found on https://doc.servo.org/servo/index.html
|
The generated documentation can be found on https://doc.servo.org/servo/index.html
|
||||||
|
|
||||||
[manual-build]: https://github.com/servo/servo/wiki/Building#manual-build-setup
|
[manual-build]: https://github.com/servo/servo/wiki/Building#manual-build-setup
|
||||||
[windows-tips]: https://github.com/servo/servo/wiki/Building#troubleshooting-the-windows-build
|
[windows-tips]: https://github.com/servo/servo/wiki/Building#troubleshooting-the-windows-build
|
||||||
|
|
|
@ -241,13 +241,8 @@ def bootstrap_command_only(topdir):
|
||||||
import servo.platform
|
import servo.platform
|
||||||
import servo.util
|
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:
|
try:
|
||||||
servo.platform.get().bootstrap(
|
servo.platform.get().bootstrap('-f' in sys.argv or '--force' in sys.argv)
|
||||||
servo.util.get_default_cache_dir(topdir), '-f' in sys.argv)
|
|
||||||
except NotImplementedError as exception:
|
except NotImplementedError as exception:
|
||||||
print(exception)
|
print(exception)
|
||||||
return 1
|
return 1
|
||||||
|
|
|
@ -46,7 +46,7 @@ class MachCommands(CommandBase):
|
||||||
# ./mach bootstrap calls mach_bootstrap.bootstrap_command_only so that
|
# ./mach bootstrap calls mach_bootstrap.bootstrap_command_only so that
|
||||||
# it can install dependencies without needing mach's dependencies
|
# it can install dependencies without needing mach's dependencies
|
||||||
try:
|
try:
|
||||||
servo.platform.get().bootstrap(self.context.sharedir, force)
|
servo.platform.get().bootstrap(force)
|
||||||
except NotImplementedError as exception:
|
except NotImplementedError as exception:
|
||||||
print(exception)
|
print(exception)
|
||||||
return 1
|
return 1
|
||||||
|
@ -60,7 +60,7 @@ class MachCommands(CommandBase):
|
||||||
help='Boostrap without confirmation')
|
help='Boostrap without confirmation')
|
||||||
def bootstrap_gstreamer(self, force=False):
|
def bootstrap_gstreamer(self, force=False):
|
||||||
try:
|
try:
|
||||||
servo.platform.get().bootstrap_gstreamer(self.context.sharedir, force)
|
servo.platform.get().bootstrap_gstreamer(force)
|
||||||
except NotImplementedError as exception:
|
except NotImplementedError as exception:
|
||||||
print(exception)
|
print(exception)
|
||||||
return 1
|
return 1
|
||||||
|
|
|
@ -178,11 +178,11 @@ class MachCommands(CommandBase):
|
||||||
|
|
||||||
# Override any existing GStreamer installation with the vendored libraries.
|
# Override any existing GStreamer installation with the vendored libraries.
|
||||||
env["GSTREAMER_1_0_ROOT_" + arch['gst']] = path.join(
|
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(
|
env["PKG_CONFIG_PATH"] = path.join(
|
||||||
self.msvc_package_dir("gstreamer-uwp"), arch['gst_root'],
|
servo.platform.windows.get_dependency_dir("gstreamer-uwp"),
|
||||||
"lib", "pkgconfig"
|
arch['gst_root'], "lib", "pkgconfig"
|
||||||
)
|
)
|
||||||
|
|
||||||
if 'windows' in host:
|
if 'windows' in host:
|
||||||
|
|
|
@ -476,8 +476,7 @@ class CommandBase(object):
|
||||||
return self.get_executable(destination_folder)
|
return self.get_executable(destination_folder)
|
||||||
|
|
||||||
def msvc_package_dir(self, package):
|
def msvc_package_dir(self, package):
|
||||||
return path.join(self.context.sharedir, "msvc-dependencies", package,
|
return servo.platform.windows.get_dependency_dir(package)
|
||||||
servo.platform.windows.DEPENDENCIES[package])
|
|
||||||
|
|
||||||
def vs_dirs(self):
|
def vs_dirs(self):
|
||||||
assert 'windows' in servo.platform.host_triple()
|
assert 'windows' in servo.platform.host_triple()
|
||||||
|
@ -508,11 +507,7 @@ class CommandBase(object):
|
||||||
extra_path = []
|
extra_path = []
|
||||||
effective_target = self.cross_compile_target or servo.platform.host_triple()
|
effective_target = self.cross_compile_target or servo.platform.host_triple()
|
||||||
if "msvc" in effective_target:
|
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("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("CC", "clang-cl.exe")
|
||||||
env.setdefault("CXX", "clang-cl.exe")
|
env.setdefault("CXX", "clang-cl.exe")
|
||||||
if self.is_uwp_build:
|
if self.is_uwp_build:
|
||||||
|
@ -911,10 +906,7 @@ class CommandBase(object):
|
||||||
if self.context.bootstrapped:
|
if self.context.bootstrapped:
|
||||||
return
|
return
|
||||||
|
|
||||||
# Always check if all needed MSVC dependencies are installed
|
servo.platform.get().passive_bootstrap()
|
||||||
target_platform = self.cross_compile_target or servo.platform.host_triple()
|
|
||||||
if "msvc" in target_platform:
|
|
||||||
Registrar.dispatch("bootstrap", context=self.context)
|
|
||||||
|
|
||||||
if self.config["tools"]["use-rustup"]:
|
if self.config["tools"]["use-rustup"]:
|
||||||
self.ensure_rustup_version()
|
self.ensure_rustup_version()
|
||||||
|
|
|
@ -75,7 +75,7 @@ class Base:
|
||||||
def executable_suffix(self):
|
def executable_suffix(self):
|
||||||
return ""
|
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.")
|
raise NotImplementedError("Bootstrap installation detection not yet available.")
|
||||||
|
|
||||||
def _platform_bootstrap_gstreamer(self, _force: bool) -> bool:
|
def _platform_bootstrap_gstreamer(self, _force: bool) -> bool:
|
||||||
|
@ -97,11 +97,17 @@ class Base:
|
||||||
== 0
|
== 0
|
||||||
)
|
)
|
||||||
|
|
||||||
def bootstrap(self, cache_dir: str, force: bool):
|
def bootstrap(self, force: bool):
|
||||||
if not self._platform_bootstrap(cache_dir, force):
|
if not self._platform_bootstrap(force):
|
||||||
print("Dependencies were already installed!")
|
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):
|
if not self._platform_bootstrap_gstreamer(force):
|
||||||
root = self.gstreamer_root(None)
|
root = self.gstreamer_root(None)
|
||||||
if root:
|
if root:
|
||||||
|
|
|
@ -94,7 +94,7 @@ class Linux(Base):
|
||||||
|
|
||||||
return (distrib, version)
|
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':
|
if self.distro.lower() == 'nixos':
|
||||||
print('NixOS does not need bootstrap, it will automatically enter a nix-shell')
|
print('NixOS does not need bootstrap, it will automatically enter a nix-shell')
|
||||||
print('Just run ./mach build')
|
print('Just run ./mach build')
|
||||||
|
|
|
@ -54,7 +54,7 @@ class MacOS(Base):
|
||||||
return False
|
return False
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def _platform_bootstrap(self, _cache_dir: str, force: bool) -> bool:
|
def _platform_bootstrap(self, _force: bool) -> bool:
|
||||||
installed_something = False
|
installed_something = False
|
||||||
try:
|
try:
|
||||||
brewfile = os.path.join(util.SERVO_ROOT, "etc", "homebrew", "Brewfile")
|
brewfile = os.path.join(util.SERVO_ROOT, "etc", "homebrew", "Brewfile")
|
||||||
|
|
|
@ -8,25 +8,19 @@
|
||||||
# except according to those terms.
|
# except according to those terms.
|
||||||
|
|
||||||
import os
|
import os
|
||||||
import shutil
|
|
||||||
import subprocess
|
import subprocess
|
||||||
import tempfile
|
import tempfile
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
import urllib
|
import urllib
|
||||||
import zipfile
|
import zipfile
|
||||||
from distutils.version import LooseVersion
|
|
||||||
|
|
||||||
import six
|
|
||||||
from .. import util
|
from .. import util
|
||||||
from .base import Base
|
from .base import Base
|
||||||
|
|
||||||
DEPS_URL = "https://github.com/servo/servo-build-deps/releases/download/msvc-deps/"
|
DEPS_URL = "https://github.com/servo/servo-build-deps/releases/download/msvc-deps/"
|
||||||
DEPENDENCIES = {
|
DEPENDENCIES = {
|
||||||
"cmake": "3.14.3",
|
|
||||||
"llvm": "15.0.5",
|
"llvm": "15.0.5",
|
||||||
"moztools": "3.2",
|
"moztools": "3.2",
|
||||||
"ninja": "1.7.1",
|
|
||||||
"nuget": "08-08-2019",
|
|
||||||
"openssl": "111.3.0+1.1.1c-vs2017-2019-09-18",
|
"openssl": "111.3.0+1.1.1c-vs2017-2019-09-18",
|
||||||
"gstreamer-uwp": "1.16.0.5",
|
"gstreamer-uwp": "1.16.0.5",
|
||||||
"openxr-loader-uwp": "1.0",
|
"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")
|
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):
|
class Windows(Base):
|
||||||
def __init__(self, triple: str):
|
def __init__(self, triple: str):
|
||||||
super().__init__(triple)
|
super().__init__(triple)
|
||||||
|
@ -49,64 +48,65 @@ class Windows(Base):
|
||||||
def library_path_variable_name(self):
|
def library_path_variable_name(self):
|
||||||
return "LIB"
|
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
|
@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):
|
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)
|
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:
|
try:
|
||||||
util.extract(zip_path, deps_dir)
|
util.extract(zip_path, zip_dir)
|
||||||
except zipfile.BadZipfile:
|
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)
|
os.remove(zip_path)
|
||||||
cls.prepare_file(deps_dir, zip_path, full_spec)
|
cls.download_and_extract_dependency(zip_path, full_spec)
|
||||||
else:
|
else:
|
||||||
print("done")
|
print("done")
|
||||||
|
|
||||||
def _platform_bootstrap(self, cache_dir: str, _force: bool = False) -> bool:
|
def _platform_bootstrap(self, force: bool = False) -> bool:
|
||||||
deps_dir = os.path.join(cache_dir, "msvc-dependencies")
|
installed_something = self.passive_bootstrap()
|
||||||
|
|
||||||
def get_package_dir(package, version) -> str:
|
try:
|
||||||
return os.path.join(deps_dir, package, version)
|
choco_config = os.path.join(util.SERVO_ROOT, "support", "windows", "chocolatey.config")
|
||||||
|
|
||||||
to_install = {}
|
# This is the format that PowerShell wants arguments passed to it.
|
||||||
for package, version in DEPENDENCIES.items():
|
cmd_exe_args = f"'/K','choco','install','-y','{choco_config}'"
|
||||||
# Don't install CMake if it already exists in PATH
|
if force:
|
||||||
if package == "cmake" and self.cmake_already_installed(version):
|
cmd_exe_args += ",'-f'"
|
||||||
continue
|
|
||||||
|
|
||||||
if not os.path.isdir(get_package_dir(package, version)):
|
print(cmd_exe_args)
|
||||||
to_install[package] = version
|
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:
|
if not to_install:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
print("Installing missing MSVC dependencies...")
|
print("Installing missing MSVC dependencies...")
|
||||||
for package, version in to_install.items():
|
for package in to_install:
|
||||||
full_spec = "{}-{}".format(package, version)
|
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)
|
parent_dir = os.path.dirname(package_dir)
|
||||||
if not os.path.isdir(parent_dir):
|
if not os.path.isdir(parent_dir):
|
||||||
os.makedirs(parent_dir)
|
os.makedirs(parent_dir)
|
||||||
|
|
||||||
self.prepare_file(deps_dir, package_dir + ".zip", full_spec)
|
self.download_and_extract_dependency(package_dir + ".zip", full_spec)
|
||||||
|
os.rename(os.path.join(parent_dir, full_spec), package_dir)
|
||||||
extracted_path = os.path.join(deps_dir, full_spec)
|
|
||||||
os.rename(extracted_path, package_dir)
|
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,7 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<packages>
|
<packages>
|
||||||
<package id="cmake" version="3.26.4" />
|
<package id="cmake" version="3.26.4" installArguments="ADD_CMAKE_TO_PATH=System"/>
|
||||||
<package id="git"/>
|
<package id="git"/>
|
||||||
<package id="llvm"/>
|
|
||||||
<package id="ninja"/>
|
<package id="ninja"/>
|
||||||
<package id="nuget.commandline" version="6.6.0" />
|
<package id="nuget.commandline" version="6.6.0" />
|
||||||
<package id="python"/>
|
<package id="python"/>
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue