diff --git a/.clang-format-ignore b/.clang-format-ignore index f56cafcb7a..ecc5f4b973 100644 --- a/.clang-format-ignore +++ b/.clang-format-ignore @@ -8,7 +8,5 @@ FEXCore/Source/Common/SoftFloat-3e/* Source/Common/cpp-optparse/* # Files with human-indented tables for readability - don't mess with these -FEXCore/Source/Interface/Core/X86Tables/X87Tables.cpp -FEXCore/Source/Interface/Core/X86Tables/XOPTables.cpp FEXCore/Source/Interface/Core/X86Tables/* diff --git a/External/code-format-helper/code-format-helper.py b/External/code-format-helper/code-format-helper.py index 176839849d..bbd764ef83 100644 --- a/External/code-format-helper/code-format-helper.py +++ b/External/code-format-helper/code-format-helper.py @@ -173,6 +173,12 @@ class ClangFormatHelper(FormatHelper): name = "clang-format" friendly_name = "C/C++ code formatter" + @property + def cformat_wrapper_path(self) -> str: + relpath = "../../Scripts/clang-format.py" + curpath = os.path.dirname(os.path.abspath(__file__)) + return os.path.abspath(os.path.normpath(os.path.join(curpath, relpath))) + @property def instructions(self) -> str: return " ".join(self.cf_cmd) @@ -210,7 +216,11 @@ def format_run(self, changed_files: List[str], args: FormatArgs) -> Optional[str if not cpp_files: return None - cf_cmd = [self.clang_fmt_path, "--diff"] + cf_cmd = [ + self.clang_fmt_path, + f"--binary={self.cformat_wrapper_path}", + "--diff", + ] if args.start_rev and args.end_rev: cf_cmd.append(args.start_rev) diff --git a/Scripts/clang-format.py b/Scripts/clang-format.py old mode 100644 new mode 100755 index 4f4aa91494..67f97d5013 --- a/Scripts/clang-format.py +++ b/Scripts/clang-format.py @@ -1,6 +1,13 @@ +#!/usr/bin/env python3 # Imported from LLVM # It's basically a clang-format wrapper with .clang-format-ignore support. # Can be removed once we adopt Clang19, which supports this out of the box. +# +# This is called by git-clang-format and it works in two modes. +# It has a path in the command line to format, and it will output the result post format to stdout. +# Or it has -assume-filename= in the command line, and the file is passed via stdin. +# Post format output will be given in stdout. + import subprocess import sys import os @@ -20,6 +27,10 @@ def glob_to_regex(pattern): def load_ignore_patterns(ignore_file_path): + # Check if the file exists + if not os.path.exists(ignore_file_path): + raise FileNotFoundError(f"No such file: '{ignore_file_path}'") + with open(ignore_file_path, "r") as file: lines = file.readlines() @@ -29,6 +40,10 @@ def load_ignore_patterns(ignore_file_path): if line and not line.startswith("#"): # Ignore empty lines and comments pattern = glob_to_regex(line) patterns.append(re.compile(pattern)) + + # Print the number of patterns found + print(f"Number of patterns found: {len(patterns)}", file=sys.stderr) + return patterns @@ -49,24 +64,52 @@ def should_ignore(file_path, ignore_patterns): return False -def find_valid_file_paths(args): - return [arg for arg in args if os.path.isfile(arg)] - - def main(): ignore_patterns = load_ignore_patterns(ignore_file_path) - valid_paths = find_valid_file_paths(sys.argv[1:]) - - if len(valid_paths) != 1: - print("Error: Expected exactly one valid file path as argument.") - sys.exit(1) - - file_path = valid_paths[0] - if should_ignore(file_path, ignore_patterns): - print(f"Ignoring {file_path} based on ignore patterns.") - return - - subprocess.run([clang_format_command] + sys.argv[1:], check=True) + assume_filename = None + args = sys.argv[1:] + + # Extract and handle `-assume-filename=` + args_filtered = [] + for arg in args: + if arg.startswith("-assume-filename="): + _, assume_filename = arg.split("=", 1) + else: + args_filtered.append(arg) + + args = args_filtered + + if assume_filename is not None: + if should_ignore(assume_filename, ignore_patterns): + print( + f"Ignoring {assume_filename} based on ignore patterns.", file=sys.stderr + ) + sys.stdout.write(sys.stdin.read()) + sys.exit(0) + input_stream = sys.stdin.read() + subprocess.run( + [clang_format_command, "-assume-filename=" + assume_filename] + args, + input=input_stream.encode(), + check=True, + ) + else: + # Find all valid file paths + valid_paths = [arg for arg in args if os.path.isfile(arg)] + if len(valid_paths) != 1: + print( + "Error: Exactly one valid file path is required when -assume-filename is not present.", + file=sys.stderr, + ) + sys.exit(1) + + file_path = valid_paths[0] + if should_ignore(file_path, ignore_patterns): + print(f"Ignoring {file_path} based on ignore patterns.", file=sys.stderr) + with open(file_path, "r") as file: + sys.stdout.write(file.read()) + sys.exit(0) + + subprocess.run([clang_format_command] + args, check=True) if __name__ == "__main__":