| #!/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() |