Make the --release/--dev more consistent and less surprising (#30091)

There were some issues with the way that the `--release` and `--dev`
arguments were handled in mach commands.

 - Not all commands accepted them in the same way. For instance `./mach
   test-wpt` didn't really accept them at all.
 - If you did not pass either of them, mach would try to guess which
   build you meant. This guess was often quite surprising as it wasn't
   printed and it depended on the state of the your target directory,
   which is difficult to remember.
 - The `dev` profile is colloquially called a "debug" profile and some
   commands accepted `-d` or `--debug...` like arguments, but `--debug`
   with `./mach run` meant run in a debugger. It was easy to mix this
   up.

This change:

 - Centralizes where build type argument processing happens. Now it the
   same shared decorator in CommandBase.
 - Uses a `BuildType` enum instead of passing around two different
   booleans. This reduces the error checking for situations where both
   are true.
 - Be much less clever about guessing what build to use. Now if you
   don't specify a build type, `--dev` is chosen. I think this behavior
   matches cargo.
 - Makes it so that `./mach test-wpt` accepts the exact same arguments
   and has the same behavior as other commands. In addition, the suite
   correct for `test-wpt` is removed. There are only two suites now and
   it's quite unlikely that people will confuse WPT tests for rust unit
   tests.
This commit is contained in:
Martin Robinson 2023-08-14 12:21:29 +02:00 committed by GitHub
parent cc86854c4e
commit 1d79f5dad2
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
13 changed files with 242 additions and 344 deletions

View file

@ -80,7 +80,7 @@ jobs:
- name: Release build - name: Release build
run: python3 ./mach build --release run: python3 ./mach build --release
- name: Smoketest - name: Smoketest
run: xvfb-run python3 ./mach smoketest run: xvfb-run python3 ./mach smoketest --release
- name: Script tests - name: Script tests
run: ./mach test-scripts run: ./mach test-scripts
- name: Unit tests - name: Unit tests

View file

@ -42,7 +42,7 @@ jobs:
python3 -m pip install --upgrade pip virtualenv python3 -m pip install --upgrade pip virtualenv
python3 ./mach bootstrap python3 ./mach bootstrap
- name: Smoketest - name: Smoketest
run: python3 ./mach smoketest run: python3 ./mach smoketest --release
- name: Run tests - name: Run tests
run: | run: |
python3 ./mach test-wpt --with-${{ inputs.layout }} \ python3 ./mach test-wpt --with-${{ inputs.layout }} \

View file

@ -69,7 +69,7 @@ jobs:
run: | run: |
python3 ./mach build --release python3 ./mach build --release
- name: Smoketest - name: Smoketest
run: python3 ./mach smoketest run: python3 ./mach smoketest --release
- name: Script tests - name: Script tests
run: ./mach test-scripts run: ./mach test-scripts
- name: Unit tests - name: Unit tests

View file

@ -30,6 +30,6 @@ jobs:
- name: Release build - name: Release build
run: python3 ./mach build --release run: python3 ./mach build --release
- name: Smoketest - name: Smoketest
run: xvfb-run python3 ./mach smoketest run: xvfb-run python3 ./mach smoketest --release
- name: Unit tests - name: Unit tests
run: python3 ./mach test-unit --release run: python3 ./mach test-unit --release

View file

@ -67,7 +67,7 @@ jobs:
- name: Copy resources - name: Copy resources
run: cp D:\a\servo\servo\resources C:\a\servo\servo -Recurse run: cp D:\a\servo\servo\resources C:\a\servo\servo -Recurse
- name: Smoketest - name: Smoketest
run: python mach smoketest --angle run: python mach smoketest --angle --release
- name: Unit tests - name: Unit tests
if: ${{ inputs.unit-tests || github.ref_name == 'try-windows' }} if: ${{ inputs.unit-tests || github.ref_name == 'try-windows' }}
run: python mach test-unit --release run: python mach test-unit --release

View file

@ -35,21 +35,13 @@ from mach.registrar import Registrar
import servo.platform import servo.platform
import servo.util import servo.util
from servo.command_base import CommandBase, call, check_call from servo.command_base import BuildType, CommandBase, call, check_call
from servo.gstreamer import windows_dlls, windows_plugins, macos_plugins from servo.gstreamer import windows_dlls, windows_plugins, macos_plugins
@CommandProvider @CommandProvider
class MachCommands(CommandBase): class MachCommands(CommandBase):
@Command('build', @Command('build', description='Build Servo', category='build')
description='Build Servo',
category='build')
@CommandArgument('--release', '-r',
action='store_true',
help='Build in release mode')
@CommandArgument('--dev', '-d',
action='store_true',
help='Build in development mode')
@CommandArgument('--jobs', '-j', @CommandArgument('--jobs', '-j',
default=None, default=None,
help='Number of jobs to run in parallel') help='Number of jobs to run in parallel')
@ -64,41 +56,14 @@ class MachCommands(CommandBase):
help='Print very verbose output') help='Print very verbose output')
@CommandArgument('params', nargs='...', @CommandArgument('params', nargs='...',
help="Command-line arguments to be passed through to Cargo") help="Command-line arguments to be passed through to Cargo")
@CommandBase.build_like_command_arguments @CommandBase.common_command_arguments(build_configuration=True, build_type=True)
def build(self, release=False, dev=False, jobs=None, params=None, no_package=False, def build(self, build_type: BuildType, jobs=None, params=None, no_package=False,
verbose=False, very_verbose=False, libsimpleservo=False, **kwargs): verbose=False, very_verbose=False, libsimpleservo=False, **kwargs):
opts = params or [] opts = params or []
has_media_stack = "media-gstreamer" in self.features has_media_stack = "media-gstreamer" in self.features
release_path = path.join(self.target_path, "release", "servo") if build_type == BuildType.RELEASE:
dev_path = path.join(self.target_path, "debug", "servo")
release_exists = path.exists(release_path)
dev_exists = path.exists(dev_path)
if not (release or dev):
if self.config["build"]["mode"] == "dev":
dev = True
elif self.config["build"]["mode"] == "release":
release = True
elif release_exists and not dev_exists:
release = True
elif dev_exists and not release_exists:
dev = True
else:
print("Please specify either --dev (-d) for a development")
print(" build, or --release (-r) for an optimized build.")
sys.exit(1)
if release and dev:
print("Please specify either --dev or --release.")
sys.exit(1)
if release:
opts += ["--release"] opts += ["--release"]
servo_path = release_path
else:
servo_path = dev_path
if jobs is not None: if jobs is not None:
opts += ["-j", jobs] opts += ["-j", jobs]
@ -176,15 +141,14 @@ class MachCommands(CommandBase):
flavor = "googlevr" flavor = "googlevr"
elif "oculusvr" in self.features: elif "oculusvr" in self.features:
flavor = "oculusvr" flavor = "oculusvr"
rv = Registrar.dispatch("package", context=self.context, rv = Registrar.dispatch("package", context=self.context, build_type=build_type,
release=release, dev=dev, target=self.cross_compile_target, target=self.cross_compile_target, flavor=flavor)
flavor=flavor)
if rv: if rv:
return rv return rv
if sys.platform == "win32": if sys.platform == "win32":
servo_exe_dir = os.path.dirname( servo_exe_dir = os.path.dirname(
self.get_binary_path(release, dev, target=self.cross_compile_target, simpleservo=libsimpleservo) self.get_binary_path(build_type, target=self.cross_compile_target, simpleservo=libsimpleservo)
) )
assert os.path.exists(servo_exe_dir) assert os.path.exists(servo_exe_dir)
@ -224,7 +188,7 @@ class MachCommands(CommandBase):
elif sys.platform == "darwin": elif sys.platform == "darwin":
servo_path = self.get_binary_path( servo_path = self.get_binary_path(
release, dev, target=self.cross_compile_target, simpleservo=libsimpleservo) build_type, target=self.cross_compile_target, simpleservo=libsimpleservo)
servo_bin_dir = os.path.dirname(servo_path) servo_bin_dir = os.path.dirname(servo_path)
assert os.path.exists(servo_bin_dir) assert os.path.exists(servo_bin_dir)

View file

@ -10,6 +10,7 @@
from __future__ import print_function from __future__ import print_function
import contextlib import contextlib
from enum import Enum
from typing import Dict, List, Optional from typing import Dict, List, Optional
import functools import functools
import gzip import gzip
@ -42,10 +43,15 @@ import servo.platform
import servo.util as util import servo.util as util
from servo.util import download_file, get_default_cache_dir from servo.util import download_file, get_default_cache_dir
BIN_SUFFIX = ".exe" if sys.platform == "win32" else ""
NIGHTLY_REPOSITORY_URL = "https://servo-builds2.s3.amazonaws.com/" NIGHTLY_REPOSITORY_URL = "https://servo-builds2.s3.amazonaws.com/"
class BuildType(Enum):
""" The build type of this Servo build. Either `DEV` or `RELEASE`."""
DEV = 1
RELEASE = 2
@contextlib.contextmanager @contextlib.contextmanager
def cd(new_path): def cd(new_path):
"""Context manager for changing the current working directory""" """Context manager for changing the current working directory"""
@ -281,26 +287,22 @@ class CommandBase(object):
def get_top_dir(self): def get_top_dir(self):
return self.context.topdir return self.context.topdir
def get_apk_path(self, release): def get_apk_path(self, build_type: BuildType):
base_path = util.get_target_dir() base_path = util.get_target_dir()
base_path = path.join(base_path, "android", self.config["android"]["target"]) base_path = path.join(base_path, "android", self.config["android"]["target"])
apk_name = "servoapp.apk" apk_name = "servoapp.apk"
build_type = "release" if release else "debug" build_type_string = "release" if build_type == BuildType.RELEASE else "debug"
return path.join(base_path, build_type, apk_name) return path.join(base_path, build_type_string, apk_name)
def get_binary_path(self, release, dev, target=None, android=False, simpleservo=False): def get_binary_path(self, build_type: BuildType, target=None, android=False, simpleservo=False):
# TODO(autrilla): this function could still use work - it shouldn't
# handle quitting, or printing. It should return the path, or an error.
base_path = util.get_target_dir() base_path = util.get_target_dir()
binary_name = "servo" + BIN_SUFFIX
if android: if android:
base_path = path.join(base_path, "android", self.config["android"]["target"]) base_path = path.join(base_path, "android", self.config["android"]["target"])
simpleservo = True simpleservo = True
elif target: elif target:
base_path = path.join(base_path, target) base_path = path.join(base_path, target)
binary_name = f"servo{servo.platform.get().executable_suffix()}"
if simpleservo: if simpleservo:
if sys.platform == "win32": if sys.platform == "win32":
binary_name = "simpleservo.dll" binary_name = "simpleservo.dll"
@ -309,41 +311,12 @@ class CommandBase(object):
else: else:
binary_name = "libsimpleservo.so" binary_name = "libsimpleservo.so"
release_path = path.join(base_path, "release", binary_name) build_type_string = "release" if build_type == BuildType.RELEASE else "debug"
dev_path = path.join(base_path, "debug", binary_name) binary_path = path.join(base_path, build_type_string, binary_name)
# Prefer release if both given if not path.exists(binary_path):
if release and dev: raise BuildNotFound('No Servo binary found. Perhaps you forgot to run `./mach build`?')
dev = False return binary_path
release_exists = path.exists(release_path)
dev_exists = path.exists(dev_path)
if not release_exists and not dev_exists:
raise BuildNotFound('No Servo binary found.'
' Perhaps you forgot to run `./mach build`?')
if release and release_exists:
return release_path
if dev and dev_exists:
return dev_path
if not dev and not release and release_exists and dev_exists:
print("You have multiple profiles built. Please specify which "
"one to run with '--release' or '--dev'.")
sys.exit()
if not dev and not release:
if release_exists:
return release_path
else:
return dev_path
print("The %s profile is not built. Please run './mach build%s' "
"and try again." % ("release" if release else "dev",
" --release" if release else ""))
sys.exit()
def detach_volume(self, mounted_volume): def detach_volume(self, mounted_volume):
print("Detaching volume {}".format(mounted_volume)) print("Detaching volume {}".format(mounted_volume))
@ -739,8 +712,21 @@ class CommandBase(object):
env['PKG_CONFIG_ALLOW_CROSS'] = "1" env['PKG_CONFIG_ALLOW_CROSS'] = "1"
@staticmethod @staticmethod
def build_like_command_arguments(original_function): def common_command_arguments(build_configuration=False, build_type=False):
decorators = [ decorators = []
if build_type:
decorators += [
CommandArgumentGroup('Build Type'),
CommandArgument('--release', '-r', group="Build Type",
action='store_true',
help='Build in release mode'),
CommandArgument('--dev', '--debug', '-d', group="Build Type",
action='store_true',
help='Build in development mode'),
]
if build_configuration:
decorators += [
CommandArgumentGroup('Cross Compilation'), CommandArgumentGroup('Cross Compilation'),
CommandArgument( CommandArgument(
'--target', '-t', '--target', '-t',
@ -793,18 +779,48 @@ class CommandBase(object):
CommandArgument('--without-wgl', group="Feature Selection", default=None, action='store_true'), CommandArgument('--without-wgl', group="Feature Selection", default=None, action='store_true'),
] ]
def decorator_function(original_function):
def configuration_decorator(self, *args, **kwargs): def configuration_decorator(self, *args, **kwargs):
self.configure_cross_compilation(kwargs['target'], kwargs['android'], kwargs['win_arm64']) if build_type:
# If `build_type` already exists in kwargs we are doing a recursive dispatch.
if 'build_type' not in kwargs:
kwargs['build_type'] = self.configure_build_type(kwargs['release'], kwargs['dev'])
kwargs.pop('release', None)
kwargs.pop('dev', None)
if build_configuration:
self.configure_cross_compilation(kwargs['target'], kwargs['android'], kwargs['win_arm64'])
self.features = kwargs.get("features", None) or [] self.features = kwargs.get("features", None) or []
self.configure_media_stack(kwargs['media_stack']) self.configure_media_stack(kwargs['media_stack'])
return original_function(self, *args, **kwargs) return original_function(self, *args, **kwargs)
decorators.reverse() decorators.reverse()
decorated_function = configuration_decorator
for decorator in decorators: for decorator in decorators:
decorated_function = decorator(decorated_function) decorator(configuration_decorator)
return decorated_function
return configuration_decorator
return decorator_function
def configure_build_type(self, release: bool, dev: bool) -> BuildType:
if release and dev:
print("Please specify either --dev (-d) for a development")
print(" build, or --release (-r) for an optimized build.")
sys.exit(1)
if not release and not dev:
if self.config["build"]["mode"] == "dev":
print("No build type specified, but .servobuild specified `--dev`.")
dev = True
elif self.config["build"]["mode"] == "release":
print("No build type specified, but .servobuild specified `--release`.")
release = True
else:
print("No build type specified so assuming `--dev`.")
dev = True
return BuildType.DEV if dev else BuildType.RELEASE
def configure_cross_compilation( def configure_cross_compilation(
self, self,
@ -972,7 +988,7 @@ class CommandBase(object):
def ensure_rustup_version(self): def ensure_rustup_version(self):
try: try:
version_line = subprocess.check_output( version_line = subprocess.check_output(
["rustup" + BIN_SUFFIX, "--version"], ["rustup" + servo.platform.get().executable_suffix(), "--version"],
# Silence "info: This is the version for the rustup toolchain manager, # Silence "info: This is the version for the rustup toolchain manager,
# not the rustc compiler." # not the rustc compiler."
stderr=open(os.devnull, "wb") stderr=open(os.devnull, "wb")

View file

@ -44,7 +44,7 @@ class MachCommands(CommandBase):
@CommandArgument( @CommandArgument(
'params', default=None, nargs='...', 'params', default=None, nargs='...',
help="Command-line arguments to be passed through to cargo check") help="Command-line arguments to be passed through to cargo check")
@CommandBase.build_like_command_arguments @CommandBase.common_command_arguments(build_configuration=True, build_type=False)
def check(self, params, **kwargs): def check(self, params, **kwargs):
if not params: if not params:
params = [] params = []
@ -129,7 +129,7 @@ class MachCommands(CommandBase):
@CommandArgument( @CommandArgument(
'params', default=None, nargs='...', 'params', default=None, nargs='...',
help="Command-line arguments to be passed through to cargo-fix") help="Command-line arguments to be passed through to cargo-fix")
@CommandBase.build_like_command_arguments @CommandBase.common_command_arguments(build_configuration=True, build_type=False)
def cargo_fix(self, params, **kwargs): def cargo_fix(self, params, **kwargs):
if not params: if not params:
params = [] params = []
@ -144,7 +144,7 @@ class MachCommands(CommandBase):
@CommandArgument( @CommandArgument(
'params', default=None, nargs='...', 'params', default=None, nargs='...',
help="Command-line arguments to be passed through to cargo-clippy") help="Command-line arguments to be passed through to cargo-clippy")
@CommandBase.build_like_command_arguments @CommandBase.common_command_arguments(build_configuration=True, build_type=False)
def cargo_clippy(self, params, **kwargs): def cargo_clippy(self, params, **kwargs):
if not params: if not params:
params = [] params = []

View file

@ -29,6 +29,7 @@ from mach.decorators import (
from mach.registrar import Registrar from mach.registrar import Registrar
from servo.command_base import ( from servo.command_base import (
BuildType,
archive_deterministically, archive_deterministically,
BuildNotFound, BuildNotFound,
cd, cd,
@ -112,10 +113,6 @@ class PackageCommands(CommandBase):
@Command('package', @Command('package',
description='Package Servo', description='Package Servo',
category='package') category='package')
@CommandArgument('--release', '-r', action='store_true',
help='Package the release build')
@CommandArgument('--dev', '-d', action='store_true',
help='Package the dev build')
@CommandArgument('--android', @CommandArgument('--android',
default=None, default=None,
action='store_true', action='store_true',
@ -130,8 +127,8 @@ class PackageCommands(CommandBase):
default=None, default=None,
action='store_true', action='store_true',
help='Create a local Maven repository') help='Create a local Maven repository')
def package(self, release=False, dev=False, android=None, target=None, @CommandBase.common_command_arguments(build_configuration=False, build_type=True)
flavor=None, maven=False): def package(self, build_type: BuildType, android=None, target=None, flavor=None, maven=False):
if android is None: if android is None:
android = self.config["build"]["android"] android = self.config["build"]["android"]
if target and android: if target and android:
@ -144,26 +141,21 @@ class PackageCommands(CommandBase):
self.cross_compile_target = target self.cross_compile_target = target
env = self.build_env() env = self.build_env()
binary_path = self.get_binary_path( binary_path = self.get_binary_path(build_type, target=target, android=android)
release, dev, target=target, android=android,
)
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 android: if android:
android_target = self.config["android"]["target"] android_target = self.config["android"]["target"]
if "aarch64" in android_target: if "aarch64" in android_target:
build_type = "Arm64" arch_string = "Arm64"
elif "armv7" in android_target: elif "armv7" in android_target:
build_type = "Armv7" arch_string = "Armv7"
elif "i686" in android_target: elif "i686" in android_target:
build_type = "x86" arch_string = "x86"
else: else:
build_type = "Arm" arch_string = "Arm"
if dev: build_type_string = "Debug" if build_type == BuildType.DEV else "Release"
build_mode = "Debug"
else:
build_mode = "Release"
flavor_name = "Main" flavor_name = "Main"
if flavor is not None: if flavor is not None:
@ -178,7 +170,7 @@ class PackageCommands(CommandBase):
shutil.copytree(path.join(dir_to_root, 'resources'), dir_to_resources) shutil.copytree(path.join(dir_to_root, 'resources'), dir_to_resources)
change_prefs(dir_to_resources, "android", vr=vr) change_prefs(dir_to_resources, "android", vr=vr)
variant = ":assemble" + flavor_name + build_type + build_mode variant = ":assemble" + flavor_name + arch_string + build_type_string
apk_task_name = ":servoapp" + variant apk_task_name = ":servoapp" + variant
aar_task_name = ":servoview" + variant aar_task_name = ":servoview" + variant
maven_task_name = ":servoview:uploadArchive" maven_task_name = ":servoview:uploadArchive"
@ -354,10 +346,6 @@ class PackageCommands(CommandBase):
@Command('install', @Command('install',
description='Install Servo (currently, Android and Windows only)', description='Install Servo (currently, Android and Windows only)',
category='package') category='package')
@CommandArgument('--release', '-r', action='store_true',
help='Install the release build')
@CommandArgument('--dev', '-d', action='store_true',
help='Install the dev build')
@CommandArgument('--android', @CommandArgument('--android',
action='store_true', action='store_true',
help='Install on Android') help='Install on Android')
@ -370,7 +358,8 @@ class PackageCommands(CommandBase):
@CommandArgument('--target', '-t', @CommandArgument('--target', '-t',
default=None, default=None,
help='Install the given target platform') help='Install the given target platform')
def install(self, release=False, dev=False, android=False, emulator=False, usb=False, target=None): @CommandBase.common_command_arguments(build_configuration=False, build_type=True)
def install(self, build_type: BuildType, android=False, emulator=False, usb=False, target=None):
if target and android: if target and android:
print("Please specify either --target or --android.") print("Please specify either --target or --android.")
sys.exit(1) sys.exit(1)
@ -380,21 +369,21 @@ class PackageCommands(CommandBase):
env = self.build_env() env = self.build_env()
try: try:
binary_path = self.get_binary_path(release, dev, android=android) binary_path = self.get_binary_path(build_type, android=android)
except BuildNotFound: except BuildNotFound:
print("Servo build not found. Building servo...") print("Servo build not found. Building servo...")
result = Registrar.dispatch( result = Registrar.dispatch(
"build", context=self.context, release=release, dev=dev, android=android, "build", context=self.context, build_type=build_type, android=android,
) )
if result: if result:
return result return result
try: try:
binary_path = self.get_binary_path(release, dev, android=android) binary_path = self.get_binary_path(build_type, android=android)
except BuildNotFound: except BuildNotFound:
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 android: if android:
pkg_path = self.get_apk_path(release) pkg_path = self.get_apk_path(build_type)
exec_command = [self.android_adb_path(env)] exec_command = [self.android_adb_path(env)]
if emulator and usb: if emulator and usb:
print("Cannot install to both emulator and USB at the same time.") print("Cannot install to both emulator and USB at the same time.")
@ -411,7 +400,7 @@ class PackageCommands(CommandBase):
if not path.exists(pkg_path): if not path.exists(pkg_path):
print("Servo package not found. Packaging servo...") print("Servo package not found. Packaging servo...")
result = Registrar.dispatch( result = Registrar.dispatch(
"package", context=self.context, release=release, dev=dev, android=android, "package", context=self.context, build_type=build_type, android=android,
) )
if result != 0: if result != 0:
return result return result

View file

@ -16,7 +16,7 @@ import subprocess
from shutil import copytree, rmtree, copy2 from shutil import copytree, rmtree, copy2
from typing import List from typing import List
import servo.util import mozdebug
from mach.decorators import ( from mach.decorators import (
CommandArgument, CommandArgument,
@ -24,9 +24,13 @@ from mach.decorators import (
Command, Command,
) )
import servo.util
import servo.platform
from servo.command_base import ( from servo.command_base import (
BuildType,
CommandBase, CommandBase,
check_call, check_output, BIN_SUFFIX, check_call, check_output,
is_linux, is_linux,
) )
@ -50,10 +54,6 @@ class PostBuildCommands(CommandBase):
@Command('run', @Command('run',
description='Run Servo', description='Run Servo',
category='post-build') category='post-build')
@CommandArgument('--release', '-r', action='store_true',
help='Run the release build')
@CommandArgument('--dev', '-d', action='store_true',
help='Run the dev build')
@CommandArgument('--android', action='store_true', default=None, @CommandArgument('--android', action='store_true', default=None,
help='Run on an Android device through `adb shell`') help='Run on an Android device through `adb shell`')
@CommandArgument('--emulator', @CommandArgument('--emulator',
@ -62,12 +62,12 @@ class PostBuildCommands(CommandBase):
@CommandArgument('--usb', @CommandArgument('--usb',
action='store_true', action='store_true',
help='For Android, run in the only USB device') help='For Android, run in the only USB device')
@CommandArgument('--debug', action='store_true', @CommandArgument('--debugger', action='store_true',
help='Enable the debugger. Not specifying a ' help='Enable the debugger. Not specifying a '
'--debugger option will result in the default ' '--debugger-cmd option will result in the default '
'debugger being used. The following arguments ' 'debugger being used. The following arguments '
'have no effect without this.') 'have no effect without this.')
@CommandArgument('--debugger', default=None, type=str, @CommandArgument('--debugger-cmd', default=None, type=str,
help='Name of debugger to use.') help='Name of debugger to use.')
@CommandArgument('--headless', '-z', action='store_true', @CommandArgument('--headless', '-z', action='store_true',
help='Launch in headless mode') help='Launch in headless mode')
@ -80,7 +80,8 @@ class PostBuildCommands(CommandBase):
@CommandArgument( @CommandArgument(
'params', nargs='...', 'params', nargs='...',
help="Command-line arguments to be passed through to Servo") help="Command-line arguments to be passed through to Servo")
def run(self, params, release=False, dev=False, android=None, debug=False, debugger=None, @CommandBase.common_command_arguments(build_configuration=False, build_type=True)
def run(self, params, build_type: BuildType, android=None, debugger=False, debugger_cmd=None,
headless=False, software=False, bin=None, emulator=False, usb=False, nightly=None): headless=False, software=False, bin=None, emulator=False, usb=False, nightly=None):
env = self.build_env() env = self.build_env()
env["RUST_BACKTRACE"] = "1" env["RUST_BACKTRACE"] = "1"
@ -92,15 +93,15 @@ class PostBuildCommands(CommandBase):
env['LIBGL_ALWAYS_SOFTWARE'] = "1" env['LIBGL_ALWAYS_SOFTWARE'] = "1"
os.environ.update(env) os.environ.update(env)
# Make --debugger imply --debug # Make --debugger-cmd imply --debugger
if debugger: if debugger_cmd:
debug = True debugger = True
if android is None: if android is None:
android = self.config["build"]["android"] android = self.config["build"]["android"]
if android: if android:
if debug: 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")
return return
@ -133,55 +134,54 @@ class PostBuildCommands(CommandBase):
shell.communicate("\n".join(script) + "\n") shell.communicate("\n".join(script) + "\n")
return shell.wait() return shell.wait()
args = [bin or self.get_nightly_binary_path(nightly) or self.get_binary_path(release, dev)] args = [bin or self.get_nightly_binary_path(nightly) or self.get_binary_path(build_type)]
if headless: if headless:
args.append('-z') args.append('-z')
# Borrowed and modified from: # Borrowed and modified from:
# http://hg.mozilla.org/mozilla-central/file/c9cfa9b91dea/python/mozbuild/mozbuild/mach_commands.py#l883 # http://hg.mozilla.org/mozilla-central/file/c9cfa9b91dea/python/mozbuild/mozbuild/mach_commands.py#l883
if debug: if debugger:
import mozdebug if not debugger_cmd:
if not debugger:
# No debugger name was provided. Look for the default ones on # No debugger name was provided. Look for the default ones on
# current OS. # current OS.
debugger = mozdebug.get_default_debugger_name( debugger_cmd = mozdebug.get_default_debugger_name(
mozdebug.DebuggerSearch.KeepLooking) mozdebug.DebuggerSearch.KeepLooking)
self.debuggerInfo = mozdebug.get_debugger_info(debugger) debugger_info = mozdebug.get_debugger_info(debugger_cmd)
if not self.debuggerInfo: if not debugger_info:
print("Could not find a suitable debugger in your PATH.") print("Could not find a suitable debugger in your PATH.")
return 1 return 1
command = self.debuggerInfo.path command = debugger_info.path
if debugger == 'gdb' or debugger == 'lldb': if debugger_cmd == 'gdb' or debugger_cmd == 'lldb':
rustCommand = 'rust-' + debugger rust_command = 'rust-' + debugger_cmd
try: try:
subprocess.check_call([rustCommand, '--version'], env=env, stdout=open(os.devnull, 'w')) subprocess.check_call([rust_command, '--version'], env=env, stdout=open(os.devnull, 'w'))
except (OSError, subprocess.CalledProcessError): except (OSError, subprocess.CalledProcessError):
pass pass
else: else:
command = rustCommand command = rust_command
# Prepend the debugger args. # Prepend the debugger args.
args = ([command] + self.debuggerInfo.args + args + params) args = ([command] + debugger_info.args + args + params)
else: else:
args = args + params args = args + params
try: try:
check_call(args, env=env) check_call(args, env=env)
except subprocess.CalledProcessError as e: except subprocess.CalledProcessError as exception:
if e.returncode < 0: if exception.returncode < 0:
print(f"Servo was terminated by signal {-e.returncode}") print(f"Servo was terminated by signal {-exception.returncode}")
else: else:
print(f"Servo exited with non-zero status {e.returncode}") print(f"Servo exited with non-zero status {exception.returncode}")
return e.returncode return exception.returncode
except OSError as e: except OSError as exception:
if e.errno == 2: if exception.errno == 2:
print("Servo Binary can't be found! Run './mach build'" print("Servo Binary can't be found! Run './mach build'"
" and try again!") " and try again!")
else: else:
raise e raise exception
@Command('android-emulator', @Command('android-emulator',
description='Run the Android emulator', description='Run the Android emulator',
@ -198,10 +198,6 @@ class PostBuildCommands(CommandBase):
@Command('rr-record', @Command('rr-record',
description='Run Servo whilst recording execution with rr', description='Run Servo whilst recording execution with rr',
category='post-build') category='post-build')
@CommandArgument('--release', '-r', action='store_true',
help='Use release build')
@CommandArgument('--dev', '-d', action='store_true',
help='Use dev build')
@CommandArgument('--bin', default=None, @CommandArgument('--bin', default=None,
help='Launch with specific binary') help='Launch with specific binary')
@CommandArgument('--nightly', '-n', default=None, @CommandArgument('--nightly', '-n', default=None,
@ -209,12 +205,13 @@ class PostBuildCommands(CommandBase):
@CommandArgument( @CommandArgument(
'params', nargs='...', 'params', nargs='...',
help="Command-line arguments to be passed through to Servo") help="Command-line arguments to be passed through to Servo")
def rr_record(self, release=False, dev=False, bin=None, nightly=None, params=[]): @CommandBase.common_command_arguments(build_configuration=False, build_type=True)
def rr_record(self, build_type: BuildType, bin=None, nightly=None, params=[]):
env = self.build_env() env = self.build_env()
env["RUST_BACKTRACE"] = "1" env["RUST_BACKTRACE"] = "1"
servo_cmd = [bin or self.get_nightly_binary_path(nightly) servo_cmd = [bin or self.get_nightly_binary_path(nightly)
or self.get_binary_path(release, dev)] + params or self.get_binary_path(build_type)] + params
rr_cmd = ['rr', '--fatal-errors', 'record'] rr_cmd = ['rr', '--fatal-errors', 'record']
try: try:
check_call(rr_cmd + servo_cmd) check_call(rr_cmd + servo_cmd)
@ -242,11 +239,11 @@ class PostBuildCommands(CommandBase):
@CommandArgument( @CommandArgument(
'params', nargs='...', 'params', nargs='...',
help="Command-line arguments to be passed through to cargo doc") help="Command-line arguments to be passed through to cargo doc")
@CommandBase.build_like_command_arguments @CommandBase.common_command_arguments(build_configuration=True, build_type=False)
def doc(self, params: List[str], **kwargs): def doc(self, params: List[str], **kwargs):
self.ensure_bootstrapped() self.ensure_bootstrapped()
rustc_path = check_output( rustc_path = check_output(
["rustup" + BIN_SUFFIX, "which", "rustc"], [f"rustup{servo.platform.get().executable_suffix()}", "which", "rustc"],
cwd=self.context.topdir).decode("utf-8") cwd=self.context.topdir).decode("utf-8")
assert path.basename(path.dirname(rustc_path)) == "bin" assert path.basename(path.dirname(rustc_path)) == "bin"
toolchain_path = path.dirname(path.dirname(rustc_path)) toolchain_path = path.dirname(path.dirname(rustc_path))

View file

@ -33,10 +33,9 @@ from mach.decorators import (
Command, Command,
) )
import servo.util
import tidy import tidy
from servo.command_base import CommandBase, call, check_call from servo.command_base import BuildType, CommandBase, call, check_call
from servo.util import delete from servo.util import delete
from distutils.dir_util import copy_tree from distutils.dir_util import copy_tree
@ -166,7 +165,7 @@ class MachCommands(CommandBase):
help="Run in bench mode") help="Run in bench mode")
@CommandArgument('--nocapture', default=False, action="store_true", @CommandArgument('--nocapture', default=False, action="store_true",
help="Run tests with nocapture ( show test stdout )") help="Run tests with nocapture ( show test stdout )")
@CommandBase.build_like_command_arguments @CommandBase.common_command_arguments(build_configuration=True, build_type=False)
def test_unit(self, test_name=None, package=None, bench=False, nocapture=False, **kwargs): def test_unit(self, test_name=None, package=None, bench=False, nocapture=False, **kwargs):
if test_name is None: if test_name is None:
test_name = [] test_name = []
@ -242,13 +241,6 @@ class MachCommands(CommandBase):
# We are setting is_build here to true, because running `cargo test` can trigger builds. # We are setting is_build here to true, because running `cargo test` can trigger builds.
env = self.build_env(is_build=True) env = self.build_env(is_build=True)
# on MSVC, we need some DLLs in the path. They were copied
# in to the servo.exe build dir, so just point PATH to that.
# TODO(mrobinson): This should be removed entirely.
if "msvc" in servo.platform.host_triple():
servo.util.prepend_paths_to_env(
env, "PATH", path.dirname(self.get_binary_path(False, False)))
return self.run_cargo_build_like_command( return self.run_cargo_build_like_command(
"bench" if bench else "test", "bench" if bench else "test",
args, args,
@ -332,57 +324,42 @@ class MachCommands(CommandBase):
description='Run the tests harness that verifies that the test failures are reported correctly', description='Run the tests harness that verifies that the test failures are reported correctly',
category='testing', category='testing',
parser=wpt.create_parser) parser=wpt.create_parser)
def test_wpt_failure(self, **kwargs): @CommandBase.common_command_arguments(build_configuration=False, build_type=True)
def test_wpt_failure(self, build_type: BuildType, **kwargs):
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(**kwargs) return not self._test_wpt(build_type=build_type, **kwargs)
@Command('test-wpt', @Command('test-wpt',
description='Run the regular web platform test suite', description='Run the regular web platform test suite',
category='testing', category='testing',
parser=wpt.create_parser) parser=wpt.create_parser)
def test_wpt(self, **kwargs): @CommandBase.common_command_arguments(build_configuration=False, build_type=True)
ret = self.run_test_list_or_dispatch(kwargs["test_list"], "wpt", self._test_wpt, **kwargs) def test_wpt(self, build_type: BuildType, **kwargs):
if kwargs["always_succeed"]: return self._test_wpt(build_type=build_type, **kwargs)
return 0
else:
return ret
@Command('test-wpt-android', @Command('test-wpt-android',
description='Run the web platform test suite in an Android emulator', description='Run the web platform test suite in an Android emulator',
category='testing', category='testing',
parser=wpt.create_parser) parser=wpt.create_parser)
def test_wpt_android(self, release=False, dev=False, binary_args=None, **kwargs): @CommandBase.common_command_arguments(build_configuration=False, build_type=True)
def test_wpt_android(self, build_type: BuildType, binary_args=None, **kwargs):
kwargs.update( kwargs.update(
release=release,
dev=dev,
product="servodriver", product="servodriver",
processes=1, processes=1,
binary_args=self.in_android_emulator(release, dev) + (binary_args or []), binary_args=self.in_android_emulator(build_type) + (binary_args or []),
binary=sys.executable, binary=sys.executable,
) )
return self._test_wpt(android=True, **kwargs) return self._test_wpt(build_type=build_type, android=True, **kwargs)
def _test_wpt(self, android=False, **kwargs): def _test_wpt(self, build_type: BuildType, android=False, **kwargs):
if not android: if not android:
os.environ.update(self.build_env()) os.environ.update(self.build_env())
return wpt.run.run_tests(**kwargs)
# Helper to ensure all specified paths are handled, otherwise dispatch to appropriate test suite. # TODO(mrobinson): Why do we pass the wrong binary path in when running WPT on Android?
def run_test_list_or_dispatch(self, requested_paths, correct_suite, correct_function, **kwargs): binary_path = self.get_binary_path(build_type=build_type)
if not requested_paths: return_value = wpt.run.run_tests(binary_path, **kwargs)
return correct_function(**kwargs) return return_value if not kwargs["always_succeed"] else 0
# Paths specified on command line. Ensure they can be handled, re-dispatch otherwise.
all_handled = True
for test_path in requested_paths:
suite = self.suite_for_path(test_path)
if suite is not None and correct_suite != suite:
all_handled = False
print("Warning: %s is not a %s test. Delegating to test-%s." % (test_path, correct_suite, suite))
if all_handled:
return correct_function(**kwargs)
# Dispatch each test to the correct suite via test()
Registrar.dispatch("test", context=self.context, params=requested_paths)
@Command('update-manifest', @Command('update-manifest',
description='Run test-wpt --manifest-update SKIP_TESTS to regenerate MANIFEST.json', description='Run test-wpt --manifest-update SKIP_TESTS to regenerate MANIFEST.json',
@ -411,18 +388,15 @@ class MachCommands(CommandBase):
@Command('test-android-startup', @Command('test-android-startup',
description='Extremely minimal testing of Servo for Android', description='Extremely minimal testing of Servo for Android',
category='testing') category='testing')
@CommandArgument('--release', '-r', action='store_true', @CommandBase.common_command_arguments(build_configuration=False, build_type=True)
help='Run the release build') def test_android_startup(self, build_type: BuildType):
@CommandArgument('--dev', '-d', action='store_true',
help='Run the dev build')
def test_android_startup(self, release, dev):
html = """ html = """
<script> <script>
window.alert("JavaScript is running!") window.alert("JavaScript is running!")
</script> </script>
""" """
url = "data:text/html;base64," + html.encode("base64").replace("\n", "") url = "data:text/html;base64," + html.encode("base64").replace("\n", "")
args = self.in_android_emulator(release, dev) args = self.in_android_emulator(build_type)
args = [sys.executable] + args + [url] args = [sys.executable] + args + [url]
process = subprocess.Popen(args, stdout=subprocess.PIPE) process = subprocess.Popen(args, stdout=subprocess.PIPE)
try: try:
@ -437,11 +411,7 @@ class MachCommands(CommandBase):
finally: finally:
process.terminate() process.terminate()
def in_android_emulator(self, release, dev): def in_android_emulator(self, build_type: BuildType):
if (release and dev) or not (release or dev):
print("Please specify one of --dev or --release.")
sys.exit(1)
avd = "servo-x86" avd = "servo-x86"
target = "i686-linux-android" target = "i686-linux-android"
print("Assuming --target " + target) print("Assuming --target " + target)
@ -450,42 +420,28 @@ class MachCommands(CommandBase):
env = self.build_env() env = self.build_env()
os.environ["PATH"] = env["PATH"] os.environ["PATH"] = env["PATH"]
assert self.setup_configuration_for_android_target(target) assert self.setup_configuration_for_android_target(target)
apk = self.get_apk_path(release) apk = self.get_apk_path(build_type)
py = path.join(self.context.topdir, "etc", "run_in_headless_android_emulator.py") py = path.join(self.context.topdir, "etc", "run_in_headless_android_emulator.py")
return [py, avd, apk] return [py, avd, apk]
@Command('test-jquery', @Command('test-jquery', description='Run the jQuery test suite', category='testing')
description='Run the jQuery test suite', @CommandBase.common_command_arguments(build_configuration=False, build_type=True)
category='testing') def test_jquery(self, build_type: BuildType):
@CommandArgument('--release', '-r', action='store_true', return self.jquery_test_runner("test", build_type)
help='Run the release build')
@CommandArgument('--dev', '-d', action='store_true',
help='Run the dev build')
def test_jquery(self, release, dev):
return self.jquery_test_runner("test", release, dev)
@Command('test-dromaeo', @Command('test-dromaeo', description='Run the Dromaeo test suite', category='testing')
description='Run the Dromaeo test suite', @CommandArgument('tests', default=["recommended"], nargs="...", help="Specific tests to run")
category='testing') @CommandBase.common_command_arguments(build_configuration=False, build_type=True)
@CommandArgument('tests', default=["recommended"], nargs="...", def test_dromaeo(self, tests, build_type: BuildType):
help="Specific tests to run") return self.dromaeo_test_runner(tests, build_type)
@CommandArgument('--release', '-r', action='store_true',
help='Run the release build')
@CommandArgument('--dev', '-d', action='store_true',
help='Run the dev build')
def test_dromaeo(self, tests, release, dev):
return self.dromaeo_test_runner(tests, release, dev)
@Command('update-jquery', @Command('update-jquery',
description='Update the jQuery test suite expected results', description='Update the jQuery test suite expected results',
category='testing') category='testing')
@CommandArgument('--release', '-r', action='store_true', @CommandBase.common_command_arguments(build_configuration=False, build_type=True)
help='Run the release build') def update_jquery(self, build_type: BuildType):
@CommandArgument('--dev', '-d', action='store_true', return self.jquery_test_runner("update", build_type)
help='Run the dev build')
def update_jquery(self, release, dev):
return self.jquery_test_runner("update", release, dev)
@Command('compare_dromaeo', @Command('compare_dromaeo',
description='Compare outputs of two runs of ./mach test-dromaeo command', description='Compare outputs of two runs of ./mach test-dromaeo command',
@ -543,7 +499,7 @@ class MachCommands(CommandBase):
print("{}|{}|{}|{}".format(a1.ljust(width_col1), str(b1).ljust(width_col2), print("{}|{}|{}|{}".format(a1.ljust(width_col1), str(b1).ljust(width_col2),
str(c1).ljust(width_col3), str(d1).ljust(width_col4))) str(c1).ljust(width_col3), str(d1).ljust(width_col4)))
def jquery_test_runner(self, cmd, release, dev): def jquery_test_runner(self, cmd, build_type: BuildType):
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")
@ -558,11 +514,11 @@ class MachCommands(CommandBase):
["git", "-C", jquery_dir, "pull"]) ["git", "-C", jquery_dir, "pull"])
# Check that a release servo build exists # Check that a release servo build exists
bin_path = path.abspath(self.get_binary_path(release, dev)) bin_path = path.abspath(self.get_binary_path(build_type))
return call([run_file, cmd, bin_path, base_dir]) return call([run_file, cmd, bin_path, base_dir])
def dromaeo_test_runner(self, tests, release, dev): def dromaeo_test_runner(self, tests, build_type: BuildType):
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")
@ -581,7 +537,7 @@ class MachCommands(CommandBase):
["make", "-C", dromaeo_dir, "web"]) ["make", "-C", dromaeo_dir, "web"])
# Check that a release servo build exists # Check that a release servo build exists
bin_path = path.abspath(self.get_binary_path(release, dev)) bin_path = path.abspath(self.get_binary_path(build_type))
return check_call( return check_call(
[run_file, "|".join(tests), bin_path, base_dir]) [run_file, "|".join(tests), bin_path, base_dir])
@ -832,10 +788,10 @@ tests/wpt/mozilla/tests for Servo-only tests""" % reference_path)
category='testing') category='testing')
@CommandArgument('params', nargs='...', @CommandArgument('params', nargs='...',
help="Command-line arguments to be passed through to Servo") help="Command-line arguments to be passed through to Servo")
def smoketest(self, params): @CommandBase.common_command_arguments(build_configuration=False, build_type=True)
def smoketest(self, build_type: BuildType, params):
# 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.
params = params + ['-f', 'tests/html/close-on-load.html'] return self.context.commands.dispatch('run', self.context, build_type=build_type,
return self.context.commands.dispatch( params=params + ['-f', 'tests/html/close-on-load.html'])
'run', self.context, params=params)

View file

@ -27,8 +27,6 @@ import wptrunner.wptcommandline # noqa: E402
def create_parser(): def create_parser():
parser = wptrunner.wptcommandline.create_parser() parser = wptrunner.wptcommandline.create_parser()
parser.add_argument('--release', default=False, action="store_true",
help="Run with a release build of servo")
parser.add_argument('--rr-chaos', default=False, action="store_true", parser.add_argument('--rr-chaos', default=False, action="store_true",
help="Run under chaos mode in rr until a failure is captured") help="Run under chaos mode in rr until a failure is captured")
parser.add_argument('--pref', default=[], action="append", dest="prefs", parser.add_argument('--pref', default=[], action="append", dest="prefs",

View file

@ -33,24 +33,13 @@ TRACKER_API_ENV_VAR = "INTERMITTENT_TRACKER_API"
TRACKER_DASHBOARD_SECRET_ENV_VAR = "INTERMITTENT_TRACKER_DASHBOARD_SECRET" TRACKER_DASHBOARD_SECRET_ENV_VAR = "INTERMITTENT_TRACKER_DASHBOARD_SECRET"
def determine_build_type(kwargs: dict, target_dir: str):
if kwargs["release"]:
return "release"
elif kwargs["debug"]:
return "debug"
elif os.path.exists(os.path.join(target_dir, "debug")):
return "debug"
elif os.path.exists(os.path.join(target_dir, "release")):
return "release"
return "debug"
def set_if_none(args: dict, key: str, value): def set_if_none(args: dict, key: str, value):
if key not in args or args[key] is None: if key not in args or args[key] is None:
args[key] = value args[key] = value
def run_tests(**kwargs): def run_tests(default_binary_path: str, **kwargs):
print(default_binary_path)
# By default, Rayon selects the number of worker threads based on the # By default, Rayon selects the number of worker threads based on the
# available CPU count. This doesn't work very well when running tests on CI, # available CPU count. This doesn't work very well when running tests on CI,
# since we run so many Servo processes in parallel. The result is a lot of # since we run so many Servo processes in parallel. The result is a lot of
@ -79,17 +68,6 @@ def run_tests(**kwargs):
kwargs["user_stylesheets"].append(os.path.join(SERVO_ROOT, "resources", "ahem.css")) kwargs["user_stylesheets"].append(os.path.join(SERVO_ROOT, "resources", "ahem.css"))
if "CARGO_TARGET_DIR" in os.environ:
target_dir = os.path.join(os.environ["CARGO_TARGET_DIR"])
else:
target_dir = os.path.join(SERVO_ROOT, "target")
binary_name = ("servo.exe" if sys.platform == "win32" else "servo")
default_binary_path = os.path.join(
target_dir, determine_build_type(kwargs, target_dir), binary_name
)
set_if_none(kwargs, "binary", default_binary_path) set_if_none(kwargs, "binary", default_binary_path)
set_if_none(kwargs, "webdriver_binary", default_binary_path) set_if_none(kwargs, "webdriver_binary", default_binary_path)