8000 mpy-cross/mpy_cross: Multiple backend support for mpy-cross. · micropython/micropython@24b3cc8 · GitHub
[go: up one dir, main page]

Skip to content {"props":{"docsUrl":"https://docs.github.com/get-started/accessibility/keyboard-shortcuts"}}

Commit 24b3cc8

Browse files
committed
mpy-cross/mpy_cross: Multiple backend support for mpy-cross.
This adds support for using either a native or WASM binary in the Python wrapper for mpy-cross. This work was funded through GitHub Sponsors. Signed-off-by: Jim Mussared <jim.mussared@gmail.com>
1 parent a4a6047 commit 24b3cc8

File tree

6 files changed

+486
-120
lines changed

6 files changed

+486
-120
lines changed

mpy-cross/mpy_cross/__init__.py

Lines changed: 20 additions & 114 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
#!/usr/bin/env python3
2-
#
31
# This file is part of the MicroPython project, http://micropython.org/
42
#
53
# The MIT License (MIT)
@@ -25,127 +23,35 @@
2523
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
2624
# THE SOFTWARE.
2725

28-
from __future__ import print_function
29-
import os
30-
import re
31-
import stat
32-
import subprocess
33-
34-
NATIVE_ARCHS = {
35-
"NATIVE_ARCH_NONE": "",
36-
"NATIVE_ARCH_X86": "x86",
37-
"NATIVE_ARCH_X64": "x64",
38-
"NATIVE_ARCH_ARMV6": "armv6",
39-
"NATIVE_ARCH_ARMV6M": "armv6m",
40-
"NATIVE_ARCH_ARMV7M": "armv7m",
41-
"NATIVE_ARCH_ARMV7EM": "armv7em",
42-
"NATIVE_ARCH_ARMV7EMSP": "armv7emsp",
43-
"NATIVE_ARCH_ARMV7EMDP": "armv7emdp",
44-
"NATIVE_ARCH_XTENSA": "xtensa",
45-
"NATIVE_ARCH_XTENSAWIN": "xtensawin",
46-
}
47-
48-
globals().update(NATIVE_ARCHS)
49-
50-
__all__ = ["version", "compile", "run", "CrossCompileError"] + list(NATIVE_ARCHS.keys())
51-
52-
53-
class CrossCompileError(Exception):
54-
pass
55-
56-
57-
_VERSION_RE = re.compile("mpy-cross emitting mpy v([0-9]+)(?:.([0-9]+))?")
58-
59-
60-
def _find_mpy_cross_binary(mpy_cross):
61-
if mpy_cross:
62-
return mpy_cross
63-
return os.path.abspath(os.path.join(os.path.dirname(__file__), "../build/mpy-cross"))
64-
65-
66-
def mpy_version(mpy_cross=None):
67-
"""
68-
Get the version and sub-version of the .mpy file format generated by this version of mpy-cross.
69-
70-
Returns: A tuple of `(mpy_version, mpy_sub_version)`
71-
Optional keyword arguments:
72-
- mpy_cross: Specific mpy-cross binary to use
73-
"""
74-
version_info = run(["--version"], mpy_cross=mpy_cross)
75-
match = re.search(_VERSION_RE, version_info)
76-
mpy_version, mpy_sub_version = int(match.group(1)), int(match.group(2) or "0")
77-
return (
78-
mpy_version,
79-
mpy_sub_version,
80-
)
81-
82-
83-
def compile(src, dest=None, src_path=None, opt=None, march=None, mpy_cross=None, extra_args=None):
84-
"""
85-
Compile the specified .py file with mpy-cross.
86-
87-
Returns: Standard output from mpy-cross as a string.
88-
89-
Required arguments:
90-
- src: The path to the .py file
91-
92-
Optional keyword arguments:
93-
- dest: The output .mpy file. Defaults to `src` (with .mpy extension)
94-
- src_path: The path to embed in the .mpy file (defaults to `src`)
95-
- opt: Optimisation level (0-3, default 0)
96-
- march: One of the `NATIVE_ARCH_*` constants (defaults to NATIVE_ARCH_NONE)
97-
- mpy_cross: Specific mpy-cross binary to use
98-
- extra_args: Additional arguments to pass to mpy-cross (e.g. `["-X", "emit=native"]`)
99-
"""
100-
if not src:
101-
raise ValueError("src is required")
102-
if not os.path.exists(src):
103-
raise CrossCompileError("Input .py file not found: {}.".format(src_py))
104-
105-
args = []
106-
107-
if src_path:
108-
args += ["-s", src_path]
109-
110-
if dest:
111-
args += ["-o", dest]
11226

113-
if march:
114-
args += ["-march=" + march]
27+
from .compiler import *
11528

116-
if opt is not None:
117-
args += ["-O{}".format(opt)]
11829

119-
if extra_args:
120-
args += extra_args
30+
def mpy_version():
31+
return default_compiler().mpy_version()
12132

122-
args += [src]
12333

124-
run(args, mpy_cross)
34+
def version():
35+
return default_compiler().version()
12536

12637

127-
def run(args, mpy_cross=None):
128-
"""
129-
Run mpy-cross with the specified command line arguments.
130-
Prefer to use `compile()` instead.
38+
def compile(*args, **kwargs):
39+
compiler_kwargs = {}
13140

132-
Returns: Standard output from mpy-cross as a string.
41+
if "mpy_cross" in kwargs:
42+
compiler_kwargs.update(binary=kwargs["mpy_cross"])
43+
del kwargs["mpy_cross"]
13344

134-
Optional keyword arguments:
135-
- mpy_cross: Specific mpy-cross binary to use
136-
"""
137-
mpy_cross = _find_mpy_cross_binary(mpy_cross)
45+
if "extra_args" in kwargs:
46+
for arg in kwargs["extra_args"]:
47+
if arg.startswith("-march="):
48+
kwargs.update(march=arg[7:])
49+
else:
50+
raise ValueError("Unknown mpy-cross arg: {}".format(arg))
51+
del kwargs["extra_args"]
13852

139-
if not os.path.exists(mpy_cross):
140-
raise CrossCompileError("mpy-cross binary not found at {}.".format(mpy_cross))
53+
return default_compiler(**compiler_kwargs).compile(*args, **kwargs)
14154

142-
try:
143-
st = os.stat(mpy_cross)
144-
os.chmod(mpy_cross, st.st_mode | stat.S_IEXEC)
145-
except OSError:
146-
pass
14755

148-
try:
149-
return subprocess.check_output([mpy_cross] + args, stderr=subprocess.STDOUT).decode()
150-
except subprocess.CalledProcessError as er:
151-
raise CrossCompileError(er.output.decode())
56+
def description():
57+
return default_compiler().description()

mpy-cross/mpy_cross/__main__.py

Lines changed: 77 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,9 @@
1-
#!/usr/bin/env python3
2-
#
31
# This file is part of the MicroPython project, http://micropython.org/
42
#
53
# The MIT License (MIT)
64
#
75
# Copyright (c) 2022 Andrew Leech
8-
# Copyright (c) 2022 Jim Mussared
6+
# Copyright (c) 2023 Jim Mussared
97
#
108
# Permission is hereby granted, free of charge, to any person obtaining a copy
119
# of this software and associated documentation files (the "Software"), to deal
@@ -25,14 +23,87 @@
2523
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
2624
# THE SOFTWARE.
2725

28-
from __future__ import print_function
2926
import argparse
3027
import sys
3128

32-
from . import run, CrossCompileError
29+
from .compiler import (
30+
default_compiler,
31+
CrossCompileError,
32+
EMIT_BYTECODE,
33+
EMIT_NATIVE,
34+
EMIT_VIPER,
35+
NATIVE_ARCHS,
36+
)
37+
38+
march_opts = [v for v in NATIVE_ARCHS.values() if v]
39+
emit_opts = [EMIT_BYTECODE, EMIT_NATIVE, EMIT_VIPER]
40+
41+
cmd_parser = argparse.ArgumentParser(description="MicroPython cross-compiler")
42+
cmd_parser.add_argument(
43+
"-v", "--version", default=False, action="store_true", help="Display version information"
44+
)
45+
cmd_parser.add_argument(
46+
"-o",
47+
"--output",
48+
type=str,
49+
help="output file for compiled bytecode (defaults to input with .mpy extension)",
50+
)
51+
cmd_parser.add_argument(
52+
"-s",
53+
"--source",
54+
type=str,
55+
help="source filename to embed in the compiled bytecode (defaults to input file)",
56+
)
57+
cmd_parser.add_argument(
58+
"-march",
59+
"--march",
60+
type=str,
61+
choices=march_opts,
62+
metavar="MARCH",
63+
help="set architecture for native emitter; " + ", ".join(march_opts),
64+
)
65+
cmd_parser.add_argument(
66+
"-O",
67+
"--opt",
68+
type=int,
69+
choices=range(0, 4),
70+
metavar="N",
71+
help="apply bytecode optimizations of level N",
72+
)
73+
cmd_parser.add_argument(
74+
"-X",
75+
metavar="emit=EMIT",
76+
choices=["emit=" + x for x in emit_opts],
77+
help="set the default code emitter; " + ", ".join(emit_opts),
78+
)
79+
cmd_parser.add_argument("file", nargs="?", help="input filename")
80+
args = cmd_parser.parse_args()
81+
82+
try:
83+
c = default_compiler()
84+
except RuntimeError as e:
85+
print("Error: " + str(e), file=sys.stderr)
86+
sys.exit(1)
87+
88+
if args.version:
89+
print("{} ({})".format(c.version(), c.description()))
90+
v, s = c.mpy_version()
91+
print("mpy-cross emitting mpy v{}.{}".format(v, s))
92+
sys.exit(0)
93+
94+
if not args.file:
95+
print("Error: Input filename is required", file=sys.stderr)
96+
sys.exit(1)
3 1241 397

3498
try:
35-
print(run(sys.argv[1:]))
99+
kwargs = {}
100+
if args.march:
101+
kwargs.update(march=args.march)
102+
if args.X:
103+
kwargs.update(emit=args.X.split("=")[1])
104+
if args.opt:
105+
kwargs.update(opt=args.opt)
106+
c.compile(args.file, src_path=args.source, dest=args.output, **kwargs)
36107
except CrossCompileError as er:
37108
print(er.args[0], file=sys.stderr)
38109
raise SystemExit(1)

0 commit comments

Comments
 (0)
0