Bug 1874684 - Part 6: Limit day length calculations to safe integers. r=mgaudet
[gecko.git] / tools / lint / mach_commands.py
blob9b5648d9b039d392ba4d47236fa1b1443d1ca855
1 # This Source Code Form is subject to the terms of the Mozilla Public
2 # License, v. 2.0. If a copy of the MPL was not distributed with this
3 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
5 import argparse
6 import copy
7 import os
9 from mach.decorators import Command, CommandArgument
10 from mozbuild.base import BuildEnvironmentNotFoundException
11 from mozbuild.base import MachCommandConditions as conditions
13 here = os.path.abspath(os.path.dirname(__file__))
14 EXCLUSION_FILES = [
15 os.path.join("tools", "rewriting", "Generated.txt"),
16 os.path.join("tools", "rewriting", "ThirdPartyPaths.txt"),
19 EXCLUSION_FILES_OPTIONAL = []
20 thunderbird_excludes = os.path.join("comm", "tools", "lint", "GlobalExclude.txt")
21 if os.path.exists(thunderbird_excludes):
22 EXCLUSION_FILES_OPTIONAL.append(thunderbird_excludes)
24 GLOBAL_EXCLUDES = ["**/node_modules", "tools/lint/test/files", ".hg", ".git"]
26 VALID_FORMATTERS = {"black", "clang-format", "rustfmt"}
27 VALID_ANDROID_FORMATTERS = {"android-format"}
29 # Code-review bot must index issues from the whole codebase when pushing
30 # to autoland or try repositories. In such cases, output warnings in the
31 # task's JSON artifact but do not fail if only warnings are found.
32 REPORT_WARNINGS = os.environ.get("GECKO_HEAD_REPOSITORY", "").rstrip("/") in (
33 "https://hg.mozilla.org/mozilla-central",
34 "https://hg.mozilla.org/integration/autoland",
35 "https://hg.mozilla.org/try",
39 def setup_argument_parser():
40 from mozlint import cli
42 return cli.MozlintParser()
45 def get_global_excludes(**lintargs):
46 # exclude misc paths
47 excludes = GLOBAL_EXCLUDES[:]
48 topsrcdir = lintargs["root"]
50 # exclude top level paths that look like objdirs
51 excludes.extend(
53 name
54 for name in os.listdir(topsrcdir)
55 if name.startswith("obj") and os.path.isdir(name)
59 if lintargs.get("include_third-party"):
60 # For some linters, we want to include the thirdparty code too.
61 # Example: trojan-source linter should run also on third party code.
62 return excludes
64 for path in EXCLUSION_FILES + EXCLUSION_FILES_OPTIONAL:
65 with open(os.path.join(topsrcdir, path), "r") as fh:
66 excludes.extend([f.strip() for f in fh.readlines()])
68 return excludes
71 @Command(
72 "lint",
73 category="devenv",
74 description="Run linters.",
75 parser=setup_argument_parser,
76 virtualenv_name="lint",
78 def lint(command_context, *runargs, **lintargs):
79 """Run linters."""
80 command_context.activate_virtualenv()
81 from mozlint import cli, parser
83 try:
84 buildargs = {}
85 buildargs["substs"] = copy.deepcopy(dict(command_context.substs))
86 buildargs["defines"] = copy.deepcopy(dict(command_context.defines))
87 buildargs["topobjdir"] = command_context.topobjdir
88 lintargs.update(buildargs)
89 except BuildEnvironmentNotFoundException:
90 pass
92 lintargs.setdefault("root", command_context.topsrcdir)
93 lintargs["exclude"] = get_global_excludes(**lintargs)
94 lintargs["config_paths"].insert(0, here)
95 lintargs["virtualenv_bin_path"] = command_context.virtualenv_manager.bin_path
96 lintargs["virtualenv_manager"] = command_context.virtualenv_manager
97 if REPORT_WARNINGS and lintargs.get("show_warnings") is None:
98 lintargs["show_warnings"] = "soft"
99 for path in EXCLUSION_FILES:
100 parser.GLOBAL_SUPPORT_FILES.append(
101 os.path.join(command_context.topsrcdir, path)
103 setupargs = {
104 "mach_command_context": command_context,
106 return cli.run(*runargs, setupargs=setupargs, **lintargs)
109 @Command(
110 "eslint",
111 category="devenv",
112 description="Run eslint or help configure eslint for optimal development.",
114 @CommandArgument(
115 "paths",
116 default=None,
117 nargs="*",
118 help="Paths to file or directories to lint, like "
119 "'browser/' Defaults to the "
120 "current directory if not given.",
122 @CommandArgument(
123 "-s",
124 "--setup",
125 default=False,
126 action="store_true",
127 help="Configure eslint for optimal development.",
129 @CommandArgument("-b", "--binary", default=None, help="Path to eslint binary.")
130 @CommandArgument(
131 "--fix",
132 default=False,
133 action="store_true",
134 help="Request that eslint automatically fix errors, where possible.",
136 @CommandArgument(
137 "--rule",
138 default=[],
139 dest="rules",
140 action="append",
141 help="Specify an additional rule for ESLint to run, e.g. 'no-new-object: error'",
143 @CommandArgument(
144 "extra_args",
145 nargs=argparse.REMAINDER,
146 help="Extra args that will be forwarded to eslint.",
148 def eslint(command_context, paths, extra_args=[], **kwargs):
149 command_context._mach_context.commands.dispatch(
150 "lint",
151 command_context._mach_context,
152 linters=["eslint"],
153 paths=paths,
154 argv=extra_args,
155 **kwargs
159 @Command(
160 "format",
161 category="devenv",
162 description="Format files, alternative to 'lint --fix' ",
163 parser=setup_argument_parser,
165 def format_files(command_context, paths, extra_args=[], **kwargs):
166 linters = kwargs["linters"]
168 formatters = VALID_FORMATTERS
169 if conditions.is_android(command_context):
170 formatters |= VALID_ANDROID_FORMATTERS
172 if not linters:
173 linters = formatters
174 else:
175 invalid_linters = set(linters) - formatters
176 if invalid_linters:
177 print(
178 "error: One or more linters passed are not valid formatters. "
179 "Note that only the following linters are valid formatters:"
181 print("\n".join(sorted(formatters)))
182 return 1
184 kwargs["linters"] = list(linters)
186 kwargs["fix"] = True
187 command_context._mach_context.commands.dispatch(
188 "lint", command_context._mach_context, paths=paths, argv=extra_args, **kwargs