diff options
Diffstat (limited to 'tools/lint/mach_commands.py')
-rw-r--r-- | tools/lint/mach_commands.py | 189 |
1 files changed, 189 insertions, 0 deletions
diff --git a/tools/lint/mach_commands.py b/tools/lint/mach_commands.py new file mode 100644 index 0000000000..c9778e28a0 --- /dev/null +++ b/tools/lint/mach_commands.py @@ -0,0 +1,189 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +import argparse +import copy +import os + +from mach.decorators import Command, CommandArgument +from mozbuild.base import BuildEnvironmentNotFoundException +from mozbuild.base import MachCommandConditions as conditions + +here = os.path.abspath(os.path.dirname(__file__)) +EXCLUSION_FILES = [ + os.path.join("tools", "rewriting", "Generated.txt"), + os.path.join("tools", "rewriting", "ThirdPartyPaths.txt"), +] + +EXCLUSION_FILES_OPTIONAL = [] +thunderbird_excludes = os.path.join("comm", "tools", "lint", "GlobalExclude.txt") +if os.path.exists(thunderbird_excludes): + EXCLUSION_FILES_OPTIONAL.append(thunderbird_excludes) + +GLOBAL_EXCLUDES = ["**/node_modules", "tools/lint/test/files", ".hg", ".git"] + +VALID_FORMATTERS = {"black", "clang-format", "rustfmt", "isort"} +VALID_ANDROID_FORMATTERS = {"android-format"} + +# Code-review bot must index issues from the whole codebase when pushing +# to autoland or try repositories. In such cases, output warnings in the +# task's JSON artifact but do not fail if only warnings are found. +REPORT_WARNINGS = os.environ.get("GECKO_HEAD_REPOSITORY", "").rstrip("/") in ( + "https://hg.mozilla.org/mozilla-central", + "https://hg.mozilla.org/integration/autoland", + "https://hg.mozilla.org/try", +) + + +def setup_argument_parser(): + from mozlint import cli + + return cli.MozlintParser() + + +def get_global_excludes(**lintargs): + # exclude misc paths + excludes = GLOBAL_EXCLUDES[:] + topsrcdir = lintargs["root"] + + # exclude top level paths that look like objdirs + excludes.extend( + [ + name + for name in os.listdir(topsrcdir) + if name.startswith("obj") and os.path.isdir(name) + ] + ) + + if lintargs.get("include_thirdparty"): + # For some linters, we want to include the thirdparty code too. + # Example: trojan-source linter should run also on third party code. + return excludes + + for path in EXCLUSION_FILES + EXCLUSION_FILES_OPTIONAL: + with open(os.path.join(topsrcdir, path), "r") as fh: + excludes.extend([f.strip() for f in fh.readlines()]) + + return excludes + + +@Command( + "lint", + category="devenv", + description="Run linters.", + parser=setup_argument_parser, + virtualenv_name="lint", +) +def lint(command_context, *runargs, **lintargs): + """Run linters.""" + command_context.activate_virtualenv() + from mozlint import cli, parser + + try: + buildargs = {} + buildargs["substs"] = copy.deepcopy(dict(command_context.substs)) + buildargs["defines"] = copy.deepcopy(dict(command_context.defines)) + buildargs["topobjdir"] = command_context.topobjdir + lintargs.update(buildargs) + except BuildEnvironmentNotFoundException: + pass + + lintargs.setdefault("root", command_context.topsrcdir) + lintargs["exclude"] = get_global_excludes(**lintargs) + lintargs["config_paths"].insert(0, here) + lintargs["virtualenv_bin_path"] = command_context.virtualenv_manager.bin_path + lintargs["virtualenv_manager"] = command_context.virtualenv_manager + if REPORT_WARNINGS and lintargs.get("show_warnings") is None: + lintargs["show_warnings"] = "soft" + for path in EXCLUSION_FILES: + parser.GLOBAL_SUPPORT_FILES.append( + os.path.join(command_context.topsrcdir, path) + ) + setupargs = { + "mach_command_context": command_context, + } + return cli.run(*runargs, setupargs=setupargs, **lintargs) + + +@Command( + "eslint", + category="devenv", + description="Run eslint or help configure eslint for optimal development.", +) +@CommandArgument( + "paths", + default=None, + nargs="*", + help="Paths to file or directories to lint, like " + "'browser/' Defaults to the " + "current directory if not given.", +) +@CommandArgument( + "-s", + "--setup", + default=False, + action="store_true", + help="Configure eslint for optimal development.", +) +@CommandArgument("-b", "--binary", default=None, help="Path to eslint binary.") +@CommandArgument( + "--fix", + default=False, + action="store_true", + help="Request that eslint automatically fix errors, where possible.", +) +@CommandArgument( + "--rule", + default=[], + dest="rules", + action="append", + help="Specify an additional rule for ESLint to run, e.g. 'no-new-object: error'", +) +@CommandArgument( + "extra_args", + nargs=argparse.REMAINDER, + help="Extra args that will be forwarded to eslint.", +) +def eslint(command_context, paths, extra_args=[], **kwargs): + command_context._mach_context.commands.dispatch( + "lint", + command_context._mach_context, + linters=["eslint"], + paths=paths, + argv=extra_args, + **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): + linters = kwargs["linters"] + + formatters = VALID_FORMATTERS + if conditions.is_android(command_context): + formatters |= VALID_ANDROID_FORMATTERS + + if not linters: + linters = formatters + else: + invalid_linters = set(linters) - formatters + if invalid_linters: + print( + "error: One or more linters passed are not valid formatters. " + "Note that only the following linters are valid formatters:" + ) + print("\n".join(sorted(formatters))) + return 1 + + kwargs["linters"] = list(linters) + + kwargs["fix"] = True + command_context._mach_context.commands.dispatch( + "lint", command_context._mach_context, paths=paths, argv=extra_args, **kwargs + ) |