diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 14:29:10 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 14:29:10 +0000 |
commit | 2aa4a82499d4becd2284cdb482213d541b8804dd (patch) | |
tree | b80bf8bf13c3766139fbacc530efd0dd9d54394c /tools/lint/clang-format | |
parent | Initial commit. (diff) | |
download | firefox-upstream.tar.xz firefox-upstream.zip |
Adding upstream version 86.0.1.upstream/86.0.1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'tools/lint/clang-format')
-rw-r--r-- | tools/lint/clang-format/__init__.py | 176 |
1 files changed, 176 insertions, 0 deletions
diff --git a/tools/lint/clang-format/__init__.py b/tools/lint/clang-format/__init__.py new file mode 100644 index 0000000000..fb0ed9a654 --- /dev/null +++ b/tools/lint/clang-format/__init__.py @@ -0,0 +1,176 @@ +# 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 os +import signal +import re + +from buildconfig import substs +from mozboot.util import get_state_dir +from mozlint import result +from mozlint.pathutils import expand_exclusions +from mozprocess import ProcessHandler + +CLANG_FORMAT_NOT_FOUND = """ +Could not find clang-format! Install clang-format with: + + $ ./mach bootstrap + +And make sure that it is in the PATH +""".strip() + + +def parse_issues(config, output, paths, log): + + diff_line = re.compile("^(.*):(.*):(.*): warning: .*;(.*);(.*)") + results = [] + for line in output: + match = diff_line.match(line) + file, line_no, col, diff, diff2 = match.groups() + log.debug( + "file={} line={} col={} diff={} diff2={}".format( + file, line_no, col, diff, diff2 + ) + ) + d = diff + "\n" + diff2 + res = { + "path": file, + "diff": d, + "level": "warning", + "lineno": line_no, + "column": col, + } + results.append(result.from_config(config, **res)) + + return results + + +class ClangFormatProcess(ProcessHandler): + def __init__(self, config, *args, **kwargs): + self.config = config + kwargs["stream"] = False + kwargs["universal_newlines"] = True + ProcessHandler.__init__(self, *args, **kwargs) + + def run(self, *args, **kwargs): + orig = signal.signal(signal.SIGINT, signal.SIG_IGN) + ProcessHandler.run(self, *args, **kwargs) + signal.signal(signal.SIGINT, orig) + + +def run_process(config, cmd): + proc = ClangFormatProcess(config, cmd) + proc.run() + try: + proc.wait() + except KeyboardInterrupt: + proc.kill() + + return proc.output + + +def get_clang_format_binary(): + """ + Returns the path of the first clang-format binary available + if not found returns None + """ + binary = os.environ.get("CLANG_FORMAT") + if binary: + return binary + + clang_tools_path = os.path.join(get_state_dir(), "clang-tools") + bin_path = os.path.join(clang_tools_path, "clang-tidy", "bin") + return os.path.join(bin_path, "clang-format" + substs.get("HOST_BIN_SUFFIX", "")) + + +def is_ignored_path(ignored_dir_re, topsrcdir, f): + # Remove up to topsrcdir in pathname and match + if f.startswith(topsrcdir + "/"): + match_f = f[len(topsrcdir + "/") :] + else: + match_f = f + return re.match(ignored_dir_re, match_f) + + +def remove_ignored_path(paths, topsrcdir, log): + path_to_third_party = os.path.join(topsrcdir, ".clang-format-ignore") + + ignored_dir = [] + with open(path_to_third_party, "r") as fh: + for line in fh: + # In case it starts with a space + line = line.strip() + # Remove comments and empty lines + if line.startswith("#") or len(line) == 0: + continue + # The regexp is to make sure we are managing relative paths + ignored_dir.append(r"^[\./]*" + line.rstrip()) + + # Generates the list of regexp + ignored_dir_re = "(%s)" % "|".join(ignored_dir) + + path_list = [] + for f in paths: + if is_ignored_path(ignored_dir_re, topsrcdir, f): + # Early exit if we have provided an ignored directory + log.debug("Ignored third party code '{0}'".format(f)) + continue + path_list.append(f) + + return path_list + + +def lint(paths, config, fix=None, **lintargs): + log = lintargs["log"] + paths = list(expand_exclusions(paths, config, lintargs["root"])) + + # We ignored some specific files for a bunch of reasons. + # Not using excluding to avoid duplication + if lintargs.get("use_filters", True): + paths = remove_ignored_path(paths, lintargs["root"], log) + + # An empty path array can occur when the user passes in `-n`. If we don't + # return early in this case, rustfmt will attempt to read stdin and hang. + if not paths: + return [] + + binary = get_clang_format_binary() + + if not binary: + print(CLANG_FORMAT_NOT_FOUND) + if "MOZ_AUTOMATION" in os.environ: + return 1 + return [] + + cmd_args = [binary] + + base_command = cmd_args + ["--version"] + version = run_process(config, base_command) + log.debug("Version: {}".format(version)) + + if fix: + cmd_args.append("-i") + else: + cmd_args.append("--dry-run") + base_command = cmd_args + paths + log.debug("Command: {}".format(" ".join(cmd_args))) + output = run_process(config, base_command) + output_list = [] + + if len(output) % 3 != 0: + raise Exception( + "clang-format output should be a multiple of 3. Output: %s" % output + ) + + for i in range(0, len(output), 3): + # Merge the element 3 by 3 (clang-format output) + line = output[i] + line += ";" + output[i + 1] + line += ";" + output[i + 2] + output_list.append(line) + + if fix: + # clang-format is able to fix all issues so don't bother parsing the output. + return [] + return parse_issues(config, output_list, paths, log) |