Source code

Revision control

Copy as Markdown

Other Tools

# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
import ctypes
import os
import platform
import subprocess
import sys
from pathlib import Path
from mozbuild.util import mozilla_build_version
from packaging.version import Version
from mozboot.base import BaseBootstrapper
def is_aarch64_host():
from ctypes import wintypes
kernel32 = ctypes.windll.kernel32
IMAGE_FILE_MACHINE_UNKNOWN = 0
IMAGE_FILE_MACHINE_ARM64 = 0xAA64
try:
iswow64process2 = kernel32.IsWow64Process2
except Exception:
# If we can't access the symbol, we know we're not on aarch64.
return False
currentProcess = kernel32.GetCurrentProcess()
processMachine = wintypes.USHORT(IMAGE_FILE_MACHINE_UNKNOWN)
nativeMachine = wintypes.USHORT(IMAGE_FILE_MACHINE_UNKNOWN)
gotValue = iswow64process2(
currentProcess, ctypes.byref(processMachine), ctypes.byref(nativeMachine)
)
# If this call fails, we have no idea.
if not gotValue:
return False
return nativeMachine.value == IMAGE_FILE_MACHINE_ARM64
def get_is_windefender_disabled():
import winreg
try:
with winreg.OpenKeyEx(
winreg.HKEY_LOCAL_MACHINE, r"SOFTWARE\Microsoft\Windows Defender"
) as windefender_key:
is_antivirus_disabled, _ = winreg.QueryValueEx(
windefender_key, "DisableAntiSpyware"
)
# is_antivirus_disabled is either 0 (False) or 1 (True)
return bool(is_antivirus_disabled)
except FileNotFoundError:
return True
def get_windefender_exclusion_paths():
import winreg
paths = []
try:
with winreg.OpenKeyEx(
winreg.HKEY_LOCAL_MACHINE,
r"SOFTWARE\Microsoft\Windows Defender\Exclusions\Paths",
) as exclusions_key:
_, values_count, __ = winreg.QueryInfoKey(exclusions_key)
for i in range(0, values_count):
path, _, __ = winreg.EnumValue(exclusions_key, i)
paths.append(Path(path))
except FileNotFoundError:
pass
return paths
def is_windefender_affecting_srcdir(src_dir: Path):
if get_is_windefender_disabled():
return False
# When there's a match, but path cases aren't the same between srcdir and exclusion_path,
# commonpath will use the casing of the first path provided.
# To avoid surprises here, we normcase(...) so we don't get unexpected breakage if we change
# the path order.
src_dir = src_dir.resolve()
try:
exclusion_paths = get_windefender_exclusion_paths()
except OSError as e:
if e.winerror == 5:
# A version of Windows 10 released in 2021 raises an "Access is denied"
# error (ERROR_ACCESS_DENIED == 5) to un-elevated processes when they
# query Windows Defender's exclusions. Skip the exclusion path checking.
return
raise
for exclusion_path in exclusion_paths:
exclusion_path = exclusion_path.resolve()
try:
if Path(os.path.commonpath((exclusion_path, src_dir))) == exclusion_path:
# exclusion_path is an ancestor of srcdir
return False
except ValueError:
# ValueError: Paths don't have the same drive - can't be ours
pass
return True
class MozillaBuildBootstrapper(BaseBootstrapper):
"""Bootstrapper for MozillaBuild to install rustup."""
def __init__(self, no_interactive=False, no_system_changes=False):
BaseBootstrapper.__init__(
self, no_interactive=no_interactive, no_system_changes=no_system_changes
)
def validate_environment(self):
if is_windefender_affecting_srcdir(self.srcdir):
print(
"Warning: the Firefox checkout directory is currently not in the "
"Windows Defender exclusion list. This can cause the build process "
"to be dramatically slowed or broken. To resolve this, follow the "
"directions here: "
"#antivirus-performance",
file=sys.stderr,
)
def install_system_packages(self):
pass
def upgrade_mercurial(self, current):
# Mercurial upstream sometimes doesn't upload wheels, and building
# from source requires MS Visual C++ 9.0. So we force pip to install
# the last version that comes with wheels.
if mozilla_build_version() >= Version("4.0"):
pip_dir = (
Path(os.environ["MOZILLABUILD"]) / "python3" / "Scripts" / "pip.exe"
)
else:
pip_dir = (
Path(os.environ["MOZILLABUILD"]) / "python" / "Scripts" / "pip.exe"
)
command = [
str(pip_dir),
"install",
"--upgrade",
"mercurial",
"--only-binary",
"mercurial",
]
self.run(command)
def install_browser_packages(self, mozconfig_builder):
pass
def install_browser_artifact_mode_packages(self, mozconfig_builder):
pass
def _os_arch(self):
os_arch = platform.machine()
if os_arch == "AMD64":
# On Windows, x86_64 is reported as AMD64 but we use x86_64
# everywhere else, so let's normalized it here.
return "x86_64"
return os_arch
def install_mobile_android_packages(self, mozconfig_builder, artifact_mode=False):
from mozboot import android
os_arch = self._os_arch()
android.ensure_android(
"windows",
os_arch,
artifact_mode=artifact_mode,
no_interactive=self.no_interactive,
)
android.ensure_android(
"windows",
os_arch,
system_images_only=True,
artifact_mode=artifact_mode,
no_interactive=self.no_interactive,
avd_manifest_path=android.AVD_MANIFEST_X86_64,
)
android.ensure_android(
"windows",
os_arch,
system_images_only=True,
artifact_mode=artifact_mode,
no_interactive=self.no_interactive,
avd_manifest_path=android.AVD_MANIFEST_ARM,
)
def ensure_mobile_android_packages(self):
from mozboot import android
android.ensure_java("windows", self._os_arch())
self.install_toolchain_artifact(android.WINDOWS_X86_64_ANDROID_AVD)
self.install_toolchain_artifact(android.WINDOWS_ARM_ANDROID_AVD)
def install_mobile_android_artifact_mode_packages(self, mozconfig_builder):
self.install_mobile_android_packages(mozconfig_builder, artifact_mode=True)
def generate_mobile_android_mozconfig(self, artifact_mode=False):
from mozboot import android
return android.generate_mozconfig("windows", artifact_mode=artifact_mode)
def generate_mobile_android_artifact_mode_mozconfig(self):
return self.generate_mobile_android_mozconfig(artifact_mode=True)
def ensure_sccache_packages(self):
from mozboot import sccache
self.install_toolchain_artifact(sccache.RUSTC_DIST_TOOLCHAIN, no_unpack=True)
self.install_toolchain_artifact(sccache.CLANG_DIST_TOOLCHAIN, no_unpack=True)
def _update_package_manager(self):
pass
def run(self, command):
subprocess.check_call(command, stdin=sys.stdin)