Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(547)

Side by Side Diff: ppapi/generate_ppapi_size_checks.py

Issue 5703008: Revert 69187, http://codereview.chromium.org/5730003/. DEPS check failed due... (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src/
Patch Set: Created 10 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « no previous file | ppapi/tests/clang/Makefile » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 #!/usr/bin/python
2
3 # Copyright (c) 2010 The Chromium Authors. All rights reserved.
4 # Use of this source code is governed by a BSD-style license that can be
5 # found in the LICENSE file.
6
7 """This script should be run manually on occasion to make sure all PPAPI types
8 have appropriate size checking.
9
10 """
11
12 import optparse
13 import os
14 import subprocess
15 import sys
16
17
18 # The string that the PrintNamesAndSizes plugin uses to indicate whether or not
19 # a type has a pointer. Types that have pointers are not expected to have the
20 # same size on 32-bit and 64-bit architectures.
21 HAS_POINTER_STRING = "HasPointer"
22
23 # These are types that don't include a pointer but nonetheless have
24 # architecture-dependent size. They all are ultimately typedefs to 'long' or
25 # 'unsigned long'. If there get to be too many types that use 'long' or
26 # 'unsigned long', we can add detection of them to the plugin to automate this.
27 ARCH_DEPENDENT_TYPES = set(["khronos_intptr_t",
28 "khronos_uintptr_t",
29 "khronos_ssize_t",
30 "khronos_usize_t",
31 "GLintptr",
32 "GLsizeiptr"
33 ])
34
35
36
37 class SourceLocation:
38
39 """A class representing the source location of a definiton."""
40
41 def __init__(self, filename="", start_line=-1, end_line=-1):
42 self.filename = os.path.normpath(filename)
43 self.start_line = start_line
44 self.end_line = end_line
45
46
47
48 class TypeInfo:
49
50 """A class representing information about a C++ type."""
51
52 def __init__(self, info_string, target):
53 [self.kind, self.name, self.size, has_pointer_string, source_file,
54 start_line, end_line] = info_string.split(',')
55 self.target = target
56 self.parsed_line = info_string
57 # Note that Clang counts line numbers from 1, but we want to count from 0.
58 self.source_location = SourceLocation(source_file,
59 int(start_line)-1,
60 int(end_line)-1)
61 # If the type is one of our known special cases, mark it as architecture-
62 # dependent.
63 if self.name in ARCH_DEPENDENT_TYPES:
64 self.arch_dependent = True
65 # Otherwise, its size can be architecture dependent if it contains one or
66 # more pointers (or is a pointer).
67 else:
68 self.arch_dependent = (has_pointer_string == HAS_POINTER_STRING)
69 self.target = target
70 self.parsed_line = info_string
71
72
73
74 class FilePatch:
75
76 """A class representing a set of line-by-line changes to a particular file.
77 None
78 of the changes are applied until Apply is called. All line numbers are
79 counted from 0.
80 """
81
82 def __init__(self, filename):
83 self.filename = filename
84 self.linenums_to_delete = set()
85 # A dictionary from line number to an array of strings to be inserted at
86 # that line number.
87 self.lines_to_add = {}
88
89 def Delete(self, start_line, end_line):
90 """Make the patch delete the lines [start_line, end_line)."""
91 self.linenums_to_delete |= set(range(start_line, end_line))
92
93 def Add(self, text, line_number):
94 """Add the given text before the text on the given line number."""
95 if line_number in self.lines_to_add:
96 self.lines_to_add[line_number].append(text)
97 else:
98 self.lines_to_add[line_number] = [text]
99
100 def Apply(self):
101 """Apply the patch by writing it to self.filename"""
102 # Read the lines of the existing file in to a list.
103 sourcefile = open(self.filename, "r")
104 file_lines = sourcefile.readlines()
105 sourcefile.close()
106 # Now apply the patch. Our strategy is to keep the array at the same size,
107 # and just edit strings in the file_lines list as necessary. When we delete
108 # lines, we just blank the line and keep it in the list. When we add lines,
109 # we just prepend the added source code to the start of the existing line at
110 # that line number. This way, all the line numbers we cached from calls to
111 # Add and Delete remain valid list indices, and we don't have to worry about
112 # maintaining any offsets. Each element of file_lines at the end may
113 # contain any number of lines (0 or more) delimited by carriage returns.
114 for linenum_to_delete in self.linenums_to_delete:
115 file_lines[linenum_to_delete] = "";
116 for linenum, sourcelines in self.lines_to_add.items():
117 # Sort the lines we're adding so we get relatively consistent results.
118 sourcelines.sort()
119 # Prepend the new lines. When we output
120 file_lines[linenum] = "".join(sourcelines) + file_lines[linenum]
121 newsource = open(self.filename, "w")
122 for line in file_lines:
123 newsource.write(line)
124 newsource.close()
125
126
127 def CheckAndInsert(typeinfo, typeinfo_map):
128 """Check if a TypeInfo exists already in the given map with the same name. If
129 so, make sure the size is consistent.
130 - If the name exists but the sizes do not match, print a message and
131 exit with non-zero exit code.
132 - If the name exists and the sizes match, do nothing.
133 - If the name does not exist, insert the typeinfo in to the map.
134
135 """
136 # If the type is unnamed, ignore it.
137 if typeinfo.name == "":
138 return
139 # If the size is 0, ignore it.
140 elif int(typeinfo.size) == 0:
141 return
142 # If the type is not defined under ppapi, ignore it.
143 elif typeinfo.source_location.filename.find("ppapi") == -1:
144 return
145 # If the type is defined under GLES2, ignore it.
146 elif typeinfo.source_location.filename.find("GLES2") > -1:
147 return
148 # If the type is an interface (by convention, starts with PPP_ or PPB_),
149 # ignore it.
150 elif (typeinfo.name[:4] == "PPP_") or (typeinfo.name[:4] == "PPB_"):
151 return
152 elif typeinfo.name in typeinfo_map:
153 if typeinfo.size != typeinfo_map[typeinfo.name].size:
154 print "Error: '" + typeinfo.name + "' is", \
155 typeinfo_map[typeinfo.name].size, \
156 "bytes on target '" + typeinfo_map[typeinfo.name].target + \
157 "', but", typeinfo.size, "on target '" + typeinfo.target + "'"
158 print typeinfo_map[typeinfo.name].parsed_line
159 print typeinfo.parsed_line
160 sys.exit(1)
161 else:
162 # It's already in the map and the sizes match.
163 pass
164 else:
165 typeinfo_map[typeinfo.name] = typeinfo
166
167
168 def ProcessTarget(clang_command, target, arch_types, ind_types):
169 """Run clang using the given clang_command for the given target string. Parse
170 the output to create TypeInfos for each discovered type. Insert each type in
171 to the appropriate dictionary. For each type that has architecture-dependent
172 size, insert it in to arch_types. Types with independent size go in to
173 ind_types. If the type already exists in the appropriate map, make sure that
174 the size matches what's already in the map. If not, the script terminates
175 with an error message.
176 """
177 p = subprocess.Popen(clang_command + " -triple " + target,
178 shell=True,
179 stdout=subprocess.PIPE)
180 lines = p.communicate()[0].split()
181 for line in lines:
182 typeinfo = TypeInfo(line, target)
183 # Put types which have architecture-specific size in to arch_types. All
184 # other types are 'architecture-independent' and get put in ind_types.
185 # in the appropraite dictionary.
186 if typeinfo.arch_dependent:
187 CheckAndInsert(typeinfo, arch_types)
188 else:
189 CheckAndInsert(typeinfo, ind_types)
190
191
192 def ToAssertionCode(typeinfo):
193 """Convert the TypeInfo to an appropriate C compile assertion.
194 If it's a struct (Record in Clang terminology), we want a line like this:
195 PP_COMPILE_ASSERT_STRUCT_SIZE_IN_BYTES(<name>, <size>);\n
196 Enums:
197 PP_COMPILE_ASSERT_ENUM_SIZE_IN_BYTES(<name>, <size>);\n
198 Typedefs:
199 PP_COMPILE_ASSERT_SIZE_IN_BYTES(<name>, <size>);\n
200
201 """
202 line = "PP_COMPILE_ASSERT_"
203 if typeinfo.kind == "Enum":
204 line += "ENUM_"
205 elif typeinfo.kind == "Record":
206 line += "STRUCT_"
207 line += "SIZE_IN_BYTES("
208 line += typeinfo.name
209 line += ", "
210 line += typeinfo.size
211 line += ");\n"
212 return line
213
214
215 def IsMacroDefinedName(typename):
216 """Return true iff the given typename came from a PPAPI compile assertion."""
217 return typename.find("PP_Dummy_Struct_For_") == 0
218
219
220 COPYRIGHT_STRING_C = \
221 """/* Copyright (c) 2010 The Chromium Authors. All rights reserved.
222 * Use of this source code is governed by a BSD-style license that can be
223 * found in the LICENSE file.
224 *
225 * This file has compile assertions for the sizes of types that are dependent
226 * on the architecture for which they are compiled (i.e., 32-bit vs 64-bit).
227 */
228
229 """
230
231
232 def WriteArchSpecificCode(types, root, filename):
233 """Write a header file that contains a compile-time assertion for the size of
234 each of the given typeinfos, in to a file named filename rooted at root.
235 """
236 assertion_lines = [ToAssertionCode(typeinfo) for typeinfo in types]
237 assertion_lines.sort()
238 outfile = open(os.path.join(root, filename), "w")
239 header_guard = "PPAPI_TESTS_" + filename.upper().replace(".", "_") + "_"
240 outfile.write(COPYRIGHT_STRING_C)
241 outfile.write('#ifndef ' + header_guard + '\n')
242 outfile.write('#define ' + header_guard + '\n\n')
243 outfile.write('#include "ppapi/tests/test_struct_sizes.c"\n\n')
244 for line in assertion_lines:
245 outfile.write(line)
246 outfile.write('\n#endif /* ' + header_guard + ' */\n')
247
248
249 def main(argv):
250 parser = optparse.OptionParser()
251 parser.add_option(
252 '-c', '--clang-path', dest='clang_path',
253 default=(''),
254 help='the path to the clang binary (default is to get it from your path)')
255 parser.add_option(
256 '-p', '--plugin', dest='plugin',
257 default='tests/clang/libPrintNamesAndSizes.so',
258 help='The path to the PrintNamesAndSizes plugin library.')
259 parser.add_option(
260 '--targets32', dest='targets32',
261 default='i386-pc-linux,arm-pc-linux,i386-pc-win32',
262 help='Which 32-bit target triples to provide to clang.')
263 parser.add_option(
264 '--targets64', dest='targets64',
265 default='x86_64-pc-linux,x86_64-pc-win',
266 help='Which 32-bit target triples to provide to clang.')
267 parser.add_option(
268 '-r', '--ppapi-root', dest='ppapi_root',
269 default='.',
270 help='The root directory of ppapi.')
271 options, args = parser.parse_args(argv)
272 if args:
273 parser.print_help()
274 print 'ERROR: invalid argument'
275 sys.exit(1)
276
277 clang_executable = os.path.join(options.clang_path, 'clang')
278 clang_command = clang_executable + " -cc1" \
279 + " -load " + options.plugin \
280 + " -plugin PrintNamesAndSizes" \
281 + " -I" + os.path.join(options.ppapi_root, "..") \
282 + " " \
283 + os.path.join(options.ppapi_root, "tests", "test_struct_sizes.c")
284
285 # Dictionaries mapping type names to TypeInfo objects.
286 # Types that have size dependent on architecture, for 32-bit
287 types32 = {}
288 # Types that have size dependent on architecture, for 64-bit
289 types64 = {}
290 # Note that types32 and types64 should contain the same types, but with
291 # different sizes.
292
293 # Types whose size should be consistent regardless of architecture.
294 types_independent = {}
295
296 # Now run clang for each target. Along the way, make sure architecture-
297 # dependent types are consistent sizes on all 32-bit platforms and consistent
298 # on all 64-bit platforms. Any types in 'types_independent' are checked for
299 # all targets to make sure their size is consistent across all of them.
300 targets32 = options.targets32.split(',');
301 for target in targets32:
302 ProcessTarget(clang_command, target, types32, types_independent)
303 targets64 = options.targets64.split(',');
304 for target in targets64:
305 ProcessTarget(clang_command, target, types64, types_independent)
306
307 # This dictionary maps file names to FilePatch objects.
308 file_patches = {}
309
310 # Find locations of existing macros, and just delete them all.
311 for name, typeinfo in \
312 types_independent.items() + types32.items() + types64.items():
313 if IsMacroDefinedName(name):
314 sourcefile = typeinfo.source_location.filename
315 if sourcefile not in file_patches:
316 file_patches[sourcefile] = FilePatch(sourcefile)
317 file_patches[sourcefile].Delete(typeinfo.source_location.start_line,
318 typeinfo.source_location.end_line+1)
319
320 # Add a compile-time assertion for each type whose size is independent of
321 # architecture. These assertions go immediately after the class definition.
322 for name, typeinfo in types_independent.items():
323 # Ignore macros and types that are 0 bytes (i.e., typedefs to void)
324 if not IsMacroDefinedName(name) and typeinfo.size > 0:
325 sourcefile = typeinfo.source_location.filename
326 if sourcefile not in file_patches:
327 file_patches[sourcefile] = FilePatch(sourcefile)
328 # Add the assertion code just after the definition of the type.
329 file_patches[sourcefile].Add(ToAssertionCode(typeinfo),
330 typeinfo.source_location.end_line+1)
331
332 for filename, patch in file_patches.items():
333 patch.Apply()
334
335 c_source_root = os.path.join(options.ppapi_root, "tests")
336 WriteArchSpecificCode(types32.values(),
337 c_source_root,
338 "arch_dependent_sizes_32.h")
339 WriteArchSpecificCode(types64.values(),
340 c_source_root,
341 "arch_dependent_sizes_64.h")
342
343 return 0
344
345
346 if __name__ == '__main__':
347 sys.exit(main(sys.argv[1:]))
348
OLDNEW
« no previous file with comments | « no previous file | ppapi/tests/clang/Makefile » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698