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