diff --git a/applications/applications.h b/applications/applications.h index bebe438ab..012e80ddb 100644 --- a/applications/applications.h +++ b/applications/applications.h @@ -18,6 +18,8 @@ typedef struct { typedef void (*FlipperOnStartHook)(void); +extern const char* FLIPPER_AUTORUN_APP_NAME; + /* Services list * Spawned on startup */ diff --git a/applications/loader/loader.c b/applications/loader/loader.c index 3f4e876f3..9ece7f271 100644 --- a/applications/loader/loader.c +++ b/applications/loader/loader.c @@ -466,9 +466,9 @@ int32_t loader_srv(void* p) { furi_record_create(RECORD_LOADER, loader_instance); -#ifdef LOADER_AUTOSTART - loader_start(loader_instance, LOADER_AUTOSTART, NULL); -#endif + if(FLIPPER_AUTORUN_APP_NAME && strlen(FLIPPER_AUTORUN_APP_NAME)) { + loader_start(loader_instance, FLIPPER_AUTORUN_APP_NAME, NULL); + } while(1) { uint32_t flags = diff --git a/fbt_options.py b/fbt_options.py index 7276f579e..b154d26aa 100644 --- a/fbt_options.py +++ b/fbt_options.py @@ -1,5 +1,7 @@ import posixpath +# For more details on these options, run 'fbt -h' + # Default hardware target TARGET_HW = 7 @@ -59,6 +61,9 @@ SVD_FILE = "debug/STM32WB55_CM4.svd" # Look for blackmagic probe on serial ports and local network BLACKMAGIC = "auto" +# Application to start on boot +LOADER_AUTOSTART = "" + FIRMWARE_APPS = { "default": [ "crypto_start", diff --git a/firmware.scons b/firmware.scons index 064e0ddab..863b35fca 100644 --- a/firmware.scons +++ b/firmware.scons @@ -139,7 +139,7 @@ fwenv.AppendUnique( # Depends on virtual value-only node, so it only gets rebuilt when set of apps changes apps_c = fwenv.ApplicationsC( "applications/applications.c", - Value(fwenv["APPS"]), + [Value(fwenv["APPS"]), Value(fwenv["LOADER_AUTOSTART"])], ) # Adding dependency on manifest files so apps.c is rebuilt when any manifest is changed fwenv.Depends(apps_c, fwenv.GlobRecursive("*.fam", "#/applications")) @@ -210,11 +210,19 @@ fwelf = fwenv["FW_ELF"] = fwenv.Program( Depends(fwelf, lib_targets) # Output extra details after building firmware AddPostAction(fwelf, fwenv["APPBUILD_DUMP"]) -AddPostAction(fwelf, Action("@$SIZECOM")) +AddPostAction( + fwelf, + Action('${PYTHON3} "${ROOT_DIR}/scripts/fwsize.py" elf ${TARGET}', "Firmware size"), +) # Produce extra firmware files fwhex = fwenv["FW_HEX"] = fwenv.HEXBuilder("${FIRMWARE_BUILD_CFG}") fwbin = fwenv["FW_BIN"] = fwenv.BINBuilder("${FIRMWARE_BUILD_CFG}") +AddPostAction( + fwbin, + Action('@${PYTHON3} "${ROOT_DIR}/scripts/fwsize.py" bin ${TARGET}'), +) + fwdfu = fwenv["FW_DFU"] = fwenv.DFUBuilder("${FIRMWARE_BUILD_CFG}") Alias(fwenv["FIRMWARE_BUILD_CFG"] + "_dfu", fwdfu) diff --git a/scripts/fwsize.py b/scripts/fwsize.py new file mode 100644 index 000000000..b381f6e9a --- /dev/null +++ b/scripts/fwsize.py @@ -0,0 +1,52 @@ +#!/usr/bin/env python3 + +from flipper.app import App +import subprocess +import os +import math + + +class Main(App): + def init(self): + self.subparsers = self.parser.add_subparsers(help="sub-command help") + + self.parser_elfsize = self.subparsers.add_parser("elf", help="Dump elf stats") + self.parser_elfsize.add_argument("elfname", action="store") + self.parser_elfsize.set_defaults(func=self.process_elf) + + self.parser_binsize = self.subparsers.add_parser("bin", help="Dump bin stats") + self.parser_binsize.add_argument("binname", action="store") + self.parser_binsize.set_defaults(func=self.process_bin) + + def process_elf(self): + all_sizes = subprocess.check_output( + ["arm-none-eabi-size", "-A", self.args.elfname], shell=False + ) + all_sizes = all_sizes.splitlines() + + sections_to_keep = (".text", ".rodata", ".data", ".bss", ".free_flash") + for line in all_sizes: + line = line.decode("utf-8") + parts = line.split() + if len(parts) != 3: + continue + section, size, _ = parts + if section not in sections_to_keep: + continue + print(f"{section:<11} {size:>8} ({(int(size)/1024):6.2f} K)") + + return 0 + + def process_bin(self): + PAGE_SIZE = 4096 + binsize = os.path.getsize(self.args.binname) + pages = math.ceil(binsize / PAGE_SIZE) + last_page_state = (binsize % PAGE_SIZE) * 100 / PAGE_SIZE + print( + f"{os.path.basename(self.args.binname):<11}: {pages:>4} flash pages (last page {last_page_state:.02f}% full)" + ) + return 0 + + +if __name__ == "__main__": + Main()() diff --git a/site_scons/commandline.scons b/site_scons/commandline.scons index 5a7a0dd2d..4c96268b6 100644 --- a/site_scons/commandline.scons +++ b/site_scons/commandline.scons @@ -174,6 +174,12 @@ vars.Add( default="update_default", ) +vars.Add( + "LOADER_AUTOSTART", + help="Application name to automatically run on Flipper boot", + default="", +) + vars.Add( "FIRMWARE_APPS", diff --git a/site_scons/fbt/appmanifest.py b/site_scons/fbt/appmanifest.py index 990bd5b3b..aacf248ec 100644 --- a/site_scons/fbt/appmanifest.py +++ b/site_scons/fbt/appmanifest.py @@ -200,8 +200,9 @@ class ApplicationsCGenerator: FlipperAppType.STARTUP: ("FlipperOnStartHook", "FLIPPER_ON_SYSTEM_START"), } - def __init__(self, buildset: AppBuildset): + def __init__(self, buildset: AppBuildset, autorun_app: str = ""): self.buildset = buildset + self.autorun = autorun_app def get_app_ep_forward(self, app: FlipperApplication): if app.apptype == FlipperAppType.STARTUP: @@ -219,7 +220,11 @@ class ApplicationsCGenerator: .flags = {'|'.join(f"FlipperApplicationFlag{flag}" for flag in app.flags)} }}""" def generate(self): - contents = ['#include "applications.h"', "#include "] + contents = [ + '#include "applications.h"', + "#include ", + f'const char* FLIPPER_AUTORUN_APP_NAME = "{self.autorun}";', + ] for apptype in self.APP_TYPE_MAP: contents.extend( map(self.get_app_ep_forward, self.buildset.get_apps_of_type(apptype)) diff --git a/site_scons/site_tools/crosscc.py b/site_scons/site_tools/crosscc.py index dbedf5c07..aacda58c6 100644 --- a/site_scons/site_tools/crosscc.py +++ b/site_scons/site_tools/crosscc.py @@ -7,7 +7,6 @@ from SCons.Tool import gnulink import strip import gdb import objdump -import size from SCons.Action import _subproc import subprocess @@ -38,7 +37,7 @@ def _get_tool_version(env, tool): def generate(env, **kw): - for orig_tool in (asm, gcc, gxx, ar, gnulink, strip, gdb, objdump, size): + for orig_tool in (asm, gcc, gxx, ar, gnulink, strip, gdb, objdump): orig_tool.generate(env) env.SetDefault( TOOLCHAIN_PREFIX=kw.get("toolchain_prefix"), @@ -57,7 +56,6 @@ def generate(env, **kw): "GDB", "GDBPY", "OBJDUMP", - "SIZE", ], ) # Call CC to check version diff --git a/site_scons/site_tools/fbt_apps.py b/site_scons/site_tools/fbt_apps.py index 2cd63b70b..3dc35049b 100644 --- a/site_scons/site_tools/fbt_apps.py +++ b/site_scons/site_tools/fbt_apps.py @@ -51,7 +51,7 @@ def DumpApplicationConfig(target, source, env): def build_apps_c(target, source, env): target_file_name = target[0].path - gen = ApplicationsCGenerator(env["APPBUILD"]) + gen = ApplicationsCGenerator(env["APPBUILD"], env.subst("$LOADER_AUTOSTART")) with open(target_file_name, "w") as file: file.write(gen.generate()) diff --git a/site_scons/site_tools/size.py b/site_scons/site_tools/size.py deleted file mode 100644 index 56d4f3c9e..000000000 --- a/site_scons/site_tools/size.py +++ /dev/null @@ -1,24 +0,0 @@ -from SCons.Builder import Builder -from SCons.Action import Action - - -def generate(env): - env.SetDefault( - SIZE="size", - SIZEFLAGS=[], - SIZECOM="$SIZE $SIZEFLAGS $TARGETS", - ) - env.Append( - BUILDERS={ - "ELFSize": Builder( - action=Action( - "${SIZECOM}", - "${SIZECOMSTR}", - ), - ), - } - ) - - -def exists(env): - return True