You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

265 lines
8.2KB

  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. """Compiles python string tables/arrays into .cc and .h files."""
  22. import os
  23. import string
  24. import sys
  25. class ResourceEntry(object):
  26. def __init__(self, index, key, value, dupe_of, table):
  27. self._index = index
  28. self._key = key
  29. self._value = value
  30. self._dupe_of = self._key if dupe_of is None else dupe_of
  31. self._table = table
  32. @property
  33. def variable_name(self):
  34. return '%s_%s' % (self._table.prefix.lower(), self._dupe_of)
  35. @property
  36. def declaration(self):
  37. c_type = self._table.c_type
  38. name = self.variable_name
  39. return 'const %(c_type)s %(name)s[] PROGMEM' % locals()
  40. def Declare(self, f):
  41. if self._dupe_of == self._key:
  42. # Dupes are not declared.
  43. f.write('extern %s;\n' % self.declaration)
  44. def DeclareAlias(self, f):
  45. prefix = self._table.prefix
  46. key = self._key.upper()
  47. index = self._index
  48. if self._table.python_type == str:
  49. comment = ' // %s' % self._value
  50. size = None
  51. else:
  52. comment = ''
  53. size = len(self._value)
  54. f.write('#define %(prefix)s_%(key)s %(index)d%(comment)s\n' % locals())
  55. if not size is None:
  56. f.write('#define %(prefix)s_%(key)s_SIZE %(size)d\n' % locals())
  57. def Compile(self, f):
  58. # Do not create declaration for dupes.
  59. if self._dupe_of != self._key:
  60. return
  61. declaration = self.declaration
  62. if self._table.python_type == str:
  63. value = self._value
  64. f.write('static %(declaration)s = "%(value)s";\n' % locals())
  65. else:
  66. f.write('%(declaration)s = {\n' % locals())
  67. n_elements = len(self._value)
  68. for i in xrange(0, n_elements, 8):
  69. f.write(' ');
  70. f.write(', '.join(
  71. '%6d' % self._value[j] for j in xrange(i, min(n_elements, i + 8))))
  72. f.write(',\n');
  73. f.write('};\n')
  74. class ResourceTable(object):
  75. def __init__(self, resource_tuple):
  76. self.name = resource_tuple[1]
  77. self.prefix = resource_tuple[2]
  78. self.c_type = resource_tuple[3]
  79. self.python_type = resource_tuple[4]
  80. self.ram_based_table = resource_tuple[5]
  81. self.entries = []
  82. self._ComputeIdentifierRewriteTable()
  83. keys = set()
  84. values = {}
  85. for index, entry in enumerate(resource_tuple[0]):
  86. if self.python_type == str:
  87. # There is no name/value for string entries
  88. key, value = entry, entry.strip()
  89. else:
  90. key, value = entry
  91. # Add a prefix to avoid key duplicates.
  92. key = self._MakeIdentifier(key)
  93. while key in keys:
  94. key = '_%s' % key
  95. keys.add(key)
  96. hashable_value = tuple(value)
  97. self.entries.append(ResourceEntry(index, key, value,
  98. values.get(hashable_value, None), self))
  99. if not hashable_value in values:
  100. values[hashable_value] = key
  101. def _ComputeIdentifierRewriteTable(self):
  102. in_chr = ''.join(map(chr, range(256)))
  103. out_chr = [ord('_')] * 256
  104. # Tolerated characters.
  105. for i in string.uppercase + string.lowercase + string.digits:
  106. out_chr[ord(i)] = ord(i.lower())
  107. # Rewritten characters.
  108. in_rewritten = '\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09~*+=><^"|'
  109. out_rewritten = '0123456789TPSeglxpv'
  110. for rewrite in zip(in_rewritten, out_rewritten):
  111. out_chr[ord(rewrite[0])] = ord(rewrite[1])
  112. table = string.maketrans(in_chr, ''.join(map(chr, out_chr)))
  113. bad_chars = '\t\n\r-:()[]"\',;'
  114. self._MakeIdentifier = lambda s:s.translate(table, bad_chars)
  115. def DeclareEntries(self, f):
  116. if self.python_type != str:
  117. for entry in self.entries:
  118. entry.Declare(f)
  119. def DeclareAliases(self, f):
  120. for entry in self.entries:
  121. entry.DeclareAlias(f)
  122. def Compile(self, f):
  123. # Write a declaration for each entry.
  124. for entry in self.entries:
  125. entry.Compile(f)
  126. # Write the resource pointer table.
  127. modifier = 'PROGMEM ' if not self.ram_based_table else ''
  128. c_type = self.c_type
  129. name = self.name
  130. f.write(
  131. '\n\n%(modifier)sconst %(c_type)s* const %(name)s_table[] = {\n' % locals())
  132. for entry in self.entries:
  133. f.write(' %s,\n' % entry.variable_name)
  134. f.write('};\n\n')
  135. class ResourceLibrary(object):
  136. def __init__(self, root):
  137. self._tables = []
  138. self._root = root
  139. # Create resource table objects for all resources.
  140. for resource_tuple in root.resources:
  141. # Split a multiline string into a list of strings
  142. if resource_tuple[-2] == str:
  143. resource_tuple = list(resource_tuple)
  144. resource_tuple[0] = [x for x in resource_tuple[0].split('\n') if x]
  145. resource_tuple = tuple(resource_tuple)
  146. self._tables.append(ResourceTable(resource_tuple))
  147. @property
  148. def max_num_entries(self):
  149. max_num_entries = 0
  150. for table in self._tables:
  151. max_num_entries = max(max_num_entries, len(table.entries))
  152. return max_num_entries
  153. def _OpenNamespace(self, f):
  154. if self._root.namespace:
  155. f.write('\nnamespace %s {\n\n' % self._root.namespace)
  156. def _CloseNamespace(self, f):
  157. if self._root.namespace:
  158. f.write('\n} // namespace %s\n' % self._root.namespace)
  159. def _DeclareTables(self, f):
  160. for table in self._tables:
  161. f.write('extern const %s* const %s_table[];\n\n' % (table.c_type, table.name))
  162. def _DeclareEntries(self, f):
  163. for table in self._tables:
  164. table.DeclareEntries(f)
  165. def _DeclareAliases(self, f):
  166. for table in self._tables:
  167. table.DeclareAliases(f)
  168. def _CompileTables(self, f):
  169. for table in self._tables:
  170. table.Compile(f)
  171. def GenerateHeader(self):
  172. root = self._root
  173. f = file(os.path.join(root.target, 'resources.h'), 'wb')
  174. # Write header and header guard
  175. header_guard = root.target.replace(os.path.sep, '_').upper()
  176. header_guard = '%s_RESOURCES_H_' % header_guard
  177. f.write(root.header + '\n\n')
  178. f.write('#ifndef %s\n' % header_guard)
  179. f.write('#define %s\n\n' % header_guard)
  180. f.write(root.includes + '\n\n')
  181. if root.create_specialized_manager:
  182. f.write('#include "avrlib/resources_manager.h"\n')
  183. self._OpenNamespace(f)
  184. f.write('typedef %s ResourceId;\n\n' % \
  185. root.types[self.max_num_entries > 255])
  186. self._DeclareTables(f)
  187. self._DeclareEntries(f)
  188. self._DeclareAliases(f)
  189. if root.create_specialized_manager:
  190. f.write('typedef avrlib::ResourcesManager<\n')
  191. f.write(' ResourceId,\n')
  192. f.write(' avrlib::ResourcesTables<\n')
  193. f.write(' %s_table,\n' % root.resources[0][1])
  194. f.write(' %s_table> > ResourcesManager; \n' % root.resources[1][1])
  195. self._CloseNamespace(f)
  196. f.write('\n#endif // %s\n' % (header_guard))
  197. f.close()
  198. def GenerateCc(self):
  199. root = self._root
  200. file_name = os.path.join(self._root.target, 'resources.cc')
  201. f = file(file_name, 'wb')
  202. f.write(self._root.header + '\n\n')
  203. f.write('#include "%s"\n' % file_name.replace('.cc', '.h'))
  204. self._OpenNamespace(f)
  205. self._CompileTables(f)
  206. self._CloseNamespace(f)
  207. f.close()
  208. def Compile(path):
  209. # A hacky way of loading the py file passed as an argument as a module +
  210. # a descent along the module path.
  211. base_name = os.path.splitext(path)[0]
  212. sys.path += [os.path.abspath('.')]
  213. resource_module = __import__(base_name.replace('/', '.'))
  214. for part in base_name.split('/')[1:]:
  215. resource_module = getattr(resource_module, part)
  216. library = ResourceLibrary(resource_module)
  217. library.GenerateHeader()
  218. library.GenerateCc()
  219. def main(argv):
  220. for i in xrange(1, len(argv)):
  221. Compile(argv[i])
  222. if __name__ == '__main__':
  223. main(sys.argv)