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
This commit is contained in:
hedger 2022-11-10 15:55:11 +04:00 committed by GitHub
parent 3985b456c3
commit a959fa32bc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 66 additions and 29 deletions

View File

@ -7,6 +7,7 @@
# construction of certain targets behind command-line options. # construction of certain targets behind command-line options.
import os import os
from fbt.util import path_as_posix
DefaultEnvironment(tools=[]) DefaultEnvironment(tools=[])
@ -200,9 +201,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").replace( FBT_FAP_DEBUG_ELF_ROOT=path_as_posix(firmware_env.subst("$FBT_FAP_DEBUG_ELF_ROOT")),
"\\", "/"
),
) )
distenv.Depends(firmware_debug, firmware_flash) distenv.Depends(firmware_debug, firmware_flash)
@ -212,9 +211,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").replace( FBT_FAP_DEBUG_ELF_ROOT=path_as_posix(firmware_env.subst("$FBT_FAP_DEBUG_ELF_ROOT")),
"\\", "/"
),
) )
# Debug alien elf # Debug alien elf

View File

@ -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. * **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.* * **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. * **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 #### Parameters for external applications

View File

@ -40,11 +40,11 @@ env = ENV.Clone(
FW_LIB_OPTS={ FW_LIB_OPTS={
"Default": { "Default": {
"CCFLAGS": [ "CCFLAGS": [
"-Os", "-Og" if ENV["LIB_DEBUG"] else "-Os",
], ],
"CPPDEFINES": [ "CPPDEFINES": [
"NDEBUG", "NDEBUG",
"FURI_NDEBUG", "FURI_DEBUG" if ENV["LIB_DEBUG"] else "FURI_NDEBUG",
], ],
# You can add other entries named after libraries # You can add other entries named after libraries
# If they are present, they have precedence over Default # If they are present, they have precedence over Default

View File

@ -52,6 +52,8 @@ class FlipperApplication:
icon: Optional[str] = None icon: Optional[str] = None
order: int = 0 order: int = 0
sdk_headers: List[str] = field(default_factory=list) sdk_headers: List[str] = field(default_factory=list)
targets: List[str] = field(default_factory=lambda: ["all"])
# .fap-specific # .fap-specific
sources: List[str] = field(default_factory=lambda: ["*.c*"]) sources: List[str] = field(default_factory=lambda: ["*.c*"])
fap_version: Tuple[int] = field(default_factory=lambda: (0, 1)) fap_version: Tuple[int] = field(default_factory=lambda: (0, 1))
@ -135,8 +137,8 @@ class AppManager:
raise FlipperManifestException(f"Duplicate app declaration: {app.appid}") raise FlipperManifestException(f"Duplicate app declaration: {app.appid}")
self.known_apps[app.appid] = app self.known_apps[app.appid] = app
def filter_apps(self, applist: List[str]): def filter_apps(self, applist: List[str], hw_target: str):
return AppBuildset(self, applist) return AppBuildset(self, applist, hw_target)
class AppBuilderException(Exception): class AppBuilderException(Exception):
@ -155,11 +157,13 @@ class AppBuildset:
FlipperAppType.STARTUP, 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.appmgr = appmgr
self.appnames = set(appnames) self.appnames = set(appnames)
self.hw_target = hw_target
self._orig_appnames = appnames self._orig_appnames = appnames
self._process_deps() self._process_deps()
self._filter_by_target()
self._check_conflicts() self._check_conflicts()
self._check_unsatisfied() # unneeded? self._check_unsatisfied() # unneeded?
self.apps = sorted( self.apps = sorted(
@ -170,6 +174,16 @@ class AppBuildset:
def _is_missing_dep(self, dep_name: str): def _is_missing_dep(self, dep_name: str):
return dep_name not in self.appnames 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): def _process_deps(self):
while True: while True:
provided = [] provided = []

View File

@ -1,7 +1,6 @@
import SCons import SCons
from SCons.Subst import quote_spaces from SCons.Subst import quote_spaces
from SCons.Errors import StopError from SCons.Errors import StopError
from SCons.Node.FS import _my_normcase
import re import re
import os import os
@ -58,3 +57,9 @@ def extract_abs_dir_path(node):
raise StopError(f"Can't find absolute path for {node.name}") raise StopError(f"Can't find absolute path for {node.name}")
return abs_dir_node.abspath 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

View File

@ -1,7 +1,6 @@
from SCons.Builder import Builder from SCons.Builder import Builder
from SCons.Action import Action from SCons.Action import Action
from SCons.Warnings import warn, WarningOnByDefault from SCons.Warnings import warn, WarningOnByDefault
import SCons
from ansi.color import fg from ansi.color import fg
from fbt.appmanifest import ( from fbt.appmanifest import (
@ -33,14 +32,12 @@ def LoadAppManifest(env, entry):
def PrepareApplicationsBuild(env): 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( env.Append(
SDK_HEADERS=appbuild.get_sdk_headers(), SDK_HEADERS=appbuild.get_sdk_headers(),
) )
env["APPBUILD_DUMP"] = env.Action(
DumpApplicationConfig,
"\tINFO\t",
)
def DumpApplicationConfig(target, source, env): def DumpApplicationConfig(target, source, env):
@ -68,6 +65,10 @@ def generate(env):
env.AddMethod(PrepareApplicationsBuild) env.AddMethod(PrepareApplicationsBuild)
env.SetDefault( env.SetDefault(
APPMGR=AppManager(), APPMGR=AppManager(),
APPBUILD_DUMP=env.Action(
DumpApplicationConfig,
"\tINFO\t",
),
) )
env.Append( env.Append(

View File

@ -41,12 +41,12 @@ def generate(env, **kw):
"|openocd -c 'gdb_port pipe; log_output ${FBT_DEBUG_DIR}/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",
"set pagination off",
"-ex", "-ex",
"target extended-remote ${GDBREMOTE}", "target extended-remote ${GDBREMOTE}",
"-ex", "-ex",
"set confirm off", "set confirm off",
"-ex",
"set pagination off",
], ],
GDBOPTS_BLACKMAGIC=[ GDBOPTS_BLACKMAGIC=[
"-ex", "-ex",

View File

@ -14,6 +14,7 @@ import json
from fbt.sdk.collector import SdkCollector from fbt.sdk.collector import SdkCollector
from fbt.sdk.cache import SdkCache from fbt.sdk.cache import SdkCache
from fbt.util import path_as_posix
def ProcessSdkDepends(env, filename): def ProcessSdkDepends(env, filename):
@ -52,6 +53,8 @@ def prebuild_sdk_create_origin_file(target, source, env):
class SdkMeta: class SdkMeta:
MAP_FILE_SUBST = "SDK_MAP_FILE_SUBST"
def __init__(self, env, tree_builder: "SdkTreeBuilder"): def __init__(self, env, tree_builder: "SdkTreeBuilder"):
self.env = env self.env = env
self.treebuilder = tree_builder self.treebuilder = tree_builder
@ -67,6 +70,7 @@ class SdkMeta:
"linker_libs": self.env.subst("${LIBS}"), "linker_libs": self.env.subst("${LIBS}"),
"app_ep_subst": self.env.subst("${APP_ENTRY}"), "app_ep_subst": self.env.subst("${APP_ENTRY}"),
"sdk_path_subst": self.env.subst("${SDK_DIR_SUBST}"), "sdk_path_subst": self.env.subst("${SDK_DIR_SUBST}"),
"map_file_subst": self.MAP_FILE_SUBST,
"hardware": self.env.subst("${TARGET_HW}"), "hardware": self.env.subst("${TARGET_HW}"),
} }
with open(json_manifest_path, "wt") as f: with open(json_manifest_path, "wt") as f:
@ -75,9 +79,9 @@ class SdkMeta:
def _wrap_scons_vars(self, vars: str): def _wrap_scons_vars(self, vars: str):
expanded_vars = self.env.subst( expanded_vars = self.env.subst(
vars, vars,
target=Entry("dummy"), target=Entry(self.MAP_FILE_SUBST),
) )
return expanded_vars.replace("\\", "/") return path_as_posix(expanded_vars)
class SdkTreeBuilder: class SdkTreeBuilder:
@ -142,13 +146,15 @@ class SdkTreeBuilder:
meta.save_to(self.target[0].path) meta.save_to(self.target[0].path)
def build_sdk_file_path(self, orig_path: str) -> str: def build_sdk_file_path(self, orig_path: str) -> str:
return posixpath.normpath( return path_as_posix(
posixpath.join( posixpath.normpath(
self.SDK_DIR_SUBST, posixpath.join(
self.target_sdk_dir_name, self.SDK_DIR_SUBST,
orig_path, self.target_sdk_dir_name,
orig_path,
)
) )
).replace("\\", "/") )
def emitter(self, target, source, env): def emitter(self, target, source, env):
target_folder = target[0] target_folder = target[0]

View File

@ -63,6 +63,11 @@ vars.AddVariables(
help="Enable debug build", help="Enable debug build",
default=True, default=True,
), ),
BoolVariable(
"LIB_DEBUG",
help="Enable debug build for libraries",
default=False,
),
BoolVariable( BoolVariable(
"COMPACT", "COMPACT",
help="Optimize for size", help="Optimize for size",

View File

@ -1,6 +1,6 @@
from dataclasses import dataclass, field from dataclasses import dataclass, field
from SCons.Errors import UserError
from SCons.Node import NodeList from SCons.Node import NodeList
from SCons.Warnings import warn, WarningOnByDefault
Import("ENV") 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(","))) known_extapps.extend(map(appenv["APPMGR"].get, extra_app_list.split(",")))
for app in known_extapps: 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) appenv.BuildAppElf(app)