Skip to content

Commit 4ff9a53

Browse files
committed
tools/mpremote: Add manifest function.
1 parent 6a7838d commit 4ff9a53

File tree

5 files changed

+193
-74
lines changed

5 files changed

+193
-74
lines changed

docs/reference/mpremote.rst

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,23 @@ The full list of supported commands are:
141141
142142
$ mpremote umount
143143
144+
- compile a manifest.py from the current folder:
145+
146+
.. code-block:: bash
147+
148+
$ MPY_DIR=../../micropython
149+
$ PORT_DIR=../../micropython/ports/esp32
150+
$ mpremote manifest .
151+
152+
This will assemble / mpy-cross everything specified in the manifest.py into the folder ``_manifest``.
153+
If the current folder is also mounted, this folder will automatically be added to the path, eg:
154+
155+
.. code-block:: bash
156+
157+
$ mpremote manifest . mount .
158+
159+
A soft-reset will re-process the manifest file to include any local updates.
160+
144161
Multiple commands can be specified and they will be run sequentially.
145162

146163

tools/makemanifest.py

Lines changed: 98 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -135,10 +135,20 @@ def freeze_mpy(path, script=None, opt=0):
135135
KIND_MPY = 3
136136

137137
VARS = {}
138+
MPY_CROSS = None
139+
MPY_TOOL = None
138140

139141
manifest_list = []
140142

141143

144+
QUIET = False
145+
146+
147+
def log(*args):
148+
if not QUIET:
149+
print(*args)
150+
151+
142152
class IncludeOptions:
143153
def __init__(self, **kwargs):
144154
self._kwargs = kwargs
@@ -166,7 +176,12 @@ def system(cmd):
166176
def convert_path(path):
167177
# Perform variable substituion.
168178
for name, value in VARS.items():
169-
path = path.replace("$({})".format(name), value)
179+
pattern = "$({})".format(name)
180+
if value is not None:
181+
path = path.replace(pattern, value)
182+
elif pattern in path:
183+
raise SystemExit("{} variable must be specified".format(name))
184+
170185
# Convert to absolute path (so that future operations don't rely on
171186
# still being chdir'ed).
172187
return os.path.abspath(path)
@@ -225,7 +240,7 @@ def freeze_internal(kind, path, script, opt):
225240
kind = k
226241
break
227242
else:
228-
print("warn: unsupported file type, skipped freeze: {}".format(script))
243+
log("warn: unsupported file type, skipped freeze: {}".format(script))
229244
return
230245
wanted_extension = extension_kind[kind]
231246
if not script.endswith(wanted_extension):
@@ -325,23 +340,42 @@ def main():
325340
VARS[name] = value
326341

327342
if "MPY_DIR" not in VARS or "PORT_DIR" not in VARS:
328-
print("MPY_DIR and PORT_DIR variables must be specified")
343+
log.error("MPY_DIR and PORT_DIR variables must be specified")
329344
sys.exit(1)
330345

331-
# Get paths to tools
332-
MPY_CROSS = VARS["MPY_DIR"] + "/mpy-cross/mpy-cross"
333-
if sys.platform == "win32":
334-
MPY_CROSS += ".exe"
335-
MPY_CROSS = os.getenv("MICROPY_MPYCROSS", MPY_CROSS)
336-
MPY_TOOL = VARS["MPY_DIR"] + "/tools/mpy-tool.py"
346+
process(
347+
args.files,
348+
args.build_dir,
349+
args.output,
350+
args.mpy_tool_flags,
351+
args.mpy_cross_flags,
352+
)
337353

338-
# Ensure mpy-cross is built
354+
355+
def process(files, build_dir, output=None, mpy_tool_flags="", mpy_cross_flags=""):
356+
# Get paths to tools
357+
global MPY_CROSS, MPY_TOOL
358+
if MPY_CROSS is None:
359+
MPY_CROSS = VARS["MPY_DIR"] + "/mpy-cross/mpy-cross"
360+
if sys.platform == "win32":
361+
MPY_CROSS += ".exe"
362+
MPY_CROSS = os.getenv("MICROPY_MPYCROSS", MPY_CROSS)
363+
if MPY_TOOL is None:
364+
MPY_TOOL = VARS["MPY_DIR"] + "/tools/mpy-tool.py"
365+
366+
# Ensure mpy-cross is built / available
367+
if not os.path.exists(MPY_CROSS):
368+
try:
369+
from mpy_cross import mpy_cross
370+
MPY_CROSS = mpy_cross
371+
except ImportError:
372+
pass
339373
if not os.path.exists(MPY_CROSS):
340374
print("mpy-cross not found at {}, please build it first".format(MPY_CROSS))
341375
sys.exit(1)
342376

343377
# Include top-level inputs, to generate the manifest
344-
for input_manifest in args.files:
378+
for input_manifest in files:
345379
try:
346380
if input_manifest.endswith(".py"):
347381
include(input_manifest)
@@ -354,29 +388,32 @@ def main():
354388
# Process the manifest
355389
str_paths = []
356390
mpy_files = []
391+
new_files = []
357392
ts_newest = 0
358393
for kind, path, script, opt in manifest_list:
359394
if kind == KIND_AS_STR:
360395
str_paths.append(path)
361396
ts_outfile = get_timestamp_newest(path)
362397
elif kind == KIND_AS_MPY:
363398
infile = "{}/{}".format(path, script)
364-
outfile = "{}/frozen_mpy/{}.mpy".format(args.build_dir, script[:-3])
399+
subdir = "frozen_mpy" if output else ""
400+
outfile = "{}/{}/{}.mpy".format(build_dir, subdir, script[:-3])
365401
ts_infile = get_timestamp(infile)
366402
ts_outfile = get_timestamp(outfile, 0)
367403
if ts_infile >= ts_outfile:
368-
print("MPY", script)
404+
log("MPY", script)
369405
mkdir(outfile)
370406
res, out = system(
371407
[MPY_CROSS]
372-
+ args.mpy_cross_flags.split()
408+
+ mpy_cross_flags.split()
373409
+ ["-o", outfile, "-s", script, "-O{}".format(opt), infile]
374410
)
375411
if res != 0:
376412
print("error compiling {}:".format(infile))
377413
sys.stdout.buffer.write(out)
378414
raise SystemExit(1)
379415
ts_outfile = get_timestamp(outfile)
416+
new_files.append(outfile)
380417
mpy_files.append(outfile)
381418
else:
382419
assert kind == KIND_MPY
@@ -385,50 +422,53 @@ def main():
385422
ts_outfile = get_timestamp(infile)
386423
ts_newest = max(ts_newest, ts_outfile)
387424

388-
# Check if output file needs generating
389-
if ts_newest < get_timestamp(args.output, 0):
390-
# No files are newer than output file so it does not need updating
391-
return
392-
393-
# Freeze paths as strings
394-
output_str = generate_frozen_str_content(str_paths)
395-
396-
# Freeze .mpy files
397-
if mpy_files:
398-
res, output_mpy = system(
399-
[
400-
sys.executable,
401-
MPY_TOOL,
402-
"-f",
403-
"-q",
404-
args.build_dir + "/genhdr/qstrdefs.preprocessed.h",
405-
]
406-
+ args.mpy_tool_flags.split()
407-
+ mpy_files
408-
)
409-
if res != 0:
410-
print("error freezing mpy {}:".format(mpy_files))
411-
print(output_mpy.decode())
412-
sys.exit(1)
413-
else:
414-
output_mpy = (
415-
b'#include "py/emitglue.h"\n'
416-
b"extern const qstr_pool_t mp_qstr_const_pool;\n"
417-
b"const qstr_pool_t mp_qstr_frozen_const_pool = {\n"
418-
b" (qstr_pool_t*)&mp_qstr_const_pool, MP_QSTRnumber_of, 0, 0\n"
419-
b"};\n"
420-
b'const char mp_frozen_names[] = { MP_FROZEN_STR_NAMES "\\0"};\n'
421-
b"const mp_raw_code_t *const mp_frozen_mpy_content[] = {NULL};\n"
422-
)
423-
424-
# Generate output
425-
print("GEN", args.output)
426-
mkdir(args.output)
427-
with open(args.output, "wb") as f:
428-
f.write(b"//\n// Content for MICROPY_MODULE_FROZEN_STR\n//\n")
429-
f.write(output_str)
430-
f.write(b"//\n// Content for MICROPY_MODULE_FROZEN_MPY\n//\n")
431-
f.write(output_mpy)
425+
if output:
426+
# Check if output file needs generating
427+
if ts_newest < get_timestamp(output, 0):
428+
# No files are newer than output file so it does not need updating
429+
return
430+
431+
# Freeze paths as strings
432+
output_str = generate_frozen_str_content(str_paths)
433+
434+
# Freeze .mpy files
435+
if mpy_files:
436+
res, output_mpy = system(
437+
[
438+
sys.executable,
439+
MPY_TOOL,
440+
"-f",
441+
"-q",
442+
build_dir + "/genhdr/qstrdefs.preprocessed.h",
443+
]
444+
+ mpy_tool_flags.split()
445+
+ mpy_files
446+
)
447+
if res != 0:
448+
print("error freezing mpy {}:".format(mpy_files))
449+
print(output_mpy.decode())
450+
sys.exit(1)
451+
else:
452+
output_mpy = (
453+
b'#include "py/emitglue.h"\n'
454+
b"extern const qstr_pool_t mp_qstr_const_pool;\n"
455+
b"const qstr_pool_t mp_qstr_frozen_const_pool = {\n"
456+
b" (qstr_pool_t*)&mp_qstr_const_pool, MP_QSTRnumber_of, 0, 0\n"
457+
b"};\n"
458+
b'const char mp_frozen_names[] = { MP_FROZEN_STR_NAMES "\\0"};\n'
459+
b"const mp_raw_code_t *const mp_frozen_mpy_content[] = {NULL};\n"
460+
)
461+
462+
# Generate output
463+
log("GEN {}".format(output))
464+
mkdir(output)
465+
with open(output, "wb") as f:
466+
f.write(b"//\n// Content for MICROPY_MODULE_FROZEN_STR\n//\n")
467+
f.write(output_str)
468+
f.write(b"//\n// Content for MICROPY_MODULE_FROZEN_MPY\n//\n")
469+
f.write(output_mpy)
470+
471+
return mpy_files, new_files
432472

433473

434474
if __name__ == "__main__":

tools/mpremote/mpremote/main.py

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,12 @@
4242
"disconnect": (False, False, 0, "disconnect current device"),
4343
"resume": (False, False, 0, "resume a previous mpremote session (will not auto soft-reset)"),
4444
"soft-reset": (False, True, 0, "perform a soft-reset of the device"),
45+
"manifest": (
46+
True,
47+
False,
48+
1,
49+
"compile manifest.py",
50+
),
4551
"mount": (True, False, 1, "mount local directory on device"),
4652
"umount": (True, False, 0, "unmount the local directory"),
4753
"repl": (
@@ -275,7 +281,7 @@ def do_filesystem(pyb, args):
275281
args.pop(0)
276282
assert args[-1] == ":"
277283
args.pop()
278-
cp_recursive(args)
284+
pyb.cp_recursive(args)
279285
else:
280286
pyboard.filesystem_command(pyb, args)
281287
args.clear()
@@ -517,6 +523,9 @@ def main():
517523
elif cmd == "soft-reset":
518524
pyb.enter_raw_repl(soft_reset=True)
519525
auto_soft_reset = False
526+
elif cmd == "manifest":
527+
path = args.pop(0)
528+
pyb.build_manifest(path)
520529
elif cmd == "mount":
521530
path = args.pop(0)
522531
pyb.mount_local(path)

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy