Skip to content

Commit

Permalink
fbt: improvements (#3217)
Browse files Browse the repository at this point in the history
* 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: あく <[email protected]>
  • Loading branch information
hedger and skotopes authored Nov 15, 2023
1 parent 457aa53 commit 98d5718
Show file tree
Hide file tree
Showing 31 changed files with 271 additions and 166 deletions.
36 changes: 21 additions & 15 deletions SConstruct
Original file line number Diff line number Diff line change
Expand Up @@ -172,17 +172,19 @@ Alias("fap_dist", fap_dist)

fap_deploy = distenv.PhonyTarget(
"fap_deploy",
[
Action(
[
"${PYTHON3}",
"${FBT_SCRIPT_DIR}/storage.py",
"-p",
"${FLIP_PORT}",
"send",
"${SOURCE}",
"/ext/apps",
[
"${PYTHON3}",
"${FBT_SCRIPT_DIR}/storage.py",
"-p",
"${FLIP_PORT}",
"send",
"${SOURCE}",
"/ext/apps",
]
]
],
),
source=firmware_env.Dir(("${RESOURCES_ROOT}/apps")),
)
Depends(fap_deploy, firmware_env["FW_RESOURCES_MANIFEST"])
Expand Down Expand Up @@ -261,7 +263,7 @@ distenv.PhonyTarget(
distenv.PhonyTarget(
"debug_other_blackmagic",
"${GDBPYCOM}",
GDBOPTS="${GDBOPTS_BASE} ${GDBOPTS_BLACKMAGIC}",
GDBOPTS="${GDBOPTS_BASE} ${GDBOPTS_BLACKMAGIC}",
GDBREMOTE="${BLACKMAGIC_ADDR}",
GDBPYOPTS=debug_other_opts,
)
Expand All @@ -276,13 +278,13 @@ distenv.PhonyTarget(
# Linter
distenv.PhonyTarget(
"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"]],
)

distenv.PhonyTarget(
"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"]],
)

Expand Down Expand Up @@ -323,10 +325,14 @@ distenv.PhonyTarget(
)

# 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
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
Expand Down Expand Up @@ -361,5 +367,5 @@ distenv.Alias("vscode_dist", vscode_dist)
# Configure shell with build tools
distenv.PhonyTarget(
"env",
"@echo $( ${FBT_SCRIPT_DIR}/toolchain/fbtenv.sh $)",
"@echo $( ${FBT_SCRIPT_DIR.abspath}/toolchain/fbtenv.sh $)",
)
1 change: 0 additions & 1 deletion applications/debug/accessor/application.fam
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ App(
apptype=FlipperAppType.DEBUG,
targets=["f7"],
entry_point="accessor_app",
cdefines=["APP_ACCESSOR"],
requires=["gui"],
stack_size=4 * 1024,
order=40,
Expand Down
1 change: 0 additions & 1 deletion applications/debug/battery_test_app/application.fam
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ App(
name="Battery Test",
apptype=FlipperAppType.DEBUG,
entry_point="battery_test_app",
cdefines=["APP_BATTERY_TEST"],
requires=[
"gui",
"power",
Expand Down
1 change: 0 additions & 1 deletion applications/debug/blink_test/application.fam
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ App(
name="Blink Test",
apptype=FlipperAppType.DEBUG,
entry_point="blink_test_app",
cdefines=["APP_BLINK"],
requires=["gui"],
stack_size=1 * 1024,
order=10,
Expand Down
1 change: 0 additions & 1 deletion applications/debug/ccid_test/application.fam
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ App(
name="CCID Debug",
apptype=FlipperAppType.DEBUG,
entry_point="ccid_test_app",
cdefines=["CCID_TEST"],
requires=[
"gui",
],
Expand Down
1 change: 0 additions & 1 deletion applications/debug/crash_test/application.fam
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ App(
name="Crash Test",
apptype=FlipperAppType.DEBUG,
entry_point="crash_test_app",
cdefines=["APP_CRASH_TEST"],
requires=["gui"],
stack_size=1 * 1024,
fap_category="Debug",
Expand Down
1 change: 0 additions & 1 deletion applications/debug/display_test/application.fam
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ App(
name="Display Test",
apptype=FlipperAppType.DEBUG,
entry_point="display_test_app",
cdefines=["APP_DISPLAY_TEST"],
requires=["gui"],
fap_libs=["misc"],
stack_size=1 * 1024,
Expand Down
1 change: 0 additions & 1 deletion applications/debug/file_browser_test/application.fam
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ App(
name="File Browser Test",
apptype=FlipperAppType.DEBUG,
entry_point="file_browser_app",
cdefines=["APP_FILE_BROWSER_TEST"],
requires=["gui"],
stack_size=2 * 1024,
order=150,
Expand Down
1 change: 0 additions & 1 deletion applications/debug/keypad_test/application.fam
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ App(
name="Keypad Test",
apptype=FlipperAppType.DEBUG,
entry_point="keypad_test_app",
cdefines=["APP_KEYPAD_TEST"],
requires=["gui"],
stack_size=1 * 1024,
order=30,
Expand Down
1 change: 0 additions & 1 deletion applications/debug/locale_test/application.fam
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ App(
name="Locale Test",
apptype=FlipperAppType.DEBUG,
entry_point="locale_test_app",
cdefines=["APP_LOCALE"],
requires=["gui", "locale"],
stack_size=2 * 1024,
order=70,
Expand Down
1 change: 0 additions & 1 deletion applications/debug/text_box_test/application.fam
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ App(
name="Text Box Test",
apptype=FlipperAppType.DEBUG,
entry_point="text_box_test_app",
cdefines=["APP_TEXT_BOX_TEST"],
requires=["gui"],
stack_size=1 * 1024,
order=140,
Expand Down
1 change: 0 additions & 1 deletion applications/debug/uart_echo/application.fam
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ App(
name="UART Echo",
apptype=FlipperAppType.DEBUG,
entry_point="uart_echo_app",
cdefines=["APP_UART_ECHO"],
requires=["gui"],
stack_size=2 * 1024,
order=70,
Expand Down
1 change: 0 additions & 1 deletion applications/debug/usb_mouse/application.fam
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ App(
name="USB Mouse Demo",
apptype=FlipperAppType.DEBUG,
entry_point="usb_mouse_app",
cdefines=["APP_USB_MOUSE"],
requires=["gui"],
stack_size=1 * 1024,
order=60,
Expand Down
1 change: 0 additions & 1 deletion applications/debug/usb_test/application.fam
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ App(
name="USB Test",
apptype=FlipperAppType.DEBUG,
entry_point="usb_test_app",
cdefines=["APP_USB_TEST"],
requires=["gui"],
stack_size=1 * 1024,
order=50,
Expand Down
1 change: 0 additions & 1 deletion applications/debug/vibro_test/application.fam
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ App(
name="Vibro Test",
apptype=FlipperAppType.DEBUG,
entry_point="vibro_test_app",
cdefines=["APP_VIBRO_TEST"],
requires=["gui"],
stack_size=1 * 1024,
order=20,
Expand Down
2 changes: 1 addition & 1 deletion documentation/AppManifests.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ Only two parameters are mandatory: **_appid_** and **_apptype_**. Others are opt
- **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.
- **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.
- **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.
Expand Down
4 changes: 2 additions & 2 deletions firmware.scons
Original file line number Diff line number Diff line change
Expand Up @@ -219,7 +219,7 @@ AddPostAction(fwelf, fwenv["APPBUILD_DUMP"])
AddPostAction(
fwelf,
Action(
'${PYTHON3} "${BIN_SIZE_SCRIPT}" elf ${TARGET}',
[["${PYTHON3}", "${BIN_SIZE_SCRIPT}", "elf", "${TARGET}"]],
"Firmware size",
),
)
Expand All @@ -229,7 +229,7 @@ fwhex = fwenv["FW_HEX"] = fwenv.HEXBuilder("${FIRMWARE_BUILD_CFG}")
fwbin = fwenv["FW_BIN"] = fwenv.BINBuilder("${FIRMWARE_BUILD_CFG}")
AddPostAction(
fwbin,
Action('@${PYTHON3} "${BIN_SIZE_SCRIPT}" bin ${TARGET}'),
Action([["@${PYTHON3}", "${BIN_SIZE_SCRIPT}", "bin", "${TARGET}"]]),
)

fwdfu = fwenv["FW_DFU"] = fwenv.DFUBuilder("${FIRMWARE_BUILD_CFG}")
Expand Down
129 changes: 28 additions & 101 deletions scripts/fbt/appmanifest.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ class FlipperAppType(Enum):
@dataclass
class FlipperApplication:
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
class ExternallyBuiltFile:
Expand All @@ -48,8 +50,6 @@ class Library:
cdefines: List[str] = field(default_factory=list)
cincludes: List[str] = field(default_factory=list)

PRIVATE_FIELD_PREFIX = "_"

appid: str
apptype: FlipperAppType
name: Optional[str] = ""
Expand Down Expand Up @@ -117,8 +117,10 @@ def __post_init__(self):
self.fap_version = tuple(int(v) for v in self.fap_version.split("."))
except ValueError:
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:
Expand Down Expand Up @@ -155,11 +157,20 @@ def _validate_app_params(self, *args, **kw):
raise FlipperManifestException(
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"):
# raise FlipperManifestException(
# f"External app {kw.get('appid')} must not have 'cdefines' in manifest"
# )

if apptype in AppBuildset.dist_app_types:
# For distributing .fap's resources, there's "fap_file_assets"
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):
if not os.path.exists(app_manifest_path):
Expand Down Expand Up @@ -241,12 +252,21 @@ class AppBuildset:
FlipperAppType.STARTUP,
)
EXTERNAL_APP_TYPES_MAP = {
# AppType -> bool: true if always deploy, false if obey app set
FlipperAppType.EXTERNAL: True,
FlipperAppType.PLUGIN: True,
FlipperAppType.DEBUG: True,
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
def print_writer(message):
print(message)
Expand Down Expand Up @@ -432,96 +452,3 @@ def get_builtin_app_folders(self):
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)
Loading

0 comments on commit 98d5718

Please sign in to comment.