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