/util/sort_includes.py
Python | 220 lines | 160 code | 29 blank | 31 comment | 23 complexity | 3e00d37b6302da28bee176bb04ced6fa MD5 | raw file
Possible License(s): BSD-3-Clause, LGPL-2.1, WTFPL
- #!/usr/bin/env python
- import os
- import re
- import sys
- from file_types import *
- cpp_c_headers = {
- 'assert.h' : 'cassert',
- 'ctype.h' : 'cctype',
- 'errno.h' : 'cerrno',
- 'float.h' : 'cfloat',
- 'limits.h' : 'climits',
- 'locale.h' : 'clocale',
- 'math.h' : 'cmath',
- 'setjmp.h' : 'csetjmp',
- 'signal.h' : 'csignal',
- 'stdarg.h' : 'cstdarg',
- 'stddef.h' : 'cstddef',
- 'stdio.h' : 'cstdio',
- 'stdlib.h' : 'cstdlib',
- 'string.h' : 'cstring',
- 'time.h' : 'ctime',
- 'wchar.h' : 'cwchar',
- 'wctype.h' : 'cwctype',
- }
- include_re = re.compile(r'([#%])(include|import).*[<"](.*)[">]')
- def include_key(line):
- '''Mark directories with a leading space so directories
- are sorted before files'''
- match = include_re.match(line)
- assert match, line
- keyword = match.group(2)
- include = match.group(3)
- # Everything but the file part needs to have a space prepended
- parts = include.split('/')
- if len(parts) == 2 and parts[0] == 'dnet':
- # Don't sort the dnet includes with respect to each other, but
- # make them sorted with respect to non dnet includes. Python
- # guarantees that sorting is stable, so just clear the
- # basename part of the filename.
- parts[1] = ' '
- parts[0:-1] = [ ' ' + s for s in parts[0:-1] ]
- key = '/'.join(parts)
- return key
- class SortIncludes(object):
- # different types of includes for different sorting of headers
- # <Python.h> - Python header needs to be first if it exists
- # <*.h> - system headers (directories before files)
- # <*> - STL headers
- # <*.(hh|hxx|hpp|H)> - C++ Headers (directories before files)
- # "*" - M5 headers (directories before files)
- includes_re = (
- ('python', '<>', r'^(#include)[ \t]+<(Python.*\.h)>(.*)'),
- ('c', '<>', r'^(#include)[ \t]<(.+\.h)>(.*)'),
- ('stl', '<>', r'^(#include)[ \t]+<([0-9A-z_]+)>(.*)'),
- ('cc', '<>', r'^(#include)[ \t]+<([0-9A-z_]+\.(hh|hxx|hpp|H))>(.*)'),
- ('m5cc', '""', r'^(#include)[ \t]"(.+\.h{1,2})"(.*)'),
- ('swig0', '<>', r'^(%import)[ \t]<(.+)>(.*)'),
- ('swig1', '<>', r'^(%include)[ \t]<(.+)>(.*)'),
- ('swig2', '""', r'^(%import)[ \t]"(.+)"(.*)'),
- ('swig3', '""', r'^(%include)[ \t]"(.+)"(.*)'),
- )
- # compile the regexes
- includes_re = tuple((a, b, re.compile(c)) for a,b,c in includes_re)
- def __init__(self):
- self.reset()
- def reset(self):
- # clear all stored headers
- self.includes = {}
- for include_type,_,_ in self.includes_re:
- self.includes[include_type] = []
- def dump_block(self):
- '''dump the includes'''
- first = True
- for include,_,_ in self.includes_re:
- if not self.includes[include]:
- continue
- if not first:
- # print a newline between groups of
- # include types
- yield ''
- first = False
- # print out the includes in the current group
- # and sort them according to include_key()
- prev = None
- for l in sorted(self.includes[include],
- key=include_key):
- if l != prev:
- yield l
- prev = l
- def __call__(self, lines, filename, language):
- leading_blank = False
- blanks = 0
- block = False
- for line in lines:
- if not line:
- blanks += 1
- if not block:
- # if we're not in an include block, spit out the
- # newline otherwise, skip it since we're going to
- # control newlines withinin include block
- yield ''
- continue
- # Try to match each of the include types
- for include_type,(ldelim,rdelim),include_re in self.includes_re:
- match = include_re.match(line)
- if not match:
- continue
- # if we've got a match, clean up the #include line,
- # fix up stl headers and store it in the proper category
- groups = match.groups()
- keyword = groups[0]
- include = groups[1]
- extra = groups[-1]
- if include_type == 'c' and language == 'C++':
- stl_inc = cpp_c_headers.get(include, None)
- if stl_inc:
- include = stl_inc
- include_type = 'stl'
- line = keyword + ' ' + ldelim + include + rdelim + extra
- self.includes[include_type].append(line)
- # We've entered a block, don't keep track of blank
- # lines while in a block
- block = True
- blanks = 0
- break
- else:
- # this line did not match a #include
- assert not include_re.match(line)
- # if we're not in a block and we didn't match an include
- # to enter a block, just emit the line and continue
- if not block:
- yield line
- continue
- # We've exited an include block.
- for block_line in self.dump_block():
- yield block_line
- # if there are any newlines after the include block,
- # emit a single newline (removing extras)
- if blanks and block:
- yield ''
- blanks = 0
- block = False
- self.reset()
- # emit the line that ended the block
- yield line
- if block:
- # We've exited an include block.
- for block_line in self.dump_block():
- yield block_line
- # default language types to try to apply our sorting rules to
- default_languages = frozenset(('C', 'C++', 'isa', 'python', 'scons', 'swig'))
- def options():
- import optparse
- options = optparse.OptionParser()
- add_option = options.add_option
- add_option('-d', '--dir_ignore', metavar="DIR[,DIR]", type='string',
- default=','.join(default_dir_ignore),
- help="ignore directories")
- add_option('-f', '--file_ignore', metavar="FILE[,FILE]", type='string',
- default=','.join(default_file_ignore),
- help="ignore files")
- add_option('-l', '--languages', metavar="LANG[,LANG]", type='string',
- default=','.join(default_languages),
- help="languages")
- add_option('-n', '--dry-run', action='store_true',
- help="don't overwrite files")
- return options
- def parse_args(parser):
- opts,args = parser.parse_args()
- opts.dir_ignore = frozenset(opts.dir_ignore.split(','))
- opts.file_ignore = frozenset(opts.file_ignore.split(','))
- opts.languages = frozenset(opts.languages.split(','))
- return opts,args
- if __name__ == '__main__':
- parser = options()
- opts, args = parse_args(parser)
- for base in args:
- for filename,language in find_files(base, languages=opts.languages,
- file_ignore=opts.file_ignore, dir_ignore=opts.dir_ignore):
- if opts.dry_run:
- print "%s: %s" % (filename, language)
- else:
- update_file(filename, filename, language, SortIncludes())