From 6bf0a5cb5034a7e684dcc3500e841785237ce2dd Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sun, 7 Apr 2024 19:32:43 +0200 Subject: Adding upstream version 1:115.7.0. Signed-off-by: Daniel Baumann --- tools/lint/clang-format/__init__.py | 237 ++++++++++++++++++++++++++++++++++++ 1 file changed, 237 insertions(+) create mode 100644 tools/lint/clang-format/__init__.py (limited to 'tools/lint/clang-format') diff --git a/tools/lint/clang-format/__init__.py b/tools/lint/clang-format/__init__.py new file mode 100644 index 0000000000..d0a81f7b06 --- /dev/null +++ b/tools/lint/clang-format/__init__.py @@ -0,0 +1,237 @@ +# 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 re +import signal +import sys +import xml.etree.ElementTree as ET + +from mozboot.util import get_tools_dir +from mozlint import result +from mozlint.pathutils import expand_exclusions +from mozprocess import ProcessHandler + +CLANG_FORMAT_NOT_FOUND = """ +Could not find clang-format! It should've been installed automatically - \ +please report a bug here: +https://bugzilla.mozilla.org/enter_bug.cgi?product=Firefox%20Build%20System&component=Lint%20and%20Formatting +""".strip() + + +def setup(root, mach_command_context, **lintargs): + if get_clang_format_binary(): + return 0 + + from mozbuild.code_analysis.mach_commands import get_clang_tools + + rc, _ = get_clang_tools(mach_command_context) + if rc: + return 1 + + +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_tools_dir(), "clang-tools") + bin_path = os.path.join(clang_tools_path, "clang-tidy", "bin") + binary = os.path.join(bin_path, "clang-format") + + if sys.platform.startswith("win"): + binary += ".exe" + + if not os.path.isfile(binary): + return None + + return binary + + +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)) + + cmd_args.append("--output-replacements-xml") + base_command = cmd_args + paths + log.debug("Command: {}".format(" ".join(cmd_args))) + output = run_process(config, base_command) + + def replacement(parser): + for end, e in parser.read_events(): + assert end == "end" + if e.tag == "replacement": + item = {k: int(v) for k, v in e.items()} + assert sorted(item.keys()) == ["length", "offset"] + item["with"] = (e.text or "").encode("utf-8") + yield item + + # When given multiple paths as input, --output-replacements-xml + # will output one xml per path, in the order they are given, but + # XML parsers don't know how to handle that, so do it manually. + parser = None + replacements = [] + for line in output: + if line.startswith("