From 6714da6e006e416c6aa703f0f17e4d83bd3a82cc Mon Sep 17 00:00:00 2001 From: Marcus Kida Date: Mon, 6 Jul 2026 08:48:59 +0200 Subject: [PATCH] Dockerize build --- .gitignore | 1 + build-firmware.sh | 28 +++++++++++++ docker/Dockerfile | 21 ++++++++++ docker/build.sh | 21 ++++++++++ docker/firmware.mk | 99 ++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 170 insertions(+) create mode 100755 build-firmware.sh create mode 100644 docker/Dockerfile create mode 100755 docker/build.sh create mode 100644 docker/firmware.mk diff --git a/.gitignore b/.gitignore index ed86553..ee228ea 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ .DS_Store build/ +codec_bin_section_1.bin diff --git a/build-firmware.sh b/build-firmware.sh new file mode 100755 index 0000000..c8fe8b9 --- /dev/null +++ b/build-firmware.sh @@ -0,0 +1,28 @@ +#!/usr/bin/env bash +# +# Build the OpenGD77 firmware for the Retevis RT-3S / TYT MD-UV380 in Docker. +# Nothing is installed on the host: the ARM toolchain lives only in the image, +# and the build output lands in MDUV380_firmware/build/OpenMDUV380.bin. +# +# Usage: ./build-firmware.sh +set -euo pipefail + +REPO_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +IMAGE=opengd77-mduv380-builder + +echo "==> Building toolchain image ($IMAGE)" +docker build --platform linux/amd64 -t "$IMAGE" "$REPO_DIR/docker" + +echo "==> Running build in container" +docker run --rm --platform linux/amd64 \ + -v "$REPO_DIR":/work \ + "$IMAGE" \ + bash /work/docker/build.sh + +echo +echo "==> Firmware ready:" +echo " $REPO_DIR/MDUV380_firmware/build/OpenMDUV380.bin" +echo +echo "Flash it (with the radio in firmware-update mode) using:" +echo " ./MDUV380_firmware/tools/opengd77_stm32_firmware_loader.py \\" +echo " -m MD-UV380 -f MDUV380_firmware/build/OpenMDUV380.bin" diff --git a/docker/Dockerfile b/docker/Dockerfile new file mode 100644 index 0000000..8562b73 --- /dev/null +++ b/docker/Dockerfile @@ -0,0 +1,21 @@ +# Toolchain image for building OpenGD77 (MD-UV380 / Retevis RT-3S) firmware. +# +# Pinned to linux/amd64 because the project's codec_cleaner tool ships only as +# an x86-64 Linux ELF (tools/codec_cleaner.Linux). On Apple Silicon this runs +# under Docker's built-in emulation (Rosetta/qemu). Keeping the whole build on +# one architecture avoids mixing native/emulated toolchains. +FROM --platform=linux/amd64 ubuntu:22.04 + +ENV DEBIAN_FRONTEND=noninteractive + +RUN apt-get update && apt-get install -y --no-install-recommends \ + gcc-arm-none-eabi \ + binutils-arm-none-eabi \ + libnewlib-arm-none-eabi \ + libstdc++-arm-none-eabi-newlib \ + make \ + git \ + ca-certificates \ + && rm -rf /var/lib/apt/lists/* + +WORKDIR /work diff --git a/docker/build.sh b/docker/build.sh new file mode 100755 index 0000000..4f51f13 --- /dev/null +++ b/docker/build.sh @@ -0,0 +1,21 @@ +#!/usr/bin/env bash +# +# In-container build script. Runs inside the toolchain image with the repo +# mounted at /work. Produces MDUV380_firmware/build/OpenMDUV380.bin +# (the flashable OpenGD77 image for the MD-UV380 / Retevis RT-3S). +set -euo pipefail + +cd /work/MDUV380_firmware + +echo "==> Preparing codec section (codec_cleaner -C)" +chmod +x tools/codec_cleaner.Linux +( cd application/source/linkerdata && ../../../tools/codec_cleaner.Linux -C ) + +echo "==> Building firmware" +mkdir -p build +cd build +make -f /work/docker/firmware.mk -j"$(nproc)" + +echo +echo "==> Build complete:" +ls -l /work/MDUV380_firmware/build/OpenMDUV380.bin diff --git a/docker/firmware.mk b/docker/firmware.mk new file mode 100644 index 0000000..f80c279 --- /dev/null +++ b/docker/firmware.mk @@ -0,0 +1,99 @@ +# Makefile for the OpenGD77 MD-UV380 / Retevis RT-3S firmware (MDUV380_FW config). +# +# This reproduces the STM32CubeIDE "MDUV380_FW" build configuration (extracted +# from MDUV380_firmware/.cproject) as a plain Makefile so the firmware can be +# built headlessly. +# +# It is intended to be invoked with the current working directory set to a +# build directory that is a *direct child* of MDUV380_firmware/ (so that "../" +# resolves to the project root), exactly like CubeIDE's generated makefiles. +# The .incbin path inside codec_bin.S ("../application/source/linkerdata/...") +# relies on this layout. + +PREFIX := arm-none-eabi- +CC := $(PREFIX)gcc +OBJCOPY := $(PREFIX)objcopy +SIZE := $(PREFIX)size + +TARGET := OpenMDUV380 + +# ---- MCU / FPU ------------------------------------------------------------ +CPU := -mcpu=cortex-m4 -mfpu=fpv4-sp-d16 -mfloat-abi=hard -mthumb + +# ---- Preprocessor defines ------------------------------------------------- +DEFS := -DUSE_HAL_DRIVER -DSTM32F405xx -DPLATFORM_MDUV380 -DDEBUG + +# Short git hash if available, otherwise UNKNOWN (matches the .cproject rule). +GITVERSION := $(shell git -C .. rev-parse --short HEAD 2>/dev/null || echo UNKNOWN) + +# ---- Include paths (relative to build dir; ../ == project root) ------------ +INCLUDES := \ + -I../Middlewares/Third_Party/FreeRTOS/Source/include \ + -I../application/include \ + -I../Middlewares/Third_Party/FreeRTOS/Source/portable/GCC/ARM_CM4F \ + -I../USB_DEVICE/Target \ + -I../Drivers/CMSIS/Include \ + -I../Core/Inc \ + -I../Drivers/STM32F4xx_HAL_Driver/Inc \ + -I../Middlewares/ST/STM32_USB_Device_Library/Class/CDC/Inc \ + -I../USB_DEVICE/App \ + -I../Drivers/CMSIS/Device/ST/STM32F4xx/Include \ + -I../Middlewares/Third_Party/FreeRTOS/Source/CMSIS_RTOS_V2 \ + -I../Middlewares/ST/STM32_USB_Device_Library/Core/Inc \ + -I../Drivers/STM32F4xx_HAL_Driver/Inc/Legacy \ + -I../ + +# ---- Flags ---------------------------------------------------------------- +WARN := -Wall -Wno-format -Wno-format-truncation -Wno-stringop-overflow +CFLAGS := $(CPU) -std=gnu11 $(DEFS) -DGITVERSION=$(GITVERSION) $(INCLUDES) \ + -Os -ffunction-sections -fdata-sections $(WARN) -fstack-usage -g3 \ + -MMD -MP +ASFLAGS := $(CPU) -x assembler-with-cpp -DDEBUG -g3 -MMD -MP +LDSCRIPT := ../STM32F405VGTX_FLASH.ld +LDFLAGS := $(CPU) -T$(LDSCRIPT) --specs=nano.specs \ + -Wl,-Map=$(TARGET).map,--cref -Wl,--gc-sections -static \ + -Wl,--start-group -lc -lm -Wl,--end-group + +# ---- Sources -------------------------------------------------------------- +# All .c under the CubeIDE source paths, excluding the host-side language +# builder (which is excluded in the MDUV380_FW config and defines its own main). +C_SRCS := $(shell find ../Core ../Drivers ../Middlewares ../USB_DEVICE ../SeggerRTT ../application \ + -name '*.c' ! -name 'languages_builder.c') +ASM_SRCS := $(shell find ../Core ../SeggerRTT ../application \ + \( -name '*.s' -o -name '*.S' \)) + +OBJS := $(patsubst ../%,obj/%.o,$(C_SRCS) $(ASM_SRCS)) +DEPS := $(OBJS:.o=.d) + +# ---- Rules ---------------------------------------------------------------- +.PHONY: all size +all: $(TARGET).bin size + +obj/%.c.o: ../%.c + @mkdir -p $(dir $@) + $(CC) $(CFLAGS) -c $< -o $@ + +obj/%.s.o: ../%.s + @mkdir -p $(dir $@) + $(CC) $(ASFLAGS) -c $< -o $@ + +obj/%.S.o: ../%.S + @mkdir -p $(dir $@) + $(CC) $(ASFLAGS) -c $< -o $@ + +$(TARGET).elf: $(OBJS) + $(CC) $(OBJS) $(LDFLAGS) -o $@ + +# Raw binary straight from the ELF... +$(TARGET)_raw.bin: $(TARGET).elf + $(OBJCOPY) -O binary $< $@ + +# ...then run codec_cleaner (post-build step from .cproject) to clear the +# codec regions, producing the final flashable image. +$(TARGET).bin: $(TARGET)_raw.bin + ../tools/codec_cleaner.Linux -i $< -o $@ + +size: $(TARGET).elf + $(SIZE) $(TARGET).elf + +-include $(DEPS)