1#!/usr/bin/python2.5 2# 3# Copyright 2009 Olivier Gillet. 4# 5# Author: Olivier Gillet (ol.gillet@gmail.com) 6# 7# This program is free software: you can redistribute it and/or modify 8# it under the terms of the GNU General Public License as published by 9# the Free Software Foundation, either version 3 of the License, or 10# (at your option) any later version. 11# This program is distributed in the hope that it will be useful, 12# but WITHOUT ANY WARRANTY; without even the implied warranty of 13# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14# GNU General Public License for more details. 15# You should have received a copy of the GNU General Public License 16# along with this program. If not, see <http://www.gnu.org/licenses/>. 17# 18# ----------------------------------------------------------------------------- 19# 20# Generates .cc and .h files for string, lookup tables, etc. 21 22"""Compiles python string tables/arrays into .cc and .h files.""" 23 24import os 25import string 26import sys 27 28 29class ResourceEntry(object): 30 31 def __init__(self, index, key, value, dupe_of, table): 32 self._index = index 33 self._key = key 34 self._value = value 35 self._dupe_of = self._key if dupe_of is None else dupe_of 36 self._table = table 37 38 @property 39 def variable_name(self): 40 return '%s_%s' % (self._table.prefix.lower(), self._dupe_of) 41 42 @property 43 def declaration(self): 44 c_type = self._table.c_type 45 name = self.variable_name 46 return 'const %(c_type)s %(name)s[] PROGMEM' % locals() 47 48 def Declare(self, f): 49 if self._dupe_of == self._key: 50 # Dupes are not declared. 51 f.write('extern %s;\n' % self.declaration) 52 53 def DeclareAlias(self, f): 54 prefix = self._table.prefix 55 key = self._key.upper() 56 index = self._index 57 if self._table.python_type == str: 58 comment = ' // %s' % self._value 59 size = None 60 else: 61 comment = '' 62 size = len(self._value) 63 f.write('#define %(prefix)s_%(key)s %(index)d%(comment)s\n' % locals()) 64 if not size is None: 65 f.write('#define %(prefix)s_%(key)s_SIZE %(size)d\n' % locals()) 66 67 def Compile(self, f): 68 # Do not create declaration for dupes. 69 if self._dupe_of != self._key: 70 return 71 72 declaration = self.declaration 73 if self._table.python_type == str: 74 value = self._value 75 f.write('static %(declaration)s = "%(value)s";\n' % locals()) 76 else: 77 f.write('%(declaration)s = {\n' % locals()) 78 n_elements = len(self._value) 79 for i in xrange(0, n_elements, 8): 80 f.write(' '); 81 f.write(', '.join( 82 '%6d' % self._value[j] for j in xrange(i, min(n_elements, i + 8)))) 83 f.write(',\n'); 84 f.write('};\n') 85 86 87class ResourceTable(object): 88 89 def __init__(self, resource_tuple): 90 self.name = resource_tuple[1] 91 self.prefix = resource_tuple[2] 92 self.c_type = resource_tuple[3] 93 self.python_type = resource_tuple[4] 94 self.ram_based_table = resource_tuple[5] 95 self.entries = [] 96 self._ComputeIdentifierRewriteTable() 97 keys = set() 98 values = {} 99 for index, entry in enumerate(resource_tuple[0]): 100 if self.python_type == str: 101 # There is no name/value for string entries 102 key, value = entry, entry.strip() 103 else: 104 key, value = entry 105 106 # Add a prefix to avoid key duplicates. 107 key = self._MakeIdentifier(key) 108 while key in keys: 109 key = '_%s' % key 110 keys.add(key) 111 hashable_value = tuple(value) 112 self.entries.append(ResourceEntry(index, key, value, 113 values.get(hashable_value, None), self)) 114 if not hashable_value in values: 115 values[hashable_value] = key 116 117 def _ComputeIdentifierRewriteTable(self): 118 in_chr = ''.join(map(chr, range(256))) 119 out_chr = [ord('_')] * 256 120 # Tolerated characters. 121 for i in string.uppercase + string.lowercase + string.digits: 122 out_chr[ord(i)] = ord(i.lower()) 123 124 # Rewritten characters. 125 in_rewritten = '\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09~*+=><^"|' 126 out_rewritten = '0123456789TPSeglxpv' 127 for rewrite in zip(in_rewritten, out_rewritten): 128 out_chr[ord(rewrite[0])] = ord(rewrite[1]) 129 130 table = string.maketrans(in_chr, ''.join(map(chr, out_chr))) 131 bad_chars = '\t\n\r-:()[]"\',;' 132 self._MakeIdentifier = lambda s:s.translate(table, bad_chars) 133 134 def DeclareEntries(self, f): 135 if self.python_type != str: 136 for entry in self.entries: 137 entry.Declare(f) 138 139 def DeclareAliases(self, f): 140 for entry in self.entries: 141 entry.DeclareAlias(f) 142 143 def Compile(self, f): 144 # Write a declaration for each entry. 145 for entry in self.entries: 146 entry.Compile(f) 147 148 # Write the resource pointer table. 149 modifier = 'PROGMEM ' if not self.ram_based_table else '' 150 c_type = self.c_type 151 name = self.name 152 f.write( 153 '\n\n%(modifier)sconst %(c_type)s* const %(name)s_table[] = {\n' % locals()) 154 for entry in self.entries: 155 f.write(' %s,\n' % entry.variable_name) 156 f.write('};\n\n') 157 158 159class ResourceLibrary(object): 160 161 def __init__(self, root): 162 self._tables = [] 163 self._root = root 164 # Create resource table objects for all resources. 165 for resource_tuple in root.resources: 166 # Split a multiline string into a list of strings 167 if resource_tuple[-2] == str: 168 resource_tuple = list(resource_tuple) 169 resource_tuple[0] = [x for x in resource_tuple[0].split('\n') if x] 170 resource_tuple = tuple(resource_tuple) 171 self._tables.append(ResourceTable(resource_tuple)) 172 173 @property 174 def max_num_entries(self): 175 max_num_entries = 0 176 for table in self._tables: 177 max_num_entries = max(max_num_entries, len(table.entries)) 178 return max_num_entries 179 180 def _OpenNamespace(self, f): 181 if self._root.namespace: 182 f.write('\nnamespace %s {\n\n' % self._root.namespace) 183 184 def _CloseNamespace(self, f): 185 if self._root.namespace: 186 f.write('\n} // namespace %s\n' % self._root.namespace) 187 188 def _DeclareTables(self, f): 189 for table in self._tables: 190 f.write('extern const %s* const %s_table[];\n\n' % (table.c_type, table.name)) 191 192 def _DeclareEntries(self, f): 193 for table in self._tables: 194 table.DeclareEntries(f) 195 196 def _DeclareAliases(self, f): 197 for table in self._tables: 198 table.DeclareAliases(f) 199 200 def _CompileTables(self, f): 201 for table in self._tables: 202 table.Compile(f) 203 204 def GenerateHeader(self): 205 root = self._root 206 f = file(os.path.join(root.target, 'resources.h'), 'wb') 207 # Write header and header guard 208 header_guard = root.target.replace(os.path.sep, '_').upper() 209 header_guard = '%s_RESOURCES_H_' % header_guard 210 f.write(root.header + '\n\n') 211 f.write('#ifndef %s\n' % header_guard) 212 f.write('#define %s\n\n' % header_guard) 213 f.write(root.includes + '\n\n') 214 if root.create_specialized_manager: 215 f.write('#include "avrlib/resources_manager.h"\n') 216 self._OpenNamespace(f) 217 f.write('typedef %s ResourceId;\n\n' % \ 218 root.types[self.max_num_entries > 255]) 219 self._DeclareTables(f) 220 self._DeclareEntries(f) 221 self._DeclareAliases(f) 222 if root.create_specialized_manager: 223 f.write('typedef avrlib::ResourcesManager<\n') 224 f.write(' ResourceId,\n') 225 f.write(' avrlib::ResourcesTables<\n') 226 f.write(' %s_table,\n' % root.resources[0][1]) 227 f.write(' %s_table> > ResourcesManager; \n' % root.resources[1][1]) 228 self._CloseNamespace(f) 229 f.write('\n#endif // %s\n' % (header_guard)) 230 f.close() 231 232 def GenerateCc(self): 233 root = self._root 234 file_name = os.path.join(self._root.target, 'resources.cc') 235 f = file(file_name, 'wb') 236 f.write(self._root.header + '\n\n') 237 f.write('#include "%s"\n' % file_name.replace('.cc', '.h')) 238 self._OpenNamespace(f) 239 self._CompileTables(f) 240 self._CloseNamespace(f) 241 f.close() 242 243 244def Compile(path): 245 # A hacky way of loading the py file passed as an argument as a module + 246 # a descent along the module path. 247 base_name = os.path.splitext(path)[0] 248 sys.path += [os.path.abspath('.')] 249 resource_module = __import__(base_name.replace('/', '.')) 250 for part in base_name.split('/')[1:]: 251 resource_module = getattr(resource_module, part) 252 253 library = ResourceLibrary(resource_module) 254 library.GenerateHeader() 255 library.GenerateCc() 256 257 258def main(argv): 259 for i in xrange(1, len(argv)): 260 Compile(argv[i]) 261 262 263if __name__ == '__main__': 264 main(sys.argv) 265