xref: /nrf52832-nimble/rt-thread/tools/mkromfs.py (revision 104654410c56c573564690304ae786df310c91fc)
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