fbt: improvements (#3217)

* fbt: changed cdefines & lib handling for external apps; added extra checks for app manifest fields; moved around AppsC generator
* fbt: commandline fixes for spaces in paths
* fbt: fixed stringification for FAP_VERSION
* fbt: Removed excessive quoting for gdb
* docs: update for cdefines; fbt: typo fix
* fbt: enforcing at least 2 components in app version=

Co-authored-by: あく <alleteam@gmail.com>
This commit is contained in:
hedger 2023-11-15 20:27:35 +04:00 committed by GitHub
parent 457aa5331f
commit 98d5718ec9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
31 changed files with 271 additions and 166 deletions

View File

@ -172,17 +172,19 @@ Alias("fap_dist", fap_dist)
fap_deploy = distenv.PhonyTarget( fap_deploy = distenv.PhonyTarget(
"fap_deploy", "fap_deploy",
[ Action(
[ [
"${PYTHON3}", [
"${FBT_SCRIPT_DIR}/storage.py", "${PYTHON3}",
"-p", "${FBT_SCRIPT_DIR}/storage.py",
"${FLIP_PORT}", "-p",
"send", "${FLIP_PORT}",
"${SOURCE}", "send",
"/ext/apps", "${SOURCE}",
"/ext/apps",
]
] ]
], ),
source=firmware_env.Dir(("${RESOURCES_ROOT}/apps")), source=firmware_env.Dir(("${RESOURCES_ROOT}/apps")),
) )
Depends(fap_deploy, firmware_env["FW_RESOURCES_MANIFEST"]) Depends(fap_deploy, firmware_env["FW_RESOURCES_MANIFEST"])
@ -261,7 +263,7 @@ distenv.PhonyTarget(
distenv.PhonyTarget( distenv.PhonyTarget(
"debug_other_blackmagic", "debug_other_blackmagic",
"${GDBPYCOM}", "${GDBPYCOM}",
GDBOPTS="${GDBOPTS_BASE} ${GDBOPTS_BLACKMAGIC}", GDBOPTS="${GDBOPTS_BASE} ${GDBOPTS_BLACKMAGIC}",
GDBREMOTE="${BLACKMAGIC_ADDR}", GDBREMOTE="${BLACKMAGIC_ADDR}",
GDBPYOPTS=debug_other_opts, GDBPYOPTS=debug_other_opts,
) )
@ -276,13 +278,13 @@ distenv.PhonyTarget(
# Linter # Linter
distenv.PhonyTarget( distenv.PhonyTarget(
"lint", "lint",
"${PYTHON3} ${FBT_SCRIPT_DIR}/lint.py check ${LINT_SOURCES}", [["${PYTHON3}", "${FBT_SCRIPT_DIR}/lint.py", "check", "${LINT_SOURCES}"]],
LINT_SOURCES=[n.srcnode() for n in firmware_env["LINT_SOURCES"]], LINT_SOURCES=[n.srcnode() for n in firmware_env["LINT_SOURCES"]],
) )
distenv.PhonyTarget( distenv.PhonyTarget(
"format", "format",
"${PYTHON3} ${FBT_SCRIPT_DIR}/lint.py format ${LINT_SOURCES}", [["${PYTHON3}", "${FBT_SCRIPT_DIR}/lint.py", "format", "${LINT_SOURCES}"]],
LINT_SOURCES=[n.srcnode() for n in firmware_env["LINT_SOURCES"]], LINT_SOURCES=[n.srcnode() for n in firmware_env["LINT_SOURCES"]],
) )
@ -323,10 +325,14 @@ distenv.PhonyTarget(
) )
# Start Flipper CLI via PySerial's miniterm # Start Flipper CLI via PySerial's miniterm
distenv.PhonyTarget("cli", "${PYTHON3} ${FBT_SCRIPT_DIR}/serial_cli.py -p ${FLIP_PORT}") distenv.PhonyTarget(
"cli", [["${PYTHON3}", "${FBT_SCRIPT_DIR}/serial_cli.py", "-p", "${FLIP_PORT}"]]
)
# Update WiFi devboard firmware # Update WiFi devboard firmware
distenv.PhonyTarget("devboard_flash", "${PYTHON3} ${FBT_SCRIPT_DIR}/wifi_board.py") distenv.PhonyTarget(
"devboard_flash", [["${PYTHON3}", "${FBT_SCRIPT_DIR}/wifi_board.py"]]
)
# Find blackmagic probe # Find blackmagic probe
@ -361,5 +367,5 @@ distenv.Alias("vscode_dist", vscode_dist)
# Configure shell with build tools # Configure shell with build tools
distenv.PhonyTarget( distenv.PhonyTarget(
"env", "env",
"@echo $( ${FBT_SCRIPT_DIR}/toolchain/fbtenv.sh $)", "@echo $( ${FBT_SCRIPT_DIR.abspath}/toolchain/fbtenv.sh $)",
) )

View File

@ -4,7 +4,6 @@ App(
apptype=FlipperAppType.DEBUG, apptype=FlipperAppType.DEBUG,
targets=["f7"], targets=["f7"],
entry_point="accessor_app", entry_point="accessor_app",
cdefines=["APP_ACCESSOR"],
requires=["gui"], requires=["gui"],
stack_size=4 * 1024, stack_size=4 * 1024,
order=40, order=40,

View File

@ -3,7 +3,6 @@ App(
name="Battery Test", name="Battery Test",
apptype=FlipperAppType.DEBUG, apptype=FlipperAppType.DEBUG,
entry_point="battery_test_app", entry_point="battery_test_app",
cdefines=["APP_BATTERY_TEST"],
requires=[ requires=[
"gui", "gui",
"power", "power",

View File

@ -3,7 +3,6 @@ App(
name="Blink Test", name="Blink Test",
apptype=FlipperAppType.DEBUG, apptype=FlipperAppType.DEBUG,
entry_point="blink_test_app", entry_point="blink_test_app",
cdefines=["APP_BLINK"],
requires=["gui"], requires=["gui"],
stack_size=1 * 1024, stack_size=1 * 1024,
order=10, order=10,

View File

@ -3,7 +3,6 @@ App(
name="CCID Debug", name="CCID Debug",
apptype=FlipperAppType.DEBUG, apptype=FlipperAppType.DEBUG,
entry_point="ccid_test_app", entry_point="ccid_test_app",
cdefines=["CCID_TEST"],
requires=[ requires=[
"gui", "gui",
], ],

View File

@ -3,7 +3,6 @@ App(
name="Crash Test", name="Crash Test",
apptype=FlipperAppType.DEBUG, apptype=FlipperAppType.DEBUG,
entry_point="crash_test_app", entry_point="crash_test_app",
cdefines=["APP_CRASH_TEST"],
requires=["gui"], requires=["gui"],
stack_size=1 * 1024, stack_size=1 * 1024,
fap_category="Debug", fap_category="Debug",

View File

@ -3,7 +3,6 @@ App(
name="Display Test", name="Display Test",
apptype=FlipperAppType.DEBUG, apptype=FlipperAppType.DEBUG,
entry_point="display_test_app", entry_point="display_test_app",
cdefines=["APP_DISPLAY_TEST"],
requires=["gui"], requires=["gui"],
fap_libs=["misc"], fap_libs=["misc"],
stack_size=1 * 1024, stack_size=1 * 1024,

View File

@ -3,7 +3,6 @@ App(
name="File Browser Test", name="File Browser Test",
apptype=FlipperAppType.DEBUG, apptype=FlipperAppType.DEBUG,
entry_point="file_browser_app", entry_point="file_browser_app",
cdefines=["APP_FILE_BROWSER_TEST"],
requires=["gui"], requires=["gui"],
stack_size=2 * 1024, stack_size=2 * 1024,
order=150, order=150,

View File

@ -3,7 +3,6 @@ App(
name="Keypad Test", name="Keypad Test",
apptype=FlipperAppType.DEBUG, apptype=FlipperAppType.DEBUG,
entry_point="keypad_test_app", entry_point="keypad_test_app",
cdefines=["APP_KEYPAD_TEST"],
requires=["gui"], requires=["gui"],
stack_size=1 * 1024, stack_size=1 * 1024,
order=30, order=30,

View File

@ -3,7 +3,6 @@ App(
name="Locale Test", name="Locale Test",
apptype=FlipperAppType.DEBUG, apptype=FlipperAppType.DEBUG,
entry_point="locale_test_app", entry_point="locale_test_app",
cdefines=["APP_LOCALE"],
requires=["gui", "locale"], requires=["gui", "locale"],
stack_size=2 * 1024, stack_size=2 * 1024,
order=70, order=70,

View File

@ -3,7 +3,6 @@ App(
name="Text Box Test", name="Text Box Test",
apptype=FlipperAppType.DEBUG, apptype=FlipperAppType.DEBUG,
entry_point="text_box_test_app", entry_point="text_box_test_app",
cdefines=["APP_TEXT_BOX_TEST"],
requires=["gui"], requires=["gui"],
stack_size=1 * 1024, stack_size=1 * 1024,
order=140, order=140,

View File

@ -3,7 +3,6 @@ App(
name="UART Echo", name="UART Echo",
apptype=FlipperAppType.DEBUG, apptype=FlipperAppType.DEBUG,
entry_point="uart_echo_app", entry_point="uart_echo_app",
cdefines=["APP_UART_ECHO"],
requires=["gui"], requires=["gui"],
stack_size=2 * 1024, stack_size=2 * 1024,
order=70, order=70,

View File

@ -3,7 +3,6 @@ App(
name="USB Mouse Demo", name="USB Mouse Demo",
apptype=FlipperAppType.DEBUG, apptype=FlipperAppType.DEBUG,
entry_point="usb_mouse_app", entry_point="usb_mouse_app",
cdefines=["APP_USB_MOUSE"],
requires=["gui"], requires=["gui"],
stack_size=1 * 1024, stack_size=1 * 1024,
order=60, order=60,

View File

@ -3,7 +3,6 @@ App(
name="USB Test", name="USB Test",
apptype=FlipperAppType.DEBUG, apptype=FlipperAppType.DEBUG,
entry_point="usb_test_app", entry_point="usb_test_app",
cdefines=["APP_USB_TEST"],
requires=["gui"], requires=["gui"],
stack_size=1 * 1024, stack_size=1 * 1024,
order=50, order=50,

View File

@ -3,7 +3,6 @@ App(
name="Vibro Test", name="Vibro Test",
apptype=FlipperAppType.DEBUG, apptype=FlipperAppType.DEBUG,
entry_point="vibro_test_app", entry_point="vibro_test_app",
cdefines=["APP_VIBRO_TEST"],
requires=["gui"], requires=["gui"],
stack_size=1 * 1024, stack_size=1 * 1024,
order=20, order=20,

View File

@ -32,7 +32,7 @@ Only two parameters are mandatory: **_appid_** and **_apptype_**. Others are opt
- **name**: name displayed in menus. - **name**: name displayed in menus.
- **entry_point**: C function to be used as the application's entry point. Note that C++ function names are mangled, so you need to wrap them in `extern "C"` to use them as entry points. - **entry_point**: C function to be used as the application's entry point. Note that C++ function names are mangled, so you need to wrap them in `extern "C"` to use them as entry points.
- **flags**: internal flags for system apps. Do not use. - **flags**: internal flags for system apps. Do not use.
- **cdefines**: C preprocessor definitions to declare globally for other apps when the current application is included in the active build configuration. - **cdefines**: C preprocessor definitions to declare globally for other apps when the current application is included in the active build configuration. **For external applications**: specified definitions are used when building the application itself.
- **requires**: list of application IDs to include in the build configuration when the current application is referenced in the list of applications to build. - **requires**: list of application IDs to include in the build configuration when the current application is referenced in the list of applications to build.
- **conflicts**: list of application IDs with which the current application conflicts. If any of them is found in the constructed application list, **`fbt`** will abort the firmware build process. - **conflicts**: list of application IDs with which the current application conflicts. If any of them is found in the constructed application list, **`fbt`** will abort the firmware build process.
- **provides**: functionally identical to **_requires_** field. - **provides**: functionally identical to **_requires_** field.

View File

@ -219,7 +219,7 @@ AddPostAction(fwelf, fwenv["APPBUILD_DUMP"])
AddPostAction( AddPostAction(
fwelf, fwelf,
Action( Action(
'${PYTHON3} "${BIN_SIZE_SCRIPT}" elf ${TARGET}', [["${PYTHON3}", "${BIN_SIZE_SCRIPT}", "elf", "${TARGET}"]],
"Firmware size", "Firmware size",
), ),
) )
@ -229,7 +229,7 @@ fwhex = fwenv["FW_HEX"] = fwenv.HEXBuilder("${FIRMWARE_BUILD_CFG}")
fwbin = fwenv["FW_BIN"] = fwenv.BINBuilder("${FIRMWARE_BUILD_CFG}") fwbin = fwenv["FW_BIN"] = fwenv.BINBuilder("${FIRMWARE_BUILD_CFG}")
AddPostAction( AddPostAction(
fwbin, fwbin,
Action('@${PYTHON3} "${BIN_SIZE_SCRIPT}" bin ${TARGET}'), Action([["@${PYTHON3}", "${BIN_SIZE_SCRIPT}", "bin", "${TARGET}"]]),
) )
fwdfu = fwenv["FW_DFU"] = fwenv.DFUBuilder("${FIRMWARE_BUILD_CFG}") fwdfu = fwenv["FW_DFU"] = fwenv.DFUBuilder("${FIRMWARE_BUILD_CFG}")

View File

@ -33,6 +33,8 @@ class FlipperAppType(Enum):
@dataclass @dataclass
class FlipperApplication: class FlipperApplication:
APP_ID_REGEX: ClassVar[re.Pattern] = re.compile(r"^[a-z0-9_]+$") APP_ID_REGEX: ClassVar[re.Pattern] = re.compile(r"^[a-z0-9_]+$")
PRIVATE_FIELD_PREFIX: ClassVar[str] = "_"
APP_MANIFEST_DEFAULT_NAME: ClassVar[str] = "application.fam"
@dataclass @dataclass
class ExternallyBuiltFile: class ExternallyBuiltFile:
@ -48,8 +50,6 @@ class FlipperApplication:
cdefines: List[str] = field(default_factory=list) cdefines: List[str] = field(default_factory=list)
cincludes: List[str] = field(default_factory=list) cincludes: List[str] = field(default_factory=list)
PRIVATE_FIELD_PREFIX = "_"
appid: str appid: str
apptype: FlipperAppType apptype: FlipperAppType
name: Optional[str] = "" name: Optional[str] = ""
@ -117,8 +117,10 @@ class FlipperApplication:
self.fap_version = tuple(int(v) for v in self.fap_version.split(".")) self.fap_version = tuple(int(v) for v in self.fap_version.split("."))
except ValueError: except ValueError:
raise FlipperManifestException( raise FlipperManifestException(
f"Invalid version string '{self.fap_version}'. Must be in the form 'major.minor'" f"Invalid version '{self.fap_version}'. Must be in the form 'major.minor'"
) )
if len(self.fap_version) < 2:
raise ValueError("Not enough version components")
class AppManager: class AppManager:
@ -155,11 +157,20 @@ class AppManager:
raise FlipperManifestException( raise FlipperManifestException(
f"App {kw.get('appid')} cannot have fal_embedded set" f"App {kw.get('appid')} cannot have fal_embedded set"
) )
# Harmless - cdefines for external apps are meaningless
# if apptype == FlipperAppType.EXTERNAL and kw.get("cdefines"): if apptype in AppBuildset.dist_app_types:
# raise FlipperManifestException( # For distributing .fap's resources, there's "fap_file_assets"
# f"External app {kw.get('appid')} must not have 'cdefines' in manifest" for app_property in ("resources",):
# ) if kw.get(app_property):
raise FlipperManifestException(
f"App {kw.get('appid')} of type {apptype} cannot have '{app_property}' in manifest"
)
else:
for app_property in ("fap_extbuild", "fap_private_libs", "fap_icon_assets"):
if kw.get(app_property):
raise FlipperManifestException(
f"App {kw.get('appid')} of type {apptype} must not have '{app_property}' in manifest"
)
def load_manifest(self, app_manifest_path: str, app_dir_node: object): def load_manifest(self, app_manifest_path: str, app_dir_node: object):
if not os.path.exists(app_manifest_path): if not os.path.exists(app_manifest_path):
@ -241,12 +252,21 @@ class AppBuildset:
FlipperAppType.STARTUP, FlipperAppType.STARTUP,
) )
EXTERNAL_APP_TYPES_MAP = { EXTERNAL_APP_TYPES_MAP = {
# AppType -> bool: true if always deploy, false if obey app set
FlipperAppType.EXTERNAL: True, FlipperAppType.EXTERNAL: True,
FlipperAppType.PLUGIN: True, FlipperAppType.PLUGIN: True,
FlipperAppType.DEBUG: True, FlipperAppType.DEBUG: True,
FlipperAppType.MENUEXTERNAL: False, FlipperAppType.MENUEXTERNAL: False,
} }
@classmethod
@property
def dist_app_types(cls):
"""Applications that are installed on SD card"""
return list(
entry[0] for entry in cls.EXTERNAL_APP_TYPES_MAP.items() if entry[1]
)
@staticmethod @staticmethod
def print_writer(message): def print_writer(message):
print(message) print(message)
@ -432,96 +452,3 @@ class AppBuildset:
for source_type in app.sources for source_type in app.sources
) )
) )
class ApplicationsCGenerator:
APP_TYPE_MAP = {
FlipperAppType.SERVICE: ("FlipperInternalApplication", "FLIPPER_SERVICES"),
FlipperAppType.SYSTEM: ("FlipperInternalApplication", "FLIPPER_SYSTEM_APPS"),
FlipperAppType.APP: ("FlipperInternalApplication", "FLIPPER_APPS"),
FlipperAppType.DEBUG: ("FlipperInternalApplication", "FLIPPER_DEBUG_APPS"),
FlipperAppType.SETTINGS: (
"FlipperInternalApplication",
"FLIPPER_SETTINGS_APPS",
),
FlipperAppType.STARTUP: (
"FlipperInternalOnStartHook",
"FLIPPER_ON_SYSTEM_START",
),
}
APP_EXTERNAL_TYPE = (
"FlipperExternalApplication",
"FLIPPER_EXTERNAL_APPS",
)
def __init__(self, buildset: AppBuildset, autorun_app: str = ""):
self.buildset = buildset
self.autorun = autorun_app
def get_app_ep_forward(self, app: FlipperApplication):
if app.apptype == FlipperAppType.STARTUP:
return f"extern void {app.entry_point}();"
return f"extern int32_t {app.entry_point}(void* p);"
def get_app_descr(self, app: FlipperApplication):
if app.apptype == FlipperAppType.STARTUP:
return app.entry_point
return f"""
{{.app = {app.entry_point},
.name = "{app.name}",
.appid = "{app.appid}",
.stack_size = {app.stack_size},
.icon = {f"&{app.icon}" if app.icon else "NULL"},
.flags = {'|'.join(f"FlipperInternalApplicationFlag{flag}" for flag in app.flags)} }}"""
def get_external_app_descr(self, app: FlipperApplication):
app_path = "/ext/apps"
if app.fap_category:
app_path += f"/{app.fap_category}"
app_path += f"/{app.appid}.fap"
return f"""
{{
.name = "{app.name}",
.icon = {f"&{app.icon}" if app.icon else "NULL"},
.path = "{app_path}" }}"""
def generate(self):
contents = [
'#include "applications.h"',
"#include <assets_icons.h>",
f'const char* FLIPPER_AUTORUN_APP_NAME = "{self.autorun}";',
]
for apptype in self.APP_TYPE_MAP:
contents.extend(
map(self.get_app_ep_forward, self.buildset.get_apps_of_type(apptype))
)
entry_type, entry_block = self.APP_TYPE_MAP[apptype]
contents.append(f"const {entry_type} {entry_block}[] = {{")
contents.append(
",\n".join(
map(self.get_app_descr, self.buildset.get_apps_of_type(apptype))
)
)
contents.append("};")
contents.append(
f"const size_t {entry_block}_COUNT = COUNT_OF({entry_block});"
)
archive_app = self.buildset.get_apps_of_type(FlipperAppType.ARCHIVE)
if archive_app:
contents.extend(
[
self.get_app_ep_forward(archive_app[0]),
f"const FlipperInternalApplication FLIPPER_ARCHIVE = {self.get_app_descr(archive_app[0])};",
]
)
entry_type, entry_block = self.APP_EXTERNAL_TYPE
external_apps = self.buildset.get_apps_of_type(FlipperAppType.MENUEXTERNAL)
contents.append(f"const {entry_type} {entry_block}[] = {{")
contents.append(",\n".join(map(self.get_external_app_descr, external_apps)))
contents.append("};")
contents.append(f"const size_t {entry_block}_COUNT = COUNT_OF({entry_block});")
return "\n".join(contents)

View File

@ -1,25 +1,118 @@
from ansi.color import fg from ansi.color import fg
from fbt.appmanifest import ( from fbt.appmanifest import (
ApplicationsCGenerator,
AppManager, AppManager,
AppBuildset,
FlipperApplication,
FlipperAppType, FlipperAppType,
FlipperManifestException, FlipperManifestException,
) )
from SCons.Action import Action from SCons.Action import Action
from SCons.Builder import Builder from SCons.Builder import Builder
from SCons.Errors import StopError from SCons.Errors import StopError
from SCons.Warnings import WarningOnByDefault, warn
from SCons.Script import GetOption from SCons.Script import GetOption
from SCons.Warnings import WarningOnByDefault, warn
# Adding objects for application management to env # Adding objects for application management to env
# AppManager env["APPMGR"] - loads all manifests; manages list of known apps # AppManager env["APPMGR"] - loads all manifests; manages list of known apps
# AppBuildset env["APPBUILD"] - contains subset of apps, filtered for current config # AppBuildset env["APPBUILD"] - contains subset of apps, filtered for current config
class ApplicationsCGenerator:
APP_TYPE_MAP = {
FlipperAppType.SERVICE: ("FlipperInternalApplication", "FLIPPER_SERVICES"),
FlipperAppType.SYSTEM: ("FlipperInternalApplication", "FLIPPER_SYSTEM_APPS"),
FlipperAppType.APP: ("FlipperInternalApplication", "FLIPPER_APPS"),
FlipperAppType.DEBUG: ("FlipperInternalApplication", "FLIPPER_DEBUG_APPS"),
FlipperAppType.SETTINGS: (
"FlipperInternalApplication",
"FLIPPER_SETTINGS_APPS",
),
FlipperAppType.STARTUP: (
"FlipperInternalOnStartHook",
"FLIPPER_ON_SYSTEM_START",
),
}
APP_EXTERNAL_TYPE = (
"FlipperExternalApplication",
"FLIPPER_EXTERNAL_APPS",
)
def __init__(self, buildset: AppBuildset, autorun_app: str = ""):
self.buildset = buildset
self.autorun = autorun_app
def get_app_ep_forward(self, app: FlipperApplication):
if app.apptype == FlipperAppType.STARTUP:
return f"extern void {app.entry_point}();"
return f"extern int32_t {app.entry_point}(void* p);"
def get_app_descr(self, app: FlipperApplication):
if app.apptype == FlipperAppType.STARTUP:
return app.entry_point
return f"""
{{.app = {app.entry_point},
.name = "{app.name}",
.appid = "{app.appid}",
.stack_size = {app.stack_size},
.icon = {f"&{app.icon}" if app.icon else "NULL"},
.flags = {'|'.join(f"FlipperInternalApplicationFlag{flag}" for flag in app.flags)} }}"""
def get_external_app_descr(self, app: FlipperApplication):
app_path = "/ext/apps"
if app.fap_category:
app_path += f"/{app.fap_category}"
app_path += f"/{app.appid}.fap"
return f"""
{{
.name = "{app.name}",
.icon = {f"&{app.icon}" if app.icon else "NULL"},
.path = "{app_path}" }}"""
def generate(self):
contents = [
'#include "applications.h"',
"#include <assets_icons.h>",
f'const char* FLIPPER_AUTORUN_APP_NAME = "{self.autorun}";',
]
for apptype in self.APP_TYPE_MAP:
contents.extend(
map(self.get_app_ep_forward, self.buildset.get_apps_of_type(apptype))
)
entry_type, entry_block = self.APP_TYPE_MAP[apptype]
contents.append(f"const {entry_type} {entry_block}[] = {{")
contents.append(
",\n".join(
map(self.get_app_descr, self.buildset.get_apps_of_type(apptype))
)
)
contents.append("};")
contents.append(
f"const size_t {entry_block}_COUNT = COUNT_OF({entry_block});"
)
archive_app = self.buildset.get_apps_of_type(FlipperAppType.ARCHIVE)
if archive_app:
contents.extend(
[
self.get_app_ep_forward(archive_app[0]),
f"const FlipperInternalApplication FLIPPER_ARCHIVE = {self.get_app_descr(archive_app[0])};",
]
)
entry_type, entry_block = self.APP_EXTERNAL_TYPE
external_apps = self.buildset.get_apps_of_type(FlipperAppType.MENUEXTERNAL)
contents.append(f"const {entry_type} {entry_block}[] = {{")
contents.append(",\n".join(map(self.get_external_app_descr, external_apps)))
contents.append("};")
contents.append(f"const size_t {entry_block}_COUNT = COUNT_OF({entry_block});")
return "\n".join(contents)
def LoadAppManifest(env, entry): def LoadAppManifest(env, entry):
try: try:
APP_MANIFEST_NAME = "application.fam" manifest_glob = entry.glob(FlipperApplication.APP_MANIFEST_DEFAULT_NAME)
manifest_glob = entry.glob(APP_MANIFEST_NAME)
if len(manifest_glob) == 0: if len(manifest_glob) == 0:
try: try:
disk_node = next(filter(lambda d: d.exists(), entry.get_all_rdirs())) disk_node = next(filter(lambda d: d.exists(), entry.get_all_rdirs()))
@ -27,7 +120,7 @@ def LoadAppManifest(env, entry):
disk_node = entry disk_node = entry
raise FlipperManifestException( raise FlipperManifestException(
f"App folder '{disk_node.abspath}': missing manifest ({APP_MANIFEST_NAME})" f"App folder '{disk_node.abspath}': missing manifest ({FlipperApplication.APP_MANIFEST_DEFAULT_NAME})"
) )
app_manifest_file_path = manifest_glob[0].rfile().abspath app_manifest_file_path = manifest_glob[0].rfile().abspath

View File

@ -58,7 +58,8 @@ class AppBuilder:
) )
self.app_env.Append( self.app_env.Append(
CPPDEFINES=[ CPPDEFINES=[
("FAP_VERSION", f'"{".".join(map(str, self.app.fap_version))}"') ("FAP_VERSION", f'\\"{".".join(map(str, self.app.fap_version))}\\"'),
*self.app.cdefines,
], ],
) )
self.app_env.VariantDir(self.app_work_dir, self.app._appdir, duplicate=False) self.app_env.VariantDir(self.app_work_dir, self.app._appdir, duplicate=False)
@ -143,8 +144,8 @@ class AppBuilder:
self.app._assets_dirs = [self.app._appdir.Dir(self.app.fap_file_assets)] self.app._assets_dirs = [self.app._appdir.Dir(self.app.fap_file_assets)]
self.app_env.Append( self.app_env.Append(
LIBS=[*self.app.fap_libs, *self.private_libs], LIBS=[*self.app.fap_libs, *self.private_libs, *self.app.fap_libs],
CPPPATH=[self.app_work_dir, self.app._appdir], CPPPATH=[self.app_env.Dir(self.app_work_dir), self.app._appdir],
) )
app_sources = self.app_env.GatherSources( app_sources = self.app_env.GatherSources(
@ -472,7 +473,19 @@ def AddAppLaunchTarget(env, appname, launch_target_name):
components = _gather_app_components(env, appname) components = _gather_app_components(env, appname)
target = env.PhonyTarget( target = env.PhonyTarget(
launch_target_name, launch_target_name,
'${PYTHON3} "${APP_RUN_SCRIPT}" -p ${FLIP_PORT} ${EXTRA_ARGS} -s ${SOURCES} -t ${FLIPPER_FILE_TARGETS}', [
[
"${PYTHON3}",
"${APP_RUN_SCRIPT}",
"-p",
"${FLIP_PORT}",
"${EXTRA_ARGS}",
"-s",
"${SOURCES}",
"-t",
"${FLIPPER_FILE_TARGETS}",
]
],
source=components.deploy_sources.values(), source=components.deploy_sources.values(),
FLIPPER_FILE_TARGETS=components.deploy_sources.keys(), FLIPPER_FILE_TARGETS=components.deploy_sources.keys(),
EXTRA_ARGS=components.extra_launch_args, EXTRA_ARGS=components.extra_launch_args,

View File

@ -285,7 +285,20 @@ def generate(env, **kw):
"$SDK_AMALGAMATE_HEADER_COMSTR", "$SDK_AMALGAMATE_HEADER_COMSTR",
), ),
Action( Action(
"$CC -o $TARGET -E -P $CCFLAGS $_CCCOMCOM $SDK_PP_FLAGS -MMD ${TARGET}.c", [
[
"$CC",
"-o",
"$TARGET",
"-E",
"-P",
"$CCFLAGS",
"$_CCCOMCOM",
"$SDK_PP_FLAGS",
"-MMD",
"${TARGET}.c",
]
],
"$SDK_AMALGAMATE_PP_COMSTR", "$SDK_AMALGAMATE_PP_COMSTR",
), ),
], ],

View File

@ -19,10 +19,22 @@ def generate(env):
BUILDERS={ BUILDERS={
"VersionBuilder": Builder( "VersionBuilder": Builder(
action=Action( action=Action(
'${PYTHON3} "${VERSION_SCRIPT}" generate ' [
"-t ${TARGET_HW} -fw-origin ${FIRMWARE_ORIGIN} " [
'-o ${TARGET.dir.posix} --dir "${ROOT_DIR}"', "${PYTHON3}",
"${VERSIONCOMSTR}", "${VERSION_SCRIPT}",
"generate",
"-t",
"${TARGET_HW}",
"--fw-origin",
"${FIRMWARE_ORIGIN}",
"-o",
"${TARGET.dir.posix}",
"--dir",
"${ROOT_DIR}",
"${VERSIONCOMSTR}",
]
]
), ),
emitter=_version_emitter, emitter=_version_emitter,
), ),

View File

@ -25,7 +25,7 @@ def generate(env):
BUILDERS={ BUILDERS={
"HEXBuilder": Builder( "HEXBuilder": Builder(
action=Action( action=Action(
'${OBJCOPY} -O ihex "${SOURCE}" "${TARGET}"', [["${OBJCOPY}", "-O", "ihex", "${SOURCE}", "${TARGET}"]],
"${HEXCOMSTR}", "${HEXCOMSTR}",
), ),
suffix=".hex", suffix=".hex",
@ -33,7 +33,7 @@ def generate(env):
), ),
"BINBuilder": Builder( "BINBuilder": Builder(
action=Action( action=Action(
'${OBJCOPY} -O binary -S "${SOURCE}" "${TARGET}"', [["${OBJCOPY}", "-O", "binary", "-S", "${SOURCE}", "${TARGET}"]],
"${BINCOMSTR}", "${BINCOMSTR}",
), ),
suffix=".bin", suffix=".bin",
@ -41,7 +41,20 @@ def generate(env):
), ),
"DFUBuilder": Builder( "DFUBuilder": Builder(
action=Action( action=Action(
'${PYTHON3} "${BIN2DFU}" -i "${SOURCE}" -o "${TARGET}" -a ${IMAGE_BASE_ADDRESS} -l "Flipper Zero F${TARGET_HW}"', [
[
"${PYTHON3}",
"${BIN2DFU}",
"-i",
"${SOURCE}",
"-o",
"${TARGET}",
"-a",
"${IMAGE_BASE_ADDRESS}",
"-l",
"Flipper Zero F${TARGET_HW}",
]
],
"${DFUCOMSTR}", "${DFUCOMSTR}",
), ),
suffix=".dfu", suffix=".dfu",

View File

@ -1,5 +1,6 @@
from SCons.Builder import Builder from SCons.Builder import Builder
from SCons.Defaults import Touch from SCons.Defaults import Touch
from SCons.Action import Action
def generate(env): def generate(env):
@ -9,13 +10,21 @@ def generate(env):
"-auto", "-auto",
"-exit", "-exit",
], ],
JFLASHCOM="${JFLASH} -openprj${JFLASHPROJECT} -open${SOURCE},${JFLASHADDR} ${JFLASHFLAGS}",
) )
env.Append( env.Append(
BUILDERS={ BUILDERS={
"JFlash": Builder( "JFlash": Builder(
action=[ action=[
"${JFLASHCOM}", Action(
[
[
"${JFLASH}",
"-openprj${JFLASHPROJECT}",
"-open${SOURCE},${JFLASHADDR}",
"${JFLASHFLAGS}",
]
]
),
Touch("${TARGET}"), Touch("${TARGET}"),
], ],
), ),

View File

@ -6,13 +6,12 @@ def generate(env):
env.SetDefault( env.SetDefault(
OBJDUMP="objdump", OBJDUMP="objdump",
OBJDUMPFLAGS=[], OBJDUMPFLAGS=[],
OBJDUMPCOM="$OBJDUMP $OBJDUMPFLAGS -S $SOURCES > $TARGET",
) )
env.Append( env.Append(
BUILDERS={ BUILDERS={
"ObjDump": Builder( "ObjDump": Builder(
action=Action( action=Action(
"${OBJDUMPCOM}", [["$OBJDUMP", "$OBJDUMPFLAGS", "-S", "$SOURCES", ">", "$TARGET"]],
"${OBJDUMPCOMSTR}", "${OBJDUMPCOMSTR}",
), ),
suffix=".lst", suffix=".lst",

View File

@ -5,6 +5,7 @@ from SCons.Defaults import Touch
__OPENOCD_BIN = "openocd" __OPENOCD_BIN = "openocd"
# TODO: FL-3663: rework argument passing to lists
_oocd_action = Action( _oocd_action = Action(
"${OPENOCD} ${OPENOCD_OPTS} ${OPENOCD_COMMAND}", "${OPENOCD} ${OPENOCD_OPTS} ${OPENOCD_COMMAND}",
"${OPENOCDCOMSTR}", "${OPENOCDCOMSTR}",

View File

@ -79,7 +79,17 @@ def generate(env):
BUILDERS={ BUILDERS={
"PVSCheck": Builder( "PVSCheck": Builder(
action=Action( action=Action(
'${PVSCHECKBIN} analyze ${PVSOPTIONS} -f "${SOURCE}" -o "${TARGET}"', [
[
"${PVSCHECKBIN}",
"analyze",
"${PVSOPTIONS}",
"-f",
"${SOURCE}",
"-o",
"${TARGET}",
]
],
"${PVSCHECKCOMSTR}", "${PVSCHECKCOMSTR}",
), ),
suffix=".log", suffix=".log",
@ -92,7 +102,17 @@ def generate(env):
# PlogConverter.exe and plog-converter have different behavior # PlogConverter.exe and plog-converter have different behavior
Mkdir("${TARGET.dir}") if env["PLATFORM"] == "win32" else None, Mkdir("${TARGET.dir}") if env["PLATFORM"] == "win32" else None,
Action(_set_browser_action, None), Action(_set_browser_action, None),
'${PVSCONVBIN} ${PVSCONVOPTIONS} "${SOURCE}" -o "${REPORT_DIR}"', Action(
[
[
"${PVSCONVBIN}",
"${PVSCONVOPTIONS}",
"${SOURCE}",
"-o",
"${REPORT_DIR}",
]
]
),
], ],
"${PVSCONVCOMSTR}", "${PVSCONVCOMSTR}",
), ),

View File

@ -6,13 +6,12 @@ def generate(env):
env.SetDefault( env.SetDefault(
STRIP="strip", STRIP="strip",
STRIPFLAGS=[], STRIPFLAGS=[],
STRIPCOM="$STRIP $STRIPFLAGS $SOURCES -o $TARGET",
) )
env.Append( env.Append(
BUILDERS={ BUILDERS={
"ELFStripper": Builder( "ELFStripper": Builder(
action=Action( action=Action(
"${STRIPCOM}", [["$STRIP", "$STRIPFLAGS", "$SOURCES", "-o", "$TARGET"]],
"${STRIPCOMSTR}", "${STRIPCOMSTR}",
), ),
suffix=".elf", suffix=".elf",

View File

@ -325,24 +325,26 @@ else:
appenv.PhonyTarget( appenv.PhonyTarget(
"cli", "cli",
'${PYTHON3} "${FBT_SCRIPT_DIR}/serial_cli.py" -p ${FLIP_PORT}', [["${PYTHON3}", "${FBT_SCRIPT_DIR}/serial_cli.py", "-p", "${FLIP_PORT}"]],
) )
# Update WiFi devboard firmware # Update WiFi devboard firmware
dist_env.PhonyTarget("devboard_flash", "${PYTHON3} ${FBT_SCRIPT_DIR}/wifi_board.py") dist_env.PhonyTarget(
"devboard_flash", [["${PYTHON3}", "${FBT_SCRIPT_DIR}/wifi_board.py"]]
)
# Linter # Linter
dist_env.PhonyTarget( dist_env.PhonyTarget(
"lint", "lint",
"${PYTHON3} ${FBT_SCRIPT_DIR}/lint.py check ${LINT_SOURCES}", [["${PYTHON3}", "${FBT_SCRIPT_DIR}/lint.py", "check", "${LINT_SOURCES}"]],
source=original_app_dir.File(".clang-format"), source=original_app_dir.File(".clang-format"),
LINT_SOURCES=[original_app_dir], LINT_SOURCES=[original_app_dir],
) )
dist_env.PhonyTarget( dist_env.PhonyTarget(
"format", "format",
"${PYTHON3} ${FBT_SCRIPT_DIR}/lint.py format ${LINT_SOURCES}", [["${PYTHON3}", "${FBT_SCRIPT_DIR}/lint.py", "format", "${LINT_SOURCES}"]],
source=original_app_dir.File(".clang-format"), source=original_app_dir.File(".clang-format"),
LINT_SOURCES=[original_app_dir], LINT_SOURCES=[original_app_dir],
) )
@ -455,7 +457,17 @@ if dolphin_src_dir.exists():
) )
dist_env.PhonyTarget( dist_env.PhonyTarget(
"dolphin_ext", "dolphin_ext",
'${PYTHON3} ${FBT_SCRIPT_DIR}/storage.py -p ${FLIP_PORT} send "${SOURCE}" /ext/dolphin', [
[
"${PYTHON3}",
"${FBT_SCRIPT_DIR}/storage.py",
"-p",
"${FLIP_PORT}",
"send",
"${SOURCE}",
"/ext/dolphin",
]
],
source=ufbt_build_dir.Dir("dolphin"), source=ufbt_build_dir.Dir("dolphin"),
) )
else: else:
@ -467,7 +479,7 @@ else:
dist_env.PhonyTarget( dist_env.PhonyTarget(
"env", "env",
"@echo $( ${FBT_SCRIPT_DIR}/toolchain/fbtenv.sh $)", "@echo $( ${FBT_SCRIPT_DIR.abspath}/toolchain/fbtenv.sh $)",
) )
dist_env.PostConfigureUfbtEnvionment() dist_env.PostConfigureUfbtEnvionment()

View File

@ -101,7 +101,7 @@ class Main(App):
required=True, required=True,
) )
self.parser_generate.add_argument( self.parser_generate.add_argument(
"-fw-origin", "--fw-origin",
dest="firmware_origin", dest="firmware_origin",
type=str, type=str,
help="firmware origin", help="firmware origin",

View File

@ -27,6 +27,8 @@ variables_to_forward = [
"PYTHONNOUSERSITE", "PYTHONNOUSERSITE",
"TMP", "TMP",
"TEMP", "TEMP",
# ccache
"CCACHE_DISABLE",
# Colors for tools # Colors for tools
"TERM", "TERM",
] ]
@ -62,7 +64,7 @@ coreenv = VAR_ENV.Clone(
# 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,
ROOT_DIR=Dir("#"), ROOT_DIR=Dir("#"),
FBT_SCRIPT_DIR="${ROOT_DIR}/scripts", FBT_SCRIPT_DIR=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)