Run DCE after a LoopFlatten test to reduce spurious output [nfc]
[llvm-project.git] / clang / docs / tools / generate_formatted_state.py
blob66cebbf7af33a4a9dab303b93d83f91428d79b05
1 #!/usr/bin/env python
2 # A tool to parse creates a document outlining how clang formatted the
3 # LLVM project is.
5 import sys
6 import os
7 import subprocess
8 from datetime import datetime
11 def get_git_revision_short_hash():
12 """Get the get SHA in short hash form."""
13 return (
14 subprocess.check_output(["git", "rev-parse", "--short", "HEAD"])
15 .decode(sys.stdout.encoding)
16 .strip()
20 def get_style(count, passed):
21 """Determine if this directory is good based on the number of clean
22 files vs the number of files in total."""
23 if passed == count:
24 return ":good:"
25 if passed != 0:
26 return ":part:"
27 return ":none:"
30 TOP_DIR = os.path.join(os.path.dirname(__file__), "../../..")
31 CLANG_DIR = os.path.join(os.path.dirname(__file__), "../..")
32 DOC_FILE = os.path.join(CLANG_DIR, "docs/ClangFormattedStatus.rst")
33 CLEAN_FILE = os.path.join(CLANG_DIR, "docs/tools/clang-formatted-files.txt")
35 rootdir = TOP_DIR
37 skipped_dirs = [".git", "test"]
38 suffixes = (".cpp", ".h")
40 RST_PREFIX = """\
41 .. raw:: html
43 <style type="text/css">
44 .total {{ font-weight: bold; }}
45 .none {{ background-color: #FFFF99; height: 20px; display: inline-block; width: 120px; text-align: center; border-radius: 5px; color: #000000; font-family="Verdana,Geneva,DejaVu Sans,sans-serif" }}
46 .part {{ background-color: #FFCC99; height: 20px; display: inline-block; width: 120px; text-align: center; border-radius: 5px; color: #000000; font-family="Verdana,Geneva,DejaVu Sans,sans-serif" }}
47 .good {{ background-color: #2CCCFF; height: 20px; display: inline-block; width: 120px; text-align: center; border-radius: 5px; color: #000000; font-family="Verdana,Geneva,DejaVu Sans,sans-serif" }}
48 </style>
50 .. role:: none
51 .. role:: part
52 .. role:: good
53 .. role:: total
55 ======================
56 Clang Formatted Status
57 ======================
59 :doc:`ClangFormattedStatus` describes the state of LLVM source
60 tree in terms of conformance to :doc:`ClangFormat` as of: {today} (`{sha} <https://github.com/llvm/llvm-project/commit/{sha}>`_).
63 .. list-table:: LLVM Clang-Format Status
64 :widths: 50 25 25 25 25
65 :header-rows: 1\n
66 * - Directory
67 - Total Files
68 - Formatted Files
69 - Unformatted Files
70 - % Complete
71 """
73 TABLE_ROW = """\
74 * - {path}
75 - {style}`{count}`
76 - {style}`{passes}`
77 - {style}`{fails}`
78 - {style2}`{percent}%`
79 """
81 FNULL = open(os.devnull, "w")
84 with open(DOC_FILE, "wb") as output:
85 cleanfiles = open(CLEAN_FILE, "wb")
86 sha = get_git_revision_short_hash()
87 today = datetime.now().strftime("%B %d, %Y %H:%M:%S")
88 output.write(bytes(RST_PREFIX.format(today=today, sha=sha).encode("utf-8")))
90 total_files_count = 0
91 total_files_pass = 0
92 total_files_fail = 0
93 for root, subdirs, files in os.walk(rootdir):
94 for subdir in subdirs:
95 if any(sd == subdir for sd in skipped_dirs):
96 subdirs.remove(subdir)
97 else:
98 act_sub_dir = os.path.join(root, subdir)
99 # Check the git index to see if the directory contains tracked
100 # files. Reditect the output to a null descriptor as we aren't
101 # interested in it, just the return code.
102 git_check = subprocess.Popen(
103 ["git", "ls-files", "--error-unmatch", act_sub_dir],
104 stdout=FNULL,
105 stderr=FNULL,
107 if git_check.wait() != 0:
108 print("Skipping directory: ", act_sub_dir)
109 subdirs.remove(subdir)
111 path = os.path.relpath(root, TOP_DIR)
112 path = path.replace("\\", "/")
114 file_count = 0
115 file_pass = 0
116 file_fail = 0
117 for filename in files:
118 file_path = os.path.join(root, filename)
119 ext = os.path.splitext(file_path)[-1].lower()
120 if not ext.endswith(suffixes):
121 continue
123 file_count += 1
125 args = ["clang-format", "-n", file_path]
126 cmd = subprocess.Popen(args, stderr=subprocess.PIPE)
127 stdout, err = cmd.communicate()
129 relpath = os.path.relpath(file_path, TOP_DIR)
130 relpath = relpath.replace("\\", "/")
131 if err.decode(sys.stdout.encoding).find(": warning:") > 0:
132 print(relpath, ":", "FAIL")
133 file_fail += 1
134 else:
135 print(relpath, ":", "PASS")
136 file_pass += 1
137 cleanfiles.write(bytes(relpath + "\n"))
138 cleanfiles.flush()
140 total_files_count += file_count
141 total_files_pass += file_pass
142 total_files_fail += file_fail
144 if file_count > 0:
145 percent = int(100.0 * (float(file_pass) / float(file_count)))
146 style = get_style(file_count, file_pass)
147 output.write(
148 bytes(
149 TABLE_ROW.format(
150 path=path,
151 count=file_count,
152 passes=file_pass,
153 fails=file_fail,
154 percent=str(percent),
155 style="",
156 style2=style,
157 ).encode("utf-8")
160 output.flush()
162 print("----\n")
163 print(path, file_count, file_pass, file_fail, percent)
164 print("----\n")
166 total_percent = float(total_files_pass) / float(total_files_count)
167 percent_str = str(int(100.0 * total_percent))
168 output.write(
169 bytes(
170 TABLE_ROW.format(
171 path="Total",
172 count=total_files_count,
173 passes=total_files_pass,
174 fails=total_files_fail,
175 percent=percent_str,
176 style=":total:",
177 style2=":total:",
178 ).encode("utf-8")