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:
hedger 2022-11-05 15:47:59 +04:00 committed by GitHub
parent bf8fd71c00
commit 04e50c9f89
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
20 changed files with 179 additions and 126 deletions

View File

@ -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(

View File

@ -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"],
) )

View File

@ -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"],
) )

View File

@ -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(

View File

@ -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(

View File

@ -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(

View File

@ -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"],
) )

View File

@ -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}")

View File

@ -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"

View File

@ -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

View File

@ -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(

View File

@ -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)

View File

@ -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,

View File

@ -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",
) )

View File

@ -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}"),
] ]

View File

@ -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(

View File

@ -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

View File

@ -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(

View File

@ -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)

View File

@ -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)