OLD | NEW |
| (Empty) |
1 #!/usr/bin/python2.4 | |
2 # Copyright (c) 2011 The Native Client Authors. All rights reserved. | |
3 # Use of this source code is governed by a BSD-style license that can be | |
4 # found in the LICENSE file. | |
5 # | |
6 # Redistribution and use in source and binary forms, with or without | |
7 # modification, are permitted provided that the following conditions are | |
8 # met: | |
9 # | |
10 # * Redistributions of source code must retain the above copyright | |
11 # notice, this list of conditions and the following disclaimer. | |
12 # * Redistributions in binary form must reproduce the above | |
13 # copyright notice, this list of conditions and the following disclaimer | |
14 # in the documentation and/or other materials provided with the | |
15 # distribution. | |
16 # * Neither the name of Google Inc. nor the names of its | |
17 # contributors may be used to endorse or promote products derived from | |
18 # this software without specific prior written permission. | |
19 # | |
20 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |
21 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |
22 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | |
23 # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | |
24 # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | |
25 # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | |
26 # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | |
27 # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | |
28 # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
29 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | |
30 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
31 | |
32 """ Generates C header/source files for an enumerated type. | |
33 | |
34 Usage: python enum_gen.py <enum_spec>.enum | |
35 """ | |
36 | |
37 import getopt | |
38 import logging | |
39 import os | |
40 import re | |
41 import sys | |
42 | |
43 logging.getLogger().setLevel(logging.INFO) | |
44 | |
45 def _Usage(): | |
46 print >>sys.stderr, 'Usage: enum_gen [options] <enum_spec>.enum' | |
47 | |
48 # Command line options, and thier default values. | |
49 _ClOptions = { | |
50 # Header file to generate - default is <enum_spec>.h | |
51 'header': None, | |
52 # Source file to generate - default is <enum_spec>.c | |
53 'source': None, | |
54 # Name of enumerated type - default is <enum_spec> | |
55 'name': None, | |
56 # Prefix to remove print print names - default is None | |
57 'name_prefix': None, | |
58 # Path prefix to remove from descriptions of generated source. | |
59 'path_prefix': None, | |
60 # Sort enumerated constants | |
61 'sort': 0, | |
62 # Add a "EnumSize" constant at the end of the list. | |
63 'add_size': 0 | |
64 } | |
65 | |
66 # Pattern expected for enumerated values to generate. | |
67 _filename_form = re.compile(r'^(.*)\.enum$') | |
68 | |
69 # Verify that the enumeration filename matches expectations. | |
70 def _ValidateFilename(filename): | |
71 if not _filename_form.match(filename): | |
72 logging.error("Enum argument of wrong form: %s", filename) | |
73 sys.exit(1) | |
74 | |
75 # Given the enumeration filename, return the corresponding header | |
76 # file that contains the enumeration and the name function declaration. | |
77 def _GetHeaderFilename(filename): | |
78 # Use command line specification if defined. | |
79 header = _ClOptions['header'] | |
80 if header is None: | |
81 # Replace the .enum suffix of with .h | |
82 for prefix in _filename_form.findall(filename): | |
83 return prefix + ".h" | |
84 # This should not be reached. | |
85 logging.error("Unable to generate header file name: %s", filename) | |
86 sys.exit(1) | |
87 else: | |
88 return header | |
89 | |
90 # Given the enumeration filename, return the name of the enumerated type | |
91 # being defined. | |
92 def _GetEnumName(filename): | |
93 # Use the command line specification if defined. | |
94 name = _ClOptions['name'] | |
95 if name is None: | |
96 # Use the filename without the suffix. | |
97 for prefix in _filename_form.findall(filename): | |
98 return prefix | |
99 # This should not be reached. | |
100 logging.error("Unable to generate enum name: %s", filename) | |
101 sys.exit(1) | |
102 else: | |
103 return name | |
104 | |
105 # Generate the enumeration constant name to use for the constant | |
106 # specified in the enumeration file. | |
107 def _GetPrintName(constant): | |
108 # See if the command line specifies a prefix. If so, add it to the | |
109 # constant name. | |
110 prefix = _ClOptions['name_prefix'] | |
111 if prefix is None: | |
112 return constant | |
113 else: | |
114 return prefix + constant | |
115 | |
116 # Given the enumeration filename, return the name of the file containing | |
117 # the implementation of the enumeration. | |
118 def _GetSourceFilename(filename): | |
119 # Use the comand line specification if defined. | |
120 source = _ClOptions['source'] | |
121 if source is None: | |
122 # Replace the .enum suffix with .c | |
123 for prefix in _filename_form.findall(filename): | |
124 return prefix + ".c" | |
125 # This should not be reached. | |
126 logging.error("Unable to generate source file name: %s", filename) | |
127 sys.exit(1) | |
128 else: | |
129 return source | |
130 | |
131 # Given a filename, remove the path_prefix if possible. | |
132 def _GetSimplifiedFilename(filename): | |
133 # Use the path prefix specified on the command line. | |
134 path_prefix = _ClOptions['path_prefix'] | |
135 if path_prefix is not None: | |
136 if filename.startswith(path_prefix): | |
137 filename = filename[len(path_prefix):] | |
138 return filename | |
139 | |
140 # Given the list of enumerated constants from the given enumeration file, | |
141 # update the constants based on command line options. | |
142 def _ApplyFilters(constants): | |
143 if _ClOptions['sort']: | |
144 constants.sort() | |
145 return constants | |
146 | |
147 # Given the enumeration file name, open the file and return the list of | |
148 # constants defined in the file. | |
149 def _ReadConstants(filename): | |
150 name_pattern = re.compile(r'^(\w+)\s*\#?') | |
151 infile = open(filename, "r") | |
152 constants = [] | |
153 for line in infile: | |
154 line = line.rstrip() | |
155 trimmed_line = line.lstrip() | |
156 if len(trimmed_line) > 0 and not trimmed_line.startswith("#"): | |
157 if name_pattern.match(trimmed_line): | |
158 for name in name_pattern.findall(trimmed_line): | |
159 constants.append(name) | |
160 else: | |
161 logging.error("Line not understood: %s", line) | |
162 sys.exit(1) | |
163 if constants == []: | |
164 logging.error("No enumerated values found") | |
165 sys.exit(1) | |
166 return constants | |
167 | |
168 # Generate a DO NOT EDIT banner in the given file. | |
169 # file - The file to put the banner | |
170 # filename - The name of the file to put the banner in. | |
171 # enumfile - The name of the enumeration file. | |
172 def _AddDoNotEditMessage(file, filename, enumfile): | |
173 print >>file, "/* %s" % _GetSimplifiedFilename(filename) | |
174 print >>file, " * THIS FILE IS AUTO_GENERATED DO NOT EDIT." | |
175 print >>file, " *" | |
176 print >>file, " * This file was auto-generated by enum_gen.py" | |
177 print >>file, " * from file %s" % os.path.basename(enumfile) | |
178 print >>file, " */" | |
179 print >>file, "" | |
180 | |
181 # Given a file name, convert it to the DEFINE name to use to make sure | |
182 # the file is included at most once. | |
183 def _GetDefineName(filename): | |
184 return filename.replace(".", "_").replace( | |
185 "/", "_").replace("\\", "_").replace("-","_").upper() | |
186 | |
187 # Given the enumeration file name, and the constants defined within that file, | |
188 # Generate a header file defining the enumeration, and the corresponding functio
n | |
189 # to print out symbolic names for each constant. | |
190 def _GenerateHeader(enumfile, constants): | |
191 filename = _GetHeaderFilename(enumfile) | |
192 outfile = open(filename, "w") | |
193 simplified_filename = _GetSimplifiedFilename(filename) | |
194 _AddDoNotEditMessage(outfile, filename, enumfile) | |
195 enumname = _GetEnumName(enumfile) | |
196 print >>outfile, "#ifndef %s__" % _GetDefineName(simplified_filename) | |
197 print >>outfile, "#define %s__" % _GetDefineName(simplified_filename) | |
198 print >>outfile, "" | |
199 print >>outfile, '#include "native_client/src/include/portability.h"' | |
200 print >>outfile, "" | |
201 print >>outfile, "EXTERN_C_BEGIN" | |
202 print >>outfile, "typedef enum %s {" % enumname | |
203 enum_value = 0 | |
204 for constant in constants: | |
205 print >>outfile, " %s = %d," % (_GetPrintName(constant), enum_value) | |
206 enum_value += 1 | |
207 if _ClOptions['add_size']: | |
208 print >>outfile, (" %sEnumSize = %d, " + | |
209 "/* special size marker */") % (enumname, enum_value) | |
210 print >>outfile, "} %s;" % enumname | |
211 print >>outfile, "" | |
212 print >>outfile, "/* Returns the name of an %s constant. */" % enumname | |
213 print >>outfile, "extern const char* %sName(%s name);" % (enumname, enumname) | |
214 print >>outfile, "" | |
215 print >>outfile, "EXTERN_C_END" | |
216 print >>outfile, "" | |
217 print >>outfile, "#endif /* %s__ */" % _GetDefineName(simplified_filename) | |
218 | |
219 # Given the enumeration file name, and the constants defined within that file, | |
220 # Generate an implementation file defining the corresponding function to print | |
221 # out symbolic names for each constant. | |
222 def _GenerateSource(enumfile, constants): | |
223 filename = _GetSourceFilename(enumfile) | |
224 outfile = open(filename, "w") | |
225 _AddDoNotEditMessage(outfile, filename, enumfile) | |
226 enumname = _GetEnumName(enumfile) | |
227 sizename = constants[-1] | |
228 if _ClOptions['add_size']: | |
229 sizename = enumname + 'EnumSize' | |
230 print >>outfile, "/* Define the corresponding names of %s. */" % enumname | |
231 print >>outfile, ("static const char* " + | |
232 "const g_%sName[%s + 1] = {") % (enumname, sizename) | |
233 for constant in constants: | |
234 print >>outfile, ' "%s",' % constant | |
235 if _ClOptions['add_size']: | |
236 print >>outfile, ' "%sEnumSize"' % enumname | |
237 print >>outfile, "};" | |
238 print >>outfile, "" | |
239 print >>outfile, "const char* %sName(%s name) {" % (enumname, enumname) | |
240 print >>outfile, " return name <= %s" % sizename | |
241 print >>outfile, " ? g_%sName[name]" % enumname | |
242 print >>outfile, ' : "%s???";' % enumname | |
243 print >>outfile, "}" | |
244 | |
245 # Given an array of command line arguments, process command line options, and | |
246 # return a list of arguments that aren't command line options. | |
247 def _ProcessOptions(argv): | |
248 """Process command line options and return the unprocessed left overs.""" | |
249 try: | |
250 opts, args = getopt.getopt(argv, '', [x + '=' for x in _ClOptions]) | |
251 except getopt.GetoptError, err: | |
252 print(str(err)) # will print something like 'option -a not recognized' | |
253 sys.exit(-1) | |
254 | |
255 for o, a in opts: | |
256 # strip the leading '--' | |
257 option = o[2:] | |
258 assert option in _ClOptions | |
259 if type(_ClOptions[option]) == int: | |
260 _ClOptions[option] = int(a) | |
261 else: | |
262 _ClOptions[option] = a | |
263 # return the unprocessed options, i.e. the command | |
264 return args | |
265 | |
266 # Given an enumeration file to generate, build the corresponding header/source | |
267 # files implementing the enumerated type. | |
268 def main(argv): | |
269 command = _ProcessOptions(argv) | |
270 if len(command) != 1: | |
271 _Usage() | |
272 return 1 | |
273 enumfile = command[0] | |
274 _ValidateFilename(enumfile) | |
275 constants = _ApplyFilters(_ReadConstants(enumfile)) | |
276 _GenerateHeader(enumfile, constants) | |
277 _GenerateSource(enumfile, constants) | |
278 return 0 | |
279 | |
280 if __name__ == '__main__': | |
281 sys.exit(main(sys.argv[1:])) | |
OLD | NEW |