Caffe2 - Python API
A deep learning, cross platform ML framework
clang_format.py
1 #!/usr/bin/env python
2 """
3 A script that runs clang-format on changes detected via git. It will
4 report if running clang-format generated any changes.
5 
6 In CI, the script considers it a failure if running clang-format makes a change.
7 In the pre-commit hook, the user is prompted to apply any clang-format changes.
8 Running tools/clang_format.py manually with no arguments should replicate the pre-commit hook behavior.
9 
10 Only files that are in CLANG_FORMAT_WHITELIST are checked.
11 """
12 import subprocess
13 import os
14 import argparse
15 import fnmatch
16 import difflib
17 import sys
18 import re
19 
20 
21 # Whitelist of directories to check. All files that in that directory
22 # (recursively) will be checked.
23 CLANG_FORMAT_WHITELIST = ["torch/csrc/jit/", "test/cpp/jit/"]
24 
25 CPP_FILE_REGEX = re.compile("^.*\\.(h|cpp|cc|c|hpp)$")
26 
27 
28 def parse_args():
29  parser = argparse.ArgumentParser(
30  description="Execute clang-format on your working copy changes."
31  )
32  parser.add_argument(
33  "-d",
34  "--diff",
35  default="HEAD",
36  help="Git revision to diff against to get changes",
37  )
38  parser.add_argument(
39  "--accept-changes",
40  action="store_true",
41  default=False,
42  help=(
43  "If true, apply whatever changes clang-format creates. "
44  "Otherwise, just print the changes and exit"
45  ),
46  )
47  parser.add_argument(
48  "--check-all",
49  action="store_true",
50  default=False,
51  help="If true, check all whitelisted files instead of just working copy changes",
52  )
53  parser.add_argument("--verbose", "-v", action="store_true", default=False)
54  return parser.parse_args()
55 
56 
58  """
59  Parse CLANG_FORMAT_WHITELIST and resolve all directories.
60  Returns the set of whitelist cpp source files.
61  """
62  matches = []
63  for dir in CLANG_FORMAT_WHITELIST:
64  for root, dirnames, filenames in os.walk(dir):
65  for filename in filenames:
66  if CPP_FILE_REGEX.match(filename):
67  matches.append(os.path.join(root, filename))
68  return set(matches)
69 
70 
72  """
73  Get all changed files between the working tree and `rev`
74  """
75  changed_files = (
76  subprocess.check_output(
77  ["git", "diff-index", "--diff-filter=AMU", "--name-only", rev]
78  )
79  .decode()
80  .split("\n")
81  )
82  return set(changed_files)
83 
84 
85 def get_diffs(files):
86  """
87  Run clang-format on all `files` and report if it changed anything.
88  Returns a mapping of filename => diff generator
89  """
90  name_to_diffs = {}
91  for f in files:
92  formatted_text = subprocess.check_output(["clang-format", f]).decode()
93  with open(f) as orig:
94  orig_text = orig.read()
95  if formatted_text != orig_text:
96  orig_lines = orig_text.split("\n")
97  formatted_lines = formatted_text.split("\n")
98  diff = difflib.unified_diff(
99  orig_lines, formatted_lines, "original", "formatted"
100  )
101  name_to_diffs[f] = diff
102 
103  return name_to_diffs
104 
105 
106 def main():
107  args = parse_args()
108 
109  whitelisted_files = get_whitelisted_files()
110 
111  if args.check_all:
112  files_to_check = whitelisted_files
113  else:
114  changed_files = get_changed_files(args.diff)
115  files_to_check = changed_files & whitelisted_files
116 
117  if args.verbose:
118  print("Running clang-format on whitelisted files: ")
119  for f in files_to_check:
120  print(f)
121 
122  name_to_diffs = get_diffs(files_to_check)
123 
124  if len(name_to_diffs) == 0:
125  return
126 
127  if args.accept_changes:
128  # run clang-format on the necessary files
129  args = ["clang-format", "-i"]
130  args.extend(name_to_diffs.keys())
131  subprocess.check_output(args)
132 
133  # add the changes so they will be committed
134  args = ["git", "add"]
135  args.extend(name_to_diffs.keys())
136  subprocess.check_output(args)
137  else:
138  print("ERROR: Running clang-format created changes: ")
139  for name, diff in name_to_diffs.items():
140  print("In ", name)
141  for line in diff:
142  print(line)
143  print("\n")
144 
145 
146 if __name__ == "__main__":
147  main()
Module caffe2.python.layers.split.
def get_whitelisted_files()
Definition: clang_format.py:57
def get_diffs(files)
Definition: clang_format.py:85
def get_changed_files(rev)
Definition: clang_format.py:71