1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187
|
#!/usr/bin/env python
# Copyright (c) 2012 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""Given a filename as an argument, sort the #include/#imports in that file.
Shows a diff and prompts for confirmation before doing the deed.
Works great with tools/git/for-all-touched-files.py.
"""
import optparse
import os
import sys
from yes_no import YesNo
def IsInclude(line):
"""Returns True if the line is an #include/#import/import line."""
return any([line.startswith('#include '), line.startswith('#import '),
line.startswith('import ')])
def IncludeCompareKey(line, for_blink):
"""Sorting comparator key used for comparing two #include lines.
Returns an integer, optionally followed by a string. The integer is used
for coarse sorting of different categories of headers, and the string is
used for fine sorting of headers within categeries.
"""
for prefix in ('#include ', '#import ', 'import '):
if line.startswith(prefix):
line = line[len(prefix):]
break
if for_blink:
# Blink likes to have its "config.h" include first.
if line.startswith('"config.h"'):
return '0'
# Blink sorts system headers after others. This is handled by sorting
# alphabetically so no need to do anything tricky.
return '1' + line
# The win32 api has all sorts of implicit include order dependencies :-/
# Give a few headers special sort keys that make sure they appear before all
# other headers.
if line.startswith('<windows.h>'): # Must be before e.g. shellapi.h
return '0'
if line.startswith('<atlbase.h>'): # Must be before atlapp.h.
return '1' + line
if line.startswith('<ole2.h>'): # Must be before e.g. intshcut.h
return '1' + line
if line.startswith('<unknwn.h>'): # Must be before e.g. intshcut.h
return '1' + line
# C++ system headers should come after C system headers.
if line.startswith('<'):
if line.find('.h>') != -1:
return '2' + line.lower()
else:
return '3' + line.lower()
return '4' + line
def SortHeader(infile, outfile, for_blink):
"""Sorts the headers in infile, writing the sorted file to outfile."""
def CompareKey(line):
return IncludeCompareKey(line, for_blink)
for line in infile:
if IsInclude(line):
headerblock = []
while IsInclude(line):
infile_ended_on_include_line = False
headerblock.append(line)
# Ensure we don't die due to trying to read beyond the end of the file.
try:
line = infile.next()
except StopIteration:
infile_ended_on_include_line = True
break
for header in sorted(headerblock, key=CompareKey):
outfile.write(header)
if infile_ended_on_include_line:
# We already wrote the last line above; exit to ensure it isn't written
# again.
return
# Intentionally fall through, to write the line that caused
# the above while loop to exit.
outfile.write(line)
def FixFileWithConfirmFunction(filename, confirm_function,
perform_safety_checks, for_blink=False):
"""Creates a fixed version of the file, invokes |confirm_function|
to decide whether to use the new file, and cleans up.
|confirm_function| takes two parameters, the original filename and
the fixed-up filename, and returns True to use the fixed-up file,
false to not use it.
If |perform_safety_checks| is True, then the function checks whether it is
unsafe to reorder headers in this file and skips the reorder with a warning
message in that case.
"""
if perform_safety_checks and IsUnsafeToReorderHeaders(filename):
print ('Not reordering headers in %s as the script thinks that the '
'order of headers in this file is semantically significant.'
% (filename))
return
fixfilename = filename + '.new'
infile = open(filename, 'rb')
outfile = open(fixfilename, 'wb')
SortHeader(infile, outfile, for_blink)
infile.close()
outfile.close() # Important so the below diff gets the updated contents.
try:
if confirm_function(filename, fixfilename):
if sys.platform == 'win32':
os.unlink(filename)
os.rename(fixfilename, filename)
finally:
try:
os.remove(fixfilename)
except OSError:
# If the file isn't there, we don't care.
pass
def DiffAndConfirm(filename, should_confirm, perform_safety_checks, for_blink):
"""Shows a diff of what the tool would change the file named
filename to. Shows a confirmation prompt if should_confirm is true.
Saves the resulting file if should_confirm is false or the user
answers Y to the confirmation prompt.
"""
def ConfirmFunction(filename, fixfilename):
diff = os.system('diff -u %s %s' % (filename, fixfilename))
if sys.platform != 'win32':
diff >>= 8
if diff == 0: # Check exit code.
print '%s: no change' % filename
return False
return (not should_confirm or YesNo('Use new file (y/N)?'))
FixFileWithConfirmFunction(filename, ConfirmFunction, perform_safety_checks,
for_blink)
def IsUnsafeToReorderHeaders(filename):
# *_message_generator.cc is almost certainly a file that generates IPC
# definitions. Changes in include order in these files can result in them not
# building correctly.
if filename.find("message_generator.cc") != -1:
return True
return False
def main():
parser = optparse.OptionParser(usage='%prog filename1 filename2 ...')
parser.add_option('-f', '--force', action='store_false', default=True,
dest='should_confirm',
help='Turn off confirmation prompt.')
parser.add_option('--no_safety_checks',
action='store_false', default=True,
dest='perform_safety_checks',
help='Do not perform the safety checks via which this '
'script refuses to operate on files for which it thinks '
'the include ordering is semantically significant.')
parser.add_option('--for_blink', action='store_true', default=False,
dest='for_blink', help='Whether the blink header sorting '
'rules should be applied.')
opts, filenames = parser.parse_args()
if len(filenames) < 1:
parser.print_help()
return 1
for filename in filenames:
DiffAndConfirm(filename, opts.should_confirm, opts.perform_safety_checks,
opts.for_blink)
if __name__ == '__main__':
sys.exit(main())
|