Teach the include sorter to not choke on include guards. ;]
[oota-llvm.git] / utils / sort_includes.py
1 #!/usr/bin/env python
2
3 """Script to sort the top-most block of #include lines.
4
5 Assumes the LLVM coding conventions.
6
7 Currently, this script only bothers sorting the llvm/... headers. Patches
8 welcome for more functionality, and sorting other header groups.
9 """
10
11 import argparse
12 import os
13 import re
14 import sys
15 import tempfile
16
17 def sort_includes(f):
18   lines = f.readlines()
19   look_for_api_header = f.name[-4:] == '.cpp'
20   headers_begin = 0
21   headers_end = 0
22   api_headers = []
23   local_headers = []
24   project_headers = []
25   system_headers = []
26   for (i, l) in enumerate(lines):
27     if l.strip() == '':
28       continue
29     if l.startswith('#include'):
30       if headers_begin == 0:
31         headers_begin = i
32       headers_end = i
33       header = l[len('#include'):].lstrip()
34       if look_for_api_header and header.startswith('"'):
35         api_headers.append(header)
36         look_for_api_header = False
37         continue
38       if header.startswith('<'):
39         system_headers.append(header)
40         continue
41       if header.startswith('"llvm/') or header.startswith('"clang/'):
42         project_headers.append(header)
43         continue
44       local_headers.append(header)
45       continue
46
47     # Only allow comments and #defines prior to any includes. If either are
48     # mixed with includes, the order might be sensitive.
49     if headers_begin != 0:
50       break
51     if l.startswith('//') or l.startswith('#define') or l.startswith('#ifndef'):
52       continue
53     break
54   if headers_begin == 0:
55     return
56
57   local_headers.sort()
58   project_headers.sort()
59   system_headers.sort()
60   headers = api_headers + local_headers + project_headers + system_headers
61   header_lines = ['#include ' + h for h in headers]
62   lines = lines[:headers_begin] + header_lines + lines[headers_end + 1:]
63
64   #for l in lines[headers_begin:headers_end]:
65   #  print l.rstrip()
66   f.seek(0)
67   f.truncate()
68   f.writelines(lines)
69
70 def main():
71   parser = argparse.ArgumentParser(description=__doc__)
72   parser.add_argument('files', nargs='+', type=argparse.FileType('r+'),
73                       help='the source files to sort includes within')
74   args = parser.parse_args()
75   for f in args.files:
76     sort_includes(f)
77
78 if __name__ == '__main__':
79   main()