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.
41 for prefix
in ('#include ', '#import '):
42 if line
.startswith(prefix
):
43 line
= line
[len(prefix
):]
46 # The win32 api has all sorts of implicit include order dependencies :-/
47 # Give a few headers special sort keys that make sure they appear before all
49 if line
.startswith('<windows.h>'): # Must be before e.g. shellapi.h
51 if line
.startswith('<unknwn.h>'): # Must be before e.g. intshcut.h
54 # C++ system headers should come after C system headers.
55 if line
.startswith('<'):
56 if line
.find('.h>') != -1:
65 """Returns True if the line is an #include/#import line."""
66 return line
.startswith('#include ') or line
.startswith('#import ')
69 def SortHeader(infile
, outfile
):
70 """Sorts the headers in infile, writing the sorted file to outfile."""
74 while IsInclude(line
):
75 headerblock
.append(line
)
77 for header
in sorted(headerblock
, key
=IncludeCompareKey
):
79 # Intentionally fall through, to write the line that caused
80 # the above while loop to exit.
84 def DiffAndConfirm(filename
, should_confirm
):
85 """Shows a diff of what the tool would change the file named
86 filename to. Shows a confirmation prompt if should_confirm is true.
87 Saves the resulting file if should_confirm is false or the user
88 answers Y to the confirmation prompt.
90 fixfilename
= filename
+ '.new'
91 infile
= open(filename
, 'r')
92 outfile
= open(fixfilename
, 'w')
93 SortHeader(infile
, outfile
)
95 outfile
.close() # Important so the below diff gets the updated contents.
98 diff
= os
.system('diff -u %s %s' % (filename
, fixfilename
))
99 if diff
>> 8 == 0: # Check exit code.
100 print '%s: no change' % filename
103 if not should_confirm
or YesNo('Use new file (y/N)?'):
104 os
.rename(fixfilename
, filename
)
107 os
.remove(fixfilename
)
109 # If the file isn't there, we don't care.
114 parser
= optparse
.OptionParser(usage
='%prog filename1 filename2 ...')
115 parser
.add_option('-f', '--force', action
='store_false', default
=True,
116 dest
='should_confirm',
117 help='Turn off confirmation prompt.')
118 opts
, filenames
= parser
.parse_args()
120 if len(filenames
) < 1:
124 for filename
in filenames
:
125 DiffAndConfirm(filename
, opts
.should_confirm
)
128 if __name__
== '__main__':