OLD | NEW |
(Empty) | |
| 1 #!/usr/bin/python2.4 |
| 2 # |
| 3 # Copyright 2008 Google Inc. |
| 4 # |
| 5 # Licensed under the Apache License, Version 2.0 (the "License"); |
| 6 # you may not use this file except in compliance with the License. |
| 7 # You may obtain a copy of the License at |
| 8 # |
| 9 # http://www.apache.org/licenses/LICENSE-2.0 |
| 10 # |
| 11 # Unless required by applicable law or agreed to in writing, software |
| 12 # distributed under the License is distributed on an "AS IS" BASIS, |
| 13 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 14 # See the License for the specific language governing permissions and |
| 15 # limitations under the License. |
| 16 |
| 17 """Code generator |
| 18 |
| 19 This file is the main entry point for the code generator. |
| 20 To use: |
| 21 codegen.py --output-dir=output-path --generate=npapi file1.idl file2.idl ... |
| 22 """ |
| 23 |
| 24 import glob |
| 25 import imp |
| 26 # Use hashlib if present (Python 2.5 and up), otherwise fall back to md5. |
| 27 try: |
| 28 import hashlib |
| 29 except ImportError: |
| 30 import md5 |
| 31 import os |
| 32 import sys |
| 33 |
| 34 import gflags |
| 35 |
| 36 # local imports |
| 37 import idl_parser |
| 38 import locking |
| 39 import log |
| 40 import syntax_tree |
| 41 |
| 42 # default supported generators |
| 43 import header_generator |
| 44 import cpp_header_generator |
| 45 import js_header_generator |
| 46 import npapi_generator |
| 47 |
| 48 # default supported binding models |
| 49 import pod_binding |
| 50 import enum_binding |
| 51 import callback_binding |
| 52 import by_value_binding |
| 53 import by_pointer_binding |
| 54 import unsized_array_binding |
| 55 import nullable_binding |
| 56 |
| 57 generators = {'header': header_generator, |
| 58 'cppheader': cpp_header_generator, |
| 59 'jsheader': js_header_generator, |
| 60 'npapi': npapi_generator} |
| 61 |
| 62 binding_models = {'pod': pod_binding, |
| 63 'callback': callback_binding, |
| 64 'enum': enum_binding, |
| 65 'by_value': by_value_binding, |
| 66 'by_pointer': by_pointer_binding, |
| 67 'unsized_array': unsized_array_binding, |
| 68 'nullable': nullable_binding} |
| 69 |
| 70 |
| 71 FLAGS = gflags.FLAGS |
| 72 gflags.DEFINE_multistring('binding-module', [], 'include a binding model' |
| 73 ' module. Value is name:path where \'name\' is the' |
| 74 ' binding model name, and \'path\' is the binding' |
| 75 ' model module path.') |
| 76 |
| 77 gflags.DEFINE_multistring('generator-module', [], 'include a generator module.' |
| 78 ' Value is name:path where \'name\' is the generator' |
| 79 ' name, and \'path\' is the generator module path.') |
| 80 |
| 81 gflags.DEFINE_multistring('generate', [], 'the generator to use') |
| 82 gflags.DEFINE_string('output-dir', '.', 'the output directory') |
| 83 |
| 84 gflags.DEFINE_boolean('exclusive-lock', False, 'Use file locking to make sure' |
| 85 ' there is only one instance running at a time.') |
| 86 gflags.DEFINE_boolean('force', False, 'force generation even if the source' |
| 87 ' files have not changed') |
| 88 gflags.DEFINE_boolean('force-docs', False, 'force all members to have' |
| 89 ' documentation blocks or else raise an exception.') |
| 90 gflags.DEFINE_boolean('no-return-docs', False, 'remove docs marked as' |
| 91 ' noreturndocs.') |
| 92 gflags.DEFINE_boolean('overloaded-function-docs', False, |
| 93 'generate special overloaded function docs.') |
| 94 gflags.DEFINE_boolean('properties-equal-undefined', False, |
| 95 'Emit class.prototype.property = undefined;') |
| 96 |
| 97 class NativeType(syntax_tree.Definition): |
| 98 defn_type = 'Native' |
| 99 |
| 100 def __init__(self, source, attributes, name, podtype): |
| 101 syntax_tree.Definition.__init__(self, source, attributes, name) |
| 102 self.podtype = podtype |
| 103 self.is_type = True |
| 104 |
| 105 def LookUpBindingModel(self): |
| 106 """Implementation of LookUpBindingModel for NativeType.""" |
| 107 return 'pod' |
| 108 |
| 109 |
| 110 def GetStdNamespace(): |
| 111 pod_attributes = {'binding_model': 'pod'} |
| 112 source_file = idl_parser.File('<internal>') |
| 113 source_file.header = "common.h" |
| 114 source_file.npapi_cpp = None |
| 115 source_file.npapi_header = None |
| 116 source = idl_parser.SourceLocation(source_file, 0) |
| 117 defn_list = [NativeType(source, pod_attributes, 'string', 'string'), |
| 118 NativeType(source, pod_attributes, 'wstring', 'wstring')] |
| 119 return syntax_tree.Namespace(source, [], 'std', defn_list) |
| 120 |
| 121 |
| 122 def GetNativeTypes(): |
| 123 pod_attributes = {'binding_model': 'pod'} |
| 124 source_file = idl_parser.File('<internal>') |
| 125 source_file.header = None |
| 126 source_file.npapi_cpp = None |
| 127 source_file.npapi_header = None |
| 128 source = idl_parser.SourceLocation(source_file, 0) |
| 129 return [NativeType(source, pod_attributes, 'void', 'void'), |
| 130 NativeType(source, pod_attributes, 'int', 'int'), |
| 131 NativeType(source, pod_attributes, 'unsigned int', 'int'), |
| 132 NativeType(source, pod_attributes, 'size_t', 'int'), |
| 133 NativeType(source, pod_attributes, 'bool', 'bool'), |
| 134 NativeType(source, pod_attributes, 'float', 'float'), |
| 135 NativeType(source, pod_attributes, 'double', 'float'), |
| 136 NativeType(source, pod_attributes, 'Variant', 'variant'), |
| 137 GetStdNamespace()] |
| 138 |
| 139 |
| 140 def AddModulesFromFlags(table, flag_values, md5_hash): |
| 141 for entry in flag_values: |
| 142 string_list = entry.split(':') |
| 143 name = string_list[0] |
| 144 path = ':'.join(string_list[1:]) |
| 145 try: |
| 146 # hash the extra modules that we load |
| 147 md5_hash.update(open(path).read()) |
| 148 table[name] = imp.load_source(name, path) |
| 149 except IOError: |
| 150 print 'Could not load module %s.' % path |
| 151 raise |
| 152 |
| 153 |
| 154 def main(argv): |
| 155 files = argv[1:] |
| 156 # generate a hash of all the inputs to figure out if we need to re-generate |
| 157 # the outputs. |
| 158 # Use hashlib if present (Python 2.5 and up), otherwise fall back to md5. |
| 159 if globals().has_key('hashlib'): |
| 160 md5_hash = hashlib.md5() |
| 161 else: |
| 162 md5_hash = md5.new(); |
| 163 # hash the input files and the source python files (globbing *.py in the |
| 164 # directory of this file) |
| 165 for source_file in files + glob.glob(os.path.join(os.path.dirname(__file__), |
| 166 '*.py')): |
| 167 md5_hash.update(open(source_file).read()) |
| 168 # hash the options since they may affect the output |
| 169 for s in (FLAGS['generator-module'].value + FLAGS['binding-module'].value + |
| 170 FLAGS.generate + [FLAGS['output-dir'].value]): |
| 171 md5_hash.update(s) |
| 172 |
| 173 # import generator and binding model modules, and hash them |
| 174 AddModulesFromFlags(generators, FLAGS['generator-module'].value, md5_hash) |
| 175 AddModulesFromFlags(binding_models, FLAGS['binding-module'].value, md5_hash) |
| 176 |
| 177 output_dir = FLAGS['output-dir'].value |
| 178 if not os.path.isdir(output_dir): |
| 179 os.makedirs(output_dir) |
| 180 |
| 181 hash_filename = os.path.join(output_dir, 'hash') |
| 182 hash_value = md5_hash.hexdigest() |
| 183 if not FLAGS.force: |
| 184 try: |
| 185 hash_file = open(hash_filename, 'r') |
| 186 # Don't read while others are writing... |
| 187 if FLAGS['exclusive-lock'].value: |
| 188 locking.lockf(hash_file, locking.LOCK_SH) |
| 189 |
| 190 old_hash = hash_file.read() |
| 191 |
| 192 if FLAGS['exclusive-lock'].value: |
| 193 locking.lockf(hash_file, locking.LOCK_UN) |
| 194 hash_file.close() |
| 195 |
| 196 if hash_value == old_hash: |
| 197 print "Source files haven't changed: nothing to generate." |
| 198 return |
| 199 except IOError: |
| 200 # Could not load the hash file, so there must be stuff to |
| 201 # generate. |
| 202 pass |
| 203 |
| 204 hash_file = open(hash_filename, 'w') |
| 205 if FLAGS['exclusive-lock'].value: |
| 206 locking.lockf(hash_file, locking.LOCK_EX) |
| 207 |
| 208 my_parser = idl_parser.Parser(output_dir) |
| 209 pairs = [] |
| 210 for f in files: |
| 211 idl_file = idl_parser.File(f) |
| 212 defn = my_parser.Parse(idl_file) |
| 213 pairs.append((idl_file, defn)) |
| 214 definitions = sum([defn for (f, defn) in pairs], []) + GetNativeTypes() |
| 215 global_namespace = syntax_tree.Namespace(None, [], '', definitions) |
| 216 syntax_tree.FinalizeObjects(global_namespace, binding_models) |
| 217 |
| 218 writer_list = [] |
| 219 for generator_name in FLAGS.generate: |
| 220 try: |
| 221 generator = generators[generator_name] |
| 222 writer_list += generator.ProcessFiles(output_dir, pairs, global_namespace) |
| 223 except KeyError: |
| 224 print 'Unknown generator %s.' % generator_name |
| 225 raise |
| 226 for writer in writer_list: |
| 227 writer.Write() |
| 228 |
| 229 # Save hash for next time |
| 230 hash_file.write(hash_value) |
| 231 if FLAGS['exclusive-lock'].value: |
| 232 locking.lockf(hash_file, locking.LOCK_UN) |
| 233 hash_file.close() |
| 234 log.FailIfHaveErrors() |
| 235 |
| 236 |
| 237 if __name__ == '__main__': |
| 238 main(FLAGS(sys.argv)) |
OLD | NEW |