diff --git a/SConstruct b/SConstruct index 13a698c8..67eac382 100644 --- a/SConstruct +++ b/SConstruct @@ -43,6 +43,7 @@ distenv = coreenv.Clone( "jflash", ], ENV=os.environ, + UPDATE_BUNDLE_DIR="dist/${DIST_DIR}/f${TARGET_HW}-update-${DIST_SUFFIX}", ) firmware_env = distenv.AddFwProject( @@ -140,21 +141,28 @@ distenv.Default(basic_dist) dist_dir = distenv.GetProjetDirName() fap_dist = [ distenv.Install( - f"#/dist/{dist_dir}/apps/debug_elf", - firmware_env["FW_EXTAPPS"]["debug"].values(), + distenv.Dir(f"#/dist/{dist_dir}/apps/debug_elf"), + list( + app_artifact.debug + for app_artifact in firmware_env["FW_EXTAPPS"].applications.values() + ), ), - *( - distenv.Install(f"#/dist/{dist_dir}/apps/{dist_entry[0]}", dist_entry[1]) - for dist_entry in firmware_env["FW_EXTAPPS"]["dist"].values() + distenv.Install( + f"#/dist/{dist_dir}/apps", + "#/assets/resources/apps", ), ] -Depends(fap_dist, firmware_env["FW_EXTAPPS"]["validators"].values()) +Depends( + fap_dist, + list( + app_artifact.validator + for app_artifact in firmware_env["FW_EXTAPPS"].applications.values() + ), +) Alias("fap_dist", fap_dist) # distenv.Default(fap_dist) -distenv.Depends( - firmware_env["FW_RESOURCES"], firmware_env["FW_EXTAPPS"]["resources_dist"] -) +distenv.Depends(firmware_env["FW_RESOURCES"], firmware_env["FW_EXTAPPS"].resources_dist) # Target for bundling core2 package for qFlipper @@ -192,6 +200,7 @@ firmware_debug = distenv.PhonyTarget( source=firmware_env["FW_ELF"], GDBOPTS="${GDBOPTS_BASE}", GDBREMOTE="${OPENOCD_GDB_PIPE}", + FBT_FAP_DEBUG_ELF_ROOT=firmware_env.subst("$FBT_FAP_DEBUG_ELF_ROOT"), ) distenv.Depends(firmware_debug, firmware_flash) @@ -201,6 +210,7 @@ distenv.PhonyTarget( source=firmware_env["FW_ELF"], GDBOPTS="${GDBOPTS_BASE} ${GDBOPTS_BLACKMAGIC}", GDBREMOTE="${BLACKMAGIC_ADDR}", + FBT_FAP_DEBUG_ELF_ROOT=firmware_env.subst("$FBT_FAP_DEBUG_ELF_ROOT"), ) # Debug alien elf @@ -209,7 +219,7 @@ distenv.PhonyTarget( "${GDBPYCOM}", GDBOPTS="${GDBOPTS_BASE}", GDBREMOTE="${OPENOCD_GDB_PIPE}", - GDBPYOPTS='-ex "source debug/PyCortexMDebug/PyCortexMDebug.py" ', + GDBPYOPTS='-ex "source ${FBT_DEBUG_DIR}/PyCortexMDebug/PyCortexMDebug.py" ', ) distenv.PhonyTarget( diff --git a/applications/main/bad_usb/application.fam b/applications/main/bad_usb/application.fam index 4da34f0d..2442dd3a 100644 --- a/applications/main/bad_usb/application.fam +++ b/applications/main/bad_usb/application.fam @@ -11,4 +11,5 @@ App( stack_size=2 * 1024, icon="A_BadUsb_14", order=70, + fap_libs=["assets"], ) diff --git a/applications/main/gpio/application.fam b/applications/main/gpio/application.fam index 64f8db5b..efeb8b6f 100644 --- a/applications/main/gpio/application.fam +++ b/applications/main/gpio/application.fam @@ -8,4 +8,5 @@ App( stack_size=1 * 1024, icon="A_GPIO_14", order=50, + fap_libs=["assets"], ) diff --git a/applications/main/ibutton/application.fam b/applications/main/ibutton/application.fam index 0bc6f8a9..77bb9a33 100644 --- a/applications/main/ibutton/application.fam +++ b/applications/main/ibutton/application.fam @@ -12,6 +12,7 @@ App( icon="A_iButton_14", stack_size=2 * 1024, order=60, + fap_libs=["assets"], ) App( diff --git a/applications/main/infrared/application.fam b/applications/main/infrared/application.fam index 6f76ed42..9c5eaf39 100644 --- a/applications/main/infrared/application.fam +++ b/applications/main/infrared/application.fam @@ -12,6 +12,7 @@ App( icon="A_Infrared_14", stack_size=3 * 1024, order=40, + fap_libs=["assets"], ) App( diff --git a/applications/main/lfrfid/application.fam b/applications/main/lfrfid/application.fam index 4a149818..150a6f3d 100644 --- a/applications/main/lfrfid/application.fam +++ b/applications/main/lfrfid/application.fam @@ -14,6 +14,7 @@ App( icon="A_125khz_14", stack_size=2 * 1024, order=20, + fap_libs=["assets"], ) App( diff --git a/applications/main/u2f/application.fam b/applications/main/u2f/application.fam index 6b32e022..82010ffb 100644 --- a/applications/main/u2f/application.fam +++ b/applications/main/u2f/application.fam @@ -11,4 +11,5 @@ App( stack_size=2 * 1024, icon="A_U2F_14", order=80, + fap_libs=["assets"], ) diff --git a/debug/flipperapps.py b/debug/flipperapps.py index 8e1aa2da..e815e40b 100644 --- a/debug/flipperapps.py +++ b/debug/flipperapps.py @@ -1,5 +1,5 @@ from dataclasses import dataclass -from typing import Tuple, Dict +from typing import Optional, Tuple, Dict, ClassVar import struct import posixpath import os @@ -22,14 +22,18 @@ class AppState: debug_link_elf: str = "" debug_link_crc: int = 0 + DEBUG_ELF_ROOT: ClassVar[Optional[str]] = None + def __post_init__(self): if self.other_sections is None: self.other_sections = {} - def get_original_elf_path(self, elf_path="build/latest/.extapps") -> str: + def get_original_elf_path(self) -> str: + if self.DEBUG_ELF_ROOT is None: + raise ValueError("DEBUG_ELF_ROOT not set; call fap-set-debug-elf-root") return ( - posixpath.join(elf_path, self.debug_link_elf) - if elf_path + posixpath.join(self.DEBUG_ELF_ROOT, self.debug_link_elf) + if self.DEBUG_ELF_ROOT else self.debug_link_elf ) @@ -84,7 +88,9 @@ class AppState: if debug_link_size := int(app_state["debug_link_info"]["debug_link_size"]): debug_link_data = ( gdb.selected_inferior() - .read_memory(int(app_state["debug_link_info"]["debug_link"]), debug_link_size) + .read_memory( + int(app_state["debug_link_info"]["debug_link"]), debug_link_size + ) .tobytes() ) state.debug_link_elf, state.debug_link_crc = AppState.parse_debug_link_data( @@ -103,6 +109,29 @@ class AppState: return state +class SetFapDebugElfRoot(gdb.Command): + """Set path to original ELF files for debug info""" + + def __init__(self): + super().__init__( + "fap-set-debug-elf-root", gdb.COMMAND_FILES, gdb.COMPLETE_FILENAME + ) + self.dont_repeat() + + def invoke(self, arg, from_tty): + AppState.DEBUG_ELF_ROOT = arg + try: + global helper + print(f"Set '{arg}' as debug info lookup path for Flipper external apps") + helper.attach_fw() + gdb.events.stop.connect(helper.handle_stop) + except gdb.error as e: + print(f"Support for Flipper external apps debug is not available: {e}") + + +SetFapDebugElfRoot() + + class FlipperAppDebugHelper: def __init__(self): self.app_ptr = None @@ -149,9 +178,4 @@ class FlipperAppDebugHelper: helper = FlipperAppDebugHelper() -try: - helper.attach_fw() - print("Support for Flipper external apps debug is enabled") - gdb.events.stop.connect(helper.handle_stop) -except gdb.error as e: - print(f"Support for Flipper external apps debug is not available: {e}") +print("Support for Flipper external apps debug is loaded") diff --git a/fbt_options.py b/fbt_options.py index 6ef9759e..11124b93 100644 --- a/fbt_options.py +++ b/fbt_options.py @@ -49,12 +49,12 @@ OPENOCD_OPTS = [ "-c", "transport select hla_swd", "-f", - "debug/stm32wbx.cfg", + "${FBT_DEBUG_DIR}/stm32wbx.cfg", "-c", "stm32wbx.cpu configure -rtos auto", ] -SVD_FILE = "debug/STM32WB55_CM4.svd" +SVD_FILE = "${FBT_DEBUG_DIR}/STM32WB55_CM4.svd" # Look for blackmagic probe on serial ports and local network BLACKMAGIC = "auto" diff --git a/firmware.scons b/firmware.scons index a0c1ab33..6feb73ac 100644 --- a/firmware.scons +++ b/firmware.scons @@ -20,8 +20,7 @@ env = ENV.Clone( BUILD_DIR=fw_build_meta["build_dir"], IS_BASE_FIRMWARE=fw_build_meta["type"] == "firmware", FW_FLAVOR=fw_build_meta["flavor"], - PLUGIN_ELF_DIR="${BUILD_DIR}", - LIB_DIST_DIR="${BUILD_DIR}/lib", + LIB_DIST_DIR=fw_build_meta["build_dir"].Dir("lib"), LINT_SOURCES=[ "applications", ], @@ -142,12 +141,14 @@ for app_dir, _ in env["APPDIRS"]: fwenv.PrepareApplicationsBuild() -# Build external apps +# Build external apps + configure SDK if env["IS_BASE_FIRMWARE"]: - extapps = fwenv["FW_EXTAPPS"] = SConscript( - "site_scons/extapps.scons", exports={"ENV": fwenv} + fwenv.SetDefault(FBT_FAP_DEBUG_ELF_ROOT="${BUILD_DIR}/.extapps") + fwenv["FW_EXTAPPS"] = SConscript( + "site_scons/extapps.scons", + exports={"ENV": fwenv}, ) - fw_artifacts.append(extapps["sdk_tree"]) + fw_artifacts.append(fwenv["FW_EXTAPPS"].sdk_tree) # Add preprocessor definitions for current set of apps diff --git a/scripts/fbt/elfmanifest.py b/scripts/fbt/elfmanifest.py index 313a64c0..17bceddf 100644 --- a/scripts/fbt/elfmanifest.py +++ b/scripts/fbt/elfmanifest.py @@ -5,6 +5,7 @@ import struct from dataclasses import dataclass, field from .appmanifest import FlipperApplication +from flipper.assets.icon import file2image _MANIFEST_MAGIC = 0x52474448 @@ -53,8 +54,6 @@ def assemble_manifest_data( ): image_data = b"" if app_manifest.fap_icon: - from flipper.assets.icon import file2image - image = file2image(os.path.join(app_manifest._apppath, app_manifest.fap_icon)) if (image.width, image.height) != (10, 10): raise ValueError( diff --git a/scripts/fbt/sdk/cache.py b/scripts/fbt/sdk/cache.py index 62d42798..756c3782 100644 --- a/scripts/fbt/sdk/cache.py +++ b/scripts/fbt/sdk/cache.py @@ -89,6 +89,9 @@ class SdkCache: syms.update(map(lambda e: e.name, self.get_variables())) return syms + def get_disabled_names(self): + return set(map(lambda e: e.name, self.disabled_entries)) + def get_functions(self): return self._filter_enabled(self.sdk.functions) diff --git a/scripts/fbt_tools/fbt_assets.py b/scripts/fbt_tools/fbt_assets.py index 521c37e9..e1748735 100644 --- a/scripts/fbt_tools/fbt_assets.py +++ b/scripts/fbt_tools/fbt_assets.py @@ -139,7 +139,7 @@ def generate(env): BUILDERS={ "IconBuilder": Builder( action=Action( - '${PYTHON3} "${ASSETS_COMPILER}" icons ${ABSPATHGETTERFUNC(SOURCE)} ${TARGET.dir} --filename ${ICON_FILE_NAME}', + '${PYTHON3} "${ASSETS_COMPILER}" icons "${ABSPATHGETTERFUNC(SOURCE)}" "${TARGET.dir}" --filename ${ICON_FILE_NAME}', "${ICONSCOMSTR}", ), emitter=icons_emitter, diff --git a/scripts/fbt_tools/fbt_debugopts.py b/scripts/fbt_tools/fbt_debugopts.py index 0c0ce3d3..c3be5ca4 100644 --- a/scripts/fbt_tools/fbt_debugopts.py +++ b/scripts/fbt_tools/fbt_debugopts.py @@ -1,7 +1,6 @@ from re import search from SCons.Errors import UserError -from fbt_options import OPENOCD_OPTS def _get_device_serials(search_str="STLink"): @@ -20,6 +19,9 @@ def GetDevices(env): def generate(env, **kw): env.AddMethod(GetDevices) + env.SetDefault( + FBT_DEBUG_DIR="${ROOT_DIR}/debug", + ) if (adapter_serial := env.subst("$OPENOCD_ADAPTER_SERIAL")) != "auto": env.Append( @@ -36,7 +38,7 @@ def generate(env, **kw): env.SetDefault( OPENOCD_GDB_PIPE=[ - "|openocd -c 'gdb_port pipe; log_output debug/openocd.log' ${[SINGLEQUOTEFUNC(OPENOCD_OPTS)]}" + "|openocd -c 'gdb_port pipe; log_output ${FBT_DEBUG_DIR}/openocd.log' ${[SINGLEQUOTEFUNC(OPENOCD_OPTS)]}" ], GDBOPTS_BASE=[ "-ex", @@ -58,17 +60,19 @@ def generate(env, **kw): ], GDBPYOPTS=[ "-ex", - "source debug/FreeRTOS/FreeRTOS.py", + "source ${FBT_DEBUG_DIR}/FreeRTOS/FreeRTOS.py", "-ex", - "source debug/flipperapps.py", + "source ${FBT_DEBUG_DIR}/flipperapps.py", "-ex", - "source debug/PyCortexMDebug/PyCortexMDebug.py", + "fap-set-debug-elf-root ${FBT_FAP_DEBUG_ELF_ROOT}", + "-ex", + "source ${FBT_DEBUG_DIR}/PyCortexMDebug/PyCortexMDebug.py", "-ex", "svd_load ${SVD_FILE}", "-ex", "compare-sections", ], - JFLASHPROJECT="${ROOT_DIR.abspath}/debug/fw.jflash", + JFLASHPROJECT="${FBT_DEBUG_DIR}/fw.jflash", ) diff --git a/scripts/fbt_tools/fbt_dist.py b/scripts/fbt_tools/fbt_dist.py index fb59e5b9..f0b44348 100644 --- a/scripts/fbt_tools/fbt_dist.py +++ b/scripts/fbt_tools/fbt_dist.py @@ -22,7 +22,7 @@ def GetProjetDirName(env, project=None): def create_fw_build_targets(env, configuration_name): flavor = GetProjetDirName(env, configuration_name) - build_dir = env.Dir("build").Dir(flavor).abspath + build_dir = env.Dir("build").Dir(flavor) return env.SConscript( "firmware.scons", variant_dir=build_dir, @@ -131,7 +131,7 @@ def generate(env): "UsbInstall": Builder( action=[ Action( - '${PYTHON3} "${SELFUPDATE_SCRIPT}" dist/${DIST_DIR}/f${TARGET_HW}-update-${DIST_SUFFIX}/update.fuf' + '${PYTHON3} "${SELFUPDATE_SCRIPT}" ${UPDATE_BUNDLE_DIR}/update.fuf' ), Touch("${TARGET}"), ] diff --git a/scripts/fbt_tools/fbt_extapps.py b/scripts/fbt_tools/fbt_extapps.py index fb4dc2f1..f1906191 100644 --- a/scripts/fbt_tools/fbt_extapps.py +++ b/scripts/fbt_tools/fbt_extapps.py @@ -1,6 +1,9 @@ +from dataclasses import dataclass, field +from typing import Optional from SCons.Builder import Builder from SCons.Action import Action from SCons.Errors import UserError +from SCons.Node import NodeList import SCons.Warnings from fbt.elfmanifest import assemble_manifest_data @@ -16,6 +19,15 @@ import shutil from ansi.color import fg +@dataclass +class FlipperExternalAppInfo: + app: FlipperApplication + compact: NodeList = field(default_factory=NodeList) + debug: NodeList = field(default_factory=NodeList) + validator: NodeList = field(default_factory=NodeList) + installer: NodeList = field(default_factory=NodeList) + + def BuildAppElf(env, app): ext_apps_work_dir = env.subst("$EXT_APPS_WORK_DIR") app_work_dir = os.path.join(ext_apps_work_dir, app.appid) @@ -26,15 +38,7 @@ def BuildAppElf(env, app): app_alias = f"fap_{app.appid}" - # Deprecation stub - legacy_app_taget_name = f"{app_env['FIRMWARE_BUILD_CFG']}_{app.appid}" - - def legacy_app_build_stub(**kw): - raise UserError( - f"Target name '{legacy_app_taget_name}' is deprecated, use '{app_alias}' instead" - ) - - app_env.PhonyTarget(legacy_app_taget_name, Action(legacy_app_build_stub, None)) + app_artifacts = FlipperExternalAppInfo(app) externally_built_files = [] if app.fap_extbuild: @@ -115,20 +119,22 @@ def BuildAppElf(env, app): CPPPATH=env.Dir(app_work_dir), ) - app_elf_raw = app_env.Program( + app_artifacts.debug = app_env.Program( os.path.join(ext_apps_work_dir, f"{app.appid}_d"), app_sources, APP_ENTRY=app.entry_point, ) - app_env.Clean(app_elf_raw, [*externally_built_files, app_env.Dir(app_work_dir)]) + app_env.Clean( + app_artifacts.debug, [*externally_built_files, app_env.Dir(app_work_dir)] + ) - app_elf_dump = app_env.ObjDump(app_elf_raw) + app_elf_dump = app_env.ObjDump(app_artifacts.debug) app_env.Alias(f"{app_alias}_list", app_elf_dump) - app_elf_augmented = app_env.EmbedAppMetadata( + app_artifacts.compact = app_env.EmbedAppMetadata( os.path.join(ext_apps_work_dir, app.appid), - app_elf_raw, + app_artifacts.debug, APP=app, ) @@ -139,19 +145,21 @@ def BuildAppElf(env, app): } app_env.Depends( - app_elf_augmented, + app_artifacts.compact, [app_env["SDK_DEFINITION"], app_env.Value(manifest_vals)], ) if app.fap_icon: app_env.Depends( - app_elf_augmented, + app_artifacts.compact, app_env.File(f"{app._apppath}/{app.fap_icon}"), ) - app_elf_import_validator = app_env.ValidateAppImports(app_elf_augmented) - app_env.AlwaysBuild(app_elf_import_validator) - app_env.Alias(app_alias, app_elf_import_validator) - return (app_elf_augmented, app_elf_raw, app_elf_import_validator) + app_artifacts.validator = app_env.ValidateAppImports(app_artifacts.compact) + app_env.AlwaysBuild(app_artifacts.validator) + app_env.Alias(app_alias, app_artifacts.validator) + + env["EXT_APPS"][app.appid] = app_artifacts + return app_artifacts def prepare_app_metadata(target, source, env): @@ -182,11 +190,17 @@ def validate_app_imports(target, source, env): app_syms.add(line.split()[0]) unresolved_syms = app_syms - sdk_cache.get_valid_names() if unresolved_syms: - SCons.Warnings.warn( - SCons.Warnings.LinkWarning, - fg.brightyellow(f"{source[0].path}: app won't run. Unresolved symbols: ") - + fg.brightmagenta(f"{unresolved_syms}"), - ) + warning_msg = fg.brightyellow( + f"{source[0].path}: app won't run. Unresolved symbols: " + ) + fg.brightmagenta(f"{unresolved_syms}") + disabled_api_syms = unresolved_syms.intersection(sdk_cache.get_disabled_names()) + if disabled_api_syms: + warning_msg += ( + fg.brightyellow(" (in API, but disabled: ") + + fg.brightmagenta(f"{disabled_api_syms}") + + fg.brightyellow(")") + ) + SCons.Warnings.warn(SCons.Warnings.LinkWarning, warning_msg), def GetExtAppFromPath(env, app_dir): @@ -208,26 +222,26 @@ def GetExtAppFromPath(env, app_dir): if not app: raise UserError(f"Failed to resolve application for given APPSRC={app_dir}") - app_elf = env["_extapps"]["compact"].get(app.appid, None) - if not app_elf: + app_artifacts = env["EXT_APPS"].get(app.appid, None) + if not app_artifacts: raise UserError( f"Application {app.appid} is not configured for building as external" ) - app_validator = env["_extapps"]["validators"].get(app.appid, None) - - return (app, app_elf[0], app_validator[0]) + return app_artifacts def fap_dist_emitter(target, source, env): target_dir = target[0] target = [] - for dist_entry in env["_extapps"]["dist"].values(): - target.append(target_dir.Dir(dist_entry[0]).File(dist_entry[1][0].name)) - - for compact_entry in env["_extapps"]["compact"].values(): - source.extend(compact_entry) + for _, app_artifacts in env["EXT_APPS"].items(): + source.extend(app_artifacts.compact) + target.append( + target_dir.Dir(app_artifacts.app.fap_category).File( + app_artifacts.compact[0].name + ) + ) return (target, source) @@ -244,10 +258,9 @@ def fap_dist_action(target, source, env): def generate(env, **kw): env.SetDefault( - EXT_APPS_WORK_DIR=kw.get("EXT_APPS_WORK_DIR"), + EXT_APPS_WORK_DIR="${FBT_FAP_DEBUG_ELF_ROOT}", APP_RUN_SCRIPT="${FBT_SCRIPT_DIR}/runfap.py", ) - if not env["VERBOSE"]: env.SetDefault( FAPDISTCOMSTR="\tFAPDIST\t${TARGET}", @@ -256,6 +269,10 @@ def generate(env, **kw): APPCHECK_COMSTR="\tAPPCHK\t${SOURCE}", ) + env.SetDefault( + EXT_APPS={}, # appid -> FlipperExternalAppInfo + ) + env.AddMethod(BuildAppElf) env.AddMethod(GetExtAppFromPath) env.Append( diff --git a/scripts/requirements.txt b/scripts/requirements.txt index 35cac774..5b6fac5f 100644 --- a/scripts/requirements.txt +++ b/scripts/requirements.txt @@ -1,7 +1,9 @@ -pyserial==3.5 +ansi==0.3.6 +black==22.6.0 +colorlog==6.7.0 heatshrink2==0.11.0 Pillow==9.1.1 -grpcio==1.47.0 -grpcio-tools==1.47.0 -protobuf==3.20.2 +protobuf==3.20.1 +pyserial==3.5 python3-protobuf==2.5.0 +SCons==4.4.0 diff --git a/site_scons/commandline.scons b/site_scons/commandline.scons index cadb417f..044de6b3 100644 --- a/site_scons/commandline.scons +++ b/site_scons/commandline.scons @@ -147,7 +147,7 @@ vars.AddVariables( PathVariable( "SVD_FILE", help="Path to SVD file", - validator=PathVariable.PathIsFile, + validator=PathVariable.PathAccept, default="", ), PathVariable( diff --git a/site_scons/environ.scons b/site_scons/environ.scons index 96424caa..acdc83e2 100644 --- a/site_scons/environ.scons +++ b/site_scons/environ.scons @@ -61,8 +61,8 @@ coreenv = VAR_ENV.Clone( ABSPATHGETTERFUNC=extract_abs_dir_path, # Setting up temp file parameters - to overcome command line length limits TEMPFILEARGESCFUNC=tempfile_arg_esc_func, - FBT_SCRIPT_DIR=Dir("#/scripts"), ROOT_DIR=Dir("#"), + FBT_SCRIPT_DIR="${ROOT_DIR}/scripts", ) # If DIST_SUFFIX is set in environment, is has precedence (set by CI) diff --git a/site_scons/extapps.scons b/site_scons/extapps.scons index bb1d65ff..3d2a9878 100644 --- a/site_scons/extapps.scons +++ b/site_scons/extapps.scons @@ -1,4 +1,6 @@ +from dataclasses import dataclass, field from SCons.Errors import UserError +from SCons.Node import NodeList Import("ENV") @@ -7,14 +9,7 @@ from fbt.appmanifest import FlipperAppType appenv = ENV["APPENV"] = ENV.Clone( tools=[ - ( - "fbt_extapps", - { - "EXT_APPS_WORK_DIR": ENV.subst( - "${BUILD_DIR}/.extapps", - ) - }, - ), + "fbt_extapps", "fbt_assets", "fbt_sdk", ] @@ -60,22 +55,11 @@ appenv.AppendUnique( ) -extapps = appenv["_extapps"] = { - "compact": {}, - "debug": {}, - "validators": {}, - "dist": {}, - "resources_dist": None, - "sdk_tree": None, -} - - -def build_app_as_external(env, appdef): - compact_elf, debug_elf, validator = env.BuildAppElf(appdef) - extapps["compact"][appdef.appid] = compact_elf - extapps["debug"][appdef.appid] = debug_elf - extapps["validators"][appdef.appid] = validator - extapps["dist"][appdef.appid] = (appdef.fap_category, compact_elf) +@dataclass +class FlipperExtAppBuildArtifacts: + applications: dict = field(default_factory=dict) + resources_dist: NodeList = field(default_factory=NodeList) + sdk_tree: NodeList = field(default_factory=NodeList) apps_to_build_as_faps = [ @@ -85,38 +69,39 @@ apps_to_build_as_faps = [ if appenv["DEBUG_TOOLS"]: apps_to_build_as_faps.append(FlipperAppType.DEBUG) -for apptype in apps_to_build_as_faps: - for app in appenv["APPBUILD"].get_apps_of_type(apptype, True): - build_app_as_external(appenv, app) +known_extapps = [ + app + for apptype in apps_to_build_as_faps + for app in appenv["APPBUILD"].get_apps_of_type(apptype, True) +] # Ugly access to global option if extra_app_list := GetOption("extra_ext_apps"): - for extra_app in extra_app_list.split(","): - build_app_as_external(appenv, appenv["APPMGR"].get(extra_app)) + known_extapps.extend(map(appenv["APPMGR"].get, extra_app_list.split(","))) + +for app in known_extapps: + appenv.BuildAppElf(app) if appenv["FORCE"]: - appenv.AlwaysBuild(extapps["compact"].values()) + appenv.AlwaysBuild( + list(app_artifact.compact for app_artifact in appenv["EXT_APPS"].values()) + ) -# Deprecation stub -def legacy_app_build_stub(**kw): - raise UserError(f"Target name 'firmware_extapps' is deprecated, use 'faps' instead") +Alias( + "faps", list(app_artifact.validator for app_artifact in appenv["EXT_APPS"].values()) +) - -appenv.PhonyTarget("firmware_extapps", appenv.Action(legacy_app_build_stub, None)) - - -Alias("faps", extapps["compact"].values()) -Alias("faps", extapps["validators"].values()) - -extapps["resources_dist"] = appenv.FapDist(appenv.Dir("#/assets/resources/apps"), []) +extapps = FlipperExtAppBuildArtifacts() +extapps.applications = appenv["EXT_APPS"] +extapps.resources_dist = appenv.FapDist(appenv.Dir("#/assets/resources/apps"), []) if appsrc := appenv.subst("$APPSRC"): app_manifest, fap_file, app_validator = appenv.GetExtAppFromPath(appsrc) appenv.PhonyTarget( "launch_app", - '${PYTHON3} "${APP_RUN_SCRIPT}" ${SOURCE} --fap_dst_dir "/ext/apps/${FAP_CATEGORY}"', + '${PYTHON3} "${APP_RUN_SCRIPT}" "${SOURCE}" --fap_dst_dir "/ext/apps/${FAP_CATEGORY}"', source=fap_file, FAP_CATEGORY=app_manifest.fap_category, ) @@ -131,12 +116,14 @@ sdk_source = appenv.SDKPrebuilder( (appenv["SDK_HEADERS"], appenv["FW_ASSETS_HEADERS"]), ) # Extra deps on headers included in deeper levels +# Available on second and subsequent builds Depends(sdk_source, appenv.ProcessSdkDepends(f"{sdk_origin_path}.d")) appenv["SDK_DIR"] = appenv.Dir("${BUILD_DIR}/sdk") -sdk_tree = extapps["sdk_tree"] = appenv.SDKTree(appenv["SDK_DIR"], sdk_origin_path) +sdk_tree = appenv.SDKTree(appenv["SDK_DIR"], sdk_origin_path) # AlwaysBuild(sdk_tree) Alias("sdk_tree", sdk_tree) +extapps.sdk_tree = sdk_tree sdk_apicheck = appenv.SDKSymUpdater(appenv["SDK_DEFINITION"], sdk_origin_path) Precious(sdk_apicheck)