Files
UnrealEngine/Engine/Extras/clang-format/experimental-clang-format.py
Brandyn / Techy fcc1b09210 init
2026-04-04 15:40:51 -05:00

132 lines
4.3 KiB
Python

#!/usr/bin/env python3
import argparse, os, re, subprocess, sys
# Helpers
def run(cmd, **kw):
"""Run subprocess, capture output, raise on error."""
return subprocess.run(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True, check=True, **kw)
def get_opened_depots():
out = run(["p4", "opened"]).stdout.splitlines()
# each line: //depot/.../File.cpp#rev - edit change ...
return [line.split()[0].split("#")[0] for line in out if line]
def depot_to_client(path):
# p4 where //depot/... -> "//depot/... //client/... C:/.../File.cpp"
out = run(["p4", "where", path]).stdout.split()
return out[2] if len(out) >= 3 else None
def build_diff(path):
return run(["p4", "diff", "-du0", path]).stdout
# Main
def main():
p = argparse.ArgumentParser(
description="Simple Perforce wrapper for clang-format-diff.py with verbose output"
)
p.add_argument("files", nargs="*", help="Depot or local paths to format")
p.add_argument("--verify", action="store_true",
help="Dry-run: report files needing formatting")
p.add_argument("--diff-script", default="../../Engine/Extras/clang-format/clang-format-diff.py",
help="Path to LLVM clang-format-diff.py")
p.add_argument("--clang-format", default="clang-format",
help="Path to clang-format binary")
p.add_argument("-c", "--config-file", default=None,
help="Explicit path to .clang-format to use")
p.add_argument("-v", "--verbose", action="store_true",
help="Show detailed operations and diffs")
args = p.parse_args()
verbose = args.verbose
# determine list of targets
if args.files:
depots = [f.split("#")[0] for f in args.files]
else:
depots = get_opened_depots()
if verbose:
print("Opened files:")
for c in depots:
print(f" - {c}")
# filter .cpp/.h
clients = []
for d in depots:
c = depot_to_client(d)
if c and re.search(r'\.(cpp|h)$', c, re.IGNORECASE):
clients.append(c)
if not clients:
print("No .cpp/.h files to format.")
return 0
if verbose:
print("Formatting .h/.cpp:")
for c in clients:
print(f" - {c}")
status = 0
for local in clients:
diff = build_diff(local)
if not diff:
if verbose:
print(f"No changes in: {local}")
continue # no changes
if verbose:
print(f"\n=== Processing: {local} ===")
print("--- original diff ---")
print(diff)
cmd = [
sys.executable, args.diff_script,
f"-binary={args.clang_format}",
"-style=file",
]
# Build style argument: use explicit config when provided, else local .clang-format discovery
style_arg = "-style=file"
if args.config_file:
cfg = os.path.abspath(args.config_file)
style_arg = f"-style=file:{cfg}"
if args.verbose:
print(f"Using clang-format config: {cfg}")
cmd = [
sys.executable, args.diff_script,
f"-binary={args.clang_format}",
style_arg,
]
if not args.verify:
cmd.append("-i")
if verbose:
print("Running:", ' '.join(cmd))
proc = subprocess.run(cmd, input=diff, text=True,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE)
if verbose and proc.stdout:
print("--- clang-format-diff output ---")
print(proc.stdout)
if verbose and proc.stderr:
print("--- errors ---", file=sys.stderr)
print(proc.stderr, file=sys.stderr)
if args.verify:
print(f"*** NEEDS FORMATTING: {local}")
status = 1
else:
if proc.returncode == 0:
print(f"Patched diff-hunks in: {local}")
else:
# covers proc.returncode != 0
print(f"Error running clang-format-diff on {local}", file=sys.stderr)
print(proc.stderr, file=sys.stderr)
status = max(status, proc.returncode)
return status
if __name__ == "__main__":
sys.exit(main())