mirror of
https://github.com/servo/servo.git
synced 2025-06-08 08:33:26 +00:00
Introduce a new `BuildTarget` abstraction to centralize the code for supporting different ways of choosing the build target (e.g --android, --target x86_64-linux-android , --target aarch64-linux-ohos). This is currently handled in an adhoc fashion in different commands ( mach package, install, run) leading to a proliferation of keyword parameters for the commands and duplicated logic. The patch introduces a new `allow_target_configuration` decorator to do the validation and parsing of these parameters into the appropriate `BuildTarget` subclass, which is now stored as an instance attribute of the CommandBase class. All the code that previously relied on `self.cross_compile_target` has been switched to use the BuildTarget. Signed-off-by: Mukilan Thiyagarajan <mukilan@igalia.com>
178 lines
6.9 KiB
Python
178 lines
6.9 KiB
Python
# Copyright 2023 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.
|
|
|
|
import os
|
|
import subprocess
|
|
import tempfile
|
|
from typing import Optional
|
|
import urllib
|
|
import zipfile
|
|
|
|
from servo import util
|
|
|
|
from .base import Base
|
|
from .build_target import BuildTarget
|
|
|
|
DEPS_URL = "https://github.com/servo/servo-build-deps/releases/download/msvc-deps"
|
|
DEPENDENCIES = {
|
|
"moztools": "4.0",
|
|
}
|
|
|
|
GSTREAMER_URL = f"{DEPS_URL}/gstreamer-1.0-msvc-x86_64-1.22.8.msi"
|
|
GSTREAMER_DEVEL_URL = f"{DEPS_URL}/gstreamer-1.0-devel-msvc-x86_64-1.22.8.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)
|
|
self.is_windows = True
|
|
|
|
def executable_suffix(self):
|
|
return ".exe"
|
|
|
|
@classmethod
|
|
def download_and_extract_dependency(cls, zip_path: str, full_spec: str):
|
|
if not os.path.isfile(zip_path):
|
|
zip_url = f"{DEPS_URL}/{urllib.parse.quote(full_spec)}.zip"
|
|
util.download_file(full_spec, zip_url, zip_path)
|
|
|
|
zip_dir = os.path.dirname(zip_path)
|
|
print(f"Extracting {full_spec} to {zip_dir}...", end="")
|
|
try:
|
|
util.extract(zip_path, zip_dir)
|
|
except zipfile.BadZipfile:
|
|
print(f"\nError: {full_spec}.zip is not a valid zip file, redownload...")
|
|
os.remove(zip_path)
|
|
cls.download_and_extract_dependency(zip_path, full_spec)
|
|
else:
|
|
print("done")
|
|
|
|
def _platform_bootstrap(self, force: bool) -> bool:
|
|
installed_something = self.passive_bootstrap()
|
|
|
|
try:
|
|
choco_config = os.path.join(util.SERVO_ROOT, "support", "windows", "chocolatey.config")
|
|
|
|
# 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'"
|
|
|
|
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
|
|
|
|
target = BuildTarget.from_triple(None)
|
|
installed_something |= self._platform_bootstrap_gstreamer(target, force)
|
|
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 in to_install:
|
|
full_spec = "{}-{}".format(package, DEPENDENCIES[package])
|
|
|
|
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.download_and_extract_dependency(package_dir + ".zip", full_spec)
|
|
os.rename(os.path.join(parent_dir, full_spec), package_dir)
|
|
|
|
return True
|
|
|
|
def gstreamer_root(self, target: BuildTarget) -> Optional[str]:
|
|
build_target_triple = target.triple()
|
|
gst_arch_names = {
|
|
"x86_64": "X86_64",
|
|
"x86": "X86",
|
|
"aarch64": "ARM64",
|
|
}
|
|
gst_arch_name = gst_arch_names[build_target_triple.split("-")[0]]
|
|
|
|
# The bootstraped version of GStreamer always takes precedance of the installed vesion.
|
|
prepackaged_root = os.path.join(
|
|
DEPENDENCIES_DIR, "gstreamer", "1.0", f"msvc_{gst_arch_name}"
|
|
)
|
|
if os.path.exists(os.path.join(prepackaged_root, "bin", "ffi-7.dll")):
|
|
return prepackaged_root
|
|
|
|
# The installed version of GStreamer often sets an environment variable pointing to
|
|
# the install location.
|
|
root_from_env = os.environ.get(f"GSTREAMER_1_0_ROOT_MSVC_{gst_arch_name}")
|
|
if root_from_env and os.path.exists(os.path.join(root_from_env, "bin", "ffi-7.dll")):
|
|
return root_from_env
|
|
|
|
# If all else fails, look for an installation in the default install directory.
|
|
default_root = os.path.join("C:\\gstreamer\\1.0", f"msvc_{gst_arch_name}")
|
|
if os.path.exists(os.path.join(default_root, "bin", "ffi-7.dll")):
|
|
return default_root
|
|
|
|
return None
|
|
|
|
def is_gstreamer_installed(self, target: BuildTarget) -> bool:
|
|
return self.gstreamer_root(target) is not None
|
|
|
|
def _platform_bootstrap_gstreamer(self, target: BuildTarget, force: bool) -> bool:
|
|
if not force and self.is_gstreamer_installed(target):
|
|
return False
|
|
|
|
if "x86_64" not in self.triple:
|
|
print("Bootstrapping gstreamer not supported on "
|
|
"non-x86-64 Windows. Please install manually")
|
|
return False
|
|
|
|
with tempfile.TemporaryDirectory() as temp_dir:
|
|
libs_msi = os.path.join(temp_dir, GSTREAMER_URL.rsplit("/", maxsplit=1)[-1])
|
|
devel_msi = os.path.join(
|
|
temp_dir, GSTREAMER_DEVEL_URL.rsplit("/", maxsplit=1)[-1]
|
|
)
|
|
|
|
util.download_file("GStreamer libraries", GSTREAMER_URL, libs_msi)
|
|
util.download_file(
|
|
"GStreamer development support", GSTREAMER_DEVEL_URL, devel_msi
|
|
)
|
|
|
|
print(f"Installing GStreamer packages to {DEPENDENCIES_DIR}...")
|
|
os.makedirs(DEPENDENCIES_DIR, exist_ok=True)
|
|
|
|
for installer in [libs_msi, devel_msi]:
|
|
arguments = [
|
|
"/a",
|
|
f'"{installer}"'
|
|
f'TARGETDIR="{DEPENDENCIES_DIR}"', # Install destination
|
|
"/qn", # Quiet mode
|
|
]
|
|
quoted_arguments = ",".join((f"'{arg}'" for arg in arguments))
|
|
subprocess.check_call([
|
|
"powershell", "exit (Start-Process", "-PassThru", "-Wait", "-verb", "runAs",
|
|
"msiexec.exe", "-ArgumentList", f"@({quoted_arguments})", ").ExitCode"
|
|
])
|
|
|
|
assert self.is_gstreamer_installed(target)
|
|
return True
|