113540Sandrea.mondelli@ucf.edu#!/usr/bin/env python2.7 211398SN/A# 311398SN/A# Copyright (c) 2014-2015 ARM Limited 411398SN/A# All rights reserved 511398SN/A# 611398SN/A# The license below extends only to copyright in the software and shall 711398SN/A# not be construed as granting a license to any other intellectual 811398SN/A# property including but not limited to intellectual property relating 911398SN/A# to a hardware implementation of the functionality of the software 1011398SN/A# licensed hereunder. You may use the software subject to the license 1111398SN/A# terms below provided that you ensure that this notice is replicated 1211398SN/A# unmodified and in its entirety in all distributions of the software, 1311398SN/A# modified or unmodified, in source code or in binary form. 1411398SN/A# 1511397SN/A# Copyright (c) 2011 The Hewlett-Packard Development Company 1611397SN/A# All rights reserved. 1711397SN/A# 1811397SN/A# Redistribution and use in source and binary forms, with or without 1911397SN/A# modification, are permitted provided that the following conditions are 2011397SN/A# met: redistributions of source code must retain the above copyright 2111397SN/A# notice, this list of conditions and the following disclaimer; 2211397SN/A# redistributions in binary form must reproduce the above copyright 2311397SN/A# notice, this list of conditions and the following disclaimer in the 2411397SN/A# documentation and/or other materials provided with the distribution; 2511397SN/A# neither the name of the copyright holders nor the names of its 2611397SN/A# contributors may be used to endorse or promote products derived from 2711397SN/A# this software without specific prior written permission. 2811397SN/A# 2911397SN/A# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 3011397SN/A# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 3111397SN/A# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 3211397SN/A# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 3311397SN/A# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 3411397SN/A# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 3511397SN/A# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 3611397SN/A# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 3711397SN/A# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 3811397SN/A# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 3911397SN/A# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 4011397SN/A# 4111397SN/A# Authors: Nathan Binkert 4211398SN/A# Andreas Sandberg 4311397SN/A 448225SN/Aimport os 458225SN/Aimport re 468225SN/Aimport sys 478225SN/A 488225SN/Afrom file_types import * 498225SN/A 508225SN/Acpp_c_headers = { 518225SN/A 'assert.h' : 'cassert', 528225SN/A 'ctype.h' : 'cctype', 538225SN/A 'errno.h' : 'cerrno', 548225SN/A 'float.h' : 'cfloat', 558225SN/A 'limits.h' : 'climits', 568225SN/A 'locale.h' : 'clocale', 578225SN/A 'math.h' : 'cmath', 588225SN/A 'setjmp.h' : 'csetjmp', 598225SN/A 'signal.h' : 'csignal', 608225SN/A 'stdarg.h' : 'cstdarg', 618225SN/A 'stddef.h' : 'cstddef', 628225SN/A 'stdio.h' : 'cstdio', 638225SN/A 'stdlib.h' : 'cstdlib', 648225SN/A 'string.h' : 'cstring', 658225SN/A 'time.h' : 'ctime', 668225SN/A 'wchar.h' : 'cwchar', 678225SN/A 'wctype.h' : 'cwctype', 688225SN/A} 698225SN/A 708225SN/Ainclude_re = re.compile(r'([#%])(include|import).*[<"](.*)[">]') 718225SN/Adef include_key(line): 728225SN/A '''Mark directories with a leading space so directories 738225SN/A are sorted before files''' 748225SN/A 758225SN/A match = include_re.match(line) 768225SN/A assert match, line 778225SN/A keyword = match.group(2) 788225SN/A include = match.group(3) 798225SN/A 808225SN/A # Everything but the file part needs to have a space prepended 818225SN/A parts = include.split('/') 828225SN/A if len(parts) == 2 and parts[0] == 'dnet': 838225SN/A # Don't sort the dnet includes with respect to each other, but 848225SN/A # make them sorted with respect to non dnet includes. Python 858225SN/A # guarantees that sorting is stable, so just clear the 868225SN/A # basename part of the filename. 878225SN/A parts[1] = ' ' 888225SN/A parts[0:-1] = [ ' ' + s for s in parts[0:-1] ] 898225SN/A key = '/'.join(parts) 908225SN/A 918225SN/A return key 928225SN/A 9310674SN/A 9410674SN/Adef _include_matcher(keyword="#include", delim="<>"): 9510674SN/A """Match an include statement and return a (keyword, file, extra) 9610674SN/A duple, or a touple of None values if there isn't a match.""" 9710674SN/A 9810674SN/A rex = re.compile(r'^(%s)\s*%s(.*)%s(.*)$' % (keyword, delim[0], delim[1])) 9910674SN/A 10010674SN/A def matcher(context, line): 10110674SN/A m = rex.match(line) 10210674SN/A return m.groups() if m else (None, ) * 3 10310674SN/A 10410674SN/A return matcher 10510674SN/A 10610674SN/Adef _include_matcher_fname(fname, **kwargs): 10710674SN/A """Match an include of a specific file name. Any keyword arguments 10810674SN/A are forwarded to _include_matcher, which is used to match the 10910674SN/A actual include line.""" 11010674SN/A 11110674SN/A rex = re.compile(fname) 11210674SN/A base_matcher = _include_matcher(**kwargs) 11310674SN/A 11410674SN/A def matcher(context, line): 11510674SN/A (keyword, fname, extra) = base_matcher(context, line) 11610674SN/A if fname and rex.match(fname): 11710674SN/A return (keyword, fname, extra) 11810674SN/A else: 11910674SN/A return (None, ) * 3 12010674SN/A 12110674SN/A return matcher 12210674SN/A 12310674SN/A 12410674SN/Adef _include_matcher_main(): 12510674SN/A """Match a C/C++ source file's primary header (i.e., a file with 12610674SN/A the same base name, but a header extension).""" 12710674SN/A 12810674SN/A base_matcher = _include_matcher(delim='""') 12910674SN/A rex = re.compile(r"^src/(.*)\.([^.]+)$") 13010674SN/A header_map = { 13110674SN/A "c" : "h", 13210674SN/A "cc" : "hh", 13310674SN/A "cpp" : "hh", 13410674SN/A } 13510674SN/A def matcher(context, line): 13610674SN/A m = rex.match(context["filename"]) 13710674SN/A if not m: 13810674SN/A return (None, ) * 3 13910674SN/A base, ext = m.groups() 14010674SN/A (keyword, fname, extra) = base_matcher(context, line) 14110674SN/A try: 14210674SN/A if fname == "%s.%s" % (base, header_map[ext]): 14310674SN/A return (keyword, fname, extra) 14410674SN/A except KeyError: 14510674SN/A pass 14610674SN/A 14710674SN/A return (None, ) * 3 14810674SN/A 14910674SN/A return matcher 15010674SN/A 1518225SN/Aclass SortIncludes(object): 1528225SN/A # different types of includes for different sorting of headers 1538225SN/A # <Python.h> - Python header needs to be first if it exists 1548225SN/A # <*.h> - system headers (directories before files) 1558225SN/A # <*> - STL headers 1568225SN/A # <*.(hh|hxx|hpp|H)> - C++ Headers (directories before files) 1578225SN/A # "*" - M5 headers (directories before files) 1588225SN/A includes_re = ( 15910674SN/A ('main', '""', _include_matcher_main()), 16010674SN/A ('python', '<>', _include_matcher_fname("^Python\.h$")), 16112009Sandreas.sandberg@arm.com ('pybind', '""', _include_matcher_fname("^pybind11/.*\.h$", 16212009Sandreas.sandberg@arm.com delim='""')), 16312162Sandreas.sandberg@arm.com ('m5shared', '<>', _include_matcher_fname("^gem5/")), 16410674SN/A ('c', '<>', _include_matcher_fname("^.*\.h$")), 16510674SN/A ('stl', '<>', _include_matcher_fname("^\w+$")), 16610674SN/A ('cc', '<>', _include_matcher_fname("^.*\.(hh|hxx|hpp|H)$")), 16710674SN/A ('m5header', '""', _include_matcher_fname("^.*\.h{1,2}$", delim='""')), 16810674SN/A ('swig0', '<>', _include_matcher(keyword="%import")), 16910674SN/A ('swig1', '<>', _include_matcher(keyword="%include")), 17010674SN/A ('swig2', '""', _include_matcher(keyword="%import", delim='""')), 17110674SN/A ('swig3', '""', _include_matcher(keyword="%include", delim='""')), 1728225SN/A ) 1738225SN/A 17410674SN/A block_order = ( 17511808Sandreas.sandberg@arm.com ('python', ), 17612009Sandreas.sandberg@arm.com ('pybind', ), 17710674SN/A ('main', ), 17810674SN/A ('c', ), 17910674SN/A ('stl', ), 18010674SN/A ('cc', ), 18112162Sandreas.sandberg@arm.com ('m5shared', ), 18210674SN/A ('m5header', ), 18310674SN/A ('swig0', 'swig1', 'swig2', 'swig3', ), 18410674SN/A ) 1858225SN/A 1868225SN/A def __init__(self): 18710674SN/A self.block_priority = {} 18810674SN/A for prio, keys in enumerate(self.block_order): 18910674SN/A for key in keys: 19010674SN/A self.block_priority[key] = prio 1918225SN/A 1928225SN/A def reset(self): 1938225SN/A # clear all stored headers 1948225SN/A self.includes = {} 1958225SN/A 19610674SN/A def dump_blocks(self, block_types): 19710674SN/A """Merge includes of from several block types into one large 19810674SN/A block of sorted includes. This is useful when we have multiple 19910674SN/A include block types (e.g., swig includes) with the same 20010674SN/A priority.""" 2018225SN/A 20210674SN/A includes = [] 20310674SN/A for block_type in block_types: 20410674SN/A try: 20510674SN/A includes += self.includes[block_type] 20610674SN/A except KeyError: 20710674SN/A pass 2088225SN/A 20910674SN/A return sorted(set(includes)) 21010674SN/A 21110674SN/A def dump_includes(self): 21211402SN/A includes = [] 21310674SN/A for types in self.block_order: 21411402SN/A block = self.dump_blocks(types) 21511402SN/A if includes and block: 21611402SN/A includes.append("") 21711402SN/A includes += block 21810674SN/A 21910674SN/A self.reset() 22011402SN/A return includes 2218225SN/A 2228225SN/A def __call__(self, lines, filename, language): 22310275SN/A self.reset() 2248225SN/A 22510674SN/A context = { 22610674SN/A "filename" : filename, 22710674SN/A "language" : language, 22810674SN/A } 22910674SN/A 23010674SN/A def match_line(line): 23110674SN/A if not line: 23210674SN/A return (None, line) 23310674SN/A 23410674SN/A for include_type, (ldelim, rdelim), matcher in self.includes_re: 23510674SN/A keyword, include, extra = matcher(context, line) 23610674SN/A if keyword: 23710674SN/A # if we've got a match, clean up the #include line, 23810674SN/A # fix up stl headers and store it in the proper category 23910674SN/A if include_type == 'c' and language == 'C++': 24010674SN/A stl_inc = cpp_c_headers.get(include, None) 24110674SN/A if stl_inc: 24210674SN/A include = stl_inc 24310674SN/A include_type = 'stl' 24410674SN/A 24510674SN/A return (include_type, 24610674SN/A keyword + ' ' + ldelim + include + rdelim + extra) 24710674SN/A 24810674SN/A return (None, line) 24910674SN/A 25010674SN/A processing_includes = False 2518225SN/A for line in lines: 25210674SN/A include_type, line = match_line(line) 25310674SN/A if include_type: 25410674SN/A try: 25510674SN/A self.includes[include_type].append(line) 25610674SN/A except KeyError: 25710674SN/A self.includes[include_type] = [ line ] 2588225SN/A 25910674SN/A processing_includes = True 26010674SN/A elif processing_includes and not line.strip(): 26110674SN/A # Skip empty lines while processing includes 26210674SN/A pass 26310674SN/A elif processing_includes: 26410674SN/A # We are now exiting an include block 26510674SN/A processing_includes = False 2668225SN/A 26710674SN/A # Output pending includes, a new line between, and the 26810674SN/A # current l. 26911402SN/A for include in self.dump_includes(): 27011402SN/A yield include 27110674SN/A yield '' 27210674SN/A yield line 2738225SN/A else: 27410674SN/A # We are not in an include block, so just emit the line 2758225SN/A yield line 2768225SN/A 27710674SN/A # We've reached EOF, so dump any pending includes 27810674SN/A if processing_includes: 27911402SN/A for include in self.dump_includes(): 28011402SN/A yield include 2818225SN/A 2828225SN/A# default language types to try to apply our sorting rules to 2838225SN/Adefault_languages = frozenset(('C', 'C++', 'isa', 'python', 'scons', 'swig')) 2848225SN/A 2858225SN/Adef options(): 2868225SN/A import optparse 2878225SN/A options = optparse.OptionParser() 2888225SN/A add_option = options.add_option 2898225SN/A add_option('-d', '--dir_ignore', metavar="DIR[,DIR]", type='string', 2908225SN/A default=','.join(default_dir_ignore), 2918225SN/A help="ignore directories") 2928225SN/A add_option('-f', '--file_ignore', metavar="FILE[,FILE]", type='string', 2938225SN/A default=','.join(default_file_ignore), 2948225SN/A help="ignore files") 2958225SN/A add_option('-l', '--languages', metavar="LANG[,LANG]", type='string', 2968225SN/A default=','.join(default_languages), 2978225SN/A help="languages") 2988225SN/A add_option('-n', '--dry-run', action='store_true', 2998225SN/A help="don't overwrite files") 3008225SN/A 3018225SN/A return options 3028225SN/A 3038225SN/Adef parse_args(parser): 3048225SN/A opts,args = parser.parse_args() 3058225SN/A 3068225SN/A opts.dir_ignore = frozenset(opts.dir_ignore.split(',')) 3078225SN/A opts.file_ignore = frozenset(opts.file_ignore.split(',')) 3088225SN/A opts.languages = frozenset(opts.languages.split(',')) 3098225SN/A 3108225SN/A return opts,args 3118225SN/A 3128225SN/Aif __name__ == '__main__': 3138225SN/A parser = options() 3148225SN/A opts, args = parse_args(parser) 3158225SN/A 3168225SN/A for base in args: 3178225SN/A for filename,language in find_files(base, languages=opts.languages, 3188225SN/A file_ignore=opts.file_ignore, dir_ignore=opts.dir_ignore): 3198225SN/A if opts.dry_run: 3208225SN/A print "%s: %s" % (filename, language) 3218225SN/A else: 3228225SN/A update_file(filename, filename, language, SortIncludes()) 323