132 lines
4.3 KiB
Python
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())
|