Backed out changeset 53b1fa0faa6d (bug 1696251) for breaking the static-analysis integration. a=backout

This commit is contained in:
Butkovits Atila
2021-09-23 13:06:40 +03:00
parent 97525089a1
commit 776d615efa
67 changed files with 13701 additions and 13303 deletions

View File

@@ -12,8 +12,10 @@ import os
from mach.decorators import (
Command,
CommandArgument,
CommandProvider,
)
from mozbuild.base import (
MachCommandBase,
MachCommandConditions as conditions,
BinaryNotFoundException,
)
@@ -25,6 +27,8 @@ def is_valgrind_build(cls):
return "MOZ_VALGRIND" in defines and "MOZ_MEMORY" not in defines
@CommandProvider
class MachCommands(MachCommandBase):
@Command(
"valgrind-test",
category="testing",
@@ -40,7 +44,7 @@ def is_valgrind_build(cls):
"--suppression multiple times to specify multiple suppression "
"files.",
)
def valgrind_test(command_context, suppressions):
def valgrind_test(self, command_context, suppressions):
"""
Run Valgrind tests.
"""

View File

@@ -19,9 +19,11 @@ import subprocess
from mozbuild import shellutil
from mozbuild.base import (
MozbuildObject,
MachCommandBase,
BinaryNotFoundException,
)
from mach.decorators import (
CommandProvider,
Command,
)
@@ -36,20 +38,22 @@ def stringify(obj):
return json.dumps(obj, sort_keys=True, indent=2, separators=(",", ": "))
@CommandProvider
class MachCommands(MachCommandBase):
@Command(
"devtools-css-db",
category="post-build",
description="Rebuild the devtool's static css properties database.",
)
def generate_css_db(command_context):
def generate_css_db(self, command_context):
"""Generate the static css properties database for devtools and write it to file."""
print("Re-generating the css properties database...")
db = get_properties_db_from_xpcshell(command_context)
db = self.get_properties_db_from_xpcshell(command_context)
if not db:
return 1
output_template(
self.output_template(
command_context,
{
"preferences": stringify(db["preferences"]),
@@ -58,8 +62,7 @@ def generate_css_db(command_context):
},
)
def get_properties_db_from_xpcshell(command_context):
def get_properties_db_from_xpcshell(self, command_context):
"""Generate the static css properties db for devtools from an xpcshell script."""
build = MozbuildObject.from_environment()
@@ -99,8 +102,7 @@ def get_properties_db_from_xpcshell(command_context):
return json.loads(contents)
def output_template(command_context, substitutions):
def output_template(self, command_context, substitutions):
"""Output a the properties-db.js from a template."""
js_template_path = resolve_path(
command_context.topsrcdir,

View File

@@ -9,9 +9,11 @@ import sys
from mach.decorators import (
CommandArgument,
CommandProvider,
Command,
)
from mozbuild.base import MachCommandBase
from mozbuild.util import mkdir
@@ -21,6 +23,8 @@ def get_test_parser():
return runtests.get_parser
@CommandProvider
class WebIDLProvider(MachCommandBase):
@Command(
"webidl-example",
category="misc",
@@ -29,22 +33,23 @@ def get_test_parser():
@CommandArgument(
"interface", nargs="+", help="Interface(s) whose examples to generate."
)
def webidl_example(command_context, interface):
def webidl_example(self, command_context, interface):
from mozwebidlcodegen import BuildSystemWebIDL
manager = command_context._spawn(BuildSystemWebIDL).manager
for i in interface:
manager.generate_example_files(i)
@Command(
"webidl-parser-test",
category="testing",
parser=get_test_parser,
description="Run WebIDL tests (Interface Browser parser).",
)
def webidl_test(command_context, **kwargs):
sys.path.insert(0, os.path.join(command_context.topsrcdir, "other-licenses", "ply"))
def webidl_test(self, command_context, **kwargs):
sys.path.insert(
0, os.path.join(command_context.topsrcdir, "other-licenses", "ply")
)
# Ensure the topobjdir exists. On a Taskcluster test run there won't be
# an objdir yet.

View File

@@ -16,12 +16,14 @@ import textwrap
from mach.base import FailedCommandError, MachError
from mach.decorators import (
CommandArgument,
CommandProvider,
Command,
SubCommand,
)
from mach.registrar import Registrar
from mozbuild.mozconfig import MozconfigLoader
from mozbuild.base import MachCommandBase
# Command files like this are listed in build/mach_initialize.py in alphabetical
# order, but we need to access commands earlier in the sorted order to grab
@@ -55,11 +57,12 @@ def inherit_command_args(command, subcommand=None):
return inherited
def state_dir():
@CommandProvider
class MachCommands(MachCommandBase):
def state_dir(self):
return os.environ.get("MOZBUILD_STATE_PATH", os.path.expanduser("~/.mozbuild"))
def tools_dir():
def tools_dir(self):
if os.environ.get("MOZ_FETCHES_DIR"):
# In automation, tools are provided by toolchain dependencies.
return os.path.join(os.environ["HOME"], os.environ["MOZ_FETCHES_DIR"])
@@ -68,77 +71,70 @@ def tools_dir():
# to avoid colliding with the "main" compiler versions, which can
# change separately (and the precompiled sixgill and compiler version
# must match exactly).
return os.path.join(state_dir(), "hazard-tools")
return os.path.join(self.state_dir(), "hazard-tools")
def sixgill_dir(self):
return os.path.join(self.tools_dir(), "sixgill")
def sixgill_dir():
return os.path.join(tools_dir(), "sixgill")
def gcc_dir(self):
return os.path.join(self.tools_dir(), "gcc")
def gcc_dir():
return os.path.join(tools_dir(), "gcc")
def script_dir(command_context):
def script_dir(self, command_context):
return os.path.join(command_context.topsrcdir, "js/src/devtools/rootAnalysis")
def get_work_dir(command_context, application, given):
def get_work_dir(self, command_context, application, given):
if given is not None:
return given
return os.path.join(command_context.topsrcdir, "haz-" + application)
def ensure_dir_exists(dir):
def ensure_dir_exists(self, dir):
os.makedirs(dir, exist_ok=True)
return dir
# Force the use of hazard-compatible installs of tools.
def setup_env_for_tools(env):
gccbin = os.path.join(gcc_dir(), "bin")
def setup_env_for_tools(self, env):
gccbin = os.path.join(self.gcc_dir(), "bin")
env["CC"] = os.path.join(gccbin, "gcc")
env["CXX"] = os.path.join(gccbin, "g++")
env["PATH"] = "{sixgill_dir}/usr/bin:{gccbin}:{PATH}".format(
sixgill_dir=sixgill_dir(), gccbin=gccbin, PATH=env["PATH"]
sixgill_dir=self.sixgill_dir(), gccbin=gccbin, PATH=env["PATH"]
)
def setup_env_for_shell(env, shell):
def setup_env_for_shell(self, env, shell):
"""Add JS shell directory to dynamic lib search path"""
for var in ("LD_LIBRARY_PATH", "DYLD_LIBRARY_PATH"):
env[var] = ":".join(p for p in (env.get(var), os.path.dirname(shell)) if p)
@Command(
"hazards",
category="build",
order="declaration",
description="Commands for running the static analysis for GC rooting hazards",
)
def hazards(command_context):
def hazards(self, command_context):
"""Commands related to performing the GC rooting hazard analysis"""
print("See `mach hazards --help` for a list of subcommands")
@inherit_command_args("artifact", "toolchain")
@SubCommand(
"hazards",
"bootstrap",
description="Install prerequisites for the hazard analysis",
)
def bootstrap(command_context, **kwargs):
def bootstrap(self, command_context, **kwargs):
orig_dir = os.getcwd()
os.chdir(ensure_dir_exists(tools_dir()))
os.chdir(self.ensure_dir_exists(self.tools_dir()))
try:
kwargs["from_build"] = ("linux64-gcc-sixgill", "linux64-gcc-9")
command_context._mach_context.commands.dispatch(
"artifact", command_context._mach_context, subcommand="toolchain", **kwargs
"artifact",
command_context._mach_context,
subcommand="toolchain",
**kwargs
)
finally:
os.chdir(orig_dir)
@inherit_command_args("build")
@SubCommand(
"hazards", "build-shell", description="Build a shell for the hazard analysis"
@@ -149,7 +145,7 @@ def bootstrap(command_context, **kwargs):
metavar="FILENAME",
help="Build with the given mozconfig.",
)
def build_shell(command_context, **kwargs):
def build_shell(self, command_context, **kwargs):
"""Build a JS shell to use to run the rooting hazard analysis."""
# The JS shell requires some specific configuration settings to execute
# the hazard analysis code, and configuration is done via mozconfig.
@@ -177,7 +173,7 @@ def build_shell(command_context, **kwargs):
# Transmit the mozconfig location to build subprocesses.
os.environ["MOZCONFIG"] = mozconfig_path
setup_env_for_tools(os.environ)
self.setup_env_for_tools(os.environ)
# Set a default objdir for the shell, for developer builds.
os.environ.setdefault(
@@ -188,18 +184,16 @@ def build_shell(command_context, **kwargs):
"build", command_context._mach_context, **kwargs
)
def read_json_file(filename):
def read_json_file(self, filename):
with open(filename) as fh:
return json.load(fh)
def ensure_shell(command_context, objdir):
def ensure_shell(self, command_context, objdir):
if objdir is None:
objdir = os.path.join(command_context.topsrcdir, "obj-haz-shell")
try:
binaries = read_json_file(os.path.join(objdir, "binaries.json"))
binaries = self.read_json_file(os.path.join(objdir, "binaries.json"))
info = [b for b in binaries["programs"] if b["program"] == "js"][0]
return os.path.join(objdir, info["install_target"], "js")
except (OSError, KeyError):
@@ -209,7 +203,6 @@ no shell found in %s -- must build the JS shell with `mach hazards build-shell`
% objdir
)
@inherit_command_args("build")
@SubCommand(
"hazards",
@@ -225,17 +218,19 @@ no shell found in %s -- must build the JS shell with `mach hazards build-shell`
@CommandArgument(
"--work-dir", default=None, help="Directory for output and working files."
)
def gather_hazard_data(command_context, **kwargs):
def gather_hazard_data(self, command_context, **kwargs):
"""Gather analysis information by compiling the tree"""
application = kwargs["application"]
objdir = kwargs["haz_objdir"]
if objdir is None:
objdir = os.environ.get("HAZ_OBJDIR")
if objdir is None:
objdir = os.path.join(command_context.topsrcdir, "obj-analyzed-" + application)
objdir = os.path.join(
command_context.topsrcdir, "obj-analyzed-" + application
)
work_dir = get_work_dir(command_context, application, kwargs["work_dir"])
ensure_dir_exists(work_dir)
work_dir = self.get_work_dir(command_context, application, kwargs["work_dir"])
self.ensure_dir_exists(work_dir)
with open(os.path.join(work_dir, "defaults.py"), "wt") as fh:
data = textwrap.dedent(
"""\
@@ -247,11 +242,11 @@ def gather_hazard_data(command_context, **kwargs):
gcc_bin = "{gcc_dir}/bin"
"""
).format(
script_dir=script_dir(command_context),
script_dir=self.script_dir(command_context),
objdir=objdir,
srcdir=command_context.topsrcdir,
sixgill_dir=sixgill_dir(),
gcc_dir=gcc_dir(),
sixgill_dir=self.sixgill_dir(),
gcc_dir=self.gcc_dir(),
)
fh.write(data)
@@ -265,7 +260,7 @@ def gather_hazard_data(command_context, **kwargs):
)
args = [
sys.executable,
os.path.join(script_dir(command_context), "analyze.py"),
os.path.join(self.script_dir(command_context), "analyze.py"),
"dbs",
"--upto",
"dbs",
@@ -275,7 +270,6 @@ def gather_hazard_data(command_context, **kwargs):
return command_context.run_process(args=args, cwd=work_dir, pass_thru=True)
@inherit_command_args("build")
@SubCommand("hazards", "compile", description=argparse.SUPPRESS)
@CommandArgument(
@@ -292,7 +286,7 @@ def gather_hazard_data(command_context, **kwargs):
default=os.environ.get("HAZ_OBJDIR"),
help="Write object files to this directory.",
)
def inner_compile(command_context, **kwargs):
def inner_compile(self, command_context, **kwargs):
"""Build a source tree and gather analysis information while running
under the influence of the analysis collection server."""
@@ -329,12 +323,12 @@ def inner_compile(command_context, **kwargs):
env["MOZCONFIG"] = os.path.join(command_context.topsrcdir, mozconfig_path)
# hazard mozconfigs need to find binaries in .mozbuild
env["MOZBUILD_STATE_PATH"] = state_dir()
env["MOZBUILD_STATE_PATH"] = self.state_dir()
# Suppress the gathering of sources, to save disk space and memory.
env["XGILL_NO_SOURCE"] = "1"
setup_env_for_tools(env)
self.setup_env_for_tools(env)
if "haz_objdir" in kwargs:
env["MOZ_OBJDIR"] = kwargs.pop("haz_objdir")
@@ -343,7 +337,6 @@ def inner_compile(command_context, **kwargs):
"build", command_context._mach_context, **kwargs
)
@SubCommand(
"hazards", "analyze", description="Analyzed gathered data for rooting hazards"
)
@@ -365,12 +358,12 @@ def inner_compile(command_context, **kwargs):
nargs=argparse.REMAINDER,
help="Remaining non-optional arguments to analyze.py script",
)
def analyze(command_context, application, shell_objdir, work_dir, extra):
def analyze(self, command_context, application, shell_objdir, work_dir, extra):
"""Analyzed gathered data for rooting hazards"""
shell = ensure_shell(command_context, shell_objdir)
shell = self.ensure_shell(command_context, shell_objdir)
args = [
os.path.join(script_dir(command_context), "analyze.py"),
os.path.join(self.script_dir(command_context), "analyze.py"),
"--js",
shell,
]
@@ -382,13 +375,12 @@ def analyze(command_context, application, shell_objdir, work_dir, extra):
"-v",
]
setup_env_for_tools(os.environ)
setup_env_for_shell(os.environ, shell)
self.setup_env_for_tools(os.environ)
self.setup_env_for_shell(os.environ, shell)
work_dir = get_work_dir(command_context, application, work_dir)
work_dir = self.get_work_dir(command_context, application, work_dir)
return command_context.run_process(args=args, cwd=work_dir, pass_thru=True)
@SubCommand(
"hazards",
"self-test",
@@ -399,21 +391,21 @@ def analyze(command_context, application, shell_objdir, work_dir, extra):
default=None,
help="objdir containing the optimized JS shell for running the analysis.",
)
def self_test(command_context, shell_objdir):
def self_test(self, command_context, shell_objdir):
"""Analyzed gathered data for rooting hazards"""
shell = ensure_shell(command_context, shell_objdir)
shell = self.ensure_shell(command_context, shell_objdir)
args = [
os.path.join(script_dir(command_context), "run-test.py"),
os.path.join(self.script_dir(command_context), "run-test.py"),
"-v",
"--js",
shell,
"--sixgill",
os.path.join(tools_dir(), "sixgill"),
os.path.join(self.tools_dir(), "sixgill"),
"--gccdir",
gcc_dir(),
self.gcc_dir(),
]
setup_env_for_tools(os.environ)
setup_env_for_shell(os.environ, shell)
self.setup_env_for_tools(os.environ)
self.setup_env_for_shell(os.environ, shell)
return command_context.run_process(args=args, pass_thru=True)

View File

@@ -10,11 +10,13 @@ import sys
from argparse import Namespace
from mozbuild.base import (
MachCommandBase,
MachCommandConditions as conditions,
MozbuildObject,
)
from mach.decorators import (
CommandProvider,
Command,
)
@@ -226,16 +228,17 @@ def get_parser():
return parser
@CommandProvider
class MachCommands(MachCommandBase):
@Command(
"reftest",
category="testing",
description="Run reftests (layout and graphics correctness).",
parser=get_parser,
)
def run_reftest(command_context, **kwargs):
def run_reftest(self, command_context, **kwargs):
kwargs["suite"] = "reftest"
return _run_reftest(command_context, **kwargs)
return self._run_reftest(command_context, **kwargs)
@Command(
"jstestbrowser",
@@ -243,7 +246,7 @@ def run_reftest(command_context, **kwargs):
description="Run js/src/tests in the browser.",
parser=get_parser,
)
def run_jstestbrowser(command_context, **kwargs):
def run_jstestbrowser(self, command_context, **kwargs):
if "--enable-js-shell" not in command_context.mozconfig["configure_args"]:
raise Exception(
"jstestbrowser requires --enable-js-shell be specified in mozconfig."
@@ -252,8 +255,7 @@ def run_jstestbrowser(command_context, **kwargs):
"build", command_context._mach_context, what=["stage-jstests"]
)
kwargs["suite"] = "jstestbrowser"
return _run_reftest(command_context, **kwargs)
return self._run_reftest(command_context, **kwargs)
@Command(
"crashtest",
@@ -261,12 +263,11 @@ def run_jstestbrowser(command_context, **kwargs):
description="Run crashtests (Check if crashes on a page).",
parser=get_parser,
)
def run_crashtest(command_context, **kwargs):
def run_crashtest(self, command_context, **kwargs):
kwargs["suite"] = "crashtest"
return _run_reftest(command_context, **kwargs)
return self._run_reftest(command_context, **kwargs)
def _run_reftest(command_context, **kwargs):
def _run_reftest(self, command_context, **kwargs):
kwargs["topsrcdir"] = command_context.topsrcdir
process_test_objects(kwargs)
reftest = command_context._spawn(ReftestRunner)
@@ -279,7 +280,9 @@ def _run_reftest(command_context, **kwargs):
InstallIntent,
)
install = InstallIntent.NO if kwargs.get("no_install") else InstallIntent.YES
install = (
InstallIntent.NO if kwargs.get("no_install") else InstallIntent.YES
)
verbose = False
if (
kwargs.get("log_mach_verbose")

View File

@@ -10,8 +10,10 @@ from argparse import Namespace
from functools import partial
from mach.decorators import (
CommandProvider,
Command,
)
from mozbuild.base import MachCommandBase
here = os.path.abspath(os.path.dirname(__file__))
logger = None
@@ -105,13 +107,15 @@ def setup_argument_parser():
return parser
@CommandProvider
class ReftestCommands(MachCommandBase):
@Command(
"reftest",
category="testing",
description="Run the reftest harness.",
parser=setup_argument_parser,
)
def reftest(command_context, **kwargs):
def reftest(self, command_context, **kwargs):
command_context._mach_context.activate_mozharness_venv()
kwargs["suite"] = "reftest"
return run_reftest(command_context._mach_context, **kwargs)

View File

@@ -11,6 +11,7 @@ import os
import mozpack.path as mozpath
from mozbuild.base import (
MachCommandBase,
MachCommandConditions as conditions,
)
@@ -20,6 +21,7 @@ from mozbuild.shellutil import (
from mach.decorators import (
CommandArgument,
CommandProvider,
Command,
SubCommand,
)
@@ -47,16 +49,17 @@ def REMOVED(cls):
return False
@CommandProvider
class MachCommands(MachCommandBase):
@Command(
"android",
category="devenv",
description="Run Android-specific commands.",
conditions=[conditions.is_android],
)
def android(command_context):
def android(self, command_context):
pass
@SubCommand(
"android",
"assemble-app",
@@ -64,8 +67,8 @@ def android(command_context):
See http://firefox-source-docs.mozilla.org/build/buildsystem/toolchains.html#firefox-for-android-with-gradle""", # NOQA: E501
)
@CommandArgument("args", nargs=argparse.REMAINDER)
def android_assemble_app(command_context, args):
ret = gradle(
def android_assemble_app(self, command_context, args):
ret = self.gradle(
command_context,
command_context.substs["GRADLE_ANDROID_APP_TASKS"] + ["-x", "lint"] + args,
verbose=True,
@@ -73,7 +76,6 @@ def android_assemble_app(command_context, args):
return ret
@SubCommand(
"android",
"generate-sdk-bindings",
@@ -82,20 +84,24 @@ def android_assemble_app(command_context, args):
@CommandArgument(
"inputs",
nargs="+",
help="config files, like [/path/to/ClassName-classes.txt]+",
help="config files, " "like [/path/to/ClassName-classes.txt]+",
)
@CommandArgument("args", nargs=argparse.REMAINDER)
def android_generate_sdk_bindings(command_context, inputs, args):
def android_generate_sdk_bindings(self, command_context, inputs, args):
import itertools
def stem(input):
# Turn "/path/to/ClassName-classes.txt" into "ClassName".
return os.path.basename(input).rsplit("-classes.txt", 1)[0]
bindings_inputs = list(itertools.chain(*((input, stem(input)) for input in inputs)))
bindings_args = "-Pgenerate_sdk_bindings_args={}".format(";".join(bindings_inputs))
bindings_inputs = list(
itertools.chain(*((input, stem(input)) for input in inputs))
)
bindings_args = "-Pgenerate_sdk_bindings_args={}".format(
";".join(bindings_inputs)
)
ret = gradle(
ret = self.gradle(
command_context,
command_context.substs["GRADLE_ANDROID_GENERATE_SDK_BINDINGS_TASKS"]
+ [bindings_args]
@@ -105,68 +111,64 @@ def android_generate_sdk_bindings(command_context, inputs, args):
return ret
@SubCommand(
"android",
"generate-generated-jni-wrappers",
"""Generate GeckoView JNI wrappers used when building GeckoView.""",
)
@CommandArgument("args", nargs=argparse.REMAINDER)
def android_generate_generated_jni_wrappers(command_context, args):
ret = gradle(
def android_generate_generated_jni_wrappers(self, command_context, args):
ret = self.gradle(
command_context,
command_context.substs["GRADLE_ANDROID_GENERATE_GENERATED_JNI_WRAPPERS_TASKS"]
command_context.substs[
"GRADLE_ANDROID_GENERATE_GENERATED_JNI_WRAPPERS_TASKS"
]
+ args,
verbose=True,
)
return ret
@SubCommand(
"android",
"api-lint",
"""Run Android api-lint.
REMOVED/DEPRECATED: Use 'mach lint --linter android-api-lint'.""",
)
def android_apilint_REMOVED(command_context):
def android_apilint_REMOVED(self, command_context):
print(LINT_DEPRECATION_MESSAGE)
return 1
@SubCommand(
"android",
"test",
"""Run Android test.
REMOVED/DEPRECATED: Use 'mach lint --linter android-test'.""",
)
def android_test_REMOVED(command_context):
def android_test_REMOVED(self, command_context):
print(LINT_DEPRECATION_MESSAGE)
return 1
@SubCommand(
"android",
"lint",
"""Run Android lint.
REMOVED/DEPRECATED: Use 'mach lint --linter android-lint'.""",
)
def android_lint_REMOVED(command_context):
def android_lint_REMOVED(self, command_context):
print(LINT_DEPRECATION_MESSAGE)
return 1
@SubCommand(
"android",
"checkstyle",
"""Run Android checkstyle.
REMOVED/DEPRECATED: Use 'mach lint --linter android-checkstyle'.""",
)
def android_checkstyle_REMOVED(command_context):
def android_checkstyle_REMOVED(self, command_context):
print(LINT_DEPRECATION_MESSAGE)
return 1
@SubCommand(
"android",
"gradle-dependencies",
@@ -174,11 +176,11 @@ def android_checkstyle_REMOVED(command_context):
See http://firefox-source-docs.mozilla.org/build/buildsystem/toolchains.html#firefox-for-android-with-gradle""", # NOQA: E501
)
@CommandArgument("args", nargs=argparse.REMAINDER)
def android_gradle_dependencies(command_context, args):
def android_gradle_dependencies(self, command_context, args):
# We don't want to gate producing dependency archives on clean
# lint or checkstyle, particularly because toolchain versions
# can change the outputs for those processes.
gradle(
self.gradle(
command_context,
command_context.substs["GRADLE_ANDROID_DEPENDENCIES_TASKS"]
+ ["--continue"]
@@ -188,7 +190,6 @@ def android_gradle_dependencies(command_context, args):
return 0
@SubCommand(
"android",
"archive-geckoview",
@@ -196,8 +197,8 @@ def android_gradle_dependencies(command_context, args):
See http://firefox-source-docs.mozilla.org/build/buildsystem/toolchains.html#firefox-for-android-with-gradle""", # NOQA: E501
)
@CommandArgument("args", nargs=argparse.REMAINDER)
def android_archive_geckoview(command_context, args):
ret = gradle(
def android_archive_geckoview(self, command_context, args):
ret = self.gradle(
command_context,
command_context.substs["GRADLE_ANDROID_ARCHIVE_GECKOVIEW_TASKS"] + args,
verbose=True,
@@ -205,13 +206,13 @@ def android_archive_geckoview(command_context, args):
return ret
@SubCommand("android", "build-geckoview_example", """Build geckoview_example """)
@CommandArgument("args", nargs=argparse.REMAINDER)
def android_build_geckoview_example(command_context, args):
gradle(
def android_build_geckoview_example(self, command_context, args):
self.gradle(
command_context,
command_context.substs["GRADLE_ANDROID_BUILD_GECKOVIEW_EXAMPLE_TASKS"] + args,
command_context.substs["GRADLE_ANDROID_BUILD_GECKOVIEW_EXAMPLE_TASKS"]
+ args,
verbose=True,
)
@@ -222,13 +223,15 @@ def android_build_geckoview_example(command_context, args):
return 0
@SubCommand("android", "install-geckoview_example", """Install geckoview_example """)
@SubCommand(
"android", "install-geckoview_example", """Install geckoview_example """
)
@CommandArgument("args", nargs=argparse.REMAINDER)
def android_install_geckoview_example(command_context, args):
gradle(
def android_install_geckoview_example(self, command_context, args):
self.gradle(
command_context,
command_context.substs["GRADLE_ANDROID_INSTALL_GECKOVIEW_EXAMPLE_TASKS"] + args,
command_context.substs["GRADLE_ANDROID_INSTALL_GECKOVIEW_EXAMPLE_TASKS"]
+ args,
verbose=True,
)
@@ -239,17 +242,19 @@ def android_install_geckoview_example(command_context, args):
return 0
@SubCommand(
"android",
"geckoview-docs",
"""Create GeckoView javadoc and optionally upload to Github""",
)
@CommandArgument("--archive", action="store_true", help="Generate a javadoc archive.")
@CommandArgument(
"--archive", action="store_true", help="Generate a javadoc archive."
)
@CommandArgument(
"--upload",
metavar="USER/REPO",
help="Upload geckoview documentation to Github, using the specified USER/REPO.",
help="Upload geckoview documentation to Github, "
"using the specified USER/REPO.",
)
@CommandArgument(
"--upload-branch",
@@ -270,6 +275,7 @@ def android_install_geckoview_example(command_context, args):
help="Use the specified message for commits.",
)
def android_geckoview_docs(
self,
command_context,
archive,
upload,
@@ -284,7 +290,7 @@ def android_geckoview_docs(
else command_context.substs["GRADLE_ANDROID_GECKOVIEW_DOCS_TASKS"]
)
ret = gradle(command_context, tasks, verbose=True)
ret = self.gradle(command_context, tasks, verbose=True)
if ret or not upload:
return ret
@@ -360,7 +366,9 @@ def android_geckoview_docs(
mozfile.extract_zip(src_tar, dst_path)
# Commit and push.
command_context.run_process(["git", "add", "--all"], append_env=env, pass_thru=True)
command_context.run_process(
["git", "add", "--all"], append_env=env, pass_thru=True
)
if (
command_context.run_process(
["git", "diff", "--cached", "--quiet"],
@@ -385,7 +393,6 @@ def android_geckoview_docs(
mozfile.remove(keyfile)
return 0
@Command(
"gradle",
category="devenv",
@@ -399,7 +406,7 @@ def android_geckoview_docs(
help="Verbose output for what commands the build is running.",
)
@CommandArgument("args", nargs=argparse.REMAINDER)
def gradle(command_context, args, verbose=False):
def gradle(self, command_context, args, verbose=False):
if not verbose:
# Avoid logging the command
command_context.log_manager.terminal_handler.setLevel(logging.CRITICAL)
@@ -457,12 +464,13 @@ def gradle(command_context, args, verbose=False):
cwd=mozpath.join(command_context.topsrcdir),
)
@Command("gradle-install", category="devenv", conditions=[REMOVED])
def gradle_install_REMOVED(command_context):
def gradle_install_REMOVED(self, command_context):
pass
@CommandProvider
class AndroidEmulatorCommands(MachCommandBase):
@Command(
"android-emulator",
category="devenv",
@@ -482,12 +490,15 @@ def gradle_install_REMOVED(command_context):
'By default, "arm" will be used if the current build environment '
'architecture is arm; otherwise "x86_64".',
)
@CommandArgument("--wait", action="store_true", help="Wait for emulator to be closed.")
@CommandArgument(
"--wait", action="store_true", help="Wait for emulator to be closed."
)
@CommandArgument("--gpu", help="Over-ride the emulator -gpu argument.")
@CommandArgument(
"--verbose", action="store_true", help="Log informative status messages."
)
def emulator(
self,
command_context,
version,
wait=False,

View File

@@ -9,22 +9,26 @@ Mach commands are defined via Python decorators.
All the relevant decorators are defined in the *mach.decorators* module.
The important decorators are as follows:
:py:func:`CommandProvider <mach.decorators.CommandProvider>`
A class decorator that denotes that a class contains mach
commands. The decorator takes no arguments.
:py:func:`Command <mach.decorators.Command>`
A function decorator that denotes that the function should be called when
A method decorator that denotes that the method should be called when
the specified command is requested. The decorator takes a command name
as its first argument and a number of additional arguments to
configure the behavior of the command. The decorated function must take a
``command_context`` argument as its first.
configure the behavior of the command. The decorated method must take a
``command_context`` argument as its first (after ``self``).
``command_context`` is a properly configured instance of a ``MozbuildObject``
subclass, meaning it can be used for accessing things like the current config
and running processes.
:py:func:`CommandArgument <mach.decorators.CommandArgument>`
A function decorator that defines an argument to the command. Its
A method decorator that defines an argument to the command. Its
arguments are essentially proxied to ArgumentParser.add_argument()
:py:func:`SubCommand <mach.decorators.SubCommand>`
A function decorator that denotes that the function should be a
A method decorator that denotes that the method should be a
sub-command to an existing ``@Command``. The decorator takes the
parent command name as its first argument and the sub-command name
as its second argument.
@@ -32,6 +36,8 @@ The important decorators are as follows:
``@CommandArgument`` can be used on ``@SubCommand`` instances just
like they can on ``@Command`` instances.
Classes with the ``@CommandProvider`` decorator **must** subclass
``MachCommandBase`` and have a compatible ``__init__`` method.
Here is a complete example:
@@ -39,13 +45,17 @@ Here is a complete example:
from mach.decorators import (
CommandArgument,
CommandProvider,
Command,
)
from mozbuild.base import MachCommandBase
@CommandProvider
class MyClass(MachCommandBase):
@Command('doit', help='Do ALL OF THE THINGS.')
@CommandArgument('--force', '-f', action='store_true',
help='Force doing it.')
def doit(command_context, force=False):
def doit(self, command_context, force=False):
# Do stuff here.
When the module is loaded, the decorators tell mach about all handlers.
@@ -69,7 +79,7 @@ define a series of conditions on the
:py:func:`Command <mach.decorators.Command>` decorator.
A condition is simply a function that takes an instance of the
:py:func:`mozbuild.base.MachCommandBase` class as an argument, and
:py:func:`mach.decorators.CommandProvider` class as an argument, and
returns ``True`` or ``False``. If any of the conditions defined on a
command return ``False``, the command will not be runnable. The
docstring of a condition function is used in error messages, to explain
@@ -80,6 +90,7 @@ Here is an example:
.. code-block:: python
from mach.decorators import (
CommandProvider,
Command,
)
@@ -87,10 +98,19 @@ Here is an example:
"""The build needs to be available."""
return cls.build_path is not None
@CommandProvider
class MyClass(MachCommandBase):
def __init__(self, *args, **kwargs):
super(MyClass, self).__init__(*args, **kwargs)
self.build_path = ...
@Command('run_tests', conditions=[build_available])
def run_tests(command_context):
def run_tests(self, command_context):
# Do stuff here.
It is important to make sure that any state needed by the condition is
available to instances of the command provider.
By default all commands without any conditions applied will be runnable,
but it is possible to change this behaviour by setting
``require_conditions`` to ``True``:

View File

@@ -116,6 +116,7 @@ For example:
from mach.decorators import (
Command,
CommandProvider,
SettingsProvider,
)
from mozbuild.base import MachCommandBase
@@ -128,10 +129,15 @@ For example:
('foo.baz', 'int', 'desc', 0, {'choices': set([0,1,2])}),
]
@CommandProvider
class Commands(MachCommandBase):
def __init__(self, *args, **kwargs):
super(Commands, self).__init__(*args, **kwargs)
self.settings = self._mach_context.settings
@Command('command', category='misc',
description='Prints a setting')
def command(command_context):
settings = command_context._mach_context.settings
print(settings.a.b)
for option in settings.foo:
print(settings.foo[option])
def command(self):
print(self.settings.a.b)
for option in self.settings.foo:
print(self.settings.foo[option])

View File

@@ -19,15 +19,17 @@ Adding Metrics to a new Command
If you would like to submit telemetry metrics from your mach ``@Command``, you should take two steps:
#. Parameterize your ``@Command`` annotation with ``metrics_path``.
#. Use the ``command_context.metrics`` handle provided by ``MachCommandBase``
#. Use the ``self.metrics`` handle provided by ``MachCommandBase``
For example::
METRICS_PATH = os.path.abspath(os.path.join(__file__, '..', '..', 'metrics.yaml'))
@CommandProvider
class CustomCommand(MachCommandBase):
@Command('custom-command', metrics_path=METRICS_PATH)
def custom_command(command_context):
command_context.metrics.custom.foo.set('bar')
def custom_command(self):
self.metrics.custom.foo.set('bar')
Updating Generated Metrics Docs
===============================

View File

@@ -13,7 +13,8 @@ from itertools import chain
import attr
from mach.decorators import Command, CommandArgument, SubCommand
from mach.decorators import CommandProvider, Command, CommandArgument, SubCommand
from mozbuild.base import MachCommandBase
from mozbuild.util import memoize
here = os.path.abspath(os.path.dirname(__file__))
@@ -36,19 +37,19 @@ def render_template(shell, context):
return template % context
@CommandProvider
class BuiltinCommands(MachCommandBase):
@memoize
def command_handlers(command_context):
def command_handlers(self, command_context):
"""A dictionary of command handlers keyed by command name."""
return command_context._mach_context.commands.command_handlers
@memoize
def commands(command_context):
def commands(self, command_context):
"""A sorted list of all command names."""
return sorted(command_handlers(command_context))
return sorted(self.command_handlers(command_context))
def _get_parser_options(parser):
def _get_parser_options(self, parser):
options = {}
for action in parser._actions:
# ignore positional args
@@ -62,20 +63,18 @@ def _get_parser_options(parser):
options[tuple(action.option_strings)] = action.help or ""
return options
@memoize
def global_options(command_context):
def global_options(self, command_context):
"""Return a dict of global options.
Of the form `{("-o", "--option"): "description"}`.
"""
for group in command_context._mach_context.global_parser._action_groups:
if group.title == "Global Arguments":
return _get_parser_options(group)
return self._get_parser_options(group)
@memoize
def _get_handler_options(handler):
def _get_handler_options(self, handler):
"""Return a dict of options for the given handler.
Of the form `{("-o", "--option"): "description"}`.
@@ -89,14 +88,13 @@ def _get_handler_options(handler):
options[tuple(option_strings)] = val.get("help", "")
if handler._parser:
options.update(_get_parser_options(handler.parser))
options.update(self._get_parser_options(handler.parser))
return options
def _get_handler_info(handler):
def _get_handler_info(self, handler):
try:
options = _get_handler_options(handler)
options = self._get_handler_options(handler)
except (Exception, SystemExit):
# We don't want misbehaving commands to break tab completion,
# ignore any exceptions.
@@ -104,7 +102,7 @@ def _get_handler_info(handler):
subcommands = []
for sub in sorted(handler.subcommand_handlers):
subcommands.append(_get_handler_info(handler.subcommand_handlers[sub]))
subcommands.append(self._get_handler_info(handler.subcommand_handlers[sub]))
return CommandInfo(
name=handler.name,
@@ -114,22 +112,21 @@ def _get_handler_info(handler):
subcommand=handler.subcommand,
)
@memoize
def commands_info(command_context):
def commands_info(self, command_context):
"""Return a list of CommandInfo objects for each command."""
commands_info = []
# Loop over self.commands() rather than self.command_handlers().items() for
# alphabetical order.
for c in commands(command_context):
commands_info.append(_get_handler_info(command_handlers(command_context)[c]))
for c in self.commands(command_context):
commands_info.append(
self._get_handler_info(self.command_handlers(command_context)[c])
)
return commands_info
@Command("mach-commands", category="misc", description="List all mach commands.")
def run_commands(command_context):
print("\n".join(commands(command_context)))
def run_commands(self, command_context):
print("\n".join(self.commands(command_context)))
@Command(
"mach-debug-commands",
@@ -143,10 +140,10 @@ def run_commands(command_context):
nargs="?",
help="Only display commands containing given substring.",
)
def run_debug_commands(command_context, match=None):
def run_debug_commands(self, command_context, match=None):
import inspect
for command, handler in command_handlers(command_context).items():
for command, handler in self.command_handlers(command_context).items():
if match and match not in command:
continue
@@ -161,7 +158,6 @@ def run_debug_commands(command_context, match=None):
print("Method: %s" % handler.method)
print("")
@Command(
"mach-completion",
category="misc",
@@ -170,25 +166,25 @@ def run_debug_commands(command_context, match=None):
@CommandArgument(
"args", default=None, nargs=argparse.REMAINDER, help="Command to complete."
)
def run_completion(command_context, args):
def run_completion(self, command_context, args):
if not args:
print("\n".join(commands(command_context)))
print("\n".join(self.commands(command_context)))
return
is_help = "help" in args
command = None
for i, arg in enumerate(args):
if arg in commands(command_context):
if arg in self.commands(command_context):
command = arg
args = args[i + 1 :]
break
# If no command is typed yet, just offer the commands.
if not command:
print("\n".join(commands(command_context)))
print("\n".join(self.commands(command_context)))
return
handler = command_handlers(command_context)[command]
handler = self.command_handlers(command_context)[command]
# If a subcommand was typed, update the handler.
for arg in args:
if arg in handler.subcommand_handlers:
@@ -201,11 +197,10 @@ def run_completion(command_context, args):
return
targets.append("help")
targets.extend(chain(*_get_handler_options(handler).keys()))
targets.extend(chain(*self._get_handler_options(handler).keys()))
print("\n".join(targets))
def _zsh_describe(value, description=None):
def _zsh_describe(self, value, description=None):
value = '"' + value.replace(":", "\\:")
if description:
description = subprocess.list2cmdline(
@@ -221,7 +216,6 @@ def _zsh_describe(value, description=None):
return value
@SubCommand(
"mach-completion",
"bash",
@@ -234,16 +228,16 @@ def _zsh_describe(value, description=None):
default=None,
help="File path to save completion script.",
)
def completion_bash(command_context, outfile):
def completion_bash(self, command_context, outfile):
commands_subcommands = []
case_options = []
case_subcommands = []
for i, cmd in enumerate(commands_info(command_context)):
for i, cmd in enumerate(self.commands_info(command_context)):
# Build case statement for options.
options = []
for opt_strs, description in cmd.options.items():
for opt in opt_strs:
options.append(_zsh_describe(opt, None).strip('"'))
options.append(self._zsh_describe(opt, None).strip('"'))
if options:
case_options.append(
@@ -262,14 +256,18 @@ def completion_bash(command_context, outfile):
options = []
for opt_strs, description in sub.options.items():
for opt in opt_strs:
options.append(_zsh_describe(opt, None))
options.append(self._zsh_describe(opt, None))
if options:
case_options.append(
"\n".join(
[
' ("{} {}")'.format(sub.name, sub.subcommand),
' opts="${{opts}} {}"'.format(" ".join(options)),
' ("{} {}")'.format(
sub.name, sub.subcommand
),
' opts="${{opts}} {}"'.format(
" ".join(options)
),
" ;;",
"",
]
@@ -277,7 +275,9 @@ def completion_bash(command_context, outfile):
)
# Build case statement for subcommands.
subcommands = [_zsh_describe(s.subcommand, None) for s in cmd.subcommands]
subcommands = [
self._zsh_describe(s.subcommand, None) for s in cmd.subcommands
]
if subcommands:
commands_subcommands.append(
'[{}]=" {} "'.format(
@@ -289,7 +289,9 @@ def completion_bash(command_context, outfile):
"\n".join(
[
" ({})".format(cmd.name),
' subs="${{subs}} {}"'.format(" ".join(subcommands)),
' subs="${{subs}} {}"'.format(
" ".join(subcommands)
),
" ;;",
"",
]
@@ -297,12 +299,12 @@ def completion_bash(command_context, outfile):
)
globalopts = [
opt for opt_strs in global_options(command_context) for opt in opt_strs
opt for opt_strs in self.global_options(command_context) for opt in opt_strs
]
context = {
"case_options": "\n".join(case_options),
"case_subcommands": "\n".join(case_subcommands),
"commands": " ".join(commands(command_context)),
"commands": " ".join(self.commands(command_context)),
"commands_subcommands": " ".join(sorted(commands_subcommands)),
"globalopts": " ".join(sorted(globalopts)),
}
@@ -310,7 +312,6 @@ def completion_bash(command_context, outfile):
outfile = open(outfile, "w") if outfile else sys.stdout
print(render_template("bash", context), file=outfile)
@SubCommand(
"mach-completion",
"zsh",
@@ -323,19 +324,19 @@ def completion_bash(command_context, outfile):
default=None,
help="File path to save completion script.",
)
def completion_zsh(command_context, outfile):
def completion_zsh(self, command_context, outfile):
commands_descriptions = []
commands_subcommands = []
case_options = []
case_subcommands = []
for i, cmd in enumerate(commands_info(command_context)):
commands_descriptions.append(_zsh_describe(cmd.name, cmd.description))
for i, cmd in enumerate(self.commands_info(command_context)):
commands_descriptions.append(self._zsh_describe(cmd.name, cmd.description))
# Build case statement for options.
options = []
for opt_strs, description in cmd.options.items():
for opt in opt_strs:
options.append(_zsh_describe(opt, description))
options.append(self._zsh_describe(opt, description))
if options:
case_options.append(
@@ -354,7 +355,7 @@ def completion_zsh(command_context, outfile):
options = []
for opt_strs, description in sub.options.items():
for opt in opt_strs:
options.append(_zsh_describe(opt, description))
options.append(self._zsh_describe(opt, description))
if options:
case_options.append(
@@ -370,7 +371,7 @@ def completion_zsh(command_context, outfile):
# Build case statement for subcommands.
subcommands = [
_zsh_describe(s.subcommand, s.description) for s in cmd.subcommands
self._zsh_describe(s.subcommand, s.description) for s in cmd.subcommands
]
if subcommands:
commands_subcommands.append(
@@ -391,9 +392,9 @@ def completion_zsh(command_context, outfile):
)
globalopts = []
for opt_strings, description in global_options(command_context).items():
for opt_strings, description in self.global_options(command_context).items():
for opt in opt_strings:
globalopts.append(_zsh_describe(opt, description))
globalopts.append(self._zsh_describe(opt, description))
context = {
"case_options": "\n".join(case_options),
@@ -406,7 +407,6 @@ def completion_zsh(command_context, outfile):
outfile = open(outfile, "w") if outfile else sys.stdout
print(render_template("zsh", context), file=outfile)
@SubCommand(
"mach-completion",
"fish",
@@ -419,7 +419,7 @@ def completion_zsh(command_context, outfile):
default=None,
help="File path to save completion script.",
)
def completion_fish(command_context, outfile):
def completion_fish(self, command_context, outfile):
def _append_opt_strs(comp, opt_strs):
for opt in opt_strs:
if opt.startswith("--"):
@@ -429,7 +429,7 @@ def completion_fish(command_context, outfile):
return comp
globalopts = []
for opt_strs, description in global_options(command_context).items():
for opt_strs, description in self.global_options(command_context).items():
comp = (
"complete -c mach -n '__fish_mach_complete_no_command' "
"-d '{}'".format(description.replace("'", "\\'"))
@@ -439,7 +439,7 @@ def completion_fish(command_context, outfile):
cmds = []
cmds_opts = []
for i, cmd in enumerate(commands_info(command_context)):
for i, cmd in enumerate(self.commands_info(command_context)):
cmds.append(
"complete -c mach -f -n '__fish_mach_complete_no_command' "
"-a {} -d '{}'".format(cmd.name, cmd.description.replace("'", "\\'"))
@@ -451,7 +451,9 @@ def completion_fish(command_context, outfile):
for opt_strs, description in cmd.options.items():
comp = (
"complete -c mach -A -n '__fish_mach_complete_command {} {}' "
"-d '{}'".format(cmd.name, subcommands, description.replace("'", "\\'"))
"-d '{}'".format(
cmd.name, subcommands, description.replace("'", "\\'")
)
)
comp = _append_opt_strs(comp, opt_strs)
cmds_opts.append(comp)
@@ -478,11 +480,11 @@ def completion_fish(command_context, outfile):
)
cmds_opts.append(comp)
if i < len(commands(command_context)) - 1:
if i < len(self.commands(command_context)) - 1:
cmds_opts.append("")
context = {
"commands": " ".join(commands(command_context)),
"commands": " ".join(self.commands(command_context)),
"command_completions": "\n".join(cmds),
"command_option_completions": "\n".join(cmds_opts),
"global_option_completions": "\n".join(globalopts),

View File

@@ -7,17 +7,22 @@ from __future__ import absolute_import, print_function, unicode_literals
from textwrap import TextWrapper
from mach.config import TYPE_CLASSES
from mach.decorators import CommandArgument, Command
from mach.decorators import CommandArgument, CommandProvider, Command
from mozbuild.base import MachCommandBase
# Interact with settings for mach.
@CommandProvider
class Settings(MachCommandBase):
"""Interact with settings for mach.
# Currently, we only provide functionality to view what settings are
# available. In the future, this module will be used to modify settings, help
# people create configs via a wizard, etc.
Currently, we only provide functionality to view what settings are
available. In the future, this module will be used to modify settings, help
people create configs via a wizard, etc.
"""
@Command("settings", category="devenv", description="Show available config settings.")
@Command(
"settings", category="devenv", description="Show available config settings."
)
@CommandArgument(
"-l",
"--list",
@@ -25,7 +30,7 @@ from mach.decorators import CommandArgument, Command
action="store_true",
help="Show settings in a concise list",
)
def run_settings(command_context, short=None):
def run_settings(self, command_context, short=None):
"""List available settings."""
types = {v: k for k, v in TYPE_CLASSES.items()}
wrapper = TextWrapper(initial_indent="# ", subsequent_indent="# ")
@@ -33,7 +38,9 @@ def run_settings(command_context, short=None):
if not short:
print("%s[%s]" % ("" if i == 0 else "\n", section))
for option in sorted(command_context._mach_context.settings[section]._settings):
for option in sorted(
command_context._mach_context.settings[section]._settings
):
meta = command_context._mach_context.settings[section].get_meta(option)
desc = meta["description"]

View File

@@ -31,12 +31,18 @@ class _MachCommand(object):
# By default, subcommands will be sorted. If this is set to
# 'declaration', they will be left in declaration order.
"order",
# This is the function or callable that will be called when
# the command is invoked
"func",
# Describes how dispatch is performed.
# The Python class providing the command. This is the class type not
# an instance of the class. Mach will instantiate a new instance of
# the class if the command is executed.
"cls",
# The path to the `metrics.yaml` file that describes data that telemetry will
# gather for this command. This path is optional.
"metrics_path",
# The name of the method providing the command. In other words, this
# is the str name of the attribute on the class type corresponding to
# the name of the function.
"method",
# Dict of string to _MachCommand defining sub-commands for this
# command.
"subcommand_handlers",
@@ -73,8 +79,9 @@ class _MachCommand(object):
)
self.ok_if_tests_disabled = ok_if_tests_disabled
self.func = None
self.cls = None
self.metrics_path = None
self.method = None
self.subcommand_handlers = {}
self.decl_order = None
@@ -82,11 +89,7 @@ class _MachCommand(object):
metrics = None
if self.metrics_path:
metrics = context.telemetry.metrics(self.metrics_path)
# This ensures the resulting class is defined inside `mach` so that logging
# works as expected, and has a meaningful name
subclass = type(self.name, (MachCommandBase,), {})
return subclass(context, virtualenv_name=virtualenv_name, metrics=metrics)
return self.cls(context, virtualenv_name=virtualenv_name, metrics=metrics)
@property
def parser(self):
@@ -99,7 +102,7 @@ class _MachCommand(object):
@property
def docstring(self):
return self.func.__doc__
return self.cls.__dict__[self.method].__doc__
def __ior__(self, other):
if not isinstance(other, _MachCommand):
@@ -111,11 +114,37 @@ class _MachCommand(object):
return self
def register(self, func):
"""Register the command in the Registrar with the function to be called on invocation."""
if not self.subcommand:
if not self.conditions and Registrar.require_conditions:
return
def CommandProvider(cls):
if not issubclass(cls, MachCommandBase):
raise MachError(
"Mach command provider class %s must be a subclass of "
"mozbuild.base.MachComandBase" % cls.__name__
)
seen_commands = set()
# We scan __dict__ because we only care about the classes' own attributes,
# not inherited ones. If we did inherited attributes, we could potentially
# define commands multiple times. We also sort keys so commands defined in
# the same class are grouped in a sane order.
command_methods = sorted(
[
(name, value._mach_command)
for name, value in cls.__dict__.items()
if hasattr(value, "_mach_command")
]
)
for method, command in command_methods:
# Ignore subcommands for now: we handle them later.
if command.subcommand:
continue
seen_commands.add(command.name)
if not command.conditions and Registrar.require_conditions:
continue
msg = (
"Mach command '%s' implemented incorrectly. "
@@ -123,32 +152,46 @@ class _MachCommand(object):
+ "of functions. Found %s instead."
)
if not isinstance(self.conditions, collections.abc.Iterable):
msg = msg % (self.name, type(self.conditions))
if not isinstance(command.conditions, collections.abc.Iterable):
msg = msg % (command.name, type(command.conditions))
raise MachError(msg)
for c in self.conditions:
for c in command.conditions:
if not hasattr(c, "__call__"):
msg = msg % (self.name, type(c))
msg = msg % (command.name, type(c))
raise MachError(msg)
self.func = func
command.cls = cls
command.method = method
Registrar.register_command_handler(self)
Registrar.register_command_handler(command)
else:
if self.name not in Registrar.command_handlers:
# Now do another pass to get sub-commands. We do this in two passes so
# we can check the parent command existence without having to hold
# state and reconcile after traversal.
for method, command in command_methods:
# It is a regular command.
if not command.subcommand:
continue
if command.name not in seen_commands:
raise MachError(
"Command referenced by sub-command does not exist: %s" % self.name
"Command referenced by sub-command does not exist: %s" % command.name
)
self.func = func
parent = Registrar.command_handlers[self.name]
if command.name not in Registrar.command_handlers:
continue
if self.subcommand in parent.subcommand_handlers:
raise MachError("sub-command already defined: %s" % self.subcommand)
command.cls = cls
command.method = method
parent = Registrar.command_handlers[command.name]
parent.subcommand_handlers[self.subcommand] = self
if command.subcommand in parent.subcommand_handlers:
raise MachError("sub-command already defined: %s" % command.subcommand)
parent.subcommand_handlers[command.subcommand] = command
return cls
class Command(object):
@@ -182,7 +225,6 @@ class Command(object):
func._mach_command = _MachCommand()
func._mach_command |= self._mach_command
func._mach_command.register(func)
return func
@@ -223,7 +265,6 @@ class SubCommand(object):
func._mach_command = _MachCommand()
func._mach_command |= self._mach_command
func._mach_command.register(func)
return func

View File

@@ -95,7 +95,7 @@ class MachRegistrar(object):
return 1
self.command_depth += 1
fn = handler.func
fn = getattr(instance, handler.method)
start_time = time.time()

View File

@@ -6,16 +6,19 @@ from __future__ import unicode_literals
from mach.decorators import (
CommandArgument,
CommandProvider,
Command,
)
from mozbuild.base import MachCommandBase
@CommandProvider
class ConditionsProvider(MachCommandBase):
@Command("cmd_foo", category="testing")
def run_foo(command_context):
def run_foo(self, command_context):
pass
@Command("cmd_bar", category="testing")
@CommandArgument("--baz", action="store_true", help="Run with baz")
def run_bar(command_context, baz=None):
def run_bar(self, command_context, baz=None):
pass

View File

@@ -8,8 +8,10 @@ from functools import partial
from mach.decorators import (
CommandArgument,
CommandProvider,
Command,
)
from mozbuild.base import MachCommandBase
def is_foo(cls):
@@ -22,17 +24,22 @@ def is_bar(val, cls):
return cls.bar == val
@CommandProvider
class MachCommands(MachCommandBase):
foo = True
bar = False
@Command("cmd_foo", category="testing")
@CommandArgument("--arg", default=None, help="Argument help.")
def run_foo(command_context):
def run_foo(self, command_context):
pass
@Command("cmd_bar", category="testing", conditions=[partial(is_bar, False)])
def run_bar(command_context):
def run_bar(self, command_context):
pass
@Command("cmd_foobar", category="testing", conditions=[is_foo, partial(is_bar, True)])
def run_foobar(command_context):
@Command(
"cmd_foobar", category="testing", conditions=[is_foo, partial(is_bar, True)]
)
def run_foobar(self, command_context):
pass

View File

@@ -6,55 +6,50 @@ from __future__ import absolute_import
from __future__ import unicode_literals
from mach.decorators import (
CommandProvider,
Command,
)
from mozbuild.base import MachCommandBase
def is_true(cls):
return True
def is_false(cls):
return False
@Command("cmd_condition_true", category="testing", conditions=[is_true])
def run_condition_true(self, command_context):
pass
@Command("cmd_condition_false", category="testing", conditions=[is_false])
def run_condition_false(self, command_context):
pass
@Command(
"cmd_condition_true_and_false", category="testing", conditions=[is_true, is_false]
)
def run_condition_true_and_false(self, command_context):
pass
def is_ctx_foo(cls):
def is_foo(cls):
"""Foo must be true"""
return cls._mach_context.foo
return cls.foo
def is_ctx_bar(cls):
def is_bar(cls):
"""Bar must be true"""
return cls._mach_context.bar
return cls.bar
@Command("cmd_foo_ctx", category="testing", conditions=[is_ctx_foo])
def run_foo_ctx(self, command_context):
@CommandProvider
class ConditionsProvider(MachCommandBase):
foo = True
bar = False
@Command("cmd_foo", category="testing", conditions=[is_foo])
def run_foo(self, command_context):
pass
@Command("cmd_bar", category="testing", conditions=[is_bar])
def run_bar(self, command_context):
pass
@Command("cmd_foobar", category="testing", conditions=[is_foo, is_bar])
def run_foobar(self, command_context):
pass
@Command("cmd_bar_ctx", category="testing", conditions=[is_ctx_bar])
def run_bar_ctx(self, command_context):
@CommandProvider
class ConditionsContextProvider(MachCommandBase):
@Command("cmd_foo_ctx", category="testing", conditions=[is_foo])
def run_foo(self, command_context):
pass
@Command("cmd_foobar_ctx", category="testing", conditions=[is_ctx_foo, is_ctx_bar])
def run_foobar_ctx(self, command_context):
@Command("cmd_bar_ctx", category="testing", conditions=[is_bar])
def run_bar(self, command_context):
pass
@Command("cmd_foobar_ctx", category="testing", conditions=[is_foo, is_bar])
def run_foobar(self, command_context):
pass

View File

@@ -6,10 +6,14 @@ from __future__ import absolute_import
from __future__ import unicode_literals
from mach.decorators import (
CommandProvider,
Command,
)
from mozbuild.base import MachCommandBase
@CommandProvider
class ConditionsProvider(MachCommandBase):
@Command("cmd_foo", category="testing", conditions=["invalid"])
def run_foo(command_context):
def run_foo(self, command_context):
pass

View File

@@ -7,18 +7,21 @@ from __future__ import unicode_literals
from mach.decorators import (
CommandArgument,
CommandProvider,
Command,
)
from mach.test.providers import throw2
from mozbuild.base import MachCommandBase
@CommandProvider
class TestCommandProvider(MachCommandBase):
@Command("throw", category="testing")
@CommandArgument("--message", "-m", default="General Error")
def throw(command_context, message):
def throw(self, command_context, message):
raise Exception(message)
@Command("throw_deep", category="testing")
@CommandArgument("--message", "-m", default="General Error")
def throw_deep(command_context, message):
def throw_deep(self, command_context, message):
throw2.throw_deep(message)

View File

@@ -49,7 +49,7 @@ class TestConditions(TestBase):
def test_conditions_pass(self):
"""Test that a command which passes its conditions is runnable."""
self.assertEquals((0, "", ""), self._run(["cmd_condition_true"]))
self.assertEquals((0, "", ""), self._run(["cmd_foo"]))
self.assertEquals((0, "", ""), self._run(["cmd_foo_ctx"], _populate_context))
def test_invalid_context_message(self):
@@ -61,7 +61,7 @@ class TestConditions(TestBase):
fail_conditions = [is_bar]
for name in ("cmd_condition_false", "cmd_condition_true_and_false"):
for name in ("cmd_bar", "cmd_foobar"):
result, stdout, stderr = self._run([name])
self.assertEquals(1, result)
@@ -90,9 +90,9 @@ class TestConditions(TestBase):
"""Test that commands that are not runnable do not show up in help."""
result, stdout, stderr = self._run(["help"], _populate_context)
self.assertIn("cmd_condition_true", stdout)
self.assertNotIn("cmd_condition_false", stdout)
self.assertNotIn("cmd_condition_true_and_false", stdout)
self.assertIn("cmd_foo", stdout)
self.assertNotIn("cmd_bar", stdout)
self.assertNotIn("cmd_foobar", stdout)
self.assertIn("cmd_foo_ctx", stdout)
self.assertNotIn("cmd_bar_ctx", stdout)
self.assertNotIn("cmd_foobar_ctx", stdout)

View File

@@ -9,12 +9,13 @@ import os
import pytest
from unittest.mock import Mock
from mozbuild.base import MachCommandBase
from mozunit import main
import mach.registrar
import mach.decorators
from mach.base import MachError
from mach.decorators import CommandArgument, Command, SubCommand
from mach.decorators import CommandArgument, CommandProvider, Command, SubCommand
@pytest.fixture
@@ -32,9 +33,11 @@ def test_register_command_with_argument(registrar):
context = Mock()
context.cwd = "."
@CommandProvider
class CommandFoo(MachCommandBase):
@Command("cmd_foo", category="testing")
@CommandArgument("--arg", default=None, help="Argument help.")
def run_foo(command_context, arg):
def run_foo(self, command_context, arg):
inner_function(arg)
registrar.dispatch("cmd_foo", context, arg="argument")
@@ -50,12 +53,14 @@ def test_register_command_with_metrics_path(registrar):
metrics_mock = Mock()
context.telemetry.metrics.return_value = metrics_mock
@CommandProvider
class CommandFoo(MachCommandBase):
@Command("cmd_foo", category="testing", metrics_path=metrics_path)
def run_foo(command_context):
def run_foo(self, command_context):
assert command_context.metrics == metrics_mock
@SubCommand("cmd_foo", "sub_foo", metrics_path=metrics_path + "2")
def run_subfoo(command_context):
def run_subfoo(self, command_context):
assert command_context.metrics == metrics_mock
registrar.dispatch("cmd_foo", context)
@@ -73,10 +78,12 @@ def test_register_command_sets_up_class_at_runtime(registrar):
context = Mock()
context.cwd = "."
# We test that the virtualenv is set up properly dynamically on
# the instance that actually runs the command.
# Inside the following class, we test that the virtualenv is set up properly
# dynamically on the instance that actually runs the command.
@CommandProvider
class CommandFoo(MachCommandBase):
@Command("cmd_foo", category="testing", virtualenv_name="env_foo")
def run_foo(command_context):
def run_foo(self, command_context):
assert (
os.path.basename(command_context.virtualenv_manager.virtualenv_root)
== "env_foo"
@@ -84,7 +91,7 @@ def test_register_command_sets_up_class_at_runtime(registrar):
inner_function("foo")
@Command("cmd_bar", category="testing", virtualenv_name="env_bar")
def run_bar(command_context):
def run_bar(self, command_context):
assert (
os.path.basename(command_context.virtualenv_manager.virtualenv_root)
== "env_bar"
@@ -100,16 +107,20 @@ def test_register_command_sets_up_class_at_runtime(registrar):
def test_cannot_create_command_nonexisting_category(registrar):
with pytest.raises(MachError):
@CommandProvider
class CommandFoo(MachCommandBase):
@Command("cmd_foo", category="bar")
def run_foo(command_context):
def run_foo(self, command_context):
pass
def test_subcommand_requires_parent_to_exist(registrar):
with pytest.raises(MachError):
@CommandProvider
class CommandFoo(MachCommandBase):
@SubCommand("sub_foo", "foo")
def run_foo(command_context):
def run_foo(self, command_context):
pass

View File

@@ -20,13 +20,16 @@ from mozfile import which
from manifestparser import TestManifest
from manifestparser import filters as mpf
from mozbuild.base import MachCommandBase
from mach.decorators import CommandArgument, Command
from mach.decorators import CommandArgument, CommandProvider, Command
from mach.util import UserError
here = os.path.abspath(os.path.dirname(__file__))
@CommandProvider
class MachCommands(MachCommandBase):
@Command("python", category="devenv", description="Run Python.")
@CommandArgument(
"--no-virtualenv", action="store_true", help="Do not set up a virtualenv"
@@ -50,6 +53,7 @@ here = os.path.abspath(os.path.dirname(__file__))
)
@CommandArgument("args", nargs=argparse.REMAINDER)
def python(
self,
command_context,
no_virtualenv,
no_activate,
@@ -110,7 +114,6 @@ def python(
append_env=append_env,
)
@Command(
"python-test",
category="testing",
@@ -161,21 +164,21 @@ def python(
"passed as it is to pytest"
),
)
def python_test(command_context, *args, **kwargs):
def python_test(self, command_context, *args, **kwargs):
try:
tempdir = str(tempfile.mkdtemp(suffix="-python-test"))
if six.PY2:
os.environ[b"PYTHON_TEST_TMP"] = tempdir
else:
os.environ["PYTHON_TEST_TMP"] = tempdir
return run_python_tests(command_context, *args, **kwargs)
return self.run_python_tests(command_context, *args, **kwargs)
finally:
import mozfile
mozfile.remove(tempdir)
def run_python_tests(
self,
command_context,
tests=None,
test_objects=None,
@@ -276,7 +279,9 @@ def run_python_tests(
with ThreadPoolExecutor(max_workers=jobs) as executor:
futures = [
executor.submit(_run_python_test, command_context, test, jobs, verbose)
executor.submit(
self._run_python_test, command_context, test, jobs, verbose
)
for test in parallel
]
@@ -292,7 +297,7 @@ def run_python_tests(
for test in sequential:
return_code = on_test_finished(
_run_python_test(command_context, test, jobs, verbose)
self._run_python_test(command_context, test, jobs, verbose)
)
if return_code and exitfirst:
break
@@ -305,8 +310,7 @@ def run_python_tests(
)
return return_code
def _run_python_test(command_context, test, jobs, verbose):
def _run_python_test(self, command_context, test, jobs, verbose):
from mozprocess import ProcessHandler
output = []
@@ -325,7 +329,9 @@ def _run_python_test(command_context, test, jobs, verbose):
def _line_handler(line):
line = six.ensure_str(line)
if not file_displayed_test:
output = "Ran" in line or "collected" in line or line.startswith("TEST-")
output = (
"Ran" in line or "collected" in line or line.startswith("TEST-")
)
if output:
file_displayed_test.append(True)

View File

@@ -7,10 +7,13 @@ from __future__ import absolute_import, print_function, unicode_literals
import errno
import sys
from mach.decorators import CommandArgument, Command
from mach.decorators import CommandArgument, CommandProvider, Command
from mozbuild.base import MachCommandBase
from mozboot.bootstrap import APPLICATIONS
@CommandProvider
class Bootstrap(MachCommandBase):
@Command(
"bootstrap",
category="devenv",
@@ -27,9 +30,11 @@ from mozboot.bootstrap import APPLICATIONS
"--no-system-changes",
dest="no_system_changes",
action="store_true",
help="Only execute actions that leave the system configuration alone.",
help="Only execute actions that leave the system " "configuration alone.",
)
def bootstrap(command_context, application_choice=None, no_system_changes=False):
def bootstrap(
self, command_context, application_choice=None, no_system_changes=False
):
"""Bootstrap system and mach for optimal development experience."""
from mozboot.bootstrap import Bootstrapper
@@ -42,6 +47,8 @@ def bootstrap(command_context, application_choice=None, no_system_changes=False)
bootstrapper.bootstrap(command_context.settings)
@CommandProvider
class VersionControlCommands(MachCommandBase):
@Command(
"vcs-setup",
category="devenv",
@@ -53,7 +60,7 @@ def bootstrap(command_context, application_choice=None, no_system_changes=False)
action="store_true",
help="Only update recommended extensions, don't run the wizard.",
)
def vcs_setup(command_context, update_only=False):
def vcs_setup(self, command_context, update_only=False):
"""Ensure a Version Control System (Mercurial or Git) is optimally
configured.
@@ -72,7 +79,9 @@ def vcs_setup(command_context, update_only=False):
import mozversioncontrol
from mozfile import which
repo = mozversioncontrol.get_repository_object(command_context._mach_context.topdir)
repo = mozversioncontrol.get_repository_object(
command_context._mach_context.topdir
)
tool = "hg"
if repo.name == "git":
tool = "git"
@@ -105,4 +114,6 @@ def vcs_setup(command_context, update_only=False):
command_context._mach_context.topdir,
)
else:
bootstrap.configure_mercurial(vcs, command_context._mach_context.state_dir)
bootstrap.configure_mercurial(
vcs, command_context._mach_context.state_dir
)

View File

@@ -13,9 +13,9 @@ import six
from collections import OrderedDict
from mach.decorators import CommandArgument, Command, SubCommand
from mach.decorators import CommandArgument, CommandProvider, Command, SubCommand
from mozbuild.artifact_builds import JOB_CHOICES
from mozbuild.base import MachCommandConditions as conditions
from mozbuild.base import MachCommandBase, MachCommandConditions as conditions
from mozbuild.util import ensureParentDir
import mozversioncontrol
@@ -55,15 +55,16 @@ class ArtifactSubCommand(SubCommand):
return after
# Fetch and install binary artifacts from Mozilla automation.
@CommandProvider
class PackageFrontend(MachCommandBase):
"""Fetch and install binary artifacts from Mozilla automation."""
@Command(
"artifact",
category="post-build",
description="Use pre-built artifacts to build Firefox.",
)
def artifact(command_context):
def artifact(self, command_context):
"""Download, cache, and install pre-built binary artifacts to build Firefox.
Use |mach build| as normal to freshen your installed binary libraries:
@@ -77,8 +78,8 @@ def artifact(command_context):
"""
pass
def _make_artifacts(
self,
command_context,
tree=None,
job=None,
@@ -101,7 +102,9 @@ def _make_artifacts(
git = command_context.substs["GIT"]
# If we're building Thunderbird, we should be checking for comm-central artifacts.
topsrcdir = command_context.substs.get("commtopsrcdir", command_context.topsrcdir)
topsrcdir = command_context.substs.get(
"commtopsrcdir", command_context.topsrcdir
)
if download_maven_zip:
if download_tests:
@@ -135,7 +138,6 @@ def _make_artifacts(
)
return artifacts
@ArtifactSubCommand("artifact", "install", "Install a good pre-built artifact.")
@CommandArgument(
"source",
@@ -154,7 +156,9 @@ def _make_artifacts(
default=False,
)
@CommandArgument("--no-tests", action="store_true", help="Don't install tests.")
@CommandArgument("--symbols", nargs="?", action=SymbolsAction, help="Download symbols.")
@CommandArgument(
"--symbols", nargs="?", action=SymbolsAction, help="Download symbols."
)
@CommandArgument("--host-bins", action="store_true", help="Download host binaries.")
@CommandArgument("--distdir", help="Where to install artifacts to.")
@CommandArgument(
@@ -166,6 +170,7 @@ def _make_artifacts(
"--maven-zip", action="store_true", help="Download Maven zip (Android-only)."
)
def artifact_install(
self,
command_context,
source=None,
skip_cache=False,
@@ -180,7 +185,7 @@ def artifact_install(
maven_zip=False,
):
command_context._set_log_level(verbose)
artifacts = _make_artifacts(
artifacts = self._make_artifacts(
command_context,
tree=tree,
job=job,
@@ -194,21 +199,21 @@ def artifact_install(
return artifacts.install_from(source, distdir or command_context.distdir)
@ArtifactSubCommand(
"artifact",
"clear-cache",
"Delete local artifacts and reset local artifact cache.",
)
def artifact_clear_cache(command_context, tree=None, job=None, verbose=False):
def artifact_clear_cache(self, command_context, tree=None, job=None, verbose=False):
command_context._set_log_level(verbose)
artifacts = _make_artifacts(command_context, tree=tree, job=job)
artifacts = self._make_artifacts(command_context, tree=tree, job=job)
artifacts.clear_cache()
return 0
@SubCommand("artifact", "toolchain")
@CommandArgument("--verbose", "-v", action="store_true", help="Print verbose output.")
@CommandArgument(
"--verbose", "-v", action="store_true", help="Print verbose output."
)
@CommandArgument(
"--cache-dir",
metavar="DIR",
@@ -250,6 +255,7 @@ def artifact_clear_cache(command_context, tree=None, job=None, verbose=False):
help="Store a manifest about the downloaded taskcluster artifacts",
)
def artifact_toolchain(
self,
command_context,
verbose=False,
cache_dir=None,
@@ -288,7 +294,9 @@ def artifact_toolchain(
command_context.log_manager.structured_filter
)
if not cache_dir:
cache_dir = os.path.join(command_context._mach_context.state_dir, "toolchains")
cache_dir = os.path.join(
command_context._mach_context.state_dir, "toolchains"
)
tooltool_host = os.environ.get("TOOLTOOL_HOST", "tooltool.mozilla-releng.net")
taskcluster_proxy_url = os.environ.get("TASKCLUSTER_PROXY_URL")

View File

@@ -9,14 +9,17 @@ import logging
import os
import subprocess
from mozbuild import build_commands
from mozbuild.base import MachCommandBase
from mozbuild.build_commands import Build
from mozfile import which
from mach.decorators import CommandArgument, Command
from mach.decorators import CommandArgument, CommandProvider, Command
import mozpack.path as mozpath
@CommandProvider
class MachCommands(MachCommandBase):
@Command(
"ide",
category="devenv",
@@ -25,7 +28,7 @@ import mozpack.path as mozpath
)
@CommandArgument("ide", choices=["eclipse", "visualstudio", "vscode"])
@CommandArgument("args", nargs=argparse.REMAINDER)
def run(command_context, ide, args):
def run(self, command_context, ide, args):
if ide == "eclipse":
backend = "CppEclipse"
elif ide == "visualstudio":
@@ -50,7 +53,7 @@ def run(command_context, ide, args):
if ide == "vscode":
# Check if platform has VSCode installed
vscode_cmd = find_vscode_cmd(command_context)
vscode_cmd = self.find_vscode_cmd(command_context)
if vscode_cmd is None:
choice = prompt_bool(
"VSCode cannot be found, and may not be installed. Proceed?"
@@ -58,13 +61,15 @@ def run(command_context, ide, args):
if not choice:
return 1
rc = build_commands.configure(command_context)
# Create the Build environment to configure the tree
builder = Build(command_context._mach_context, None)
rc = builder.configure(command_context)
if rc != 0:
return rc
# First install what we can through install manifests.
rc = build_commands._run_make(
rc = builder._run_make(
directory=command_context.topobjdir,
target="pre-export",
line_handler=None,
@@ -75,7 +80,7 @@ def run(command_context, ide, args):
# Then build the rest of the build dependencies by running the full
# export target, because we can't do anything better.
for target in ("export", "pre-compile"):
rc = build_commands._run_make(
rc = builder._run_make(
directory=command_context.topobjdir,
target=target,
line_handler=None,
@@ -103,30 +108,29 @@ def run(command_context, ide, args):
return 1
if ide == "eclipse":
eclipse_workspace_dir = get_eclipse_workspace_path(command_context)
eclipse_workspace_dir = self.get_eclipse_workspace_path(command_context)
subprocess.check_call(["eclipse", "-data", eclipse_workspace_dir])
elif ide == "visualstudio":
visual_studio_workspace_dir = get_visualstudio_workspace_path(command_context)
visual_studio_workspace_dir = self.get_visualstudio_workspace_path(
command_context
)
subprocess.call(["explorer.exe", visual_studio_workspace_dir])
elif ide == "vscode":
return setup_vscode(command_context, vscode_cmd)
return self.setup_vscode(command_context, vscode_cmd)
def get_eclipse_workspace_path(command_context):
def get_eclipse_workspace_path(self, command_context):
from mozbuild.backend.cpp_eclipse import CppEclipseBackend
return CppEclipseBackend.get_workspace_path(
command_context.topsrcdir, command_context.topobjdir
)
def get_visualstudio_workspace_path(command_context):
def get_visualstudio_workspace_path(self, command_context):
return os.path.normpath(
os.path.join(command_context.topobjdir, "msvc", "mozilla.sln")
)
def find_vscode_cmd(command_context):
def find_vscode_cmd(self, command_context):
import shutil
# Try to look up the `code` binary on $PATH, and use it if present. This
@@ -193,8 +197,7 @@ def find_vscode_cmd(command_context):
# Path cannot be found
return None
def setup_vscode(command_context, vscode_cmd):
def setup_vscode(self, command_context, vscode_cmd):
vscode_settings = mozpath.join(
command_context.topsrcdir, ".vscode", "settings.json"
)
@@ -219,7 +222,7 @@ def setup_vscode(command_context, vscode_cmd):
{},
"Unable to locate clangd in {}.".format(clang_tidy_bin),
)
rc = _get_clang_tools(command_context, clang_tools_path)
rc = self._get_clang_tools(command_context, clang_tools_path)
if rc != 0:
return rc
@@ -335,8 +338,7 @@ def setup_vscode(command_context, vscode_cmd):
return 0
def _get_clang_tools(command_context, clang_tools_path):
def _get_clang_tools(self, command_context, clang_tools_path):
import shutil

View File

@@ -8,8 +8,9 @@ import argparse
import os
import subprocess
from mach.decorators import CommandArgument, Command
from mach.decorators import CommandArgument, CommandProvider, Command
from mozbuild.base import MachCommandBase
from mozbuild.util import MOZBUILD_METRICS_PATH
from mozbuild.mozconfig import MozconfigLoader
import mozpack.path as mozpath
@@ -68,8 +69,9 @@ def _set_priority(priority, verbose):
return True
# Interface to build the tree.
@CommandProvider
class Build(MachCommandBase):
"""Interface to build the tree."""
@Command(
"build",
@@ -121,6 +123,7 @@ def _set_priority(priority, verbose):
help="idle/less/normal/more/high. (Default less)",
)
def build(
self,
command_context,
what=None,
jobs=0,
@@ -169,7 +172,9 @@ def build(
if doing_pgo:
if what:
raise Exception("Cannot specify targets (%s) in MOZ_PGO=1 builds" % what)
raise Exception(
"Cannot specify targets (%s) in MOZ_PGO=1 builds" % what
)
instr = command_context._spawn(BuildDriver)
orig_topobjdir = instr._topobjdir
instr._topobjdir = mozpath.join(instr._topobjdir, "instrumented")
@@ -229,7 +234,6 @@ def build(
append_env=append_env,
)
@Command(
"configure",
category="build",
@@ -241,6 +245,7 @@ def build(
"options", default=None, nargs=argparse.REMAINDER, help="Configure options"
)
def configure(
self,
command_context,
options=None,
buildstatus_messages=False,
@@ -258,7 +263,6 @@ def configure(
line_handler=line_handler,
)
@Command(
"resource-usage",
category="post-build",
@@ -282,7 +286,9 @@ def configure(
help="Web browser to automatically open. See webbrowser Python module.",
)
@CommandArgument("--url", help="URL of JSON document to display")
def resource_usage(command_context, address=None, port=None, browser=None, url=None):
def resource_usage(
self, command_context, address=None, port=None, browser=None, url=None
):
import webbrowser
from mozbuild.html_build_viewer import BuildViewerServer
@@ -313,14 +319,15 @@ def resource_usage(command_context, address=None, port=None, browser=None, url=N
print("Hit CTRL+c to stop server.")
server.run()
@Command(
"build-backend",
category="build",
description="Generate a backend used to build the tree.",
virtualenv_name="build",
)
@CommandArgument("-d", "--diff", action="store_true", help="Show a diff of changes.")
@CommandArgument(
"-d", "--diff", action="store_true", help="Show a diff of changes."
)
# It would be nice to filter the choices below based on
# conditions, but that is for another day.
@CommandArgument(
@@ -337,7 +344,9 @@ def resource_usage(command_context, address=None, port=None, browser=None, url=N
action="store_true",
help="Do everything except writing files out.",
)
def build_backend(command_context, backend, diff=False, verbose=False, dry_run=False):
def build_backend(
self, command_context, backend, diff=False, verbose=False, dry_run=False
):
python = command_context.virtualenv_manager.python_path
config_status = os.path.join(command_context.topobjdir, "config.status")

File diff suppressed because it is too large Load Diff

View File

@@ -6,13 +6,15 @@
from __future__ import absolute_import, print_function
from mach.decorators import CommandArgument, Command
from mach.decorators import CommandArgument, CommandProvider, Command
from mozbuild.base import MachCommandBase
from mozbuild.shellutil import split as shell_split, quote as shell_quote
# Instropection commands.
@CommandProvider
class Introspection(MachCommandBase):
"""Instropection commands."""
@Command(
"compileflags",
@@ -22,7 +24,7 @@ from mozbuild.shellutil import split as shell_split, quote as shell_quote
@CommandArgument(
"what", default=None, help="Source file to display compilation flags for"
)
def compileflags(command_context, what):
def compileflags(self, command_context, what):
from mozbuild.util import resolve_target_to_make
from mozbuild.compilation import util
@@ -38,7 +40,7 @@ def compileflags(command_context, what):
if make_dir is None and make_target is None:
return 1
build_vars = util.get_build_vars(make_dir, command_context)
build_vars = util.get_build_vars(make_dir, self)
if what.endswith(".c"):
cc = "CC"

View File

@@ -9,8 +9,9 @@ import json
import os
import sys
from mach.decorators import CommandArgument, Command, SubCommand
from mach.decorators import CommandArgument, CommandProvider, Command, SubCommand
from mozbuild.base import MachCommandBase
import mozpack.path as mozpath
TOPSRCDIR = os.path.abspath(os.path.join(__file__, "../../../../../"))
@@ -20,6 +21,8 @@ class InvalidPathException(Exception):
"""Represents an error due to an invalid path."""
@CommandProvider
class MozbuildFileCommands(MachCommandBase):
@Command(
"mozbuild-reference",
category="build-dev",
@@ -38,7 +41,7 @@ class InvalidPathException(Exception):
action="store_true",
help="Print symbol names only.",
)
def reference(command_context, symbol, name_only=False):
def reference(self, command_context, symbol, name_only=False):
# mozbuild.sphinx imports some Sphinx modules, so we need to be sure
# the optional Sphinx package is installed.
command_context.activate_virtualenv()
@@ -90,24 +93,24 @@ def reference(command_context, symbol, name_only=False):
return 0
@Command(
"file-info", category="build-dev", description="Query for metadata about files."
)
def file_info(command_context):
def file_info(self, command_context):
"""Show files metadata derived from moz.build files.
moz.build files contain "Files" sub-contexts for declaring metadata
against file patterns. This command suite is used to query that data.
"""
@SubCommand(
"file-info",
"bugzilla-component",
"Show Bugzilla component info for files listed.",
)
@CommandArgument("-r", "--rev", help="Version control revision to look up info from")
@CommandArgument(
"-r", "--rev", help="Version control revision to look up info from"
)
@CommandArgument(
"--format",
choices={"json", "plain"},
@@ -116,7 +119,7 @@ def file_info(command_context):
dest="fmt",
)
@CommandArgument("paths", nargs="+", help="Paths whose data to query")
def file_info_bugzilla(command_context, paths, rev=None, fmt=None):
def file_info_bugzilla(self, command_context, paths, rev=None, fmt=None):
"""Show Bugzilla component for a set of files.
Given a requested set of files (which can be specified using
@@ -124,7 +127,7 @@ def file_info_bugzilla(command_context, paths, rev=None, fmt=None):
"""
components = defaultdict(set)
try:
for p, m in _get_files_info(command_context, paths, rev=rev).items():
for p, m in self._get_files_info(command_context, paths, rev=rev).items():
components[m.get("BUG_COMPONENT")].add(p)
except InvalidPathException as e:
print(e)
@@ -158,11 +161,12 @@ def file_info_bugzilla(command_context, paths, rev=None, fmt=None):
print("unhandled output format: %s" % fmt)
return 1
@SubCommand(
"file-info", "missing-bugzilla", "Show files missing Bugzilla component info"
)
@CommandArgument("-r", "--rev", help="Version control revision to look up info from")
@CommandArgument(
"-r", "--rev", help="Version control revision to look up info from"
)
@CommandArgument(
"--format",
choices={"json", "plain"},
@@ -171,11 +175,11 @@ def file_info_bugzilla(command_context, paths, rev=None, fmt=None):
help="Output format",
)
@CommandArgument("paths", nargs="+", help="Paths whose data to query")
def file_info_missing_bugzilla(command_context, paths, rev=None, fmt=None):
def file_info_missing_bugzilla(self, command_context, paths, rev=None, fmt=None):
missing = set()
try:
for p, m in _get_files_info(command_context, paths, rev=rev).items():
for p, m in self._get_files_info(command_context, paths, rev=rev).items():
if "BUG_COMPONENT" not in m:
missing.add(p)
except InvalidPathException as e:
@@ -192,14 +196,13 @@ def file_info_missing_bugzilla(command_context, paths, rev=None, fmt=None):
print("unhandled output format: %s" % fmt)
return 1
@SubCommand(
"file-info",
"bugzilla-automation",
"Perform Bugzilla metadata analysis as required for automation",
)
@CommandArgument("out_dir", help="Where to write files")
def bugzilla_automation(command_context, out_dir):
def bugzilla_automation(self, command_context, out_dir):
"""Analyze and validate Bugzilla metadata as required by automation.
This will write out JSON and gzipped JSON files for Bugzilla metadata.
@@ -215,7 +218,7 @@ def bugzilla_automation(command_context, out_dir):
# TODO operate in VCS space. This requires teaching the VCS reader
# to understand wildcards and/or for the relative path issue in the
# VCS finder to be worked out.
for p, m in sorted(_get_files_info(command_context, ["**"]).items()):
for p, m in sorted(self._get_files_info(command_context, ["**"]).items()):
if "BUG_COMPONENT" not in m:
missing_component.add(p)
print(
@@ -281,8 +284,7 @@ def bugzilla_automation(command_context, out_dir):
if missing_component:
return 1
def _get_files_info(command_context, paths, rev=None):
def _get_files_info(self, command_context, paths, rev=None):
reader = command_context.mozbuild_reader(config_mode="empty", vcs_revision=rev)
# Normalize to relative from topsrcdir.
@@ -311,7 +313,9 @@ def _get_files_info(command_context, paths, rev=None):
continue
if rev:
raise InvalidPathException("cannot use wildcard in version control mode")
raise InvalidPathException(
"cannot use wildcard in version control mode"
)
# finder is rooted at / for now.
# TODO bug 1171069 tracks changing to relative.
@@ -324,12 +328,11 @@ def _get_files_info(command_context, paths, rev=None):
return reader.files_info(allpaths)
@SubCommand(
"file-info", "schedules", "Show the combined SCHEDULES for the files listed."
)
@CommandArgument("paths", nargs="+", help="Paths whose data to query")
def file_info_schedules(command_context, paths):
def file_info_schedules(self, command_context, paths):
"""Show what is scheduled by the given files.
Given a requested set of files (which can be specified using

View File

@@ -24,6 +24,7 @@ import mozpack.path as mozpath
from mach.decorators import (
CommandArgument,
CommandArgumentGroup,
CommandProvider,
Command,
SettingsProvider,
SubCommand,
@@ -32,6 +33,7 @@ from mach.decorators import (
from mozbuild.base import (
BinaryNotFoundException,
BuildEnvironmentNotFoundException,
MachCommandBase,
MachCommandConditions as conditions,
MozbuildObject,
)
@@ -65,6 +67,8 @@ class StoreDebugParamsAndWarnAction(argparse.Action):
setattr(namespace, self.dest, values)
@CommandProvider
class Watch(MachCommandBase):
@Command(
"watch",
category="post-build",
@@ -77,7 +81,7 @@ class StoreDebugParamsAndWarnAction(argparse.Action):
action="store_true",
help="Verbose output for what commands the watcher is running.",
)
def watch(command_context, verbose=False):
def watch(self, command_context, verbose=False):
"""Watch and re-build (parts of) the source tree."""
if not conditions.is_artifact_build(command_context):
print(
@@ -112,13 +116,14 @@ def watch(command_context, verbose=False):
sys.exit(3)
@CommandProvider
class CargoProvider(MachCommandBase):
@Command("cargo", category="build", description="Invoke cargo in useful ways.")
def cargo(command_context):
def cargo(self, command_context):
"""Invoke cargo in useful ways."""
command_context._sub_mach(["help", "cargo"])
self._sub_mach(["help", "cargo"])
return 1
@SubCommand(
"cargo",
"check",
@@ -130,7 +135,9 @@ def cargo(command_context):
action="store_true",
help="Check all of the crates in the tree.",
)
@CommandArgument("crates", default=None, nargs="*", help="The crate name(s) to check.")
@CommandArgument(
"crates", default=None, nargs="*", help="The crate name(s) to check."
)
@CommandArgument(
"--jobs",
"-j",
@@ -141,7 +148,9 @@ def cargo(command_context):
help="Run the tests in parallel using multiple processes.",
)
@CommandArgument("-v", "--verbose", action="store_true", help="Verbose output.")
def check(command_context, all_crates=None, crates=None, jobs=0, verbose=False):
def check(
self, command_context, all_crates=None, crates=None, jobs=0, verbose=False
):
# XXX duplication with `mach vendor rust`
crates_and_roots = {
"gkrust": "toolkit/library/rust",
@@ -186,6 +195,8 @@ def check(command_context, all_crates=None, crates=None, jobs=0, verbose=False):
return 0
@CommandProvider
class Doctor(MachCommandBase):
@Command(
"doctor",
category="devenv",
@@ -203,7 +214,7 @@ def check(command_context, all_crates=None, crates=None, jobs=0, verbose=False):
action="store_true",
help="Print verbose information found by checks.",
)
def doctor(command_context, fix=False, verbose=False):
def doctor(self, command_context, fix=False, verbose=False):
"""Diagnose common build environment problems"""
command_context.activate_virtualenv()
from mozbuild.doctor import run_doctor
@@ -216,10 +227,11 @@ def doctor(command_context, fix=False, verbose=False):
)
@CommandProvider
class Clobber(MachCommandBase):
NO_AUTO_LOG = True
CLOBBER_CHOICES = {"objdir", "python", "gradle"}
@Command(
"clobber",
category="build",
@@ -233,7 +245,7 @@ CLOBBER_CHOICES = {"objdir", "python", "gradle"}
"objdir and python).".format(", ".join(CLOBBER_CHOICES)),
)
@CommandArgument("--full", action="store_true", help="Perform a full clobber")
def clobber(command_context, what, full=False):
def clobber(self, command_context, what, full=False):
"""Clean up the source and object directories.
Performing builds and running various commands generate various files.
@@ -255,11 +267,11 @@ def clobber(command_context, what, full=False):
By default, the command clobbers the `objdir` and `python` targets.
"""
what = set(what)
invalid = what - CLOBBER_CHOICES
invalid = what - self.CLOBBER_CHOICES
if invalid:
print(
"Unknown clobber target(s): {}. Choose from {{{}}}".format(
", ".join(invalid), ", ".join(CLOBBER_CHOICES)
", ".join(invalid), ", ".join(self.CLOBBER_CHOICES)
)
)
return 1
@@ -332,8 +344,10 @@ def clobber(command_context, what, full=False):
return ret
NO_AUTO_LOG = True
@CommandProvider
class Logs(MachCommandBase):
NO_AUTO_LOG = True
@Command("show-log", category="post-build", description="Display mach logs")
@CommandArgument(
@@ -343,7 +357,7 @@ NO_AUTO_LOG = True
help="Filename to read log data from. Defaults to the log of the last "
"mach command.",
)
def show_log(command_context, log_file=None):
def show_log(self, command_context, log_file=None):
"""Show mach logs."""
if not log_file:
path = command_context._get_state_filename("last_log.json")
@@ -368,7 +382,9 @@ def show_log(command_context, log_file=None):
created, action, params = json.loads(line)
if not startTime:
startTime = created
command_context.log_manager.terminal_handler.formatter.start_time = created
command_context.log_manager.terminal_handler.formatter.start_time = (
created
)
if "line" in params:
record = logging.makeLogRecord(
{
@@ -394,17 +410,17 @@ def show_log(command_context, log_file=None):
less.wait()
# Provide commands for inspecting warnings.
@CommandProvider
class Warnings(MachCommandBase):
"""Provide commands for inspecting warnings."""
def database_path(command_context):
def database_path(self, command_context):
return command_context._get_state_filename("warnings.json")
def get_warnings_database(command_context):
def get_warnings_database(self, command_context):
from mozbuild.compilation.warnings import WarningsDatabase
path = database_path(command_context)
path = self.database_path(command_context)
database = WarningsDatabase()
@@ -413,7 +429,6 @@ def get_warnings_database(command_context):
return database
@Command(
"warnings-summary",
category="post-build",
@@ -429,13 +444,14 @@ def get_warnings_database(command_context):
"report",
default=None,
nargs="?",
help="Warnings report to display. If not defined, show the most recent report.",
help="Warnings report to display. If not defined, show the most "
"recent report.",
)
def summary(command_context, directory=None, report=None):
database = get_warnings_database(command_context)
def summary(self, command_context, directory=None, report=None):
database = self.get_warnings_database(command_context)
if directory:
dirpath = join_ensure_dir(command_context.topsrcdir, directory)
dirpath = self.join_ensure_dir(command_context.topsrcdir, directory)
if not dirpath:
return 1
else:
@@ -451,7 +467,6 @@ def summary(command_context, directory=None, report=None):
print("%d\tTotal" % total)
@Command(
"warnings-list",
category="post-build",
@@ -470,10 +485,11 @@ def summary(command_context, directory=None, report=None):
"report",
default=None,
nargs="?",
help="Warnings report to display. If not defined, show the most recent report.",
help="Warnings report to display. If not defined, show the most "
"recent report.",
)
def list_warnings(command_context, directory=None, flags=None, report=None):
database = get_warnings_database(command_context)
def list_warnings(self, command_context, directory=None, flags=None, report=None):
database = self.get_warnings_database(command_context)
by_name = sorted(database.warnings)
@@ -481,7 +497,7 @@ def list_warnings(command_context, directory=None, flags=None, report=None):
if directory:
directory = mozpath.normsep(directory)
dirpath = join_ensure_dir(topsrcdir, directory)
dirpath = self.join_ensure_dir(topsrcdir, directory)
if not dirpath:
return 1
@@ -518,8 +534,7 @@ def list_warnings(command_context, directory=None, flags=None, report=None):
% (filename, warning["line"], warning["flag"], warning["message"])
)
def join_ensure_dir(dir1, dir2):
def join_ensure_dir(self, dir1, dir2):
dir1 = mozpath.normpath(dir1)
dir2 = mozpath.normsep(dir2)
joined_path = mozpath.join(dir1, dir2)
@@ -529,7 +544,11 @@ def join_ensure_dir(dir1, dir2):
return None
@Command("gtest", category="testing", description="Run GTest unit tests (C++ tests).")
@CommandProvider
class GTestCommands(MachCommandBase):
@Command(
"gtest", category="testing", description="Run GTest unit tests (C++ tests)."
)
@CommandArgument(
"gtest_filter",
default="*",
@@ -540,7 +559,9 @@ def join_ensure_dir(dir1, dir2):
"and another ':'-separated pattern list (called the negative patterns)."
"Test names are of the format SUITE.NAME. Use --list-tests to see all.",
)
@CommandArgument("--list-tests", action="store_true", help="list all available tests")
@CommandArgument(
"--list-tests", action="store_true", help="list all available tests"
)
@CommandArgument(
"--jobs",
"-j",
@@ -591,7 +612,7 @@ def join_ensure_dir(dir1, dir2):
"--remoteTestRoot",
dest="remote_test_root",
group="Android",
help="Remote directory to use as test root (eg. /data/local/tmp/test_root).",
help="Remote directory to use as test root " "(eg. /data/local/tmp/test_root).",
)
@CommandArgument(
"--libxul", dest="libxul_path", group="Android", help="Path to gtest libxul.so."
@@ -628,6 +649,7 @@ def join_ensure_dir(dir1, dir2):
"split as the Bourne shell would.",
)
def gtest(
self,
command_context,
shuffle,
jobs,
@@ -674,10 +696,12 @@ def gtest(
if jobs != 1:
print("--jobs is not supported on Android and will be ignored")
if debug or debugger or debugger_args:
print("--debug options are not supported on Android and will be ignored")
print(
"--debug options are not supported on Android and will be ignored"
)
from mozrunner.devices.android_device import InstallIntent
return android_gtest(
return self.android_gtest(
command_context,
cwd,
shuffle,
@@ -726,7 +750,9 @@ def gtest(
# Note: we must normalize the path here so that gtest on Windows sees
# a MOZ_GMP_PATH which has only Windows dir seperators, because
# nsIFile cannot open the paths with non-Windows dir seperators.
xre_path = os.path.join(os.path.normpath(command_context.topobjdir), "dist", "bin")
xre_path = os.path.join(
os.path.normpath(command_context.topobjdir), "dist", "bin"
)
gtest_env["MOZ_XRE_DIR"] = xre_path
gtest_env["MOZ_GMP_PATH"] = os.pathsep.join(
os.path.join(xre_path, p, "1.0") for p in ("gmp-fake", "gmp-fakeopenh264")
@@ -789,8 +815,8 @@ def gtest(
return exit_code
def android_gtest(
self,
command_context,
test_dir,
shuffle,
@@ -852,6 +878,8 @@ def android_gtest(
return exit_code
@CommandProvider
class Package(MachCommandBase):
@Command(
"package",
category="post-build",
@@ -863,7 +891,7 @@ def android_gtest(
action="store_true",
help="Verbose output for what commands the packaging process is running.",
)
def package(command_context, verbose=False):
def package(self, command_context, verbose=False):
"""Package the built product for distribution."""
ret = command_context._run_make(
directory=".", target="package", silent=not verbose, ensure_exit_code=False
@@ -878,7 +906,7 @@ def _get_android_install_parser():
parser.add_argument(
"--app",
default="org.mozilla.geckoview_example",
help="Android package to install (default: org.mozilla.geckoview_example)",
help="Android package to install " "(default: org.mozilla.geckoview_example)",
)
parser.add_argument(
"--verbose",
@@ -896,6 +924,8 @@ def setup_install_parser():
return argparse.ArgumentParser()
@CommandProvider
class Install(MachCommandBase):
@Command(
"install",
category="post-build",
@@ -903,7 +933,7 @@ def setup_install_parser():
parser=setup_install_parser,
description="Install the package on the machine (or device in the case of Android).",
)
def install(command_context, **kwargs):
def install(self, command_context, **kwargs):
"""Install a package."""
if conditions.is_android(command_context):
from mozrunner.devices.android_device import (
@@ -912,7 +942,9 @@ def install(command_context, **kwargs):
)
ret = (
verify_android_device(command_context, install=InstallIntent.YES, **kwargs)
verify_android_device(
command_context, install=InstallIntent.YES, **kwargs
)
== 0
)
else:
@@ -946,7 +978,7 @@ def _get_android_run_parser():
group.add_argument(
"--app",
default="org.mozilla.geckoview_example",
help="Android package to run (default: org.mozilla.geckoview_example)",
help="Android package to run " "(default: org.mozilla.geckoview_example)",
)
group.add_argument(
"--intent",
@@ -1205,6 +1237,8 @@ def setup_run_parser():
return _get_desktop_run_parser()
@CommandProvider
class RunProgram(MachCommandBase):
@Command(
"run",
category="post-build",
@@ -1212,16 +1246,16 @@ def setup_run_parser():
parser=setup_run_parser,
description="Run the compiled program, possibly under a debugger or DMD.",
)
def run(command_context, **kwargs):
def run(self, command_context, **kwargs):
"""Run the compiled program."""
if conditions.is_android(command_context):
return _run_android(command_context, **kwargs)
return self._run_android(command_context, **kwargs)
if conditions.is_jsshell(command_context):
return _run_jsshell(command_context, **kwargs)
return _run_desktop(command_context, **kwargs)
return self._run_jsshell(command_context, **kwargs)
return self._run_desktop(command_context, **kwargs)
def _run_android(
self,
command_context,
app="org.mozilla.geckoview_example",
intent=None,
@@ -1515,7 +1549,9 @@ platform connect {connect_url}
process attach {continue_flag}-p {pid!s}
""".lstrip()
obj_xul = os.path.join(command_context.topobjdir, "toolkit", "library", "build")
obj_xul = os.path.join(
command_context.topobjdir, "toolkit", "library", "build"
)
obj_mozglue = os.path.join(command_context.topobjdir, "mozglue", "build")
obj_nss = os.path.join(command_context.topobjdir, "security")
@@ -1567,12 +1603,13 @@ process attach {continue_flag}-p {pid!s}
if not use_existing_process:
device.shell("am clear-debug-app")
def _run_jsshell(command_context, params, debug, debugger, debugger_args):
def _run_jsshell(self, command_context, params, debug, debugger, debugger_args):
try:
binpath = command_context.get_binary_path("app")
except BinaryNotFoundException as e:
command_context.log(logging.ERROR, "run", {"error": str(e)}, "ERROR: {error}")
command_context.log(
logging.ERROR, "run", {"error": str(e)}, "ERROR: {error}"
)
command_context.log(logging.INFO, "run", {"help": e.help()}, "{help}")
return 1
@@ -1610,8 +1647,8 @@ def _run_jsshell(command_context, params, debug, debugger, debugger_args):
args=args, ensure_exit_code=False, pass_thru=True, append_env=extra_env
)
def _run_desktop(
self,
command_context,
params,
packaged,
@@ -1641,7 +1678,9 @@ def _run_desktop(
else:
binpath = app or command_context.get_binary_path("app")
except BinaryNotFoundException as e:
command_context.log(logging.ERROR, "run", {"error": str(e)}, "ERROR: {error}")
command_context.log(
logging.ERROR, "run", {"error": str(e)}, "ERROR: {error}"
)
if packaged:
command_context.log(
logging.INFO,
@@ -1824,18 +1863,22 @@ def _run_desktop(
)
@CommandProvider
class Buildsymbols(MachCommandBase):
@Command(
"buildsymbols",
category="post-build",
description="Produce a package of Breakpad-format symbols.",
)
def buildsymbols(command_context):
def buildsymbols(self, command_context):
"""Produce a package of debug symbols suitable for use with Breakpad."""
return command_context._run_make(
directory=".", target="buildsymbols", ensure_exit_code=False
)
@CommandProvider
class MachDebug(MachCommandBase):
@Command(
"environment",
category="build-dev",
@@ -1848,11 +1891,11 @@ def buildsymbols(command_context):
help="Print data in the given format.",
)
@CommandArgument("--output", "-o", type=str, help="Output to the given file.")
@CommandArgument("--verbose", "-v", action="store_true", help="Print verbose output.")
def environment(command_context, format, output=None, verbose=False):
func = {"pretty": _environment_pretty, "json": _environment_json}[
format.replace(".", "_")
]
@CommandArgument(
"--verbose", "-v", action="store_true", help="Print verbose output."
)
def environment(self, command_context, format, output=None, verbose=False):
func = getattr(self, "_environment_%s" % format.replace(".", "_"))
if output:
# We want to preserve mtimes if the output file already exists
@@ -1863,8 +1906,7 @@ def environment(command_context, format, output=None, verbose=False):
return func(command_context, out, verbose)
return func(command_context, sys.stdout, verbose)
def _environment_pretty(command_context, out, verbose):
def _environment_pretty(self, command_context, out, verbose):
state_dir = command_context._mach_context.state_dir
print("platform:\n\t%s" % platform.platform(), file=out)
@@ -1915,8 +1957,7 @@ def _environment_pretty(command_context, out, verbose):
for k in sorted(config.defines):
print("\t%s" % k, file=out)
def _environment_json(command_context, out, verbose):
def _environment_json(self, command_context, out, verbose):
import json
class EnvironmentEncoder(json.JSONEncoder):
@@ -1938,12 +1979,14 @@ def _environment_json(command_context, out, verbose):
json.dump(command_context, cls=EnvironmentEncoder, sort_keys=True, fp=out)
@CommandProvider
class Repackage(MachCommandBase):
@Command(
"repackage",
category="misc",
description="Repackage artifacts into different formats.",
)
def repackage(command_context):
def repackage(self, command_context):
"""Repackages artifacts into different formats.
This is generally used after packages are signed by the signing
@@ -1952,11 +1995,12 @@ def repackage(command_context):
"""
print("Usage: ./mach repackage [dmg|installer|mar] [args...]")
@SubCommand("repackage", "dmg", description="Repackage a tar file into a .dmg for OSX")
@SubCommand(
"repackage", "dmg", description="Repackage a tar file into a .dmg for OSX"
)
@CommandArgument("--input", "-i", type=str, required=True, help="Input filename")
@CommandArgument("--output", "-o", type=str, required=True, help="Output filename")
def repackage_dmg(command_context, input, output):
def repackage_dmg(self, command_context, input, output):
if not os.path.exists(input):
print("Input file does not exist: %s" % input)
return 1
@@ -1972,7 +2016,6 @@ def repackage_dmg(command_context, input, output):
repackage_dmg(input, output)
@SubCommand(
"repackage", "installer", description="Repackage into a Windows installer exe"
)
@@ -2011,6 +2054,7 @@ def repackage_dmg(command_context, input, output):
help="Run UPX on the self-extraction stub.",
)
def repackage_installer(
self,
command_context,
tag,
setupexe,
@@ -2033,7 +2077,6 @@ def repackage_installer(
use_upx=use_upx,
)
@SubCommand("repackage", "msi", description="Repackage into a MSI")
@CommandArgument(
"--wsx",
@@ -2054,10 +2097,15 @@ def repackage_installer(
"--arch", type=str, required=True, help="The architecture you are building."
)
@CommandArgument("--setupexe", type=str, required=True, help="setup.exe installer")
@CommandArgument("--candle", type=str, required=False, help="location of candle binary")
@CommandArgument("--light", type=str, required=False, help="location of light binary")
@CommandArgument(
"--candle", type=str, required=False, help="location of candle binary"
)
@CommandArgument(
"--light", type=str, required=False, help="location of light binary"
)
@CommandArgument("--output", "-o", type=str, required=True, help="Output filename")
def repackage_msi(
self,
command_context,
wsx,
version,
@@ -2082,7 +2130,6 @@ def repackage_msi(
output=output,
)
@SubCommand("repackage", "msix", description="Repackage into an MSIX")
@CommandArgument(
"--input",
@@ -2173,6 +2220,7 @@ def repackage_msi(
"(Default: false)",
)
def repackage_msix(
self,
command_context,
input,
version=None,
@@ -2233,8 +2281,9 @@ def repackage_msix(
)
if sign:
repackage_sign_msix(command_context, output, force=False, verbose=verbose)
self.repackage_sign_msix(
command_context, output, force=False, verbose=verbose
)
@SubCommand("repackage", "sign-msix", description="Sign an MSIX for local testing")
@CommandArgument("--input", type=str, required=True, help="MSIX to sign.")
@@ -2250,7 +2299,7 @@ def repackage_msix(
action="store_true",
help="Be verbose. (Default: false)",
)
def repackage_sign_msix(command_context, input, force=False, verbose=False):
def repackage_sign_msix(self, command_context, input, force=False, verbose=False):
from mozbuild.repackaging.msix import sign_msix
command_context._set_log_level(verbose)
@@ -2259,7 +2308,6 @@ def repackage_sign_msix(command_context, input, force=False, verbose=False):
return 0
@SubCommand("repackage", "mar", description="Repackage into complete MAR file")
@CommandArgument("--input", "-i", type=str, required=True, help="Input filename")
@CommandArgument("--mar", type=str, required=True, help="Mar binary path")
@@ -2268,7 +2316,7 @@ def repackage_sign_msix(command_context, input, force=False, verbose=False):
"--arch", type=str, required=True, help="The architecture you are building."
)
@CommandArgument("--mar-channel-id", type=str, help="Mar channel id")
def repackage_mar(command_context, input, mar, output, arch, mar_channel_id):
def repackage_mar(self, command_context, input, mar, output, arch, mar_channel_id):
from mozbuild.repackaging.mar import repackage_mar
repackage_mar(
@@ -2281,6 +2329,8 @@ def repackage_mar(command_context, input, mar, output, arch, mar_channel_id):
)
@CommandProvider
class L10NCommands(MachCommandBase):
@Command(
"package-multi-locale",
category="post-build",
@@ -2297,7 +2347,7 @@ def repackage_mar(command_context, input, mar, output, arch, mar_channel_id):
@CommandArgument(
"--verbose", action="store_true", help="Log informative status messages."
)
def package_l10n(command_context, verbose=False, locales=[]):
def package_l10n(self, command_context, verbose=False, locales=[]):
if "RecursiveMake" not in command_context.substs["BUILD_BACKENDS"]:
print(
"Artifact builds do not support localization. "
@@ -2425,6 +2475,8 @@ def package_l10n(command_context, verbose=False, locales=[]):
return 0
@CommandProvider
class CreateMachEnvironment(MachCommandBase):
@Command(
"create-mach-environment",
category="devenv",
@@ -2434,9 +2486,9 @@ def package_l10n(command_context, verbose=False, locales=[]):
"-f",
"--force",
action="store_true",
help=("Force re-creating the virtualenv even if it is already up-to-date."),
help=("Force re-creating the virtualenv even if it is already " "up-to-date."),
)
def create_mach_environment(command_context, force=False):
def create_mach_environment(self, command_context, force=False):
"""Create the mach virtualenv."""
from mozboot.util import get_mach_virtualenv_root
from mozbuild.virtualenv import VirtualenvManager

View File

@@ -32,19 +32,20 @@ class TestStaticAnalysis(unittest.TestCase):
# world we should test the clang_analysis mach command
# since that small function is an internal detail.
# But there is zero test infra for that mach command
from mozbuild.code_analysis.mach_commands import _is_ignored_path
from mozbuild.code_analysis.mach_commands import StaticAnalysis
config = MozbuildObject.from_environment()
context = mock.MagicMock()
context.cwd = config.topsrcdir
cmd = StaticAnalysis(context)
command_context = mock.MagicMock()
command_context.topsrcdir = os.path.join("/root", "dir")
path = os.path.join("/root", "dir", "path1")
ignored_dirs_re = r"path1|path2/here|path3\there"
self.assertTrue(
_is_ignored_path(command_context, ignored_dirs_re, path) is not None
cmd._is_ignored_path(command_context, ignored_dirs_re, path) is not None
)
# simulating a win32 env
@@ -54,26 +55,27 @@ class TestStaticAnalysis(unittest.TestCase):
os.sep = "\\"
try:
self.assertTrue(
_is_ignored_path(command_context, ignored_dirs_re, win32_path)
cmd._is_ignored_path(command_context, ignored_dirs_re, win32_path)
is not None
)
finally:
os.sep = old_sep
self.assertTrue(
_is_ignored_path(command_context, ignored_dirs_re, "path2") is None
cmd._is_ignored_path(command_context, ignored_dirs_re, "path2") is None
)
def test_get_files(self):
from mozbuild.code_analysis.mach_commands import get_abspath_files
from mozbuild.code_analysis.mach_commands import StaticAnalysis
config = MozbuildObject.from_environment()
context = mock.MagicMock()
context.cwd = config.topsrcdir
cmd = StaticAnalysis(context)
command_context = mock.MagicMock()
command_context.topsrcdir = mozpath.join("/root", "dir")
source = get_abspath_files(
source = cmd.get_abspath_files(
command_context, ["file1", mozpath.join("directory", "file2")]
)

View File

@@ -7,11 +7,14 @@ from __future__ import absolute_import, print_function, unicode_literals
import sys
import logging
from mach.decorators import CommandArgument, Command, SubCommand
from mach.decorators import CommandArgument, CommandProvider, Command, SubCommand
from mozbuild.base import MachCommandBase
from mozbuild.vendor.moz_yaml import load_moz_yaml, MozYamlVerifyError
@CommandProvider
class Vendor(MachCommandBase):
# Fun quirk of ./mach - you can specify a default argument as well as subcommands.
# If the default argument matches a subcommand, the subcommand gets called. If it
# doesn't, we wind up in the default command.
@@ -42,8 +45,11 @@ from mozbuild.vendor.moz_yaml import load_moz_yaml, MozYamlVerifyError
@CommandArgument(
"--verify", "-v", action="store_true", help="(Only) verify the manifest"
)
@CommandArgument("library", nargs=1, help="The moz.yaml file of the library to vendor.")
@CommandArgument(
"library", nargs=1, help="The moz.yaml file of the library to vendor."
)
def vendor(
self,
command_context,
library,
revision,
@@ -76,19 +82,20 @@ def vendor(
sys.exit(1)
if not ignore_modified and not check_for_update:
check_modified_files(command_context)
self.check_modified_files(command_context)
if not revision:
revision = "HEAD"
from mozbuild.vendor.vendor_manifest import VendorManifest
vendor_command = command_context._spawn(VendorManifest)
vendor_command.vendor(library, manifest, revision, check_for_update, add_to_exports)
vendor_command.vendor(
library, manifest, revision, check_for_update, add_to_exports
)
sys.exit(0)
def check_modified_files(command_context):
def check_modified_files(self, command_context):
"""
Ensure that there aren't any uncommitted changes to files
in the working copy, since we're going to change some state
@@ -111,10 +118,8 @@ Please commit or stash these changes before vendoring, or re-run with `--ignore-
)
sys.exit(1)
# =====================================================================
@SubCommand(
"vendor",
"rust",
@@ -136,16 +141,14 @@ Please commit or stash these changes before vendoring, or re-run with `--ignore-
),
default=False,
)
def vendor_rust(command_context, **kwargs):
def vendor_rust(self, command_context, **kwargs):
from mozbuild.vendor.vendor_rust import VendorRust
vendor_command = command_context._spawn(VendorRust)
vendor_command.vendor(**kwargs)
# =====================================================================
@SubCommand(
"vendor",
"python",
@@ -160,7 +163,7 @@ def vendor_rust(command_context, **kwargs):
default=False,
help="Keep all files, including tests and documentation.",
)
def vendor_python(command_context, **kwargs):
def vendor_python(self, command_context, **kwargs):
from mozbuild.vendor.vendor_python import VendorPython
if sys.version_info[:2] != (3, 6):

View File

@@ -6,8 +6,8 @@ import sys
from functools import partial
import json
from mach.decorators import Command, CommandArgument
from mozbuild.base import MachCommandConditions as conditions
from mach.decorators import CommandProvider, Command, CommandArgument
from mozbuild.base import MachCommandBase, MachCommandConditions as conditions
_TRY_PLATFORMS = {
@@ -30,9 +30,10 @@ def get_perftest_parser():
return PerftestArgumentParser
def get_parser():
return run_perftest._mach_command._parser
@CommandProvider
class Perftest(MachCommandBase):
def get_parser(self):
return self.run_perftest._mach_command._parser
@Command(
"perftest",
@@ -41,9 +42,9 @@ def get_parser():
description="Run any flavor of perftest",
parser=get_perftest_parser,
)
def run_perftest(command_context, **kwargs):
def run_perftest(self, command_context, **kwargs):
# original parser that brought us there
original_parser = get_parser()
original_parser = self.get_parser()
from pathlib import Path
@@ -111,7 +112,9 @@ def run_perftest(command_context, **kwargs):
if isinstance(platform, str):
platform = [platform]
platform = ["%s-%s" % (plat, script_info.script_type.name) for plat in platform]
platform = [
"%s-%s" % (plat, script_info.script_type.name) for plat in platform
]
for plat in platform:
if plat not in _TRY_PLATFORMS:
@@ -158,6 +161,8 @@ def run_perftest(command_context, **kwargs):
print("\nFirefox. Fast For Good.\n")
@CommandProvider
class PerftestTests(MachCommandBase):
@Command("perftest-test", category="testing", description="Run perftest tests")
@CommandArgument(
"tests", default=None, nargs="*", help="Tests to run. By default will run all"
@@ -172,7 +177,7 @@ def run_perftest(command_context, **kwargs):
@CommandArgument(
"-v", "--verbose", action="store_true", default=False, help="Verbose mode"
)
def run_tests(command_context, **kwargs):
def run_tests(self, command_context, **kwargs):
command_context.activate_virtualenv()
from pathlib import Path
@@ -181,10 +186,9 @@ def run_tests(command_context, **kwargs):
with temporary_env(
COVERAGE_RCFILE=str(Path(HERE, ".coveragerc")), RUNNING_TESTS="YES"
):
_run_tests(command_context, **kwargs)
self._run_tests(command_context, **kwargs)
def _run_tests(command_context, **kwargs):
def _run_tests(self, command_context, **kwargs):
from pathlib import Path
from mozperftest.runner import _setup_path
from mozperftest.utils import (
@@ -210,14 +214,18 @@ def _run_tests(command_context, **kwargs):
# pip-installing dependencies that require compilation or special setup
for dep in vendors:
install_package(command_context.virtualenv_manager, str(Path(pydeps, dep)))
install_package(
command_context.virtualenv_manager, str(Path(pydeps, dep))
)
if not ON_TRY and not skip_linters:
cmd = "./mach lint "
if verbose:
cmd += " -v"
cmd += " " + str(HERE)
if not checkout_script(cmd, label="linters", display=verbose, verbose=verbose):
if not checkout_script(
cmd, label="linters", display=verbose, verbose=verbose
):
raise AssertionError("Please fix your code.")
# running pytest with coverage

View File

@@ -17,10 +17,9 @@ from mach.registrar import Registrar
Registrar.categories = {"testing": []}
Registrar.commands_by_category = {"testing": set()}
from mozbuild.base import MachCommandBase # noqa
import mozperftest.mach_commands # noqa
from mozperftest.environment import MachEnvironment # noqa
from mozperftest.mach_commands import Perftest, PerftestTests # noqa
from mozperftest.tests.support import EXAMPLE_TEST, ROOT, running_on_try # noqa
from mozperftest.utils import temporary_env, silence # noqa
@@ -46,7 +45,7 @@ class _TestMachEnvironment(MachEnvironment):
@contextmanager
def _get_command(command=mozperftest.mach_commands.run_perftest):
def _get_command(klass=Perftest):
from mozbuild.base import MozbuildObject
from mozperftest.argparser import PerftestArgumentParser
@@ -69,59 +68,59 @@ def _get_command(command=mozperftest.mach_commands.run_perftest):
return _run
try:
command_context = MachCommandBase(context())
obj = klass(context())
parser = PerftestArgumentParser()
obj.get_parser = lambda: parser
if command == mozperftest.mach_commands.run_perftest:
command = _run_perftest(command)
if isinstance(obj, Perftest):
obj.run_perftest = _run_perftest(obj.run_perftest)
with mock.patch("mozperftest.mach_commands.get_parser", new=lambda: parser):
yield command, command_context
yield obj
finally:
shutil.rmtree(context.state_dir)
@mock.patch("mozperftest.MachEnvironment", new=_TestMachEnvironment)
@mock.patch("mozbuild.base.MachCommandBase.activate_virtualenv")
@mock.patch("mozperftest.mach_commands.MachCommandBase.activate_virtualenv")
def test_command(mocked_func):
with _get_command() as (cmd, command_context), silence(command_context):
cmd(command_context, tests=[EXAMPLE_TEST], flavor="desktop-browser")
with _get_command() as test, silence(test):
test.run_perftest(test, tests=[EXAMPLE_TEST], flavor="desktop-browser")
@mock.patch("mozperftest.MachEnvironment")
@mock.patch("mozbuild.base.MachCommandBase.activate_virtualenv")
@mock.patch("mozperftest.mach_commands.MachCommandBase.activate_virtualenv")
def test_command_iterations(venv, env):
kwargs = {
"tests": [EXAMPLE_TEST],
"hooks": ITERATION_HOOKS,
"flavor": "desktop-browser",
}
with _get_command() as (cmd, command_context), silence(command_context):
cmd(command_context, **kwargs)
with _get_command() as test, silence(test):
test.run_perftest(test, **kwargs)
# the hook changes the iteration value to 5.
# each iteration generates 5 calls, so we want to see 25
assert len(env.mock_calls) == 25
@mock.patch("mozperftest.MachEnvironment")
@mock.patch("mozbuild.base.MachCommandBase.activate_virtualenv")
@mock.patch("mozperftest.mach_commands.MachCommandBase.activate_virtualenv")
def test_hooks_state(venv, env):
kwargs = {
"tests": [EXAMPLE_TEST],
"hooks": STATE_HOOKS,
"flavor": "desktop-browser",
}
with _get_command() as (cmd, command_context), silence(command_context):
cmd(command_context, **kwargs)
with _get_command() as test, silence(test):
test.run_perftest(test, **kwargs)
@mock.patch("mozperftest.MachEnvironment", new=_TestMachEnvironment)
@mock.patch("mozbuild.base.MachCommandBase.activate_virtualenv")
@mock.patch("mozperftest.mach_commands.MachCommandBase.activate_virtualenv")
@mock.patch("tryselect.push.push_to_try")
def test_push_command(push_to_try, venv):
with _get_command() as (cmd, command_context), silence(command_context):
cmd(
command_context,
with _get_command() as test, silence(test):
test.run_perftest(
test,
tests=[EXAMPLE_TEST],
flavor="desktop-browser",
push_to_try=True,
@@ -132,13 +131,13 @@ def test_push_command(push_to_try, venv):
@mock.patch("mozperftest.MachEnvironment", new=_TestMachEnvironment)
@mock.patch("mozbuild.base.MachCommandBase.activate_virtualenv")
@mock.patch("mozperftest.mach_commands.MachCommandBase.activate_virtualenv")
@mock.patch("tryselect.push.push_to_try")
def test_push_command_unknown_platforms(push_to_try, venv):
# full stop when a platform is unknown
with _get_command() as (cmd, command_context), pytest.raises(NotImplementedError):
cmd(
command_context,
with _get_command() as test, pytest.raises(NotImplementedError):
test.run_perftest(
test,
tests=[EXAMPLE_TEST],
flavor="desktop-browser",
push_to_try=True,
@@ -147,15 +146,12 @@ def test_push_command_unknown_platforms(push_to_try, venv):
@mock.patch("mozperftest.MachEnvironment", new=_TestMachEnvironment)
@mock.patch("mozbuild.base.MachCommandBase.activate_virtualenv")
@mock.patch("mozperftest.mach_commands.MachCommandBase.activate_virtualenv")
@mock.patch("tryselect.push.push_to_try")
def test_push_command_several_platforms(push_to_try, venv):
with running_on_try(False), _get_command() as (
cmd,
command_context,
): # , silence(command_context):
cmd(
command_context,
with running_on_try(False), _get_command() as test: # , silence(test):
test.run_perftest(
test,
tests=[EXAMPLE_TEST],
flavor="desktop-browser",
push_to_try=True,
@@ -169,45 +165,39 @@ def test_push_command_several_platforms(push_to_try, venv):
@mock.patch("mozperftest.MachEnvironment", new=_TestMachEnvironment)
@mock.patch("mozbuild.base.MachCommandBase.activate_virtualenv")
@mock.patch("mozperftest.mach_commands.MachCommandBase.activate_virtualenv")
def test_doc_flavor(mocked_func):
with _get_command() as (cmd, command_context), silence(command_context):
cmd(command_context, tests=[EXAMPLE_TEST], flavor="doc")
with _get_command() as test, silence(test):
test.run_perftest(test, tests=[EXAMPLE_TEST], flavor="doc")
@mock.patch("mozperftest.MachEnvironment", new=_TestMachEnvironment)
@mock.patch("mozbuild.base.MachCommandBase.activate_virtualenv")
@mock.patch("mozperftest.mach_commands.MachCommandBase.activate_virtualenv")
@mock.patch("mozperftest.utils.run_script")
def test_test_runner(*mocked):
from mozperftest.mach_commands import run_tests
with running_on_try(False), _get_command(run_tests) as (cmd, command_context):
cmd(command_context, tests=[EXAMPLE_TEST], verbose=True)
with running_on_try(False), _get_command(PerftestTests) as test:
test.run_tests(test, tests=[EXAMPLE_TEST], verbose=True)
@mock.patch("mozperftest.MachEnvironment", new=_TestMachEnvironment)
@mock.patch("mozbuild.base.MachCommandBase.activate_virtualenv")
@mock.patch("mozperftest.mach_commands.MachCommandBase.activate_virtualenv")
@mock.patch("mozperftest.utils.run_python_script")
def test_test_runner_on_try(*mocked):
from mozperftest.mach_commands import run_tests
# simulating on try to run the paths parser
with running_on_try(), _get_command(run_tests) as (cmd, command_context):
cmd(command_context, tests=[EXAMPLE_TEST])
with running_on_try(), _get_command(PerftestTests) as test:
test.run_tests(test, tests=[EXAMPLE_TEST])
@mock.patch("mozperftest.MachEnvironment", new=_TestMachEnvironment)
@mock.patch("mozbuild.base.MachCommandBase.activate_virtualenv")
@mock.patch("mozperftest.mach_commands.MachCommandBase.activate_virtualenv")
@mock.patch("mozperftest.utils.run_script")
def test_test_runner_coverage(*mocked):
from mozperftest.mach_commands import run_tests
# simulating with coverage not installed
with running_on_try(False), _get_command(run_tests) as (cmd, command_context):
with running_on_try(False), _get_command(PerftestTests) as test:
old = list(sys.meta_path)
sys.meta_path = []
try:
cmd(command_context, tests=[EXAMPLE_TEST])
test.run_tests(test, tests=[EXAMPLE_TEST])
finally:
sys.meta_path = old
@@ -233,24 +223,21 @@ def resolve_tests(tests=None):
@mock.patch("mozperftest.MachEnvironment", new=_TestMachEnvironment)
@mock.patch("mozbuild.base.MachCommandBase.activate_virtualenv")
@mock.patch("mozperftest.mach_commands.MachCommandBase.activate_virtualenv")
@mock.patch("mozperftest.fzf.fzf.select", new=fzf_selection)
@mock.patch("moztest.resolve.TestResolver.resolve_tests", new=resolve_tests())
def test_fzf_flavor(*mocked):
with running_on_try(False), _get_command() as (
cmd,
command_context,
): # , silence():
cmd(command_context, flavor="desktop-browser")
with running_on_try(False), _get_command() as test: # , silence():
test.run_perftest(test, flavor="desktop-browser")
@mock.patch("mozperftest.MachEnvironment", new=_TestMachEnvironment)
@mock.patch("mozbuild.base.MachCommandBase.activate_virtualenv")
@mock.patch("mozperftest.mach_commands.MachCommandBase.activate_virtualenv")
@mock.patch("mozperftest.fzf.fzf.select", new=fzf_selection)
@mock.patch("moztest.resolve.TestResolver.resolve_tests", new=resolve_tests([]))
def test_fzf_nothing_selected(*mocked):
with running_on_try(False), _get_command() as (cmd, command_context), silence():
cmd(command_context, flavor="desktop-browser")
with running_on_try(False), _get_command() as test, silence():
test.run_perftest(test, flavor="desktop-browser")
if __name__ == "__main__":

View File

@@ -10,22 +10,24 @@ from __future__ import absolute_import, print_function, unicode_literals
import sys
import logging
from mach.decorators import CommandArgument, Command, SubCommand
from mach.decorators import CommandArgument, CommandProvider, Command, SubCommand
from mozbuild.base import MachCommandBase
from mozilla_version.gecko import GeckoVersion
@CommandProvider
class MachCommands(MachCommandBase):
@Command(
"release",
category="release",
description="Task that are part of the release process.",
)
def release(command_context):
def release(self, command_context):
"""
The release subcommands all relate to the release process.
"""
@SubCommand(
"release",
"buglist",
@@ -40,8 +42,8 @@ def release(command_context):
@CommandArgument("--product", required=True, help="The product being built.")
@CommandArgument("--repo", help="The repo being built.")
@CommandArgument("--revision", required=True, help="The revision being built.")
def buglist(command_context, version, product, revision, repo):
setup_logging(command_context)
def buglist(self, command_context, version, product, revision, repo):
self.setup_logging(command_context)
from mozrelease.buglist_creator import create_bugs_url
print(
@@ -53,7 +55,6 @@ def buglist(command_context, version, product, revision, repo):
)
)
@SubCommand(
"release",
"send-buglist-email",
@@ -78,13 +79,12 @@ def buglist(command_context, version, product, revision, repo):
@CommandArgument("--revision", required=True, help="The revision being built.")
@CommandArgument("--build-number", required=True, help="The build number")
@CommandArgument("--task-group-id", help="The task group of the build.")
def buglist_email(command_context, **options):
setup_logging(command_context)
def buglist_email(self, command_context, **options):
self.setup_logging(command_context)
from mozrelease.buglist_creator import email_release_drivers
email_release_drivers(**options)
@SubCommand(
"release",
"push-scriptworker-canary",
@@ -110,8 +110,10 @@ def buglist_email(command_context, **options):
required=False,
help="Taskcluster secret with ssh-key to use for hg.mozilla.org",
)
def push_scriptworker_canary(command_context, scriptworkers, addresses, ssh_key_secret):
setup_logging(command_context)
def push_scriptworker_canary(
self, command_context, scriptworkers, addresses, ssh_key_secret
):
self.setup_logging(command_context)
from mozrelease.scriptworker_canary import push_canary
push_canary(
@@ -120,8 +122,7 @@ def push_scriptworker_canary(command_context, scriptworkers, addresses, ssh_key_
ssh_key_secret=ssh_key_secret,
)
def setup_logging(command_context, quiet=False, verbose=True):
def setup_logging(self, command_context, quiet=False, verbose=True):
"""
Set up Python logging for all loggers, sending results to stderr (so
that command output can be redirected easily) and adding the typical

View File

@@ -24,10 +24,12 @@ from six import iteritems
from mach.decorators import (
Command,
CommandArgument,
CommandProvider,
SubCommand,
)
from mozbuild.base import (
MachCommandBase,
MozbuildObject,
BinaryNotFoundException,
)
@@ -50,17 +52,19 @@ def setup():
os.environ["PATH"] = "{}:{}".format(path, os.environ["PATH"])
def remotedir(command_context):
@CommandProvider
class RemoteCommands(MachCommandBase):
def remotedir(self, command_context):
return os.path.join(command_context.topsrcdir, "remote")
@Command("remote", category="misc", description="Remote protocol related operations.")
def remote(command_context):
@Command(
"remote", category="misc", description="Remote protocol related operations."
)
def remote(self, command_context):
"""The remote subcommands all relate to the remote protocol."""
command_context._sub_mach(["help", "remote"])
return 1
@SubCommand(
"remote", "vendor-puppeteer", "Pull in latest changes of the Puppeteer client."
)
@@ -83,13 +87,15 @@ def remote(command_context):
default=True,
help="Do not install the just-pulled Puppeteer package,",
)
def vendor_puppeteer(command_context, repository, commitish, install):
puppeteer_dir = os.path.join(remotedir(command_context), "test", "puppeteer")
def vendor_puppeteer(self, command_context, repository, commitish, install):
puppeteer_dir = os.path.join(
self.remotedir(command_context), "test", "puppeteer"
)
# Preserve our custom mocha reporter
shutil.move(
os.path.join(puppeteer_dir, "json-mocha-reporter.js"),
remotedir(command_context),
self.remotedir(command_context),
)
shutil.rmtree(puppeteer_dir, ignore_errors=True)
os.makedirs(puppeteer_dir)
@@ -120,7 +126,7 @@ def vendor_puppeteer(command_context, repository, commitish, install):
shutil.rmtree(dir_path)
shutil.move(
os.path.join(remotedir(command_context), "json-mocha-reporter.js"),
os.path.join(self.remotedir(command_context), "json-mocha-reporter.js"),
puppeteer_dir,
)
@@ -578,6 +584,8 @@ def create_parser_puppeteer():
return p
@CommandProvider
class PuppeteerTest(MachCommandBase):
@Command(
"puppeteer-test",
category="testing",
@@ -585,6 +593,7 @@ def create_parser_puppeteer():
parser=create_parser_puppeteer,
)
def puppeteer_test(
self,
command_context,
binary=None,
ci=False,
@@ -645,7 +654,7 @@ def puppeteer_test(
if verbosity > 2:
prefs["remote.log.truncate"] = False
install_puppeteer(command_context, product, ci)
self.install_puppeteer(command_context, product, ci)
params = {
"binary": binary,
@@ -667,8 +676,7 @@ def puppeteer_test(
except Exception as e:
exit(EX_SOFTWARE, e)
def install_puppeteer(command_context, product, ci):
def install_puppeteer(self, command_context, product, ci):
setup()
env = {}
from mozversioncontrol import get_repository_object
@@ -689,7 +697,9 @@ def install_puppeteer(command_context, product, ci):
shutil.rmtree(lib_dir)
command = "ci" if ci else "install"
npm(command, cwd=os.path.join(command_context.topsrcdir, puppeteer_dir), env=env)
npm(
command, cwd=os.path.join(command_context.topsrcdir, puppeteer_dir), env=env
)
def exit(code, error=None):

View File

@@ -6,12 +6,14 @@ import os
import sys
from mach.util import UserError
from mozbuild.base import MachCommandBase
from mozpack.files import FileFinder
from mozpack.path import basedir
from mach.decorators import (
CommandArgument,
CommandProvider,
Command,
)
@@ -62,6 +64,8 @@ def is_excluded_directory(directory, exclusions):
return False
@CommandProvider
class MachCommands(MachCommandBase):
@Command(
"generate-test-certs",
category="devenv",
@@ -72,7 +76,7 @@ def is_excluded_directory(directory, exclusions):
nargs="*",
help="Specification files for test certs. If omitted, all certs are regenerated.",
)
def generate_test_certs(command_context, specifications):
def generate_test_certs(self, command_context, specifications):
"""Generate test certificates and keys from specifications."""
command_context.activate_virtualenv()
@@ -80,7 +84,7 @@ def generate_test_certs(command_context, specifications):
import pykey
if not specifications:
specifications = find_all_specifications(command_context)
specifications = self.find_all_specifications(command_context)
for specification in specifications:
if is_certspec_file(specification):
@@ -94,8 +98,7 @@ def generate_test_certs(command_context, specifications):
run_module_main_on(module, os.path.abspath(specification))
return 0
def find_all_specifications(command_context):
def find_all_specifications(self, command_context):
"""Searches the source tree for all specification files
and returns them as a list."""
specifications = []

View File

@@ -18,9 +18,11 @@ from functools import partial
from mach.decorators import (
Command,
CommandArgument,
CommandProvider,
SettingsProvider,
SubCommand,
)
from mozbuild.base import MachCommandBase
import taskgraph.main
from taskgraph.main import commands as taskgraph_commands
@@ -143,28 +145,28 @@ def get_taskgraph_decision_parser():
return parser
@CommandProvider
class MachCommands(MachCommandBase):
@Command(
"taskgraph",
category="ci",
description="Manipulate TaskCluster task graphs defined in-tree",
)
def taskgraph_command(command_context):
def taskgraph(self, command_context):
"""The taskgraph subcommands all relate to the generation of task graphs
for Gecko continuous integration. A task graph is a set of tasks linked
by dependencies: for example, a binary must be built before it is tested,
and that build may further depend on various toolchains, libraries, etc.
"""
@SubCommand(
"taskgraph",
"tasks",
description="Show all tasks in the taskgraph",
parser=partial(get_taskgraph_command_parser, "tasks"),
)
def taskgraph_tasks(command_context, **options):
return run_show_taskgraph(command_context, **options)
def taskgraph_tasks(self, command_context, **options):
return self.run_show_taskgraph(command_context, **options)
@SubCommand(
"taskgraph",
@@ -172,9 +174,8 @@ def taskgraph_tasks(command_context, **options):
description="Show the full taskgraph",
parser=partial(get_taskgraph_command_parser, "full"),
)
def taskgraph_full(command_context, **options):
return run_show_taskgraph(command_context, **options)
def taskgraph_full(self, command_context, **options):
return self.run_show_taskgraph(command_context, **options)
@SubCommand(
"taskgraph",
@@ -182,9 +183,8 @@ def taskgraph_full(command_context, **options):
description="Show the target task set",
parser=partial(get_taskgraph_command_parser, "target"),
)
def taskgraph_target(command_context, **options):
return run_show_taskgraph(command_context, **options)
def taskgraph_target(self, command_context, **options):
return self.run_show_taskgraph(command_context, **options)
@SubCommand(
"taskgraph",
@@ -192,9 +192,8 @@ def taskgraph_target(command_context, **options):
description="Show the target taskgraph",
parser=partial(get_taskgraph_command_parser, "target-graph"),
)
def taskgraph_target_graph(command_context, **options):
return run_show_taskgraph(command_context, **options)
def taskgraph_target_graph(self, command_context, **options):
return self.run_show_taskgraph(command_context, **options)
@SubCommand(
"taskgraph",
@@ -202,9 +201,8 @@ def taskgraph_target_graph(command_context, **options):
description="Show the optimized taskgraph",
parser=partial(get_taskgraph_command_parser, "optimized"),
)
def taskgraph_optimized(command_context, **options):
return run_show_taskgraph(command_context, **options)
def taskgraph_optimized(self, command_context, **options):
return self.run_show_taskgraph(command_context, **options)
@SubCommand(
"taskgraph",
@@ -212,17 +210,16 @@ def taskgraph_optimized(command_context, **options):
description="Show the morphed taskgraph",
parser=partial(get_taskgraph_command_parser, "morphed"),
)
def taskgraph_morphed(command_context, **options):
return run_show_taskgraph(command_context, **options)
def taskgraph_morphed(self, command_context, **options):
return self.run_show_taskgraph(command_context, **options)
def run_show_taskgraph(command_context, **options):
def run_show_taskgraph(self, command_context, **options):
# There are cases where we don't want to set up mach logging (e.g logs
# are being redirected to disk). By monkeypatching the 'setup_logging'
# function we can let 'taskgraph.main' decide whether or not to log to
# the terminal.
taskgraph.main.setup_logging = partial(
setup_logging,
self.setup_logging,
command_context,
quiet=options["quiet"],
verbose=options["verbose"],
@@ -230,7 +227,6 @@ def run_show_taskgraph(command_context, **options):
show_taskgraph = options.pop("func")
return show_taskgraph(options)
@SubCommand("taskgraph", "actions", description="Write actions.json to stdout")
@CommandArgument(
"--root", "-r", help="root of the taskgraph definition relative to topsrcdir"
@@ -248,11 +244,11 @@ def run_show_taskgraph(command_context, **options):
"--parameters",
"-p",
default="project=mozilla-central",
help="parameters file (.yml or .json; see `taskcluster/docs/parameters.rst`)`",
help="parameters file (.yml or .json; see "
"`taskcluster/docs/parameters.rst`)`",
)
def taskgraph_actions(command_context, **options):
return show_actions(command_context, options)
def taskgraph_actions(self, command_context, **options):
return self.show_actions(command_context, options)
@SubCommand(
"taskgraph",
@@ -260,14 +256,14 @@ def taskgraph_actions(command_context, **options):
description="Run the decision task",
parser=get_taskgraph_decision_parser,
)
def taskgraph_decision(command_context, **options):
def taskgraph_decision(self, command_context, **options):
"""Run the decision task: generate a task graph and submit to
TaskCluster. This is only meant to be called within decision tasks,
and requires a great many arguments. Commands like `mach taskgraph
optimized` are better suited to use on the command line, and can take
the parameters file generated by a decision task."""
try:
setup_logging(command_context)
self.setup_logging(command_context)
start = time.monotonic()
ret = taskgraph_commands["decision"].func(options)
end = time.monotonic()
@@ -293,47 +289,43 @@ def taskgraph_decision(command_context, **options):
traceback.print_exc()
sys.exit(1)
@SubCommand(
"taskgraph",
"cron",
description="Provide a pointer to the new `.cron.yml` handler.",
)
def taskgraph_cron(command_context, **options):
def taskgraph_cron(self, command_context, **options):
print(
'Handling of ".cron.yml" files has move to '
"https://hg.mozilla.org/ci/ci-admin/file/default/build-decision."
)
sys.exit(1)
@SubCommand(
"taskgraph",
"action-callback",
description="Run action callback used by action tasks",
parser=partial(get_taskgraph_command_parser, "action-callback"),
)
def action_callback(command_context, **options):
setup_logging(command_context)
def action_callback(self, command_context, **options):
self.setup_logging(command_context)
taskgraph_commands["action-callback"].func(options)
@SubCommand(
"taskgraph",
"test-action-callback",
description="Run an action callback in a testing mode",
parser=partial(get_taskgraph_command_parser, "test-action-callback"),
)
def test_action_callback(command_context, **options):
setup_logging(command_context)
def test_action_callback(self, command_context, **options):
self.setup_logging(command_context)
if not options["parameters"]:
options["parameters"] = "project=mozilla-central"
taskgraph_commands["test-action-callback"].func(options)
def setup_logging(command_context, quiet=False, verbose=True):
def setup_logging(self, command_context, quiet=False, verbose=True):
"""
Set up Python logging for all loggers, sending results to stderr (so
that command output can be redirected easily) and adding the typical
@@ -355,15 +347,14 @@ def setup_logging(command_context, quiet=False, verbose=True):
# all of the taskgraph logging is unstructured logging
command_context.log_manager.enable_unstructured()
def show_actions(command_context, options):
def show_actions(self, command_context, options):
import taskgraph
import taskgraph.actions
import taskgraph.generator
import taskgraph.parameters
try:
setup_logging(
self.setup_logging(
command_context, quiet=options["quiet"], verbose=options["verbose"]
)
parameters = taskgraph.parameters.parameters_loader(options["parameters"])
@@ -384,6 +375,8 @@ def show_actions(command_context, options):
sys.exit(1)
@CommandProvider
class TaskClusterImagesProvider(MachCommandBase):
@Command(
"taskcluster-load-image",
category="ci",
@@ -391,24 +384,22 @@ def show_actions(command_context, options):
"have docker installed and running for this to work.",
parser=partial(get_taskgraph_command_parser, "load-image"),
)
def load_image(command_context, **kwargs):
def load_image(self, command_context, **kwargs):
taskgraph_commands["load-image"].func(kwargs)
@Command(
"taskcluster-build-image",
category="ci",
description="Build a Docker image",
parser=partial(get_taskgraph_command_parser, "build-image"),
)
def build_image(command_context, **kwargs):
def build_image(self, command_context, **kwargs):
try:
taskgraph_commands["build-image"].func(kwargs)
except Exception:
traceback.print_exc()
sys.exit(1)
@Command(
"taskcluster-image-digest",
category="ci",
@@ -416,10 +407,12 @@ def build_image(command_context, **kwargs):
"current contents of the tree.",
parser=partial(get_taskgraph_command_parser, "build-image"),
)
def image_digest(command_context, **kwargs):
def image_digest(self, command_context, **kwargs):
taskgraph_commands["image-digest"].func(kwargs)
@CommandProvider
class TaskClusterPartialsData(MachCommandBase):
@Command(
"release-history",
category="ci",
@@ -434,13 +427,15 @@ def image_digest(command_context, **kwargs):
@CommandArgument(
"--product", default="Firefox", help="The product identifier, such as 'Firefox'"
)
def generate_partials_builds(command_context, product, branch):
def generate_partials_builds(self, command_context, product, branch):
from taskgraph.util.partials import populate_release_history
try:
import yaml
release_history = {"release_history": populate_release_history(product, branch)}
release_history = {
"release_history": populate_release_history(product, branch)
}
print(
yaml.safe_dump(
release_history, allow_unicode=True, default_flow_style=False

View File

@@ -12,6 +12,7 @@ import sys
import six
from mozbuild.base import (
MachCommandBase,
MachCommandConditions as conditions,
BinaryNotFoundException,
)
@@ -19,6 +20,7 @@ from mozbuild.base import (
from mach.decorators import (
CommandArgument,
CommandArgumentGroup,
CommandProvider,
Command,
)
@@ -35,13 +37,14 @@ def setup_awsy_argument_parser():
return parser
@CommandProvider
class MachCommands(MachCommandBase):
AWSY_PATH = os.path.dirname(os.path.realpath(__file__))
if AWSY_PATH not in sys.path:
sys.path.append(AWSY_PATH)
from awsy import ITERATIONS, PER_TAB_PAUSE, SETTLE_WAIT_TIME, MAX_TABS
def run_awsy(command_context, tests, binary=None, **kwargs):
def run_awsy(self, command_context, tests, binary=None, **kwargs):
import json
from mozlog.structured import commandline
@@ -132,7 +135,9 @@ def run_awsy(command_context, tests, binary=None, **kwargs):
tp5nzip = os.path.join(page_load_test_dir, "tp5n.zip")
tp5nmanifest = os.path.join(page_load_test_dir, "tp5n", "tp5n.manifest")
if not os.path.exists(tp5nmanifest):
unzip_args = {"args": ["unzip", "-q", "-o", tp5nzip, "-d", page_load_test_dir]}
unzip_args = {
"args": ["unzip", "-q", "-o", tp5nzip, "-d", page_load_test_dir]
}
try:
command_context.run_process(**unzip_args)
except Exception as exc:
@@ -154,7 +159,9 @@ def run_awsy(command_context, tests, binary=None, **kwargs):
# If '--preferences' was not specified supply our default set.
if not kwargs["prefs_files"]:
kwargs["prefs_files"] = [os.path.join(awsy_source_dir, "conf", "prefs.json")]
kwargs["prefs_files"] = [
os.path.join(awsy_source_dir, "conf", "prefs.json")
]
# Setup DMD env vars if necessary.
if kwargs["dmd"]:
@@ -196,7 +203,6 @@ def run_awsy(command_context, tests, binary=None, **kwargs):
else:
return 0
@Command(
"awsy-test",
category="testing",
@@ -249,7 +255,7 @@ def run_awsy(command_context, tests, binary=None, **kwargs):
action="store",
type=int,
dest="entities",
help="Number of urls to load. Defaults to the total number of urls.",
help="Number of urls to load. Defaults to the total number of " "urls.",
)
@CommandArgument(
"--max-tabs",
@@ -257,7 +263,7 @@ def run_awsy(command_context, tests, binary=None, **kwargs):
action="store",
type=int,
dest="maxTabs",
help="Maximum number of tabs to open. Defaults to %s." % MAX_TABS,
help="Maximum number of tabs to open. " "Defaults to %s." % MAX_TABS,
)
@CommandArgument(
"--iterations",
@@ -274,7 +280,8 @@ def run_awsy(command_context, tests, binary=None, **kwargs):
action="store",
type=int,
dest="perTabPause",
help="Seconds to wait in between opening tabs. Defaults to %s." % PER_TAB_PAUSE,
help="Seconds to wait in between opening tabs. "
"Defaults to %s." % PER_TAB_PAUSE,
)
@CommandArgument(
"--settle-wait-time",
@@ -301,7 +308,7 @@ def run_awsy(command_context, tests, binary=None, **kwargs):
default=False,
help="Use the tp6 pageset during testing.",
)
def run_awsy_test(command_context, tests, **kwargs):
def run_awsy_test(self, command_context, tests, **kwargs):
"""mach awsy-test runs the in-tree version of the Are We Slim Yet
(AWSY) tests.
@@ -347,4 +354,4 @@ def run_awsy_test(command_context, tests, **kwargs):
)
command_context.log(logging.INFO, "awsy", {"help": e.help()}, "{help}")
return 1
return run_awsy(command_context, tests, **kwargs)
return self.run_awsy(command_context, tests, **kwargs)

View File

@@ -7,19 +7,20 @@ import sys
import os
import tempfile
from mach.decorators import CommandArgument, Command
from mozbuild.base import BinaryNotFoundException
from mach.decorators import CommandArgument, CommandProvider, Command
from mozbuild.base import MachCommandBase, BinaryNotFoundException
requirements = os.path.join(os.path.dirname(__file__), "requirements", "base.txt")
def _init(command_context):
@CommandProvider
class CondprofileCommandProvider(MachCommandBase):
def _init(self, command_context):
command_context.activate_virtualenv()
command_context.virtualenv_manager.install_pip_requirements(
requirements, require_hashes=False
)
@Command("fetch-condprofile", category="testing")
@CommandArgument("--target-dir", default=None, help="Target directory")
@CommandArgument("--platform", default=None, help="Platform")
@@ -34,6 +35,7 @@ def _init(command_context):
help="Repository",
)
def fetch(
self,
command_context,
target_dir,
platform,
@@ -43,7 +45,7 @@ def fetch(
download_cache,
repo,
):
_init(command_context)
self._init(command_context)
from condprof.client import get_profile
from condprof.util import get_current_platform
@@ -57,7 +59,6 @@ def fetch(
target_dir, platform, scenario, customization, task_id, download_cache, repo
)
@Command("run-condprofile", category="testing")
@CommandArgument("archive", help="Archives Dir", type=str, default=None)
@CommandArgument("--firefox", help="Firefox Binary", type=str, default=None)
@@ -88,9 +89,9 @@ def fetch(
default=sys.platform.startswith("win") and "geckodriver.exe" or "geckodriver",
)
@CommandArgument("--device-name", help="Name of the device", type=str, default=None)
def run(command_context, **kw):
def run(self, command_context, **kw):
os.environ["MANUAL_MACH_RUN"] = "1"
_init(command_context)
self._init(command_context)
if kw["firefox"] is None:
try:

View File

@@ -10,12 +10,14 @@ import os
import sys
from mozbuild.base import (
MachCommandBase,
MachCommandConditions as conditions,
BinaryNotFoundException,
)
from mach.decorators import (
Command,
CommandProvider,
)
@@ -85,6 +87,8 @@ def run_firefox_ui_test(testtype=None, topsrcdir=None, **kwargs):
return 0
@CommandProvider
class MachCommands(MachCommandBase):
@Command(
"firefox-ui-functional",
category="testing",
@@ -92,9 +96,11 @@ def run_firefox_ui_test(testtype=None, topsrcdir=None, **kwargs):
description="Run the functional test suite of Firefox UI tests.",
parser=setup_argument_parser_functional,
)
def run_firefox_ui_functional(command_context, **kwargs):
def run_firefox_ui_functional(self, command_context, **kwargs):
try:
kwargs["binary"] = kwargs["binary"] or command_context.get_binary_path("app")
kwargs["binary"] = kwargs["binary"] or command_context.get_binary_path(
"app"
)
except BinaryNotFoundException as e:
command_context.log(
logging.ERROR,

View File

@@ -11,11 +11,14 @@ from mach.decorators import (
Command,
CommandArgument,
CommandArgumentGroup,
CommandProvider,
)
from mozbuild.base import BinaryNotFoundException
from mozbuild.base import MachCommandBase, BinaryNotFoundException
@CommandProvider
class GeckoDriver(MachCommandBase):
@Command(
"geckodriver",
category="post-build",
@@ -49,9 +52,10 @@ from mozbuild.base import BinaryNotFoundException
metavar="params",
type=str,
group="debugging",
help="Flags to pass to the debugger itself; split as the Bourne shell would.",
help="Flags to pass to the debugger itself; "
"split as the Bourne shell would.",
)
def run(command_context, binary, params, debug, debugger, debugger_args):
def run(self, command_context, binary, params, debug, debugger, debugger_args):
try:
binpath = command_context.get_binary_path("geckodriver")
except BinaryNotFoundException as e:

View File

@@ -9,8 +9,10 @@ import sys
from argparse import Namespace
from mach.decorators import (
CommandProvider,
Command,
)
from mozbuild.base import MachCommandBase
here = os.path.abspath(os.path.dirname(__file__))
parser = None
@@ -117,13 +119,15 @@ def setup_argument_parser():
return parser
@CommandProvider
class GtestCommands(MachCommandBase):
@Command(
"gtest",
category="testing",
description="Run the gtest harness.",
parser=setup_argument_parser,
)
def gtest(command_context, **kwargs):
def gtest(self, command_context, **kwargs):
command_context._mach_context.activate_mozharness_venv()
result = run_gtest(command_context._mach_context, **kwargs)
return 0 if result else 1

View File

@@ -12,6 +12,7 @@ import subprocess
from mach.decorators import (
CommandArgument,
CommandProvider,
Command,
SettingsProvider,
SubCommand,
@@ -19,6 +20,7 @@ from mach.decorators import (
from mozbuild.base import (
BuildEnvironmentNotFoundException,
MachCommandBase,
MachCommandConditions as conditions,
)
@@ -158,6 +160,8 @@ def create_parser_addtest():
return parser
@CommandProvider
class AddTest(MachCommandBase):
@Command(
"addtest",
category="testing",
@@ -165,6 +169,7 @@ def create_parser_addtest():
parser=create_parser_addtest,
)
def addtest(
self,
command_context,
suite=None,
test=None,
@@ -190,9 +195,9 @@ def addtest(
abs_test = os.path.abspath(test)
if doc is None:
doc = guess_doc(abs_test)
doc = self.guess_doc(abs_test)
if suite is None:
guessed_suite, err = guess_suite(abs_test)
guessed_suite, err = self.guess_suite(abs_test)
if err:
print(err)
return 1
@@ -292,13 +297,11 @@ def addtest(
return 0
def guess_doc(abs_test):
def guess_doc(self, abs_test):
filename = os.path.basename(abs_test)
return os.path.splitext(filename)[1].strip(".")
def guess_suite(abs_test):
def guess_suite(self, abs_test):
# If you pass a abs_test, try to detect the type based on the name
# and folder. This detection can be skipped if you pass the `type` arg.
err = None
@@ -322,7 +325,7 @@ def guess_suite(abs_test):
elif (
filename.startswith("test_")
and has_xpcshell_ini
and guess_doc(abs_test) == "js"
and self.guess_doc(abs_test) == "js"
):
guessed_suite = "xpcshell"
else:
@@ -341,13 +344,15 @@ def guess_suite(abs_test):
return guessed_suite, err
@CommandProvider
class Test(MachCommandBase):
@Command(
"test",
category="testing",
description="Run tests (detects the kind of test and runs it).",
parser=get_test_parser,
)
def test(command_context, what, extra_args, **log_args):
def test(self, command_context, what, extra_args, **log_args):
"""Run tests from names or paths.
mach test accepts arguments specifying which tests to run. Each argument
@@ -468,6 +473,8 @@ def test(command_context, what, extra_args, **log_args):
return status
@CommandProvider
class MachCommands(MachCommandBase):
@Command(
"cppunittest", category="testing", description="Run cpp unit tests (C++ tests)."
)
@@ -486,7 +493,7 @@ def test(command_context, what, extra_args, **log_args):
"directories, or omitted. If omitted, the entire test suite is "
"executed.",
)
def run_cppunit_test(command_context, **params):
def run_cppunit_test(self, command_context, **params):
from mozlog import commandline
log = params.get("log")
@@ -517,15 +524,14 @@ def run_cppunit_test(command_context, **params):
)
verify_android_device(command_context, install=InstallIntent.NO)
return run_android_test(tests, symbols_path, manifest_path, log)
return self.run_android_test(tests, symbols_path, manifest_path, log)
return run_desktop_test(
return self.run_desktop_test(
command_context, tests, symbols_path, manifest_path, utility_path, log
)
def run_desktop_test(
command_context, tests, symbols_path, manifest_path, utility_path, log
self, command_context, tests, symbols_path, manifest_path, utility_path, log
):
import runcppunittests as cppunittests
from mozlog import commandline
@@ -548,8 +554,9 @@ def run_desktop_test(
return 0 if result else 1
def run_android_test(command_context, tests, symbols_path, manifest_path, log):
def run_android_test(
self, command_context, tests, symbols_path, manifest_path, log
):
import remotecppunittests as remotecppunittests
from mozlog import commandline
@@ -567,7 +574,9 @@ def run_android_test(command_context, tests, symbols_path, manifest_path, log):
options.local_lib = command_context.bindir.replace("bin", "fennec")
for file in os.listdir(os.path.join(command_context.topobjdir, "dist")):
if file.endswith(".apk") and file.startswith("fennec"):
options.local_apk = os.path.join(command_context.topobjdir, "dist", file)
options.local_apk = os.path.join(
command_context.topobjdir, "dist", file
)
log.info("using APK: " + options.local_apk)
break
@@ -585,6 +594,8 @@ def executable_name(name):
return name + ".exe" if sys.platform.startswith("win") else name
@CommandProvider
class SpiderMonkeyTests(MachCommandBase):
@Command(
"jstests",
category="testing",
@@ -596,7 +607,7 @@ def executable_name(name):
nargs=argparse.REMAINDER,
help="Extra arguments to pass down to the test harness.",
)
def run_jstests(command_context, shell, params):
def run_jstests(self, command_context, shell, params):
import subprocess
command_context.virtualenv_manager.ensure()
@@ -611,7 +622,6 @@ def run_jstests(command_context, shell, params):
return subprocess.call(jstest_cmd)
@Command(
"jit-test",
category="testing",
@@ -630,7 +640,7 @@ def run_jstests(command_context, shell, params):
nargs=argparse.REMAINDER,
help="Extra arguments to pass down to the test harness.",
)
def run_jittests(command_context, shell, cgc, params):
def run_jittests(self, command_context, shell, cgc, params):
import subprocess
command_context.virtualenv_manager.ensure()
@@ -639,7 +649,9 @@ def run_jittests(command_context, shell, cgc, params):
js = shell or os.path.join(command_context.bindir, executable_name("js"))
jittest_cmd = [
python,
os.path.join(command_context.topsrcdir, "js", "src", "jit-test", "jit_test.py"),
os.path.join(
command_context.topsrcdir, "js", "src", "jit-test", "jit_test.py"
),
js,
] + params
@@ -649,8 +661,9 @@ def run_jittests(command_context, shell, cgc, params):
return subprocess.call(jittest_cmd, env=env)
@Command("jsapi-tests", category="testing", description="Run SpiderMonkey JSAPI tests.")
@Command(
"jsapi-tests", category="testing", description="Run SpiderMonkey JSAPI tests."
)
@CommandArgument(
"test_name",
nargs="?",
@@ -658,7 +671,7 @@ def run_jittests(command_context, shell, cgc, params):
help="Test to run. Can be a prefix or omitted. If "
"omitted, the entire test suite is executed.",
)
def run_jsapitests(command_context, test_name=None):
def run_jsapitests(self, command_context, test_name=None):
import subprocess
jsapi_tests_cmd = [
@@ -675,8 +688,7 @@ def run_jsapitests(command_context, test_name=None):
print(f"jsapi-tests failed, exit code {result}")
return result
def run_check_js_msg(command_context):
def run_check_js_msg(self, command_context):
import subprocess
command_context.virtualenv_manager.ensure()
@@ -684,7 +696,9 @@ def run_check_js_msg(command_context):
check_cmd = [
python,
os.path.join(command_context.topsrcdir, "config", "check_js_msg_encoding.py"),
os.path.join(
command_context.topsrcdir, "config", "check_js_msg_encoding.py"
),
]
return subprocess.call(check_cmd)
@@ -696,19 +710,23 @@ def get_jsshell_parser():
return get_parser()
@CommandProvider
class JsShellTests(MachCommandBase):
@Command(
"jsshell-bench",
category="testing",
parser=get_jsshell_parser,
description="Run benchmarks in the SpiderMonkey JS shell.",
)
def run_jsshelltests(command_context, **kwargs):
def run_jsshelltests(self, command_context, **kwargs):
command_context.activate_virtualenv()
from jsshell import benchmark
return benchmark.run(**kwargs)
@CommandProvider
class CramTest(MachCommandBase):
@Command(
"cramtest",
category="testing",
@@ -727,7 +745,9 @@ def run_jsshelltests(command_context, **kwargs):
help="Extra arguments to pass down to the cram binary. See "
"'./mach python -m cram -- -h' for a list of available options.",
)
def cramtest(command_context, cram_args=None, test_paths=None, test_objects=None):
def cramtest(
self, command_context, cram_args=None, test_paths=None, test_objects=None
):
command_context.activate_virtualenv()
import mozinfo
from manifestparser import TestManifest
@@ -757,24 +777,26 @@ def cramtest(command_context, cram_args=None, test_paths=None, test_objects=None
return subprocess.call(cmd, cwd=command_context.topsrcdir)
@CommandProvider
class TestInfoCommand(MachCommandBase):
from datetime import date, timedelta
@Command(
"test-info", category="testing", description="Display historical test results."
)
def test_info(command_context):
def test_info(self, command_context):
"""
All functions implemented as subcommands.
"""
@SubCommand(
"test-info",
"tests",
description="Display historical test result summary for named tests.",
)
@CommandArgument("test_names", nargs=argparse.REMAINDER, help="Test(s) of interest.")
@CommandArgument(
"test_names", nargs=argparse.REMAINDER, help="Test(s) of interest."
)
@CommandArgument(
"--start",
default=(date.today() - timedelta(7)).strftime("%Y-%m-%d"),
@@ -795,6 +817,7 @@ def test_info(command_context):
)
@CommandArgument("--verbose", action="store_true", help="Enable debug logging.")
def test_info_tests(
self,
command_context,
test_names,
start,
@@ -814,7 +837,6 @@ def test_info_tests(
show_bugs,
)
@SubCommand(
"test-info",
"report",
@@ -876,6 +898,7 @@ def test_info_tests(
@CommandArgument("--output-file", help="Path to report file.")
@CommandArgument("--verbose", action="store_true", help="Enable debug logging.")
def test_report(
self,
command_context,
components,
flavor,
@@ -917,7 +940,6 @@ def test_report(
output_file,
)
@SubCommand(
"test-info",
"report-diff",
@@ -937,20 +959,22 @@ def test_report(
"will be written to standard output.",
)
@CommandArgument("--verbose", action="store_true", help="Enable debug logging.")
def test_report_diff(command_context, before, after, output_file, verbose):
def test_report_diff(self, command_context, before, after, output_file, verbose):
import testinfo
ti = testinfo.TestInfoReport(verbose)
ti.report_diff(before, after, output_file)
@CommandProvider
class RustTests(MachCommandBase):
@Command(
"rusttests",
category="testing",
conditions=[conditions.is_non_artifact_build],
description="Run rust unit tests (via cargo test).",
)
def run_rusttests(command_context, **kwargs):
def run_rusttests(self, command_context, **kwargs):
return command_context._mach_context.commands.dispatch(
"build",
command_context._mach_context,
@@ -958,13 +982,15 @@ def run_rusttests(command_context, **kwargs):
)
@CommandProvider
class TestFluentMigration(MachCommandBase):
@Command(
"fluent-migration-test",
category="testing",
description="Test Fluent migration recipes.",
)
@CommandArgument("test_paths", nargs="*", metavar="N", help="Recipe paths to test.")
def run_migration_tests(command_context, test_paths=None, **kwargs):
def run_migration_tests(self, command_context, test_paths=None, **kwargs):
if not test_paths:
test_paths = []
command_context.activate_virtualenv()

View File

@@ -13,10 +13,12 @@ import sys
from six import iteritems
from mach.decorators import (
CommandProvider,
Command,
)
from mozbuild.base import (
MachCommandBase,
MachCommandConditions as conditions,
BinaryNotFoundException,
)
@@ -61,6 +63,8 @@ def run_marionette(tests, binary=None, topsrcdir=None, **kwargs):
return 0
@CommandProvider
class MarionetteTest(MachCommandBase):
@Command(
"marionette-test",
category="testing",
@@ -68,7 +72,7 @@ def run_marionette(tests, binary=None, topsrcdir=None, **kwargs):
conditions=[functools.partial(conditions.is_buildapp_in, apps=SUPPORTED_APPS)],
parser=create_parser_tests,
)
def marionette_test(command_context, tests, **kwargs):
def marionette_test(self, command_context, tests, **kwargs):
if "test_objects" in kwargs:
tests = []
for obj in kwargs["test_objects"]:

View File

@@ -11,8 +11,10 @@ import sys
from functools import partial
from mach.decorators import (
CommandProvider,
Command,
)
from mozbuild.base import MachCommandBase
parser = None
@@ -59,6 +61,8 @@ def setup_marionette_argument_parser():
return parser
@CommandProvider
class MachCommands(MachCommandBase):
@Command(
"marionette-test",
category="testing",
@@ -66,6 +70,6 @@ def setup_marionette_argument_parser():
"using marionette).",
parser=setup_marionette_argument_parser,
)
def run_marionette_test(command_context, **kwargs):
def run_marionette_test(self, command_context, **kwargs):
command_context.context.activate_mozharness_venv()
return run_marionette(command_context.context, **kwargs)

View File

@@ -14,12 +14,14 @@ import sys
import warnings
from mozbuild.base import (
MachCommandBase,
MachCommandConditions as conditions,
MozbuildObject,
)
from mach.decorators import (
CommandArgument,
CommandProvider,
Command,
)
@@ -295,6 +297,8 @@ def verify_host_bin():
return 0
@CommandProvider
class MachCommands(MachCommandBase):
@Command(
"mochitest",
category="testing",
@@ -303,7 +307,12 @@ def verify_host_bin():
parser=setup_argument_parser,
)
def run_mochitest_general(
command_context, flavor=None, test_objects=None, resolve_tests=True, **kwargs
self,
command_context,
flavor=None,
test_objects=None,
resolve_tests=True,
**kwargs
):
from mochitest_options import ALL_FLAVORS
from mozlog.commandline import setup_logging
@@ -330,7 +339,9 @@ def run_mochitest_general(
break
else:
flavors = [
f for f, v in six.iteritems(ALL_FLAVORS) if buildapp in v["enabled_apps"]
f
for f, v in six.iteritems(ALL_FLAVORS)
if buildapp in v["enabled_apps"]
]
from mozbuild.controller.building import BuildDriver
@@ -355,7 +366,9 @@ def run_mochitest_general(
if not kwargs.get("log"):
# Create shared logger
format_args = {"level": command_context._mach_context.settings["test"]["level"]}
format_args = {
"level": command_context._mach_context.settings["test"]["level"]
}
if len(tests) == 1:
format_args["verbose"] = True
format_args["compact"] = False
@@ -465,7 +478,9 @@ def run_mochitest_general(
if not app:
app = "org.mozilla.geckoview.test"
device_serial = kwargs.get("deviceSerial")
install = InstallIntent.NO if kwargs.get("no_install") else InstallIntent.YES
install = (
InstallIntent.NO if kwargs.get("no_install") else InstallIntent.YES
)
# verify installation
verify_android_device(
@@ -493,9 +508,7 @@ def run_mochitest_general(
# specific mochitest suite has to be loaded. See Bug 1637463.
harness_args.update({"suite_name": suite_name})
result = run_mochitest(
command_context._mach_context, tests=tests, **harness_args
)
result = run_mochitest(command_context, tests=tests, **harness_args)
if result:
overall = result
@@ -511,6 +524,8 @@ def run_mochitest_general(
return overall
@CommandProvider
class GeckoviewJunitCommands(MachCommandBase):
@Command(
"geckoview-junit",
category="testing",
@@ -525,7 +540,7 @@ def run_mochitest_general(
action="store_true",
default=False,
)
def run_junit(command_context, no_install, **kwargs):
def run_junit(self, command_context, no_install, **kwargs):
command_context._ensure_state_subdir_exists(".")
from mozrunner.devices.android_device import (
@@ -551,11 +566,15 @@ def run_junit(command_context, no_install, **kwargs):
if not kwargs.get("log"):
from mozlog.commandline import setup_logging
format_args = {"level": command_context._mach_context.settings["test"]["level"]}
format_args = {
"level": command_context._mach_context.settings["test"]["level"]
}
default_format = command_context._mach_context.settings["test"]["format"]
kwargs["log"] = setup_logging(
"mach-mochitest", kwargs, {default_format: sys.stdout}, format_args
)
mochitest = command_context._spawn(MochitestRunner)
return mochitest.run_geckoview_junit_test(command_context._mach_context, **kwargs)
return mochitest.run_geckoview_junit_test(
command_context._mach_context, **kwargs
)

View File

@@ -11,8 +11,10 @@ from argparse import Namespace
from functools import partial
from mach.decorators import (
CommandProvider,
Command,
)
from mozbuild.base import MachCommandBase
here = os.path.abspath(os.path.dirname(__file__))
parser = None
@@ -191,23 +193,24 @@ def setup_junit_argument_parser():
return parser
@CommandProvider
class MochitestCommands(MachCommandBase):
@Command(
"mochitest",
category="testing",
description="Run the mochitest harness.",
parser=setup_mochitest_argument_parser,
)
def mochitest(command_context, **kwargs):
def mochitest(self, command_context, **kwargs):
command_context._mach_context.activate_mozharness_venv()
return run_test(command_context._mach_context, False, **kwargs)
@Command(
"geckoview-junit",
category="testing",
description="Run the geckoview-junit harness.",
parser=setup_junit_argument_parser,
)
def geckoview_junit(command_context, **kwargs):
def geckoview_junit(self, command_context, **kwargs):
command_context._mach_context.activate_mozharness_venv()
return run_test(command_context._mach_context, True, **kwargs)

View File

@@ -16,10 +16,11 @@ from six.moves.urllib.request import pathname2url
from mach.decorators import (
CommandArgument,
CommandProvider,
Command,
)
from mozbuild.base import MozbuildObject
from mozbuild.base import MachCommandBase, MozbuildObject
from mozbuild.base import MachCommandConditions as conditions
from argparse import ArgumentParser
@@ -206,6 +207,8 @@ class MozharnessRunner(MozbuildObject):
return rv
@CommandProvider
class MozharnessCommands(MachCommandBase):
@Command(
"mozharness",
category="testing",
@@ -213,6 +216,6 @@ class MozharnessRunner(MozbuildObject):
conditions=[conditions.is_firefox_or_android],
parser=get_parser,
)
def mozharness(command_context, **kwargs):
def mozharness(self, command_context, **kwargs):
runner = command_context._spawn(MozharnessRunner)
return runner.run_suite(kwargs.pop("suite_name")[0], **kwargs)

View File

@@ -17,9 +17,10 @@ import subprocess
import sys
import mozfile
from mach.decorators import Command
from mach.decorators import Command, CommandProvider
from mozboot.util import get_state_dir
from mozbuild.base import (
MachCommandBase,
MozbuildObject,
BinaryNotFoundException,
)
@@ -309,20 +310,24 @@ def create_parser():
return create_parser(mach_interface=True)
@CommandProvider
class MachRaptor(MachCommandBase):
@Command(
"raptor",
category="testing",
description="Run Raptor performance tests.",
parser=create_parser,
)
def run_raptor(command_context, **kwargs):
def run_raptor(self, command_context, **kwargs):
# Defers this import so that a transitive dependency doesn't
# stop |mach bootstrap| from running
from raptor.power import enable_charging, disable_charging
build_obj = command_context
is_android = Conditions.is_android(build_obj) or kwargs["app"] in ANDROID_BROWSERS
is_android = (
Conditions.is_android(build_obj) or kwargs["app"] in ANDROID_BROWSERS
)
if is_android:
from mozrunner.devices.android_device import (
@@ -332,7 +337,9 @@ def run_raptor(command_context, **kwargs):
from mozdevice import ADBDeviceFactory
install = (
InstallIntent.NO if kwargs.pop("noinstall", False) else InstallIntent.YES
InstallIntent.NO
if kwargs.pop("noinstall", False)
else InstallIntent.YES
)
verbose = False
if (
@@ -383,12 +390,11 @@ def run_raptor(command_context, **kwargs):
if kwargs["power_test"] and device:
enable_charging(device)
@Command(
"raptor-test",
category="testing",
description="Run Raptor performance tests.",
parser=create_parser,
)
def run_raptor_test(command_context, **kwargs):
return run_raptor(command_context, **kwargs)
def run_raptor_test(self, command_context, **kwargs):
return self.run_raptor(command_context, **kwargs)

View File

@@ -15,9 +15,10 @@ import socket
from mozbuild.base import (
MozbuildObject,
MachCommandBase,
BinaryNotFoundException,
)
from mach.decorators import Command
from mach.decorators import CommandProvider, Command
HERE = os.path.dirname(os.path.realpath(__file__))
@@ -122,13 +123,15 @@ def create_parser():
return create_parser(mach_interface=True)
@CommandProvider
class MachCommands(MachCommandBase):
@Command(
"talos-test",
category="testing",
description="Run talos tests (performance testing).",
parser=create_parser,
)
def run_talos_test(command_context, **kwargs):
def run_talos_test(self, command_context, **kwargs):
talos = command_context._spawn(TalosRunner)
try:

View File

@@ -5,14 +5,18 @@
from __future__ import absolute_import, print_function
import os
from mach.decorators import Command, CommandArgument
from mach.decorators import Command, CommandArgument, CommandProvider
from mozbuild.base import MachCommandBase
from mozpack.copier import Jarrer
from mozpack.files import FileFinder
@CommandProvider
class MachCommands(MachCommandBase):
@Command("tps-build", category="testing", description="Build TPS add-on.")
@CommandArgument("--dest", default=None, help="Where to write add-on.")
def build(command_context, dest):
def build(self, command_context, dest):
"""TPS tests for Sync."""
src = os.path.join(
command_context.topsrcdir, "services", "sync", "tps", "extensions", "tps"
)

View File

@@ -12,11 +12,13 @@ import sys
from six import iteritems
from mozbuild.base import (
MachCommandBase,
MachCommandConditions as conditions,
MozbuildObject,
)
from mach.decorators import (
CommandProvider,
Command,
)
@@ -466,10 +468,12 @@ def create_parser_testpaths():
return parser
@CommandProvider
class MachCommands(MachCommandBase):
@staticmethod
def setup(command_context):
command_context.activate_virtualenv()
@Command(
"web-platform-tests",
category="testing",
@@ -477,8 +481,8 @@ def setup(command_context):
description="Run web-platform-tests.",
parser=create_parser_wpt,
)
def run_web_platform_tests(command_context, **params):
setup(command_context)
def run_web_platform_tests(self, command_context, **params):
self.setup(command_context)
if params["product"] is None:
if conditions.is_android(command_context):
params["product"] = "firefox_android"
@@ -510,11 +514,12 @@ def run_web_platform_tests(command_context, **params):
conditions.is_android(command_context)
and params["product"] != "firefox_android"
):
logger.warning("Must specify --product=firefox_android in Android environment.")
logger.warning(
"Must specify --product=firefox_android in Android environment."
)
return wpt_runner.run(logger, **params)
@Command(
"wpt",
category="testing",
@@ -522,9 +527,8 @@ def run_web_platform_tests(command_context, **params):
description="Run web-platform-tests.",
parser=create_parser_wpt,
)
def run_wpt(command_context, **params):
return run_web_platform_tests(command_context, **params)
def run_wpt(self, command_context, **params):
return self.run_web_platform_tests(command_context, **params)
@Command(
"web-platform-tests-update",
@@ -532,8 +536,8 @@ def run_wpt(command_context, **params):
description="Update web-platform-test metadata.",
parser=create_parser_update,
)
def update_web_platform_tests(command_context, **params):
setup(command_context)
def update_web_platform_tests(self, command_context, **params):
self.setup(command_context)
command_context.virtualenv_manager.install_pip_package("html5lib==1.0.1")
command_context.virtualenv_manager.install_pip_package("ujson")
command_context.virtualenv_manager.install_pip_package("requests")
@@ -542,16 +546,14 @@ def update_web_platform_tests(command_context, **params):
logger = wpt_updater.setup_logging(**params)
return wpt_updater.run_update(logger, **params)
@Command(
"wpt-update",
category="testing",
description="Update web-platform-test metadata.",
parser=create_parser_update,
)
def update_wpt(command_context, **params):
return update_web_platform_tests(command_context, **params)
def update_wpt(self, command_context, **params):
return self.update_web_platform_tests(command_context, **params)
@Command(
"wpt-manifest-update",
@@ -559,8 +561,8 @@ def update_wpt(command_context, **params):
description="Update web-platform-test manifests.",
parser=create_parser_manifest_update,
)
def wpt_manifest_update(command_context, **params):
setup(command_context)
def wpt_manifest_update(self, command_context, **params):
self.setup(command_context)
wpt_setup = command_context._spawn(WebPlatformTestsRunnerSetup)
wpt_runner = WebPlatformTestsRunner(wpt_setup)
logger = wpt_runner.setup_logging(**params)
@@ -570,15 +572,14 @@ def wpt_manifest_update(command_context, **params):
)
return 0 if wpt_runner.update_manifest(logger, **params) else 1
@Command(
"wpt-serve",
category="testing",
description="Run the wpt server",
parser=create_parser_serve,
)
def wpt_serve(command_context, **params):
setup(command_context)
def wpt_serve(self, command_context, **params):
self.setup(command_context)
import logging
logger = logging.getLogger("web-platform-tests")
@@ -586,61 +587,58 @@ def wpt_serve(command_context, **params):
wpt_serve = command_context._spawn(WebPlatformTestsServeRunner)
return wpt_serve.run(**params)
@Command(
"wpt-metadata-summary",
category="testing",
description="Create a json summary of the wpt metadata",
parser=create_parser_metadata_summary,
)
def wpt_summary(command_context, **params):
def wpt_summary(self, command_context, **params):
import metasummary
wpt_setup = command_context._spawn(WebPlatformTestsRunnerSetup)
return metasummary.run(wpt_setup.topsrcdir, wpt_setup.topobjdir, **params)
@Command("wpt-metadata-merge", category="testing", parser=create_parser_metadata_merge)
def wpt_meta_merge(command_context, **params):
@Command(
"wpt-metadata-merge", category="testing", parser=create_parser_metadata_merge
)
def wpt_meta_merge(self, command_context, **params):
import metamerge
if params["dest"] is None:
params["dest"] = params["current"]
return metamerge.run(**params)
@Command(
"wpt-unittest",
category="testing",
description="Run the wpt tools and wptrunner unit tests",
parser=create_parser_unittest,
)
def wpt_unittest(command_context, **params):
setup(command_context)
def wpt_unittest(self, command_context, **params):
self.setup(command_context)
command_context.virtualenv_manager.install_pip_package("tox")
runner = command_context._spawn(WebPlatformTestsUnittestRunner)
return 0 if runner.run(**params) else 1
@Command(
"wpt-test-paths",
category="testing",
description="Get a mapping from test ids to files",
parser=create_parser_testpaths,
)
def wpt_test_paths(command_context, **params):
def wpt_test_paths(self, command_context, **params):
runner = command_context._spawn(WebPlatformTestsTestPathsRunner)
runner.run(**params)
return 0
@Command(
"wpt-fission-regressions",
category="testing",
description="Dump a list of fission-specific regressions",
parser=create_parser_fission_regressions,
)
def wpt_fission_regressions(command_context, **params):
def wpt_fission_regressions(self, command_context, **params):
runner = command_context._spawn(WebPlatformTestsFissionRegressionsRunner)
runner.run(**params)
return 0

View File

@@ -9,8 +9,10 @@ import sys
from mach_commands_base import WebPlatformTestsRunner, create_parser_wpt
from mach.decorators import (
CommandProvider,
Command,
)
from mozbuild.base import MachCommandBase
class WebPlatformTestsRunnerSetup(object):
@@ -70,14 +72,15 @@ class WebPlatformTestsRunnerSetup(object):
raise NotImplementedError
@CommandProvider
class MachCommands(MachCommandBase):
@Command("web-platform-tests", category="testing", parser=create_parser_wpt)
def run_web_platform_tests(command_context, **kwargs):
def run_web_platform_tests(self, command_context, **kwargs):
command_context._mach_context.activate_mozharness_venv()
return WebPlatformTestsRunner(
WebPlatformTestsRunnerSetup(command_context._mach_context)
).run(**kwargs)
@Command("wpt", category="testing", parser=create_parser_wpt)
def run_wpt(command_context, **params):
def run_wpt(self, command_context, **params):
return command_context.run_web_platform_tests(**params)

View File

@@ -14,12 +14,14 @@ import sys
from mozlog import structured
from mozbuild.base import (
MachCommandBase,
MozbuildObject,
MachCommandConditions as conditions,
BinaryNotFoundException,
)
from mach.decorators import (
CommandProvider,
Command,
)
@@ -217,6 +219,8 @@ def get_parser():
return parser_desktop()
@CommandProvider
class MachCommands(MachCommandBase):
@Command(
"xpcshell-test",
category="testing",
@@ -224,7 +228,7 @@ def get_parser():
conditions=[lambda *args: True],
parser=get_parser,
)
def run_xpcshell_test(command_context, test_objects=None, **params):
def run_xpcshell_test(self, command_context, test_objects=None, **params):
from mozbuild.controller.building import BuildDriver
if test_objects is not None:

View File

@@ -14,8 +14,10 @@ import mozlog
from xpcshellcommandline import parser_desktop
from mach.decorators import (
CommandProvider,
Command,
)
from mozbuild.base import MachCommandBase
def run_xpcshell(context, **kwargs):
@@ -50,12 +52,14 @@ def run_xpcshell(context, **kwargs):
return xpcshell.runTests(**vars(args))
@CommandProvider
class MochitestCommands(MachCommandBase):
@Command(
"xpcshell-test",
category="testing",
description="Run the xpcshell harness.",
parser=parser_desktop,
)
def xpcshell(command_context, **kwargs):
def xpcshell(self, command_context, **kwargs):
command_context._mach_context.activate_mozharness_venv()
return run_xpcshell(command_context._mach_context, **kwargs)

View File

@@ -2,9 +2,12 @@
# 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/.
from mach.decorators import Command, CommandArgument
from mach.decorators import CommandProvider, Command, CommandArgument
from mozbuild.base import MachCommandBase
@CommandProvider
class DataReviewer(MachCommandBase):
@Command(
"data-review",
category="misc",
@@ -13,7 +16,7 @@ from mach.decorators import Command, CommandArgument
@CommandArgument(
"bug", default=None, nargs="?", type=str, help="bug number or search pattern"
)
def data_review(command_context, bug=None):
def data_review(self, command_context, bug=None):
# Get the metrics_index's list of metrics indices
# by loading the index as a module.
from os import path

View File

@@ -8,9 +8,10 @@ import logging
import os
import sys
from mach.decorators import Command
from mach.decorators import CommandProvider, Command
from mozbuild.base import (
MachCommandBase,
MachCommandConditions as conditions,
BinaryNotFoundException,
)
@@ -64,6 +65,8 @@ def run_telemetry(tests, binary=None, topsrcdir=None, **kwargs):
return 0
@CommandProvider
class TelemetryTest(MachCommandBase):
@Command(
"telemetry-tests-client",
category="testing",
@@ -71,7 +74,7 @@ def run_telemetry(tests, binary=None, topsrcdir=None, **kwargs):
conditions=[conditions.is_firefox_or_android],
parser=create_parser_tests,
)
def telemetry_test(command_context, tests, **kwargs):
def telemetry_test(self, command_context, tests, **kwargs):
if "test_objects" in kwargs:
tests = []
for obj in kwargs["test_objects"]:

View File

@@ -42,7 +42,7 @@ import sys
import time
from six import StringIO
from mach.decorators import CommandArgument, Command
from mach.decorators import CommandArgument, CommandProvider, Command
from mozbuild.base import MachCommandBase, BinaryNotFoundException
from mozbuild.util import mkdir
import mozpack.path as mozpath
@@ -151,19 +151,21 @@ host_fetches = {
}
def artifact_cache_path(command_context):
@CommandProvider
class MachBrowsertime(MachCommandBase):
def artifact_cache_path(self, command_context):
r"""Downloaded artifacts will be kept here."""
# The convention is $MOZBUILD_STATE_PATH/cache/$FEATURE.
return mozpath.join(command_context._mach_context.state_dir, "cache", "browsertime")
return mozpath.join(
command_context._mach_context.state_dir, "cache", "browsertime"
)
def state_path(command_context):
def state_path(self, command_context):
r"""Unpacked artifacts will be kept here."""
# The convention is $MOZBUILD_STATE_PATH/$FEATURE.
return mozpath.join(command_context._mach_context.state_dir, "browsertime")
def setup_prerequisites(command_context):
def setup_prerequisites(self, command_context):
r"""Install browsertime and visualmetrics.py prerequisites."""
from mozbuild.action.tooltool import unpack_file
@@ -189,7 +191,7 @@ def setup_prerequisites(command_context):
# Download the visualmetrics.py requirements.
artifact_cache = ArtifactCache(
artifact_cache_path(command_context),
self.artifact_cache_path(command_context),
log=command_context.log,
skip_cache=False,
)
@@ -202,8 +204,8 @@ def setup_prerequisites(command_context):
if fetch.get("unpack", True):
cwd = os.getcwd()
try:
mkdir(state_path(command_context))
os.chdir(state_path(command_context))
mkdir(self.state_path(command_context))
os.chdir(self.state_path(command_context))
command_context.log(
logging.INFO,
"browsertime",
@@ -216,15 +218,19 @@ def setup_prerequisites(command_context):
# so we make one for it here
mkdir(fetch.get("path"))
os.chdir(
os.path.join(state_path(command_context), fetch.get("path"))
os.path.join(
self.state_path(command_context), fetch.get("path")
)
)
unpack_file(archive)
os.chdir(state_path(command_context))
os.chdir(self.state_path(command_context))
else:
unpack_file(archive)
# Make sure the expected path exists after extraction
path = os.path.join(state_path(command_context), fetch.get("path"))
path = os.path.join(
self.state_path(command_context), fetch.get("path")
)
if not os.path.exists(path):
raise Exception("Cannot find an extracted directory: %s" % path)
@@ -242,20 +248,24 @@ def setup_prerequisites(command_context):
os.chmod(loc_to_change, st.st_mode | stat.S_IEXEC)
except Exception as e:
raise Exception(
"Could not set executable bit in %s, error: %s" % (path, str(e))
"Could not set executable bit in %s, error: %s"
% (path, str(e))
)
finally:
os.chdir(cwd)
def setup_browsertime(command_context, should_clobber=False, new_upstream_url=""):
def setup_browsertime(
self, command_context, should_clobber=False, new_upstream_url=""
):
r"""Install browsertime and visualmetrics.py prerequisites and the Node.js package."""
sys.path.append(mozpath.join(command_context.topsrcdir, "tools", "lint", "eslint"))
sys.path.append(
mozpath.join(command_context.topsrcdir, "tools", "lint", "eslint")
)
import setup_helper
if not new_upstream_url:
setup_prerequisites(command_context)
self.setup_prerequisites(command_context)
if new_upstream_url:
package_json_path = os.path.join(BROWSERTIME_ROOT, "package.json")
@@ -321,21 +331,19 @@ def setup_browsertime(command_context, should_clobber=False, new_upstream_url=""
if new_upstream_url or AUTOMATION:
return 0
return check(command_context)
return self.check(command_context)
def node(command_context, args):
def node(self, command_context, args):
r"""Invoke node (interactively) with the given arguments."""
return command_context.run_process(
[node_path()] + args,
append_env=append_env(command_context),
append_env=self.append_env(command_context),
pass_thru=True, # Allow user to run Node interactively.
ensure_exit_code=False, # Don't throw on non-zero exit code.
cwd=mozpath.join(command_context.topsrcdir),
)
def append_env(command_context, append_path=True):
def append_env(self, command_context, append_path=True):
fetches = host_fetches[host_platform()]
# Ensure that bare `ffmpeg` and ImageMagick commands
@@ -344,13 +352,13 @@ def append_env(command_context, append_path=True):
# We should update the script itself to accept this configuration.
path = os.environ.get("PATH", "").split(os.pathsep) if append_path else []
path_to_ffmpeg = mozpath.join(
state_path(command_context), fetches["ffmpeg"]["path"]
self.state_path(command_context), fetches["ffmpeg"]["path"]
)
path_to_imagemagick = None
if "ImageMagick" in fetches:
path_to_imagemagick = mozpath.join(
state_path(command_context), fetches["ImageMagick"]["path"]
self.state_path(command_context), fetches["ImageMagick"]["path"]
)
if path_to_imagemagick:
@@ -358,7 +366,7 @@ def append_env(command_context, append_path=True):
# want to ensure that our ffmpeg goes first, just in case.
path.insert(
0,
state_path(command_context)
self.state_path(command_context)
if host_platform().startswith("win")
else mozpath.join(path_to_imagemagick, "bin"),
) # noqa
@@ -425,8 +433,7 @@ def append_env(command_context, append_path=True):
return append_env
def _need_install(command_context, package):
def _need_install(self, command_context, package):
from pip._internal.req.constructors import install_req_from_line
req = install_req_from_line(package)
@@ -439,8 +446,7 @@ def _need_install(command_context, package):
site_packages = os.path.abspath(req.satisfied_by.location)
return not site_packages.startswith(venv_site_lib)
def activate_browsertime_virtualenv(command_context, *args, **kwargs):
def activate_browsertime_virtualenv(self, command_context, *args, **kwargs):
r"""Activates virtualenv.
This function will also install Pillow and pyssim if needed.
@@ -450,21 +456,21 @@ def activate_browsertime_virtualenv(command_context, *args, **kwargs):
# installing Python deps on the fly
for dep in ("Pillow==%s" % PILLOW_VERSION, "pyssim==%s" % PYSSIM_VERSION):
if _need_install(command_context, dep):
if self._need_install(command_context, dep):
command_context.virtualenv_manager._run_pip(["install", dep])
def check(command_context):
def check(self, command_context):
r"""Run `visualmetrics.py --check`."""
command_context.activate_virtualenv()
args = ["--check"]
status = command_context.run_process(
[command_context.virtualenv_manager.python_path, visualmetrics_path()] + args,
[command_context.virtualenv_manager.python_path, visualmetrics_path()]
+ args,
# For --check, don't allow user's path to interfere with
# path testing except on Linux, where ImageMagick needs to
# be installed manually.
append_env=append_env(
append_env=self.append_env(
command_context, append_path=host_platform().startswith("linux")
),
pass_thru=True,
@@ -485,10 +491,9 @@ def check(command_context):
sys.stdout.flush()
sys.stderr.flush()
return node(command_context, [browsertime_path()] + ["--version"])
return self.node(command_context, [browsertime_path()] + ["--version"])
def extra_default_args(command_context, args=[]):
def extra_default_args(self, command_context, args=[]):
# Add Mozilla-specific default arguments. This is tricky because browsertime is quite
# loose about arguments; repeat arguments are generally accepted but then produce
# difficult to interpret type errors.
@@ -568,10 +573,11 @@ def extra_default_args(command_context, args=[]):
return extra_args
def _verify_node_install(command_context):
def _verify_node_install(self, command_context):
# check if Node is installed
sys.path.append(mozpath.join(command_context.topsrcdir, "tools", "lint", "eslint"))
sys.path.append(
mozpath.join(command_context.topsrcdir, "tools", "lint", "eslint")
)
import setup_helper
with silence():
@@ -589,7 +595,6 @@ def _verify_node_install(command_context):
return True
@Command(
"browsertime",
category="testing",
@@ -619,6 +624,7 @@ def _verify_node_install(command_context):
)
@CommandArgument("args", nargs=argparse.REMAINDER)
def browsertime(
self,
command_context,
args,
verbose=False,
@@ -652,21 +658,23 @@ def browsertime(
time.sleep(5)
if update_upstream_url:
return setup_browsertime(command_context, new_upstream_url=update_upstream_url)
return self.setup_browsertime(
command_context, new_upstream_url=update_upstream_url
)
elif setup:
return setup_browsertime(command_context, should_clobber=clobber)
return self.setup_browsertime(command_context, should_clobber=clobber)
else:
if not _verify_node_install(command_context):
if not self._verify_node_install(command_context):
return 1
if check:
return check(command_context)
return self.check(command_context)
if browsertime_help:
args.append("--help")
activate_browsertime_virtualenv(command_context)
default_args = extra_default_args(command_context, args)
self.activate_browsertime_virtualenv(command_context)
default_args = self.extra_default_args(command_context, args)
if default_args == 1:
return 1
return node(command_context, [browsertime_path()] + default_args + args)
return self.node(command_context, [browsertime_path()] + default_args + args)

View File

@@ -8,9 +8,11 @@ from appdirs import user_config_dir
from hglib.error import CommandError
from mach.decorators import (
CommandArgument,
CommandProvider,
Command,
)
from mach.base import FailedCommandError
from mozbuild.base import MachCommandBase
from mozrelease.scriptworker_canary import get_secret
from pathlib import Path
from redo import retry
@@ -20,6 +22,8 @@ import os
import tempfile
@CommandProvider
class CompareLocales(MachCommandBase):
@Command(
"compare-locales",
category="build",
@@ -40,7 +44,7 @@ import tempfile
"locales",
nargs="*",
metavar="locale-code",
help="Locale code and top-level directory of each localization",
help="Locale code and top-level directory of " "each localization",
)
@CommandArgument(
"-q",
@@ -52,7 +56,9 @@ Specified once, don't show obsolete entities. Specified twice, also hide
missing entities. Specify thrice to exclude warnings and four times to
just show stats""",
)
@CommandArgument("-m", "--merge", help="""Use this directory to stage merged files""")
@CommandArgument(
"-m", "--merge", help="""Use this directory to stage merged files"""
)
@CommandArgument(
"--validate", action="store_true", help="Run compare-locales against reference"
)
@@ -76,7 +82,7 @@ the output file, pass "-" to serialize to stdout and hide the default output.
@CommandArgument(
"--return-zero", action="store_true", help="Return 0 regardless of l10n status"
)
def compare(command_context, **kwargs):
def compare(self, command_context, **kwargs):
"""Run compare-locales."""
from compare_locales.commands import CompareLocales
@@ -114,6 +120,8 @@ FXTREE_PATH = VCT_PATH / "hgext" / "firefoxtree"
HGRC_PATH = Path(user_config_dir("hg")).joinpath("hgrc")
@CommandProvider
class CrossChannel(MachCommandBase):
@Command(
"l10n-cross-channel",
category="misc",
@@ -157,6 +165,7 @@ HGRC_PATH = Path(user_config_dir("hg")).joinpath("hgrc")
""",
)
def cross_channel(
self,
command_context,
strings_path,
outgoing_path,
@@ -182,7 +191,7 @@ def cross_channel(
try:
with tempfile.TemporaryDirectory() as ssh_key_dir:
retry(
_do_create_content,
self._do_create_content,
attempts=attempts,
retry_exceptions=(RetryError,),
args=(
@@ -197,8 +206,8 @@ def cross_channel(
except RetryError as exc:
raise FailedCommandError(exc) from exc
def _do_create_content(
self,
command_context,
strings_path,
outgoing_path,
@@ -228,7 +237,7 @@ def _do_create_content(
ssh_key_file.chmod(0o600)
# Set up firefoxtree for comm per bug 1659691 comment 22
if os.environ.get("MOZ_AUTOMATION") and not HGRC_PATH.exists():
_clone_hg_repo(command_context, VCT_URL, VCT_PATH)
self._clone_hg_repo(command_context, VCT_URL, VCT_PATH)
hgrc_content = [
"[extensions]",
f"firefoxtree = {FXTREE_PATH}",
@@ -243,18 +252,20 @@ def _do_create_content(
]
)
HGRC_PATH.write_text("\n".join(hgrc_content))
if strings_path.exists() and _check_outgoing(command_context, strings_path):
_strip_outgoing(command_context, strings_path)
if strings_path.exists() and self._check_outgoing(
command_context, strings_path
):
self._strip_outgoing(command_context, strings_path)
# Clone strings + source repos, pull heads
for repo_config in (config["strings"], *config["source"].values()):
if not repo_config["path"].exists():
_clone_hg_repo(
self._clone_hg_repo(
command_context, repo_config["url"], str(repo_config["path"])
)
for head in repo_config["heads"].keys():
command = ["hg", "--cwd", str(repo_config["path"]), "pull"]
command.append(head)
status = _retry_run_process(
status = self._retry_run_process(
command_context, command, ensure_exit_code=False
)
if status not in (0, 255): # 255 on pull with no changes
@@ -269,32 +280,34 @@ def _do_create_content(
"-r",
head,
]
status = _retry_run_process(
status = self._retry_run_process(
command_context, command, ensure_exit_code=False
)
if status not in (0, 255): # 255 on pull with no changes
raise RetryError(f"Failure on update: status {status}!")
_check_hg_repo(
self._check_hg_repo(
command_context,
repo_config["path"],
heads=repo_config.get("heads", {}).keys(),
)
else:
_check_hg_repo(command_context, strings_path)
self._check_hg_repo(command_context, strings_path)
for repo_config in config.get("source", {}).values():
_check_hg_repo(
self._check_hg_repo(
command_context,
repo_config["path"],
heads=repo_config.get("heads", {}).keys(),
)
if _check_outgoing(command_context, strings_path):
if self._check_outgoing(command_context, strings_path):
raise RetryError(f"check: Outgoing changes in {strings_path}!")
if "create" in actions:
try:
status = ccc.create_content()
changes = True
_create_outgoing_patch(command_context, outgoing_path, strings_path)
self._create_outgoing_patch(
command_context, outgoing_path, strings_path
)
except CommandError as exc:
if exc.ret != 1:
raise RetryError(exc) from exc
@@ -302,7 +315,7 @@ def _do_create_content(
if "push" in actions:
if changes:
_retry_run_process(
self._retry_run_process(
command_context,
[
"hg",
@@ -320,9 +333,8 @@ def _do_create_content(
return status
def _check_outgoing(command_context, strings_path):
status = _retry_run_process(
def _check_outgoing(self, command_context, strings_path):
status = self._retry_run_process(
command_context,
["hg", "--cwd", str(strings_path), "out", "-r", "."],
ensure_exit_code=False,
@@ -331,11 +343,12 @@ def _check_outgoing(command_context, strings_path):
return True
if status == 1:
return False
raise RetryError(f"Outgoing check in {strings_path} returned unexpected {status}!")
raise RetryError(
f"Outgoing check in {strings_path} returned unexpected {status}!"
)
def _strip_outgoing(command_context, strings_path):
_retry_run_process(
def _strip_outgoing(self, command_context, strings_path):
self._retry_run_process(
command_context,
[
"hg",
@@ -349,8 +362,7 @@ def _strip_outgoing(command_context, strings_path):
],
)
def _create_outgoing_patch(command_context, path, strings_path):
def _create_outgoing_patch(self, command_context, path, strings_path):
if not path:
return
if not path.parent.exists():
@@ -360,7 +372,7 @@ def _create_outgoing_patch(command_context, path, strings_path):
def writeln(line):
fh.write(f"{line}\n")
_retry_run_process(
self._retry_run_process(
command_context,
[
"hg",
@@ -375,25 +387,22 @@ def _create_outgoing_patch(command_context, path, strings_path):
line_handler=writeln,
)
def _retry_run_process(command_context, *args, error_msg=None, **kwargs):
def _retry_run_process(self, command_context, *args, error_msg=None, **kwargs):
try:
return command_context.run_process(*args, **kwargs)
except Exception as exc:
raise RetryError(error_msg or str(exc)) from exc
def _check_hg_repo(command_context, path, heads=None):
def _check_hg_repo(self, command_context, path, heads=None):
if not (path.is_dir() and (path / ".hg").is_dir()):
raise RetryError(f"{path} is not a Mercurial repository")
if heads:
for head in heads:
_retry_run_process(
self._retry_run_process(
command_context,
["hg", "--cwd", str(path), "log", "-r", head],
error_msg=f"check: {path} has no head {head}!",
)
def _clone_hg_repo(command_context, url, path):
_retry_run_process(command_context, ["hg", "clone", url, str(path)])
def _clone_hg_repo(self, command_context, url, path):
self._retry_run_process(command_context, ["hg", "clone", url, str(path)])

View File

@@ -8,11 +8,13 @@ import os
from mozbuild.base import (
BuildEnvironmentNotFoundException,
MachCommandBase,
)
from mach.decorators import (
CommandArgument,
CommandProvider,
Command,
)
@@ -59,13 +61,15 @@ def get_global_excludes(topsrcdir):
return excludes
@CommandProvider
class MachCommands(MachCommandBase):
@Command(
"lint",
category="devenv",
description="Run linters.",
parser=setup_argument_parser,
)
def lint(command_context, *runargs, **lintargs):
def lint(self, command_context, *runargs, **lintargs):
"""Run linters."""
command_context.activate_virtualenv()
from mozlint import cli, parser
@@ -90,7 +94,6 @@ def lint(command_context, *runargs, **lintargs):
)
return cli.run(*runargs, **lintargs)
@Command(
"eslint",
category="devenv",
@@ -123,7 +126,7 @@ def lint(command_context, *runargs, **lintargs):
nargs=argparse.REMAINDER,
help="Extra args that will be forwarded to eslint.",
)
def eslint(command_context, paths, extra_args=[], **kwargs):
def eslint(self, command_context, paths, extra_args=[], **kwargs):
command_context._mach_context.commands.dispatch(
"lint",
command_context._mach_context,
@@ -133,14 +136,13 @@ def eslint(command_context, paths, extra_args=[], **kwargs):
**kwargs
)
@Command(
"format",
category="devenv",
description="Format files, alternative to 'lint --fix' ",
parser=setup_argument_parser,
)
def format_files(command_context, paths, extra_args=[], **kwargs):
def format_files(self, command_context, paths, extra_args=[], **kwargs):
linters = kwargs["linters"]
if not linters:
@@ -159,5 +161,9 @@ def format_files(command_context, paths, extra_args=[], **kwargs):
kwargs["fix"] = True
command_context._mach_context.commands.dispatch(
"lint", command_context._mach_context, paths=paths, argv=extra_args, **kwargs
"lint",
command_context._mach_context,
paths=paths,
argv=extra_args,
**kwargs
)

View File

@@ -12,11 +12,12 @@ import sys
from mach.decorators import (
CommandArgument,
CommandProvider,
Command,
SubCommand,
)
from mozbuild.base import MozbuildObject
from mozbuild.base import MachCommandBase, MozbuildObject
def _get_busted_bugs(payload):
@@ -30,12 +31,14 @@ def _get_busted_bugs(payload):
return response.json().get("bugs", [])
@CommandProvider
class BustedProvider(MachCommandBase):
@Command(
"busted",
category="misc",
description="Query known bugs in our tooling, and file new ones.",
)
def busted_default(command_context):
def busted_default(self, command_context):
unresolved = _get_busted_bugs({"resolution": "---"})
creation_time = datetime.now() - timedelta(days=15)
creation_time = creation_time.strftime("%Y-%m-%dT%H-%M-%SZ")
@@ -59,7 +62,6 @@ def busted_default(command_context):
else:
print("No known tooling issues found.")
@SubCommand("busted", "file", description="File a bug for busted tooling.")
@CommandArgument(
"against",
@@ -70,7 +72,7 @@ def busted_default(command_context):
"can also run `mach busted file general`."
),
)
def busted_file(command_context, against):
def busted_file(self, command_context, against):
import webbrowser
if (
@@ -234,6 +236,8 @@ appropriate highlighter.
"""
@CommandProvider
class PastebinProvider(MachCommandBase):
@Command("pastebin", category="misc", description=MACH_PASTEBIN_DESCRIPTION)
@CommandArgument(
"--list-highlighters",
@@ -260,7 +264,9 @@ appropriate highlighter.
default=None,
help="Path to file for upload to paste.mozilla.org",
)
def pastebin(command_context, list_highlighters, highlighter, expires, verbose, path):
def pastebin(
self, command_context, list_highlighters, highlighter, expires, verbose, path
):
import requests
def verbose_print(*args, **kwargs):
@@ -271,7 +277,7 @@ def pastebin(command_context, list_highlighters, highlighter, expires, verbose,
# Show known highlighters and exit.
if list_highlighters:
lexers = set(EXTENSION_TO_HIGHLIGHTER.values())
print("Available lexers:\n - %s" % "\n - ".join(sorted(lexers)))
print("Available lexers:\n" " - %s" % "\n - ".join(sorted(lexers)))
return 0
# Get a correct expiry value.
@@ -426,25 +432,29 @@ def mozregression_create_parser():
return loader.create_parser()
@CommandProvider
class MozregressionCommand(MachCommandBase):
@Command(
"mozregression",
category="misc",
description=("Regression range finder for nightly and inbound builds."),
description=("Regression range finder for nightly" " and inbound builds."),
parser=mozregression_create_parser,
)
def run(command_context, **options):
def run(self, command_context, **options):
command_context.activate_virtualenv()
mozregression = PypiBasedTool("mozregression")
mozregression.run(**options)
@CommandProvider
class NodeCommands(MachCommandBase):
@Command(
"node",
category="devenv",
description="Run the NodeJS interpreter used for building.",
)
@CommandArgument("args", nargs=argparse.REMAINDER)
def node(command_context, args):
def node(self, command_context, args):
from mozbuild.nodeutil import find_node_executable
# Avoid logging the command
@@ -458,14 +468,13 @@ def node(command_context, args):
ensure_exit_code=False, # Don't throw on non-zero exit code.
)
@Command(
"npm",
category="devenv",
description="Run the npm executable from the NodeJS used for building.",
)
@CommandArgument("args", nargs=argparse.REMAINDER)
def npm(command_context, args):
def npm(self, command_context, args):
from mozbuild.nodeutil import find_npm_executable
# Avoid logging the command
@@ -503,31 +512,30 @@ def logspam_create_parser(subcommand):
from functools import partial
@CommandProvider
class LogspamCommand(MachCommandBase):
@Command(
"logspam",
category="misc",
description=("Warning categorizer for treeherder test runs."),
)
def logspam(command_context):
def logspam(self, command_context):
pass
@SubCommand("logspam", "report", parser=partial(logspam_create_parser, "report"))
def report(command_context, **options):
def report(self, command_context, **options):
command_context.activate_virtualenv()
logspam = PypiBasedTool("logspam")
logspam.run(command="report", **options)
@SubCommand("logspam", "bisect", parser=partial(logspam_create_parser, "bisect"))
def bisect(command_context, **options):
def bisect(self, command_context, **options):
command_context.activate_virtualenv()
logspam = PypiBasedTool("logspam")
logspam.run(command="bisect", **options)
@SubCommand("logspam", "file", parser=partial(logspam_create_parser, "file"))
def create(command_context, **options):
def create(self, command_context, **options):
command_context.activate_virtualenv()
logspam = PypiBasedTool("logspam")
logspam.run(command="file", **options)

View File

@@ -20,10 +20,12 @@ from functools import partial
from pprint import pprint
from mach.registrar import Registrar
from mozbuild.base import MachCommandBase
from mozbuild.util import memoize
from mach.decorators import (
Command,
CommandArgument,
CommandProvider,
SubCommand,
)
@@ -33,8 +35,9 @@ DOC_ROOT = os.path.join(topsrcdir, "docs")
BASE_LINK = "http://gecko-docs.mozilla.org-l1.s3-website.us-west-2.amazonaws.com/"
# Helps manage in-tree documentation.
@CommandProvider
class Documentation(MachCommandBase):
"""Helps manage in-tree documentation."""
@Command(
"doc",
@@ -81,7 +84,9 @@ BASE_LINK = "http://gecko-docs.mozilla.org-l1.s3-website.us-west-2.amazonaws.com
help="Serve documentation on the specified host and port, "
'default "localhost:5500".',
)
@CommandArgument("--upload", action="store_true", help="Upload generated files to S3.")
@CommandArgument(
"--upload", action="store_true", help="Upload generated files to S3."
)
@CommandArgument(
"-j",
"--jobs",
@@ -89,9 +94,14 @@ BASE_LINK = "http://gecko-docs.mozilla.org-l1.s3-website.us-west-2.amazonaws.com
dest="jobs",
help="Distribute the build over N processes in parallel.",
)
@CommandArgument("--write-url", default=None, help="Write S3 Upload URL to text file")
@CommandArgument("--verbose", action="store_true", help="Run Sphinx in verbose mode")
@CommandArgument(
"--write-url", default=None, help="Write S3 Upload URL to text file"
)
@CommandArgument(
"--verbose", action="store_true", help="Run Sphinx in verbose mode"
)
def build_docs(
self,
command_context,
path=None,
fmt="html",
@@ -107,7 +117,9 @@ def build_docs(
):
# TODO: Bug 1704891 - move the ESLint setup tools to a shared place.
sys.path.append(mozpath.join(command_context.topsrcdir, "tools", "lint", "eslint"))
sys.path.append(
mozpath.join(command_context.topsrcdir, "tools", "lint", "eslint")
)
import setup_helper
setup_helper.set_project_root(command_context.topsrcdir)
@@ -122,7 +134,7 @@ def build_docs(
os.environ["PATH"] = (
mozpath.join(command_context.topsrcdir, "node_modules", ".bin")
+ os.pathsep
+ _node_path()
+ self._node_path()
+ os.pathsep
+ os.environ["PATH"]
)
@@ -136,7 +148,7 @@ def build_docs(
from livereload import Server
from moztreedocs.package import create_tarball
unique_id = "%s/%s" % (project(), str(uuid.uuid1()))
unique_id = "%s/%s" % (self.project(), str(uuid.uuid1()))
outdir = outdir or os.path.join(command_context.topobjdir, "docs")
savedir = os.path.join(outdir, fmt)
@@ -144,17 +156,17 @@ def build_docs(
path = path or command_context.topsrcdir
path = os.path.normpath(os.path.abspath(path))
docdir = _find_doc_dir(path)
docdir = self._find_doc_dir(path)
if not docdir:
print(_dump_sphinx_backtrace())
print(self._dump_sphinx_backtrace())
return die(
"failed to generate documentation:\n"
"%s: could not find docs at this location" % path
)
result = _run_sphinx(docdir, savedir, fmt=fmt, jobs=jobs, verbose=verbose)
result = self._run_sphinx(docdir, savedir, fmt=fmt, jobs=jobs, verbose=verbose)
if result != 0:
print(_dump_sphinx_backtrace())
print(self._dump_sphinx_backtrace())
return die(
"failed to generate documentation:\n"
"%s: sphinx return code %d" % (path, result)
@@ -172,12 +184,12 @@ def build_docs(
print("Generated " + write_url)
if archive:
archive_path = os.path.join(outdir, "%s.tar.gz" % project())
archive_path = os.path.join(outdir, "%s.tar.gz" % self.project())
create_tarball(archive_path, savedir)
print("Archived to %s" % archive_path)
if upload:
_s3_upload(savedir, project(), unique_id, version())
self._s3_upload(savedir, self.project(), unique_id, self.version())
if not serve:
index_path = os.path.join(savedir, "index.html")
@@ -195,10 +207,10 @@ def build_docs(
server = Server()
sphinx_trees = manager().trees or {savedir: docdir}
sphinx_trees = self.manager().trees or {savedir: docdir}
for _, src in sphinx_trees.items():
run_sphinx = partial(
_run_sphinx, src, savedir, fmt=fmt, jobs=jobs, verbose=verbose
self._run_sphinx, src, savedir, fmt=fmt, jobs=jobs, verbose=verbose
)
server.watch(src, run_sphinx)
server.serve(
@@ -208,8 +220,7 @@ def build_docs(
open_url_delay=0.1 if auto_open else None,
)
def _dump_sphinx_backtrace():
def _dump_sphinx_backtrace(self):
"""
If there is a sphinx dump file, read and return
its content.
@@ -234,11 +245,12 @@ def _dump_sphinx_backtrace():
output += f.read()
return output
def _run_sphinx(docdir, savedir, config=None, fmt="html", jobs=None, verbose=None):
def _run_sphinx(
self, docdir, savedir, config=None, fmt="html", jobs=None, verbose=None
):
import sphinx.cmd.build
config = config or manager().conf_py_path
config = config or self.manager().conf_py_path
# When running sphinx with sentry, it adds significant overhead
# and makes the build generation very very very slow
# So, disable it to generate the doc faster
@@ -260,18 +272,16 @@ def _run_sphinx(docdir, savedir, config=None, fmt="html", jobs=None, verbose=Non
print(args)
return sphinx.cmd.build.build_main(args)
def manager():
def manager(self):
from moztreedocs import manager
return manager
@memoize
def _read_project_properties():
def _read_project_properties(self):
import imp
path = os.path.normpath(manager().conf_py_path)
path = os.path.normpath(self.manager().conf_py_path)
with open(path, "r") as fh:
conf = imp.load_module("doc_conf", fh, path, (".py", "r", imp.PY_SOURCE))
@@ -283,24 +293,20 @@ def _read_project_properties():
return {"project": project, "version": getattr(conf, "version", None)}
def project(self):
return self._read_project_properties()["project"]
def project():
return _read_project_properties()["project"]
def version(self):
return self._read_project_properties()["version"]
def version():
return _read_project_properties()["version"]
def _node_path():
def _node_path(self):
from mozbuild.nodeutil import find_node_executable
node, _ = find_node_executable()
return os.path.dirname(node)
def _find_doc_dir(path):
def _find_doc_dir(self, path):
if os.path.isfile(path):
return
@@ -313,8 +319,7 @@ def _find_doc_dir(path):
if os.path.isdir(p):
return p
def _s3_upload(root, project, unique_id, version=None):
def _s3_upload(self, root, project, unique_id, version=None):
from moztreedocs.package import distribution_files
from moztreedocs.upload import s3_upload, s3_set_redirects
@@ -373,13 +378,12 @@ def _s3_upload(root, project, unique_id, version=None):
unique_link = BASE_LINK + unique_id + "/index.html"
print("Uploaded documentation can be accessed here " + unique_link)
@SubCommand(
"doc",
"mach-telemetry",
description="Generate documentation from Glean metrics.yaml files",
)
def generate_telemetry_docs(command_context):
def generate_telemetry_docs(self, command_context):
args = [
sys.executable,
"-m" "glean_parser",
@@ -397,7 +401,10 @@ def generate_telemetry_docs(command_context):
if handler.metrics_path is not None
]
args.extend(
[os.path.join(command_context.topsrcdir, path) for path in set(metrics_paths)]
[
os.path.join(command_context.topsrcdir, path)
for path in set(metrics_paths)
]
)
subprocess.check_call(args)

View File

@@ -5,9 +5,12 @@
from __future__ import absolute_import, unicode_literals
import mozfile
from mach.decorators import Command, CommandArgument
from mach.decorators import CommandProvider, Command, CommandArgument
from mozbuild.base import MachCommandBase
@CommandProvider
class PhabricatorCommandProvider(MachCommandBase):
@Command(
"install-moz-phab",
category="misc",
@@ -19,7 +22,7 @@ from mach.decorators import Command, CommandArgument
action="store_true",
help="Force installation even if already installed.",
)
def install_moz_phab(command_context, force=False):
def install_moz_phab(self, command_context, force=False):
import logging
import os
import re

View File

@@ -9,7 +9,9 @@ from distutils.version import StrictVersion
from mach.decorators import (
Command,
CommandArgument,
CommandProvider,
)
from mozbuild.base import MachCommandBase
def is_osx_10_10_or_greater(cls):
@@ -19,7 +21,8 @@ def is_osx_10_10_or_greater(cls):
return release and StrictVersion(release) >= StrictVersion("10.10")
# Get system power consumption and related measurements.
@CommandProvider
class MachCommands(MachCommandBase):
@Command(
"power",
category="misc",
@@ -35,7 +38,7 @@ def is_osx_10_10_or_greater(cls):
default=30000,
help="The sample period, measured in milliseconds. Defaults to 30000.",
)
def power(command_context, interval):
def power(self, command_context, interval):
"""
Get system power consumption and related measurements.
"""

View File

@@ -9,12 +9,13 @@ import os
import sys
from mach.decorators import (
CommandProvider,
Command,
SettingsProvider,
SubCommand,
)
from mozboot.util import get_state_dir
from mozbuild.base import BuildEnvironmentNotFoundException
from mozbuild.base import BuildEnvironmentNotFoundException, MachCommandBase
from mozbuild.util import memoize
@@ -67,14 +68,15 @@ class TryConfig:
]
def init(command_context):
@CommandProvider
class TrySelect(MachCommandBase):
def init(self, command_context):
from tryselect import push
push.MAX_HISTORY = command_context._mach_context.settings["try"]["maxhistory"]
@memoize
def presets(command_context):
def presets(self, command_context):
from tryselect.preset import MergedHandler
# Create our handler using both local and in-tree presets. The first
@@ -93,9 +95,8 @@ def presets(command_context):
return MergedHandler(*preset_paths)
def handle_presets(
command_context, preset_action=None, save=None, preset=None, **kwargs
self, command_context, preset_action=None, save=None, preset=None, **kwargs
):
"""Handle preset related arguments.
@@ -105,9 +106,9 @@ def handle_presets(
"""
from tryselect.util.dicttools import merge
user_presets = presets(command_context).handlers[0]
user_presets = self.presets(command_context).handlers[0]
if preset_action == "list":
presets(command_context).list()
self.presets(command_context).list()
sys.exit()
if preset_action == "edit":
@@ -132,13 +133,13 @@ def handle_presets(
sys.exit()
if preset:
if preset not in presets(command_context):
if preset not in self.presets(command_context):
command_context._mach_context.parser.error(
"preset '{}' does not exist".format(preset)
)
name = preset
preset = presets(command_context)[name]
preset = self.presets(command_context)[name]
selector = preset.pop("selector")
preset.pop("description", None) # description isn't used by any selectors
@@ -147,7 +148,9 @@ def handle_presets(
elif subcommand != selector:
print(
"error: preset '{}' exists for a different selector "
"(did you mean to run 'mach try {}' instead?)".format(name, selector)
"(did you mean to run 'mach try {}' instead?)".format(
name, selector
)
)
sys.exit(1)
@@ -165,8 +168,7 @@ def handle_presets(
return kwargs
def handle_try_config(command_context, **kwargs):
def handle_try_config(self, command_context, **kwargs):
from tryselect.util.dicttools import merge
to_validate = []
@@ -186,12 +188,11 @@ def handle_try_config(command_context, **kwargs):
cls.validate(**kwargs)
return kwargs
def run(command_context, **kwargs):
kwargs = handle_presets(command_context, **kwargs)
def run(self, command_context, **kwargs):
kwargs = self.handle_presets(command_context, **kwargs)
if command_context._mach_context.handler.parser.task_configs:
kwargs = handle_try_config(command_context, **kwargs)
kwargs = self.handle_try_config(command_context, **kwargs)
mod = importlib.import_module(
"tryselect.selectors.{}".format(
@@ -200,14 +201,13 @@ def run(command_context, **kwargs):
)
return mod.run(**kwargs)
@Command(
"try",
category="ci",
description="Push selected tasks to the try server",
parser=generic_parser,
)
def try_default(command_context, argv=None, **kwargs):
def try_default(self, command_context, argv=None, **kwargs):
"""Push selected tests to the try server.
The |mach try| command is a frontend for scheduling tasks to
@@ -219,32 +219,31 @@ def try_default(command_context, argv=None, **kwargs):
default. Run |mach try auto --help| for more information on
scheduling with the `auto` selector.
"""
init(command_context)
self.init(command_context)
subcommand = command_context._mach_context.handler.subcommand
# We do special handling of presets here so that `./mach try --preset foo`
# works no matter what subcommand 'foo' was saved with.
preset = kwargs["preset"]
if preset:
if preset not in presets(command_context):
if preset not in self.presets(command_context):
command_context._mach_context.handler.parser.error(
"preset '{}' does not exist".format(preset)
)
subcommand = presets(command_context)[preset]["selector"]
subcommand = self.presets(command_context)[preset]["selector"]
sub = subcommand or command_context._mach_context.settings["try"]["default"]
return command_context._mach_context.commands.dispatch(
"try", command_context._mach_context, subcommand=sub, argv=argv, **kwargs
)
@SubCommand(
"try",
"fuzzy",
description="Select tasks on try using a fuzzy finder",
parser=get_parser("fuzzy"),
)
def try_fuzzy(command_context, **kwargs):
def try_fuzzy(self, command_context, **kwargs):
"""Select which tasks to run with a fuzzy finding interface (fzf).
When entering the fzf interface you'll be confronted by two panes. The
@@ -319,7 +318,7 @@ def try_fuzzy(command_context, **kwargs):
For more detailed documentation, please see:
https://firefox-source-docs.mozilla.org/tools/try/selectors/fuzzy.html
"""
init(command_context)
self.init(command_context)
if kwargs.pop("interactive"):
kwargs["query"].append("INTERACTIVE")
@@ -333,23 +332,22 @@ def try_fuzzy(command_context, **kwargs):
kwargs_copy = kwargs.copy()
kwargs_copy["push"] = False
kwargs_copy["save"] = None
kwargs["query"] = run(command_context, save_query=True, **kwargs_copy)
kwargs["query"] = self.run(command_context, save_query=True, **kwargs_copy)
if not kwargs["query"]:
return
if kwargs.get("paths"):
kwargs["test_paths"] = kwargs["paths"]
return run(command_context, **kwargs)
return self.run(command_context, **kwargs)
@SubCommand(
"try",
"chooser",
description="Schedule tasks by selecting them from a web interface.",
description="Schedule tasks by selecting them from a web " "interface.",
parser=get_parser("chooser"),
)
def try_chooser(command_context, **kwargs):
def try_chooser(self, command_context, **kwargs):
"""Push tasks selected from a web interface to try.
This selector will build the taskgraph and spin up a dynamically
@@ -357,15 +355,14 @@ def try_chooser(command_context, **kwargs):
has been made, pressing the 'Push' button will automatically push the
selection to try.
"""
init(command_context)
self.init(command_context)
command_context.activate_virtualenv()
path = os.path.join(
"tools", "tryselect", "selectors", "chooser", "requirements.txt"
)
command_context.virtualenv_manager.install_pip_requirements(path, quiet=True)
return run(command_context, **kwargs)
return self.run(command_context, **kwargs)
@SubCommand(
"try",
@@ -375,21 +372,19 @@ def try_chooser(command_context, **kwargs):
"selector is EXPERIMENTAL.",
parser=get_parser("auto"),
)
def try_auto(command_context, **kwargs):
init(command_context)
return run(command_context, **kwargs)
def try_auto(self, command_context, **kwargs):
self.init(command_context)
return self.run(command_context, **kwargs)
@SubCommand(
"try",
"again",
description="Schedule a previously generated (non try syntax) push again.",
description="Schedule a previously generated (non try syntax) " "push again.",
parser=get_parser("again"),
)
def try_again(command_context, **kwargs):
init(command_context)
return run(command_context, **kwargs)
def try_again(self, command_context, **kwargs):
self.init(command_context)
return self.run(command_context, **kwargs)
@SubCommand(
"try",
@@ -397,7 +392,7 @@ def try_again(command_context, **kwargs):
description="Push to try without scheduling any tasks.",
parser=get_parser("empty"),
)
def try_empty(command_context, **kwargs):
def try_empty(self, command_context, **kwargs):
"""Push to try, running no builds or tests
This selector does not prompt you to run anything, it just pushes
@@ -406,9 +401,8 @@ def try_empty(command_context, **kwargs):
via Treeherder's Add New Jobs feature, located in the per-push
menu.
"""
init(command_context)
return run(command_context, **kwargs)
self.init(command_context)
return self.run(command_context, **kwargs)
@SubCommand(
"try",
@@ -416,7 +410,7 @@ def try_empty(command_context, **kwargs):
description="Select tasks on try using try syntax",
parser=get_parser("syntax"),
)
def try_syntax(command_context, **kwargs):
def try_syntax(self, command_context, **kwargs):
"""Push the current tree to try, with the specified syntax.
Build options, platforms and regression tests may be selected
@@ -454,7 +448,7 @@ def try_syntax(command_context, **kwargs):
(installable from mach vcs-setup).
"""
init(command_context)
self.init(command_context)
try:
if command_context.substs.get("MOZ_ARTIFACT_BUILDS"):
kwargs["local_artifact_build"] = True
@@ -469,8 +463,7 @@ def try_syntax(command_context, **kwargs):
print(CONFIG_ENVIRONMENT_NOT_FOUND)
sys.exit(1)
return run(command_context, **kwargs)
return self.run(command_context, **kwargs)
@SubCommand(
"try",
@@ -478,11 +471,10 @@ def try_syntax(command_context, **kwargs):
description="Select tasks on try using coverage data",
parser=get_parser("coverage"),
)
def try_coverage(command_context, **kwargs):
def try_coverage(self, command_context, **kwargs):
"""Select which tasks to use using coverage data."""
init(command_context)
return run(command_context, **kwargs)
self.init(command_context)
return self.run(command_context, **kwargs)
@SubCommand(
"try",
@@ -490,11 +482,10 @@ def try_coverage(command_context, **kwargs):
description="Push the current tree to try, configured for a staging release.",
parser=get_parser("release"),
)
def try_release(command_context, **kwargs):
def try_release(self, command_context, **kwargs):
"""Push the current tree to try, configured for a staging release."""
init(command_context)
return run(command_context, **kwargs)
self.init(command_context)
return self.run(command_context, **kwargs)
@SubCommand(
"try",
@@ -502,10 +493,10 @@ def try_release(command_context, **kwargs):
description="Run scriptworker tasks against a recent release.",
parser=get_parser("scriptworker"),
)
def try_scriptworker(command_context, **kwargs):
def try_scriptworker(self, command_context, **kwargs):
"""Run scriptworker tasks against a recent release.
Requires VPN and shipit access.
"""
init(command_context)
return run(command_context, **kwargs)
self.init(command_context)
return self.run(command_context, **kwargs)

View File

@@ -13,9 +13,12 @@ import logging
from mach.decorators import (
CommandArgument,
CommandProvider,
Command,
)
from mozbuild.base import MachCommandBase
import mozpack.path as mozpath
import json
@@ -43,24 +46,32 @@ PR_REPOSITORIES = {
}
@CommandProvider
class PullRequestImporter(MachCommandBase):
@Command(
"import-pr",
category="misc",
description="Import a pull request from Github to the local repo.",
)
@CommandArgument("-b", "--bug-number", help="Bug number to use in the commit messages.")
@CommandArgument(
"-b", "--bug-number", help="Bug number to use in the commit messages."
)
@CommandArgument(
"-t",
"--bugzilla-token",
help="Bugzilla API token used to file a new bug if no bug number is provided.",
help="Bugzilla API token used to file a new bug if no bug number is "
"provided.",
)
@CommandArgument(
"-r", "--reviewer", help="Reviewer nick to apply to commit messages."
)
@CommandArgument("-r", "--reviewer", help="Reviewer nick to apply to commit messages.")
@CommandArgument(
"pull_request",
help="URL to the pull request to import (e.g. "
"https://github.com/servo/webrender/pull/3665).",
)
def import_pr(
self,
command_context,
pull_request,
bug_number=None,
@@ -122,7 +133,7 @@ def import_pr(
"be added to commit messages.",
)
else:
bug_number = _file_bug(
bug_number = self._file_bug(
command_context, bugzilla_token, repository, pr_number
)
elif bugzilla_token is not None:
@@ -136,7 +147,9 @@ def import_pr(
pr_patch = requests.get(pull_request + ".patch")
pr_patch.raise_for_status()
for patch in _split_patches(pr_patch.content, bug_number, pull_request, reviewer):
for patch in self._split_patches(
pr_patch.content, bug_number, pull_request, reviewer
):
command_context.log(
logging.INFO,
"commit_msg",
@@ -160,10 +173,11 @@ def import_pr(
command_context.repository.commit(
patch["commit_msg"], patch["author"], patch["date"], [target_dir]
)
command_context.log(logging.INFO, "commit_pass", {}, "Committed successfully.")
command_context.log(
logging.INFO, "commit_pass", {}, "Committed successfully."
)
def _file_bug(command_context, token, repo, pr_number):
def _file_bug(self, command_context, token, repo, pr_number):
import requests
bug = requests.post(
@@ -171,7 +185,8 @@ def _file_bug(command_context, token, repo, pr_number):
json={
"product": repo["bugzilla_product"],
"component": repo["bugzilla_component"],
"summary": "Land %s#%s in mozilla-central" % (repo["github"], pr_number),
"summary": "Land %s#%s in mozilla-central"
% (repo["github"], pr_number),
"version": "unspecified",
},
)
@@ -183,8 +198,7 @@ def _file_bug(command_context, token, repo, pr_number):
)
return bugnumber
def _split_patches(patchfile, bug_number, pull_request, reviewer):
def _split_patches(self, patchfile, bug_number, pull_request, reviewer):
INITIAL = 0
HEADERS = 1
STAT_AND_DIFF = 2
@@ -201,17 +215,16 @@ def _split_patches(patchfile, bug_number, pull_request, reviewer):
state = STAT_AND_DIFF
elif state == STAT_AND_DIFF:
if line.startswith(b"From "):
yield _parse_patch(patch, bug_number, pull_request, reviewer)
yield self._parse_patch(patch, bug_number, pull_request, reviewer)
patch = b""
state = HEADERS
else:
patch += line + b"\n"
if len(patch) > 0:
yield _parse_patch(patch, bug_number, pull_request, reviewer)
yield self._parse_patch(patch, bug_number, pull_request, reviewer)
return
def _parse_patch(patch, bug_number, pull_request, reviewer):
def _parse_patch(self, patch, bug_number, pull_request, reviewer):
import email
from email import (
header,