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