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…
Reference in New Issue
Block a user