|  | #!/usr/bin/env python | 
|  |  | 
|  | """Script to sort the top-most block of #include lines. | 
|  |  | 
|  | Assumes the LLVM coding conventions. | 
|  |  | 
|  | Currently, this script only bothers sorting the llvm/... headers. Patches | 
|  | welcome for more functionality, and sorting other header groups. | 
|  | """ | 
|  |  | 
|  | import argparse | 
|  | import os | 
|  |  | 
|  |  | 
|  | def sort_includes(f): | 
|  | """Sort the #include lines of a specific file.""" | 
|  |  | 
|  | # Skip files which are under INPUTS trees or test trees. | 
|  | if "INPUTS/" in f.name or "test/" in f.name: | 
|  | return | 
|  |  | 
|  | ext = os.path.splitext(f.name)[1] | 
|  | if ext not in [".cpp", ".c", ".h", ".inc", ".def"]: | 
|  | return | 
|  |  | 
|  | lines = f.readlines() | 
|  | look_for_api_header = ext in [".cpp", ".c"] | 
|  | found_headers = False | 
|  | headers_begin = 0 | 
|  | headers_end = 0 | 
|  | api_headers = [] | 
|  | local_headers = [] | 
|  | subproject_headers = [] | 
|  | llvm_headers = [] | 
|  | system_headers = [] | 
|  | for (i, l) in enumerate(lines): | 
|  | if l.strip() == "": | 
|  | continue | 
|  | if l.startswith("#include"): | 
|  | if not found_headers: | 
|  | headers_begin = i | 
|  | found_headers = True | 
|  | headers_end = i | 
|  | header = l[len("#include") :].lstrip() | 
|  | if look_for_api_header and header.startswith('"'): | 
|  | api_headers.append(header) | 
|  | look_for_api_header = False | 
|  | continue | 
|  | if ( | 
|  | header.startswith("<") | 
|  | or header.startswith('"gtest/') | 
|  | or header.startswith('"isl/') | 
|  | or header.startswith('"json/') | 
|  | ): | 
|  | system_headers.append(header) | 
|  | continue | 
|  | if ( | 
|  | header.startswith('"clang/') | 
|  | or header.startswith('"clang-c/') | 
|  | or header.startswith('"polly/') | 
|  | ): | 
|  | subproject_headers.append(header) | 
|  | continue | 
|  | if header.startswith('"llvm/') or header.startswith('"llvm-c/'): | 
|  | llvm_headers.append(header) | 
|  | continue | 
|  | local_headers.append(header) | 
|  | continue | 
|  |  | 
|  | # Only allow comments and #defines prior to any includes. If either are | 
|  | # mixed with includes, the order might be sensitive. | 
|  | if found_headers: | 
|  | break | 
|  | if l.startswith("//") or l.startswith("#define") or l.startswith("#ifndef"): | 
|  | continue | 
|  | break | 
|  | if not found_headers: | 
|  | return | 
|  |  | 
|  | local_headers = sorted(set(local_headers)) | 
|  | subproject_headers = sorted(set(subproject_headers)) | 
|  | llvm_headers = sorted(set(llvm_headers)) | 
|  | system_headers = sorted(set(system_headers)) | 
|  | headers = ( | 
|  | api_headers + local_headers + subproject_headers + llvm_headers + system_headers | 
|  | ) | 
|  | header_lines = ["#include " + h for h in headers] | 
|  | lines = lines[:headers_begin] + header_lines + lines[headers_end + 1 :] | 
|  |  | 
|  | f.seek(0) | 
|  | f.truncate() | 
|  | f.writelines(lines) | 
|  |  | 
|  |  | 
|  | def main(): | 
|  | parser = argparse.ArgumentParser(description=__doc__) | 
|  | parser.add_argument( | 
|  | "files", | 
|  | nargs="+", | 
|  | type=argparse.FileType("r+"), | 
|  | help="the source files to sort includes within", | 
|  | ) | 
|  | args = parser.parse_args() | 
|  | for f in args.files: | 
|  | sort_includes(f) | 
|  |  | 
|  |  | 
|  | if __name__ == "__main__": | 
|  | main() |