mirror of
https://github.com/servo/servo.git
synced 2025-08-05 13:40:08 +01:00
mach: Add type check on python/servo
directory (#38085)
Introduce `python/servo` folder in pyrefly type checker Testing: Manual testing via `./mach test-tidy` command --------- Signed-off-by: Jerens Lensun <jerensslensun@gmail.com>
This commit is contained in:
parent
056b1538c0
commit
93d234d270
21 changed files with 310 additions and 276 deletions
|
@ -36,8 +36,7 @@ search-path = [
|
||||||
"python/wpt",
|
"python/wpt",
|
||||||
]
|
]
|
||||||
project-includes = [
|
project-includes = [
|
||||||
"python/wpt/**/*.py",
|
"python/**/*.py",
|
||||||
"python/tidy/**/*.py",
|
|
||||||
]
|
]
|
||||||
project-excludes = [
|
project-excludes = [
|
||||||
"**/venv/**",
|
"**/venv/**",
|
||||||
|
|
|
@ -80,7 +80,7 @@ CATEGORIES = {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
def _process_exec(args, cwd):
|
def _process_exec(args: list[str], cwd) -> None:
|
||||||
try:
|
try:
|
||||||
subprocess.check_output(args, stderr=subprocess.STDOUT, cwd=cwd)
|
subprocess.check_output(args, stderr=subprocess.STDOUT, cwd=cwd)
|
||||||
except subprocess.CalledProcessError as exception:
|
except subprocess.CalledProcessError as exception:
|
||||||
|
@ -89,7 +89,7 @@ def _process_exec(args, cwd):
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
|
|
||||||
def install_virtual_env_requirements(project_path: str, marker_path: str):
|
def install_virtual_env_requirements(project_path: str, marker_path: str) -> None:
|
||||||
requirements_paths = [
|
requirements_paths = [
|
||||||
os.path.join(project_path, "python", "requirements.txt"),
|
os.path.join(project_path, "python", "requirements.txt"),
|
||||||
os.path.join(
|
os.path.join(
|
||||||
|
@ -127,7 +127,7 @@ def install_virtual_env_requirements(project_path: str, marker_path: str):
|
||||||
marker_file.write(requirements_hash)
|
marker_file.write(requirements_hash)
|
||||||
|
|
||||||
|
|
||||||
def _activate_virtualenv(topdir):
|
def _activate_virtualenv(topdir: str) -> None:
|
||||||
virtualenv_path = os.path.join(topdir, ".venv")
|
virtualenv_path = os.path.join(topdir, ".venv")
|
||||||
|
|
||||||
with open(".python-version", "r") as python_version_file:
|
with open(".python-version", "r") as python_version_file:
|
||||||
|
@ -151,7 +151,7 @@ def _activate_virtualenv(topdir):
|
||||||
warnings.filterwarnings("ignore", category=SyntaxWarning, module=r".*.venv")
|
warnings.filterwarnings("ignore", category=SyntaxWarning, module=r".*.venv")
|
||||||
|
|
||||||
|
|
||||||
def _ensure_case_insensitive_if_windows():
|
def _ensure_case_insensitive_if_windows() -> None:
|
||||||
# The folder is called 'python'. By deliberately checking for it with the wrong case, we determine if the file
|
# The folder is called 'python'. By deliberately checking for it with the wrong case, we determine if the file
|
||||||
# system is case sensitive or not.
|
# system is case sensitive or not.
|
||||||
if _is_windows() and not os.path.exists("Python"):
|
if _is_windows() and not os.path.exists("Python"):
|
||||||
|
@ -160,11 +160,11 @@ def _ensure_case_insensitive_if_windows():
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
|
|
||||||
def _is_windows():
|
def _is_windows() -> bool:
|
||||||
return sys.platform == "win32"
|
return sys.platform == "win32"
|
||||||
|
|
||||||
|
|
||||||
def bootstrap_command_only(topdir):
|
def bootstrap_command_only(topdir: str) -> int:
|
||||||
# we should activate the venv before importing servo.boostrap
|
# we should activate the venv before importing servo.boostrap
|
||||||
# because the module requires non-standard python packages
|
# because the module requires non-standard python packages
|
||||||
_activate_virtualenv(topdir)
|
_activate_virtualenv(topdir)
|
||||||
|
@ -188,7 +188,7 @@ def bootstrap_command_only(topdir):
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
|
|
||||||
def bootstrap(topdir):
|
def bootstrap(topdir: str):
|
||||||
_ensure_case_insensitive_if_windows()
|
_ensure_case_insensitive_if_windows()
|
||||||
|
|
||||||
topdir = os.path.abspath(topdir)
|
topdir = os.path.abspath(topdir)
|
||||||
|
@ -215,6 +215,7 @@ def bootstrap(topdir):
|
||||||
import mach.main
|
import mach.main
|
||||||
|
|
||||||
mach = mach.main.Mach(os.getcwd())
|
mach = mach.main.Mach(os.getcwd())
|
||||||
|
# pyrefly: ignore[bad-assignment]
|
||||||
mach.populate_context_handler = populate_context
|
mach.populate_context_handler = populate_context
|
||||||
|
|
||||||
for category, meta in CATEGORIES.items():
|
for category, meta in CATEGORIES.items():
|
||||||
|
|
|
@ -18,7 +18,7 @@ import subprocess
|
||||||
import sys
|
import sys
|
||||||
import tempfile
|
import tempfile
|
||||||
import traceback
|
import traceback
|
||||||
import urllib
|
import urllib.error
|
||||||
|
|
||||||
import toml
|
import toml
|
||||||
|
|
||||||
|
@ -40,7 +40,7 @@ class MachCommands(CommandBase):
|
||||||
@CommandArgument("--force", "-f", action="store_true", help="Boostrap without confirmation")
|
@CommandArgument("--force", "-f", action="store_true", help="Boostrap without confirmation")
|
||||||
@CommandArgument("--skip-platform", action="store_true", help="Skip platform bootstrapping.")
|
@CommandArgument("--skip-platform", action="store_true", help="Skip platform bootstrapping.")
|
||||||
@CommandArgument("--skip-lints", action="store_true", help="Skip tool necessary for linting.")
|
@CommandArgument("--skip-lints", action="store_true", help="Skip tool necessary for linting.")
|
||||||
def bootstrap(self, force=False, skip_platform=False, skip_lints=False):
|
def bootstrap(self, force=False, skip_platform=False, skip_lints=False) -> int:
|
||||||
# Note: This entry point isn't actually invoked by ./mach bootstrap.
|
# Note: This entry point isn't actually invoked by ./mach bootstrap.
|
||||||
# ./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
|
||||||
|
@ -57,7 +57,7 @@ class MachCommands(CommandBase):
|
||||||
category="bootstrap",
|
category="bootstrap",
|
||||||
)
|
)
|
||||||
@CommandArgument("--force", "-f", action="store_true", help="Boostrap without confirmation")
|
@CommandArgument("--force", "-f", action="store_true", help="Boostrap without confirmation")
|
||||||
def bootstrap_gstreamer(self, force=False):
|
def bootstrap_gstreamer(self, force=False) -> int:
|
||||||
try:
|
try:
|
||||||
servo.platform.get().bootstrap_gstreamer(force)
|
servo.platform.get().bootstrap_gstreamer(force)
|
||||||
except NotImplementedError as exception:
|
except NotImplementedError as exception:
|
||||||
|
@ -66,7 +66,7 @@ class MachCommands(CommandBase):
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
@Command("update-hsts-preload", description="Download the HSTS preload list", category="bootstrap")
|
@Command("update-hsts-preload", description="Download the HSTS preload list", category="bootstrap")
|
||||||
def bootstrap_hsts_preload(self, force=False):
|
def bootstrap_hsts_preload(self, force=False) -> None:
|
||||||
preload_filename = "hsts_preload.fstmap"
|
preload_filename = "hsts_preload.fstmap"
|
||||||
preload_path = path.join(self.context.topdir, "resources")
|
preload_path = path.join(self.context.topdir, "resources")
|
||||||
|
|
||||||
|
@ -104,7 +104,7 @@ class MachCommands(CommandBase):
|
||||||
description="Download the public domains list and update resources/public_domains.txt",
|
description="Download the public domains list and update resources/public_domains.txt",
|
||||||
category="bootstrap",
|
category="bootstrap",
|
||||||
)
|
)
|
||||||
def bootstrap_pub_suffix(self, force=False):
|
def bootstrap_pub_suffix(self, force=False) -> None:
|
||||||
list_url = "https://publicsuffix.org/list/public_suffix_list.dat"
|
list_url = "https://publicsuffix.org/list/public_suffix_list.dat"
|
||||||
dst_filename = path.join(self.context.topdir, "resources", "public_domains.txt")
|
dst_filename = path.join(self.context.topdir, "resources", "public_domains.txt")
|
||||||
not_implemented_case = re.compile(r"^[^*]+\*")
|
not_implemented_case = re.compile(r"^[^*]+\*")
|
||||||
|
@ -122,12 +122,12 @@ class MachCommands(CommandBase):
|
||||||
for suffix in suffixes:
|
for suffix in suffixes:
|
||||||
if not_implemented_case.match(suffix):
|
if not_implemented_case.match(suffix):
|
||||||
print("Warning: the new list contains a case that servo can't handle: %s" % suffix)
|
print("Warning: the new list contains a case that servo can't handle: %s" % suffix)
|
||||||
fo.write(suffix.encode("idna") + "\n")
|
fo.write(suffix.encode("idna") + b"\n")
|
||||||
|
|
||||||
@Command("clean-nightlies", description="Clean unused nightly builds of Rust and Cargo", category="bootstrap")
|
@Command("clean-nightlies", description="Clean unused nightly builds of Rust and Cargo", category="bootstrap")
|
||||||
@CommandArgument("--force", "-f", action="store_true", help="Actually remove stuff")
|
@CommandArgument("--force", "-f", action="store_true", help="Actually remove stuff")
|
||||||
@CommandArgument("--keep", default="1", help="Keep up to this many most recent nightlies")
|
@CommandArgument("--keep", default="1", help="Keep up to this many most recent nightlies")
|
||||||
def clean_nightlies(self, force=False, keep=None):
|
def clean_nightlies(self, force: bool, keep: str | int) -> None:
|
||||||
print(f"Current Rust version for Servo: {self.rust_toolchain()}")
|
print(f"Current Rust version for Servo: {self.rust_toolchain()}")
|
||||||
old_toolchains = []
|
old_toolchains = []
|
||||||
keep = int(keep)
|
keep = int(keep)
|
||||||
|
@ -158,8 +158,8 @@ class MachCommands(CommandBase):
|
||||||
@CommandArgument("--force", "-f", action="store_true", help="Actually remove stuff")
|
@CommandArgument("--force", "-f", action="store_true", help="Actually remove stuff")
|
||||||
@CommandArgument("--show-size", "-s", action="store_true", help="Show packages size")
|
@CommandArgument("--show-size", "-s", action="store_true", help="Show packages size")
|
||||||
@CommandArgument("--keep", default="1", help="Keep up to this many most recent dependencies")
|
@CommandArgument("--keep", default="1", help="Keep up to this many most recent dependencies")
|
||||||
def clean_cargo_cache(self, force=False, show_size=False, keep=None):
|
def clean_cargo_cache(self, force: bool, show_size: bool, keep: str) -> None:
|
||||||
def get_size(path):
|
def get_size(path: str) -> float:
|
||||||
if os.path.isfile(path):
|
if os.path.isfile(path):
|
||||||
return os.path.getsize(path) / (1024 * 1024.0)
|
return os.path.getsize(path) / (1024 * 1024.0)
|
||||||
total_size = 0
|
total_size = 0
|
||||||
|
@ -177,7 +177,7 @@ class MachCommands(CommandBase):
|
||||||
import toml
|
import toml
|
||||||
|
|
||||||
if os.environ.get("CARGO_HOME", ""):
|
if os.environ.get("CARGO_HOME", ""):
|
||||||
cargo_dir = os.environ.get("CARGO_HOME")
|
cargo_dir = os.environ["CARGO_HOME"]
|
||||||
else:
|
else:
|
||||||
home_dir = os.path.expanduser("~")
|
home_dir = os.path.expanduser("~")
|
||||||
cargo_dir = path.join(home_dir, ".cargo")
|
cargo_dir = path.join(home_dir, ".cargo")
|
||||||
|
@ -265,7 +265,7 @@ class MachCommands(CommandBase):
|
||||||
}
|
}
|
||||||
packages["crates"][crate_name]["exist"].append(d)
|
packages["crates"][crate_name]["exist"].append(d)
|
||||||
|
|
||||||
total_size = 0
|
total_size = 0.0
|
||||||
for packages_type in ["git", "crates"]:
|
for packages_type in ["git", "crates"]:
|
||||||
sorted_packages = sorted(packages[packages_type])
|
sorted_packages = sorted(packages[packages_type])
|
||||||
for crate_name in sorted_packages:
|
for crate_name in sorted_packages:
|
||||||
|
@ -309,7 +309,7 @@ class MachCommands(CommandBase):
|
||||||
crate_paths.append(path.join(crates_cache_dir, "{}.crate".format(exist)))
|
crate_paths.append(path.join(crates_cache_dir, "{}.crate".format(exist)))
|
||||||
crate_paths.append(path.join(crates_src_dir, exist))
|
crate_paths.append(path.join(crates_src_dir, exist))
|
||||||
|
|
||||||
size = sum(get_size(p) for p in crate_paths) if show_size else 0
|
size = sum((get_size(p) for p in crate_paths), 0.0) if show_size else 0.0
|
||||||
total_size += size
|
total_size += size
|
||||||
print_msg = (exist_name, " ({}MB)".format(round(size, 2)) if show_size else "", cargo_dir)
|
print_msg = (exist_name, " ({}MB)".format(round(size, 2)) if show_size else "", cargo_dir)
|
||||||
if force:
|
if force:
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
|
|
||||||
import datetime
|
import datetime
|
||||||
import os
|
import os
|
||||||
|
from os import PathLike
|
||||||
import os.path as path
|
import os.path as path
|
||||||
import pathlib
|
import pathlib
|
||||||
import shutil
|
import shutil
|
||||||
|
@ -17,7 +18,7 @@ import subprocess
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
from time import time
|
from time import time
|
||||||
from typing import Optional, List, Dict
|
from typing import Optional, List, Dict, Union
|
||||||
|
|
||||||
from mach.decorators import (
|
from mach.decorators import (
|
||||||
CommandArgument,
|
CommandArgument,
|
||||||
|
@ -71,7 +72,7 @@ def get_rustc_llvm_version() -> Optional[List[int]]:
|
||||||
llvm_version = llvm_version.strip()
|
llvm_version = llvm_version.strip()
|
||||||
version = llvm_version.split(".")
|
version = llvm_version.split(".")
|
||||||
print(f"Info: rustc is using LLVM version {'.'.join(version)}")
|
print(f"Info: rustc is using LLVM version {'.'.join(version)}")
|
||||||
return version
|
return list(map(int, version))
|
||||||
else:
|
else:
|
||||||
print(f"Error: Couldn't find LLVM version in output of `rustc --version --verbose`: `{result.stdout}`")
|
print(f"Error: Couldn't find LLVM version in output of `rustc --version --verbose`: `{result.stdout}`")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
@ -101,7 +102,7 @@ class MachCommands(CommandBase):
|
||||||
sanitizer: SanitizerKind = SanitizerKind.NONE,
|
sanitizer: SanitizerKind = SanitizerKind.NONE,
|
||||||
flavor=None,
|
flavor=None,
|
||||||
**kwargs,
|
**kwargs,
|
||||||
):
|
) -> int:
|
||||||
opts = params or []
|
opts = params or []
|
||||||
|
|
||||||
if build_type.is_release():
|
if build_type.is_release():
|
||||||
|
@ -178,7 +179,7 @@ class MachCommands(CommandBase):
|
||||||
# On the Mac, set a lovely icon. This makes it easier to pick out the Servo binary in tools
|
# On the Mac, set a lovely icon. This makes it easier to pick out the Servo binary in tools
|
||||||
# like Instruments.app.
|
# like Instruments.app.
|
||||||
try:
|
try:
|
||||||
import Cocoa
|
import Cocoa # pyrefly: ignore[import-error]
|
||||||
|
|
||||||
icon_path = path.join(self.get_top_dir(), "resources", "servo_1024.png")
|
icon_path = path.join(self.get_top_dir(), "resources", "servo_1024.png")
|
||||||
icon = Cocoa.NSImage.alloc().initWithContentsOfFile_(icon_path)
|
icon = Cocoa.NSImage.alloc().initWithContentsOfFile_(icon_path)
|
||||||
|
@ -192,14 +193,14 @@ class MachCommands(CommandBase):
|
||||||
elapsed_delta = datetime.timedelta(seconds=int(elapsed))
|
elapsed_delta = datetime.timedelta(seconds=int(elapsed))
|
||||||
build_message = f"{'Succeeded' if status == 0 else 'Failed'} in {elapsed_delta}"
|
build_message = f"{'Succeeded' if status == 0 else 'Failed'} in {elapsed_delta}"
|
||||||
print(build_message)
|
print(build_message)
|
||||||
|
assert isinstance(status, int)
|
||||||
return status
|
return status
|
||||||
|
|
||||||
@Command("clean", description="Clean the target/ and Python virtual environment directories", category="build")
|
@Command("clean", description="Clean the target/ and Python virtual environment directories", category="build")
|
||||||
@CommandArgument("--manifest-path", default=None, help="Path to the manifest to the package to clean")
|
@CommandArgument("--manifest-path", default=None, help="Path to the manifest to the package to clean")
|
||||||
@CommandArgument("--verbose", "-v", action="store_true", help="Print verbose output")
|
@CommandArgument("--verbose", "-v", action="store_true", help="Print verbose output")
|
||||||
@CommandArgument("params", nargs="...", help="Command-line arguments to be passed through to Cargo")
|
@CommandArgument("params", nargs="...", help="Command-line arguments to be passed through to Cargo")
|
||||||
def clean(self, manifest_path=None, params=[], verbose=False):
|
def clean(self, manifest_path=None, params=[], verbose=False) -> None:
|
||||||
self.ensure_bootstrapped()
|
self.ensure_bootstrapped()
|
||||||
|
|
||||||
virtualenv_path = path.join(self.get_top_dir(), ".venv")
|
virtualenv_path = path.join(self.get_top_dir(), ".venv")
|
||||||
|
@ -214,8 +215,8 @@ class MachCommands(CommandBase):
|
||||||
return check_call(["cargo", "clean"] + opts, env=self.build_env(), verbose=verbose)
|
return check_call(["cargo", "clean"] + opts, env=self.build_env(), verbose=verbose)
|
||||||
|
|
||||||
def build_sanitizer_env(
|
def build_sanitizer_env(
|
||||||
self, env: Dict, opts: List[str], kwargs, target_triple, sanitizer: SanitizerKind = SanitizerKind.NONE
|
self, env: Dict, opts: List[str], kwargs, target_triple: str, sanitizer: SanitizerKind = SanitizerKind.NONE
|
||||||
):
|
) -> None:
|
||||||
if sanitizer.is_none():
|
if sanitizer.is_none():
|
||||||
return
|
return
|
||||||
# do not use crown (clashes with different rust version)
|
# do not use crown (clashes with different rust version)
|
||||||
|
@ -293,7 +294,7 @@ def copy_windows_dlls_to_build_directory(servo_binary: str, target: BuildTarget)
|
||||||
|
|
||||||
# Copy in the built EGL and GLES libraries from where they were built to
|
# Copy in the built EGL and GLES libraries from where they were built to
|
||||||
# the final build dirctory
|
# the final build dirctory
|
||||||
def find_and_copy_built_dll(dll_name):
|
def find_and_copy_built_dll(dll_name: str) -> None:
|
||||||
try:
|
try:
|
||||||
file_to_copy = next(pathlib.Path(build_path).rglob(dll_name))
|
file_to_copy = next(pathlib.Path(build_path).rglob(dll_name))
|
||||||
shutil.copy(file_to_copy, servo_exe_dir)
|
shutil.copy(file_to_copy, servo_exe_dir)
|
||||||
|
@ -315,7 +316,7 @@ def copy_windows_dlls_to_build_directory(servo_binary: str, target: BuildTarget)
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
def package_gstreamer_dlls(servo_exe_dir: str, target: BuildTarget):
|
def package_gstreamer_dlls(servo_exe_dir: str, target: BuildTarget) -> bool:
|
||||||
gst_root = servo.platform.get().gstreamer_root(target)
|
gst_root = servo.platform.get().gstreamer_root(target)
|
||||||
if not gst_root:
|
if not gst_root:
|
||||||
print("Could not find GStreamer installation directory.")
|
print("Could not find GStreamer installation directory.")
|
||||||
|
@ -354,8 +355,8 @@ def package_gstreamer_dlls(servo_exe_dir: str, target: BuildTarget):
|
||||||
return not missing
|
return not missing
|
||||||
|
|
||||||
|
|
||||||
def package_msvc_dlls(servo_exe_dir: str, target: BuildTarget):
|
def package_msvc_dlls(servo_exe_dir: str, target: BuildTarget) -> bool:
|
||||||
def copy_file(dll_path: Optional[str]) -> bool:
|
def copy_file(dll_path: Union[PathLike[str], str]) -> bool:
|
||||||
if not dll_path or not os.path.exists(dll_path):
|
if not dll_path or not os.path.exists(dll_path):
|
||||||
print(f"WARNING: Could not find DLL at {dll_path}", file=sys.stderr)
|
print(f"WARNING: Could not find DLL at {dll_path}", file=sys.stderr)
|
||||||
return False
|
return False
|
||||||
|
|
|
@ -15,20 +15,20 @@ import gzip
|
||||||
import itertools
|
import itertools
|
||||||
import locale
|
import locale
|
||||||
import os
|
import os
|
||||||
import re
|
|
||||||
import shutil
|
import shutil
|
||||||
import subprocess
|
import subprocess
|
||||||
import sys
|
import sys
|
||||||
import tarfile
|
import tarfile
|
||||||
import urllib
|
import urllib
|
||||||
import zipfile
|
import zipfile
|
||||||
|
import urllib.error
|
||||||
|
import urllib.request
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
from enum import Enum
|
from enum import Enum
|
||||||
from errno import ENOENT as NO_SUCH_FILE_OR_DIRECTORY
|
|
||||||
from glob import glob
|
from glob import glob
|
||||||
from os import path
|
from os import path
|
||||||
from subprocess import PIPE
|
from subprocess import PIPE, CompletedProcess
|
||||||
from typing import Any, Dict, List, Optional
|
from typing import Any, Dict, List, Optional, Union, LiteralString, cast
|
||||||
from xml.etree.ElementTree import XML
|
from xml.etree.ElementTree import XML
|
||||||
|
|
||||||
import toml
|
import toml
|
||||||
|
@ -54,13 +54,13 @@ class BuildType:
|
||||||
CUSTOM = 3
|
CUSTOM = 3
|
||||||
|
|
||||||
kind: Kind
|
kind: Kind
|
||||||
profile: Optional[str]
|
profile: str
|
||||||
|
|
||||||
def dev() -> BuildType:
|
def dev() -> BuildType:
|
||||||
return BuildType(BuildType.Kind.DEV, None)
|
return BuildType(BuildType.Kind.DEV, "debug")
|
||||||
|
|
||||||
def release() -> BuildType:
|
def release() -> BuildType:
|
||||||
return BuildType(BuildType.Kind.RELEASE, None)
|
return BuildType(BuildType.Kind.RELEASE, "release")
|
||||||
|
|
||||||
def prod() -> BuildType:
|
def prod() -> BuildType:
|
||||||
return BuildType(BuildType.Kind.CUSTOM, "production")
|
return BuildType(BuildType.Kind.CUSTOM, "production")
|
||||||
|
@ -93,7 +93,7 @@ class BuildType:
|
||||||
|
|
||||||
|
|
||||||
@contextlib.contextmanager
|
@contextlib.contextmanager
|
||||||
def cd(new_path):
|
def cd(new_path: str):
|
||||||
"""Context manager for changing the current working directory"""
|
"""Context manager for changing the current working directory"""
|
||||||
previous_path = os.getcwd()
|
previous_path = os.getcwd()
|
||||||
try:
|
try:
|
||||||
|
@ -104,7 +104,7 @@ def cd(new_path):
|
||||||
|
|
||||||
|
|
||||||
@contextlib.contextmanager
|
@contextlib.contextmanager
|
||||||
def setlocale(name):
|
def setlocale(name: str):
|
||||||
"""Context manager for changing the current locale"""
|
"""Context manager for changing the current locale"""
|
||||||
saved_locale = locale.setlocale(locale.LC_ALL)
|
saved_locale = locale.setlocale(locale.LC_ALL)
|
||||||
try:
|
try:
|
||||||
|
@ -126,7 +126,7 @@ def find_dep_path_newest(package, bin_path):
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
def archive_deterministically(dir_to_archive, dest_archive, prepend_path=None):
|
def archive_deterministically(dir_to_archive, dest_archive, prepend_path=None) -> None:
|
||||||
"""Create a .tar.gz archive in a deterministic (reproducible) manner.
|
"""Create a .tar.gz archive in a deterministic (reproducible) manner.
|
||||||
|
|
||||||
See https://reproducible-builds.org/docs/archives/ for more details."""
|
See https://reproducible-builds.org/docs/archives/ for more details."""
|
||||||
|
@ -177,27 +177,29 @@ def archive_deterministically(dir_to_archive, dest_archive, prepend_path=None):
|
||||||
os.rename(temp_file, dest_archive)
|
os.rename(temp_file, dest_archive)
|
||||||
|
|
||||||
|
|
||||||
def call(*args, **kwargs):
|
def call(*args, **kwargs) -> int:
|
||||||
|
"""Wrap `subprocess.call`, printing the command if verbose=True."""
|
||||||
|
verbose = kwargs.pop("verbose", False)
|
||||||
|
if verbose:
|
||||||
|
print(" ".join(args[0]))
|
||||||
|
# we have to use shell=True in order to get PATH handling
|
||||||
|
# when looking for the binary on Windows\
|
||||||
|
kwargs.setdefault("shell", sys.platform == "win32")
|
||||||
|
return subprocess.call(*args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
def check_output(*args, **kwargs) -> Union[str, bytes]:
|
||||||
"""Wrap `subprocess.call`, printing the command if verbose=True."""
|
"""Wrap `subprocess.call`, printing the command if verbose=True."""
|
||||||
verbose = kwargs.pop("verbose", False)
|
verbose = kwargs.pop("verbose", False)
|
||||||
if verbose:
|
if verbose:
|
||||||
print(" ".join(args[0]))
|
print(" ".join(args[0]))
|
||||||
# we have to use shell=True in order to get PATH handling
|
# we have to use shell=True in order to get PATH handling
|
||||||
# when looking for the binary on Windows
|
# when looking for the binary on Windows
|
||||||
return subprocess.call(*args, shell=sys.platform == "win32", **kwargs)
|
kwargs.setdefault("shell", sys.platform == "win32")
|
||||||
|
return subprocess.check_output(*args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
def check_output(*args, **kwargs) -> bytes:
|
def check_call(*args, **kwargs) -> None:
|
||||||
"""Wrap `subprocess.call`, printing the command if verbose=True."""
|
|
||||||
verbose = kwargs.pop("verbose", False)
|
|
||||||
if verbose:
|
|
||||||
print(" ".join(args[0]))
|
|
||||||
# we have to use shell=True in order to get PATH handling
|
|
||||||
# when looking for the binary on Windows
|
|
||||||
return subprocess.check_output(*args, shell=sys.platform == "win32", **kwargs)
|
|
||||||
|
|
||||||
|
|
||||||
def check_call(*args, **kwargs):
|
|
||||||
"""Wrap `subprocess.check_call`, printing the command if verbose=True.
|
"""Wrap `subprocess.check_call`, printing the command if verbose=True.
|
||||||
|
|
||||||
Also fix any unicode-containing `env`, for subprocess"""
|
Also fix any unicode-containing `env`, for subprocess"""
|
||||||
|
@ -207,8 +209,9 @@ def check_call(*args, **kwargs):
|
||||||
print(" ".join(args[0]))
|
print(" ".join(args[0]))
|
||||||
# we have to use shell=True in order to get PATH handling
|
# we have to use shell=True in order to get PATH handling
|
||||||
# when looking for the binary on Windows
|
# when looking for the binary on Windows
|
||||||
proc = subprocess.Popen(*args, shell=sys.platform == "win32", **kwargs)
|
kwargs.setdefault("shell", sys.platform == "win32")
|
||||||
status = None
|
proc = subprocess.Popen(*args, **kwargs)
|
||||||
|
status: Optional[int] = None
|
||||||
# Leave it to the subprocess to handle Ctrl+C. If it terminates as
|
# Leave it to the subprocess to handle Ctrl+C. If it terminates as
|
||||||
# a result of Ctrl+C, proc.wait() will return a status code, and,
|
# a result of Ctrl+C, proc.wait() will return a status code, and,
|
||||||
# we get out of the loop. If it doesn't, like e.g. gdb, we continue
|
# we get out of the loop. If it doesn't, like e.g. gdb, we continue
|
||||||
|
@ -223,20 +226,20 @@ def check_call(*args, **kwargs):
|
||||||
raise subprocess.CalledProcessError(status, " ".join(*args))
|
raise subprocess.CalledProcessError(status, " ".join(*args))
|
||||||
|
|
||||||
|
|
||||||
def is_windows():
|
def is_windows() -> bool:
|
||||||
return sys.platform == "win32"
|
return sys.platform == "win32"
|
||||||
|
|
||||||
|
|
||||||
def is_macosx():
|
def is_macosx() -> bool:
|
||||||
return sys.platform == "darwin"
|
return sys.platform == "darwin"
|
||||||
|
|
||||||
|
|
||||||
def is_linux():
|
def is_linux() -> bool:
|
||||||
return sys.platform.startswith("linux")
|
return sys.platform.startswith("linux")
|
||||||
|
|
||||||
|
|
||||||
class BuildNotFound(Exception):
|
class BuildNotFound(Exception):
|
||||||
def __init__(self, message):
|
def __init__(self, message) -> None:
|
||||||
self.message = message
|
self.message = message
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
|
@ -248,7 +251,9 @@ class CommandBase(object):
|
||||||
|
|
||||||
This mostly handles configuration management, such as .servobuild."""
|
This mostly handles configuration management, such as .servobuild."""
|
||||||
|
|
||||||
def __init__(self, context):
|
target: BuildTarget
|
||||||
|
|
||||||
|
def __init__(self, context) -> None:
|
||||||
self.context = context
|
self.context = context
|
||||||
self.enable_media = False
|
self.enable_media = False
|
||||||
self.features = []
|
self.features = []
|
||||||
|
@ -257,13 +262,13 @@ class CommandBase(object):
|
||||||
# by `configure_build_target`
|
# by `configure_build_target`
|
||||||
self.target = BuildTarget.from_triple(None)
|
self.target = BuildTarget.from_triple(None)
|
||||||
|
|
||||||
def get_env_bool(var, default):
|
def get_env_bool(var: str, default: bool) -> bool:
|
||||||
# Contents of env vars are strings by default. This returns the
|
# Contents of env vars are strings by default. This returns the
|
||||||
# boolean value of the specified environment variable, or the
|
# boolean value of the specified environment variable, or the
|
||||||
# speciried default if the var doesn't contain True or False
|
# speciried default if the var doesn't contain True or False
|
||||||
return {"True": True, "False": False}.get(os.environ.get(var), default)
|
return {"True": True, "False": False}.get(os.environ.get(var, ""), default)
|
||||||
|
|
||||||
def resolverelative(category, key):
|
def resolverelative(category: str, key: str) -> None:
|
||||||
# Allow ~
|
# Allow ~
|
||||||
self.config[category][key] = path.expanduser(self.config[category][key])
|
self.config[category][key] = path.expanduser(self.config[category][key])
|
||||||
# Resolve relative paths
|
# Resolve relative paths
|
||||||
|
@ -327,7 +332,7 @@ class CommandBase(object):
|
||||||
def get_top_dir(self):
|
def get_top_dir(self):
|
||||||
return self.context.topdir
|
return self.context.topdir
|
||||||
|
|
||||||
def get_binary_path(self, build_type: BuildType, sanitizer: SanitizerKind = SanitizerKind.NONE):
|
def get_binary_path(self, build_type: BuildType, sanitizer: SanitizerKind = SanitizerKind.NONE) -> str:
|
||||||
base_path = util.get_target_dir()
|
base_path = util.get_target_dir()
|
||||||
if sanitizer.is_some() or self.target.is_cross_build():
|
if sanitizer.is_some() or self.target.is_cross_build():
|
||||||
base_path = path.join(base_path, self.target.triple())
|
base_path = path.join(base_path, self.target.triple())
|
||||||
|
@ -339,7 +344,7 @@ class CommandBase(object):
|
||||||
|
|
||||||
return binary_path
|
return binary_path
|
||||||
|
|
||||||
def detach_volume(self, mounted_volume):
|
def detach_volume(self, mounted_volume: str | bytes) -> None:
|
||||||
print("Detaching volume {}".format(mounted_volume))
|
print("Detaching volume {}".format(mounted_volume))
|
||||||
try:
|
try:
|
||||||
subprocess.check_call(["hdiutil", "detach", mounted_volume])
|
subprocess.check_call(["hdiutil", "detach", mounted_volume])
|
||||||
|
@ -347,11 +352,11 @@ class CommandBase(object):
|
||||||
print("Could not detach volume {} : {}".format(mounted_volume, e.returncode))
|
print("Could not detach volume {} : {}".format(mounted_volume, e.returncode))
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
def detach_volume_if_attached(self, mounted_volume):
|
def detach_volume_if_attached(self, mounted_volume: str | bytes) -> None:
|
||||||
if os.path.exists(mounted_volume):
|
if os.path.exists(mounted_volume):
|
||||||
self.detach_volume(mounted_volume)
|
self.detach_volume(mounted_volume)
|
||||||
|
|
||||||
def mount_dmg(self, dmg_path):
|
def mount_dmg(self, dmg_path) -> None:
|
||||||
print("Mounting dmg {}".format(dmg_path))
|
print("Mounting dmg {}".format(dmg_path))
|
||||||
try:
|
try:
|
||||||
subprocess.check_call(["hdiutil", "attach", dmg_path])
|
subprocess.check_call(["hdiutil", "attach", dmg_path])
|
||||||
|
@ -359,7 +364,7 @@ class CommandBase(object):
|
||||||
print("Could not mount Servo dmg : {}".format(e.returncode))
|
print("Could not mount Servo dmg : {}".format(e.returncode))
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
def extract_nightly(self, nightlies_folder, destination_folder, destination_file):
|
def extract_nightly(self, nightlies_folder: LiteralString, destination_folder: str, destination_file: str) -> None:
|
||||||
print("Extracting to {} ...".format(destination_folder))
|
print("Extracting to {} ...".format(destination_folder))
|
||||||
if is_macosx():
|
if is_macosx():
|
||||||
mounted_volume = path.join(path.sep, "Volumes", "Servo")
|
mounted_volume = path.join(path.sep, "Volumes", "Servo")
|
||||||
|
@ -382,14 +387,14 @@ class CommandBase(object):
|
||||||
with tarfile.open(os.path.join(nightlies_folder, destination_file), "r") as tar:
|
with tarfile.open(os.path.join(nightlies_folder, destination_file), "r") as tar:
|
||||||
tar.extractall(destination_folder)
|
tar.extractall(destination_folder)
|
||||||
|
|
||||||
def get_executable(self, destination_folder):
|
def get_executable(self, destination_folder: str) -> str:
|
||||||
if is_windows():
|
if is_windows():
|
||||||
return path.join(destination_folder, "PFiles", "Mozilla research", "Servo Tech Demo")
|
return path.join(destination_folder, "PFiles", "Mozilla research", "Servo Tech Demo")
|
||||||
if is_linux:
|
if is_linux:
|
||||||
return path.join(destination_folder, "servo", "servo")
|
return path.join(destination_folder, "servo", "servo")
|
||||||
return path.join(destination_folder, "servo")
|
return path.join(destination_folder, "servo")
|
||||||
|
|
||||||
def get_nightly_binary_path(self, nightly_date):
|
def get_nightly_binary_path(self, nightly_date) -> str | None:
|
||||||
if nightly_date is None:
|
if nightly_date is None:
|
||||||
return
|
return
|
||||||
if not nightly_date:
|
if not nightly_date:
|
||||||
|
@ -409,10 +414,18 @@ class CommandBase(object):
|
||||||
response = urllib.request.urlopen(req).read()
|
response = urllib.request.urlopen(req).read()
|
||||||
tree = XML(response)
|
tree = XML(response)
|
||||||
namespaces = {"ns": tree.tag[1 : tree.tag.index("}")]}
|
namespaces = {"ns": tree.tag[1 : tree.tag.index("}")]}
|
||||||
|
# pyrefly: ignore # missing-attribute
|
||||||
file_to_download = tree.find("ns:Contents", namespaces).find("ns:Key", namespaces).text
|
file_to_download = tree.find("ns:Contents", namespaces).find("ns:Key", namespaces).text
|
||||||
except urllib.error.URLError as e:
|
except urllib.error.URLError as e:
|
||||||
print("Could not fetch the available nightly versions from the repository : {}".format(e.reason))
|
print("Could not fetch the available nightly versions from the repository : {}".format(e.reason))
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
except ValueError as e:
|
||||||
|
print(
|
||||||
|
"Could not fetch a nightly version for date {} and platform {} cause of {}".format(
|
||||||
|
nightly_date, os_prefix, e
|
||||||
|
)
|
||||||
|
)
|
||||||
|
sys.exit(1)
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
print("Could not fetch a nightly version for date {} and platform {}".format(nightly_date, os_prefix))
|
print("Could not fetch a nightly version for date {} and platform {}".format(nightly_date, os_prefix))
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
@ -420,6 +433,7 @@ class CommandBase(object):
|
||||||
nightly_target_directory = path.join(self.context.topdir, "target")
|
nightly_target_directory = path.join(self.context.topdir, "target")
|
||||||
# ':' is not an authorized character for a file name on Windows
|
# ':' is not an authorized character for a file name on Windows
|
||||||
# make sure the OS specific separator is used
|
# make sure the OS specific separator is used
|
||||||
|
# pyrefly: ignore # missing-attribute
|
||||||
target_file_path = file_to_download.replace(":", "-").split("/")
|
target_file_path = file_to_download.replace(":", "-").split("/")
|
||||||
destination_file = os.path.join(nightly_target_directory, os.path.join(*target_file_path))
|
destination_file = os.path.join(nightly_target_directory, os.path.join(*target_file_path))
|
||||||
# Once extracted, the nightly folder name is the tar name without the extension
|
# Once extracted, the nightly folder name is the tar name without the extension
|
||||||
|
@ -437,6 +451,7 @@ class CommandBase(object):
|
||||||
print("The nightly file {} has already been downloaded.".format(destination_file))
|
print("The nightly file {} has already been downloaded.".format(destination_file))
|
||||||
else:
|
else:
|
||||||
print("The nightly {} does not exist yet, downloading it.".format(destination_file))
|
print("The nightly {} does not exist yet, downloading it.".format(destination_file))
|
||||||
|
# pyrefly: ignore # no-matching-overload
|
||||||
download_file(destination_file, NIGHTLY_REPOSITORY_URL + file_to_download, destination_file)
|
download_file(destination_file, NIGHTLY_REPOSITORY_URL + file_to_download, destination_file)
|
||||||
|
|
||||||
# Extract the downloaded nightly version
|
# Extract the downloaded nightly version
|
||||||
|
@ -447,10 +462,10 @@ 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) -> str:
|
||||||
return servo.platform.windows.get_dependency_dir(package)
|
return servo.platform.windows.get_dependency_dir(package)
|
||||||
|
|
||||||
def build_env(self):
|
def build_env(self) -> dict[str, str]:
|
||||||
"""Return an extended environment dictionary."""
|
"""Return an extended environment dictionary."""
|
||||||
env = os.environ.copy()
|
env = os.environ.copy()
|
||||||
|
|
||||||
|
@ -488,7 +503,7 @@ class CommandBase(object):
|
||||||
if not (self.config["build"]["ccache"] == ""):
|
if not (self.config["build"]["ccache"] == ""):
|
||||||
env["CCACHE"] = self.config["build"]["ccache"]
|
env["CCACHE"] = self.config["build"]["ccache"]
|
||||||
|
|
||||||
env["CARGO_TARGET_DIR"] = servo.util.get_target_dir()
|
env["CARGO_TARGET_DIR"] = util.get_target_dir()
|
||||||
|
|
||||||
# Work around https://github.com/servo/servo/issues/24446
|
# Work around https://github.com/servo/servo/issues/24446
|
||||||
# Argument-less str.split normalizes leading, trailing, and double spaces
|
# Argument-less str.split normalizes leading, trailing, and double spaces
|
||||||
|
@ -694,7 +709,7 @@ class CommandBase(object):
|
||||||
|
|
||||||
return target_configuration_decorator
|
return target_configuration_decorator
|
||||||
|
|
||||||
def configure_build_type(self, release: bool, dev: bool, prod: bool, profile: Optional[str]) -> BuildType:
|
def configure_build_type(self, release: bool, dev: bool, prod: bool, profile: str) -> BuildType:
|
||||||
option_count = release + dev + prod + (profile is not None)
|
option_count = release + dev + prod + (profile is not None)
|
||||||
|
|
||||||
if option_count > 1:
|
if option_count > 1:
|
||||||
|
@ -726,7 +741,7 @@ class CommandBase(object):
|
||||||
else:
|
else:
|
||||||
return BuildType.custom(profile)
|
return BuildType.custom(profile)
|
||||||
|
|
||||||
def configure_build_target(self, kwargs: Dict[str, Any], suppress_log: bool = False):
|
def configure_build_target(self, kwargs: Dict[str, Any], suppress_log: bool = False) -> None:
|
||||||
if hasattr(self.context, "target"):
|
if hasattr(self.context, "target"):
|
||||||
# This call is for a dispatched command and we've already configured
|
# This call is for a dispatched command and we've already configured
|
||||||
# the target, so just use it.
|
# the target, so just use it.
|
||||||
|
@ -763,12 +778,6 @@ class CommandBase(object):
|
||||||
if self.target.is_cross_build() and not suppress_log:
|
if self.target.is_cross_build() and not suppress_log:
|
||||||
print(f"Targeting '{self.target.triple()}' for cross-compilation")
|
print(f"Targeting '{self.target.triple()}' for cross-compilation")
|
||||||
|
|
||||||
def is_android(self):
|
|
||||||
return isinstance(self.target, AndroidTarget)
|
|
||||||
|
|
||||||
def is_openharmony(self):
|
|
||||||
return isinstance(self.target, OpenHarmonyTarget)
|
|
||||||
|
|
||||||
def is_media_enabled(self, media_stack: Optional[str]):
|
def is_media_enabled(self, media_stack: Optional[str]):
|
||||||
"""Determine whether media is enabled based on the value of the build target
|
"""Determine whether media is enabled based on the value of the build target
|
||||||
platform and the value of the '--media-stack' command-line argument.
|
platform and the value of the '--media-stack' command-line argument.
|
||||||
|
@ -804,8 +813,8 @@ class CommandBase(object):
|
||||||
capture_output=False,
|
capture_output=False,
|
||||||
target_override: Optional[str] = None,
|
target_override: Optional[str] = None,
|
||||||
**_kwargs,
|
**_kwargs,
|
||||||
):
|
) -> CompletedProcess[bytes] | int:
|
||||||
env = env or self.build_env()
|
env = cast(dict[str, str], env or self.build_env())
|
||||||
|
|
||||||
# NB: On non-Linux platforms we cannot check whether GStreamer is installed until
|
# NB: On non-Linux platforms we cannot check whether GStreamer is installed until
|
||||||
# environment variables are set via `self.build_env()`.
|
# environment variables are set via `self.build_env()`.
|
||||||
|
@ -879,21 +888,21 @@ class CommandBase(object):
|
||||||
|
|
||||||
return call(["cargo", command] + args + cargo_args, env=env, verbose=verbose)
|
return call(["cargo", command] + args + cargo_args, env=env, verbose=verbose)
|
||||||
|
|
||||||
def android_adb_path(self, env):
|
def android_adb_path(self, env) -> LiteralString:
|
||||||
if "ANDROID_SDK_ROOT" in env:
|
if "ANDROID_SDK_ROOT" in env:
|
||||||
sdk_adb = path.join(env["ANDROID_SDK_ROOT"], "platform-tools", "adb")
|
sdk_adb = path.join(env["ANDROID_SDK_ROOT"], "platform-tools", "adb")
|
||||||
if path.exists(sdk_adb):
|
if path.exists(sdk_adb):
|
||||||
return sdk_adb
|
return sdk_adb
|
||||||
return "adb"
|
return "adb"
|
||||||
|
|
||||||
def android_emulator_path(self, env):
|
def android_emulator_path(self, env) -> LiteralString:
|
||||||
if "ANDROID_SDK_ROOT" in env:
|
if "ANDROID_SDK_ROOT" in env:
|
||||||
sdk_adb = path.join(env["ANDROID_SDK_ROOT"], "emulator", "emulator")
|
sdk_adb = path.join(env["ANDROID_SDK_ROOT"], "emulator", "emulator")
|
||||||
if path.exists(sdk_adb):
|
if path.exists(sdk_adb):
|
||||||
return sdk_adb
|
return sdk_adb
|
||||||
return "emulator"
|
return "emulator"
|
||||||
|
|
||||||
def ensure_bootstrapped(self):
|
def ensure_bootstrapped(self) -> None:
|
||||||
if self.context.bootstrapped:
|
if self.context.bootstrapped:
|
||||||
return
|
return
|
||||||
|
|
||||||
|
@ -904,35 +913,13 @@ class CommandBase(object):
|
||||||
if not self.target.is_cross_build():
|
if not self.target.is_cross_build():
|
||||||
return
|
return
|
||||||
|
|
||||||
installed_targets = check_output(["rustup", "target", "list", "--installed"], cwd=self.context.topdir).decode()
|
installed_targets = check_output(["rustup", "target", "list", "--installed"], cwd=self.context.topdir)
|
||||||
|
if isinstance(installed_targets, bytes):
|
||||||
|
installed_targets = installed_targets.decode("utf-8")
|
||||||
if self.target.triple() not in installed_targets:
|
if self.target.triple() not in installed_targets:
|
||||||
check_call(["rustup", "target", "add", self.target.triple()], cwd=self.context.topdir)
|
check_call(["rustup", "target", "add", self.target.triple()], cwd=self.context.topdir)
|
||||||
|
|
||||||
def ensure_rustup_version(self):
|
def ensure_clobbered(self, target_dir=None) -> None:
|
||||||
try:
|
|
||||||
version_line = subprocess.check_output(
|
|
||||||
["rustup" + servo.platform.get().executable_suffix(), "--version"],
|
|
||||||
# Silence "info: This is the version for the rustup toolchain manager,
|
|
||||||
# not the rustc compiler."
|
|
||||||
stderr=open(os.devnull, "wb"),
|
|
||||||
)
|
|
||||||
except OSError as e:
|
|
||||||
if e.errno == NO_SUCH_FILE_OR_DIRECTORY:
|
|
||||||
print(
|
|
||||||
"It looks like rustup is not installed. See instructions at "
|
|
||||||
"https://github.com/servo/servo/#setting-up-your-environment"
|
|
||||||
)
|
|
||||||
print()
|
|
||||||
sys.exit(1)
|
|
||||||
raise
|
|
||||||
version = tuple(map(int, re.match(rb"rustup (\d+)\.(\d+)\.(\d+)", version_line).groups()))
|
|
||||||
version_needed = (1, 23, 0)
|
|
||||||
if version < version_needed:
|
|
||||||
print("rustup is at version %s.%s.%s, Servo requires %s.%s.%s or more recent." % (version + version_needed))
|
|
||||||
print("Try running 'rustup self update'.")
|
|
||||||
sys.exit(1)
|
|
||||||
|
|
||||||
def ensure_clobbered(self, target_dir=None):
|
|
||||||
if target_dir is None:
|
if target_dir is None:
|
||||||
target_dir = util.get_target_dir()
|
target_dir = util.get_target_dir()
|
||||||
auto = True if os.environ.get("AUTOCLOBBER", False) else False
|
auto = True if os.environ.get("AUTOCLOBBER", False) else False
|
||||||
|
@ -955,6 +942,6 @@ class CommandBase(object):
|
||||||
Registrar.dispatch("clean", context=self.context, verbose=True)
|
Registrar.dispatch("clean", context=self.context, verbose=True)
|
||||||
print("Successfully completed auto clobber.")
|
print("Successfully completed auto clobber.")
|
||||||
except subprocess.CalledProcessError as error:
|
except subprocess.CalledProcessError as error:
|
||||||
sys.exit(error)
|
sys.exit(error.returncode)
|
||||||
else:
|
else:
|
||||||
print("Clobber not needed.")
|
print("Clobber not needed.")
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
# option. This file may not be copied, modified, or distributed
|
# option. This file may not be copied, modified, or distributed
|
||||||
# except according to those terms.
|
# except according to those terms.
|
||||||
|
|
||||||
|
from subprocess import CompletedProcess
|
||||||
import json
|
import json
|
||||||
|
|
||||||
from mach.decorators import (
|
from mach.decorators import (
|
||||||
|
@ -26,13 +27,14 @@ class MachCommands(CommandBase):
|
||||||
"params", default=None, nargs="...", help="Command-line arguments to be passed through to cargo check"
|
"params", default=None, nargs="...", help="Command-line arguments to be passed through to cargo check"
|
||||||
)
|
)
|
||||||
@CommandBase.common_command_arguments(build_configuration=True, build_type=False)
|
@CommandBase.common_command_arguments(build_configuration=True, build_type=False)
|
||||||
def check(self, params, **kwargs):
|
def check(self, params, **kwargs) -> int:
|
||||||
if not params:
|
if not params:
|
||||||
params = []
|
params = []
|
||||||
|
|
||||||
self.ensure_bootstrapped()
|
self.ensure_bootstrapped()
|
||||||
self.ensure_clobbered()
|
self.ensure_clobbered()
|
||||||
status = self.run_cargo_build_like_command("check", params, **kwargs)
|
status = self.run_cargo_build_like_command("check", params, **kwargs)
|
||||||
|
assert isinstance(status, int)
|
||||||
if status == 0:
|
if status == 0:
|
||||||
print("Finished checking, binary NOT updated. Consider ./mach build before ./mach run")
|
print("Finished checking, binary NOT updated. Consider ./mach build before ./mach run")
|
||||||
|
|
||||||
|
@ -40,7 +42,7 @@ class MachCommands(CommandBase):
|
||||||
|
|
||||||
@Command("rustc", description="Run the Rust compiler", category="devenv")
|
@Command("rustc", description="Run the Rust compiler", category="devenv")
|
||||||
@CommandArgument("params", default=None, nargs="...", help="Command-line arguments to be passed through to rustc")
|
@CommandArgument("params", default=None, nargs="...", help="Command-line arguments to be passed through to rustc")
|
||||||
def rustc(self, params):
|
def rustc(self, params) -> int:
|
||||||
if params is None:
|
if params is None:
|
||||||
params = []
|
params = []
|
||||||
|
|
||||||
|
@ -52,13 +54,15 @@ class MachCommands(CommandBase):
|
||||||
"params", default=None, nargs="...", help="Command-line arguments to be passed through to cargo-fix"
|
"params", default=None, nargs="...", help="Command-line arguments to be passed through to cargo-fix"
|
||||||
)
|
)
|
||||||
@CommandBase.common_command_arguments(build_configuration=True, build_type=False)
|
@CommandBase.common_command_arguments(build_configuration=True, build_type=False)
|
||||||
def cargo_fix(self, params, **kwargs):
|
def cargo_fix(self, params, **kwargs) -> int:
|
||||||
if not params:
|
if not params:
|
||||||
params = []
|
params = []
|
||||||
|
|
||||||
self.ensure_bootstrapped()
|
self.ensure_bootstrapped()
|
||||||
self.ensure_clobbered()
|
self.ensure_clobbered()
|
||||||
return self.run_cargo_build_like_command("fix", params, **kwargs)
|
status = self.run_cargo_build_like_command("fix", params, **kwargs)
|
||||||
|
assert isinstance(status, int)
|
||||||
|
return status
|
||||||
|
|
||||||
@Command("clippy", description='Run "cargo clippy"', category="devenv")
|
@Command("clippy", description='Run "cargo clippy"', category="devenv")
|
||||||
@CommandArgument("params", default=None, nargs="...", help="Command-line arguments to be passed through to clippy")
|
@CommandArgument("params", default=None, nargs="...", help="Command-line arguments to be passed through to clippy")
|
||||||
|
@ -69,7 +73,7 @@ class MachCommands(CommandBase):
|
||||||
help="Emit the clippy warnings in the Github Actions annotations format",
|
help="Emit the clippy warnings in the Github Actions annotations format",
|
||||||
)
|
)
|
||||||
@CommandBase.common_command_arguments(build_configuration=True, build_type=False)
|
@CommandBase.common_command_arguments(build_configuration=True, build_type=False)
|
||||||
def cargo_clippy(self, params, github_annotations=False, **kwargs):
|
def cargo_clippy(self, params, github_annotations=False, **kwargs) -> int:
|
||||||
if not params:
|
if not params:
|
||||||
params = []
|
params = []
|
||||||
|
|
||||||
|
@ -85,6 +89,7 @@ class MachCommands(CommandBase):
|
||||||
github_annotation_manager = GitHubAnnotationManager("clippy")
|
github_annotation_manager = GitHubAnnotationManager("clippy")
|
||||||
|
|
||||||
results = self.run_cargo_build_like_command("clippy", params, env=env, capture_output=True, **kwargs)
|
results = self.run_cargo_build_like_command("clippy", params, env=env, capture_output=True, **kwargs)
|
||||||
|
assert isinstance(results, CompletedProcess)
|
||||||
if results.returncode == 0:
|
if results.returncode == 0:
|
||||||
return 0
|
return 0
|
||||||
try:
|
try:
|
||||||
|
@ -94,9 +99,11 @@ class MachCommands(CommandBase):
|
||||||
except json.JSONDecodeError:
|
except json.JSONDecodeError:
|
||||||
pass
|
pass
|
||||||
return results.returncode
|
return results.returncode
|
||||||
return self.run_cargo_build_like_command("clippy", params, env=env, **kwargs)
|
status = self.run_cargo_build_like_command("clippy", params, env=env, **kwargs)
|
||||||
|
assert isinstance(status, int)
|
||||||
|
return status
|
||||||
|
|
||||||
@Command("fetch", description="Fetch Rust, Cargo and Cargo dependencies", category="devenv")
|
@Command("fetch", description="Fetch Rust, Cargo and Cargo dependencies", category="devenv")
|
||||||
def fetch(self):
|
def fetch(self) -> int:
|
||||||
self.ensure_bootstrapped()
|
self.ensure_bootstrapped()
|
||||||
return call(["cargo", "fetch"], env=self.build_env())
|
return call(["cargo", "fetch"], env=self.build_env())
|
||||||
|
|
|
@ -153,22 +153,22 @@ of using `dumpbin` and the errors that appear when starting Servo.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
def windows_dlls():
|
def windows_dlls() -> list[str]:
|
||||||
return GSTREAMER_WIN_DEPENDENCY_LIBS + [f"{lib}-1.0-0.dll" for lib in GSTREAMER_BASE_LIBS]
|
return GSTREAMER_WIN_DEPENDENCY_LIBS + [f"{lib}-1.0-0.dll" for lib in GSTREAMER_BASE_LIBS]
|
||||||
|
|
||||||
|
|
||||||
def windows_plugins():
|
def windows_plugins() -> list[str]:
|
||||||
libs = [*GSTREAMER_PLUGIN_LIBS, *GSTREAMER_WIN_PLUGIN_LIBS]
|
libs = [*GSTREAMER_PLUGIN_LIBS, *GSTREAMER_WIN_PLUGIN_LIBS]
|
||||||
return [f"{lib}.dll" for lib in libs]
|
return [f"{lib}.dll" for lib in libs]
|
||||||
|
|
||||||
|
|
||||||
def macos_plugins():
|
def macos_plugins() -> list[str]:
|
||||||
plugins = [*GSTREAMER_PLUGIN_LIBS, *GSTREAMER_MAC_PLUGIN_LIBS]
|
plugins = [*GSTREAMER_PLUGIN_LIBS, *GSTREAMER_MAC_PLUGIN_LIBS]
|
||||||
|
|
||||||
return [f"lib{plugin}.dylib" for plugin in plugins]
|
return [f"lib{plugin}.dylib" for plugin in plugins]
|
||||||
|
|
||||||
|
|
||||||
def write_plugin_list(target):
|
def write_plugin_list(target: str) -> None:
|
||||||
plugins = []
|
plugins = []
|
||||||
if "apple-" in target:
|
if "apple-" in target:
|
||||||
plugins = macos_plugins()
|
plugins = macos_plugins()
|
||||||
|
@ -191,7 +191,7 @@ def is_macos_system_library(library_path: str) -> bool:
|
||||||
return library_path.startswith("/System/Library") or library_path.startswith("/usr/lib") or ".asan." in library_path
|
return library_path.startswith("/System/Library") or library_path.startswith("/usr/lib") or ".asan." in library_path
|
||||||
|
|
||||||
|
|
||||||
def rewrite_dependencies_to_be_relative(binary: str, dependency_lines: Set[str], relative_path: str):
|
def rewrite_dependencies_to_be_relative(binary: str, dependency_lines: Set[str], relative_path: str) -> None:
|
||||||
"""Given a path to a binary (either an executable or a dylib), rewrite the
|
"""Given a path to a binary (either an executable or a dylib), rewrite the
|
||||||
the given dependency lines to be found at the given relative path to
|
the given dependency lines to be found at the given relative path to
|
||||||
the executable in which they are used. In our case, this is typically servoshell."""
|
the executable in which they are used. In our case, this is typically servoshell."""
|
||||||
|
@ -207,7 +207,7 @@ def rewrite_dependencies_to_be_relative(binary: str, dependency_lines: Set[str],
|
||||||
print(f"{arguments} install_name_tool exited with return value {exception.returncode}")
|
print(f"{arguments} install_name_tool exited with return value {exception.returncode}")
|
||||||
|
|
||||||
|
|
||||||
def make_rpath_path_absolute(dylib_path_from_otool: str, rpath: str):
|
def make_rpath_path_absolute(dylib_path_from_otool: str, rpath: str) -> str:
|
||||||
"""Given a dylib dependency from otool, resolve the path into a full path if it
|
"""Given a dylib dependency from otool, resolve the path into a full path if it
|
||||||
contains `@rpath`."""
|
contains `@rpath`."""
|
||||||
if not dylib_path_from_otool.startswith("@rpath/"):
|
if not dylib_path_from_otool.startswith("@rpath/"):
|
||||||
|
@ -228,6 +228,7 @@ def find_non_system_dependencies_with_otool(binary_path: str) -> Set[str]:
|
||||||
"""Given a binary path, find all dylib dependency lines that do not refer to
|
"""Given a binary path, find all dylib dependency lines that do not refer to
|
||||||
system libraries."""
|
system libraries."""
|
||||||
process = subprocess.Popen(["/usr/bin/otool", "-L", binary_path], stdout=subprocess.PIPE)
|
process = subprocess.Popen(["/usr/bin/otool", "-L", binary_path], stdout=subprocess.PIPE)
|
||||||
|
assert process.stdout is not None
|
||||||
output = set()
|
output = set()
|
||||||
|
|
||||||
for line in map(lambda line: line.decode("utf8"), process.stdout):
|
for line in map(lambda line: line.decode("utf8"), process.stdout):
|
||||||
|
@ -242,15 +243,17 @@ def find_non_system_dependencies_with_otool(binary_path: str) -> Set[str]:
|
||||||
return output
|
return output
|
||||||
|
|
||||||
|
|
||||||
def package_gstreamer_dylibs(binary_path: str, library_target_directory: str, target: BuildTarget):
|
def package_gstreamer_dylibs(binary_path: str, library_target_directory: str, target: BuildTarget) -> bool:
|
||||||
"""Copy all GStreamer dependencies to the "lib" subdirectory of a built version of
|
"""Copy all GStreamer dependencies to the "lib" subdirectory of a built version of
|
||||||
Servo. Also update any transitive shared library paths so that they are relative to
|
Servo. Also update any transitive shared library paths so that they are relative to
|
||||||
this subdirectory."""
|
this subdirectory."""
|
||||||
|
|
||||||
# This import only works when called from `mach`.
|
# This import only works when called from `mach`.
|
||||||
import servo.platform
|
import servo.platform
|
||||||
|
import servo.platform.macos
|
||||||
|
|
||||||
gstreamer_root = servo.platform.get().gstreamer_root(target)
|
gstreamer_root = servo.platform.get().gstreamer_root(target)
|
||||||
|
assert gstreamer_root is not None
|
||||||
gstreamer_version = servo.platform.macos.GSTREAMER_PLUGIN_VERSION
|
gstreamer_version = servo.platform.macos.GSTREAMER_PLUGIN_VERSION
|
||||||
gstreamer_root_libs = os.path.join(gstreamer_root, "lib")
|
gstreamer_root_libs = os.path.join(gstreamer_root, "lib")
|
||||||
|
|
||||||
|
|
|
@ -25,9 +25,10 @@ def get_folders_list(path):
|
||||||
folder_name = join(path, filename)
|
folder_name = join(path, filename)
|
||||||
folder_list.append(folder_name)
|
folder_list.append(folder_name)
|
||||||
return folder_list
|
return folder_list
|
||||||
|
return folder_list
|
||||||
|
|
||||||
|
|
||||||
def mutation_test_for(mutation_path):
|
def mutation_test_for(mutation_path: str) -> None:
|
||||||
test_mapping_file = join(mutation_path, "test_mapping.json")
|
test_mapping_file = join(mutation_path, "test_mapping.json")
|
||||||
if isfile(test_mapping_file):
|
if isfile(test_mapping_file):
|
||||||
json_data = open(test_mapping_file).read()
|
json_data = open(test_mapping_file).read()
|
||||||
|
|
|
@ -9,10 +9,12 @@
|
||||||
|
|
||||||
import fileinput
|
import fileinput
|
||||||
import re
|
import re
|
||||||
|
from re import Match
|
||||||
import random
|
import random
|
||||||
|
from typing import Iterator
|
||||||
|
|
||||||
|
|
||||||
def is_comment(line):
|
def is_comment(line: str) -> Match[str] | None:
|
||||||
return re.search(r"\/\/.*", line)
|
return re.search(r"\/\/.*", line)
|
||||||
|
|
||||||
|
|
||||||
|
@ -25,14 +27,14 @@ def init_variables(if_blocks):
|
||||||
return random_index, start_counter, end_counter, lines_to_delete, line_to_mutate
|
return random_index, start_counter, end_counter, lines_to_delete, line_to_mutate
|
||||||
|
|
||||||
|
|
||||||
def deleteStatements(file_name, line_numbers):
|
def deleteStatements(file_name, line_numbers) -> None:
|
||||||
for line in fileinput.input(file_name, inplace=True):
|
for line in fileinput.input(file_name, inplace=True):
|
||||||
if fileinput.lineno() not in line_numbers:
|
if fileinput.lineno() not in line_numbers:
|
||||||
print(line.rstrip())
|
print(line.rstrip())
|
||||||
|
|
||||||
|
|
||||||
class Strategy:
|
class Strategy:
|
||||||
def __init__(self):
|
def __init__(self) -> None:
|
||||||
self._strategy_name = ""
|
self._strategy_name = ""
|
||||||
self._replace_strategy = {}
|
self._replace_strategy = {}
|
||||||
|
|
||||||
|
@ -53,28 +55,28 @@ class Strategy:
|
||||||
|
|
||||||
|
|
||||||
class AndOr(Strategy):
|
class AndOr(Strategy):
|
||||||
def __init__(self):
|
def __init__(self) -> None:
|
||||||
Strategy.__init__(self)
|
Strategy.__init__(self)
|
||||||
logical_and = r"(?<=\s)&&(?=\s)"
|
logical_and = r"(?<=\s)&&(?=\s)"
|
||||||
self._replace_strategy = {"regex": logical_and, "replaceString": "||"}
|
self._replace_strategy = {"regex": logical_and, "replaceString": "||"}
|
||||||
|
|
||||||
|
|
||||||
class IfTrue(Strategy):
|
class IfTrue(Strategy):
|
||||||
def __init__(self):
|
def __init__(self) -> None:
|
||||||
Strategy.__init__(self)
|
Strategy.__init__(self)
|
||||||
if_condition = r"(?<=if\s)\s*(?!let\s)(.*)(?=\s\{)"
|
if_condition = r"(?<=if\s)\s*(?!let\s)(.*)(?=\s\{)"
|
||||||
self._replace_strategy = {"regex": if_condition, "replaceString": "true"}
|
self._replace_strategy = {"regex": if_condition, "replaceString": "true"}
|
||||||
|
|
||||||
|
|
||||||
class IfFalse(Strategy):
|
class IfFalse(Strategy):
|
||||||
def __init__(self):
|
def __init__(self) -> None:
|
||||||
Strategy.__init__(self)
|
Strategy.__init__(self)
|
||||||
if_condition = r"(?<=if\s)\s*(?!let\s)(.*)(?=\s\{)"
|
if_condition = r"(?<=if\s)\s*(?!let\s)(.*)(?=\s\{)"
|
||||||
self._replace_strategy = {"regex": if_condition, "replaceString": "false"}
|
self._replace_strategy = {"regex": if_condition, "replaceString": "false"}
|
||||||
|
|
||||||
|
|
||||||
class ModifyComparision(Strategy):
|
class ModifyComparision(Strategy):
|
||||||
def __init__(self):
|
def __init__(self) -> None:
|
||||||
Strategy.__init__(self)
|
Strategy.__init__(self)
|
||||||
less_than_equals = r"(?<=\s)(\<)\=(?=\s)"
|
less_than_equals = r"(?<=\s)(\<)\=(?=\s)"
|
||||||
greater_than_equals = r"(?<=\s)(\<)\=(?=\s)"
|
greater_than_equals = r"(?<=\s)(\<)\=(?=\s)"
|
||||||
|
@ -82,7 +84,7 @@ class ModifyComparision(Strategy):
|
||||||
|
|
||||||
|
|
||||||
class MinusToPlus(Strategy):
|
class MinusToPlus(Strategy):
|
||||||
def __init__(self):
|
def __init__(self) -> None:
|
||||||
Strategy.__init__(self)
|
Strategy.__init__(self)
|
||||||
arithmetic_minus = r"(?<=\s)\-(?=\s.+)"
|
arithmetic_minus = r"(?<=\s)\-(?=\s.+)"
|
||||||
minus_in_shorthand = r"(?<=\s)\-(?=\=)"
|
minus_in_shorthand = r"(?<=\s)\-(?=\=)"
|
||||||
|
@ -90,7 +92,7 @@ class MinusToPlus(Strategy):
|
||||||
|
|
||||||
|
|
||||||
class PlusToMinus(Strategy):
|
class PlusToMinus(Strategy):
|
||||||
def __init__(self):
|
def __init__(self) -> None:
|
||||||
Strategy.__init__(self)
|
Strategy.__init__(self)
|
||||||
arithmetic_plus = r"(?<=[^\"]\s)\+(?=\s[^A-Z\'?\":\{]+)"
|
arithmetic_plus = r"(?<=[^\"]\s)\+(?=\s[^A-Z\'?\":\{]+)"
|
||||||
plus_in_shorthand = r"(?<=\s)\+(?=\=)"
|
plus_in_shorthand = r"(?<=\s)\+(?=\=)"
|
||||||
|
@ -98,14 +100,14 @@ class PlusToMinus(Strategy):
|
||||||
|
|
||||||
|
|
||||||
class AtomicString(Strategy):
|
class AtomicString(Strategy):
|
||||||
def __init__(self):
|
def __init__(self) -> None:
|
||||||
Strategy.__init__(self)
|
Strategy.__init__(self)
|
||||||
string_literal = r"(?<=\").+(?=\")"
|
string_literal = r"(?<=\").+(?=\")"
|
||||||
self._replace_strategy = {"regex": string_literal, "replaceString": " "}
|
self._replace_strategy = {"regex": string_literal, "replaceString": " "}
|
||||||
|
|
||||||
|
|
||||||
class DuplicateLine(Strategy):
|
class DuplicateLine(Strategy):
|
||||||
def __init__(self):
|
def __init__(self) -> None:
|
||||||
Strategy.__init__(self)
|
Strategy.__init__(self)
|
||||||
self._strategy_name = "duplicate"
|
self._strategy_name = "duplicate"
|
||||||
append_statement = r".+?append\(.+?\).*?;"
|
append_statement = r".+?append\(.+?\).*?;"
|
||||||
|
@ -133,7 +135,7 @@ class DuplicateLine(Strategy):
|
||||||
|
|
||||||
|
|
||||||
class DeleteIfBlock(Strategy):
|
class DeleteIfBlock(Strategy):
|
||||||
def __init__(self):
|
def __init__(self) -> None:
|
||||||
Strategy.__init__(self)
|
Strategy.__init__(self)
|
||||||
self.if_block = r"^\s+if\s(.+)\s\{"
|
self.if_block = r"^\s+if\s(.+)\s\{"
|
||||||
self.else_block = r"\selse(.+)\{"
|
self.else_block = r"\selse(.+)\{"
|
||||||
|
@ -175,7 +177,7 @@ class DeleteIfBlock(Strategy):
|
||||||
line_to_mutate += 1
|
line_to_mutate += 1
|
||||||
|
|
||||||
|
|
||||||
def get_strategies():
|
def get_strategies() -> Iterator[Strategy]:
|
||||||
return (
|
return (
|
||||||
AndOr,
|
AndOr,
|
||||||
IfTrue,
|
IfTrue,
|
||||||
|
@ -190,7 +192,7 @@ def get_strategies():
|
||||||
|
|
||||||
|
|
||||||
class Mutator:
|
class Mutator:
|
||||||
def __init__(self, strategy):
|
def __init__(self, strategy) -> None:
|
||||||
self._strategy = strategy
|
self._strategy = strategy
|
||||||
|
|
||||||
def mutate(self, file_name):
|
def mutate(self, file_name):
|
||||||
|
|
|
@ -42,6 +42,7 @@ from servo.command_base import (
|
||||||
from servo.util import delete, get_target_dir
|
from servo.util import delete, get_target_dir
|
||||||
|
|
||||||
from python.servo.platform.build_target import SanitizerKind
|
from python.servo.platform.build_target import SanitizerKind
|
||||||
|
from servo.platform.build_target import is_android, is_openharmony
|
||||||
|
|
||||||
PACKAGES = {
|
PACKAGES = {
|
||||||
"android": [
|
"android": [
|
||||||
|
@ -74,11 +75,11 @@ def packages_for_platform(platform):
|
||||||
yield path.join(target_dir, package)
|
yield path.join(target_dir, package)
|
||||||
|
|
||||||
|
|
||||||
def listfiles(directory):
|
def listfiles(directory) -> list[str]:
|
||||||
return [f for f in os.listdir(directory) if path.isfile(path.join(directory, f))]
|
return [f for f in os.listdir(directory) if path.isfile(path.join(directory, f))]
|
||||||
|
|
||||||
|
|
||||||
def copy_windows_dependencies(binary_path, destination):
|
def copy_windows_dependencies(binary_path: str, destination: str) -> None:
|
||||||
for f in os.listdir(binary_path):
|
for f in os.listdir(binary_path):
|
||||||
if os.path.isfile(path.join(binary_path, f)) and f.endswith(".dll"):
|
if os.path.isfile(path.join(binary_path, f)) and f.endswith(".dll"):
|
||||||
shutil.copy(path.join(binary_path, f), destination)
|
shutil.copy(path.join(binary_path, f), destination)
|
||||||
|
@ -110,12 +111,12 @@ class PackageCommands(CommandBase):
|
||||||
@CommandArgument("--target", "-t", default=None, help="Package for given target platform")
|
@CommandArgument("--target", "-t", default=None, help="Package for given target platform")
|
||||||
@CommandBase.common_command_arguments(build_configuration=False, build_type=True, package_configuration=True)
|
@CommandBase.common_command_arguments(build_configuration=False, build_type=True, package_configuration=True)
|
||||||
@CommandBase.allow_target_configuration
|
@CommandBase.allow_target_configuration
|
||||||
def package(self, build_type: BuildType, flavor=None, sanitizer: SanitizerKind = SanitizerKind.NONE):
|
def package(self, build_type: BuildType, flavor=None, sanitizer: SanitizerKind = SanitizerKind.NONE) -> int | None:
|
||||||
env = self.build_env()
|
env = self.build_env()
|
||||||
binary_path = self.get_binary_path(build_type, sanitizer=sanitizer)
|
binary_path = self.get_binary_path(build_type, sanitizer=sanitizer)
|
||||||
dir_to_root = self.get_top_dir()
|
dir_to_root = self.get_top_dir()
|
||||||
target_dir = path.dirname(binary_path)
|
target_dir = path.dirname(binary_path)
|
||||||
if self.is_android():
|
if is_android(self.target):
|
||||||
target_triple = self.target.triple()
|
target_triple = self.target.triple()
|
||||||
if "aarch64" in target_triple:
|
if "aarch64" in target_triple:
|
||||||
arch_string = "Arm64"
|
arch_string = "Arm64"
|
||||||
|
@ -155,7 +156,7 @@ class PackageCommands(CommandBase):
|
||||||
except subprocess.CalledProcessError as e:
|
except subprocess.CalledProcessError as e:
|
||||||
print("Packaging Android exited with return value %d" % e.returncode)
|
print("Packaging Android exited with return value %d" % e.returncode)
|
||||||
return e.returncode
|
return e.returncode
|
||||||
elif self.is_openharmony():
|
elif is_openharmony(self.target):
|
||||||
# hvigor doesn't support an option to place output files in a specific directory
|
# hvigor doesn't support an option to place output files in a specific directory
|
||||||
# so copy the source files into the target/openharmony directory first.
|
# so copy the source files into the target/openharmony directory first.
|
||||||
ohos_app_dir = path.join(self.get_top_dir(), "support", "openharmony")
|
ohos_app_dir = path.join(self.get_top_dir(), "support", "openharmony")
|
||||||
|
@ -197,7 +198,7 @@ class PackageCommands(CommandBase):
|
||||||
try:
|
try:
|
||||||
with cd(ohos_target_dir):
|
with cd(ohos_target_dir):
|
||||||
version = check_output(["hvigorw", "--version", "--no-daemon"])
|
version = check_output(["hvigorw", "--version", "--no-daemon"])
|
||||||
print(f"Found `hvigorw` with version {str(version, 'utf-8').strip()} in system PATH")
|
print(f"Found `hvigorw` with version {version.strip()} in system PATH")
|
||||||
hvigor_command[0:0] = ["hvigorw"]
|
hvigor_command[0:0] = ["hvigorw"]
|
||||||
except FileNotFoundError:
|
except FileNotFoundError:
|
||||||
print(
|
print(
|
||||||
|
@ -216,7 +217,6 @@ class PackageCommands(CommandBase):
|
||||||
env["NODE_PATH"] = env["HVIGOR_PATH"] + "/node_modules"
|
env["NODE_PATH"] = env["HVIGOR_PATH"] + "/node_modules"
|
||||||
hvigor_script = f"{env['HVIGOR_PATH']}/node_modules/@ohos/hvigor/bin/hvigor.js"
|
hvigor_script = f"{env['HVIGOR_PATH']}/node_modules/@ohos/hvigor/bin/hvigor.js"
|
||||||
hvigor_command[0:0] = ["node", hvigor_script]
|
hvigor_command[0:0] = ["node", hvigor_script]
|
||||||
|
|
||||||
abi_string = self.target.abi_string()
|
abi_string = self.target.abi_string()
|
||||||
ohos_libs_dir = path.join(ohos_target_dir, "entry", "libs", abi_string)
|
ohos_libs_dir = path.join(ohos_target_dir, "entry", "libs", abi_string)
|
||||||
os.makedirs(ohos_libs_dir)
|
os.makedirs(ohos_libs_dir)
|
||||||
|
@ -402,7 +402,7 @@ class PackageCommands(CommandBase):
|
||||||
usb=False,
|
usb=False,
|
||||||
sanitizer: SanitizerKind = SanitizerKind.NONE,
|
sanitizer: SanitizerKind = SanitizerKind.NONE,
|
||||||
flavor=None,
|
flavor=None,
|
||||||
):
|
) -> int:
|
||||||
env = self.build_env()
|
env = self.build_env()
|
||||||
try:
|
try:
|
||||||
binary_path = self.get_binary_path(build_type, sanitizer=sanitizer)
|
binary_path = self.get_binary_path(build_type, sanitizer=sanitizer)
|
||||||
|
@ -417,7 +417,7 @@ class PackageCommands(CommandBase):
|
||||||
print("Rebuilding Servo did not solve the missing build problem.")
|
print("Rebuilding Servo did not solve the missing build problem.")
|
||||||
return 1
|
return 1
|
||||||
|
|
||||||
if self.is_android():
|
if is_android(self.target):
|
||||||
pkg_path = self.target.get_package_path(build_type.directory_name())
|
pkg_path = self.target.get_package_path(build_type.directory_name())
|
||||||
exec_command = [self.android_adb_path(env)]
|
exec_command = [self.android_adb_path(env)]
|
||||||
if emulator and usb:
|
if emulator and usb:
|
||||||
|
@ -428,7 +428,7 @@ class PackageCommands(CommandBase):
|
||||||
if usb:
|
if usb:
|
||||||
exec_command += ["-d"]
|
exec_command += ["-d"]
|
||||||
exec_command += ["install", "-r", pkg_path]
|
exec_command += ["install", "-r", pkg_path]
|
||||||
elif self.is_openharmony():
|
elif is_openharmony(self.target):
|
||||||
pkg_path = self.target.get_package_path(build_type.directory_name(), flavor=flavor)
|
pkg_path = self.target.get_package_path(build_type.directory_name(), flavor=flavor)
|
||||||
hdc_path = path.join(env["OHOS_SDK_NATIVE"], "../", "toolchains", "hdc")
|
hdc_path = path.join(env["OHOS_SDK_NATIVE"], "../", "toolchains", "hdc")
|
||||||
exec_command = [hdc_path, "install", "-r", pkg_path]
|
exec_command = [hdc_path, "install", "-r", pkg_path]
|
||||||
|
@ -453,7 +453,7 @@ class PackageCommands(CommandBase):
|
||||||
@CommandArgument(
|
@CommandArgument(
|
||||||
"--github-release-id", default=None, type=int, help="The github release to upload the nightly builds."
|
"--github-release-id", default=None, type=int, help="The github release to upload the nightly builds."
|
||||||
)
|
)
|
||||||
def upload_nightly(self, platform, secret_from_environment, github_release_id):
|
def upload_nightly(self, platform, secret_from_environment, github_release_id) -> int:
|
||||||
import boto3
|
import boto3
|
||||||
|
|
||||||
def get_s3_secret():
|
def get_s3_secret():
|
||||||
|
@ -465,13 +465,13 @@ class PackageCommands(CommandBase):
|
||||||
aws_secret_access_key = secret["aws_secret_access_key"]
|
aws_secret_access_key = secret["aws_secret_access_key"]
|
||||||
return (aws_access_key, aws_secret_access_key)
|
return (aws_access_key, aws_secret_access_key)
|
||||||
|
|
||||||
def nightly_filename(package, timestamp):
|
def nightly_filename(package, timestamp) -> str:
|
||||||
return "{}-{}".format(
|
return "{}-{}".format(
|
||||||
timestamp.isoformat() + "Z", # The `Z` denotes UTC
|
timestamp.isoformat() + "Z", # The `Z` denotes UTC
|
||||||
path.basename(package),
|
path.basename(package),
|
||||||
)
|
)
|
||||||
|
|
||||||
def upload_to_github_release(platform, package, package_hash):
|
def upload_to_github_release(platform, package: str, package_hash: str) -> None:
|
||||||
if not github_release_id:
|
if not github_release_id:
|
||||||
return
|
return
|
||||||
|
|
||||||
|
@ -483,11 +483,12 @@ class PackageCommands(CommandBase):
|
||||||
|
|
||||||
asset_name = f"servo-latest.{extension}"
|
asset_name = f"servo-latest.{extension}"
|
||||||
release.upload_asset(package, name=asset_name)
|
release.upload_asset(package, name=asset_name)
|
||||||
|
# pyrefly: ignore[missing-attribute]
|
||||||
release.upload_asset_from_memory(
|
release.upload_asset_from_memory(
|
||||||
package_hash_fileobj, package_hash_fileobj.getbuffer().nbytes, name=f"{asset_name}.sha256"
|
package_hash_fileobj, package_hash_fileobj.getbuffer().nbytes, name=f"{asset_name}.sha256"
|
||||||
)
|
)
|
||||||
|
|
||||||
def upload_to_s3(platform, package, package_hash, timestamp):
|
def upload_to_s3(platform, package: str, package_hash: str, timestamp: datetime) -> None:
|
||||||
(aws_access_key, aws_secret_access_key) = get_s3_secret()
|
(aws_access_key, aws_secret_access_key) = get_s3_secret()
|
||||||
s3 = boto3.client("s3", aws_access_key_id=aws_access_key, aws_secret_access_key=aws_secret_access_key)
|
s3 = boto3.client("s3", aws_access_key_id=aws_access_key, aws_secret_access_key=aws_secret_access_key)
|
||||||
|
|
||||||
|
|
|
@ -14,7 +14,7 @@ from .windows import Windows
|
||||||
__platform__ = None
|
__platform__ = None
|
||||||
|
|
||||||
|
|
||||||
def host_platform():
|
def host_platform() -> str:
|
||||||
os_type = platform.system().lower()
|
os_type = platform.system().lower()
|
||||||
if os_type == "linux":
|
if os_type == "linux":
|
||||||
os_type = "unknown-linux-gnu"
|
os_type = "unknown-linux-gnu"
|
||||||
|
@ -31,7 +31,7 @@ def host_platform():
|
||||||
return os_type
|
return os_type
|
||||||
|
|
||||||
|
|
||||||
def host_triple():
|
def host_triple() -> str:
|
||||||
os_type = host_platform()
|
os_type = host_platform()
|
||||||
cpu_type = platform.machine().lower()
|
cpu_type = platform.machine().lower()
|
||||||
if cpu_type in ["i386", "i486", "i686", "i768", "x86"]:
|
if cpu_type in ["i386", "i486", "i686", "i768", "x86"]:
|
||||||
|
|
|
@ -16,7 +16,7 @@ from .build_target import BuildTarget
|
||||||
|
|
||||||
|
|
||||||
class Base:
|
class Base:
|
||||||
def __init__(self, triple: str):
|
def __init__(self, triple: str) -> None:
|
||||||
self.environ = os.environ.copy()
|
self.environ = os.environ.copy()
|
||||||
self.triple = triple
|
self.triple = triple
|
||||||
self.is_windows = False
|
self.is_windows = False
|
||||||
|
@ -29,10 +29,10 @@ class Base:
|
||||||
def executable_suffix(self) -> str:
|
def executable_suffix(self) -> str:
|
||||||
return ""
|
return ""
|
||||||
|
|
||||||
def _platform_bootstrap(self, _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, _target: BuildTarget, _force: bool) -> bool:
|
def _platform_bootstrap_gstreamer(self, target: BuildTarget, force: bool) -> bool:
|
||||||
raise NotImplementedError("GStreamer bootstrap support is not yet available for your OS.")
|
raise NotImplementedError("GStreamer bootstrap support is not yet available for your OS.")
|
||||||
|
|
||||||
def is_gstreamer_installed(self, target: BuildTarget) -> bool:
|
def is_gstreamer_installed(self, target: BuildTarget) -> bool:
|
||||||
|
@ -54,7 +54,7 @@ class Base:
|
||||||
except FileNotFoundError:
|
except FileNotFoundError:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def bootstrap(self, force: bool, skip_platform: bool, skip_lints: bool):
|
def bootstrap(self, force: bool, skip_platform: bool, skip_lints: bool) -> None:
|
||||||
installed_something = False
|
installed_something = False
|
||||||
if not skip_platform:
|
if not skip_platform:
|
||||||
installed_something |= self._platform_bootstrap(force)
|
installed_something |= self._platform_bootstrap(force)
|
||||||
|
@ -67,7 +67,7 @@ class Base:
|
||||||
if not installed_something:
|
if not installed_something:
|
||||||
print("Dependencies were already installed!")
|
print("Dependencies were already installed!")
|
||||||
|
|
||||||
def install_rust_toolchain(self):
|
def install_rust_toolchain(self) -> None:
|
||||||
# rustup 1.28.0, and rustup 1.28.1+ with RUSTUP_AUTO_INSTALL=0, require us to explicitly
|
# rustup 1.28.0, and rustup 1.28.1+ with RUSTUP_AUTO_INSTALL=0, require us to explicitly
|
||||||
# install the Rust toolchain before trying to use it.
|
# install the Rust toolchain before trying to use it.
|
||||||
print(" * Installing Rust toolchain...")
|
print(" * Installing Rust toolchain...")
|
||||||
|
@ -86,7 +86,7 @@ class Base:
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def install_cargo_deny(self, force: bool) -> bool:
|
def install_cargo_deny(self, force: bool) -> bool:
|
||||||
def cargo_deny_installed():
|
def cargo_deny_installed() -> bool:
|
||||||
if force or not shutil.which("cargo-deny"):
|
if force or not shutil.which("cargo-deny"):
|
||||||
return False
|
return False
|
||||||
# Tidy needs at least version 0.18.1 installed.
|
# Tidy needs at least version 0.18.1 installed.
|
||||||
|
@ -115,7 +115,7 @@ class Base:
|
||||||
as fast as possible."""
|
as fast as possible."""
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def bootstrap_gstreamer(self, force: bool):
|
def bootstrap_gstreamer(self, force: bool) -> None:
|
||||||
target = BuildTarget.from_triple(self.triple)
|
target = BuildTarget.from_triple(self.triple)
|
||||||
if not self._platform_bootstrap_gstreamer(target, force):
|
if not self._platform_bootstrap_gstreamer(target, force):
|
||||||
root = self.gstreamer_root(target)
|
root = self.gstreamer_root(target)
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
# option. This file may not be copied, modified, or distributed
|
# option. This file may not be copied, modified, or distributed
|
||||||
# except according to those terms.
|
# except according to those terms.
|
||||||
|
|
||||||
|
from typing import TypeGuard
|
||||||
import errno
|
import errno
|
||||||
import json
|
import json
|
||||||
import os
|
import os
|
||||||
|
@ -48,7 +49,7 @@ class SanitizerKind(Enum):
|
||||||
|
|
||||||
|
|
||||||
class BuildTarget(object):
|
class BuildTarget(object):
|
||||||
def __init__(self, target_triple: str):
|
def __init__(self, target_triple: str) -> None:
|
||||||
self.target_triple = target_triple
|
self.target_triple = target_triple
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
|
@ -69,7 +70,7 @@ class BuildTarget(object):
|
||||||
def binary_name(self) -> str:
|
def binary_name(self) -> str:
|
||||||
return f"servo{servo.platform.get().executable_suffix()}"
|
return f"servo{servo.platform.get().executable_suffix()}"
|
||||||
|
|
||||||
def configure_build_environment(self, env: Dict[str, str], config: Dict[str, Any], topdir: pathlib.Path):
|
def configure_build_environment(self, env: Dict[str, str], config: Dict[str, Any], topdir: pathlib.Path) -> None:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def is_cross_build(self) -> bool:
|
def is_cross_build(self) -> bool:
|
||||||
|
@ -124,7 +125,7 @@ class AndroidTarget(CrossBuildTarget):
|
||||||
|
|
||||||
return config
|
return config
|
||||||
|
|
||||||
def configure_build_environment(self, env: Dict[str, str], config: Dict[str, Any], topdir: pathlib.Path):
|
def configure_build_environment(self, env: Dict[str, str], config: Dict[str, Any], topdir: pathlib.Path) -> None:
|
||||||
# Paths to Android build tools:
|
# Paths to Android build tools:
|
||||||
if config["android"]["sdk"]:
|
if config["android"]["sdk"]:
|
||||||
env["ANDROID_SDK_ROOT"] = config["android"]["sdk"]
|
env["ANDROID_SDK_ROOT"] = config["android"]["sdk"]
|
||||||
|
@ -201,7 +202,7 @@ class AndroidTarget(CrossBuildTarget):
|
||||||
llvm_toolchain = path.join(llvm_prebuilt, host)
|
llvm_toolchain = path.join(llvm_prebuilt, host)
|
||||||
env["PATH"] = env["PATH"] + ":" + path.join(llvm_toolchain, "bin")
|
env["PATH"] = env["PATH"] + ":" + path.join(llvm_toolchain, "bin")
|
||||||
|
|
||||||
def to_ndk_bin(prog):
|
def to_ndk_bin(prog: str) -> str:
|
||||||
return path.join(llvm_toolchain, "bin", prog)
|
return path.join(llvm_toolchain, "bin", prog)
|
||||||
|
|
||||||
# This workaround is due to an issue in the x86_64 Android NDK that introduces
|
# This workaround is due to an issue in the x86_64 Android NDK that introduces
|
||||||
|
@ -217,6 +218,9 @@ class AndroidTarget(CrossBuildTarget):
|
||||||
env["RUSTFLAGS"] = env.get("RUSTFLAGS", "")
|
env["RUSTFLAGS"] = env.get("RUSTFLAGS", "")
|
||||||
env["RUSTFLAGS"] += f"-C link-arg={libclangrt_filename}"
|
env["RUSTFLAGS"] += f"-C link-arg={libclangrt_filename}"
|
||||||
|
|
||||||
|
assert host_cc
|
||||||
|
assert host_cxx
|
||||||
|
|
||||||
env["RUST_TARGET"] = self.triple()
|
env["RUST_TARGET"] = self.triple()
|
||||||
env["HOST_CC"] = host_cc
|
env["HOST_CC"] = host_cc
|
||||||
env["HOST_CXX"] = host_cxx
|
env["HOST_CXX"] = host_cxx
|
||||||
|
@ -289,7 +293,7 @@ class AndroidTarget(CrossBuildTarget):
|
||||||
class OpenHarmonyTarget(CrossBuildTarget):
|
class OpenHarmonyTarget(CrossBuildTarget):
|
||||||
DEFAULT_TRIPLE = "aarch64-unknown-linux-ohos"
|
DEFAULT_TRIPLE = "aarch64-unknown-linux-ohos"
|
||||||
|
|
||||||
def configure_build_environment(self, env: Dict[str, str], config: Dict[str, Any], topdir: pathlib.Path):
|
def configure_build_environment(self, env: Dict[str, str], config: Dict[str, Any], topdir: pathlib.Path) -> None:
|
||||||
# Paths to OpenHarmony SDK and build tools:
|
# Paths to OpenHarmony SDK and build tools:
|
||||||
# Note: `OHOS_SDK_NATIVE` is the CMake variable name the `hvigor` build-system
|
# Note: `OHOS_SDK_NATIVE` is the CMake variable name the `hvigor` build-system
|
||||||
# uses for the native directory of the SDK, so we use the same name to be consistent.
|
# uses for the native directory of the SDK, so we use the same name to be consistent.
|
||||||
|
@ -343,7 +347,7 @@ class OpenHarmonyTarget(CrossBuildTarget):
|
||||||
# on windows, depending on how the wrapper is called.
|
# on windows, depending on how the wrapper is called.
|
||||||
# Instead, we ensure that all the necessary flags for the c-compiler are set
|
# Instead, we ensure that all the necessary flags for the c-compiler are set
|
||||||
# via environment variables such as `TARGET_CFLAGS`.
|
# via environment variables such as `TARGET_CFLAGS`.
|
||||||
def to_sdk_llvm_bin(prog: str):
|
def to_sdk_llvm_bin(prog: str) -> str:
|
||||||
if sys.platform == "win32":
|
if sys.platform == "win32":
|
||||||
prog = prog + ".exe"
|
prog = prog + ".exe"
|
||||||
llvm_prog = llvm_bin.joinpath(prog)
|
llvm_prog = llvm_bin.joinpath(prog)
|
||||||
|
@ -498,3 +502,11 @@ class OpenHarmonyTarget(CrossBuildTarget):
|
||||||
def abi_string(self) -> str:
|
def abi_string(self) -> str:
|
||||||
abi_map = {"aarch64-unknown-linux-ohos": "arm64-v8a", "x86_64-unknown-linux-ohos": "x86_64"}
|
abi_map = {"aarch64-unknown-linux-ohos": "arm64-v8a", "x86_64-unknown-linux-ohos": "x86_64"}
|
||||||
return abi_map[self.triple()]
|
return abi_map[self.triple()]
|
||||||
|
|
||||||
|
|
||||||
|
def is_android(target: BuildTarget) -> TypeGuard[AndroidTarget]:
|
||||||
|
return isinstance(target, AndroidTarget)
|
||||||
|
|
||||||
|
|
||||||
|
def is_openharmony(target: BuildTarget) -> TypeGuard[OpenHarmonyTarget]:
|
||||||
|
return isinstance(target, OpenHarmonyTarget)
|
||||||
|
|
|
@ -164,7 +164,7 @@ GSTREAMER_URL = (
|
||||||
|
|
||||||
|
|
||||||
class Linux(Base):
|
class Linux(Base):
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs) -> None:
|
||||||
super().__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
self.is_linux = True
|
self.is_linux = True
|
||||||
(self.distro, self.version) = Linux.get_distro_and_version()
|
(self.distro, self.version) = Linux.get_distro_and_version()
|
||||||
|
@ -265,16 +265,16 @@ class Linux(Base):
|
||||||
install = True
|
install = True
|
||||||
elif self.distro in ["CentOS", "CentOS Linux", "Fedora", "Fedora Linux", "Fedora Linux Asahi Remix"]:
|
elif self.distro in ["CentOS", "CentOS Linux", "Fedora", "Fedora Linux", "Fedora Linux Asahi Remix"]:
|
||||||
command = ["dnf", "install"]
|
command = ["dnf", "install"]
|
||||||
installed_pkgs: [str] = subprocess.check_output(
|
installed_pkgs: list[str] = subprocess.check_output(
|
||||||
["rpm", "--query", "--all", "--queryformat", "%{NAME}\n"], encoding="utf-8"
|
["rpm", "--query", "--all", "--queryformat", "%{NAME}\n"], encoding="utf-8"
|
||||||
).split("\n")
|
).splitlines()
|
||||||
pkgs = DNF_PKGS
|
pkgs = DNF_PKGS
|
||||||
for pkg in pkgs:
|
for pkg in pkgs:
|
||||||
if pkg not in installed_pkgs:
|
if pkg not in installed_pkgs:
|
||||||
install = True
|
install = True
|
||||||
break
|
break
|
||||||
elif self.distro == "void":
|
elif self.distro == "void":
|
||||||
installed_pkgs = str(subprocess.check_output(["xbps-query", "-l"]))
|
installed_pkgs = subprocess.check_output(["xbps-query", "-l"], text=True).splitlines()
|
||||||
pkgs = XBPS_PKGS
|
pkgs = XBPS_PKGS
|
||||||
for pkg in pkgs:
|
for pkg in pkgs:
|
||||||
command = ["xbps-install", "-A"]
|
command = ["xbps-install", "-A"]
|
||||||
|
@ -285,13 +285,13 @@ class Linux(Base):
|
||||||
if not install:
|
if not install:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def check_sudo():
|
def check_sudo() -> bool:
|
||||||
if os.geteuid() != 0:
|
if os.geteuid() != 0:
|
||||||
if shutil.which("sudo") is None:
|
if shutil.which("sudo") is None:
|
||||||
return False
|
return False
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def run_as_root(command, force=False):
|
def run_as_root(command: list[str], force: bool = False) -> int:
|
||||||
if os.geteuid() != 0:
|
if os.geteuid() != 0:
|
||||||
command.insert(0, "sudo")
|
command.insert(0, "sudo")
|
||||||
if force:
|
if force:
|
||||||
|
@ -312,10 +312,10 @@ class Linux(Base):
|
||||||
raise EnvironmentError("Installation of dependencies failed.")
|
raise EnvironmentError("Installation of dependencies failed.")
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def gstreamer_root(self, _target: BuildTarget) -> Optional[str]:
|
def gstreamer_root(self, target: BuildTarget) -> Optional[str]:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def _platform_bootstrap_gstreamer(self, _target: BuildTarget, _force: bool) -> bool:
|
def _platform_bootstrap_gstreamer(self, target: BuildTarget, force: bool) -> bool:
|
||||||
raise EnvironmentError(
|
raise EnvironmentError(
|
||||||
"Bootstrapping GStreamer on Linux is not supported. "
|
"Bootstrapping GStreamer on Linux is not supported. "
|
||||||
+ "Please install it using your distribution package manager."
|
+ "Please install it using your distribution package manager."
|
||||||
|
|
|
@ -24,7 +24,7 @@ GSTREAMER_ROOT = "/Library/Frameworks/GStreamer.framework/Versions/1.0"
|
||||||
|
|
||||||
|
|
||||||
class MacOS(Base):
|
class MacOS(Base):
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs) -> None:
|
||||||
super().__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
self.is_macos = True
|
self.is_macos = True
|
||||||
|
|
||||||
|
@ -38,7 +38,7 @@ class MacOS(Base):
|
||||||
# Servo only supports the official GStreamer distribution on MacOS.
|
# Servo only supports the official GStreamer distribution on MacOS.
|
||||||
return not target.is_cross_build() and os.path.exists(GSTREAMER_ROOT)
|
return not target.is_cross_build() and os.path.exists(GSTREAMER_ROOT)
|
||||||
|
|
||||||
def _platform_bootstrap(self, _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, "support", "macos", "Brewfile")
|
brewfile = os.path.join(util.SERVO_ROOT, "support", "macos", "Brewfile")
|
||||||
|
|
|
@ -11,7 +11,7 @@ import os
|
||||||
import subprocess
|
import subprocess
|
||||||
import tempfile
|
import tempfile
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
import urllib
|
import urllib.parse
|
||||||
import zipfile
|
import zipfile
|
||||||
import shutil
|
import shutil
|
||||||
|
|
||||||
|
@ -32,12 +32,12 @@ DEPENDENCIES_DIR = os.path.join(util.get_target_dir(), "dependencies")
|
||||||
WINGET_DEPENDENCIES = ["Kitware.CMake", "LLVM.LLVM", "Ninja-build.Ninja", "WiXToolset.WiXToolset"]
|
WINGET_DEPENDENCIES = ["Kitware.CMake", "LLVM.LLVM", "Ninja-build.Ninja", "WiXToolset.WiXToolset"]
|
||||||
|
|
||||||
|
|
||||||
def get_dependency_dir(package):
|
def get_dependency_dir(package: str) -> str:
|
||||||
"""Get the directory that a given Windows dependency should extract to."""
|
"""Get the directory that a given Windows dependency should extract to."""
|
||||||
return os.path.join(DEPENDENCIES_DIR, package, DEPENDENCIES[package])
|
return os.path.join(DEPENDENCIES_DIR, package, DEPENDENCIES[package])
|
||||||
|
|
||||||
|
|
||||||
def _winget_import(force: bool = False):
|
def _winget_import(force: bool = False) -> None:
|
||||||
try:
|
try:
|
||||||
# We install tools like LLVM / CMake, so we probably don't want to force-upgrade
|
# We install tools like LLVM / CMake, so we probably don't want to force-upgrade
|
||||||
# a user installed version without good reason.
|
# a user installed version without good reason.
|
||||||
|
@ -56,7 +56,7 @@ def _winget_import(force: bool = False):
|
||||||
raise e
|
raise e
|
||||||
|
|
||||||
|
|
||||||
def _choco_install(force: bool = False):
|
def _choco_install(force: bool = False) -> None:
|
||||||
try:
|
try:
|
||||||
choco_config = os.path.join(util.SERVO_ROOT, "support", "windows", "chocolatey.config")
|
choco_config = os.path.join(util.SERVO_ROOT, "support", "windows", "chocolatey.config")
|
||||||
|
|
||||||
|
@ -75,15 +75,15 @@ def _choco_install(force: bool = False):
|
||||||
|
|
||||||
|
|
||||||
class Windows(Base):
|
class Windows(Base):
|
||||||
def __init__(self, triple: str):
|
def __init__(self, triple: str) -> None:
|
||||||
super().__init__(triple)
|
super().__init__(triple)
|
||||||
self.is_windows = True
|
self.is_windows = True
|
||||||
|
|
||||||
def executable_suffix(self):
|
def executable_suffix(self) -> str:
|
||||||
return ".exe"
|
return ".exe"
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def download_and_extract_dependency(cls, zip_path: str, full_spec: str):
|
def download_and_extract_dependency(cls, zip_path: str, full_spec: str) -> None:
|
||||||
if not os.path.isfile(zip_path):
|
if not os.path.isfile(zip_path):
|
||||||
zip_url = f"{DEPS_URL}/{urllib.parse.quote(full_spec)}.zip"
|
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)
|
||||||
|
|
|
@ -11,6 +11,7 @@ import json
|
||||||
import os
|
import os
|
||||||
import os.path as path
|
import os.path as path
|
||||||
import subprocess
|
import subprocess
|
||||||
|
from subprocess import CompletedProcess
|
||||||
from shutil import copy2
|
from shutil import copy2
|
||||||
from typing import List
|
from typing import List
|
||||||
|
|
||||||
|
@ -30,12 +31,12 @@ from servo.command_base import (
|
||||||
check_call,
|
check_call,
|
||||||
is_linux,
|
is_linux,
|
||||||
)
|
)
|
||||||
|
from servo.platform.build_target import is_android
|
||||||
|
|
||||||
ANDROID_APP_NAME = "org.servo.servoshell"
|
ANDROID_APP_NAME = "org.servo.servoshell"
|
||||||
|
|
||||||
|
|
||||||
def read_file(filename, if_exists=False):
|
def read_file(filename, if_exists=False) -> str | None:
|
||||||
if if_exists and not path.exists(filename):
|
if if_exists and not path.exists(filename):
|
||||||
return None
|
return None
|
||||||
with open(filename) as f:
|
with open(filename) as f:
|
||||||
|
@ -43,7 +44,7 @@ def read_file(filename, if_exists=False):
|
||||||
|
|
||||||
|
|
||||||
# Copied from Python 3.3+'s shlex.quote()
|
# Copied from Python 3.3+'s shlex.quote()
|
||||||
def shell_quote(arg):
|
def shell_quote(arg: str):
|
||||||
# use single quotes, and put single quotes into double quotes
|
# use single quotes, and put single quotes into double quotes
|
||||||
# the string $'b is then quoted as '$'"'"'b'
|
# the string $'b is then quoted as '$'"'"'b'
|
||||||
return "'" + arg.replace("'", "'\"'\"'") + "'"
|
return "'" + arg.replace("'", "'\"'\"'") + "'"
|
||||||
|
@ -81,7 +82,7 @@ class PostBuildCommands(CommandBase):
|
||||||
software=False,
|
software=False,
|
||||||
emulator=False,
|
emulator=False,
|
||||||
usb=False,
|
usb=False,
|
||||||
):
|
) -> int | None:
|
||||||
return self._run(servo_binary, params, debugger, debugger_cmd, headless, software, emulator, usb)
|
return self._run(servo_binary, params, debugger, debugger_cmd, headless, software, emulator, usb)
|
||||||
|
|
||||||
def _run(
|
def _run(
|
||||||
|
@ -94,7 +95,7 @@ class PostBuildCommands(CommandBase):
|
||||||
software=False,
|
software=False,
|
||||||
emulator=False,
|
emulator=False,
|
||||||
usb=False,
|
usb=False,
|
||||||
):
|
) -> int | None:
|
||||||
env = self.build_env()
|
env = self.build_env()
|
||||||
env["RUST_BACKTRACE"] = "1"
|
env["RUST_BACKTRACE"] = "1"
|
||||||
if software:
|
if software:
|
||||||
|
@ -109,7 +110,7 @@ class PostBuildCommands(CommandBase):
|
||||||
if debugger_cmd:
|
if debugger_cmd:
|
||||||
debugger = True
|
debugger = True
|
||||||
|
|
||||||
if self.is_android():
|
if is_android(self.target):
|
||||||
if debugger:
|
if debugger:
|
||||||
print("Android on-device debugging is not supported by mach yet. See")
|
print("Android on-device debugging is not supported by mach yet. See")
|
||||||
print("https://github.com/servo/servo/wiki/Building-for-Android#debugging-on-device")
|
print("https://github.com/servo/servo/wiki/Building-for-Android#debugging-on-device")
|
||||||
|
@ -141,7 +142,7 @@ class PostBuildCommands(CommandBase):
|
||||||
if usb:
|
if usb:
|
||||||
args += ["-d"]
|
args += ["-d"]
|
||||||
shell = subprocess.Popen(args + ["shell"], stdin=subprocess.PIPE)
|
shell = subprocess.Popen(args + ["shell"], stdin=subprocess.PIPE)
|
||||||
shell.communicate(bytes("\n".join(script) + "\n", "utf8"))
|
shell.communicate("\n".join(script) + "\n")
|
||||||
return shell.wait()
|
return shell.wait()
|
||||||
|
|
||||||
args = [servo_binary]
|
args = [servo_binary]
|
||||||
|
@ -193,8 +194,9 @@ class PostBuildCommands(CommandBase):
|
||||||
|
|
||||||
@Command("android-emulator", description="Run the Android emulator", category="post-build")
|
@Command("android-emulator", description="Run the Android emulator", category="post-build")
|
||||||
@CommandArgument("args", nargs="...", help="Command-line arguments to be passed through to the emulator")
|
@CommandArgument("args", nargs="...", help="Command-line arguments to be passed through to the emulator")
|
||||||
def android_emulator(self, args=None):
|
def android_emulator(self, args=None) -> int:
|
||||||
if not args:
|
if not args:
|
||||||
|
args = []
|
||||||
print("AVDs created by `./mach bootstrap-android` are servo-arm and servo-x86.")
|
print("AVDs created by `./mach bootstrap-android` are servo-arm and servo-x86.")
|
||||||
emulator = self.android_emulator_path(self.build_env())
|
emulator = self.android_emulator_path(self.build_env())
|
||||||
return subprocess.call([emulator] + args)
|
return subprocess.call([emulator] + args)
|
||||||
|
@ -202,7 +204,7 @@ class PostBuildCommands(CommandBase):
|
||||||
@Command("rr-record", description="Run Servo whilst recording execution with rr", category="post-build")
|
@Command("rr-record", description="Run Servo whilst recording execution with rr", category="post-build")
|
||||||
@CommandArgument("params", nargs="...", help="Command-line arguments to be passed through to Servo")
|
@CommandArgument("params", nargs="...", help="Command-line arguments to be passed through to Servo")
|
||||||
@CommandBase.common_command_arguments(binary_selection=True)
|
@CommandBase.common_command_arguments(binary_selection=True)
|
||||||
def rr_record(self, servo_binary: str, params=[]):
|
def rr_record(self, servo_binary: str, params=[]) -> None:
|
||||||
env = self.build_env()
|
env = self.build_env()
|
||||||
env["RUST_BACKTRACE"] = "1"
|
env["RUST_BACKTRACE"] = "1"
|
||||||
|
|
||||||
|
@ -221,7 +223,7 @@ class PostBuildCommands(CommandBase):
|
||||||
description="Replay the most recent execution of Servo that was recorded with rr",
|
description="Replay the most recent execution of Servo that was recorded with rr",
|
||||||
category="post-build",
|
category="post-build",
|
||||||
)
|
)
|
||||||
def rr_replay(self):
|
def rr_replay(self) -> None:
|
||||||
try:
|
try:
|
||||||
check_call(["rr", "--fatal-errors", "replay"])
|
check_call(["rr", "--fatal-errors", "replay"])
|
||||||
except OSError as e:
|
except OSError as e:
|
||||||
|
@ -233,7 +235,7 @@ class PostBuildCommands(CommandBase):
|
||||||
@Command("doc", description="Generate documentation", category="post-build")
|
@Command("doc", description="Generate documentation", category="post-build")
|
||||||
@CommandArgument("params", nargs="...", help="Command-line arguments to be passed through to cargo doc")
|
@CommandArgument("params", nargs="...", help="Command-line arguments to be passed through to cargo doc")
|
||||||
@CommandBase.common_command_arguments(build_configuration=True, build_type=False)
|
@CommandBase.common_command_arguments(build_configuration=True, build_type=False)
|
||||||
def doc(self, params: List[str], **kwargs):
|
def doc(self, params: List[str], **kwargs) -> CompletedProcess[bytes] | int | None:
|
||||||
self.ensure_bootstrapped()
|
self.ensure_bootstrapped()
|
||||||
|
|
||||||
docs = path.join(servo.util.get_target_dir(), "doc")
|
docs = path.join(servo.util.get_target_dir(), "doc")
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
# except according to those terms.
|
# except according to those terms.
|
||||||
|
|
||||||
import argparse
|
import argparse
|
||||||
|
from argparse import ArgumentParser
|
||||||
import json
|
import json
|
||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
|
@ -19,6 +20,7 @@ import subprocess
|
||||||
import sys
|
import sys
|
||||||
import textwrap
|
import textwrap
|
||||||
from time import sleep
|
from time import sleep
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
import tidy
|
import tidy
|
||||||
import wpt
|
import wpt
|
||||||
|
@ -116,7 +118,7 @@ class MachCommands(CommandBase):
|
||||||
DEFAULT_RENDER_MODE = "cpu"
|
DEFAULT_RENDER_MODE = "cpu"
|
||||||
HELP_RENDER_MODE = "Value can be 'cpu', 'gpu' or 'both' (default " + DEFAULT_RENDER_MODE + ")"
|
HELP_RENDER_MODE = "Value can be 'cpu', 'gpu' or 'both' (default " + DEFAULT_RENDER_MODE + ")"
|
||||||
|
|
||||||
def __init__(self, context):
|
def __init__(self, context) -> None:
|
||||||
CommandBase.__init__(self, context)
|
CommandBase.__init__(self, context)
|
||||||
if not hasattr(self.context, "built_tests"):
|
if not hasattr(self.context, "built_tests"):
|
||||||
self.context.built_tests = False
|
self.context.built_tests = False
|
||||||
|
@ -125,7 +127,7 @@ class MachCommands(CommandBase):
|
||||||
@CommandArgument("--base", default=None, help="the base URL for testcases")
|
@CommandArgument("--base", default=None, help="the base URL for testcases")
|
||||||
@CommandArgument("--date", default=None, help="the datestamp for the data")
|
@CommandArgument("--date", default=None, help="the datestamp for the data")
|
||||||
@CommandArgument("--submit", "-a", default=False, action="store_true", help="submit the data to perfherder")
|
@CommandArgument("--submit", "-a", default=False, action="store_true", help="submit the data to perfherder")
|
||||||
def test_perf(self, base=None, date=None, submit=False):
|
def test_perf(self, base=None, date=None, submit=False) -> int:
|
||||||
env = self.build_env()
|
env = self.build_env()
|
||||||
cmd = ["bash", "test_perf.sh"]
|
cmd = ["bash", "test_perf.sh"]
|
||||||
if base:
|
if base:
|
||||||
|
@ -144,7 +146,9 @@ class MachCommands(CommandBase):
|
||||||
"--nocapture", default=False, action="store_true", help="Run tests with nocapture ( show test stdout )"
|
"--nocapture", default=False, action="store_true", help="Run tests with nocapture ( show test stdout )"
|
||||||
)
|
)
|
||||||
@CommandBase.common_command_arguments(build_configuration=True, build_type=True)
|
@CommandBase.common_command_arguments(build_configuration=True, build_type=True)
|
||||||
def test_unit(self, build_type: BuildType, test_name=None, package=None, bench=False, nocapture=False, **kwargs):
|
def test_unit(
|
||||||
|
self, build_type: BuildType, test_name=None, package=None, bench=False, nocapture=False, **kwargs
|
||||||
|
) -> int:
|
||||||
if test_name is None:
|
if test_name is None:
|
||||||
test_name = []
|
test_name = []
|
||||||
|
|
||||||
|
@ -215,7 +219,7 @@ class MachCommands(CommandBase):
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
# Gather Cargo build timings (https://doc.rust-lang.org/cargo/reference/timings.html).
|
# Gather Cargo build timings (https://doc.rust-lang.org/cargo/reference/timings.html).
|
||||||
args = ["--timings"]
|
args: list[str] = ["--timings"]
|
||||||
|
|
||||||
if build_type.is_release():
|
if build_type.is_release():
|
||||||
args += ["--release"]
|
args += ["--release"]
|
||||||
|
@ -237,10 +241,12 @@ class MachCommands(CommandBase):
|
||||||
result = call(["cargo", "bench" if bench else "test"], cwd="support/crown")
|
result = call(["cargo", "bench" if bench else "test"], cwd="support/crown")
|
||||||
if result != 0:
|
if result != 0:
|
||||||
return result
|
return result
|
||||||
return self.run_cargo_build_like_command("bench" if bench else "test", args, env=env, **kwargs)
|
result = self.run_cargo_build_like_command("bench" if bench else "test", args, env=env, **kwargs)
|
||||||
|
assert isinstance(result, int)
|
||||||
|
return result
|
||||||
|
|
||||||
@Command("test-content", description="Run the content tests", category="testing")
|
@Command("test-content", description="Run the content tests", category="testing")
|
||||||
def test_content(self):
|
def test_content(self) -> int:
|
||||||
print("Content tests have been replaced by web-platform-tests under tests/wpt/mozilla/.")
|
print("Content tests have been replaced by web-platform-tests under tests/wpt/mozilla/.")
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
|
@ -259,7 +265,7 @@ class MachCommands(CommandBase):
|
||||||
action="store_true",
|
action="store_true",
|
||||||
help="Emit tidy warnings in the Github Actions annotations format",
|
help="Emit tidy warnings in the Github Actions annotations format",
|
||||||
)
|
)
|
||||||
def test_tidy(self, all_files, no_progress, github_annotations):
|
def test_tidy(self, all_files, no_progress, github_annotations) -> int:
|
||||||
tidy_failed = tidy.scan(not all_files, not no_progress, github_annotations)
|
tidy_failed = tidy.scan(not all_files, not no_progress, github_annotations)
|
||||||
|
|
||||||
print("\r ➤ Checking formatting of Rust files...")
|
print("\r ➤ Checking formatting of Rust files...")
|
||||||
|
@ -293,7 +299,7 @@ class MachCommands(CommandBase):
|
||||||
@CommandArgument(
|
@CommandArgument(
|
||||||
"tests", default=None, nargs="...", help="Specific WebIDL tests to run, relative to the tests directory"
|
"tests", default=None, nargs="...", help="Specific WebIDL tests to run, relative to the tests directory"
|
||||||
)
|
)
|
||||||
def test_scripts(self, verbose, very_verbose, all, tests):
|
def test_scripts(self, verbose, very_verbose, all, tests) -> int:
|
||||||
if very_verbose:
|
if very_verbose:
|
||||||
logging.getLogger().level = logging.DEBUG
|
logging.getLogger().level = logging.DEBUG
|
||||||
elif verbose:
|
elif verbose:
|
||||||
|
@ -342,7 +348,7 @@ class MachCommands(CommandBase):
|
||||||
# For the `import WebIDL` in runtests.py
|
# For the `import WebIDL` in runtests.py
|
||||||
sys.path.insert(0, test_file_dir)
|
sys.path.insert(0, test_file_dir)
|
||||||
run_file = path.abspath(path.join(test_file_dir, "runtests.py"))
|
run_file = path.abspath(path.join(test_file_dir, "runtests.py"))
|
||||||
run_globals = {"__file__": run_file}
|
run_globals: dict[str, Any] = {"__file__": run_file}
|
||||||
exec(compile(open(run_file).read(), run_file, "exec"), run_globals)
|
exec(compile(open(run_file).read(), run_file, "exec"), run_globals)
|
||||||
passed = run_globals["run_tests"](tests, verbose or very_verbose) and passed
|
passed = run_globals["run_tests"](tests, verbose or very_verbose) and passed
|
||||||
|
|
||||||
|
@ -350,7 +356,7 @@ class MachCommands(CommandBase):
|
||||||
|
|
||||||
@Command("test-devtools", description="Run tests for devtools.", category="testing")
|
@Command("test-devtools", description="Run tests for devtools.", category="testing")
|
||||||
@CommandBase.common_command_arguments(build_type=True)
|
@CommandBase.common_command_arguments(build_type=True)
|
||||||
def test_devtools(self, build_type: BuildType, **kwargs):
|
def test_devtools(self, build_type: BuildType, **kwargs) -> int:
|
||||||
print("Running devtools tests...")
|
print("Running devtools tests...")
|
||||||
passed = servo.devtools_tests.run_tests(SCRIPT_PATH, build_type)
|
passed = servo.devtools_tests.run_tests(SCRIPT_PATH, build_type)
|
||||||
return 0 if passed else 1
|
return 0 if passed else 1
|
||||||
|
@ -362,7 +368,7 @@ class MachCommands(CommandBase):
|
||||||
parser=wpt.create_parser,
|
parser=wpt.create_parser,
|
||||||
)
|
)
|
||||||
@CommandBase.common_command_arguments(build_configuration=False, build_type=True)
|
@CommandBase.common_command_arguments(build_configuration=False, build_type=True)
|
||||||
def test_wpt_failure(self, build_type: BuildType, **kwargs):
|
def test_wpt_failure(self, build_type: BuildType, **kwargs) -> bool:
|
||||||
kwargs["pause_after_test"] = False
|
kwargs["pause_after_test"] = False
|
||||||
kwargs["include"] = ["infrastructure/failing-test.html"]
|
kwargs["include"] = ["infrastructure/failing-test.html"]
|
||||||
return not self._test_wpt(build_type=build_type, **kwargs)
|
return not self._test_wpt(build_type=build_type, **kwargs)
|
||||||
|
@ -375,7 +381,7 @@ class MachCommands(CommandBase):
|
||||||
return self._test_wpt(servo_binary, **kwargs)
|
return self._test_wpt(servo_binary, **kwargs)
|
||||||
|
|
||||||
@CommandBase.allow_target_configuration
|
@CommandBase.allow_target_configuration
|
||||||
def _test_wpt(self, servo_binary: str, **kwargs):
|
def _test_wpt(self, servo_binary: str, **kwargs) -> int:
|
||||||
# TODO(mrobinson): Why do we pass the wrong binary path in when running WPT on Android?
|
# TODO(mrobinson): Why do we pass the wrong binary path in when running WPT on Android?
|
||||||
return_value = wpt.run.run_tests(servo_binary, **kwargs)
|
return_value = wpt.run.run_tests(servo_binary, **kwargs)
|
||||||
return return_value if not kwargs["always_succeed"] else 0
|
return return_value if not kwargs["always_succeed"] else 0
|
||||||
|
@ -386,11 +392,11 @@ class MachCommands(CommandBase):
|
||||||
category="testing",
|
category="testing",
|
||||||
parser=wpt.manifestupdate.create_parser,
|
parser=wpt.manifestupdate.create_parser,
|
||||||
)
|
)
|
||||||
def update_manifest(self, **kwargs):
|
def update_manifest(self, **kwargs) -> int:
|
||||||
return wpt.manifestupdate.update(check_clean=False)
|
return wpt.manifestupdate.update(check_clean=False)
|
||||||
|
|
||||||
@Command("fmt", description="Format Rust, Python, and TOML files", category="testing")
|
@Command("fmt", description="Format Rust, Python, and TOML files", category="testing")
|
||||||
def format_code(self):
|
def format_code(self) -> int:
|
||||||
result = format_python_files_with_ruff(check_only=False)
|
result = format_python_files_with_ruff(check_only=False)
|
||||||
if result != 0:
|
if result != 0:
|
||||||
return result
|
return result
|
||||||
|
@ -404,7 +410,7 @@ class MachCommands(CommandBase):
|
||||||
@Command(
|
@Command(
|
||||||
"update-wpt", description="Update the web platform tests", category="testing", parser=wpt.update.create_parser
|
"update-wpt", description="Update the web platform tests", category="testing", parser=wpt.update.create_parser
|
||||||
)
|
)
|
||||||
def update_wpt(self, **kwargs):
|
def update_wpt(self, **kwargs) -> int:
|
||||||
patch = kwargs.get("patch", False)
|
patch = kwargs.get("patch", False)
|
||||||
if not patch and kwargs["sync"]:
|
if not patch and kwargs["sync"]:
|
||||||
print("Are you sure you don't want a patch?")
|
print("Are you sure you don't want a patch?")
|
||||||
|
@ -413,32 +419,33 @@ class MachCommands(CommandBase):
|
||||||
|
|
||||||
@Command("test-jquery", description="Run the jQuery test suite", category="testing")
|
@Command("test-jquery", description="Run the jQuery test suite", category="testing")
|
||||||
@CommandBase.common_command_arguments(binary_selection=True)
|
@CommandBase.common_command_arguments(binary_selection=True)
|
||||||
def test_jquery(self, servo_binary: str):
|
def test_jquery(self, servo_binary: str) -> int:
|
||||||
return self.jquery_test_runner("test", servo_binary)
|
return self.jquery_test_runner("test", servo_binary)
|
||||||
|
|
||||||
@Command("test-dromaeo", description="Run the Dromaeo test suite", category="testing")
|
@Command("test-dromaeo", description="Run the Dromaeo test suite", category="testing")
|
||||||
@CommandArgument("tests", default=["recommended"], nargs="...", help="Specific tests to run")
|
@CommandArgument("tests", default=["recommended"], nargs="...", help="Specific tests to run")
|
||||||
@CommandArgument("--bmf-output", default=None, help="Specify BMF JSON output file")
|
@CommandArgument("--bmf-output", default=None, help="Specify BMF JSON output file")
|
||||||
@CommandBase.common_command_arguments(binary_selection=True)
|
@CommandBase.common_command_arguments(binary_selection=True)
|
||||||
def test_dromaeo(self, tests, servo_binary: str, bmf_output: str | None = None):
|
def test_dromaeo(self, tests, servo_binary: str, bmf_output: str | None = None) -> None:
|
||||||
return self.dromaeo_test_runner(tests, servo_binary, bmf_output)
|
return self.dromaeo_test_runner(tests, servo_binary, bmf_output)
|
||||||
|
|
||||||
@Command("test-speedometer", description="Run servo's speedometer", category="testing")
|
@Command("test-speedometer", description="Run servo's speedometer", category="testing")
|
||||||
@CommandArgument("--bmf-output", default=None, help="Specify BMF JSON output file")
|
@CommandArgument("--bmf-output", default=None, help="Specify BMF JSON output file")
|
||||||
@CommandBase.common_command_arguments(binary_selection=True)
|
@CommandBase.common_command_arguments(binary_selection=True)
|
||||||
def test_speedometer(self, servo_binary: str, bmf_output: str | None = None):
|
def test_speedometer(self, servo_binary: str, bmf_output: str | None = None) -> None:
|
||||||
return self.speedometer_runner(servo_binary, bmf_output)
|
return self.speedometer_runner(servo_binary, bmf_output)
|
||||||
|
|
||||||
@Command("test-speedometer-ohos", description="Run servo's speedometer on a ohos device", category="testing")
|
@Command("test-speedometer-ohos", description="Run servo's speedometer on a ohos device", category="testing")
|
||||||
@CommandArgument("--bmf-output", default=None, help="Specifcy BMF JSON output file")
|
@CommandArgument("--bmf-output", default=None, help="Specifcy BMF JSON output file")
|
||||||
@CommandArgument("--profile", default=None, help="Specify a profile which will be prepended to the output")
|
@CommandArgument("--profile", default=None, help="Specify a profile which will be prepended to the output")
|
||||||
# This needs to be a separate command because we do not need a binary locally
|
# This needs to be a separate command because we do not need a binary locally
|
||||||
def test_speedometer_ohos(self, bmf_output: str | None = None, profile: str | None = None):
|
|
||||||
|
def test_speedometer_ohos(self, bmf_output: str | None = None, profile: str | None = None) -> None:
|
||||||
return self.speedometer_runner_ohos(bmf_output, profile)
|
return self.speedometer_runner_ohos(bmf_output, profile)
|
||||||
|
|
||||||
@Command("update-jquery", description="Update the jQuery test suite expected results", category="testing")
|
@Command("update-jquery", description="Update the jQuery test suite expected results", category="testing")
|
||||||
@CommandBase.common_command_arguments(binary_selection=True)
|
@CommandBase.common_command_arguments(binary_selection=True)
|
||||||
def update_jquery(self, servo_binary: str):
|
def update_jquery(self, servo_binary: str) -> int:
|
||||||
return self.jquery_test_runner("update", servo_binary)
|
return self.jquery_test_runner("update", servo_binary)
|
||||||
|
|
||||||
@Command(
|
@Command(
|
||||||
|
@ -447,7 +454,7 @@ class MachCommands(CommandBase):
|
||||||
@CommandArgument(
|
@CommandArgument(
|
||||||
"params", default=None, nargs="...", help=" filepaths of output files of two runs of dromaeo test "
|
"params", default=None, nargs="...", help=" filepaths of output files of two runs of dromaeo test "
|
||||||
)
|
)
|
||||||
def compare_dromaeo(self, params):
|
def compare_dromaeo(self, params) -> None:
|
||||||
prev_op_filename = params[0]
|
prev_op_filename = params[0]
|
||||||
cur_op_filename = params[1]
|
cur_op_filename = params[1]
|
||||||
result = {"Test": [], "Prev_Time": [], "Cur_Time": [], "Difference(%)": []}
|
result = {"Test": [], "Prev_Time": [], "Cur_Time": [], "Difference(%)": []}
|
||||||
|
@ -527,7 +534,7 @@ class MachCommands(CommandBase):
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
def jquery_test_runner(self, cmd, binary: str):
|
def jquery_test_runner(self, cmd: str, binary: str) -> int:
|
||||||
base_dir = path.abspath(path.join("tests", "jquery"))
|
base_dir = path.abspath(path.join("tests", "jquery"))
|
||||||
jquery_dir = path.join(base_dir, "jquery")
|
jquery_dir = path.join(base_dir, "jquery")
|
||||||
run_file = path.join(base_dir, "run_jquery.py")
|
run_file = path.join(base_dir, "run_jquery.py")
|
||||||
|
@ -544,7 +551,7 @@ class MachCommands(CommandBase):
|
||||||
|
|
||||||
return call([run_file, cmd, bin_path, base_dir])
|
return call([run_file, cmd, bin_path, base_dir])
|
||||||
|
|
||||||
def dromaeo_test_runner(self, tests, binary: str, bmf_output: str | None):
|
def dromaeo_test_runner(self, tests, binary: str, bmf_output: str | None) -> None:
|
||||||
base_dir = path.abspath(path.join("tests", "dromaeo"))
|
base_dir = path.abspath(path.join("tests", "dromaeo"))
|
||||||
dromaeo_dir = path.join(base_dir, "dromaeo")
|
dromaeo_dir = path.join(base_dir, "dromaeo")
|
||||||
run_file = path.join(base_dir, "run_dromaeo.py")
|
run_file = path.join(base_dir, "run_dromaeo.py")
|
||||||
|
@ -570,11 +577,11 @@ class MachCommands(CommandBase):
|
||||||
|
|
||||||
return check_call([run_file, "|".join(tests), bin_path, base_dir, bmf_output])
|
return check_call([run_file, "|".join(tests), bin_path, base_dir, bmf_output])
|
||||||
|
|
||||||
def speedometer_to_bmf(self, speedometer: str, bmf_output: str | None = None, profile: str | None = None):
|
def speedometer_to_bmf(self, speedometer: dict[str, Any], bmf_output: str, profile: str | None = None) -> None:
|
||||||
output = dict()
|
output = dict()
|
||||||
profile = "" if profile is None else profile + "/"
|
profile = "" if profile is None else profile + "/"
|
||||||
|
|
||||||
def parse_speedometer_result(result):
|
def parse_speedometer_result(result) -> None:
|
||||||
if result["unit"] == "ms":
|
if result["unit"] == "ms":
|
||||||
output[profile + f"Speedometer/{result['name']}"] = {
|
output[profile + f"Speedometer/{result['name']}"] = {
|
||||||
"latency": { # speedometer has ms we need to convert to ns
|
"latency": { # speedometer has ms we need to convert to ns
|
||||||
|
@ -592,7 +599,7 @@ class MachCommands(CommandBase):
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else:
|
else:
|
||||||
raise "Unknown unit!"
|
raise Exception("Unknown unit!")
|
||||||
|
|
||||||
for child in result["children"]:
|
for child in result["children"]:
|
||||||
parse_speedometer_result(child)
|
parse_speedometer_result(child)
|
||||||
|
@ -602,7 +609,7 @@ class MachCommands(CommandBase):
|
||||||
with open(bmf_output, "w", encoding="utf-8") as f:
|
with open(bmf_output, "w", encoding="utf-8") as f:
|
||||||
json.dump(output, f, indent=4)
|
json.dump(output, f, indent=4)
|
||||||
|
|
||||||
def speedometer_runner(self, binary: str, bmf_output: str | None):
|
def speedometer_runner(self, binary: str, bmf_output: str | None) -> None:
|
||||||
speedometer = json.loads(
|
speedometer = json.loads(
|
||||||
subprocess.check_output(
|
subprocess.check_output(
|
||||||
[
|
[
|
||||||
|
@ -622,13 +629,16 @@ class MachCommands(CommandBase):
|
||||||
if bmf_output:
|
if bmf_output:
|
||||||
self.speedometer_to_bmf(speedometer, bmf_output)
|
self.speedometer_to_bmf(speedometer, bmf_output)
|
||||||
|
|
||||||
def speedometer_runner_ohos(self, bmf_output: str | None, profile: str | None):
|
def speedometer_runner_ohos(self, bmf_output: str | None, profile: str | None) -> None:
|
||||||
hdc_path: str = shutil.which("hdc")
|
hdc_path = shutil.which("hdc")
|
||||||
log_path: str = "/data/app/el2/100/base/org.servo.servo/cache/servo.log"
|
log_path: str = "/data/app/el2/100/base/org.servo.servo/cache/servo.log"
|
||||||
if hdc_path is None:
|
|
||||||
hdc_path = path.join(os.getenv("OHOS_SDK_NATIVE"), "../", "toolchains", "hdc")
|
|
||||||
|
|
||||||
def read_log_file() -> str:
|
if hdc_path is None:
|
||||||
|
ohos_sdk_native = os.getenv("OHOS_SDK_NATIVE")
|
||||||
|
assert ohos_sdk_native
|
||||||
|
hdc_path = path.join(ohos_sdk_native, "../", "toolchains", "hdc")
|
||||||
|
|
||||||
|
def read_log_file(hdc_path: str) -> str:
|
||||||
subprocess.call([hdc_path, "file", "recv", log_path])
|
subprocess.call([hdc_path, "file", "recv", log_path])
|
||||||
file = ""
|
file = ""
|
||||||
try:
|
try:
|
||||||
|
@ -670,12 +680,12 @@ class MachCommands(CommandBase):
|
||||||
whole_file: str = ""
|
whole_file: str = ""
|
||||||
for i in range(10):
|
for i in range(10):
|
||||||
sleep(30)
|
sleep(30)
|
||||||
whole_file = read_log_file()
|
whole_file = read_log_file(hdc_path)
|
||||||
if "[INFO script::dom::console]" in whole_file:
|
if "[INFO script::dom::console]" in whole_file:
|
||||||
# technically the file could not have been written completely yet
|
# technically the file could not have been written completely yet
|
||||||
# on devices with slow flash, we might want to wait a bit more
|
# on devices with slow flash, we might want to wait a bit more
|
||||||
sleep(2)
|
sleep(2)
|
||||||
whole_file = read_log_file()
|
whole_file = read_log_file(hdc_path)
|
||||||
break
|
break
|
||||||
start_index: int = whole_file.index("[INFO script::dom::console]") + len("[INFO script::dom::console]") + 1
|
start_index: int = whole_file.index("[INFO script::dom::console]") + len("[INFO script::dom::console]") + 1
|
||||||
json_string = whole_file[start_index:]
|
json_string = whole_file[start_index:]
|
||||||
|
@ -694,7 +704,7 @@ class MachCommands(CommandBase):
|
||||||
run_file = path.abspath(
|
run_file = path.abspath(
|
||||||
path.join(PROJECT_TOPLEVEL_PATH, "components", "net", "tests", "cookie_http_state_utils.py")
|
path.join(PROJECT_TOPLEVEL_PATH, "components", "net", "tests", "cookie_http_state_utils.py")
|
||||||
)
|
)
|
||||||
run_globals = {"__file__": run_file}
|
run_globals: dict[str, Any] = {"__file__": run_file}
|
||||||
exec(compile(open(run_file).read(), run_file, "exec"), run_globals)
|
exec(compile(open(run_file).read(), run_file, "exec"), run_globals)
|
||||||
return run_globals["update_test_file"](cache_dir)
|
return run_globals["update_test_file"](cache_dir)
|
||||||
|
|
||||||
|
@ -711,7 +721,7 @@ class MachCommands(CommandBase):
|
||||||
if os.path.exists(dest_folder):
|
if os.path.exists(dest_folder):
|
||||||
shutil.rmtree(dest_folder)
|
shutil.rmtree(dest_folder)
|
||||||
|
|
||||||
run_globals = {"__file__": run_file}
|
run_globals: dict[str, Any] = {"__file__": run_file}
|
||||||
exec(compile(open(run_file).read(), run_file, "exec"), run_globals)
|
exec(compile(open(run_file).read(), run_file, "exec"), run_globals)
|
||||||
return run_globals["update_conformance"](version, dest_folder, None, patches_dir)
|
return run_globals["update_conformance"](version, dest_folder, None, patches_dir)
|
||||||
|
|
||||||
|
@ -775,7 +785,7 @@ class MachCommands(CommandBase):
|
||||||
)
|
)
|
||||||
@CommandArgument("params", nargs="...", help="Command-line arguments to be passed through to Servo")
|
@CommandArgument("params", nargs="...", help="Command-line arguments to be passed through to Servo")
|
||||||
@CommandBase.common_command_arguments(binary_selection=True)
|
@CommandBase.common_command_arguments(binary_selection=True)
|
||||||
def smoketest(self, servo_binary: str, params, **kwargs):
|
def smoketest(self, servo_binary: str, params, **kwargs) -> int | None:
|
||||||
# We pass `-f` here so that any thread panic will cause Servo to exit,
|
# We pass `-f` here so that any thread panic will cause Servo to exit,
|
||||||
# preventing a panic from hanging execution. This means that these kind
|
# preventing a panic from hanging execution. This means that these kind
|
||||||
# of panics won't cause timeouts on CI.
|
# of panics won't cause timeouts on CI.
|
||||||
|
@ -786,7 +796,7 @@ class MachCommands(CommandBase):
|
||||||
@CommandArgument(
|
@CommandArgument(
|
||||||
"try_strings", default=["full"], nargs="...", help="A list of try strings specifying what kind of job to run."
|
"try_strings", default=["full"], nargs="...", help="A list of try strings specifying what kind of job to run."
|
||||||
)
|
)
|
||||||
def try_command(self, remote: str, try_strings: list[str]):
|
def try_command(self, remote: str, try_strings: list[str]) -> int:
|
||||||
if subprocess.check_output(["git", "diff", "--cached", "--name-only"]).strip():
|
if subprocess.check_output(["git", "diff", "--cached", "--name-only"]).strip():
|
||||||
print("Cannot run `try` with staged and uncommited changes. ")
|
print("Cannot run `try` with staged and uncommited changes. ")
|
||||||
print("Please either commit or stash them before running `try`.")
|
print("Please either commit or stash them before running `try`.")
|
||||||
|
@ -836,7 +846,7 @@ class MachCommands(CommandBase):
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
|
||||||
def create_parser_create():
|
def create_parser_create() -> ArgumentParser:
|
||||||
import argparse
|
import argparse
|
||||||
|
|
||||||
p = argparse.ArgumentParser()
|
p = argparse.ArgumentParser()
|
||||||
|
|
|
@ -61,7 +61,7 @@ class JobConfig(object):
|
||||||
self.update_name()
|
self.update_name()
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def update_name(self):
|
def update_name(self) -> None:
|
||||||
if self.workflow is Workflow.LINUX:
|
if self.workflow is Workflow.LINUX:
|
||||||
self.name = "Linux"
|
self.name = "Linux"
|
||||||
elif self.workflow is Workflow.MACOS:
|
elif self.workflow is Workflow.MACOS:
|
||||||
|
@ -158,7 +158,7 @@ def handle_preset(s: str) -> Optional[JobConfig]:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
def handle_modifier(config: JobConfig, s: str) -> Optional[JobConfig]:
|
def handle_modifier(config: Optional[JobConfig], s: str) -> Optional[JobConfig]:
|
||||||
if config is None:
|
if config is None:
|
||||||
return None
|
return None
|
||||||
s = s.lower()
|
s = s.lower()
|
||||||
|
@ -184,13 +184,13 @@ class Encoder(json.JSONEncoder):
|
||||||
|
|
||||||
|
|
||||||
class Config(object):
|
class Config(object):
|
||||||
def __init__(self, s: Optional[str] = None):
|
def __init__(self, s: Optional[str] = None) -> None:
|
||||||
self.fail_fast: bool = False
|
self.fail_fast: bool = False
|
||||||
self.matrix: list[JobConfig] = list()
|
self.matrix: list[JobConfig] = list()
|
||||||
if s is not None:
|
if s is not None:
|
||||||
self.parse(s)
|
self.parse(s)
|
||||||
|
|
||||||
def parse(self, input: str):
|
def parse(self, input: str) -> None:
|
||||||
input = input.lower().strip()
|
input = input.lower().strip()
|
||||||
|
|
||||||
if not input:
|
if not input:
|
||||||
|
@ -224,7 +224,7 @@ class Config(object):
|
||||||
else:
|
else:
|
||||||
self.add_or_merge_job_to_matrix(job)
|
self.add_or_merge_job_to_matrix(job)
|
||||||
|
|
||||||
def add_or_merge_job_to_matrix(self, job: JobConfig):
|
def add_or_merge_job_to_matrix(self, job: JobConfig) -> None:
|
||||||
for existing_job in self.matrix:
|
for existing_job in self.matrix:
|
||||||
if existing_job.merge(job):
|
if existing_job.merge(job):
|
||||||
return
|
return
|
||||||
|
@ -234,7 +234,7 @@ class Config(object):
|
||||||
return json.dumps(self, cls=Encoder, **kwargs)
|
return json.dumps(self, cls=Encoder, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main() -> None:
|
||||||
conf = Config(" ".join(sys.argv[1:]))
|
conf = Config(" ".join(sys.argv[1:]))
|
||||||
print(conf.to_json())
|
print(conf.to_json())
|
||||||
|
|
||||||
|
@ -244,7 +244,7 @@ if __name__ == "__main__":
|
||||||
|
|
||||||
|
|
||||||
class TestParser(unittest.TestCase):
|
class TestParser(unittest.TestCase):
|
||||||
def test_string(self):
|
def test_string(self) -> None:
|
||||||
self.assertDictEqual(
|
self.assertDictEqual(
|
||||||
json.loads(Config("linux-unit-tests fail-fast").to_json()),
|
json.loads(Config("linux-unit-tests fail-fast").to_json()),
|
||||||
{
|
{
|
||||||
|
@ -266,7 +266,7 @@ class TestParser(unittest.TestCase):
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
def test_empty(self):
|
def test_empty(self) -> None:
|
||||||
self.assertDictEqual(
|
self.assertDictEqual(
|
||||||
json.loads(Config("").to_json()),
|
json.loads(Config("").to_json()),
|
||||||
{
|
{
|
||||||
|
@ -348,7 +348,7 @@ class TestParser(unittest.TestCase):
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
def test_job_merging(self):
|
def test_job_merging(self) -> None:
|
||||||
self.assertDictEqual(
|
self.assertDictEqual(
|
||||||
json.loads(Config("linux-wpt").to_json()),
|
json.loads(Config("linux-wpt").to_json()),
|
||||||
{
|
{
|
||||||
|
@ -379,6 +379,8 @@ class TestParser(unittest.TestCase):
|
||||||
a = handle_modifier(a, "linux-unit-tests")
|
a = handle_modifier(a, "linux-unit-tests")
|
||||||
b = handle_preset("linux-wpt")
|
b = handle_preset("linux-wpt")
|
||||||
b = handle_modifier(b, "linux-wpt")
|
b = handle_modifier(b, "linux-wpt")
|
||||||
|
assert a is not None
|
||||||
|
assert b is not None
|
||||||
self.assertTrue(a.merge(b), "Should merge jobs that have different unit test configurations.")
|
self.assertTrue(a.merge(b), "Should merge jobs that have different unit test configurations.")
|
||||||
self.assertEqual(a, JobConfig("Linux (Unit Tests, WPT)", Workflow.LINUX, unit_tests=True, wpt=True))
|
self.assertEqual(a, JobConfig("Linux (Unit Tests, WPT)", Workflow.LINUX, unit_tests=True, wpt=True))
|
||||||
|
|
||||||
|
@ -402,14 +404,14 @@ class TestParser(unittest.TestCase):
|
||||||
self.assertFalse(a.merge(b), "Should not merge jobs with different build arguments.")
|
self.assertFalse(a.merge(b), "Should not merge jobs with different build arguments.")
|
||||||
self.assertEqual(a, JobConfig("Linux (Unit Tests)", Workflow.LINUX, unit_tests=True))
|
self.assertEqual(a, JobConfig("Linux (Unit Tests)", Workflow.LINUX, unit_tests=True))
|
||||||
|
|
||||||
def test_full(self):
|
def test_full(self) -> None:
|
||||||
self.assertDictEqual(json.loads(Config("full").to_json()), json.loads(Config("").to_json()))
|
self.assertDictEqual(json.loads(Config("full").to_json()), json.loads(Config("").to_json()))
|
||||||
|
|
||||||
def test_wpt_alias(self):
|
def test_wpt_alias(self) -> None:
|
||||||
self.assertDictEqual(json.loads(Config("wpt").to_json()), json.loads(Config("linux-wpt").to_json()))
|
self.assertDictEqual(json.loads(Config("wpt").to_json()), json.loads(Config("linux-wpt").to_json()))
|
||||||
|
|
||||||
|
|
||||||
def run_tests():
|
def run_tests() -> bool:
|
||||||
verbosity = 1 if logging.getLogger().level >= logging.WARN else 2
|
verbosity = 1 if logging.getLogger().level >= logging.WARN else 2
|
||||||
suite = unittest.TestLoader().loadTestsFromTestCase(TestParser)
|
suite = unittest.TestLoader().loadTestsFromTestCase(TestParser)
|
||||||
return unittest.TextTestRunner(verbosity=verbosity).run(suite).wasSuccessful()
|
return unittest.TextTestRunner(verbosity=verbosity).run(suite).wasSuccessful()
|
||||||
|
|
|
@ -14,9 +14,10 @@ import shutil
|
||||||
import stat
|
import stat
|
||||||
import sys
|
import sys
|
||||||
import time
|
import time
|
||||||
import urllib
|
import urllib.error
|
||||||
import urllib.request
|
import urllib.request
|
||||||
import zipfile
|
import zipfile
|
||||||
|
from zipfile import ZipInfo
|
||||||
from typing import Dict, List, Union
|
from typing import Dict, List, Union
|
||||||
|
|
||||||
from io import BufferedIOBase, BytesIO
|
from io import BufferedIOBase, BytesIO
|
||||||
|
@ -26,20 +27,20 @@ SCRIPT_PATH = os.path.abspath(os.path.dirname(__file__))
|
||||||
SERVO_ROOT = os.path.abspath(os.path.join(SCRIPT_PATH, "..", ".."))
|
SERVO_ROOT = os.path.abspath(os.path.join(SCRIPT_PATH, "..", ".."))
|
||||||
|
|
||||||
|
|
||||||
def remove_readonly(func, path, _):
|
def remove_readonly(func, path, _) -> None:
|
||||||
"Clear the readonly bit and reattempt the removal"
|
"Clear the readonly bit and reattempt the removal"
|
||||||
os.chmod(path, stat.S_IWRITE)
|
os.chmod(path, stat.S_IWRITE)
|
||||||
func(path)
|
func(path)
|
||||||
|
|
||||||
|
|
||||||
def delete(path):
|
def delete(path) -> None:
|
||||||
if os.path.isdir(path) and not os.path.islink(path):
|
if os.path.isdir(path) and not os.path.islink(path):
|
||||||
shutil.rmtree(path, onerror=remove_readonly)
|
shutil.rmtree(path, onerror=remove_readonly)
|
||||||
else:
|
else:
|
||||||
os.remove(path)
|
os.remove(path)
|
||||||
|
|
||||||
|
|
||||||
def download(description: str, url: str, writer: BufferedIOBase, start_byte: int = 0):
|
def download(description: str, url: str, writer: BufferedIOBase, start_byte: int = 0) -> None:
|
||||||
if start_byte:
|
if start_byte:
|
||||||
print("Resuming download of {} ...".format(url))
|
print("Resuming download of {} ...".format(url))
|
||||||
else:
|
else:
|
||||||
|
@ -101,13 +102,13 @@ def download(description: str, url: str, writer: BufferedIOBase, start_byte: int
|
||||||
raise
|
raise
|
||||||
|
|
||||||
|
|
||||||
def download_bytes(description: str, url: str):
|
def download_bytes(description: str, url: str) -> bytes:
|
||||||
content_writer = BytesIO()
|
content_writer = BytesIO()
|
||||||
download(description, url, content_writer)
|
download(description, url, content_writer)
|
||||||
return content_writer.getvalue()
|
return content_writer.getvalue()
|
||||||
|
|
||||||
|
|
||||||
def download_file(description: str, url: str, destination_path: str):
|
def download_file(description: str, url: str, destination_path: str) -> None:
|
||||||
tmp_path = destination_path + ".part"
|
tmp_path = destination_path + ".part"
|
||||||
try:
|
try:
|
||||||
start_byte = os.path.getsize(tmp_path)
|
start_byte = os.path.getsize(tmp_path)
|
||||||
|
@ -122,7 +123,7 @@ def download_file(description: str, url: str, destination_path: str):
|
||||||
# https://stackoverflow.com/questions/39296101/python-zipfile-removes-execute-permissions-from-binaries
|
# https://stackoverflow.com/questions/39296101/python-zipfile-removes-execute-permissions-from-binaries
|
||||||
# In particular, we want the executable bit for executable files.
|
# In particular, we want the executable bit for executable files.
|
||||||
class ZipFileWithUnixPermissions(zipfile.ZipFile):
|
class ZipFileWithUnixPermissions(zipfile.ZipFile):
|
||||||
def extract(self, member, path=None, pwd=None):
|
def extract(self, member, path=None, pwd=None) -> str:
|
||||||
if not isinstance(member, zipfile.ZipInfo):
|
if not isinstance(member, zipfile.ZipInfo):
|
||||||
member = self.getinfo(member)
|
member = self.getinfo(member)
|
||||||
|
|
||||||
|
@ -136,11 +137,12 @@ class ZipFileWithUnixPermissions(zipfile.ZipFile):
|
||||||
return extracted
|
return extracted
|
||||||
|
|
||||||
# For Python 3.x
|
# For Python 3.x
|
||||||
def _extract_member(self, member, targetpath, pwd):
|
def _extract_member(self, member: ZipInfo, targetpath, pwd) -> str:
|
||||||
if sys.version_info[0] >= 3:
|
if int(sys.version_info[0]) >= 3:
|
||||||
if not isinstance(member, zipfile.ZipInfo):
|
if not isinstance(member, zipfile.ZipInfo):
|
||||||
member = self.getinfo(member)
|
member = self.getinfo(member)
|
||||||
|
|
||||||
|
# pyrefly: ignore # missing-attribute
|
||||||
targetpath = super()._extract_member(member, targetpath, pwd)
|
targetpath = super()._extract_member(member, targetpath, pwd)
|
||||||
|
|
||||||
attr = member.external_attr >> 16
|
attr = member.external_attr >> 16
|
||||||
|
@ -148,10 +150,11 @@ class ZipFileWithUnixPermissions(zipfile.ZipFile):
|
||||||
os.chmod(targetpath, attr)
|
os.chmod(targetpath, attr)
|
||||||
return targetpath
|
return targetpath
|
||||||
else:
|
else:
|
||||||
|
# pyrefly: ignore # missing-attribute
|
||||||
return super(ZipFileWithUnixPermissions, self)._extract_member(member, targetpath, pwd)
|
return super(ZipFileWithUnixPermissions, self)._extract_member(member, targetpath, pwd)
|
||||||
|
|
||||||
|
|
||||||
def extract(src, dst, movedir=None, remove=True):
|
def extract(src, dst, movedir=None, remove=True) -> None:
|
||||||
assert src.endswith(".zip")
|
assert src.endswith(".zip")
|
||||||
ZipFileWithUnixPermissions(src).extractall(dst)
|
ZipFileWithUnixPermissions(src).extractall(dst)
|
||||||
|
|
||||||
|
@ -166,7 +169,7 @@ def extract(src, dst, movedir=None, remove=True):
|
||||||
os.remove(src)
|
os.remove(src)
|
||||||
|
|
||||||
|
|
||||||
def check_hash(filename, expected, algorithm):
|
def check_hash(filename, expected, algorithm) -> None:
|
||||||
hasher = hashlib.new(algorithm)
|
hasher = hashlib.new(algorithm)
|
||||||
with open(filename, "rb") as f:
|
with open(filename, "rb") as f:
|
||||||
while True:
|
while True:
|
||||||
|
@ -179,11 +182,11 @@ def check_hash(filename, expected, algorithm):
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
|
|
||||||
def get_default_cache_dir(topdir):
|
def get_default_cache_dir(topdir) -> str:
|
||||||
return os.environ.get("SERVO_CACHE_DIR", os.path.join(topdir, ".servo"))
|
return os.environ.get("SERVO_CACHE_DIR", os.path.join(topdir, ".servo"))
|
||||||
|
|
||||||
|
|
||||||
def append_paths_to_env(env: Dict[str, str], key: str, paths: Union[str, List[str]]):
|
def append_paths_to_env(env: Dict[str, str], key: str, paths: Union[str, List[str]]) -> None:
|
||||||
if isinstance(paths, list):
|
if isinstance(paths, list):
|
||||||
paths = os.pathsep.join(paths)
|
paths = os.pathsep.join(paths)
|
||||||
|
|
||||||
|
@ -195,7 +198,7 @@ def append_paths_to_env(env: Dict[str, str], key: str, paths: Union[str, List[st
|
||||||
env[key] = new_value
|
env[key] = new_value
|
||||||
|
|
||||||
|
|
||||||
def prepend_paths_to_env(env: Dict[str, str], key: str, paths: Union[str, List[str]]):
|
def prepend_paths_to_env(env: Dict[str, str], key: str, paths: Union[str, List[str]]) -> None:
|
||||||
if isinstance(paths, list):
|
if isinstance(paths, list):
|
||||||
paths = os.pathsep.join(paths)
|
paths = os.pathsep.join(paths)
|
||||||
|
|
||||||
|
@ -206,5 +209,5 @@ def prepend_paths_to_env(env: Dict[str, str], key: str, paths: Union[str, List[s
|
||||||
env[key] = new_value
|
env[key] = new_value
|
||||||
|
|
||||||
|
|
||||||
def get_target_dir():
|
def get_target_dir() -> str:
|
||||||
return os.environ.get("CARGO_TARGET_DIR", os.path.join(SERVO_ROOT, "target"))
|
return os.environ.get("CARGO_TARGET_DIR", os.path.join(SERVO_ROOT, "target"))
|
||||||
|
|
|
@ -30,11 +30,11 @@ class VisualStudioInstallation:
|
||||||
installation_path: str
|
installation_path: str
|
||||||
vc_install_path: str
|
vc_install_path: str
|
||||||
|
|
||||||
def __lt__(self, other):
|
def __lt__(self, other) -> bool:
|
||||||
return self.version_number < other.version_number
|
return self.version_number < other.version_number
|
||||||
|
|
||||||
|
|
||||||
def find_vswhere():
|
def find_vswhere() -> str | None:
|
||||||
for path in [PROGRAM_FILES, PROGRAM_FILES_X86]:
|
for path in [PROGRAM_FILES, PROGRAM_FILES_X86]:
|
||||||
if not path:
|
if not path:
|
||||||
continue
|
continue
|
||||||
|
@ -145,11 +145,11 @@ def find_msvc_redist_dirs(vs_platform: str) -> Generator[str, None, None]:
|
||||||
path1 = os.path.join(vs_platform, "Microsoft.{}.CRT".format(redist_version))
|
path1 = os.path.join(vs_platform, "Microsoft.{}.CRT".format(redist_version))
|
||||||
path2 = os.path.join("onecore", vs_platform, "Microsoft.{}.CRT".format(redist_version))
|
path2 = os.path.join("onecore", vs_platform, "Microsoft.{}.CRT".format(redist_version))
|
||||||
for path in [path1, path2]:
|
for path in [path1, path2]:
|
||||||
path = os.path.join(redist_path, path)
|
full_path = os.path.join(redist_path, path)
|
||||||
if os.path.isdir(path):
|
if os.path.isdir(full_path):
|
||||||
yield path
|
yield full_path
|
||||||
else:
|
else:
|
||||||
tried.append(path)
|
tried.append(full_path)
|
||||||
|
|
||||||
print("Couldn't locate MSVC redistributable directory. Tried:", file=sys.stderr)
|
print("Couldn't locate MSVC redistributable directory. Tried:", file=sys.stderr)
|
||||||
for path in tried:
|
for path in tried:
|
||||||
|
@ -169,7 +169,10 @@ def find_windows_sdk_installation_path() -> str:
|
||||||
# https://stackoverflow.com/questions/35119223/how-to-programmatically-detect-and-locate-the-windows-10-sdk
|
# https://stackoverflow.com/questions/35119223/how-to-programmatically-detect-and-locate-the-windows-10-sdk
|
||||||
key_path = r"SOFTWARE\Wow6432Node\Microsoft\Microsoft SDKs\Windows\v10.0"
|
key_path = r"SOFTWARE\Wow6432Node\Microsoft\Microsoft SDKs\Windows\v10.0"
|
||||||
try:
|
try:
|
||||||
|
if sys.platform == "win32":
|
||||||
with winreg.OpenKeyEx(winreg.HKEY_LOCAL_MACHINE, key_path) as key:
|
with winreg.OpenKeyEx(winreg.HKEY_LOCAL_MACHINE, key_path) as key:
|
||||||
return str(winreg.QueryValueEx(key, "InstallationFolder")[0])
|
return str(winreg.QueryValueEx(key, "InstallationFolder")[0])
|
||||||
|
else:
|
||||||
|
raise EnvironmentError("Couldn't locate HKEY_LOCAL_MACHINE because it's only available on Windows system")
|
||||||
except FileNotFoundError:
|
except FileNotFoundError:
|
||||||
raise Exception(f"Couldn't find Windows SDK installation path in registry at path ({key_path})")
|
raise Exception(f"Couldn't find Windows SDK installation path in registry at path ({key_path})")
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue