tools/mpy-tool.py: Add ability to merge multiple .mpy files into one.
Usage: mpy-tool.py -o merged.mpy --merge mod1.mpy mod2.mpy The constituent .mpy files are executed sequentially when the merged file is imported, and they all use the same global namespace.
This commit is contained in:
parent
3690f79afc
commit
27879844d2
|
@ -691,7 +691,10 @@ def read_mpy(filename):
|
|||
raise Exception('native architecture mismatch')
|
||||
config.mp_small_int_bits = header[3]
|
||||
qstr_win = QStrWindow(qw_size)
|
||||
return read_raw_code(f, qstr_win)
|
||||
rc = read_raw_code(f, qstr_win)
|
||||
rc.mpy_source_file = filename
|
||||
rc.qstr_win_size = qw_size
|
||||
return rc
|
||||
|
||||
def dump_mpy(raw_codes):
|
||||
for rc in raw_codes:
|
||||
|
@ -789,6 +792,55 @@ def freeze_mpy(base_qstrs, raw_codes):
|
|||
print(' &raw_code_%s,' % rc.escaped_name)
|
||||
print('};')
|
||||
|
||||
def merge_mpy(raw_codes, output_file):
|
||||
assert len(raw_codes) <= 31 # so var-uints all fit in 1 byte
|
||||
merged_mpy = bytearray()
|
||||
|
||||
if len(raw_codes) == 1:
|
||||
with open(raw_codes[0].mpy_source_file, 'rb') as f:
|
||||
merged_mpy.extend(f.read())
|
||||
else:
|
||||
header = bytearray(5)
|
||||
header[0] = ord('M')
|
||||
header[1] = config.MPY_VERSION
|
||||
header[2] = (config.native_arch << 2
|
||||
| config.MICROPY_PY_BUILTINS_STR_UNICODE << 1
|
||||
| config.MICROPY_OPT_CACHE_MAP_LOOKUP_IN_BYTECODE)
|
||||
header[3] = config.mp_small_int_bits
|
||||
header[4] = 32 # qstr_win_size
|
||||
merged_mpy.extend(header)
|
||||
|
||||
bytecode = bytearray()
|
||||
bytecode_len = 6 + len(raw_codes) * 4 + 2
|
||||
bytecode.append(bytecode_len << 2) # kind and length
|
||||
bytecode.append(0b00000000) # signature prelude
|
||||
bytecode.append(0b00001000) # size prelude
|
||||
bytecode.extend(b'\x00\x01') # MP_QSTR_
|
||||
bytecode.extend(b'\x00\x01') # MP_QSTR_
|
||||
for idx in range(len(raw_codes)):
|
||||
bytecode.append(0x32) # MP_BC_MAKE_FUNCTION
|
||||
bytecode.append(idx) # index raw code
|
||||
bytecode.extend(b'\x34\x00') # MP_BC_CALL_FUNCTION, 0 args
|
||||
bytecode.extend(b'\x51\x63') # MP_BC_LOAD_NONE, MP_BC_RETURN_VALUE
|
||||
|
||||
bytecode.append(0) # n_obj
|
||||
bytecode.append(len(raw_codes)) # n_raw_code
|
||||
|
||||
merged_mpy.extend(bytecode)
|
||||
|
||||
for rc in raw_codes:
|
||||
with open(rc.mpy_source_file, 'rb') as f:
|
||||
f.read(4) # skip header
|
||||
read_uint(f) # skip qstr_win_size
|
||||
data = f.read() # read rest of mpy file
|
||||
merged_mpy.extend(data)
|
||||
|
||||
if output_file is None:
|
||||
sys.stdout.buffer.write(merged_mpy)
|
||||
else:
|
||||
with open(output_file, 'wb') as f:
|
||||
f.write(merged_mpy)
|
||||
|
||||
def main():
|
||||
import argparse
|
||||
cmd_parser = argparse.ArgumentParser(description='A tool to work with MicroPython .mpy files.')
|
||||
|
@ -796,12 +848,16 @@ def main():
|
|||
help='dump contents of files')
|
||||
cmd_parser.add_argument('-f', '--freeze', action='store_true',
|
||||
help='freeze files')
|
||||
cmd_parser.add_argument('--merge', action='store_true',
|
||||
help='merge multiple .mpy files into one')
|
||||
cmd_parser.add_argument('-q', '--qstr-header',
|
||||
help='qstr header file to freeze against')
|
||||
cmd_parser.add_argument('-mlongint-impl', choices=['none', 'longlong', 'mpz'], default='mpz',
|
||||
help='long-int implementation used by target (default mpz)')
|
||||
cmd_parser.add_argument('-mmpz-dig-size', metavar='N', type=int, default=16,
|
||||
help='mpz digit size used by target (default 16)')
|
||||
cmd_parser.add_argument('-o', '--output', default=None,
|
||||
help='output file')
|
||||
cmd_parser.add_argument('files', nargs='+',
|
||||
help='input .mpy files')
|
||||
args = cmd_parser.parse_args()
|
||||
|
@ -835,6 +891,8 @@ def main():
|
|||
except FreezeError as er:
|
||||
print(er, file=sys.stderr)
|
||||
sys.exit(1)
|
||||
elif args.merge:
|
||||
merged_mpy = merge_mpy(raw_codes, args.output)
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
|
|
Loading…
Reference in New Issue