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