Skip to content

Commit d327ecd

Browse files
authored
Merge pull request #38 from sparkfun/feature/build_release
Feature/build release
2 parents 27bbff9 + b6b5958 commit d327ecd

File tree

3 files changed

+207
-20
lines changed

3 files changed

+207
-20
lines changed

.github/workflows/build.yml

Lines changed: 3 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
name: Build firmware
1+
name: Build Firmware
22

33
on:
44
pull_request:
@@ -7,7 +7,6 @@ on:
77
push:
88
branches:
99
- features_for_launch
10-
workflow_dispatch:
1110

1211
jobs:
1312
build:
@@ -17,21 +16,5 @@ jobs:
1716
uses: actions/checkout@v4
1817
with:
1918
submodules: true
20-
- name: Install packages
21-
run: |
22-
sudo apt install cmake python3 build-essential gcc-arm-none-eabi libnewlib-arm-none-eabi libstdc++-arm-none-eabi-newlib
23-
- name: Build MPY Cross
24-
run: make -C micropython/mpy-cross
25-
- name: MicroPython submodules
26-
run: make -C micropython/ports/rp2 BOARD=SPARKFUN_XRP_CONTROLLER submodules
27-
- name: Set Pico SDK path
28-
run: echo "PICO_SDK_PATH=$GITHUB_WORKSPACE/micropython/lib/pico-sdk" >> "$GITHUB_ENV"
29-
- name: Build OpenCV
30-
run: make -C src/opencv PLATFORM=rp2350 --no-print-directory -j4
31-
- name: Build firmware
32-
run: make BOARD=SPARKFUN_XRP_CONTROLLER -j4
33-
- name: Upload UF2
34-
uses: actions/upload-artifact@v4
35-
with:
36-
name: firmware.uf2
37-
path: micropython/ports/rp2/build-SPARKFUN_XRP_CONTROLLER-LARGE_BINARY/firmware.uf2
19+
- name: Build Firmware
20+
run: source build.sh && build_micropython_opencv

.github/workflows/release.yml

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
name: Build and Deploy Firmware Release
2+
3+
on:
4+
release:
5+
types: [created]
6+
7+
jobs:
8+
build:
9+
runs-on: ubuntu-22.04
10+
steps:
11+
- name: Checkout repository
12+
uses: actions/checkout@v4
13+
with:
14+
submodules: true
15+
- name: Build Firmware
16+
run: source build.sh && build_micropython_opencv
17+
- name: Upload Release Assets
18+
uses: shogo82148/actions-upload-release-asset@v1
19+
with:
20+
asset_path: "micropython/ports/rp2/build-SPARKFUN_XRP_CONTROLLER-LARGE_BINARY/MICROPYTHON_OPENCV_SPARKFUN_XRP_CONTROLLER.uf2"
21+
github_token: ${{ secrets.GITHUB_TOKEN }}
22+
upload_url: ${{ github.event.release.upload_url }}
23+

build.sh

Lines changed: 181 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,181 @@
1+
if which nproc > /dev/null; then
2+
MAKEOPTS="-j$(nproc)"
3+
else
4+
MAKEOPTS="-j$(sysctl -n hw.ncpu)"
5+
fi
6+
7+
# TODO: Could also make these opts into the build_micropython_opencv function if we care...
8+
FROZEN_MODULES_DIR="$(dirname "$0")/frozen_modules"
9+
FROZEN_EXAMPLES_ARCHIVE_SCRIPT="frozen_examples.py"
10+
FROZEN_EXAMPLES_UNPACKED_DIR="opencv-examples"
11+
PERSISTENT_FILE_FOR_UNPACK="/${FROZEN_EXAMPLES_UNPACKED_DIR}/reset_examples.txt"
12+
13+
# Uses freezefs to create a frozen filesystem archive for the provided directory.
14+
# See https://github.com/bixb922/freezefs for more details on freezefs
15+
# Options:
16+
# $1: The directory to freeze
17+
# $2: The name that you want the frozen directory to have once unpacked on the board
18+
# $3: The output file name for the frozen archive .py file
19+
function create_frozen_fs {
20+
local DIR_TO_FREEZE=$1
21+
local DIR_NAME_ON_BOARD=$2
22+
local OUTPUT_FILE=$3
23+
24+
echo "Creating frozen filesystem for directory: $DIR_TO_FREEZE"
25+
echo "The frozen directory will be named: $DIR_NAME_ON_BOARD"
26+
echo "The output file will be: $OUTPUT_FILE"
27+
28+
if [ $DIR_TO_FREEZE != $DIR_NAME_ON_BOARD ]; then
29+
cp -r $DIR_TO_FREEZE $DIR_NAME_ON_BOARD
30+
fi
31+
32+
# Use on-import=extract so our frozen filesystem is unpacked to '/' in flash on import
33+
# Use --compress to compress the frozen filesystem archive
34+
# Use --overwrite always to ensure that the frozen filesystem is returned to factory state if the persistent file is deleted
35+
36+
python -m freezefs $DIR_NAME_ON_BOARD $OUTPUT_FILE --on-import=extract --compress --overwrite always
37+
}
38+
39+
# Adds the provided directory to the manifest file for the specified port and board.
40+
# Options:
41+
# $1: The directory to add to the manifest
42+
# $2: The port (e.g. rp2)
43+
# $3: The board (e.g. SPARKFUN_XRP_CONTROLLER)
44+
# $4: The mpconfigboard file name (e.g. mpconfigboard.cmake or mpconfigboard.m) Default: mpconfigboard.cmake
45+
function add_to_manifest {
46+
local DIR=$1
47+
local PORT=$2
48+
local BOARD=$3
49+
local MPCONFIG_FILE="${4:-mpconfigboard.cmake}"
50+
51+
# Add the directory to the manifest file
52+
echo "Adding $DIR to the manifest for $PORT on $BOARD using $MPCONFIG_FILE"
53+
local BOARD_DIR="micropython/ports/${PORT}/boards/${BOARD}"
54+
55+
# Create manifest.py if it doesn't exist
56+
if [ ! -f ${BOARD_DIR}/manifest.py ]; then
57+
echo "include(\"\$(PORT_DIR)/boards/manifest.py\")" > ${BOARD_DIR}/manifest.py
58+
59+
# also add the necessary frozen manifest line to mpconfigboard.cmake: set(MICROPY_FROZEN_MANIFEST ${MICROPY_BOARD_DIR}/manifest.py)
60+
# We will use the optional MPCONFIG_FILE argument to determine if we should add this line
61+
62+
if [ -n "$MPCONFIG_FILE" ]; then
63+
echo "Attempting to add frozen manifest line to $MPCONFIG_FILE for $BOARD"
64+
65+
if [[ $MPCONFIG_FILE == *.mk ]]; then
66+
# e.g. for TEENSY which uses mpconfigboard.mk instead of mpconfigboard.cmake
67+
printf "\nFROZEN_MANIFEST ?= \$(BOARD_DIR)/manifest.py" >> ${BOARD_DIR}/$MPCONFIG_FILE
68+
elif [[ $MPCONFIG_FILE == *.cmake ]]; then
69+
printf "\nset(MICROPY_FROZEN_MANIFEST \"\${MICROPY_BOARD_DIR}/manifest.py\")" >> ${BOARD_DIR}/$MPCONFIG_FILE
70+
fi
71+
fi
72+
fi
73+
74+
# Add the freeze line to the manifest.py for the board
75+
echo "Adding freeze line to manifest.py for $BOARD"
76+
printf "\nfreeze(\"${DIR}\")" >> ${BOARD_DIR}/manifest.py
77+
78+
# Helpful for debugging during the build process, but can be removed if we'd rather not see this output...
79+
echo "Manifest.py for $BOARD:"
80+
cat ${BOARD_DIR}/manifest.py
81+
}
82+
83+
# Adds the frozen data filesystem to the _boot.py file for the given port
84+
# Options:
85+
# $1: Port name
86+
# $2: Frozen data file path
87+
# $3: Unpacked directory name on the board (optional). If provided, the modules in this directory will be made importable
88+
function add_frozen_data_to_boot_for_port {
89+
local TARGET_PORT_NAME=$1
90+
local FROZEN_DATA_FILE=$2
91+
local UNPACKED_DIR=$3
92+
93+
# Remove the ".py" extension from the frozen data file
94+
local FROZEN_DATA_BASENAME=$(basename $FROZEN_DATA_FILE .py)
95+
96+
# Check if the _boot.py file exists in the port's modules directory and error out if it does not
97+
if [ ! -f micropython/ports/${TARGET_PORT_NAME}/modules/_boot.py ]; then
98+
echo "Error: _boot.py file not found in ports/${TARGET_PORT_NAME}/modules/"
99+
exit 1
100+
fi
101+
102+
# Add the frozen data filesystem to the _boot.py file
103+
local BOOT_FILE="micropython/ports/${TARGET_PORT_NAME}/modules/_boot.py"
104+
105+
# Create our "persistent file for unpack" that will be used to check if the frozen data filesystem has already been unpacked
106+
# If it has not been unpacked, we will import the frozen data filesystem
107+
echo "Adding frozen data filesystem to ${BOOT_FILE}"
108+
echo "import os" >> ${BOOT_FILE}
109+
echo "try:" >> ${BOOT_FILE}
110+
echo " os.stat('${PERSISTENT_FILE_FOR_UNPACK}')" >> ${BOOT_FILE}
111+
echo "except OSError:" >> ${BOOT_FILE}
112+
echo " import ${FROZEN_DATA_BASENAME}" >> ${BOOT_FILE}
113+
echo " with open('${PERSISTENT_FILE_FOR_UNPACK}', 'w') as f:" >> ${BOOT_FILE}
114+
echo " f.write('Hi! The firmware has this directory frozen into the firmware, and the _boot.py\\n')" >> ${BOOT_FILE}
115+
echo " f.write('file has been modified to automatically unpack this directory if needed. As long\\n')" >> ${BOOT_FILE}
116+
echo " f.write('as this file exists, it will not unpack the directory, meaning you can safely\\n')" >> ${BOOT_FILE}
117+
echo " f.write('edit the files here or delete all other files to free up storage space. If you\\n')" >> ${BOOT_FILE}
118+
echo " f.write('want to restore this directory to its default state, delete this file and the\\n')" >> ${BOOT_FILE}
119+
echo " f.write('directory will be unpacked again on the next boot.\\n')" >> ${BOOT_FILE}
120+
echo " f.write('\\n')" >> ${BOOT_FILE}
121+
echo " f.write('WARNING: Deleting this file will override ALL changes to this directory!')" >> ${BOOT_FILE}
122+
123+
# If a destination directory is provided, we will add it to the sys.path so that the modules in the unpacked directory can be imported
124+
if [ -n "$UNPACKED_DIR" ]; then
125+
echo "Adding ${UNPACKED_DIR} to sys.path in _boot.py"
126+
echo "import sys" >> ${BOOT_FILE}
127+
echo "sys.path.append('/${UNPACKED_DIR}')" >> ${BOOT_FILE}
128+
fi
129+
130+
# Helpful for debugging during the build process, but can be removed if we'd rather not see this output...
131+
echo "Content of _boot.py after adding frozen data filesystem:"
132+
cat micropython/ports/${TARGET_PORT_NAME}/modules/_boot.py
133+
}
134+
135+
# Installs necessary dependencies and builds OpenCV and the firmware
136+
# Also freezes the examples directory in a filesystem archive on the board
137+
function build_micropython_opencv {
138+
# Install necessary packages (Could move into an install_dependencies.sh if we want this to be more explicit/modular)
139+
sudo apt update
140+
sudo apt install cmake python3 build-essential gcc-arm-none-eabi libnewlib-arm-none-eabi libstdc++-arm-none-eabi-newlib
141+
# Install necessary python packages (could also move this to a requirements.txt file)
142+
pip install freezefs
143+
144+
# Create a directory for frozen modules, we can add arbitrary .py files to this directory in the future.
145+
# For now it will just contain the archived examples script.
146+
mkdir "$FROZEN_MODULES_DIR"
147+
148+
# Create our frozen filesystem archive for the examples directory
149+
create_frozen_fs "opencv-examples" "$FROZEN_EXAMPLES_UNPACKED_DIR" "$FROZEN_MODULES_DIR/$FROZEN_EXAMPLES_ARCHIVE_SCRIPT"
150+
151+
# Add necessary content to the manifest file to freeze the modules in the provided directory
152+
add_to_manifest "$FROZEN_MODULES_DIR" "rp2" "SPARKFUN_XRP_CONTROLLER" "mpconfigvariant_LARGE_BINARY.cmake"
153+
154+
# Add necessary content to the boot.py file to unpack the frozen data filesystem on boot
155+
# Provide the source and destination directories to copy the frozen data filesystem to a mutable (and non-hidden) location
156+
# Provide "true" as the last argument to add the destination directory to sys.path (since our examples directory contains modules that we want to be importable...)
157+
# add_frozen_data_to_boot_for_port "rp2" "$FROZEN_EXAMPLES_ARCHIVE_SCRIPT" ".$FROZEN_EXAMPLES_UNPACKED_DIR" "$FROZEN_EXAMPLES_UNPACKED_DIR" true
158+
add_frozen_data_to_boot_for_port "rp2" "$FROZEN_EXAMPLES_ARCHIVE_SCRIPT" "$FROZEN_EXAMPLES_UNPACKED_DIR" true
159+
160+
# Set Pico SDK path to $GITHUB_WORKSPACE/micropython/lib/pico-sdk if $GITHUB_WORKSPACE is set, otherwise use the current directory
161+
if [ -n "$GITHUB_WORKSPACE" ]; then
162+
export PICO_SDK_PATH="$GITHUB_WORKSPACE/micropython/lib/pico-sdk"
163+
else
164+
export PICO_SDK_PATH=$(dirname "$0")/micropython/lib/pico-sdk
165+
fi
166+
167+
# Build MPY Cross compiler
168+
make -C micropython/mpy-cross
169+
170+
# Update necessary MicroPython submodules
171+
make -C micropython/ports/rp2 BOARD=SPARKFUN_XRP_CONTROLLER submodules
172+
173+
# Build OpenCV
174+
make -C src/opencv PLATFORM=rp2350 --no-print-directory ${MAKEOPTS}
175+
176+
# Build firmware
177+
make BOARD=SPARKFUN_XRP_CONTROLLER ${MAKEOPTS}
178+
179+
# Rename firmware file to identify it as the OpenCV build and which board it's for
180+
mv micropython/ports/rp2/build-SPARKFUN_XRP_CONTROLLER-LARGE_BINARY/firmware.uf2 micropython/ports/rp2/build-SPARKFUN_XRP_CONTROLLER-LARGE_BINARY/MICROPYTHON_OPENCV_SPARKFUN_XRP_CONTROLLER.uf2
181+
}

0 commit comments

Comments
 (0)