2 # Copyright (c) 2012 The Chromium Authors. All rights reserved.
3 # Use of this source code is governed by a BSD-style license that can be
4 # found in the LICENSE file.
6 """Given a filename as an argument, sort the #include/#imports in that file.
8 Shows a diff and prompts for confirmation before doing the deed.
9 Works great with tools/git/for-all-touched-files.py.
20 """Prompts with a yes/no question, returns True if yes."""
23 # http://code.activestate.com/recipes/134892/
24 fd
= sys
.stdin
.fileno()
25 old_settings
= termios
.tcgetattr(fd
)
28 tty
.setraw(sys
.stdin
.fileno())
29 ch
= sys
.stdin
.read(1)
31 termios
.tcsetattr(fd
, termios
.TCSADRAIN
, old_settings
)
33 return ch
in ('Y', 'y')
36 def IncludeCompareKey(line
):
37 """Sorting comparator key used for comparing two #include lines.
38 Returns the filename without the #include/#import prefix.
40 for prefix
in ('#include ', '#import '):
41 if line
.startswith(prefix
):
42 line
= line
[len(prefix
):]
45 # The win32 api has all sorts of implicit include order dependencies :-/
46 # Give a few headers special sort keys that make sure they appear before all
48 if line
.startswith('<windows.h>'): # Must be before e.g. shellapi.h
50 if line
.startswith('<unknwn.h>'): # Must be before e.g. intshcut.h
53 # C++ system headers should come after C system headers.
54 if line
.startswith('<'):
55 if line
.find('.h>') != -1:
64 """Returns True if the line is an #include/#import line."""
65 return line
.startswith('#include ') or line
.startswith('#import ')
68 def SortHeader(infile
, outfile
):
69 """Sorts the headers in infile, writing the sorted file to outfile."""
73 while IsInclude(line
):
74 headerblock
.append(line
)
76 for header
in sorted(headerblock
, key
=IncludeCompareKey
):
78 # Intentionally fall through, to write the line that caused
79 # the above while loop to exit.
83 def DiffAndConfirm(filename
, should_confirm
):
84 """Shows a diff of what the tool would change the file named
85 filename to. Shows a confirmation prompt if should_confirm is true.
86 Saves the resulting file if should_confirm is false or the user
87 answers Y to the confirmation prompt.
89 fixfilename
= filename
+ '.new'
90 infile
= open(filename
, 'r')
91 outfile
= open(fixfilename
, 'w')
92 SortHeader(infile
, outfile
)
94 outfile
.close() # Important so the below diff gets the updated contents.
97 diff
= os
.system('diff -u %s %s' % (filename
, fixfilename
))
98 if diff
>> 8 == 0: # Check exit code.
99 print '%s: no change' % filename
102 if not should_confirm
or YesNo('Use new file (y/N)?'):
103 os
.rename(fixfilename
, filename
)
106 os
.remove(fixfilename
)
108 # If the file isn't there, we don't care.
113 parser
= optparse
.OptionParser(usage
='%prog filename1 filename2 ...')
114 parser
.add_option('-f', '--force', action
='store_false', default
=True,
115 dest
='should_confirm',
116 help='Turn off confirmation prompt.')
117 opts
, filenames
= parser
.parse_args()
119 if len(filenames
) < 1:
123 for filename
in filenames
:
124 DiffAndConfirm(filename
, opts
.should_confirm
)
127 if __name__
== '__main__':