7 from file_types
import *
10 'assert.h' : 'cassert',
14 'limits.h' : 'climits',
15 'locale.h' : 'clocale',
17 'setjmp.h' : 'csetjmp',
18 'signal.h' : 'csignal',
19 'stdarg.h' : 'cstdarg',
20 'stddef.h' : 'cstddef',
22 'stdlib.h' : 'cstdlib',
23 'string.h' : 'cstring',
26 'wctype.h' : 'cwctype',
29 include_re
= re
.compile(r
'([#%])(include|import).*[<"](.*)[">]')
30 def include_key(line
):
31 '''Mark directories with a leading space so directories
32 are sorted before files'''
34 match
= include_re
.match(line
)
36 keyword
= match
.group(2)
37 include
= match
.group(3)
39 # Everything but the file part needs to have a space prepended
40 parts
= include
.split('/')
41 if len(parts
) == 2 and parts
[0] == 'dnet':
42 # Don't sort the dnet includes with respect to each other, but
43 # make them sorted with respect to non dnet includes. Python
44 # guarantees that sorting is stable, so just clear the
45 # basename part of the filename.
47 parts
[0:-1] = [ ' ' + s
for s
in parts
[0:-1] ]
53 def _include_matcher(keyword
="#include", delim
="<>"):
54 """Match an include statement and return a (keyword, file, extra)
55 duple, or a touple of None values if there isn't a match."""
57 rex
= re
.compile(r
'^(%s)\s*%s(.*)%s(.*)$' % (keyword
, delim
[0], delim
[1]))
59 def matcher(context
, line
):
61 return m
.groups() if m
else (None, ) * 3
65 def _include_matcher_fname(fname
, **kwargs
):
66 """Match an include of a specific file name. Any keyword arguments
67 are forwarded to _include_matcher, which is used to match the
68 actual include line."""
70 rex
= re
.compile(fname
)
71 base_matcher
= _include_matcher(**kwargs
)
73 def matcher(context
, line
):
74 (keyword
, fname
, extra
) = base_matcher(context
, line
)
75 if fname
and rex
.match(fname
):
76 return (keyword
, fname
, extra
)
83 def _include_matcher_main():
84 """Match a C/C++ source file's primary header (i.e., a file with
85 the same base name, but a header extension)."""
87 base_matcher
= _include_matcher(delim
='""')
88 rex
= re
.compile(r
"^src/(.*)\.([^.]+)$")
94 def matcher(context
, line
):
95 m
= rex
.match(context
["filename"])
98 base
, ext
= m
.groups()
99 (keyword
, fname
, extra
) = base_matcher(context
, line
)
101 if fname
== "%s.%s" % (base
, header_map
[ext
]):
102 return (keyword
, fname
, extra
)
110 class SortIncludes(object):
111 # different types of includes for different sorting of headers
112 # <Python.h> - Python header needs to be first if it exists
113 # <*.h> - system headers (directories before files)
115 # <*.(hh|hxx|hpp|H)> - C++ Headers (directories before files)
116 # "*" - M5 headers (directories before files)
118 ('main', '""', _include_matcher_main()),
119 ('python', '<>', _include_matcher_fname("^Python\.h$")),
120 ('c', '<>', _include_matcher_fname("^.*\.h$")),
121 ('stl', '<>', _include_matcher_fname("^\w+$")),
122 ('cc', '<>', _include_matcher_fname("^.*\.(hh|hxx|hpp|H)$")),
123 ('m5header', '""', _include_matcher_fname("^.*\.h{1,2}$", delim
='""')),
124 ('swig0', '<>', _include_matcher(keyword
="%import")),
125 ('swig1', '<>', _include_matcher(keyword
="%include")),
126 ('swig2', '""', _include_matcher(keyword
="%import", delim
='""')),
127 ('swig3', '""', _include_matcher(keyword
="%include", delim
='""')),
137 ('swig0', 'swig1', 'swig2', 'swig3', ),
141 self
.block_priority
= {}
142 for prio
, keys
in enumerate(self
.block_order
):
144 self
.block_priority
[key
] = prio
147 # clear all stored headers
150 def dump_blocks(self
, block_types
):
151 """Merge includes of from several block types into one large
152 block of sorted includes. This is useful when we have multiple
153 include block types (e.g., swig includes) with the same
157 for block_type
in block_types
:
159 includes
+= self
.includes
[block_type
]
163 return sorted(set(includes
))
165 def dump_includes(self
):
167 # Create a list of blocks in the prescribed include
168 # order. Each entry in the list is a multi-line string with
170 for types
in self
.block_order
:
171 block
= "\n".join(self
.dump_blocks(types
))
176 return "\n\n".join(blocks
)
178 def __call__(self
, lines
, filename
, language
):
182 "filename" : filename
,
183 "language" : language
,
186 def match_line(line
):
190 for include_type
, (ldelim
, rdelim
), matcher
in self
.includes_re
:
191 keyword
, include
, extra
= matcher(context
, line
)
193 # if we've got a match, clean up the #include line,
194 # fix up stl headers and store it in the proper category
195 if include_type
== 'c' and language
== 'C++':
196 stl_inc
= cpp_c_headers
.get(include
, None)
201 return (include_type
,
202 keyword
+ ' ' + ldelim
+ include
+ rdelim
+ extra
)
206 processing_includes
= False
208 include_type
, line
= match_line(line
)
211 self
.includes
[include_type
].append(line
)
213 self
.includes
[include_type
] = [ line
]
215 processing_includes
= True
216 elif processing_includes
and not line
.strip():
217 # Skip empty lines while processing includes
219 elif processing_includes
:
220 # We are now exiting an include block
221 processing_includes
= False
223 # Output pending includes, a new line between, and the
225 yield self
.dump_includes()
229 # We are not in an include block, so just emit the line
232 # We've reached EOF, so dump any pending includes
233 if processing_includes
:
234 yield self
.dump_includes()
238 # default language types to try to apply our sorting rules to
239 default_languages
= frozenset(('C', 'C++', 'isa', 'python', 'scons', 'swig'))
243 options
= optparse
.OptionParser()
244 add_option
= options
.add_option
245 add_option('-d', '--dir_ignore', metavar
="DIR[,DIR]", type='string',
246 default
=','.join(default_dir_ignore
),
247 help="ignore directories")
248 add_option('-f', '--file_ignore', metavar
="FILE[,FILE]", type='string',
249 default
=','.join(default_file_ignore
),
251 add_option('-l', '--languages', metavar
="LANG[,LANG]", type='string',
252 default
=','.join(default_languages
),
254 add_option('-n', '--dry-run', action
='store_true',
255 help="don't overwrite files")
259 def parse_args(parser
):
260 opts
,args
= parser
.parse_args()
262 opts
.dir_ignore
= frozenset(opts
.dir_ignore
.split(','))
263 opts
.file_ignore
= frozenset(opts
.file_ignore
.split(','))
264 opts
.languages
= frozenset(opts
.languages
.split(','))
268 if __name__
== '__main__':
270 opts
, args
= parse_args(parser
)
273 for filename
,language
in find_files(base
, languages
=opts
.languages
,
274 file_ignore
=opts
.file_ignore
, dir_ignore
=opts
.dir_ignore
):
276 print "%s: %s" % (filename
, language
)
278 update_file(filename
, filename
, language
, SortIncludes())