summaryrefslogtreecommitdiffstats
path: root/tools/lint/mach_commands.py
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--tools/lint/mach_commands.py189
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
+ )