fbt: fixes for ufbt pt3 (#1970)
* fbt: replaced debug dir paths with FBT_DEBUG_DIR * scripts: updated requirements.txt * fbt: fixed wrong import * fbt: removed delayed import for file2image * fbt: added UPDATE_BUNDLE_DIR internal var * fbt: cleaner internal management of extapps * applications: added fap_libs for core apps to link with resources when building with --extra-ext-apps * fbt: removed deprecation stub for faps * fbt: added quotation for icons build cmd * fbt: reworked BUILD_DIR & fap work dir handling; fap debug: using debug elf path from fbt * fbt: explicit LIB_DIST_DIR
This commit is contained in:
		
							parent
							
								
									bf8fd71c00
								
							
						
					
					
						commit
						04e50c9f89
					
				
							
								
								
									
										30
									
								
								SConstruct
									
									
									
									
									
								
							
							
						
						
									
										30
									
								
								SConstruct
									
									
									
									
									
								
							| @ -43,6 +43,7 @@ distenv = coreenv.Clone( | |||||||
|         "jflash", |         "jflash", | ||||||
|     ], |     ], | ||||||
|     ENV=os.environ, |     ENV=os.environ, | ||||||
|  |     UPDATE_BUNDLE_DIR="dist/${DIST_DIR}/f${TARGET_HW}-update-${DIST_SUFFIX}", | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| firmware_env = distenv.AddFwProject( | firmware_env = distenv.AddFwProject( | ||||||
| @ -140,21 +141,28 @@ distenv.Default(basic_dist) | |||||||
| dist_dir = distenv.GetProjetDirName() | dist_dir = distenv.GetProjetDirName() | ||||||
| fap_dist = [ | fap_dist = [ | ||||||
|     distenv.Install( |     distenv.Install( | ||||||
|         f"#/dist/{dist_dir}/apps/debug_elf", |         distenv.Dir(f"#/dist/{dist_dir}/apps/debug_elf"), | ||||||
|         firmware_env["FW_EXTAPPS"]["debug"].values(), |         list( | ||||||
|  |             app_artifact.debug | ||||||
|  |             for app_artifact in firmware_env["FW_EXTAPPS"].applications.values() | ||||||
|  |         ), | ||||||
|     ), |     ), | ||||||
|     *( |     distenv.Install( | ||||||
|         distenv.Install(f"#/dist/{dist_dir}/apps/{dist_entry[0]}", dist_entry[1]) |         f"#/dist/{dist_dir}/apps", | ||||||
|         for dist_entry in firmware_env["FW_EXTAPPS"]["dist"].values() |         "#/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) | Alias("fap_dist", fap_dist) | ||||||
| # distenv.Default(fap_dist) | # distenv.Default(fap_dist) | ||||||
| 
 | 
 | ||||||
| distenv.Depends( | distenv.Depends(firmware_env["FW_RESOURCES"], firmware_env["FW_EXTAPPS"].resources_dist) | ||||||
|     firmware_env["FW_RESOURCES"], firmware_env["FW_EXTAPPS"]["resources_dist"] |  | ||||||
| ) |  | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| # Target for bundling core2 package for qFlipper | # Target for bundling core2 package for qFlipper | ||||||
| @ -192,6 +200,7 @@ firmware_debug = distenv.PhonyTarget( | |||||||
|     source=firmware_env["FW_ELF"], |     source=firmware_env["FW_ELF"], | ||||||
|     GDBOPTS="${GDBOPTS_BASE}", |     GDBOPTS="${GDBOPTS_BASE}", | ||||||
|     GDBREMOTE="${OPENOCD_GDB_PIPE}", |     GDBREMOTE="${OPENOCD_GDB_PIPE}", | ||||||
|  |     FBT_FAP_DEBUG_ELF_ROOT=firmware_env.subst("$FBT_FAP_DEBUG_ELF_ROOT"), | ||||||
| ) | ) | ||||||
| distenv.Depends(firmware_debug, firmware_flash) | distenv.Depends(firmware_debug, firmware_flash) | ||||||
| 
 | 
 | ||||||
| @ -201,6 +210,7 @@ distenv.PhonyTarget( | |||||||
|     source=firmware_env["FW_ELF"], |     source=firmware_env["FW_ELF"], | ||||||
|     GDBOPTS="${GDBOPTS_BASE} ${GDBOPTS_BLACKMAGIC}", |     GDBOPTS="${GDBOPTS_BASE} ${GDBOPTS_BLACKMAGIC}", | ||||||
|     GDBREMOTE="${BLACKMAGIC_ADDR}", |     GDBREMOTE="${BLACKMAGIC_ADDR}", | ||||||
|  |     FBT_FAP_DEBUG_ELF_ROOT=firmware_env.subst("$FBT_FAP_DEBUG_ELF_ROOT"), | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| # Debug alien elf | # Debug alien elf | ||||||
| @ -209,7 +219,7 @@ distenv.PhonyTarget( | |||||||
|     "${GDBPYCOM}", |     "${GDBPYCOM}", | ||||||
|     GDBOPTS="${GDBOPTS_BASE}", |     GDBOPTS="${GDBOPTS_BASE}", | ||||||
|     GDBREMOTE="${OPENOCD_GDB_PIPE}", |     GDBREMOTE="${OPENOCD_GDB_PIPE}", | ||||||
|     GDBPYOPTS='-ex "source debug/PyCortexMDebug/PyCortexMDebug.py" ', |     GDBPYOPTS='-ex "source ${FBT_DEBUG_DIR}/PyCortexMDebug/PyCortexMDebug.py" ', | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| distenv.PhonyTarget( | distenv.PhonyTarget( | ||||||
|  | |||||||
| @ -11,4 +11,5 @@ App( | |||||||
|     stack_size=2 * 1024, |     stack_size=2 * 1024, | ||||||
|     icon="A_BadUsb_14", |     icon="A_BadUsb_14", | ||||||
|     order=70, |     order=70, | ||||||
|  |     fap_libs=["assets"], | ||||||
| ) | ) | ||||||
|  | |||||||
| @ -8,4 +8,5 @@ App( | |||||||
|     stack_size=1 * 1024, |     stack_size=1 * 1024, | ||||||
|     icon="A_GPIO_14", |     icon="A_GPIO_14", | ||||||
|     order=50, |     order=50, | ||||||
|  |     fap_libs=["assets"], | ||||||
| ) | ) | ||||||
|  | |||||||
| @ -12,6 +12,7 @@ App( | |||||||
|     icon="A_iButton_14", |     icon="A_iButton_14", | ||||||
|     stack_size=2 * 1024, |     stack_size=2 * 1024, | ||||||
|     order=60, |     order=60, | ||||||
|  |     fap_libs=["assets"], | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| App( | App( | ||||||
|  | |||||||
| @ -12,6 +12,7 @@ App( | |||||||
|     icon="A_Infrared_14", |     icon="A_Infrared_14", | ||||||
|     stack_size=3 * 1024, |     stack_size=3 * 1024, | ||||||
|     order=40, |     order=40, | ||||||
|  |     fap_libs=["assets"], | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| App( | App( | ||||||
|  | |||||||
| @ -14,6 +14,7 @@ App( | |||||||
|     icon="A_125khz_14", |     icon="A_125khz_14", | ||||||
|     stack_size=2 * 1024, |     stack_size=2 * 1024, | ||||||
|     order=20, |     order=20, | ||||||
|  |     fap_libs=["assets"], | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| App( | App( | ||||||
|  | |||||||
| @ -11,4 +11,5 @@ App( | |||||||
|     stack_size=2 * 1024, |     stack_size=2 * 1024, | ||||||
|     icon="A_U2F_14", |     icon="A_U2F_14", | ||||||
|     order=80, |     order=80, | ||||||
|  |     fap_libs=["assets"], | ||||||
| ) | ) | ||||||
|  | |||||||
| @ -1,5 +1,5 @@ | |||||||
| from dataclasses import dataclass | from dataclasses import dataclass | ||||||
| from typing import Tuple, Dict | from typing import Optional, Tuple, Dict, ClassVar | ||||||
| import struct | import struct | ||||||
| import posixpath | import posixpath | ||||||
| import os | import os | ||||||
| @ -22,14 +22,18 @@ class AppState: | |||||||
|     debug_link_elf: str = "" |     debug_link_elf: str = "" | ||||||
|     debug_link_crc: int = 0 |     debug_link_crc: int = 0 | ||||||
| 
 | 
 | ||||||
|  |     DEBUG_ELF_ROOT: ClassVar[Optional[str]] = None | ||||||
|  | 
 | ||||||
|     def __post_init__(self): |     def __post_init__(self): | ||||||
|         if self.other_sections is None: |         if self.other_sections is None: | ||||||
|             self.other_sections = {} |             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 ( |         return ( | ||||||
|             posixpath.join(elf_path, self.debug_link_elf) |             posixpath.join(self.DEBUG_ELF_ROOT, self.debug_link_elf) | ||||||
|             if elf_path |             if self.DEBUG_ELF_ROOT | ||||||
|             else self.debug_link_elf |             else self.debug_link_elf | ||||||
|         ) |         ) | ||||||
| 
 | 
 | ||||||
| @ -84,7 +88,9 @@ class AppState: | |||||||
|         if debug_link_size := int(app_state["debug_link_info"]["debug_link_size"]): |         if debug_link_size := int(app_state["debug_link_info"]["debug_link_size"]): | ||||||
|             debug_link_data = ( |             debug_link_data = ( | ||||||
|                 gdb.selected_inferior() |                 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() |                 .tobytes() | ||||||
|             ) |             ) | ||||||
|             state.debug_link_elf, state.debug_link_crc = AppState.parse_debug_link_data( |             state.debug_link_elf, state.debug_link_crc = AppState.parse_debug_link_data( | ||||||
| @ -103,6 +109,29 @@ class AppState: | |||||||
|         return state |         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: | class FlipperAppDebugHelper: | ||||||
|     def __init__(self): |     def __init__(self): | ||||||
|         self.app_ptr = None |         self.app_ptr = None | ||||||
| @ -149,9 +178,4 @@ class FlipperAppDebugHelper: | |||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| helper = FlipperAppDebugHelper() | helper = FlipperAppDebugHelper() | ||||||
| try: | print("Support for Flipper external apps debug is loaded") | ||||||
|     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}") |  | ||||||
|  | |||||||
| @ -49,12 +49,12 @@ OPENOCD_OPTS = [ | |||||||
|     "-c", |     "-c", | ||||||
|     "transport select hla_swd", |     "transport select hla_swd", | ||||||
|     "-f", |     "-f", | ||||||
|     "debug/stm32wbx.cfg", |     "${FBT_DEBUG_DIR}/stm32wbx.cfg", | ||||||
|     "-c", |     "-c", | ||||||
|     "stm32wbx.cpu configure -rtos auto", |     "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 | # Look for blackmagic probe on serial ports and local network | ||||||
| BLACKMAGIC = "auto" | BLACKMAGIC = "auto" | ||||||
|  | |||||||
| @ -20,8 +20,7 @@ env = ENV.Clone( | |||||||
|     BUILD_DIR=fw_build_meta["build_dir"], |     BUILD_DIR=fw_build_meta["build_dir"], | ||||||
|     IS_BASE_FIRMWARE=fw_build_meta["type"] == "firmware", |     IS_BASE_FIRMWARE=fw_build_meta["type"] == "firmware", | ||||||
|     FW_FLAVOR=fw_build_meta["flavor"], |     FW_FLAVOR=fw_build_meta["flavor"], | ||||||
|     PLUGIN_ELF_DIR="${BUILD_DIR}", |     LIB_DIST_DIR=fw_build_meta["build_dir"].Dir("lib"), | ||||||
|     LIB_DIST_DIR="${BUILD_DIR}/lib", |  | ||||||
|     LINT_SOURCES=[ |     LINT_SOURCES=[ | ||||||
|         "applications", |         "applications", | ||||||
|     ], |     ], | ||||||
| @ -142,12 +141,14 @@ for app_dir, _ in env["APPDIRS"]: | |||||||
| 
 | 
 | ||||||
| fwenv.PrepareApplicationsBuild() | fwenv.PrepareApplicationsBuild() | ||||||
| 
 | 
 | ||||||
| # Build external apps | # Build external apps + configure SDK | ||||||
| if env["IS_BASE_FIRMWARE"]: | if env["IS_BASE_FIRMWARE"]: | ||||||
|     extapps = fwenv["FW_EXTAPPS"] = SConscript( |     fwenv.SetDefault(FBT_FAP_DEBUG_ELF_ROOT="${BUILD_DIR}/.extapps") | ||||||
|         "site_scons/extapps.scons", exports={"ENV": fwenv} |     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 | # Add preprocessor definitions for current set of apps | ||||||
|  | |||||||
| @ -5,6 +5,7 @@ import struct | |||||||
| from dataclasses import dataclass, field | from dataclasses import dataclass, field | ||||||
| 
 | 
 | ||||||
| from .appmanifest import FlipperApplication | from .appmanifest import FlipperApplication | ||||||
|  | from flipper.assets.icon import file2image | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| _MANIFEST_MAGIC = 0x52474448 | _MANIFEST_MAGIC = 0x52474448 | ||||||
| @ -53,8 +54,6 @@ def assemble_manifest_data( | |||||||
| ): | ): | ||||||
|     image_data = b"" |     image_data = b"" | ||||||
|     if app_manifest.fap_icon: |     if app_manifest.fap_icon: | ||||||
|         from flipper.assets.icon import file2image |  | ||||||
| 
 |  | ||||||
|         image = file2image(os.path.join(app_manifest._apppath, app_manifest.fap_icon)) |         image = file2image(os.path.join(app_manifest._apppath, app_manifest.fap_icon)) | ||||||
|         if (image.width, image.height) != (10, 10): |         if (image.width, image.height) != (10, 10): | ||||||
|             raise ValueError( |             raise ValueError( | ||||||
|  | |||||||
| @ -89,6 +89,9 @@ class SdkCache: | |||||||
|         syms.update(map(lambda e: e.name, self.get_variables())) |         syms.update(map(lambda e: e.name, self.get_variables())) | ||||||
|         return syms |         return syms | ||||||
| 
 | 
 | ||||||
|  |     def get_disabled_names(self): | ||||||
|  |         return set(map(lambda e: e.name, self.disabled_entries)) | ||||||
|  | 
 | ||||||
|     def get_functions(self): |     def get_functions(self): | ||||||
|         return self._filter_enabled(self.sdk.functions) |         return self._filter_enabled(self.sdk.functions) | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -139,7 +139,7 @@ def generate(env): | |||||||
|         BUILDERS={ |         BUILDERS={ | ||||||
|             "IconBuilder": Builder( |             "IconBuilder": Builder( | ||||||
|                 action=Action( |                 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}", |                     "${ICONSCOMSTR}", | ||||||
|                 ), |                 ), | ||||||
|                 emitter=icons_emitter, |                 emitter=icons_emitter, | ||||||
|  | |||||||
| @ -1,7 +1,6 @@ | |||||||
| from re import search | from re import search | ||||||
| 
 | 
 | ||||||
| from SCons.Errors import UserError | from SCons.Errors import UserError | ||||||
| from fbt_options import OPENOCD_OPTS |  | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| def _get_device_serials(search_str="STLink"): | def _get_device_serials(search_str="STLink"): | ||||||
| @ -20,6 +19,9 @@ def GetDevices(env): | |||||||
| 
 | 
 | ||||||
| def generate(env, **kw): | def generate(env, **kw): | ||||||
|     env.AddMethod(GetDevices) |     env.AddMethod(GetDevices) | ||||||
|  |     env.SetDefault( | ||||||
|  |         FBT_DEBUG_DIR="${ROOT_DIR}/debug", | ||||||
|  |     ) | ||||||
| 
 | 
 | ||||||
|     if (adapter_serial := env.subst("$OPENOCD_ADAPTER_SERIAL")) != "auto": |     if (adapter_serial := env.subst("$OPENOCD_ADAPTER_SERIAL")) != "auto": | ||||||
|         env.Append( |         env.Append( | ||||||
| @ -36,7 +38,7 @@ def generate(env, **kw): | |||||||
| 
 | 
 | ||||||
|     env.SetDefault( |     env.SetDefault( | ||||||
|         OPENOCD_GDB_PIPE=[ |         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=[ |         GDBOPTS_BASE=[ | ||||||
|             "-ex", |             "-ex", | ||||||
| @ -58,17 +60,19 @@ def generate(env, **kw): | |||||||
|         ], |         ], | ||||||
|         GDBPYOPTS=[ |         GDBPYOPTS=[ | ||||||
|             "-ex", |             "-ex", | ||||||
|             "source debug/FreeRTOS/FreeRTOS.py", |             "source ${FBT_DEBUG_DIR}/FreeRTOS/FreeRTOS.py", | ||||||
|             "-ex", |             "-ex", | ||||||
|             "source debug/flipperapps.py", |             "source ${FBT_DEBUG_DIR}/flipperapps.py", | ||||||
|             "-ex", |             "-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", |             "-ex", | ||||||
|             "svd_load ${SVD_FILE}", |             "svd_load ${SVD_FILE}", | ||||||
|             "-ex", |             "-ex", | ||||||
|             "compare-sections", |             "compare-sections", | ||||||
|         ], |         ], | ||||||
|         JFLASHPROJECT="${ROOT_DIR.abspath}/debug/fw.jflash", |         JFLASHPROJECT="${FBT_DEBUG_DIR}/fw.jflash", | ||||||
|     ) |     ) | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -22,7 +22,7 @@ def GetProjetDirName(env, project=None): | |||||||
| 
 | 
 | ||||||
| def create_fw_build_targets(env, configuration_name): | def create_fw_build_targets(env, configuration_name): | ||||||
|     flavor = GetProjetDirName(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( |     return env.SConscript( | ||||||
|         "firmware.scons", |         "firmware.scons", | ||||||
|         variant_dir=build_dir, |         variant_dir=build_dir, | ||||||
| @ -131,7 +131,7 @@ def generate(env): | |||||||
|             "UsbInstall": Builder( |             "UsbInstall": Builder( | ||||||
|                 action=[ |                 action=[ | ||||||
|                     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}"), |                     Touch("${TARGET}"), | ||||||
|                 ] |                 ] | ||||||
|  | |||||||
| @ -1,6 +1,9 @@ | |||||||
|  | from dataclasses import dataclass, field | ||||||
|  | from typing import Optional | ||||||
| from SCons.Builder import Builder | from SCons.Builder import Builder | ||||||
| from SCons.Action import Action | from SCons.Action import Action | ||||||
| from SCons.Errors import UserError | from SCons.Errors import UserError | ||||||
|  | from SCons.Node import NodeList | ||||||
| import SCons.Warnings | import SCons.Warnings | ||||||
| 
 | 
 | ||||||
| from fbt.elfmanifest import assemble_manifest_data | from fbt.elfmanifest import assemble_manifest_data | ||||||
| @ -16,6 +19,15 @@ import shutil | |||||||
| from ansi.color import fg | 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): | def BuildAppElf(env, app): | ||||||
|     ext_apps_work_dir = env.subst("$EXT_APPS_WORK_DIR") |     ext_apps_work_dir = env.subst("$EXT_APPS_WORK_DIR") | ||||||
|     app_work_dir = os.path.join(ext_apps_work_dir, app.appid) |     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}" |     app_alias = f"fap_{app.appid}" | ||||||
| 
 | 
 | ||||||
|     # Deprecation stub |     app_artifacts = FlipperExternalAppInfo(app) | ||||||
|     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)) |  | ||||||
| 
 | 
 | ||||||
|     externally_built_files = [] |     externally_built_files = [] | ||||||
|     if app.fap_extbuild: |     if app.fap_extbuild: | ||||||
| @ -115,20 +119,22 @@ def BuildAppElf(env, app): | |||||||
|         CPPPATH=env.Dir(app_work_dir), |         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"), |         os.path.join(ext_apps_work_dir, f"{app.appid}_d"), | ||||||
|         app_sources, |         app_sources, | ||||||
|         APP_ENTRY=app.entry_point, |         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_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), |         os.path.join(ext_apps_work_dir, app.appid), | ||||||
|         app_elf_raw, |         app_artifacts.debug, | ||||||
|         APP=app, |         APP=app, | ||||||
|     ) |     ) | ||||||
| 
 | 
 | ||||||
| @ -139,19 +145,21 @@ def BuildAppElf(env, app): | |||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     app_env.Depends( |     app_env.Depends( | ||||||
|         app_elf_augmented, |         app_artifacts.compact, | ||||||
|         [app_env["SDK_DEFINITION"], app_env.Value(manifest_vals)], |         [app_env["SDK_DEFINITION"], app_env.Value(manifest_vals)], | ||||||
|     ) |     ) | ||||||
|     if app.fap_icon: |     if app.fap_icon: | ||||||
|         app_env.Depends( |         app_env.Depends( | ||||||
|             app_elf_augmented, |             app_artifacts.compact, | ||||||
|             app_env.File(f"{app._apppath}/{app.fap_icon}"), |             app_env.File(f"{app._apppath}/{app.fap_icon}"), | ||||||
|         ) |         ) | ||||||
| 
 | 
 | ||||||
|     app_elf_import_validator = app_env.ValidateAppImports(app_elf_augmented) |     app_artifacts.validator = app_env.ValidateAppImports(app_artifacts.compact) | ||||||
|     app_env.AlwaysBuild(app_elf_import_validator) |     app_env.AlwaysBuild(app_artifacts.validator) | ||||||
|     app_env.Alias(app_alias, app_elf_import_validator) |     app_env.Alias(app_alias, app_artifacts.validator) | ||||||
|     return (app_elf_augmented, app_elf_raw, app_elf_import_validator) | 
 | ||||||
|  |     env["EXT_APPS"][app.appid] = app_artifacts | ||||||
|  |     return app_artifacts | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| def prepare_app_metadata(target, source, env): | def prepare_app_metadata(target, source, env): | ||||||
| @ -182,11 +190,17 @@ def validate_app_imports(target, source, env): | |||||||
|             app_syms.add(line.split()[0]) |             app_syms.add(line.split()[0]) | ||||||
|     unresolved_syms = app_syms - sdk_cache.get_valid_names() |     unresolved_syms = app_syms - sdk_cache.get_valid_names() | ||||||
|     if unresolved_syms: |     if unresolved_syms: | ||||||
|         SCons.Warnings.warn( |         warning_msg = fg.brightyellow( | ||||||
|             SCons.Warnings.LinkWarning, |             f"{source[0].path}: app won't run. Unresolved symbols: " | ||||||
|             fg.brightyellow(f"{source[0].path}: app won't run. Unresolved symbols: ") |         ) + fg.brightmagenta(f"{unresolved_syms}") | ||||||
|             + 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): | def GetExtAppFromPath(env, app_dir): | ||||||
| @ -208,26 +222,26 @@ def GetExtAppFromPath(env, app_dir): | |||||||
|     if not app: |     if not app: | ||||||
|         raise UserError(f"Failed to resolve application for given APPSRC={app_dir}") |         raise UserError(f"Failed to resolve application for given APPSRC={app_dir}") | ||||||
| 
 | 
 | ||||||
|     app_elf = env["_extapps"]["compact"].get(app.appid, None) |     app_artifacts = env["EXT_APPS"].get(app.appid, None) | ||||||
|     if not app_elf: |     if not app_artifacts: | ||||||
|         raise UserError( |         raise UserError( | ||||||
|             f"Application {app.appid} is not configured for building as external" |             f"Application {app.appid} is not configured for building as external" | ||||||
|         ) |         ) | ||||||
| 
 | 
 | ||||||
|     app_validator = env["_extapps"]["validators"].get(app.appid, None) |     return app_artifacts | ||||||
| 
 |  | ||||||
|     return (app, app_elf[0], app_validator[0]) |  | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| def fap_dist_emitter(target, source, env): | def fap_dist_emitter(target, source, env): | ||||||
|     target_dir = target[0] |     target_dir = target[0] | ||||||
| 
 | 
 | ||||||
|     target = [] |     target = [] | ||||||
|     for dist_entry in env["_extapps"]["dist"].values(): |     for _, app_artifacts in env["EXT_APPS"].items(): | ||||||
|         target.append(target_dir.Dir(dist_entry[0]).File(dist_entry[1][0].name)) |         source.extend(app_artifacts.compact) | ||||||
| 
 |         target.append( | ||||||
|     for compact_entry in env["_extapps"]["compact"].values(): |             target_dir.Dir(app_artifacts.app.fap_category).File( | ||||||
|         source.extend(compact_entry) |                 app_artifacts.compact[0].name | ||||||
|  |             ) | ||||||
|  |         ) | ||||||
| 
 | 
 | ||||||
|     return (target, source) |     return (target, source) | ||||||
| 
 | 
 | ||||||
| @ -244,10 +258,9 @@ def fap_dist_action(target, source, env): | |||||||
| 
 | 
 | ||||||
| def generate(env, **kw): | def generate(env, **kw): | ||||||
|     env.SetDefault( |     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", |         APP_RUN_SCRIPT="${FBT_SCRIPT_DIR}/runfap.py", | ||||||
|     ) |     ) | ||||||
| 
 |  | ||||||
|     if not env["VERBOSE"]: |     if not env["VERBOSE"]: | ||||||
|         env.SetDefault( |         env.SetDefault( | ||||||
|             FAPDISTCOMSTR="\tFAPDIST\t${TARGET}", |             FAPDISTCOMSTR="\tFAPDIST\t${TARGET}", | ||||||
| @ -256,6 +269,10 @@ def generate(env, **kw): | |||||||
|             APPCHECK_COMSTR="\tAPPCHK\t${SOURCE}", |             APPCHECK_COMSTR="\tAPPCHK\t${SOURCE}", | ||||||
|         ) |         ) | ||||||
| 
 | 
 | ||||||
|  |     env.SetDefault( | ||||||
|  |         EXT_APPS={},  # appid -> FlipperExternalAppInfo | ||||||
|  |     ) | ||||||
|  | 
 | ||||||
|     env.AddMethod(BuildAppElf) |     env.AddMethod(BuildAppElf) | ||||||
|     env.AddMethod(GetExtAppFromPath) |     env.AddMethod(GetExtAppFromPath) | ||||||
|     env.Append( |     env.Append( | ||||||
|  | |||||||
| @ -1,7 +1,9 @@ | |||||||
| pyserial==3.5 | ansi==0.3.6 | ||||||
|  | black==22.6.0 | ||||||
|  | colorlog==6.7.0 | ||||||
| heatshrink2==0.11.0 | heatshrink2==0.11.0 | ||||||
| Pillow==9.1.1 | Pillow==9.1.1 | ||||||
| grpcio==1.47.0 | protobuf==3.20.1 | ||||||
| grpcio-tools==1.47.0 | pyserial==3.5 | ||||||
| protobuf==3.20.2 |  | ||||||
| python3-protobuf==2.5.0 | python3-protobuf==2.5.0 | ||||||
|  | SCons==4.4.0 | ||||||
|  | |||||||
| @ -147,7 +147,7 @@ vars.AddVariables( | |||||||
|     PathVariable( |     PathVariable( | ||||||
|         "SVD_FILE", |         "SVD_FILE", | ||||||
|         help="Path to SVD file", |         help="Path to SVD file", | ||||||
|         validator=PathVariable.PathIsFile, |         validator=PathVariable.PathAccept, | ||||||
|         default="", |         default="", | ||||||
|     ), |     ), | ||||||
|     PathVariable( |     PathVariable( | ||||||
|  | |||||||
| @ -61,8 +61,8 @@ coreenv = VAR_ENV.Clone( | |||||||
|     ABSPATHGETTERFUNC=extract_abs_dir_path, |     ABSPATHGETTERFUNC=extract_abs_dir_path, | ||||||
|     # Setting up temp file parameters - to overcome command line length limits |     # Setting up temp file parameters - to overcome command line length limits | ||||||
|     TEMPFILEARGESCFUNC=tempfile_arg_esc_func, |     TEMPFILEARGESCFUNC=tempfile_arg_esc_func, | ||||||
|     FBT_SCRIPT_DIR=Dir("#/scripts"), |  | ||||||
|     ROOT_DIR=Dir("#"), |     ROOT_DIR=Dir("#"), | ||||||
|  |     FBT_SCRIPT_DIR="${ROOT_DIR}/scripts", | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| # If DIST_SUFFIX is set in environment, is has precedence (set by CI) | # If DIST_SUFFIX is set in environment, is has precedence (set by CI) | ||||||
|  | |||||||
| @ -1,4 +1,6 @@ | |||||||
|  | from dataclasses import dataclass, field | ||||||
| from SCons.Errors import UserError | from SCons.Errors import UserError | ||||||
|  | from SCons.Node import NodeList | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| Import("ENV") | Import("ENV") | ||||||
| @ -7,14 +9,7 @@ from fbt.appmanifest import FlipperAppType | |||||||
| 
 | 
 | ||||||
| appenv = ENV["APPENV"] = ENV.Clone( | appenv = ENV["APPENV"] = ENV.Clone( | ||||||
|     tools=[ |     tools=[ | ||||||
|         ( |         "fbt_extapps", | ||||||
|             "fbt_extapps", |  | ||||||
|             { |  | ||||||
|                 "EXT_APPS_WORK_DIR": ENV.subst( |  | ||||||
|                     "${BUILD_DIR}/.extapps", |  | ||||||
|                 ) |  | ||||||
|             }, |  | ||||||
|         ), |  | ||||||
|         "fbt_assets", |         "fbt_assets", | ||||||
|         "fbt_sdk", |         "fbt_sdk", | ||||||
|     ] |     ] | ||||||
| @ -60,22 +55,11 @@ appenv.AppendUnique( | |||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| extapps = appenv["_extapps"] = { | @dataclass | ||||||
|     "compact": {}, | class FlipperExtAppBuildArtifacts: | ||||||
|     "debug": {}, |     applications: dict = field(default_factory=dict) | ||||||
|     "validators": {}, |     resources_dist: NodeList = field(default_factory=NodeList) | ||||||
|     "dist": {}, |     sdk_tree: NodeList = field(default_factory=NodeList) | ||||||
|     "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) |  | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| apps_to_build_as_faps = [ | apps_to_build_as_faps = [ | ||||||
| @ -85,38 +69,39 @@ apps_to_build_as_faps = [ | |||||||
| if appenv["DEBUG_TOOLS"]: | if appenv["DEBUG_TOOLS"]: | ||||||
|     apps_to_build_as_faps.append(FlipperAppType.DEBUG) |     apps_to_build_as_faps.append(FlipperAppType.DEBUG) | ||||||
| 
 | 
 | ||||||
| for apptype in apps_to_build_as_faps: | known_extapps = [ | ||||||
|     for app in appenv["APPBUILD"].get_apps_of_type(apptype, True): |     app | ||||||
|         build_app_as_external(appenv, 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 | # Ugly access to global option | ||||||
| if extra_app_list := GetOption("extra_ext_apps"): | if extra_app_list := GetOption("extra_ext_apps"): | ||||||
|     for extra_app in extra_app_list.split(","): |     known_extapps.extend(map(appenv["APPMGR"].get, extra_app_list.split(","))) | ||||||
|         build_app_as_external(appenv, appenv["APPMGR"].get(extra_app)) | 
 | ||||||
|  | for app in known_extapps: | ||||||
|  |     appenv.BuildAppElf(app) | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| if appenv["FORCE"]: | if appenv["FORCE"]: | ||||||
|     appenv.AlwaysBuild(extapps["compact"].values()) |     appenv.AlwaysBuild( | ||||||
|  |         list(app_artifact.compact for app_artifact in appenv["EXT_APPS"].values()) | ||||||
|  |     ) | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| # Deprecation stub | Alias( | ||||||
| def legacy_app_build_stub(**kw): |     "faps", list(app_artifact.validator for app_artifact in appenv["EXT_APPS"].values()) | ||||||
|     raise UserError(f"Target name 'firmware_extapps' is deprecated, use 'faps' instead") | ) | ||||||
| 
 | 
 | ||||||
| 
 | extapps = FlipperExtAppBuildArtifacts() | ||||||
| appenv.PhonyTarget("firmware_extapps", appenv.Action(legacy_app_build_stub, None)) | extapps.applications = appenv["EXT_APPS"] | ||||||
| 
 | extapps.resources_dist = appenv.FapDist(appenv.Dir("#/assets/resources/apps"), []) | ||||||
| 
 |  | ||||||
| Alias("faps", extapps["compact"].values()) |  | ||||||
| Alias("faps", extapps["validators"].values()) |  | ||||||
| 
 |  | ||||||
| extapps["resources_dist"] = appenv.FapDist(appenv.Dir("#/assets/resources/apps"), []) |  | ||||||
| 
 | 
 | ||||||
| if appsrc := appenv.subst("$APPSRC"): | if appsrc := appenv.subst("$APPSRC"): | ||||||
|     app_manifest, fap_file, app_validator = appenv.GetExtAppFromPath(appsrc) |     app_manifest, fap_file, app_validator = appenv.GetExtAppFromPath(appsrc) | ||||||
|     appenv.PhonyTarget( |     appenv.PhonyTarget( | ||||||
|         "launch_app", |         "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, |         source=fap_file, | ||||||
|         FAP_CATEGORY=app_manifest.fap_category, |         FAP_CATEGORY=app_manifest.fap_category, | ||||||
|     ) |     ) | ||||||
| @ -131,12 +116,14 @@ sdk_source = appenv.SDKPrebuilder( | |||||||
|     (appenv["SDK_HEADERS"], appenv["FW_ASSETS_HEADERS"]), |     (appenv["SDK_HEADERS"], appenv["FW_ASSETS_HEADERS"]), | ||||||
| ) | ) | ||||||
| # Extra deps on headers included in deeper levels | # Extra deps on headers included in deeper levels | ||||||
|  | # Available on second and subsequent builds | ||||||
| Depends(sdk_source, appenv.ProcessSdkDepends(f"{sdk_origin_path}.d")) | Depends(sdk_source, appenv.ProcessSdkDepends(f"{sdk_origin_path}.d")) | ||||||
| 
 | 
 | ||||||
| appenv["SDK_DIR"] = appenv.Dir("${BUILD_DIR}/sdk") | 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) | # AlwaysBuild(sdk_tree) | ||||||
| Alias("sdk_tree", sdk_tree) | Alias("sdk_tree", sdk_tree) | ||||||
|  | extapps.sdk_tree = sdk_tree | ||||||
| 
 | 
 | ||||||
| sdk_apicheck = appenv.SDKSymUpdater(appenv["SDK_DEFINITION"], sdk_origin_path) | sdk_apicheck = appenv.SDKSymUpdater(appenv["SDK_DEFINITION"], sdk_origin_path) | ||||||
| Precious(sdk_apicheck) | Precious(sdk_apicheck) | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 hedger
						hedger