OLD | NEW |
1 #!/usr/bin/env python | 1 #!/usr/bin/env python |
2 # Copyright (C) 2012 Google Inc. All rights reserved. | 2 # Copyright (C) 2012 Google Inc. All rights reserved. |
3 # | 3 # |
4 # Redistribution and use in source and binary forms, with or without | 4 # Redistribution and use in source and binary forms, with or without |
5 # modification, are permitted provided that the following conditions are | 5 # modification, are permitted provided that the following conditions are |
6 # met: | 6 # met: |
7 # | 7 # |
8 # * Redistributions of source code must retain the above copyright | 8 # * Redistributions of source code must retain the above copyright |
9 # notice, this list of conditions and the following disclaimer. | 9 # notice, this list of conditions and the following disclaimer. |
10 # * Redistributions in binary form must reproduce the above | 10 # * Redistributions in binary form must reproduce the above |
(...skipping 16 matching lines...) Expand all Loading... |
27 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | 27 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
28 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | 28 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
29 | 29 |
30 # Usage: make-file-arrays.py [--condition=condition-string] --out-h=<header-file
-name> --out-cpp=<cpp-file-name> <input-file>... | 30 # Usage: make-file-arrays.py [--condition=condition-string] --out-h=<header-file
-name> --out-cpp=<cpp-file-name> <input-file>... |
31 | 31 |
32 import os.path | 32 import os.path |
33 import re | 33 import re |
34 import sys | 34 import sys |
35 from optparse import OptionParser | 35 from optparse import OptionParser |
36 | 36 |
| 37 # Should be spam in the build log? |
| 38 DEBUG_OUTPUT = False |
37 | 39 |
38 def make_variable_name_and_read(file_name): | 40 def make_variable_name_and_read(file_name): |
39 result = re.match(r'([\w\d_]+)\.([\w\d_]+)', os.path.basename(file_name)) | 41 result = re.match(r'([\w\d_]+)\.([\w\d_]+)', os.path.basename(file_name)) |
40 if not result: | 42 if not result: |
41 print 'Invalid input file name:', os.path.basename(file_name) | 43 print 'Invalid input file name:', os.path.basename(file_name) |
42 sys.exit(1) | 44 sys.exit(1) |
43 variable_name = result.group(1)[0].lower() + result.group(1)[1:] + result.gr
oup(2).capitalize() | 45 variable_name = result.group(1)[0].lower() + result.group(1)[1:] + result.gr
oup(2).capitalize() |
44 with open(file_name, 'rb') as f: | 46 with open(file_name, 'rb') as f: |
45 content = f.read() | 47 content = f.read() |
46 return variable_name, content | 48 return variable_name, content |
47 | 49 |
48 | 50 |
| 51 def is_identifier_char(c): |
| 52 assert len(c) == 1 |
| 53 return c.isalnum() or c in "$_" |
| 54 |
| 55 |
| 56 def strip_js_whitespace_not_in_strings(content): |
| 57 """Removes whitespace we don't need in this javascript |
| 58 program. Whitespace is relevant inside strings and regexps (not |
| 59 supported) and sometimes new-lines are needed to trigger the |
| 60 automatic semi-colon insertion. |
| 61 |
| 62 This is _slightly_ worse than Closure at stripping whitespace |
| 63 but so close that there is no urgent reason to improve it.""" |
| 64 |
| 65 out = "" |
| 66 |
| 67 quoted_char = False # When True the next char is to be output as is. |
| 68 string_delimiter = None # When set we're inside a string. |
| 69 lazy_space = False |
| 70 lazy_new_line = False |
| 71 for c in content: |
| 72 if quoted_char: |
| 73 quoted_char = False |
| 74 out += c |
| 75 elif string_delimiter: |
| 76 out += c |
| 77 if c == "\\": |
| 78 quoted_char = True |
| 79 elif c == string_delimiter: |
| 80 string_delimiter = None |
| 81 else: |
| 82 if c == "\\": |
| 83 quoted_char = True |
| 84 elif c == '"' or c == "'": |
| 85 string_delimiter = c |
| 86 |
| 87 if c in " \t\n\r\f": |
| 88 lazy_space = True |
| 89 if c == "\n": |
| 90 lazy_new_line = True |
| 91 else: |
| 92 if lazy_space and out: |
| 93 output_space = False |
| 94 if is_identifier_char(out[-1]) and is_identifier_char(c): |
| 95 # Will prevent "var i" -> "vari" or |
| 96 # "function $" -> "function$" |
| 97 output_space = True |
| 98 elif out[-1] == c and c not in "{}()[]": |
| 99 # Will prevent "a - -b" -> "a--b" or "a--\n-b" -> "a---b
" |
| 100 output_space = True |
| 101 if output_space: |
| 102 if lazy_new_line: |
| 103 out += "\n" |
| 104 else: |
| 105 out += " " |
| 106 lazy_space = False |
| 107 lazy_new_line = False |
| 108 out += c |
| 109 return out |
| 110 |
| 111 # Quick self testing. If this fails, we better not proceed with the build. |
| 112 assert strip_js_whitespace_not_in_strings(' var a = \' \\" \\" \' ;') == 'va
r a=\' \\" \\" \';' |
| 113 |
| 114 assert strip_js_whitespace_not_in_strings("a-- - \t--b") == "a-- - --b" |
| 115 |
| 116 assert strip_js_whitespace_not_in_strings("if (a == $) {\n}") == "if(a==$){}" |
| 117 assert strip_js_whitespace_not_in_strings("var \n a") == "var\na" |
| 118 assert strip_js_whitespace_not_in_strings("{ { a( (1+ 2) / 3);}\n} ") == "{{a((
1+2)/3);}}" |
| 119 assert strip_js_whitespace_not_in_strings(r" ' \' ' ") == r"' \' '" |
| 120 |
49 def strip_whitespace_and_comments(file_name, content): | 121 def strip_whitespace_and_comments(file_name, content): |
50 result = re.match(r'.*\.([^.]+)', file_name) | 122 result = re.match(r'.*\.([^.]+)', file_name) |
51 if not result: | 123 if not result: |
52 print 'The file name has no extension:', file_name | 124 print 'The file name has no extension:', file_name |
53 sys.exit(1) | 125 sys.exit(1) |
54 extension = result.group(1).lower() | 126 extension = result.group(1).lower() |
55 multi_line_comment = re.compile(r'/\*.*?\*/', re.MULTILINE | re.DOTALL) | 127 multi_line_comment = re.compile(r'/\*.*?\*/', re.MULTILINE | re.DOTALL) |
56 single_line_comment = re.compile(r'^//.*$', re.MULTILINE) | 128 single_line_comment = re.compile(r'^//.*$', re.MULTILINE) |
57 # Don't accidentally match URLs (http://...) | 129 # Don't accidentally match URLs (http://...) |
58 trailing_comment = re.compile(r'([^:])//.*$', re.MULTILINE) | 130 trailing_comment = re.compile(r'([^:])//.*$', re.MULTILINE) |
59 repeating_space = re.compile(r'[ \t]+', re.MULTILINE) | 131 repeating_space = re.compile(r'[ \t]+', re.MULTILINE) |
60 leading_space = re.compile(r'^[ \t]+', re.MULTILINE) | 132 leading_space = re.compile(r'^[ \t]+', re.MULTILINE) |
61 trailing_space = re.compile(r'[ \t]+$', re.MULTILINE) | 133 trailing_space = re.compile(r'[ \t]+$', re.MULTILINE) |
62 empty_line = re.compile(r'\n+') | 134 empty_line = re.compile(r'\n+') |
| 135 orig_content = content |
63 if extension == 'js': | 136 if extension == 'js': |
| 137 content = orig_content |
64 content = multi_line_comment.sub('', content) | 138 content = multi_line_comment.sub('', content) |
65 content = single_line_comment.sub('', content) | 139 content = single_line_comment.sub('', content) |
66 content = trailing_comment.sub(r'\1', content) | 140 content = trailing_comment.sub(r'\1', content) |
67 content = repeating_space.sub(' ', content) | 141 content = strip_js_whitespace_not_in_strings(content) |
68 content = leading_space.sub('', content) | 142 optimized_content_content = content |
69 content = trailing_space.sub('', content) | 143 content = orig_content |
70 content = empty_line.sub('\n', content) | 144 if len(optimized_content_content) < len(content): |
| 145 content = optimized_content_content |
71 elif extension == 'css': | 146 elif extension == 'css': |
72 content = multi_line_comment.sub('', content) | 147 content = multi_line_comment.sub('', content) |
73 content = repeating_space.sub(' ', content) | 148 content = repeating_space.sub(' ', content) |
74 content = leading_space.sub('', content) | 149 content = leading_space.sub('', content) |
75 content = trailing_space.sub('', content) | 150 content = trailing_space.sub('', content) |
76 content = empty_line.sub('\n', content) | 151 content = empty_line.sub('\n', content) |
| 152 if DEBUG_OUTPUT: |
| 153 in_size = len(orig_content) |
| 154 out_size = len(content) |
| 155 if out_size < in_size: |
| 156 print("%s: Compaction saved %.1f%% for a total of %d bytes." % (file
_name, float(100 * (in_size - out_size)) / in_size, in_size - out_size)) |
77 return content | 157 return content |
78 | 158 |
79 | 159 |
80 def process_file(file_name): | 160 def process_file(file_name): |
81 variable_name, content = make_variable_name_and_read(file_name) | 161 variable_name, content = make_variable_name_and_read(file_name) |
82 content = strip_whitespace_and_comments(file_name, content) | 162 content = strip_whitespace_and_comments(file_name, content) |
83 size = len(content) | 163 size = len(content) |
| 164 if DEBUG_OUTPUT: |
| 165 out_debug_name = file_name + ".generated" |
| 166 with open(out_debug_name, "w") as out_debug_file: |
| 167 out_debug_file.write(content) |
84 return variable_name, content | 168 return variable_name, content |
85 | 169 |
86 | 170 |
87 def write_header_file(header_file_name, flag, names_and_contents, namespace='bli
nk'): | 171 def write_header_file(header_file_name, flag, names_and_contents, namespace='bli
nk'): |
88 with open(header_file_name, 'w') as header_file: | 172 with open(header_file_name, 'w') as header_file: |
89 if flag: | 173 if flag: |
90 header_file.write('#if ' + flag + '\n') | 174 header_file.write('#if ' + flag + '\n') |
91 header_file.write('namespace %s {\n' % namespace) | 175 header_file.write('namespace %s {\n' % namespace) |
92 for variable_name, content in names_and_contents: | 176 for variable_name, content in names_and_contents: |
93 size = len(content) | 177 size = len(content) |
(...skipping 53 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
147 | 231 |
148 names_and_contents = [process_file(file_name) for file_name in args] | 232 names_and_contents = [process_file(file_name) for file_name in args] |
149 | 233 |
150 if options.out_header: | 234 if options.out_header: |
151 write_header_file(options.out_header, options.flag, names_and_contents) | 235 write_header_file(options.out_header, options.flag, names_and_contents) |
152 write_cpp_file(options.out_cpp, options.flag, names_and_contents, options.ou
t_header) | 236 write_cpp_file(options.out_cpp, options.flag, names_and_contents, options.ou
t_header) |
153 | 237 |
154 | 238 |
155 if __name__ == '__main__': | 239 if __name__ == '__main__': |
156 sys.exit(main()) | 240 sys.exit(main()) |
OLD | NEW |