cibuildmp 0.3.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (65) hide show
  1. cibuildmp-0.3.0/.github/actions/build-natmod/action.yml +133 -0
  2. cibuildmp-0.3.0/.github/actions/build-usermod-armv7m/action.yml +98 -0
  3. cibuildmp-0.3.0/.github/actions/build-usermod-esp32/action.yml +162 -0
  4. cibuildmp-0.3.0/.github/actions/build-usermod-rp2040/action.yml +145 -0
  5. cibuildmp-0.3.0/.github/actions/build-usermod-unix/action.yml +177 -0
  6. cibuildmp-0.3.0/.github/actions/build-usermod-webassembly/action.yml +114 -0
  7. cibuildmp-0.3.0/.github/actions/build-usermod-windows/action.yml +133 -0
  8. cibuildmp-0.3.0/.github/actions/cibuildmp-matrix/action.yml +73 -0
  9. cibuildmp-0.3.0/.github/actions/clone-micropython/action.yml +45 -0
  10. cibuildmp-0.3.0/.github/actions/fetch-micropython/action.yml +24 -0
  11. cibuildmp-0.3.0/.github/dependabot.yml +33 -0
  12. cibuildmp-0.3.0/.github/workflows/build-examples.yml +65 -0
  13. cibuildmp-0.3.0/.github/workflows/publish.yml +207 -0
  14. cibuildmp-0.3.0/.gitignore +28 -0
  15. cibuildmp-0.3.0/CHANGELOG.md +311 -0
  16. cibuildmp-0.3.0/LICENSE +21 -0
  17. cibuildmp-0.3.0/PKG-INFO +528 -0
  18. cibuildmp-0.3.0/README.md +496 -0
  19. cibuildmp-0.3.0/action.yml +66 -0
  20. cibuildmp-0.3.0/cibuildmp.toml +137 -0
  21. cibuildmp-0.3.0/docs/BACKLOG.md +889 -0
  22. cibuildmp-0.3.0/examples/template/cibuildmp.toml +43 -0
  23. cibuildmp-0.3.0/examples/template/natmod/Makefile +35 -0
  24. cibuildmp-0.3.0/examples/template/natmod/template.c +21 -0
  25. cibuildmp-0.3.0/examples/template/src/facade.py +13 -0
  26. cibuildmp-0.3.0/examples/wasm2mpy/Makefile +66 -0
  27. cibuildmp-0.3.0/examples/wasm2mpy/NOTICE +32 -0
  28. cibuildmp-0.3.0/examples/wasm2mpy/cibuildmp.toml +33 -0
  29. cibuildmp-0.3.0/examples/wasm2mpy/runtime/esp8266-rom.S +562 -0
  30. cibuildmp-0.3.0/examples/wasm2mpy/runtime/libc.c +100 -0
  31. cibuildmp-0.3.0/examples/wasm2mpy/runtime/runtime.c +131 -0
  32. cibuildmp-0.3.0/examples/wasm2mpy/runtime/wasm-rt-impl-tableops.inc +78 -0
  33. cibuildmp-0.3.0/examples/wasm2mpy/runtime/wasm-rt-impl.c +294 -0
  34. cibuildmp-0.3.0/examples/wasm2mpy/runtime/wasm-rt-impl.h +66 -0
  35. cibuildmp-0.3.0/examples/wasm2mpy/runtime/wasm-rt-mem-impl-helper.inc +169 -0
  36. cibuildmp-0.3.0/examples/wasm2mpy/runtime/wasm-rt-mem-impl.c +176 -0
  37. cibuildmp-0.3.0/examples/wasm2mpy/runtime/wasm-rt.h +624 -0
  38. cibuildmp-0.3.0/examples/wasm2mpy/test/zig.wasm +0 -0
  39. cibuildmp-0.3.0/pyproject.toml +85 -0
  40. cibuildmp-0.3.0/setup.cfg +4 -0
  41. cibuildmp-0.3.0/src/cibuildmp/__init__.py +19 -0
  42. cibuildmp-0.3.0/src/cibuildmp/__main__.py +4 -0
  43. cibuildmp-0.3.0/src/cibuildmp/build.py +300 -0
  44. cibuildmp-0.3.0/src/cibuildmp/cli.py +367 -0
  45. cibuildmp-0.3.0/src/cibuildmp/options.py +267 -0
  46. cibuildmp-0.3.0/src/cibuildmp/resources/natmod.toml +167 -0
  47. cibuildmp-0.3.0/src/cibuildmp/resources.py +22 -0
  48. cibuildmp-0.3.0/src/cibuildmp/sources.py +275 -0
  49. cibuildmp-0.3.0/src/cibuildmp/targets.py +259 -0
  50. cibuildmp-0.3.0/src/cibuildmp/toolchains.py +302 -0
  51. cibuildmp-0.3.0/src/cibuildmp.egg-info/PKG-INFO +528 -0
  52. cibuildmp-0.3.0/src/cibuildmp.egg-info/SOURCES.txt +63 -0
  53. cibuildmp-0.3.0/src/cibuildmp.egg-info/dependency_links.txt +1 -0
  54. cibuildmp-0.3.0/src/cibuildmp.egg-info/entry_points.txt +2 -0
  55. cibuildmp-0.3.0/src/cibuildmp.egg-info/requires.txt +2 -0
  56. cibuildmp-0.3.0/src/cibuildmp.egg-info/scm_file_list.json +59 -0
  57. cibuildmp-0.3.0/src/cibuildmp.egg-info/scm_version.json +8 -0
  58. cibuildmp-0.3.0/src/cibuildmp.egg-info/top_level.txt +1 -0
  59. cibuildmp-0.3.0/tests/test_build.py +328 -0
  60. cibuildmp-0.3.0/tests/test_cli.py +76 -0
  61. cibuildmp-0.3.0/tests/test_options.py +230 -0
  62. cibuildmp-0.3.0/tests/test_sources.py +112 -0
  63. cibuildmp-0.3.0/tests/test_targets.py +154 -0
  64. cibuildmp-0.3.0/tests/test_toolchains.py +97 -0
  65. cibuildmp-0.3.0/uv.lock +166 -0
@@ -0,0 +1,133 @@
1
+ name: Build natmod arch
2
+ description: >
3
+ Installs whatever cross toolchain a single dynruntime.mk ARCH needs (a
4
+ plain apt package, the xtensa-lx106 tarball, or esp-idf -- picked per
5
+ ARCH, matching dynruntime.mk's own CROSS choices), builds mpy-cross, then
6
+ runs `make ARCH=<arch> dist` in the natmod directory.
7
+
8
+ Requires MPY_DIR to already be exported (e.g. by the fetch-micropython or
9
+ clone-micropython action from this same repo) and the calling repo's own
10
+ checkout to already have happened, submodules included if the natmod
11
+ Makefile needs any.
12
+
13
+ inputs:
14
+ arch:
15
+ description: >
16
+ dynruntime.mk ARCH to build: x64, x86, armv6m, armv7m, armv7emsp,
17
+ armv7emdp, rv32imc, rv64imc, xtensa, or xtensawin. (aarch64 has no
18
+ dynruntime.mk ARCH at all as of MicroPython <= v1.28 -- build it via
19
+ a usermod instead.)
20
+ required: true
21
+ natmod_dir:
22
+ description: Path to the directory containing natmod/Makefile, relative to the workspace root.
23
+ required: false
24
+ default: natmod
25
+ esp_idf_ver:
26
+ description: esp-idf tag to install for the xtensawin toolchain.
27
+ required: false
28
+ default: v5.4
29
+ extra_pip:
30
+ description: >
31
+ Extra space-separated pip packages to install alongside pyelftools/ar
32
+ (both always installed -- mpy_ld.py needs them for every ARCH).
33
+ required: false
34
+ default: ''
35
+ pre_build_command:
36
+ description: >
37
+ Optional shell command run once inside natmod_dir, after mpy-cross is
38
+ built and before `make dist` -- e.g. a7p's `make fetch-nanopb`, which
39
+ the natmod Makefile needs vendored in before it can build any ARCH.
40
+ required: false
41
+ default: ''
42
+
43
+ runs:
44
+ using: composite
45
+ steps:
46
+ - name: Install cross-compiler
47
+ shell: bash
48
+ run: |
49
+ set -e
50
+ case "${{ inputs.arch }}" in
51
+ x64|xtensa|xtensawin)
52
+ # x64 needs no cross toolchain at all; xtensa/xtensawin are
53
+ # handled by their own steps below, not a plain apt package.
54
+ exit 0
55
+ ;;
56
+ x86)
57
+ PKGS="gcc-multilib"
58
+ ;;
59
+ armv6m|armv7m|armv7emsp|armv7emdp)
60
+ PKGS="gcc-arm-none-eabi libnewlib-arm-none-eabi"
61
+ ;;
62
+ rv32imc|rv64imc)
63
+ PKGS="gcc-riscv64-unknown-elf picolibc-riscv64-unknown-elf"
64
+ ;;
65
+ *)
66
+ echo "::error::build-natmod: unknown ARCH '${{ inputs.arch }}'"
67
+ exit 1
68
+ ;;
69
+ esac
70
+ # apt-get update first: the runner image's baked-in package index
71
+ # can already be stale by the time a job runs, pointing at a
72
+ # gcc/glibc revision the mirror has since superseded (404 Not Found
73
+ # on install rather than a version error) -- seen for real on the
74
+ # x86 leg across every repo this action was extracted from.
75
+ sudo apt-get update -q
76
+ sudo apt-get install -y $PKGS
77
+
78
+ - name: Install xtensa (esp8266) toolchain
79
+ if: inputs.arch == 'xtensa'
80
+ shell: bash
81
+ # Same tarball MicroPython's own ci_esp8266_setup uses -- no apt
82
+ # package exists for this target.
83
+ run: |
84
+ wget -q https://micropython.org/resources/xtensa-lx106-elf-standalone.tar.gz
85
+ tar xzf xtensa-lx106-elf-standalone.tar.gz
86
+ echo "$PWD/xtensa-lx106-elf/bin" >> "$GITHUB_PATH"
87
+
88
+ - name: Install esp-idf (xtensawin toolchain)
89
+ if: inputs.arch == 'xtensawin'
90
+ shell: bash
91
+ # Deliberately not `git clone --recursive`: this only ever wants the
92
+ # xtensa-esp32s3-elf- toolchain that install.sh downloads separately
93
+ # from GitHub releases, not a full esp32 port build against IDF's own
94
+ # components (bt/mbedtls/lwip/openthread/...) -- so nothing here
95
+ # touches IDF_PATH or any esp-idf header/component, and the
96
+ # submodule init (most of a --recursive clone's cost) buys nothing.
97
+ env:
98
+ IDF_VER: ${{ inputs.esp_idf_ver }}
99
+ run: |
100
+ git clone --depth 1 --branch "$IDF_VER" https://github.com/espressif/esp-idf.git
101
+ ./esp-idf/install.sh
102
+
103
+ - name: Install Python deps for mpy_ld.py
104
+ shell: bash
105
+ run: pip install pyelftools ar ${{ inputs.extra_pip }}
106
+
107
+ - name: Build mpy-cross
108
+ shell: bash
109
+ run: make -C "$MPY_DIR/mpy-cross"
110
+
111
+ - name: Build ${{ inputs.arch }}
112
+ shell: bash
113
+ working-directory: ${{ inputs.natmod_dir }}
114
+ # esp-idf's own venv shadows the system pip install above the moment
115
+ # export.sh is sourced -- mpy_ld.py then runs under *that* python, so
116
+ # pyelftools/ar have to be reinstalled inside this same shell right
117
+ # before the build, not just once earlier in the job. A real failure
118
+ # without it: "RuntimeError: Please run 'pip install ar' to link .a
119
+ # files" even though the step above already ran.
120
+ # pre_build_command runs via `${{ ... || ':' }}` rather than an
121
+ # `if [ -n ... ]; then ... fi` guard: when the input is empty, that
122
+ # guard's then-body would be blank text with nothing else on the
123
+ # line, and bash rejects an empty compound-command body at parse
124
+ # time ("syntax error near unexpected token `fi'") even though the
125
+ # condition is false and the body would never run. `:` is a real,
126
+ # always-present no-op command, so the line is never empty.
127
+ run: |
128
+ if [ "${{ inputs.arch }}" = "xtensawin" ]; then
129
+ source "$GITHUB_WORKSPACE/esp-idf/export.sh"
130
+ pip install pyelftools ar ${{ inputs.extra_pip }}
131
+ fi
132
+ ${{ inputs.pre_build_command || ':' }}
133
+ make ARCH=${{ inputs.arch }} MPY_DIR="$MPY_DIR" dist
@@ -0,0 +1,98 @@
1
+ name: Build usermod armv7m
2
+ description: >
3
+ Builds a MicroPython ports/qemu "usermod" (a C module compiled straight
4
+ into the port via USER_C_MODULES=) into a firmware.elf for the given
5
+ BOARD= (default MPS2_AN385, a Cortex-M3). Installs the arm-none-eabi
6
+ toolchain, builds mpy-cross, then runs the port build under it.
7
+
8
+ QEMU itself is deliberately NOT installed here: it is a runtime emulator
9
+ for testing the resulting firmware.elf, not a build dependency -- same
10
+ split build-usermod-rp2040 uses for the rp2040py emulator. Callers
11
+ install qemu-system-arm (and whatever their own test harness needs,
12
+ e.g. pyserial) themselves, alongside their own run_qemu.py-equivalent.
13
+
14
+ Requires MPY_DIR to already be exported (fetch-micropython or
15
+ clone-micropython) and the calling repo already checked out, same
16
+ contract as build-usermod-unix/build-usermod-rp2040.
17
+
18
+ inputs:
19
+ board:
20
+ description: Value for BOARD=. Defaults to MPS2_AN385 (Cortex-M3, no FPU).
21
+ required: false
22
+ default: MPS2_AN385
23
+ user_c_modules:
24
+ description: Value for USER_C_MODULES=. Defaults to the workspace root.
25
+ required: false
26
+ default: ''
27
+ frozen_manifest:
28
+ description: >
29
+ Value for FROZEN_MANIFEST=. Defaults to usermod/manifest.py under the
30
+ workspace root. ports/qemu ships no boards/manifest.py of its own --
31
+ unlike unix/rp2/esp32, there is no port default to combine with, so a
32
+ caller-written manifest here only ever needs to freeze its own module.
33
+ required: false
34
+ default: ''
35
+ extra_make_args:
36
+ description: >
37
+ Extra space-separated VAR=value pairs appended to the build command,
38
+ e.g. a module's own precision define.
39
+ required: false
40
+ default: ''
41
+ build_dir:
42
+ description: >
43
+ Value for BUILD=. Defaults to usermod/build/armv7m under the
44
+ workspace root. A bare relative value (no leading /) resolves against
45
+ $MPY_DIR/ports/qemu instead, same as make -C's own relative-path
46
+ handling -- pass one when the caller wants the port's own
47
+ board-named default (build-$(BOARD)) instead.
48
+ required: false
49
+ default: ''
50
+
51
+ outputs:
52
+ build_dir:
53
+ description: >
54
+ The BUILD= directory actually used (resolved default included), so
55
+ the caller can find firmware.elf without recomputing it.
56
+ value: ${{ steps.vars.outputs.build_dir }}
57
+
58
+ runs:
59
+ using: composite
60
+ steps:
61
+ - name: Resolve build settings
62
+ id: vars
63
+ shell: bash
64
+ run: |
65
+ set -e
66
+ BUILD="${{ inputs.build_dir }}"
67
+ FM="${{ inputs.frozen_manifest }}"
68
+ UCM="${{ inputs.user_c_modules }}"
69
+ {
70
+ echo "user_c_modules=${UCM:-$GITHUB_WORKSPACE}"
71
+ echo "frozen_manifest=${FM:-$GITHUB_WORKSPACE/usermod/manifest.py}"
72
+ echo "build_dir=${BUILD:-$GITHUB_WORKSPACE/usermod/build/armv7m}"
73
+ } >> "$GITHUB_OUTPUT"
74
+
75
+ - name: Install toolchain
76
+ shell: bash
77
+ # apt-get update first -- a bare apt-get install against the runner
78
+ # image's stale package index intermittently 404s on a mirror.
79
+ run: |
80
+ sudo apt-get update -q
81
+ sudo apt-get install -y --no-install-recommends \
82
+ gcc-arm-none-eabi libnewlib-arm-none-eabi
83
+
84
+ - name: Build mpy-cross
85
+ shell: bash
86
+ # Explicitly, before FROZEN_MANIFEST ever appears on a command line --
87
+ # py/mkenv.mk's auto-build rule for it races/leaks under -j. Same fix
88
+ # every other action here uses.
89
+ run: make -C "$MPY_DIR/mpy-cross" -j"$(nproc)"
90
+
91
+ - name: Build usermod (armv7m, ${{ inputs.board }})
92
+ shell: bash
93
+ run: |
94
+ make -C "$MPY_DIR/ports/qemu" BOARD="${{ inputs.board }}" \
95
+ BUILD="${{ steps.vars.outputs.build_dir }}" \
96
+ USER_C_MODULES="${{ steps.vars.outputs.user_c_modules }}" \
97
+ FROZEN_MANIFEST="${{ steps.vars.outputs.frozen_manifest }}" \
98
+ ${{ inputs.extra_make_args }} -j"$(nproc)"
@@ -0,0 +1,162 @@
1
+ name: Build usermod esp32
2
+ description: >
3
+ Builds a MicroPython ports/esp32 "usermod" (a C module compiled straight
4
+ into the port via USER_C_MODULES=) into a firmware.bin/micropython.bin
5
+ for the given BOARD= (default ESP32_GENERIC). Installs ESP-IDF, builds
6
+ mpy-cross, then runs the port build under it.
7
+
8
+ --recursive on the esp-idf clone, unlike a natmod build of the same
9
+ chip: a natmod only borrows the xtensa compiler install.sh downloads,
10
+ while a usermod compiles into a firmware built against IDF's own
11
+ components. install.sh <idf_target> limits the toolchain download to
12
+ the one chip family rather than every one IDF supports.
13
+
14
+ idf_target and board are deliberately separate inputs, not derived from
15
+ one another: install.sh's argument is the chip family (esp32, esp32s3,
16
+ ...), while BOARD= is the actual MicroPython board definition -- more
17
+ than one board can exist per chip family, so string-deriving one from
18
+ the other would silently break the day a second board per chip shows up.
19
+
20
+ No caching yet: every consumer's original recipe had none either, so
21
+ this preserves behavior exactly rather than mixing an extraction with a
22
+ new capability. Left as a known follow-up, not forgotten -- see this
23
+ repo's own README/CHANGELOG.
24
+
25
+ Requires MPY_DIR to already be exported (fetch-micropython or
26
+ clone-micropython) and the calling repo already checked out, same
27
+ contract as build-usermod-unix/build-usermod-rp2040.
28
+
29
+ inputs:
30
+ board:
31
+ description: Value for BOARD=. Defaults to ESP32_GENERIC, the mainstream target.
32
+ required: false
33
+ default: ESP32_GENERIC
34
+ idf_target:
35
+ description: >
36
+ Chip family passed to esp-idf's install.sh (e.g. esp32, esp32s3).
37
+ Defaults to esp32. Kept separate from board -- see this action's own
38
+ header for why they must not be derived from one another.
39
+ required: false
40
+ default: esp32
41
+ idf_ver:
42
+ description: ESP-IDF version tag to clone (e.g. v5.5.1).
43
+ required: false
44
+ default: v5.5.1
45
+ user_c_modules:
46
+ description: >
47
+ Value for USER_C_MODULES=. Defaults to usermod/micropython.cmake
48
+ under the workspace root -- a *file*, like build-usermod-rp2040's own
49
+ user_c_modules: CMake's USER_C_MODULES takes a single .cmake entry
50
+ point, not a directory to glob.
51
+ required: false
52
+ default: ''
53
+ frozen_manifest:
54
+ description: Value for FROZEN_MANIFEST=. Defaults to usermod/manifest.py under the workspace root.
55
+ required: false
56
+ default: ''
57
+ extra_make_args:
58
+ description: >
59
+ Extra space-separated VAR=value pairs appended to the build command,
60
+ e.g. a module's own precision define.
61
+ required: false
62
+ default: ''
63
+
64
+ outputs:
65
+ build_dir:
66
+ description: >
67
+ The port's own default build-$(BOARD) directory (relative to
68
+ $MPY_DIR/ports/esp32), so the caller can find
69
+ micropython.bin/firmware.bin without recomputing it. No build_dir
70
+ input to override this: see the "Build usermod" step's own comment
71
+ for why passing BUILD= at all -- not what it's set to -- breaks the
72
+ port's internal CMake-driven mpy-cross sub-build.
73
+ value: ${{ steps.vars.outputs.build_dir }}
74
+
75
+ runs:
76
+ using: composite
77
+ steps:
78
+ - name: Resolve build settings
79
+ id: vars
80
+ shell: bash
81
+ run: |
82
+ set -e
83
+ FM="${{ inputs.frozen_manifest }}"
84
+ UCM="${{ inputs.user_c_modules }}"
85
+ {
86
+ echo "user_c_modules=${UCM:-$GITHUB_WORKSPACE/usermod/micropython.cmake}"
87
+ echo "frozen_manifest=${FM:-$GITHUB_WORKSPACE/usermod/manifest.py}"
88
+ echo "build_dir=build-${{ inputs.board }}"
89
+ } >> "$GITHUB_OUTPUT"
90
+
91
+ - name: Install esp-idf
92
+ shell: bash
93
+ run: |
94
+ git clone --depth 1 --recursive --branch "${{ inputs.idf_ver }}" \
95
+ https://github.com/espressif/esp-idf.git
96
+ ./esp-idf/install.sh "${{ inputs.idf_target }}"
97
+
98
+ - name: Build mpy-cross
99
+ shell: bash
100
+ # Before export.sh is sourced anywhere: mpy-cross is a host tool and
101
+ # has no business being built inside IDF's own python/toolchain
102
+ # environment. Explicit for the usual reason -- see build-usermod-unix's
103
+ # own comment on py/mkenv.mk's auto-build rule.
104
+ run: make -C "$MPY_DIR/mpy-cross" -j"$(nproc)"
105
+
106
+ - name: Build usermod (esp32, ${{ inputs.board }})
107
+ shell: bash
108
+ # source + make in the same step, not two: export.sh's env doesn't
109
+ # survive a composite action step boundary (each step is a fresh
110
+ # shell invocation), unlike $GITHUB_ENV-based exports.
111
+ # No BUILD= override, even though it would resolve to the exact same
112
+ # value (build-$(BOARD)) the port already defaults to: a real CI
113
+ # failure showed that passing it explicitly -- not what it's set to,
114
+ # the mere presence of BUILD= on the command line -- makes the port's
115
+ # own internal CMake-driven mpy-cross sub-build (build-$(BOARD)/mpy-cross,
116
+ # a separate copy from $MPY_DIR/mpy-cross above) pick up
117
+ # FROZEN_MANIFEST through MAKEFLAGS and fail with "undefined reference
118
+ # to mp_qstr_frozen_const_pool" -- the same symptom the explicit
119
+ # pre-build of $MPY_DIR/mpy-cross exists to prevent, just not reachable
120
+ # by that fix here since this is CMake's own separate copy, not
121
+ # py/mkrules.mk's auto-build rule. Every real caller wants the port's
122
+ # own default anyway (see build_dir's own description above), so this
123
+ # isn't a capability loss in practice.
124
+ run: |
125
+ source esp-idf/export.sh
126
+ make -C "$MPY_DIR/ports/esp32" \
127
+ BOARD="${{ inputs.board }}" \
128
+ USER_C_MODULES="${{ steps.vars.outputs.user_c_modules }}" \
129
+ FROZEN_MANIFEST="${{ steps.vars.outputs.frozen_manifest }}" \
130
+ ${{ inputs.extra_make_args }}
131
+
132
+ - name: Dump the IDF build logs on failure
133
+ if: failure()
134
+ shell: bash
135
+ # idf.py redirects the compiler's own stderr into
136
+ # build-*/log/idf_py_stderr_output_<pid> and prints only "ninja failed
137
+ # with exit code 1" to the console, so a failing compile leaves no
138
+ # diagnostic in the Actions log at all -- found on a real failure
139
+ # (o-murphy/micropython-wasm3's esp32 job, stopped in
140
+ # wasm3/source/m3_compile.c with nothing to say why). Surfaced here so
141
+ # every caller gets this for free, not just the one that hit it first.
142
+ #
143
+ # The log files turned out not to carry the diagnostic at all: idf.py's
144
+ # idf_py_stderr_output_* holds only its own bookkeeping ("Command:
145
+ # ninja all"), and the stdout copy of ninja's stream shows the failing
146
+ # target and then "ninja: build stopped" with nothing in between. So
147
+ # dump them for context, then get the real answer by re-running ninja
148
+ # verbosely in the same build directory: everything is already built
149
+ # except the one object that failed, so this recompiles just that and
150
+ # prints both the exact command line and the compiler's own output.
151
+ # `|| true` so this step reports rather than re-failing the job.
152
+ run: |
153
+ BUILD="${{ steps.vars.outputs.build_dir }}"
154
+ for f in "$BUILD"/log/idf_py_std*_output_*; do
155
+ [ -f "$f" ] || continue
156
+ echo "::group::$f"
157
+ tail -n 60 "$f"
158
+ echo "::endgroup::"
159
+ done
160
+ echo "--- re-running ninja verbosely to surface the diagnostic:"
161
+ source esp-idf/export.sh
162
+ ninja -C "$BUILD" -v -k 0 2>&1 | tail -n 120 || true
@@ -0,0 +1,145 @@
1
+ name: Build usermod rp2040
2
+ description: >
3
+ Builds a MicroPython ports/rp2 "usermod" (a C module compiled straight
4
+ into the port via USER_C_MODULES=) into a firmware.uf2 for the given
5
+ BOARD=. Installs the arm-none-eabi + CMake toolchain, builds mpy-cross,
6
+ then runs the port build under it.
7
+
8
+ Requires MPY_DIR to already be exported (fetch-micropython -- proven
9
+ sufficient for this port, see below -- or clone-micropython) and the
10
+ calling repo already checked out, same contract as build-usermod-unix
11
+ and build-usermod-webassembly.
12
+
13
+ Checkout note for callers migrating off clone-micropython +
14
+ pico_sdk_submodules: neither is required. ports/rp2/CMakeLists.txt
15
+ redirects PICO_TINYUSB_PATH/PICO_LWIP_PATH/PICO_BTSTACK_PATH/
16
+ PICO_CYW43_DRIVER_PATH at ${MICROPY_DIR}/lib/<name> -- MicroPython's own
17
+ top-level submodules, which the release tarball already vendors -- not
18
+ at pico-sdk's own nested vendored copies, so pico-sdk's internal
19
+ submodule tree is never actually touched by this build. Proven green
20
+ first on o-murphy/micropython-wasm3's rp2 row (plain fetch-micropython,
21
+ no submodule handling at all), then on ballistics-lab/micropython-bclibc
22
+ and o-murphy/a7p after switching off their original clone-micropython +
23
+ explicit submodule list + pico_sdk_submodules: "true".
24
+
25
+ inputs:
26
+ board:
27
+ description: Value for BOARD=. Defaults to RPI_PICO.
28
+ required: false
29
+ default: RPI_PICO
30
+ user_c_modules:
31
+ description: >
32
+ Value for USER_C_MODULES=. Defaults to usermod/micropython.cmake
33
+ under the workspace root -- unlike build-usermod-unix/
34
+ build-usermod-webassembly's own user_c_modules, this one is a *file*,
35
+ not a directory: CMake's USER_C_MODULES takes a single .cmake entry
36
+ point to include, not a directory to glob.
37
+ required: false
38
+ default: ''
39
+ frozen_manifest:
40
+ description: Value for FROZEN_MANIFEST=. Defaults to usermod/manifest.py under the workspace root.
41
+ required: false
42
+ default: ''
43
+ extra_make_args:
44
+ description: >
45
+ Extra space-separated VAR=value pairs appended to the build command,
46
+ e.g. a module's own precision define.
47
+ required: false
48
+ default: ''
49
+ extra_cmake_args:
50
+ description: >
51
+ Extra arguments for a direct `cmake -S . -B <build_dir>` reconfigure
52
+ step run after the port's own first configure. Left empty (the
53
+ default), this action runs the port build in a single make
54
+ invocation. Needed because ports/rp2/Makefile builds its own cmake
55
+ arguments with `CMAKE_ARGS +=`, so a define passed straight on the
56
+ make command line replaces the whole accumulated set -- including
57
+ MICROPY_BOARD, USER_C_MODULES and MICROPY_FROZEN_MANIFEST -- rather
58
+ than adding to it. o-murphy/micropython-wasm3 needs
59
+ -DMICROPY_C_HEAP_SIZE=131072 here: rp2 defaults that to 0, and its
60
+ module allocates through the port's calloc(), which fails the
61
+ configure outright at 0.
62
+ required: false
63
+ default: ''
64
+ build_dir:
65
+ description: >
66
+ Value for BUILD=. Defaults to usermod/build/rp2040 under the
67
+ workspace root. A bare relative value (no leading /) resolves
68
+ against $MPY_DIR/ports/rp2 instead, same as make -C's own
69
+ relative-path handling -- pass one when the caller wants the port's
70
+ own board-named default (e.g. build-RPI_PICO) instead.
71
+ required: false
72
+ default: ''
73
+
74
+ outputs:
75
+ build_dir:
76
+ description: >
77
+ The BUILD= directory actually used (resolved default included), so
78
+ the caller can find firmware.uf2 without recomputing it.
79
+ value: ${{ steps.vars.outputs.build_dir }}
80
+
81
+ runs:
82
+ using: composite
83
+ steps:
84
+ - name: Resolve build settings
85
+ id: vars
86
+ shell: bash
87
+ run: |
88
+ set -e
89
+ BUILD="${{ inputs.build_dir }}"
90
+ FM="${{ inputs.frozen_manifest }}"
91
+ UCM="${{ inputs.user_c_modules }}"
92
+ {
93
+ echo "user_c_modules=${UCM:-$GITHUB_WORKSPACE/usermod/micropython.cmake}"
94
+ echo "frozen_manifest=${FM:-$GITHUB_WORKSPACE/usermod/manifest.py}"
95
+ echo "build_dir=${BUILD:-$GITHUB_WORKSPACE/usermod/build/rp2040}"
96
+ } >> "$GITHUB_OUTPUT"
97
+
98
+ - name: Install toolchain
99
+ shell: bash
100
+ # apt-get update first -- a bare apt-get install against the runner
101
+ # image's stale package index intermittently 404s on a mirror.
102
+ #
103
+ # libstdc++-arm-none-eabi-newlib: pico-sdk compiles one .cpp of its
104
+ # own, which fails with "cstdlib: No such file or directory" without
105
+ # it (a real CI failure on o-murphy/micropython-wasm3's rp2 row) --
106
+ # included here even though not every caller's build happens to
107
+ # exercise that file, since whether it does depends on pico-sdk
108
+ # internals no caller should have to know.
109
+ run: |
110
+ sudo apt-get update -q
111
+ sudo apt-get install -y \
112
+ cmake \
113
+ gcc-arm-none-eabi \
114
+ libnewlib-arm-none-eabi \
115
+ libstdc++-arm-none-eabi-newlib \
116
+ python3
117
+
118
+ - name: Build mpy-cross
119
+ shell: bash
120
+ # Explicitly, before FROZEN_MANIFEST ever appears on a command line --
121
+ # py/mkenv.mk's auto-build rule for mpy-cross resets USER_C_MODULES=
122
+ # for that recursive make but not FROZEN_MANIFEST=, which leaks
123
+ # through MAKEFLAGS and fails the sub-build. Same fix as
124
+ # build-usermod-unix/build-usermod-webassembly.
125
+ run: make -C "$MPY_DIR/mpy-cross"
126
+
127
+ - name: Build usermod (rp2040, ${{ inputs.board }})
128
+ shell: bash
129
+ # cd, not make -C: the extra_cmake_args branch below needs `cmake -S
130
+ # . -B $BUILD` to run from inside ports/rp2, same as
131
+ # o-murphy/micropython-wasm3's own recipe this was extracted from.
132
+ run: |
133
+ cd "$MPY_DIR/ports/rp2"
134
+ if [ -n "${{ inputs.extra_cmake_args }}" ]; then
135
+ make BOARD="${{ inputs.board }}" \
136
+ BUILD="${{ steps.vars.outputs.build_dir }}" \
137
+ USER_C_MODULES="${{ steps.vars.outputs.user_c_modules }}" \
138
+ FROZEN_MANIFEST="${{ steps.vars.outputs.frozen_manifest }}" || true
139
+ cmake -S . -B "${{ steps.vars.outputs.build_dir }}" ${{ inputs.extra_cmake_args }}
140
+ fi
141
+ make BOARD="${{ inputs.board }}" \
142
+ BUILD="${{ steps.vars.outputs.build_dir }}" \
143
+ USER_C_MODULES="${{ steps.vars.outputs.user_c_modules }}" \
144
+ FROZEN_MANIFEST="${{ steps.vars.outputs.frozen_manifest }}" \
145
+ ${{ inputs.extra_make_args }} -j"$(nproc)"