PageRenderTime 70ms CodeModel.GetById 34ms RepoModel.GetById 0ms app.codeStats 0ms

/util/sort_includes.py

https://bitbucket.org/musleh123/gem5_cetus
Python | 220 lines | 160 code | 29 blank | 31 comment | 23 complexity | 3e00d37b6302da28bee176bb04ced6fa MD5 | raw file
Possible License(s): BSD-3-Clause, LGPL-2.1
  1. #!/usr/bin/env python
  2. import os
  3. import re
  4. import sys
  5. from file_types import *
  6. cpp_c_headers = {
  7. 'assert.h' : 'cassert',
  8. 'ctype.h' : 'cctype',
  9. 'errno.h' : 'cerrno',
  10. 'float.h' : 'cfloat',
  11. 'limits.h' : 'climits',
  12. 'locale.h' : 'clocale',
  13. 'math.h' : 'cmath',
  14. 'setjmp.h' : 'csetjmp',
  15. 'signal.h' : 'csignal',
  16. 'stdarg.h' : 'cstdarg',
  17. 'stddef.h' : 'cstddef',
  18. 'stdio.h' : 'cstdio',
  19. 'stdlib.h' : 'cstdlib',
  20. 'string.h' : 'cstring',
  21. 'time.h' : 'ctime',
  22. 'wchar.h' : 'cwchar',
  23. 'wctype.h' : 'cwctype',
  24. }
  25. include_re = re.compile(r'([#%])(include|import).*[<"](.*)[">]')
  26. def include_key(line):
  27. '''Mark directories with a leading space so directories
  28. are sorted before files'''
  29. match = include_re.match(line)
  30. assert match, line
  31. keyword = match.group(2)
  32. include = match.group(3)
  33. # Everything but the file part needs to have a space prepended
  34. parts = include.split('/')
  35. if len(parts) == 2 and parts[0] == 'dnet':
  36. # Don't sort the dnet includes with respect to each other, but
  37. # make them sorted with respect to non dnet includes. Python
  38. # guarantees that sorting is stable, so just clear the
  39. # basename part of the filename.
  40. parts[1] = ' '
  41. parts[0:-1] = [ ' ' + s for s in parts[0:-1] ]
  42. key = '/'.join(parts)
  43. return key
  44. class SortIncludes(object):
  45. # different types of includes for different sorting of headers
  46. # <Python.h> - Python header needs to be first if it exists
  47. # <*.h> - system headers (directories before files)
  48. # <*> - STL headers
  49. # <*.(hh|hxx|hpp|H)> - C++ Headers (directories before files)
  50. # "*" - M5 headers (directories before files)
  51. includes_re = (
  52. ('python', '<>', r'^(#include)[ \t]+<(Python.*\.h)>(.*)'),
  53. ('c', '<>', r'^(#include)[ \t]<(.+\.h)>(.*)'),
  54. ('stl', '<>', r'^(#include)[ \t]+<([0-9A-z_]+)>(.*)'),
  55. ('cc', '<>', r'^(#include)[ \t]+<([0-9A-z_]+\.(hh|hxx|hpp|H))>(.*)'),
  56. ('m5cc', '""', r'^(#include)[ \t]"(.+\.h{1,2})"(.*)'),
  57. ('swig0', '<>', r'^(%import)[ \t]<(.+)>(.*)'),
  58. ('swig1', '<>', r'^(%include)[ \t]<(.+)>(.*)'),
  59. ('swig2', '""', r'^(%import)[ \t]"(.+)"(.*)'),
  60. ('swig3', '""', r'^(%include)[ \t]"(.+)"(.*)'),
  61. )
  62. # compile the regexes
  63. includes_re = tuple((a, b, re.compile(c)) for a,b,c in includes_re)
  64. def __init__(self):
  65. self.reset()
  66. def reset(self):
  67. # clear all stored headers
  68. self.includes = {}
  69. for include_type,_,_ in self.includes_re:
  70. self.includes[include_type] = []
  71. def dump_block(self):
  72. '''dump the includes'''
  73. first = True
  74. for include,_,_ in self.includes_re:
  75. if not self.includes[include]:
  76. continue
  77. if not first:
  78. # print a newline between groups of
  79. # include types
  80. yield ''
  81. first = False
  82. # print out the includes in the current group
  83. # and sort them according to include_key()
  84. prev = None
  85. for l in sorted(self.includes[include],
  86. key=include_key):
  87. if l != prev:
  88. yield l
  89. prev = l
  90. def __call__(self, lines, filename, language):
  91. leading_blank = False
  92. blanks = 0
  93. block = False
  94. for line in lines:
  95. if not line:
  96. blanks += 1
  97. if not block:
  98. # if we're not in an include block, spit out the
  99. # newline otherwise, skip it since we're going to
  100. # control newlines withinin include block
  101. yield ''
  102. continue
  103. # Try to match each of the include types
  104. for include_type,(ldelim,rdelim),include_re in self.includes_re:
  105. match = include_re.match(line)
  106. if not match:
  107. continue
  108. # if we've got a match, clean up the #include line,
  109. # fix up stl headers and store it in the proper category
  110. groups = match.groups()
  111. keyword = groups[0]
  112. include = groups[1]
  113. extra = groups[-1]
  114. if include_type == 'c' and language == 'C++':
  115. stl_inc = cpp_c_headers.get(include, None)
  116. if stl_inc:
  117. include = stl_inc
  118. include_type = 'stl'
  119. line = keyword + ' ' + ldelim + include + rdelim + extra
  120. self.includes[include_type].append(line)
  121. # We've entered a block, don't keep track of blank
  122. # lines while in a block
  123. block = True
  124. blanks = 0
  125. break
  126. else:
  127. # this line did not match a #include
  128. assert not include_re.match(line)
  129. # if we're not in a block and we didn't match an include
  130. # to enter a block, just emit the line and continue
  131. if not block:
  132. yield line
  133. continue
  134. # We've exited an include block.
  135. for block_line in self.dump_block():
  136. yield block_line
  137. # if there are any newlines after the include block,
  138. # emit a single newline (removing extras)
  139. if blanks and block:
  140. yield ''
  141. blanks = 0
  142. block = False
  143. self.reset()
  144. # emit the line that ended the block
  145. yield line
  146. if block:
  147. # We've exited an include block.
  148. for block_line in self.dump_block():
  149. yield block_line
  150. # default language types to try to apply our sorting rules to
  151. default_languages = frozenset(('C', 'C++', 'isa', 'python', 'scons', 'swig'))
  152. def options():
  153. import optparse
  154. options = optparse.OptionParser()
  155. add_option = options.add_option
  156. add_option('-d', '--dir_ignore', metavar="DIR[,DIR]", type='string',
  157. default=','.join(default_dir_ignore),
  158. help="ignore directories")
  159. add_option('-f', '--file_ignore', metavar="FILE[,FILE]", type='string',
  160. default=','.join(default_file_ignore),
  161. help="ignore files")
  162. add_option('-l', '--languages', metavar="LANG[,LANG]", type='string',
  163. default=','.join(default_languages),
  164. help="languages")
  165. add_option('-n', '--dry-run', action='store_true',
  166. help="don't overwrite files")
  167. return options
  168. def parse_args(parser):
  169. opts,args = parser.parse_args()
  170. opts.dir_ignore = frozenset(opts.dir_ignore.split(','))
  171. opts.file_ignore = frozenset(opts.file_ignore.split(','))
  172. opts.languages = frozenset(opts.languages.split(','))
  173. return opts,args
  174. if __name__ == '__main__':
  175. parser = options()
  176. opts, args = parse_args(parser)
  177. for base in args:
  178. for filename,language in find_files(base, languages=opts.languages,
  179. file_ignore=opts.file_ignore, dir_ignore=opts.dir_ignore):
  180. if opts.dry_run:
  181. print "%s: %s" % (filename, language)
  182. else:
  183. update_file(filename, filename, language, SortIncludes())