Fix FullScreen crash in Webapp
[platform/framework/web/chromium-efl.git] / tools / sort_sources.py
1 #!/usr/bin/env python
2 # Copyright 2015 The Chromium Authors
3 # Use of this source code is governed by a BSD-style license that can be
4 # found in the LICENSE file.
5
6 """Given a GYP/GN filename, sort C-ish source files in that file.
7
8 Shows a diff and prompts for confirmation before doing the deed.
9 Works great with tools/git/for-all-touched-files.py.
10
11 Limitations:
12
13 1) Comments used as section headers
14
15 If a comment (1+ lines starting with #) appears in a source list without a
16 preceding blank line, the tool assumes that the comment is about the next
17 line. For example, given the following source list,
18
19   sources = [
20     "b.cc",
21     # Comment.
22     "a.cc",
23     "c.cc",
24   ]
25
26 the tool will produce the following output:
27
28   sources = [
29     # Comment.
30     "a.cc",
31     "b.cc",
32     "c.cc",
33   ]
34
35 This is not correct if the comment is for starting a new section like:
36
37   sources = [
38     "b.cc",
39     # These are for Linux.
40     "a.cc",
41     "c.cc",
42   ]
43
44 The tool cannot disambiguate the two types of comments. The problem can be
45 worked around by inserting a blank line before the comment because the tool
46 interprets a blank line as the end of a source list.
47
48 2) Sources commented out
49
50 Sometimes sources are commented out with their positions kept in the
51 alphabetical order, but what if the list is not sorted correctly? For
52 example, given the following source list,
53
54   sources = [
55     "a.cc",
56     # "b.cc",
57     "d.cc",
58     "c.cc",
59   ]
60
61 the tool will produce the following output:
62
63   sources = [
64     "a.cc",
65     "c.cc",
66     # "b.cc",
67     "d.cc",
68   ]
69
70 This is because the tool assumes that the comment (# "b.cc",) is about the
71 next line ("d.cc",). This kind of errors should be fixed manually, or the
72 commented-out code should be deleted.
73
74 3) " and ' are used both used in the same source list (GYP only problem)
75
76 If both " and ' are used in the same source list, sources quoted with " will
77 appear first in the output. The problem is rare enough so the tool does not
78 attempt to normalize them. Hence this kind of errors should be fixed
79 manually.
80
81 4) Spaces and tabs used in the same source list
82
83 Similarly, if spaces and tabs are both used in the same source list, sources
84 indented with tabs will appear first in the output. This kind of errors
85 should be fixed manually.
86
87 """
88
89 from __future__ import print_function
90
91 import difflib
92 import optparse
93 import re
94 import sys
95
96 from yes_no import YesNo
97
98 SUFFIXES = ['c', 'cc', 'cpp', 'h', 'mm', 'rc', 'rc.version', 'ico', 'def',
99             'release']
100 SOURCE_PATTERN = re.compile(r'^\s+[\'"].*\.(%s)[\'"],$' %
101                             '|'.join([re.escape(x) for x in SUFFIXES]))
102 COMMENT_PATTERN = re.compile(r'^\s+#')
103
104
105 def SortSources(original_lines):
106   """Sort source file names in |original_lines|.
107
108   Args:
109     original_lines: Lines of the original content as a list of strings.
110
111   Returns:
112     Lines of the sorted content as a list of strings.
113
114   The algorithm is fairly naive. The code tries to find a list of C-ish
115   source file names by a simple regex, then sort them. The code does not try
116   to understand the syntax of the build files. See the file comment above for
117   details.
118   """
119
120   output_lines = []
121   comments = []
122   sources = []
123   for line in original_lines:
124     if re.search(COMMENT_PATTERN, line):
125       comments.append(line)
126     elif re.search(SOURCE_PATTERN, line):
127       # Associate the line with the preceding comments.
128       sources.append([line, comments])
129       comments = []
130     else:
131       # |sources| should be flushed first, to handle comments at the end of a
132       # source list correctly.
133       if sources:
134         for source_line, source_comments in sorted(sources):
135           output_lines.extend(source_comments)
136           output_lines.append(source_line)
137         sources = []
138       if comments:
139         output_lines.extend(comments)
140         comments = []
141       output_lines.append(line)
142   return output_lines
143
144
145 def ProcessFile(filename, should_confirm):
146   """Process the input file and rewrite if needed.
147
148   Args:
149     filename: Path to the input file.
150     should_confirm: If true, diff and confirmation prompt are shown.
151   """
152
153   original_lines = []
154   with open(filename, 'r') as input_file:
155     for line in input_file:
156       original_lines.append(line)
157
158   new_lines = SortSources(original_lines)
159   if original_lines == new_lines:
160     print('%s: no change' % filename)
161     return
162
163   if should_confirm:
164     diff = difflib.unified_diff(original_lines, new_lines)
165     sys.stdout.writelines(diff)
166     if not YesNo('Use new file (y/N)'):
167       return
168
169   with open(filename, 'w') as output_file:
170     output_file.writelines(new_lines)
171
172
173 def main():
174   parser = optparse.OptionParser(usage='%prog filename1 filename2 ...')
175   parser.add_option('-f', '--force', action='store_false', default=True,
176                     dest='should_confirm',
177                     help='Turn off confirmation prompt.')
178   opts, filenames = parser.parse_args()
179
180   if len(filenames) < 1:
181     parser.print_help()
182     return 1
183
184   for filename in filenames:
185     ProcessFile(filename, opts.should_confirm)
186
187
188 if __name__ == '__main__':
189   sys.exit(main())