8000 tools/mpy-tool.py: Add ability to merge multiple .mpy files into one. · micropython/micropython@2787984 · GitHub
[go: up one dir, main page]

Skip to content

Commit 2787984

Browse files
committed
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.
1 parent 3690f79 commit 2787984

File tree

1 file changed

+59
-1
lines changed

1 file changed

+59
-1
lines changed

tools/mpy-tool.py

Lines changed: 59 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -691,7 +691,10 @@ def read_mpy(filename):
691691
raise Exception('native architecture mismatch')
692692
config.mp_small_int_bits = header[3]
693693
qstr_win = QStrWindow(qw_size)
694-
return read_raw_code(f, qstr_win)
694+
rc = read_raw_code(f, qstr_win)
695+
rc.mpy_source_file = filename
696+
rc.qstr_win_size = qw_size
697+
return rc
695698

696699
def dump_mpy(raw_codes):
697700
for rc in raw_codes:
@@ -789,19 +792,72 @@ def freeze_mpy(base_qstrs, raw_codes):
789792
print(' &raw_code_%s,' % rc.escaped_name)
790793
print('};')
791794

795+
def merge_mpy(raw_codes, output_file):
796+
assert len(raw_codes) <= 31 # so var-uints all fit in 1 byte
797+
merged_mpy = bytearray()
798+
799+
if len(raw_codes) == 1:
800+
with open(raw_codes[0].mpy_source_file, 'rb') as f:
801+
merged_mpy.extend(f.read())
802+
else:
803+
header = bytearray(5)
804+
header[0] = ord('M')
805+
header[1] = config.MPY_VERSION
806+
header[2] = (config.native_arch << 2
807+
| config.MICROPY_PY_BUILTINS_STR_UNICODE << 1
808+
| config.MICROPY_OPT_CACHE_MAP_LOOKUP_IN_BYTECODE)
809+
header[3] = config.mp_small_int_bits
810+
header[4] = 32 # qstr_win_size
811+
merged_mpy.extend(header)
812+
813+
bytecode = bytearray()
814+
bytecode_len = 6 + len(raw_codes) * 4 + 2
815+
bytecode.append(bytecode_len << 2) # kind and length
816+
bytecode.append(0b00000000) # signature prelude
817+
bytecode.append(0b00001000) # size prelude
818+
bytecode.extend(b'\x00\x01') # MP_QSTR_
819+
bytecode.extend(b'\x00\x01') # MP_QSTR_
820+
for idx in range(len(raw_codes)):
821+
bytecode.append(0x32) # MP_BC_MAKE_FUNCTION
822+
bytecode.append(idx) # index raw code
823+
bytecode.extend(b'\x34\x00') # MP_BC_CALL_FUNCTION, 0 args
824+
bytecode.extend(b'\x51\x63') # MP_BC_LOAD_NONE, MP_BC_RETURN_VALUE
825+
826+
bytecode.append(0) # n_obj
827+
bytecode.append(len(raw_codes)) # n_raw_code
828+
829+
merged_mpy.extend(bytecode)
830+
831+
for rc in raw_codes:
832+
with open(rc.mpy_source_file, 'rb') as f:
833+
f.read(4) # skip header
834+
read_uint(f) # skip qstr_win_size
835+
data = f.read() # read rest of mpy file
836+
merged_mpy.extend(data)
837+
838+
if output_file is None:
839+
sys.stdout.buffer.write(merged_mpy)
840+
else:
841+
with open(output_file, 'wb') as f:
842+
f.write(merged_mpy)
843+
792844
def main():
793845
import argparse
794846
cmd_parser = argparse.ArgumentParser(description='A tool to work with MicroPython .mpy files.')
795847
cmd_parser.add_argument('-d', '--dump', action='store_true',
796848
help='dump contents of files')
797849
cmd_parser.add_argument('-f', '--freeze', action='store_true',
798850
help='freeze files')
851+
cmd_parser.add_argument('--merge', action='store_true',
852+
help='merge multiple .mpy files into one')
799853
cmd_parser.add_argument('-q', '--qstr-header',
800854
help='qstr header file to freeze against')
801855
cmd_parser.add_argument('-mlongint-impl', choices=['none', 'longlong', 'mpz'], default='mpz',
802856
help='long-int implementation used by target (default mpz)')
803857
cmd_parser.add_argument('-mmpz-dig-size', metavar='N', type=int, default=16,
804858
help='mpz digit size used by target (default 16)')
859+
cmd_parser.add_argument('-o', '--output', default=None,
860+
help='output file')
805861
cmd_parser.add_argument('files', nargs='+',
806862
help='input .mpy files')
807863
args = cmd_parser.parse_args()
@@ -835,6 +891,8 @@ def main():
835891
except FreezeError as er:
836892
print(er, file=sys.stderr)
837893
sys.exit(1)
894+
elif args.merge:
895+
merged_mpy = merge_mpy(raw_codes, args.output)
838896

839897
if __name__ == '__main__':
840898
main()

0 commit comments

Comments
 (0)
0