OLD | NEW |
---|---|
1 #!/usr/bin/env python | 1 #!/usr/bin/env python |
2 # Copyright 2015 The Chromium Authors. All rights reserved. | 2 # Copyright 2015 The Chromium Authors. All rights reserved. |
3 # Use of this source code is governed by a BSD-style license that can be | 3 # Use of this source code is governed by a BSD-style license that can be |
4 # found in the LICENSE file. | 4 # found in the LICENSE file. |
5 | 5 |
6 """Given a GYP/GN filename, sort C-ish source files in that file. | 6 """Given a GYP/GN filename, sort C-ish source files in that file. |
7 | 7 |
8 Shows a diff and prompts for confirmation before doing the deed. | 8 Shows a diff and prompts for confirmation before doing the deed. |
9 Works great with tools/git/for-all-touched-files.py. | 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. | |
Daniel Erat
2015/02/19 14:05:17
hmm, it may not be possible to always do this. i t
satorux1
2015/02/20 00:14:29
It should be OK. Here's from ash/BUILD.gn:
#
| |
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. | |
Daniel Erat
2015/02/19 14:05:17
maybe you should say that the fix is to not check
satorux1
2015/02/20 00:14:29
good point. added.
| |
72 | |
73 3) " and ' are used both used in the same source list (GYP only problem) | |
74 | |
75 If both " and ' are used in the same source list, sources quoted with " will | |
76 appear first in the output. The problem is rare enough so the tool does not | |
77 attempt to normalize them. Hence this kind of errors should be fixed | |
78 manually. | |
79 | |
80 4) Spaces and tabs used in the same source list | |
81 | |
82 Similarly, if spaces and tabs are both used in the same source list, sources | |
83 indented with tabs will appear first in the output. This kind of errors | |
84 should be fixed manually. | |
10 """ | 85 """ |
11 | 86 |
12 import difflib | 87 import difflib |
13 import optparse | 88 import optparse |
14 import re | 89 import re |
15 import sys | 90 import sys |
16 | 91 |
17 from yes_no import YesNo | 92 from yes_no import YesNo |
18 | 93 |
19 SUFFIXES = ['c', 'cc', 'cpp', 'h', 'mm', 'rc', 'rc.version', 'ico', 'def', | 94 SUFFIXES = ['c', 'cc', 'cpp', 'h', 'mm', 'rc', 'rc.version', 'ico', 'def', |
20 'release'] | 95 'release'] |
21 SOURCE_PATTERN = re.compile('^\s+[\'"].*\.(%s)[\'"],$' % | 96 SOURCE_PATTERN = re.compile(r'^\s+[\'"].*\.(%s)[\'"],$' % |
22 '|'.join([re.escape(x) for x in SUFFIXES])) | 97 '|'.join([re.escape(x) for x in SUFFIXES])) |
23 COMMENT_PATTERN = re.compile('^\s+#') | 98 COMMENT_PATTERN = re.compile(r'^\s+#') |
99 | |
24 | 100 |
25 def SortSources(original_lines): | 101 def SortSources(original_lines): |
26 """Sort source file names in |original_lines|. | 102 """Sort source file names in |original_lines|. |
27 | 103 |
28 Args: | 104 Args: |
29 original_lines: Lines of the original content as a list of strings. | 105 original_lines: Lines of the original content as a list of strings. |
30 | 106 |
31 Returns: | 107 Returns: |
32 Lines of the sorted content as a list of strings. | 108 Lines of the sorted content as a list of strings. |
33 | 109 |
34 The algorithm is fairly naive. The code tries to find a list of C-ish | 110 The algorithm is fairly naive. The code tries to find a list of C-ish |
35 source file names by a simple regex, then sort them. The code does not try | 111 source file names by a simple regex, then sort them. The code does not try |
36 to understand the syntax of the build files, hence there are some cases | 112 to understand the syntax of the build files. See the file comment above for |
37 that the code cannot handle correctly (ex. blank lines within a list of | 113 details. |
38 source file names). | |
39 """ | 114 """ |
40 | 115 |
41 output_lines = [] | 116 output_lines = [] |
42 comments = [] | 117 comments = [] |
43 sources = [] | 118 sources = [] |
44 for line in original_lines: | 119 for line in original_lines: |
45 if re.search(COMMENT_PATTERN, line): | 120 if re.search(COMMENT_PATTERN, line): |
46 comments.append(line) | 121 comments.append(line) |
47 elif re.search(SOURCE_PATTERN, line): | 122 elif re.search(SOURCE_PATTERN, line): |
48 # Associate the line with the preceeding comments. | 123 # Associate the line with the preceding comments. |
49 sources.append([line, comments]) | 124 sources.append([line, comments]) |
50 comments = [] | 125 comments = [] |
51 else: | 126 else: |
52 # |sources| should be flushed first, to handle comments at the end of a | 127 # |sources| should be flushed first, to handle comments at the end of a |
53 # source list correctly. | 128 # source list correctly. |
54 if sources: | 129 if sources: |
55 for source_line, source_comments in sorted(sources): | 130 for source_line, source_comments in sorted(sources): |
56 output_lines.extend(source_comments) | 131 output_lines.extend(source_comments) |
57 output_lines.append(source_line) | 132 output_lines.append(source_line) |
58 sources = [] | 133 sources = [] |
(...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
101 if len(filenames) < 1: | 176 if len(filenames) < 1: |
102 parser.print_help() | 177 parser.print_help() |
103 return 1 | 178 return 1 |
104 | 179 |
105 for filename in filenames: | 180 for filename in filenames: |
106 ProcessFile(filename, opts.should_confirm) | 181 ProcessFile(filename, opts.should_confirm) |
107 | 182 |
108 | 183 |
109 if __name__ == '__main__': | 184 if __name__ == '__main__': |
110 sys.exit(main()) | 185 sys.exit(main()) |
OLD | NEW |