mirror of
https://github.com/armel/uv-k1-k5v3-firmware-custom.git
synced 2026-10-02 03:15:37 +00:00
Refine multiboot presets, UI and build output
This commit is contained in:
1 parent
a2551d97dc
commit
919a687936
4 files changed
+318
-204
No files matched your search
+53
-36
@@ -41,52 +41,32 @@ static void mb_copy_label(char *dst, uint8_t cap, const char *src, uint8_t src_c
|
||||
dst[n] = 0;
|
||||
}
|
||||
|
||||
static void mb_format_slot_label(char *dst, uint8_t cap, const mb_slot_header_t *header)
|
||||
static uint8_t mb_copy_slot_version(char *dst, uint8_t cap, const mb_slot_header_t *header)
|
||||
{
|
||||
const char *version = NULL;
|
||||
uint8_t version_cap = 0;
|
||||
uint8_t version_len = 0;
|
||||
uint8_t name_limit = cap - 1u;
|
||||
uint8_t n = 0;
|
||||
|
||||
if (!header->name[0])
|
||||
{
|
||||
mb_copy_label(dst, cap, header->fw_version, MB_VERSION_LEN);
|
||||
return;
|
||||
}
|
||||
|
||||
dst[0] = 0;
|
||||
for (uint8_t i = 0; i + 1u < MB_VERSION_LEN && header->fw_version[i]; i++)
|
||||
{
|
||||
if (header->fw_version[i] == 'v' &&
|
||||
header->fw_version[i + 1u] >= '0' &&
|
||||
header->fw_version[i + 1u] <= '9')
|
||||
{
|
||||
version = &header->fw_version[i + 1u];
|
||||
version_cap = MB_VERSION_LEN - i - 1u;
|
||||
i++;
|
||||
while (n + 1u < cap && i < MB_VERSION_LEN)
|
||||
{
|
||||
const char c = header->fw_version[i];
|
||||
if ((c < '0' || c > '9') && c != '.')
|
||||
break;
|
||||
dst[n++] = c;
|
||||
i++;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (version)
|
||||
{
|
||||
while (version_len < version_cap && version[version_len])
|
||||
version_len++;
|
||||
if (version_len + 1u < cap)
|
||||
name_limit = cap - version_len - 2u;
|
||||
}
|
||||
|
||||
while (n < name_limit && n < MB_NAME_LEN && header->name[n])
|
||||
{
|
||||
dst[n] = header->name[n];
|
||||
n++;
|
||||
}
|
||||
if (version && n + version_len + 1u < cap)
|
||||
{
|
||||
dst[n++] = ' ';
|
||||
for (uint8_t i = 0; i < version_len; i++)
|
||||
dst[n++] = version[i];
|
||||
}
|
||||
dst[n] = 0;
|
||||
return n;
|
||||
}
|
||||
|
||||
/* "MULTIBOOT" mode banner in the top status bar, shown on every screen - the
|
||||
@@ -116,6 +96,17 @@ static void mb_key_hints(const char *act_menu, const char *act_exit)
|
||||
GUI_DisplaySmallest(act_exit, (uint8_t)(xe + 16u + sp), 49, false, true);
|
||||
}
|
||||
|
||||
static void mb_invert_rounded_row(uint8_t line)
|
||||
{
|
||||
gFrameBuffer[line][0] ^= 0x7Fu;
|
||||
for (uint8_t x = 1u; x < LCD_WIDTH - 1u; x++)
|
||||
{
|
||||
gFrameBuffer[line][x] ^= 0xFFu;
|
||||
gFrameBuffer[line - 1u][x] ^= 0x80u;
|
||||
}
|
||||
gFrameBuffer[line][LCD_WIDTH - 1u] ^= 0x7Fu;
|
||||
}
|
||||
|
||||
static void mb_show_message(const char *line1, const char *line2, const char *line3)
|
||||
{
|
||||
UI_DisplayClear();
|
||||
@@ -171,6 +162,7 @@ static void mb_render_slots(uint8_t selected,
|
||||
const uint8_t status[MB_SLOT_COUNT])
|
||||
{
|
||||
char line[19]; /* 18 glyphs max: 18 * 7 px fits from x=2 to x=126. */
|
||||
char version[MB_VERSION_LEN];
|
||||
|
||||
UI_DisplayClear();
|
||||
mb_status_bar();
|
||||
@@ -178,23 +170,48 @@ static void mb_render_slots(uint8_t selected,
|
||||
for (uint8_t slot = 0; slot < MB_SLOT_COUNT; slot++)
|
||||
{
|
||||
const uint8_t fbLine = (uint8_t)(slot + 1u); /* page 1 stays blank */
|
||||
uint8_t version_len = 0;
|
||||
uint8_t version_x = 0;
|
||||
|
||||
memset(line, 0, sizeof(line));
|
||||
memset(version, 0, sizeof(version));
|
||||
line[0] = (char)('0' + slot);
|
||||
line[1] = ' ';
|
||||
line[2] = ' ';
|
||||
|
||||
if (status[slot] == MB_OK)
|
||||
mb_format_slot_label(&line[3], sizeof(line) - 3u, &headers[slot]);
|
||||
{
|
||||
uint8_t name_cap = sizeof(line) - 3u;
|
||||
|
||||
version_len = mb_copy_slot_version(version, sizeof(version), &headers[slot]);
|
||||
if (version_len)
|
||||
{
|
||||
const uint8_t name_x = 2u + 3u * 7u;
|
||||
uint8_t available;
|
||||
|
||||
version_x = (uint8_t)(LCD_WIDTH - 2u - version_len * 7u);
|
||||
available = version_x > name_x
|
||||
? (uint8_t)((version_x - name_x) / 7u)
|
||||
: 0u;
|
||||
if (available + 1u < name_cap)
|
||||
name_cap = available + 1u;
|
||||
}
|
||||
|
||||
if (headers[slot].name[0])
|
||||
mb_copy_label(&line[3], name_cap, headers[slot].name, MB_NAME_LEN);
|
||||
else if (!version_len)
|
||||
mb_copy_label(&line[3], name_cap, headers[slot].fw_version, MB_VERSION_LEN);
|
||||
}
|
||||
else
|
||||
mb_copy_label(&line[3], sizeof(line) - 3u, mb_error_text(status[slot]), 20u);
|
||||
|
||||
UI_PrintStringSmallNormal(line, 2, 0, fbLine);
|
||||
if (version_len)
|
||||
UI_PrintStringSmallNormal(version, version_x, 0, fbLine);
|
||||
|
||||
/* Selected row: full-width inverse bar, like the firmware menu list. */
|
||||
/* Selected row: full-width rounded inverse capsule. */
|
||||
if (slot == selected)
|
||||
for (uint8_t x = 0; x < LCD_WIDTH; x++)
|
||||
gFrameBuffer[fbLine][x] ^= 0xFFu;
|
||||
mb_invert_rounded_row(fbLine);
|
||||
}
|
||||
|
||||
mb_key_hints("SELECT", "QUIT");
|
||||
|
||||
+52
-119
@@ -99,99 +99,6 @@
|
||||
"TARGET": "f4hwn.custom"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "Bandscope",
|
||||
"inherits": "default",
|
||||
"cacheVariables": {
|
||||
"ENABLE_SPECTRUM": true,
|
||||
"ENABLE_FMRADIO": false,
|
||||
"ENABLE_VOX": false,
|
||||
"ENABLE_AIRCOPY": true,
|
||||
"ENABLE_FEAT_F4HWN_K5VIEWER": true,
|
||||
"ENABLE_FEAT_F4HWN_GAME": false,
|
||||
"ENABLE_FEAT_F4HWN_PMR": true,
|
||||
"ENABLE_FEAT_F4HWN_GMRS_FRS_MURS": true,
|
||||
"ENABLE_FEAT_F4HWN_RESCUE_OPS": false,
|
||||
"EDITION_STRING": "Bandscope",
|
||||
"TARGET": "f4hwn.bandscope"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "Broadcast",
|
||||
"inherits": "default",
|
||||
"cacheVariables": {
|
||||
"ENABLE_SPECTRUM": false,
|
||||
"ENABLE_FMRADIO": true,
|
||||
"ENABLE_VOX": true,
|
||||
"ENABLE_AIRCOPY": true,
|
||||
"ENABLE_FEAT_F4HWN_K5VIEWER": true,
|
||||
"ENABLE_FEAT_F4HWN_GAME": false,
|
||||
"ENABLE_FEAT_F4HWN_PMR": true,
|
||||
"ENABLE_FEAT_F4HWN_GMRS_FRS_MURS": true,
|
||||
"ENABLE_FEAT_F4HWN_RESCUE_OPS": false,
|
||||
"EDITION_STRING": "Broadcast",
|
||||
"TARGET": "f4hwn.broadcast"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "Basic",
|
||||
"inherits": "default",
|
||||
"cacheVariables": {
|
||||
"ENABLE_SPECTRUM": true,
|
||||
"ENABLE_FMRADIO": true,
|
||||
"ENABLE_VOX": false,
|
||||
"ENABLE_AIRCOPY": false,
|
||||
"ENABLE_FEAT_F4HWN_K5VIEWER": false,
|
||||
"ENABLE_FEAT_F4HWN_GAME": false,
|
||||
"ENABLE_FEAT_F4HWN_SPECTRUM": false,
|
||||
"ENABLE_FEAT_F4HWN_PMR": true,
|
||||
"ENABLE_FEAT_F4HWN_GMRS_FRS_MURS": true,
|
||||
"ENABLE_AUDIO_BAR": false,
|
||||
"ENABLE_FEAT_F4HWN_RESUME_STATE": false,
|
||||
"ENABLE_FEAT_F4HWN_CHARGING_C": false,
|
||||
"ENABLE_FEAT_F4HWN_INV": true,
|
||||
"ENABLE_FEAT_F4HWN_CTR": false,
|
||||
"ENABLE_FEAT_F4HWN_NARROWER": true,
|
||||
"ENABLE_FEAT_F4HWN_RESCUE_OPS": false,
|
||||
"EDITION_STRING": "Basic",
|
||||
"TARGET": "f4hwn.basic"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "RescueOps",
|
||||
"inherits": "default",
|
||||
"cacheVariables": {
|
||||
"ENABLE_SPECTRUM": false,
|
||||
"ENABLE_FMRADIO": false,
|
||||
"ENABLE_VOX": true,
|
||||
"ENABLE_AIRCOPY": true,
|
||||
"ENABLE_FEAT_F4HWN_K5VIEWER": true,
|
||||
"ENABLE_FEAT_F4HWN_GAME": false,
|
||||
"ENABLE_FEAT_F4HWN_PMR": true,
|
||||
"ENABLE_FEAT_F4HWN_GMRS_FRS_MURS": true,
|
||||
"ENABLE_NOAA": true,
|
||||
"ENABLE_FEAT_F4HWN_RESCUE_OPS": true,
|
||||
"EDITION_STRING": "RescueOps",
|
||||
"TARGET": "f4hwn.rescueops"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "Game",
|
||||
"inherits": "default",
|
||||
"cacheVariables": {
|
||||
"ENABLE_SPECTRUM": false,
|
||||
"ENABLE_FMRADIO": true,
|
||||
"ENABLE_VOX": false,
|
||||
"ENABLE_AIRCOPY": true,
|
||||
"ENABLE_FEAT_F4HWN_K5VIEWER": true,
|
||||
"ENABLE_FEAT_F4HWN_GAME": true,
|
||||
"ENABLE_FEAT_F4HWN_PMR": true,
|
||||
"ENABLE_FEAT_F4HWN_GMRS_FRS_MURS": true,
|
||||
"ENABLE_FEAT_F4HWN_RESCUE_OPS": false,
|
||||
"EDITION_STRING": "Game",
|
||||
"TARGET": "f4hwn.game"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "Fusion",
|
||||
"inherits": "default",
|
||||
@@ -199,12 +106,12 @@
|
||||
"ENABLE_SPECTRUM": true,
|
||||
"ENABLE_FMRADIO": true,
|
||||
"ENABLE_VOX": true,
|
||||
"ENABLE_AIRCOPY": true,
|
||||
"ENABLE_AIRCOPY": false,
|
||||
"ENABLE_FEAT_F4HWN_K5VIEWER": true,
|
||||
"ENABLE_FEAT_F4HWN_GAME": true,
|
||||
"ENABLE_FEAT_F4HWN_GAME": false,
|
||||
"ENABLE_FEAT_F4HWN_PMR": true,
|
||||
"ENABLE_FEAT_F4HWN_GMRS_FRS_MURS": true,
|
||||
"ENABLE_FEAT_F4HWN_RESCUE_OPS": true,
|
||||
"ENABLE_FEAT_F4HWN_RESCUE_OPS": false,
|
||||
"ENABLE_FEAT_F4HWN_VOL": true,
|
||||
"ENABLE_FEAT_F4HWN_AUDIO": true,
|
||||
"ENABLE_FEAT_F4HWN_AUDIO_SCOPE": true,
|
||||
@@ -212,9 +119,8 @@
|
||||
"ENABLE_FEAT_F4HWN_SCAN_FASTER": true,
|
||||
"ENABLE_FEAT_F4HWN_SCAN_RSSI": true,
|
||||
"ENABLE_FEAT_F4HWN_SCAN_SUBAUDIBLE": true,
|
||||
"ENABLE_FEAT_F4HWN_BEAM": true,
|
||||
"ENABLE_FEAT_F4HWN_FOXHUNT": true,
|
||||
"ENABLE_FEAT_F4HWN_ACTION_PICKER": true,
|
||||
"ENABLE_FEAT_F4HWN_BEAM": false,
|
||||
"ENABLE_FEAT_F4HWN_FOXHUNT": false,
|
||||
"ENABLE_FEAT_F4HWN_RXTX_LOG": true,
|
||||
"ENABLE_FEAT_F4HWN_RXTX_LOG_WRAP": false,
|
||||
"ENABLE_FEAT_F4HWN_RXTX_LOG_K5VIEWER": true,
|
||||
@@ -223,10 +129,45 @@
|
||||
"ENABLE_FEAT_F4HWN_LOGO": true,
|
||||
"ENABLE_FEAT_F4HWN_LOGO_SAV": true,
|
||||
"ENABLE_FEAT_F4HWN_MENU_CAT": true,
|
||||
"ENABLE_FEAT_F4HWN_ACTION_PICKER": true,
|
||||
"ENABLE_FEAT_F4HWN_MULTIBOOT": true,
|
||||
"ENABLE_SWD": true,
|
||||
"EDITION_STRING": "Fusion",
|
||||
"TARGET": "f4hwn.fusion"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "Transfer",
|
||||
"inherits": "Fusion",
|
||||
"cacheVariables": {
|
||||
"ENABLE_AIRCOPY": true,
|
||||
"ENABLE_FEAT_F4HWN_BEAM": true,
|
||||
"EDITION_STRING": "Transfer",
|
||||
"TARGET": "f4hwn.transfer"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "Field",
|
||||
"inherits": "Fusion",
|
||||
"cacheVariables": {
|
||||
"ENABLE_FEAT_F4HWN_RESCUE_OPS": true,
|
||||
"ENABLE_FEAT_F4HWN_FOXHUNT": true,
|
||||
"EDITION_STRING": "Field",
|
||||
"TARGET": "f4hwn.field"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "Extended",
|
||||
"inherits": "Fusion",
|
||||
"cacheVariables": {
|
||||
"ENABLE_AIRCOPY": true,
|
||||
"ENABLE_FEAT_F4HWN_GAME": true,
|
||||
"ENABLE_FEAT_F4HWN_RESCUE_OPS": true,
|
||||
"ENABLE_FEAT_F4HWN_BEAM": true,
|
||||
"ENABLE_FEAT_F4HWN_FOXHUNT": true,
|
||||
"EDITION_STRING": "Extended",
|
||||
"TARGET": "f4hwn.extended"
|
||||
}
|
||||
}
|
||||
],
|
||||
"buildPresets": [
|
||||
@@ -234,29 +175,21 @@
|
||||
"name": "Custom",
|
||||
"configurePreset": "Custom"
|
||||
},
|
||||
{
|
||||
"name": "Bandscope",
|
||||
"configurePreset": "Bandscope"
|
||||
},
|
||||
{
|
||||
"name": "Broadcast",
|
||||
"configurePreset": "Broadcast"
|
||||
},
|
||||
{
|
||||
"name": "Basic",
|
||||
"configurePreset": "Basic"
|
||||
},
|
||||
{
|
||||
"name": "RescueOps",
|
||||
"configurePreset": "RescueOps"
|
||||
},
|
||||
{
|
||||
"name": "Game",
|
||||
"configurePreset": "Game"
|
||||
},
|
||||
{
|
||||
"name": "Fusion",
|
||||
"configurePreset": "Fusion"
|
||||
},
|
||||
{
|
||||
"name": "Transfer",
|
||||
"configurePreset": "Transfer"
|
||||
},
|
||||
{
|
||||
"name": "Field",
|
||||
"configurePreset": "Field"
|
||||
},
|
||||
{
|
||||
"name": "Extended",
|
||||
"configurePreset": "Extended"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -59,18 +59,23 @@ Special thanks to Jean-Cyrille F6IWW (3 times), Fabrice 14RC123, David F4BPP, Ol
|
||||
|
||||
### Fusion edition
|
||||
|
||||
Fusion is the reference edition of the project. It provides an all-in-one firmware for the UV-K1 and UV-K5 V3, including:
|
||||
Fusion is the generic reference edition for the UV-K1 and UV-K5 V3. It is intended
|
||||
for everyday use and is the base inherited by the specialized editions. It includes:
|
||||
|
||||
- Fagci's spectrum analyzer,
|
||||
- broadcast FM radio,
|
||||
- VOX and AirCopy,
|
||||
- BEAM wireless channel transfer,
|
||||
- broadcast FM radio and VOX,
|
||||
- [UV Studio](https://armel.github.io/uvstudio/) with integrated K5Viewer screen mirroring, screenshots and remote keyboard control,
|
||||
- advanced RX audio profiles and Audio Scope,
|
||||
- first-responder-oriented controls,
|
||||
- the Breakout game,
|
||||
- automatic RX/TX activity logging with RF Log,
|
||||
- full Fox Hunt and Morse Beacon support.
|
||||
- multiboot support.
|
||||
|
||||
Specialized presets extend Fusion for specific uses:
|
||||
|
||||
- **Transfer** adds AirCopy and BEAM wireless channel transfer.
|
||||
- **Field** adds first-responder controls, Fox Hunt and Morse Beacon support.
|
||||
- **Extended** combines the Transfer and Field feature sets without claiming to include every optional feature.
|
||||
- **Custom** remains a manually configured build based directly on the hidden technical default.
|
||||
|
||||
### Radio and signal handling
|
||||
|
||||
@@ -324,9 +329,10 @@ But, they are nice toys for the price, fun to play with.
|
||||
|
||||
## Compiling and Building from Docker
|
||||
|
||||
This project provides a Docker-based build system to compile the Fusion firmware for the UV-K1 and UV-K5 V3. Everything is handled through the `compile-with-docker.sh` helper script.
|
||||
|
||||
The documented build output is generated inside `build/Fusion`, using the CMake presets defined in `CMakePresets.json`.
|
||||
This project provides a Docker-based build system for the UV-K1 and UV-K5 V3.
|
||||
Everything is handled through the `compile-with-docker.sh` helper script. Fusion is
|
||||
the default generic preset, while specialized builds are generated in their own
|
||||
`build/<Preset>` directories.
|
||||
|
||||
### Prerequisites
|
||||
|
||||
@@ -335,29 +341,37 @@ The documented build output is generated inside `build/Fusion`, using the CMake
|
||||
|
||||
### Build Script Overview
|
||||
|
||||
The script `compile-with-docker.sh` performs the following actions:
|
||||
The script `compile-with-docker.sh`:
|
||||
|
||||
1. Builds the Docker image (`uvk1-uvk5v3`) if it does not already exist.
|
||||
2. Removes any previous `build` directory to ensure a clean configuration.
|
||||
3. Runs CMake using the `Fusion` preset inside the Docker container.
|
||||
4. Builds the firmware and outputs `.elf`, `.bin` and `.hex` files.
|
||||
2. Configures the selected preset with `cmake --fresh`.
|
||||
3. Builds the firmware and outputs matching `.elf`, `.bin` and `.hex` files.
|
||||
4. Displays Flash and RAM usage; `All` keeps the individual build logs quiet.
|
||||
|
||||
### Usage
|
||||
|
||||
```bash
|
||||
./compile-with-docker.sh Fusion [extra CMake options]
|
||||
./compile-with-docker.sh [Preset] [extra CMake options]
|
||||
```
|
||||
|
||||
### Documented Preset
|
||||
The default preset is **Fusion**. Available presets are:
|
||||
|
||||
- **Custom**
|
||||
- **Fusion**
|
||||
- **Transfer**
|
||||
- **Field**
|
||||
- **Extended**
|
||||
- **All** (Fusion, Transfer, Field and Extended)
|
||||
|
||||
### Examples
|
||||
|
||||
Build Fusion:
|
||||
Examples:
|
||||
|
||||
```bash
|
||||
./compile-with-docker.sh
|
||||
./compile-with-docker.sh Fusion
|
||||
./compile-with-docker.sh Transfer
|
||||
./compile-with-docker.sh Field
|
||||
./compile-with-docker.sh Extended
|
||||
./compile-with-docker.sh All
|
||||
```
|
||||
|
||||
### Passing Additional CMake Options
|
||||
|
||||
+181
-31
@@ -5,16 +5,21 @@ set -euo pipefail
|
||||
# Usage:
|
||||
# ./compile-with-docker.sh [Preset] [CMake options...]
|
||||
# Examples:
|
||||
# ./compile-with-docker.sh Custom
|
||||
# ./compile-with-docker.sh Bandscope -DENABLE_SPECTRUM=ON
|
||||
# ./compile-with-docker.sh Broadcast -DENABLE_FEAT_F4HWN_GAME=ON -DENABLE_NOAA=ON
|
||||
# ./compile-with-docker.sh Fusion
|
||||
# ./compile-with-docker.sh Transfer
|
||||
# ./compile-with-docker.sh Field -DENABLE_NOAA=ON
|
||||
# ./compile-with-docker.sh Custom -DENABLE_SPECTRUM=ON
|
||||
# ./compile-with-docker.sh Fusion -DDEV=ON
|
||||
# ./compile-with-docker.sh All
|
||||
# Default preset: "Custom"
|
||||
# Default preset: "Fusion"
|
||||
# ---------------------------------------------
|
||||
|
||||
IMAGE=uvk1-uvk5v3
|
||||
PRESET=${1:-Custom}
|
||||
RELEASE_PRESETS=(Fusion Transfer Field Extended)
|
||||
FLASH_LIMIT=$((118 * 1024))
|
||||
RAM_LIMIT=$((16 * 1024))
|
||||
|
||||
PRESET=${1:-Fusion}
|
||||
shift || true # remove preset from arguments if present
|
||||
|
||||
# Any remaining args will be treated as CMake cache variables
|
||||
@@ -23,53 +28,198 @@ EXTRA_ARGS=("$@")
|
||||
# ---------------------------------------------
|
||||
# Validate preset name
|
||||
# ---------------------------------------------
|
||||
if [[ ! "$PRESET" =~ ^(Custom|Bandscope|Broadcast|Basic|RescueOps|Game|Fusion|All)$ ]]; then
|
||||
if [[ ! "$PRESET" =~ ^(Custom|Fusion|Transfer|Field|Extended|All)$ ]]; then
|
||||
echo "❌ Unknown preset: '$PRESET'"
|
||||
echo "Valid presets are: Custom, Bandscope, Broadcast, Basic, RescueOps, Game, Fusion, All"
|
||||
echo "Valid presets are: Custom, Fusion, Transfer, Field, Extended, All"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
QUIET=0
|
||||
if [[ "$PRESET" == "All" ]]; then
|
||||
QUIET=1
|
||||
fi
|
||||
|
||||
# ---------------------------------------------
|
||||
# Build the Docker image (only needed once)
|
||||
# ---------------------------------------------
|
||||
if [[ "$(docker images -q $IMAGE)" == "" ]]; then
|
||||
if [[ "$(docker images -q "$IMAGE")" == "" ]]; then
|
||||
echo "Building Docker image..."
|
||||
docker build -t "$IMAGE" .
|
||||
docker build -q -t "$IMAGE" . >/dev/null
|
||||
fi
|
||||
|
||||
# ---------------------------------------------
|
||||
# Clean existing CMake cache to ensure toolchain reload
|
||||
# ---------------------------------------------
|
||||
rm -rf build
|
||||
export MSYS_NO_PATHCONV=1
|
||||
# ---------------------------------------------
|
||||
# Function to build one preset
|
||||
# ---------------------------------------------
|
||||
|
||||
RESULT_PRESETS=()
|
||||
RESULT_FLASH_SIZES=()
|
||||
RESULT_RAM_SIZES=()
|
||||
|
||||
run_preset_build() {
|
||||
local preset="$1"
|
||||
|
||||
docker run --rm \
|
||||
-u "$(id -u):$(id -g)" \
|
||||
-v "$PWD":/src -w /src "$IMAGE" \
|
||||
bash -c 'which arm-none-eabi-gcc && arm-none-eabi-gcc --version &&
|
||||
cmake --fresh --preset "$1" "${@:2}" &&
|
||||
cmake --build --preset "$1" -j' \
|
||||
bash "$preset" "${EXTRA_ARGS[@]}"
|
||||
}
|
||||
|
||||
build_preset() {
|
||||
local preset="$1"
|
||||
local preset_slug log_file bin_file flash_size ram_size status
|
||||
|
||||
preset_slug="${preset,,}"
|
||||
log_file="$(mktemp)"
|
||||
bin_file=""
|
||||
|
||||
find "build/${preset}" -maxdepth 1 -type f -name 'f4hwn.*' -delete 2>/dev/null || true
|
||||
|
||||
if (( QUIET )); then
|
||||
printf "Building %-10s ... " "$preset"
|
||||
if run_preset_build "$preset" >"$log_file" 2>&1; then
|
||||
status=0
|
||||
else
|
||||
status=$?
|
||||
fi
|
||||
else
|
||||
echo ""
|
||||
echo "=== 🚀 Building preset: ${preset} ==="
|
||||
echo "---------------------------------------------"
|
||||
if run_preset_build "$preset" 2>&1 | tee "$log_file" | awk '
|
||||
/^Memory region[[:space:]]+Used Size[[:space:]]+Region Size/ { next }
|
||||
/^[[:space:]]+RAM:[[:space:]]/ { next }
|
||||
/^[[:space:]]+FLASH:[[:space:]]/ { next }
|
||||
{ print; fflush() }
|
||||
'; then
|
||||
status=0
|
||||
else
|
||||
status=$?
|
||||
fi
|
||||
fi
|
||||
|
||||
if (( status != 0 )); then
|
||||
if (( QUIET )); then
|
||||
printf "FAILED\n\n"
|
||||
cat "$log_file"
|
||||
else
|
||||
echo "Failed: ${preset}"
|
||||
fi
|
||||
rm -f -- "$log_file"
|
||||
return "$status"
|
||||
fi
|
||||
|
||||
bin_file="build/${preset}/f4hwn.${preset_slug}.bin"
|
||||
if [[ -z "$bin_file" || ! -f "$bin_file" ]]; then
|
||||
if (( QUIET )); then
|
||||
printf "FAILED\n"
|
||||
fi
|
||||
echo "Expected binary not found: $bin_file"
|
||||
if (( QUIET )); then
|
||||
cat "$log_file"
|
||||
fi
|
||||
rm -f -- "$log_file"
|
||||
return 1
|
||||
fi
|
||||
|
||||
flash_size="$(wc -c < "$bin_file")"
|
||||
ram_size="$(awk '$1 == "RAM:" {
|
||||
value = $2
|
||||
if ($3 == "KB") value *= 1024
|
||||
else if ($3 == "MB") value *= 1024 * 1024
|
||||
printf "%.0f\n", value
|
||||
}' "$log_file" | tail -n 1)"
|
||||
|
||||
if [[ ! "$ram_size" =~ ^[0-9]+$ ]]; then
|
||||
if (( QUIET )); then
|
||||
printf "FAILED\n"
|
||||
fi
|
||||
echo "Could not read RAM usage from linker output"
|
||||
if (( QUIET )); then
|
||||
cat "$log_file"
|
||||
fi
|
||||
rm -f -- "$log_file"
|
||||
return 1
|
||||
fi
|
||||
|
||||
RESULT_PRESETS+=("$preset")
|
||||
RESULT_FLASH_SIZES+=("$flash_size")
|
||||
RESULT_RAM_SIZES+=("$ram_size")
|
||||
|
||||
if (( QUIET )); then
|
||||
printf "OK\n"
|
||||
else
|
||||
echo "✅ Done: ${preset}"
|
||||
fi
|
||||
rm -f -- "$log_file"
|
||||
}
|
||||
|
||||
print_summary() {
|
||||
local i
|
||||
local flash_used flash_free flash_bp flash_pct
|
||||
local flash_used_kib100 flash_free_kib100 flash_used_kib flash_free_kib
|
||||
local ram_used ram_free ram_bp ram_pct
|
||||
local ram_used_kib100 ram_free_kib100 ram_used_kib ram_free_kib
|
||||
|
||||
echo ""
|
||||
echo "=== 🚀 Building preset: ${preset} ==="
|
||||
echo "---------------------------------------------"
|
||||
docker run --rm \
|
||||
-u $(id -u):$(id -g) \
|
||||
-it -v "$PWD":/src -w /src "$IMAGE" \
|
||||
bash -c 'which arm-none-eabi-gcc && arm-none-eabi-gcc --version &&
|
||||
cmake --preset "$1" "${@:2}" &&
|
||||
cmake --build --preset "$1" -j' \
|
||||
bash "${preset}" ${EXTRA_ARGS[@]+"${EXTRA_ARGS[@]}"}
|
||||
echo "✅ Done: ${preset}"
|
||||
printf "Flash (limit: %d B / %d.00 KiB)\n" "$FLASH_LIMIT" "$((FLASH_LIMIT / 1024))"
|
||||
printf "%-10s | %10s | %10s | %10s | %10s | %7s\n" \
|
||||
"Preset" "Used (B)" "Used (KiB)" "Free (B)" "Free (KiB)" "Usage"
|
||||
printf "%-10s-+-%10s-+-%10s-+-%10s-+-%10s-+-%7s\n" \
|
||||
"----------" "----------" "----------" "----------" "----------" "-------"
|
||||
|
||||
for i in "${!RESULT_PRESETS[@]}"; do
|
||||
flash_used="${RESULT_FLASH_SIZES[$i]}"
|
||||
flash_free=$((FLASH_LIMIT - flash_used))
|
||||
flash_bp=$(((flash_used * 10000 + FLASH_LIMIT / 2) / FLASH_LIMIT))
|
||||
flash_used_kib100=$(((flash_used * 100 + 512) / 1024))
|
||||
flash_free_kib100=$(((flash_free * 100 + 512) / 1024))
|
||||
printf -v flash_pct "%d.%02d%%" "$((flash_bp / 100))" "$((flash_bp % 100))"
|
||||
printf -v flash_used_kib "%d.%02d" "$((flash_used_kib100 / 100))" "$((flash_used_kib100 % 100))"
|
||||
printf -v flash_free_kib "%d.%02d" "$((flash_free_kib100 / 100))" "$((flash_free_kib100 % 100))"
|
||||
|
||||
printf "%-10s | %10d | %10s | %10d | %10s | %7s\n" \
|
||||
"${RESULT_PRESETS[$i]}" "$flash_used" "$flash_used_kib" \
|
||||
"$flash_free" "$flash_free_kib" "$flash_pct"
|
||||
done
|
||||
|
||||
echo ""
|
||||
printf "RAM (limit: %d B / %d.00 KiB)\n" "$RAM_LIMIT" "$((RAM_LIMIT / 1024))"
|
||||
printf "%-10s | %10s | %10s | %10s | %10s | %7s\n" \
|
||||
"Preset" "Used (B)" "Used (KiB)" "Free (B)" "Free (KiB)" "Usage"
|
||||
printf "%-10s-+-%10s-+-%10s-+-%10s-+-%10s-+-%7s\n" \
|
||||
"----------" "----------" "----------" "----------" "----------" "-------"
|
||||
|
||||
for i in "${!RESULT_PRESETS[@]}"; do
|
||||
ram_used="${RESULT_RAM_SIZES[$i]}"
|
||||
ram_free=$((RAM_LIMIT - ram_used))
|
||||
ram_bp=$(((ram_used * 10000 + RAM_LIMIT / 2) / RAM_LIMIT))
|
||||
ram_used_kib100=$(((ram_used * 100 + 512) / 1024))
|
||||
ram_free_kib100=$(((ram_free * 100 + 512) / 1024))
|
||||
printf -v ram_pct "%d.%02d%%" "$((ram_bp / 100))" "$((ram_bp % 100))"
|
||||
printf -v ram_used_kib "%d.%02d" "$((ram_used_kib100 / 100))" "$((ram_used_kib100 % 100))"
|
||||
printf -v ram_free_kib "%d.%02d" "$((ram_free_kib100 / 100))" "$((ram_free_kib100 % 100))"
|
||||
|
||||
printf "%-10s | %10d | %10s | %10d | %10s | %7s\n" \
|
||||
"${RESULT_PRESETS[$i]}" "$ram_used" "$ram_used_kib" \
|
||||
"$ram_free" "$ram_free_kib" "$ram_pct"
|
||||
done
|
||||
|
||||
echo ""
|
||||
if (( ${#RESULT_PRESETS[@]} == 1 )); then
|
||||
echo "🎉 Preset ${RESULT_PRESETS[0]} built successfully!"
|
||||
else
|
||||
echo "🎉 All presets built successfully!"
|
||||
fi
|
||||
}
|
||||
|
||||
# ---------------------------------------------
|
||||
# Handle 'All' preset
|
||||
# ---------------------------------------------
|
||||
if [[ "$PRESET" == "All" ]]; then
|
||||
PRESETS=(Bandscope Broadcast Basic RescueOps Game Fusion)
|
||||
for p in "${PRESETS[@]}"; do
|
||||
for p in "${RELEASE_PRESETS[@]}"; do
|
||||
build_preset "$p"
|
||||
done
|
||||
echo ""
|
||||
echo "🎉 All presets built successfully!"
|
||||
else
|
||||
build_preset "$PRESET"
|
||||
fi
|
||||
print_summary
|
||||
Reference in new issue
Block a user