diff options
| author | Egor Tensin <egor@tensin.name> | 2026-10-05 10:34:55 +0500 |
|---|---|---|
| committer | Egor Tensin <egor@tensin.name> | 2026-10-05 10:53:25 +0500 |
| commit | b4f6e1253356370c8d8855ea0b155e844db98b83 (patch) | |
| tree | f60faafa2ab66e391eec38480e56b2a845cdc539 /tools | |
| parent | pyproject.toml: make a guess about the required Python version (diff) | |
| download | cmake-common-b4f6e1253356370c8d8855ea0b155e844db98b83.tar.gz cmake-common-b4f6e1253356370c8d8855ea0b155e844db98b83.zip | |
tools/project-clang-format.py -> project/format.py
Diffstat (limited to 'tools')
| -rwxr-xr-x | tools/project-clang-format.py | 236 |
1 files changed, 0 insertions, 236 deletions
diff --git a/tools/project-clang-format.py b/tools/project-clang-format.py deleted file mode 100755 index 4b066c3..0000000 --- a/tools/project-clang-format.py +++ /dev/null @@ -1,236 +0,0 @@ -#!/usr/bin/env python - -# Copyright (c) 2020 Egor Tensin <egor@tensin.name> -# This file is part of the "cmake-common" project. -# For details, see https://github.com/egor-tensin/cmake-common -# Distributed under the MIT License. - -"""clang-format all C/C++ files in the project - -This script feeds every C/C++ file in the repository to clang-format, either -printing a unified diff between the original and the formatted versions, or -formatting the files in-place. -""" - -import argparse -from contextlib import contextmanager -import difflib -import logging -import os -import subprocess -import sys - - -@contextmanager -def setup_logging(): - level_names = { - logging.DEBUG: "DBG", - logging.INFO: "INFO", - logging.WARNING: "WARN", - logging.ERROR: "ERR", - logging.CRITICAL: "CRIT", - } - for lvl, name in level_names.items(): - logging.addLevelName(lvl, name) - - logging.basicConfig( - level=logging.INFO, - datefmt="%Y-%m-%d %H:%M:%S%z", - format="%(asctime)s | %(levelname)4s | %(message)s", - ) - try: - yield - except Exception as e: - logging.exception(e) - sys.exit(1) - - -@contextmanager -def cd(path): - cwd = os.getcwd() - os.chdir(path) - try: - yield - finally: - os.chdir(cwd) - - -def normalize_path(entry): - return os.path.abspath(entry) - - -def run(cmd_line): - logging.debug("Running executable: %s", cmd_line) - try: - return subprocess.run( - cmd_line, - check=True, - universal_newlines=True, - stderr=subprocess.STDOUT, - stdout=subprocess.PIPE, - ) - except subprocess.CalledProcessError as e: - logging.error("Process finished with exit code %d: %s", e.returncode, cmd_line) - logging.error("Its output was:\n%s", e.output) - raise - - -def read_file(path): - with open(path) as file: - return file.read() - - -class ClangFormat: - def __init__(self, path, style): - self.path = path - self.style = style - - def _get_command_line(self, paths, in_place=False): - cmd_line = [self.path, f"-style={self.style}"] - if in_place: - cmd_line.append("-i") - cmd_line.append("--") - cmd_line += list(paths) - return cmd_line - - def format_in_place(self, paths): - run(self._get_command_line(paths, in_place=True)) - - @staticmethod - def _show_diff(path, formatted): - original = read_file(path) - if original and not formatted: - # Assuming this file is ignored in .clang-format-ignore. - return True - - original = original.split("\n") - formatted = formatted.split("\n") - original_lbl = f"{path} (original)" - formatted_lbl = f"{path} (formatted)" - - diff = difflib.unified_diff( - original, - formatted, - fromfile=original_lbl, - tofile=formatted_lbl, - lineterm="", - ) - - clean = True - for line in diff: - clean = False - print(line) - return clean - - def show_diff(self, paths): - clean = True - for path in paths: - formatted = run(self._get_command_line([path])).stdout - clean = self._show_diff(path, formatted) and clean - return clean - - -def git_root_dir(): - cmd_line = ["git", "rev-parse", "--show-toplevel"] - root_dir = run(cmd_line).stdout - if root_dir[-1] != "\n": - raise RuntimeError("git rev-parse --show-toplevel should append a newline?") - return root_dir[:-1] - - -def list_git_files(): - cmd_line = ["git", "ls-tree", "-r", "-z", "--name-only", "HEAD"] - repo_files = run(cmd_line).stdout - repo_files = repo_files.split("\0") - return repo_files - - -def list_all_files(): - return (normalize_path(path) for path in list_git_files()) - - -def excluded(path, exclude): - for entry in exclude: - if entry == os.path.commonpath((path, entry)): - return True - return False - - -def filter_files(paths, exclude): - if not exclude: - return paths - return (path for path in paths if not excluded(path, exclude)) - - -CPP_FILE_EXTENSIONS = set( - (".c", ".h", ".cc", ".hh", ".cpp", ".hpp", ".cxx", ".hxx", ".cp", ".c++") -) - - -def list_cpp_files(): - for path in list_all_files(): - ext = os.path.splitext(path)[1].lower() - if ext in CPP_FILE_EXTENSIONS: - yield path - - -DEFAULT_VERSION = "clang-format" -DEFAULT_STYLE = "file" - - -def process_cpp_files( - version=DEFAULT_VERSION, style=DEFAULT_STYLE, in_place=False, exclude=None -): - clang_format = ClangFormat(version, style) - with cd(git_root_dir()): - cpp_files = filter_files(list_cpp_files(), exclude) - if in_place: - clang_format.format_in_place(cpp_files) - else: - if not clang_format.show_diff(cpp_files): - sys.exit(1) - - -def parse_args(argv=None): - if argv is None: - argv = sys.argv[1:] - - parser = argparse.ArgumentParser( - description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter - ) - - parser.add_argument( - "-b", - "--clang-format", - dest="version", - default=DEFAULT_VERSION, - help="clang-format executable file path", - ) - parser.add_argument( - "-s", - "--style", - default=DEFAULT_STYLE, - help="clang-format -style parameter argument", - ) - parser.add_argument( - "-i", "--in-place", action="store_true", help="edit the files in-place" - ) - parser.add_argument( - "-e", - "--exclude", - nargs="*", - type=normalize_path, - help="files or directories to exclude", - ) - - return parser.parse_args(argv) - - -def main(argv=None): - args = parse_args(argv) - with setup_logging(): - process_cpp_files(**vars(args)) - - -if __name__ == "__main__": - main() |
