From a959fa32bc65620460d2bcfd593a12ddcf55def3 Mon Sep 17 00:00:00 2001 From: hedger Date: Thu, 10 Nov 2022 15:55:11 +0400 Subject: [PATCH] fbt: 'target' field for apps; lib debugging support (#1995) * fbt: added 'target' field to application manifest * fbt: earlier pagination setup for gdb * fbt: added LIB_DEBUG flag * fbt: sdk: added SDK_MAP_FILE_SUBST --- SConstruct | 9 +++------ documentation/AppManifests.md | 1 + firmware.scons | 4 ++-- scripts/fbt/appmanifest.py | 20 +++++++++++++++++--- scripts/fbt/util.py | 7 ++++++- scripts/fbt_tools/fbt_apps.py | 13 +++++++------ scripts/fbt_tools/fbt_debugopts.py | 4 ++-- scripts/fbt_tools/fbt_sdk.py | 22 ++++++++++++++-------- site_scons/commandline.scons | 5 +++++ site_scons/extapps.scons | 10 +++++++++- 10 files changed, 66 insertions(+), 29 deletions(-) diff --git a/SConstruct b/SConstruct index 3ee89f646..34ff80bc9 100644 --- a/SConstruct +++ b/SConstruct @@ -7,6 +7,7 @@ # construction of certain targets behind command-line options. import os +from fbt.util import path_as_posix DefaultEnvironment(tools=[]) @@ -200,9 +201,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").replace( - "\\", "/" - ), + FBT_FAP_DEBUG_ELF_ROOT=path_as_posix(firmware_env.subst("$FBT_FAP_DEBUG_ELF_ROOT")), ) distenv.Depends(firmware_debug, firmware_flash) @@ -212,9 +211,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").replace( - "\\", "/" - ), + FBT_FAP_DEBUG_ELF_ROOT=path_as_posix(firmware_env.subst("$FBT_FAP_DEBUG_ELF_ROOT")), ) # Debug alien elf diff --git a/documentation/AppManifests.md b/documentation/AppManifests.md index f4814ee5d..d70a12f9c 100644 --- a/documentation/AppManifests.md +++ b/documentation/AppManifests.md @@ -40,6 +40,7 @@ Only 2 parameters are mandatory: ***appid*** and ***apptype***, others are optio * **icon**: Animated icon name from built-in assets to be used when building app as a part of firmware. * **order**: Order of an application within its group when sorting entries in it. The lower the order is, the closer to the start of the list the item is placed. *Used for ordering startup hooks and menu entries.* * **sdk_headers**: List of C header files from this app's code to include in API definitions for external applications. +* **targets**: list of strings, target names, which this application is compatible with. If not specified, application is built for all targets. Default value is `["all"]`. #### Parameters for external applications diff --git a/firmware.scons b/firmware.scons index 6a9237477..da5caba53 100644 --- a/firmware.scons +++ b/firmware.scons @@ -40,11 +40,11 @@ env = ENV.Clone( FW_LIB_OPTS={ "Default": { "CCFLAGS": [ - "-Os", + "-Og" if ENV["LIB_DEBUG"] else "-Os", ], "CPPDEFINES": [ "NDEBUG", - "FURI_NDEBUG", + "FURI_DEBUG" if ENV["LIB_DEBUG"] else "FURI_NDEBUG", ], # You can add other entries named after libraries # If they are present, they have precedence over Default diff --git a/scripts/fbt/appmanifest.py b/scripts/fbt/appmanifest.py index de7c6b682..908a64a6f 100644 --- a/scripts/fbt/appmanifest.py +++ b/scripts/fbt/appmanifest.py @@ -52,6 +52,8 @@ class FlipperApplication: icon: Optional[str] = None order: int = 0 sdk_headers: List[str] = field(default_factory=list) + targets: List[str] = field(default_factory=lambda: ["all"]) + # .fap-specific sources: List[str] = field(default_factory=lambda: ["*.c*"]) fap_version: Tuple[int] = field(default_factory=lambda: (0, 1)) @@ -135,8 +137,8 @@ class AppManager: raise FlipperManifestException(f"Duplicate app declaration: {app.appid}") self.known_apps[app.appid] = app - def filter_apps(self, applist: List[str]): - return AppBuildset(self, applist) + def filter_apps(self, applist: List[str], hw_target: str): + return AppBuildset(self, applist, hw_target) class AppBuilderException(Exception): @@ -155,11 +157,13 @@ class AppBuildset: FlipperAppType.STARTUP, ) - def __init__(self, appmgr: AppManager, appnames: List[str]): + def __init__(self, appmgr: AppManager, appnames: List[str], hw_target: str): self.appmgr = appmgr self.appnames = set(appnames) + self.hw_target = hw_target self._orig_appnames = appnames self._process_deps() + self._filter_by_target() self._check_conflicts() self._check_unsatisfied() # unneeded? self.apps = sorted( @@ -170,6 +174,16 @@ class AppBuildset: def _is_missing_dep(self, dep_name: str): return dep_name not in self.appnames + def _filter_by_target(self): + for appname in self.appnames.copy(): + app = self.appmgr.get(appname) + # if app.apptype not in self.BUILTIN_APP_TYPES: + if not any(map(lambda t: t in app.targets, ["all", self.hw_target])): + print( + f"Removing {appname} due to target mismatch (building for {self.hw_target}, app supports {app.targets}" + ) + self.appnames.remove(appname) + def _process_deps(self): while True: provided = [] diff --git a/scripts/fbt/util.py b/scripts/fbt/util.py index b8e9c5928..ee7562058 100644 --- a/scripts/fbt/util.py +++ b/scripts/fbt/util.py @@ -1,7 +1,6 @@ import SCons from SCons.Subst import quote_spaces from SCons.Errors import StopError -from SCons.Node.FS import _my_normcase import re import os @@ -58,3 +57,9 @@ def extract_abs_dir_path(node): raise StopError(f"Can't find absolute path for {node.name}") return abs_dir_node.abspath + + +def path_as_posix(path): + if SCons.Platform.platform_default() == "win32": + return path.replace(os.path.sep, os.path.altsep) + return path diff --git a/scripts/fbt_tools/fbt_apps.py b/scripts/fbt_tools/fbt_apps.py index 55e282017..96528f4e5 100644 --- a/scripts/fbt_tools/fbt_apps.py +++ b/scripts/fbt_tools/fbt_apps.py @@ -1,7 +1,6 @@ from SCons.Builder import Builder from SCons.Action import Action from SCons.Warnings import warn, WarningOnByDefault -import SCons from ansi.color import fg from fbt.appmanifest import ( @@ -33,14 +32,12 @@ def LoadAppManifest(env, entry): def PrepareApplicationsBuild(env): - appbuild = env["APPBUILD"] = env["APPMGR"].filter_apps(env["APPS"]) + appbuild = env["APPBUILD"] = env["APPMGR"].filter_apps( + env["APPS"], env.subst("f${TARGET_HW}") + ) env.Append( SDK_HEADERS=appbuild.get_sdk_headers(), ) - env["APPBUILD_DUMP"] = env.Action( - DumpApplicationConfig, - "\tINFO\t", - ) def DumpApplicationConfig(target, source, env): @@ -68,6 +65,10 @@ def generate(env): env.AddMethod(PrepareApplicationsBuild) env.SetDefault( APPMGR=AppManager(), + APPBUILD_DUMP=env.Action( + DumpApplicationConfig, + "\tINFO\t", + ), ) env.Append( diff --git a/scripts/fbt_tools/fbt_debugopts.py b/scripts/fbt_tools/fbt_debugopts.py index c3be5ca47..9ff05cb73 100644 --- a/scripts/fbt_tools/fbt_debugopts.py +++ b/scripts/fbt_tools/fbt_debugopts.py @@ -41,12 +41,12 @@ def generate(env, **kw): "|openocd -c 'gdb_port pipe; log_output ${FBT_DEBUG_DIR}/openocd.log' ${[SINGLEQUOTEFUNC(OPENOCD_OPTS)]}" ], GDBOPTS_BASE=[ + "-ex", + "set pagination off", "-ex", "target extended-remote ${GDBREMOTE}", "-ex", "set confirm off", - "-ex", - "set pagination off", ], GDBOPTS_BLACKMAGIC=[ "-ex", diff --git a/scripts/fbt_tools/fbt_sdk.py b/scripts/fbt_tools/fbt_sdk.py index c46346b65..3a37eacc9 100644 --- a/scripts/fbt_tools/fbt_sdk.py +++ b/scripts/fbt_tools/fbt_sdk.py @@ -14,6 +14,7 @@ import json from fbt.sdk.collector import SdkCollector from fbt.sdk.cache import SdkCache +from fbt.util import path_as_posix def ProcessSdkDepends(env, filename): @@ -52,6 +53,8 @@ def prebuild_sdk_create_origin_file(target, source, env): class SdkMeta: + MAP_FILE_SUBST = "SDK_MAP_FILE_SUBST" + def __init__(self, env, tree_builder: "SdkTreeBuilder"): self.env = env self.treebuilder = tree_builder @@ -67,6 +70,7 @@ class SdkMeta: "linker_libs": self.env.subst("${LIBS}"), "app_ep_subst": self.env.subst("${APP_ENTRY}"), "sdk_path_subst": self.env.subst("${SDK_DIR_SUBST}"), + "map_file_subst": self.MAP_FILE_SUBST, "hardware": self.env.subst("${TARGET_HW}"), } with open(json_manifest_path, "wt") as f: @@ -75,9 +79,9 @@ class SdkMeta: def _wrap_scons_vars(self, vars: str): expanded_vars = self.env.subst( vars, - target=Entry("dummy"), + target=Entry(self.MAP_FILE_SUBST), ) - return expanded_vars.replace("\\", "/") + return path_as_posix(expanded_vars) class SdkTreeBuilder: @@ -142,13 +146,15 @@ class SdkTreeBuilder: meta.save_to(self.target[0].path) def build_sdk_file_path(self, orig_path: str) -> str: - return posixpath.normpath( - posixpath.join( - self.SDK_DIR_SUBST, - self.target_sdk_dir_name, - orig_path, + return path_as_posix( + posixpath.normpath( + posixpath.join( + self.SDK_DIR_SUBST, + self.target_sdk_dir_name, + orig_path, + ) ) - ).replace("\\", "/") + ) def emitter(self, target, source, env): target_folder = target[0] diff --git a/site_scons/commandline.scons b/site_scons/commandline.scons index 044de6b30..6087c1c7a 100644 --- a/site_scons/commandline.scons +++ b/site_scons/commandline.scons @@ -63,6 +63,11 @@ vars.AddVariables( help="Enable debug build", default=True, ), + BoolVariable( + "LIB_DEBUG", + help="Enable debug build for libraries", + default=False, + ), BoolVariable( "COMPACT", help="Optimize for size", diff --git a/site_scons/extapps.scons b/site_scons/extapps.scons index 670d71fd0..b8f210563 100644 --- a/site_scons/extapps.scons +++ b/site_scons/extapps.scons @@ -1,6 +1,6 @@ from dataclasses import dataclass, field -from SCons.Errors import UserError from SCons.Node import NodeList +from SCons.Warnings import warn, WarningOnByDefault Import("ENV") @@ -80,6 +80,14 @@ if extra_app_list := GetOption("extra_ext_apps"): known_extapps.extend(map(appenv["APPMGR"].get, extra_app_list.split(","))) for app in known_extapps: + if not any(map(lambda t: t in app.targets, ["all", appenv.subst("f${TARGET_HW}")])): + warn( + WarningOnByDefault, + f"Can't build '{app.name}' (id '{app.appid}'): target mismatch" + f" (building for {appenv.subst('f${TARGET_HW}')}, app supports {app.targets}", + ) + continue + appenv.BuildAppElf(app)