| 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 |