mkromfs.py 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252
  1. #!/usr/bin/env python
  2. import sys
  3. import os
  4. import struct
  5. from collections import namedtuple
  6. import StringIO
  7. import argparse
  8. parser = argparse.ArgumentParser()
  9. parser.add_argument('rootdir', type=str, help='the path to rootfs')
  10. parser.add_argument('output', type=argparse.FileType('wb'), nargs='?', help='output file name')
  11. parser.add_argument('--dump', action='store_true', help='dump the fs hierarchy')
  12. parser.add_argument('--binary', action='store_true', help='output binary file')
  13. parser.add_argument('--addr', default='0', help='set the base address of the binary file, default to 0.')
  14. class File(object):
  15. def __init__(self, name):
  16. self._name = name
  17. self._data = open(name, 'rb').read()
  18. @property
  19. def name(self):
  20. return self._name
  21. @property
  22. def c_name(self):
  23. return '_' + self._name.replace('.', '_')
  24. @property
  25. def bin_name(self):
  26. # Pad to 4 bytes boundary with \0
  27. pad_len = 4
  28. bn = self._name + '\0' * (pad_len - len(self._name) % pad_len)
  29. return bn
  30. def c_data(self, prefix=''):
  31. '''Get the C code represent of the file content.'''
  32. head = 'static const rt_uint8_t %s[] = {\n' % \
  33. (prefix + self.c_name)
  34. tail = '\n};'
  35. return head + ','.join(('0x%02x' % ord(i) for i in self._data)) + tail
  36. @property
  37. def entry_size(self):
  38. return len(self._data)
  39. def bin_data(self, base_addr=0x0):
  40. return bytes(self._data)
  41. def dump(self, indent=0):
  42. print('%s%s' % (' ' * indent, self._name))
  43. class Folder(object):
  44. bin_fmt = struct.Struct('IIII')
  45. bin_item = namedtuple('dirent', 'type, name, data, size')
  46. def __init__(self, name):
  47. self._name = name
  48. self._children = []
  49. @property
  50. def name(self):
  51. return self._name
  52. @property
  53. def c_name(self):
  54. # add _ to avoid conflict with C key words.
  55. return '_' + self._name
  56. @property
  57. def bin_name(self):
  58. # Pad to 4 bytes boundary with \0
  59. pad_len = 4
  60. bn = self._name + '\0' * (pad_len - len(self._name) % pad_len)
  61. return bn
  62. def walk(self):
  63. # os.listdir will return unicode list if the argument is unicode.
  64. # TODO: take care of the unicode names
  65. for ent in os.listdir(u'.'):
  66. if os.path.isdir(ent):
  67. cwd = os.getcwdu()
  68. d = Folder(ent)
  69. # depth-first
  70. os.chdir(os.path.join(cwd, ent))
  71. d.walk()
  72. # restore the cwd
  73. os.chdir(cwd)
  74. self._children.append(d)
  75. else:
  76. self._children.append(File(ent))
  77. def sort(self):
  78. def _sort(x, y):
  79. if x.name == y.name:
  80. return 0
  81. elif x.name > y.name:
  82. return 1
  83. else:
  84. return -1
  85. self._children.sort(cmp=_sort)
  86. # sort recursively
  87. for c in self._children:
  88. if isinstance(c, Folder):
  89. c.sort()
  90. def dump(self, indent=0):
  91. print('%s%s' % (' ' * indent, self._name))
  92. for c in self._children:
  93. c.dump(indent + 1)
  94. def c_data(self, prefix=''):
  95. '''get the C code represent of the folder.
  96. It is recursive.'''
  97. # make the current dirent
  98. # static is good. Only root dirent is global visible.
  99. dhead = 'static const struct romfs_dirent %s[] = {\n' % (prefix + self.c_name)
  100. dtail = '\n};'
  101. body_fmt = ' {{{type}, "{name}", (rt_uint8_t *){data}, sizeof({data})/sizeof({data}[0])}}'
  102. # prefix of children
  103. cpf = prefix+self.c_name
  104. body_li = []
  105. payload_li = []
  106. for c in self._children:
  107. if isinstance(c, File):
  108. tp = 'ROMFS_DIRENT_FILE'
  109. elif isinstance(c, Folder):
  110. tp = 'ROMFS_DIRENT_DIR'
  111. else:
  112. assert False, 'Unkown instance:%s' % str(c)
  113. body_li.append(body_fmt.format(type=tp,
  114. name=c.name,
  115. data=cpf+c.c_name))
  116. payload_li.append(c.c_data(prefix=cpf))
  117. # All the data we need is defined in payload so we should append the
  118. # dirent to it. It also meet the depth-first policy in this code.
  119. payload_li.append(dhead + ',\n'.join(body_li) + dtail)
  120. return '\n\n'.join(payload_li)
  121. @property
  122. def entry_size(self):
  123. return len(self._children)
  124. def bin_data(self, base_addr=0x0):
  125. '''Return StringIO object'''
  126. # The binary layout is different from the C code layout. We put the
  127. # dirent before the payload in this mode. But the idea is still simple:
  128. # Depth-First.
  129. #{
  130. # rt_uint32_t type;
  131. # const char *name;
  132. # const rt_uint8_t *data;
  133. # rt_size_t size;
  134. #}
  135. d_li = []
  136. # payload base
  137. p_base = base_addr + self.bin_fmt.size * self.entry_size
  138. # the length to record how many data is in
  139. v_len = p_base
  140. # payload
  141. p_li = []
  142. for c in self._children:
  143. if isinstance(c, File):
  144. # ROMFS_DIRENT_FILE
  145. tp = 0
  146. elif isinstance(c, Folder):
  147. # ROMFS_DIRENT_DIR
  148. tp = 1
  149. else:
  150. assert False, 'Unkown instance:%s' % str(c)
  151. name = bytes(c.bin_name)
  152. name_addr = v_len
  153. v_len += len(name)
  154. data = c.bin_data(base_addr=v_len)
  155. data_addr = v_len
  156. # pad the data to 4 bytes boundary
  157. pad_len = 4
  158. if len(data) % pad_len != 0:
  159. data += '\0' * (pad_len - len(data) % pad_len)
  160. v_len += len(data)
  161. d_li.append(self.bin_fmt.pack(*self.bin_item(
  162. type=tp,
  163. name=name_addr,
  164. data=data_addr,
  165. size=c.entry_size)))
  166. p_li.extend((name, data))
  167. return bytes().join(d_li) + bytes().join(p_li)
  168. def get_c_data(tree):
  169. # Handle the root dirent specially.
  170. root_dirent_fmt = '''/* Generated by mkromfs. Edit with caution. */
  171. #include <rtthread.h>
  172. #include <dfs_romfs.h>
  173. {data}
  174. const struct romfs_dirent {name} = {{
  175. ROMFS_DIRENT_DIR, "/", (rt_uint8_t *){rootdirent}, sizeof({rootdirent})/sizeof({rootdirent}[0])
  176. }};
  177. '''
  178. return root_dirent_fmt.format(name='romfs_root',
  179. rootdirent=tree.c_name,
  180. data=tree.c_data())
  181. def get_bin_data(tree, base_addr):
  182. v_len = base_addr + Folder.bin_fmt.size
  183. name = bytes('/\0\0\0')
  184. name_addr = v_len
  185. v_len += len(name)
  186. data_addr = v_len
  187. # root entry
  188. data = Folder.bin_fmt.pack(*Folder.bin_item(type=1,
  189. name=name_addr,
  190. data=data_addr,
  191. size=tree.entry_size))
  192. return data + name + tree.bin_data(v_len)
  193. if __name__ == '__main__':
  194. args = parser.parse_args()
  195. os.chdir(args.rootdir)
  196. tree = Folder('romfs_root')
  197. tree.walk()
  198. tree.sort()
  199. if args.dump:
  200. tree.dump()
  201. if args.binary:
  202. data = get_bin_data(tree, int(args.addr, 16))
  203. else:
  204. data = get_c_data(tree)
  205. output = args.output
  206. if not output:
  207. output = sys.stdout
  208. output.write(data)