unleashed-firmware/scripts/fbt/appmanifest.py

455 lines
15 KiB
Python
Raw Normal View History

import os
import re
from dataclasses import dataclass, field
from enum import Enum
from typing import Callable, ClassVar, List, Optional, Tuple, Union
try:
from fbt.util import resolve_real_dir_node
except ImportError:
# When running outside of SCons, we don't have access to SCons.Node
def resolve_real_dir_node(node):
return node
class FlipperManifestException(Exception):
pass
class FlipperAppType(Enum):
SERVICE = "Service"
SYSTEM = "System"
APP = "App"
DEBUG = "Debug"
ARCHIVE = "Archive"
SETTINGS = "Settings"
STARTUP = "StartupHook"
EXTERNAL = "External"
MENUEXTERNAL = "MenuExternal"
METAPACKAGE = "Package"
[FL-3097] fbt, faploader: minimal app module implementation (#2420) * fbt, faploader: minimal app module implementation * faploader, libs: moved API hashtable core to flipper_application * example: compound api * lib: flipper_application: naming fixes, doxygen comments * fbt: changed `requires` manifest field behavior for app extensions * examples: refactored plugin apps; faploader: changed new API naming; fbt: changed PLUGIN app type meaning * loader: dropped support for debug apps & plugin menus * moved applications/plugins -> applications/external * Restored x bit on chiplist_convert.py * git: fixed free-dap submodule path * pvs: updated submodule paths * examples: example_advanced_plugins.c: removed potential memory leak on errors * examples: example_plugins: refined requires * fbt: not deploying app modules for debug/sample apps; extra validation for .PLUGIN-type apps * apps: removed cdefines for external apps * fbt: moved ext app path definition * fbt: reworked fap_dist handling; f18: synced api_symbols.csv * fbt: removed resources_paths for extapps * scripts: reworked storage * scripts: reworked runfap.py & selfupdate.py to use new api * wip: fal runner * fbt: moved file packaging into separate module * scripts: storage: fixes * scripts: storage: minor fixes for new api * fbt: changed internal artifact storage details for external apps * scripts: storage: additional fixes and better error reporting; examples: using APP_DATA_PATH() * fbt, scripts: reworked launch_app to deploy plugins; moved old runfap.py to distfap.py * fbt: extra check for plugins descriptors * fbt: additional checks in emitter * fbt: better info message on SDK rebuild * scripts: removed requirements.txt * loader: removed remnants of plugins & debug menus * post-review fixes
2023-03-14 14:29:28 +00:00
PLUGIN = "Plugin"
@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:
path: str
command: str
@dataclass
class Library:
name: str
fap_include_paths: List[str] = field(default_factory=lambda: ["."])
sources: List[str] = field(default_factory=lambda: ["*.c*"])
cflags: List[str] = field(default_factory=list)
cdefines: List[str] = field(default_factory=list)
cincludes: List[str] = field(default_factory=list)
appid: str
apptype: FlipperAppType
name: Optional[str] = ""
entry_point: Optional[str] = None
flags: List[str] = field(default_factory=lambda: ["Default"])
cdefines: List[str] = field(default_factory=list)
requires: List[str] = field(default_factory=list)
conflicts: List[str] = field(default_factory=list)
provides: List[str] = field(default_factory=list)
stack_size: int = 2048
icon: Optional[str] = None
order: int = 0
sdk_headers: List[str] = field(default_factory=list)
targets: List[str] = field(default_factory=lambda: ["all"])
resources: Optional[str] = None
# .fap-specific
sources: List[str] = field(default_factory=lambda: ["*.c*"])
2023-05-21 17:50:38 +00:00
fap_version: Union[str, Tuple[int]] = "0.1"
fap_icon: Optional[str] = None
fap_libs: List[str] = field(default_factory=list)
fap_category: str = ""
fap_description: str = ""
fap_author: str = ""
fap_weburl: str = ""
fap_icon_assets: Optional[str] = None
fap_icon_assets_symbol: Optional[str] = None
fap_extbuild: List[ExternallyBuiltFile] = field(default_factory=list)
fap_private_libs: List[Library] = field(default_factory=list)
fap_file_assets: Optional[str] = None
fal_embedded: bool = False
# Internally used by fbt
[FL-3097] fbt, faploader: minimal app module implementation (#2420) * fbt, faploader: minimal app module implementation * faploader, libs: moved API hashtable core to flipper_application * example: compound api * lib: flipper_application: naming fixes, doxygen comments * fbt: changed `requires` manifest field behavior for app extensions * examples: refactored plugin apps; faploader: changed new API naming; fbt: changed PLUGIN app type meaning * loader: dropped support for debug apps & plugin menus * moved applications/plugins -> applications/external * Restored x bit on chiplist_convert.py * git: fixed free-dap submodule path * pvs: updated submodule paths * examples: example_advanced_plugins.c: removed potential memory leak on errors * examples: example_plugins: refined requires * fbt: not deploying app modules for debug/sample apps; extra validation for .PLUGIN-type apps * apps: removed cdefines for external apps * fbt: moved ext app path definition * fbt: reworked fap_dist handling; f18: synced api_symbols.csv * fbt: removed resources_paths for extapps * scripts: reworked storage * scripts: reworked runfap.py & selfupdate.py to use new api * wip: fal runner * fbt: moved file packaging into separate module * scripts: storage: fixes * scripts: storage: minor fixes for new api * fbt: changed internal artifact storage details for external apps * scripts: storage: additional fixes and better error reporting; examples: using APP_DATA_PATH() * fbt, scripts: reworked launch_app to deploy plugins; moved old runfap.py to distfap.py * fbt: extra check for plugins descriptors * fbt: additional checks in emitter * fbt: better info message on SDK rebuild * scripts: removed requirements.txt * loader: removed remnants of plugins & debug menus * post-review fixes
2023-03-14 14:29:28 +00:00
_appmanager: Optional["AppManager"] = None
_appdir: Optional[object] = None
_apppath: Optional[str] = None
[FL-3097] fbt, faploader: minimal app module implementation (#2420) * fbt, faploader: minimal app module implementation * faploader, libs: moved API hashtable core to flipper_application * example: compound api * lib: flipper_application: naming fixes, doxygen comments * fbt: changed `requires` manifest field behavior for app extensions * examples: refactored plugin apps; faploader: changed new API naming; fbt: changed PLUGIN app type meaning * loader: dropped support for debug apps & plugin menus * moved applications/plugins -> applications/external * Restored x bit on chiplist_convert.py * git: fixed free-dap submodule path * pvs: updated submodule paths * examples: example_advanced_plugins.c: removed potential memory leak on errors * examples: example_plugins: refined requires * fbt: not deploying app modules for debug/sample apps; extra validation for .PLUGIN-type apps * apps: removed cdefines for external apps * fbt: moved ext app path definition * fbt: reworked fap_dist handling; f18: synced api_symbols.csv * fbt: removed resources_paths for extapps * scripts: reworked storage * scripts: reworked runfap.py & selfupdate.py to use new api * wip: fal runner * fbt: moved file packaging into separate module * scripts: storage: fixes * scripts: storage: minor fixes for new api * fbt: changed internal artifact storage details for external apps * scripts: storage: additional fixes and better error reporting; examples: using APP_DATA_PATH() * fbt, scripts: reworked launch_app to deploy plugins; moved old runfap.py to distfap.py * fbt: extra check for plugins descriptors * fbt: additional checks in emitter * fbt: better info message on SDK rebuild * scripts: removed requirements.txt * loader: removed remnants of plugins & debug menus * post-review fixes
2023-03-14 14:29:28 +00:00
_plugins: List["FlipperApplication"] = field(default_factory=list)
_assets_dirs: List[object] = field(default_factory=list)
_section_fapmeta: Optional[object] = None
_section_fapfileassets: Optional[object] = None
@property
def embeds_plugins(self):
return any(plugin.fal_embedded for plugin in self._plugins)
def supports_hardware_target(self, target: str):
return target in self.targets or "all" in self.targets
[FL-3097] fbt, faploader: minimal app module implementation (#2420) * fbt, faploader: minimal app module implementation * faploader, libs: moved API hashtable core to flipper_application * example: compound api * lib: flipper_application: naming fixes, doxygen comments * fbt: changed `requires` manifest field behavior for app extensions * examples: refactored plugin apps; faploader: changed new API naming; fbt: changed PLUGIN app type meaning * loader: dropped support for debug apps & plugin menus * moved applications/plugins -> applications/external * Restored x bit on chiplist_convert.py * git: fixed free-dap submodule path * pvs: updated submodule paths * examples: example_advanced_plugins.c: removed potential memory leak on errors * examples: example_plugins: refined requires * fbt: not deploying app modules for debug/sample apps; extra validation for .PLUGIN-type apps * apps: removed cdefines for external apps * fbt: moved ext app path definition * fbt: reworked fap_dist handling; f18: synced api_symbols.csv * fbt: removed resources_paths for extapps * scripts: reworked storage * scripts: reworked runfap.py & selfupdate.py to use new api * wip: fal runner * fbt: moved file packaging into separate module * scripts: storage: fixes * scripts: storage: minor fixes for new api * fbt: changed internal artifact storage details for external apps * scripts: storage: additional fixes and better error reporting; examples: using APP_DATA_PATH() * fbt, scripts: reworked launch_app to deploy plugins; moved old runfap.py to distfap.py * fbt: extra check for plugins descriptors * fbt: additional checks in emitter * fbt: better info message on SDK rebuild * scripts: removed requirements.txt * loader: removed remnants of plugins & debug menus * post-review fixes
2023-03-14 14:29:28 +00:00
@property
def is_default_deployable(self):
return self.apptype != FlipperAppType.DEBUG and self.fap_category != "Examples"
@property
def do_strict_import_checks(self):
return self.apptype != FlipperAppType.PLUGIN
[FL-3097] fbt, faploader: minimal app module implementation (#2420) * fbt, faploader: minimal app module implementation * faploader, libs: moved API hashtable core to flipper_application * example: compound api * lib: flipper_application: naming fixes, doxygen comments * fbt: changed `requires` manifest field behavior for app extensions * examples: refactored plugin apps; faploader: changed new API naming; fbt: changed PLUGIN app type meaning * loader: dropped support for debug apps & plugin menus * moved applications/plugins -> applications/external * Restored x bit on chiplist_convert.py * git: fixed free-dap submodule path * pvs: updated submodule paths * examples: example_advanced_plugins.c: removed potential memory leak on errors * examples: example_plugins: refined requires * fbt: not deploying app modules for debug/sample apps; extra validation for .PLUGIN-type apps * apps: removed cdefines for external apps * fbt: moved ext app path definition * fbt: reworked fap_dist handling; f18: synced api_symbols.csv * fbt: removed resources_paths for extapps * scripts: reworked storage * scripts: reworked runfap.py & selfupdate.py to use new api * wip: fal runner * fbt: moved file packaging into separate module * scripts: storage: fixes * scripts: storage: minor fixes for new api * fbt: changed internal artifact storage details for external apps * scripts: storage: additional fixes and better error reporting; examples: using APP_DATA_PATH() * fbt, scripts: reworked launch_app to deploy plugins; moved old runfap.py to distfap.py * fbt: extra check for plugins descriptors * fbt: additional checks in emitter * fbt: better info message on SDK rebuild * scripts: removed requirements.txt * loader: removed remnants of plugins & debug menus * post-review fixes
2023-03-14 14:29:28 +00:00
def __post_init__(self):
if self.apptype == FlipperAppType.PLUGIN:
self.stack_size = 0
if not self.APP_ID_REGEX.match(self.appid):
raise FlipperManifestException(
f"Invalid appid '{self.appid}'. Must match regex '{self.APP_ID_REGEX}'"
)
if isinstance(self.fap_version, str):
try:
self.fap_version = tuple(int(v) for v in self.fap_version.split("."))
except ValueError:
raise FlipperManifestException(
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")
[FL-3097] fbt, faploader: minimal app module implementation (#2420) * fbt, faploader: minimal app module implementation * faploader, libs: moved API hashtable core to flipper_application * example: compound api * lib: flipper_application: naming fixes, doxygen comments * fbt: changed `requires` manifest field behavior for app extensions * examples: refactored plugin apps; faploader: changed new API naming; fbt: changed PLUGIN app type meaning * loader: dropped support for debug apps & plugin menus * moved applications/plugins -> applications/external * Restored x bit on chiplist_convert.py * git: fixed free-dap submodule path * pvs: updated submodule paths * examples: example_advanced_plugins.c: removed potential memory leak on errors * examples: example_plugins: refined requires * fbt: not deploying app modules for debug/sample apps; extra validation for .PLUGIN-type apps * apps: removed cdefines for external apps * fbt: moved ext app path definition * fbt: reworked fap_dist handling; f18: synced api_symbols.csv * fbt: removed resources_paths for extapps * scripts: reworked storage * scripts: reworked runfap.py & selfupdate.py to use new api * wip: fal runner * fbt: moved file packaging into separate module * scripts: storage: fixes * scripts: storage: minor fixes for new api * fbt: changed internal artifact storage details for external apps * scripts: storage: additional fixes and better error reporting; examples: using APP_DATA_PATH() * fbt, scripts: reworked launch_app to deploy plugins; moved old runfap.py to distfap.py * fbt: extra check for plugins descriptors * fbt: additional checks in emitter * fbt: better info message on SDK rebuild * scripts: removed requirements.txt * loader: removed remnants of plugins & debug menus * post-review fixes
2023-03-14 14:29:28 +00:00
class AppManager:
def __init__(self):
self.known_apps = {}
def get(self, appname: str):
try:
return self.known_apps[appname]
except KeyError:
raise FlipperManifestException(
f"Missing application manifest for '{appname}'"
)
def find_by_appdir(self, appdir: str):
for app in self.known_apps.values():
if app._appdir.name == appdir:
return app
return None
[FL-3097] fbt, faploader: minimal app module implementation (#2420) * fbt, faploader: minimal app module implementation * faploader, libs: moved API hashtable core to flipper_application * example: compound api * lib: flipper_application: naming fixes, doxygen comments * fbt: changed `requires` manifest field behavior for app extensions * examples: refactored plugin apps; faploader: changed new API naming; fbt: changed PLUGIN app type meaning * loader: dropped support for debug apps & plugin menus * moved applications/plugins -> applications/external * Restored x bit on chiplist_convert.py * git: fixed free-dap submodule path * pvs: updated submodule paths * examples: example_advanced_plugins.c: removed potential memory leak on errors * examples: example_plugins: refined requires * fbt: not deploying app modules for debug/sample apps; extra validation for .PLUGIN-type apps * apps: removed cdefines for external apps * fbt: moved ext app path definition * fbt: reworked fap_dist handling; f18: synced api_symbols.csv * fbt: removed resources_paths for extapps * scripts: reworked storage * scripts: reworked runfap.py & selfupdate.py to use new api * wip: fal runner * fbt: moved file packaging into separate module * scripts: storage: fixes * scripts: storage: minor fixes for new api * fbt: changed internal artifact storage details for external apps * scripts: storage: additional fixes and better error reporting; examples: using APP_DATA_PATH() * fbt, scripts: reworked launch_app to deploy plugins; moved old runfap.py to distfap.py * fbt: extra check for plugins descriptors * fbt: additional checks in emitter * fbt: better info message on SDK rebuild * scripts: removed requirements.txt * loader: removed remnants of plugins & debug menus * post-review fixes
2023-03-14 14:29:28 +00:00
def _validate_app_params(self, *args, **kw):
apptype = kw.get("apptype")
if apptype == FlipperAppType.PLUGIN:
if kw.get("stack_size"):
raise FlipperManifestException(
f"Plugin {kw.get('appid')} cannot have stack (did you mean FlipperAppType.EXTERNAL?)"
)
if not kw.get("requires"):
raise FlipperManifestException(
f"Plugin {kw.get('appid')} must have 'requires' in manifest"
)
else:
if kw.get("fal_embedded"):
raise FlipperManifestException(
f"App {kw.get('appid')} cannot have fal_embedded set"
)
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"
)
[FL-3097] fbt, faploader: minimal app module implementation (#2420) * fbt, faploader: minimal app module implementation * faploader, libs: moved API hashtable core to flipper_application * example: compound api * lib: flipper_application: naming fixes, doxygen comments * fbt: changed `requires` manifest field behavior for app extensions * examples: refactored plugin apps; faploader: changed new API naming; fbt: changed PLUGIN app type meaning * loader: dropped support for debug apps & plugin menus * moved applications/plugins -> applications/external * Restored x bit on chiplist_convert.py * git: fixed free-dap submodule path * pvs: updated submodule paths * examples: example_advanced_plugins.c: removed potential memory leak on errors * examples: example_plugins: refined requires * fbt: not deploying app modules for debug/sample apps; extra validation for .PLUGIN-type apps * apps: removed cdefines for external apps * fbt: moved ext app path definition * fbt: reworked fap_dist handling; f18: synced api_symbols.csv * fbt: removed resources_paths for extapps * scripts: reworked storage * scripts: reworked runfap.py & selfupdate.py to use new api * wip: fal runner * fbt: moved file packaging into separate module * scripts: storage: fixes * scripts: storage: minor fixes for new api * fbt: changed internal artifact storage details for external apps * scripts: storage: additional fixes and better error reporting; examples: using APP_DATA_PATH() * fbt, scripts: reworked launch_app to deploy plugins; moved old runfap.py to distfap.py * fbt: extra check for plugins descriptors * fbt: additional checks in emitter * fbt: better info message on SDK rebuild * scripts: removed requirements.txt * loader: removed remnants of plugins & debug menus * post-review fixes
2023-03-14 14:29:28 +00:00
def load_manifest(self, app_manifest_path: str, app_dir_node: object):
if not os.path.exists(app_manifest_path):
raise FlipperManifestException(
f"App manifest not found at path {app_manifest_path}"
)
# print("Loading", app_manifest_path)
app_manifests = []
def App(*args, **kw):
nonlocal app_manifests
[FL-3097] fbt, faploader: minimal app module implementation (#2420) * fbt, faploader: minimal app module implementation * faploader, libs: moved API hashtable core to flipper_application * example: compound api * lib: flipper_application: naming fixes, doxygen comments * fbt: changed `requires` manifest field behavior for app extensions * examples: refactored plugin apps; faploader: changed new API naming; fbt: changed PLUGIN app type meaning * loader: dropped support for debug apps & plugin menus * moved applications/plugins -> applications/external * Restored x bit on chiplist_convert.py * git: fixed free-dap submodule path * pvs: updated submodule paths * examples: example_advanced_plugins.c: removed potential memory leak on errors * examples: example_plugins: refined requires * fbt: not deploying app modules for debug/sample apps; extra validation for .PLUGIN-type apps * apps: removed cdefines for external apps * fbt: moved ext app path definition * fbt: reworked fap_dist handling; f18: synced api_symbols.csv * fbt: removed resources_paths for extapps * scripts: reworked storage * scripts: reworked runfap.py & selfupdate.py to use new api * wip: fal runner * fbt: moved file packaging into separate module * scripts: storage: fixes * scripts: storage: minor fixes for new api * fbt: changed internal artifact storage details for external apps * scripts: storage: additional fixes and better error reporting; examples: using APP_DATA_PATH() * fbt, scripts: reworked launch_app to deploy plugins; moved old runfap.py to distfap.py * fbt: extra check for plugins descriptors * fbt: additional checks in emitter * fbt: better info message on SDK rebuild * scripts: removed requirements.txt * loader: removed remnants of plugins & debug menus * post-review fixes
2023-03-14 14:29:28 +00:00
self._validate_app_params(*args, **kw)
app_manifests.append(
FlipperApplication(
*args,
**kw,
_appdir=resolve_real_dir_node(app_dir_node),
_apppath=os.path.dirname(app_manifest_path),
[FL-3097] fbt, faploader: minimal app module implementation (#2420) * fbt, faploader: minimal app module implementation * faploader, libs: moved API hashtable core to flipper_application * example: compound api * lib: flipper_application: naming fixes, doxygen comments * fbt: changed `requires` manifest field behavior for app extensions * examples: refactored plugin apps; faploader: changed new API naming; fbt: changed PLUGIN app type meaning * loader: dropped support for debug apps & plugin menus * moved applications/plugins -> applications/external * Restored x bit on chiplist_convert.py * git: fixed free-dap submodule path * pvs: updated submodule paths * examples: example_advanced_plugins.c: removed potential memory leak on errors * examples: example_plugins: refined requires * fbt: not deploying app modules for debug/sample apps; extra validation for .PLUGIN-type apps * apps: removed cdefines for external apps * fbt: moved ext app path definition * fbt: reworked fap_dist handling; f18: synced api_symbols.csv * fbt: removed resources_paths for extapps * scripts: reworked storage * scripts: reworked runfap.py & selfupdate.py to use new api * wip: fal runner * fbt: moved file packaging into separate module * scripts: storage: fixes * scripts: storage: minor fixes for new api * fbt: changed internal artifact storage details for external apps * scripts: storage: additional fixes and better error reporting; examples: using APP_DATA_PATH() * fbt, scripts: reworked launch_app to deploy plugins; moved old runfap.py to distfap.py * fbt: extra check for plugins descriptors * fbt: additional checks in emitter * fbt: better info message on SDK rebuild * scripts: removed requirements.txt * loader: removed remnants of plugins & debug menus * post-review fixes
2023-03-14 14:29:28 +00:00
_appmanager=self,
),
)
def ExtFile(*args, **kw):
return FlipperApplication.ExternallyBuiltFile(*args, **kw)
def Lib(*args, **kw):
return FlipperApplication.Library(*args, **kw)
try:
with open(app_manifest_path, "rt") as manifest_file:
exec(manifest_file.read())
except Exception as e:
raise FlipperManifestException(
f"Failed parsing manifest '{app_manifest_path}' : {e}"
)
if len(app_manifests) == 0:
raise FlipperManifestException(
f"App manifest '{app_manifest_path}' is malformed"
)
# print("Built", app_manifests)
for app in app_manifests:
self._add_known_app(app)
def _add_known_app(self, app: FlipperApplication):
if self.known_apps.get(app.appid, None):
raise FlipperManifestException(f"Duplicate app declaration: {app.appid}")
self.known_apps[app.appid] = app
def filter_apps(
self,
*,
applist: List[str],
ext_applist: List[str],
hw_target: str,
):
return AppBuildset(
self,
hw_target=hw_target,
appnames=applist,
extra_ext_appnames=ext_applist,
)
class AppBuilderException(Exception):
pass
class AppBuildset:
BUILTIN_APP_TYPES = (
FlipperAppType.SERVICE,
FlipperAppType.SYSTEM,
FlipperAppType.APP,
FlipperAppType.DEBUG,
FlipperAppType.ARCHIVE,
FlipperAppType.SETTINGS,
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)
def __init__(
self,
appmgr: AppManager,
hw_target: str,
appnames: List[str],
*,
extra_ext_appnames: List[str],
message_writer: Callable | None = None,
):
self.appmgr = appmgr
self.appnames = set(appnames)
self.incompatible_extapps, self.extapps = [], []
self._extra_ext_appnames = extra_ext_appnames
self._orig_appnames = appnames
self.hw_target = hw_target
self._writer = message_writer if message_writer else self.print_writer
self._process_deps()
self._process_ext_apps()
self._check_conflicts()
self._check_unsatisfied() # unneeded?
self._check_target_match()
[FL-3097] fbt, faploader: minimal app module implementation (#2420) * fbt, faploader: minimal app module implementation * faploader, libs: moved API hashtable core to flipper_application * example: compound api * lib: flipper_application: naming fixes, doxygen comments * fbt: changed `requires` manifest field behavior for app extensions * examples: refactored plugin apps; faploader: changed new API naming; fbt: changed PLUGIN app type meaning * loader: dropped support for debug apps & plugin menus * moved applications/plugins -> applications/external * Restored x bit on chiplist_convert.py * git: fixed free-dap submodule path * pvs: updated submodule paths * examples: example_advanced_plugins.c: removed potential memory leak on errors * examples: example_plugins: refined requires * fbt: not deploying app modules for debug/sample apps; extra validation for .PLUGIN-type apps * apps: removed cdefines for external apps * fbt: moved ext app path definition * fbt: reworked fap_dist handling; f18: synced api_symbols.csv * fbt: removed resources_paths for extapps * scripts: reworked storage * scripts: reworked runfap.py & selfupdate.py to use new api * wip: fal runner * fbt: moved file packaging into separate module * scripts: storage: fixes * scripts: storage: minor fixes for new api * fbt: changed internal artifact storage details for external apps * scripts: storage: additional fixes and better error reporting; examples: using APP_DATA_PATH() * fbt, scripts: reworked launch_app to deploy plugins; moved old runfap.py to distfap.py * fbt: extra check for plugins descriptors * fbt: additional checks in emitter * fbt: better info message on SDK rebuild * scripts: removed requirements.txt * loader: removed remnants of plugins & debug menus * post-review fixes
2023-03-14 14:29:28 +00:00
self._group_plugins()
self._apps = sorted(
list(map(self.appmgr.get, self.appnames)),
key=lambda app: app.appid,
)
@property
def apps(self):
return list(self._apps)
def _is_missing_dep(self, dep_name: str):
return dep_name not in self.appnames
def _check_if_app_target_supported(self, app_name: str):
return self.appmgr.get(app_name).supports_hardware_target(self.hw_target)
def _get_app_depends(self, app_name: str) -> List[str]:
app_def = self.appmgr.get(app_name)
# Skip app if its target is not supported by the target we are building for
if not self._check_if_app_target_supported(app_name):
self._writer(
f"Skipping {app_name} due to target mismatch (building for {self.hw_target}, app supports {app_def.targets}"
)
return []
return list(
filter(
self._check_if_app_target_supported,
filter(self._is_missing_dep, app_def.provides + app_def.requires),
)
)
def _process_deps(self):
while True:
provided = []
for app_name in self.appnames:
provided.extend(self._get_app_depends(app_name))
# print("provides round: ", provided)
if len(provided) == 0:
break
self.appnames.update(provided)
def _process_ext_apps(self):
extapps = [
app
for (apptype, global_lookup) in self.EXTERNAL_APP_TYPES_MAP.items()
for app in self.get_apps_of_type(apptype, global_lookup)
]
extapps.extend(map(self.appmgr.get, self._extra_ext_appnames))
for app in extapps:
(
self.extapps
if app.supports_hardware_target(self.hw_target)
else self.incompatible_extapps
).append(app)
def get_ext_apps(self):
return self.extapps
def get_incompatible_ext_apps(self):
return self.incompatible_extapps
def _check_conflicts(self):
conflicts = []
for app in self.appnames:
if conflict_app_name := list(
filter(
lambda dep_name: dep_name in self.appnames,
self.appmgr.get(app).conflicts,
)
):
conflicts.append((app, conflict_app_name))
if len(conflicts):
raise AppBuilderException(
f"App conflicts for {', '.join(f'{conflict_dep[0]}: {conflict_dep[1]}' for conflict_dep in conflicts)}"
)
def _check_unsatisfied(self):
unsatisfied = []
for app in self.appnames:
if missing_dep := list(
filter(self._is_missing_dep, self.appmgr.get(app).requires)
):
unsatisfied.append((app, missing_dep))
if len(unsatisfied):
raise AppBuilderException(
f"Unsatisfied dependencies for {', '.join(f'{missing_dep[0]}: {missing_dep[1]}' for missing_dep in unsatisfied)}"
)
def _check_target_match(self):
incompatible = []
for app in self.appnames:
if not self.appmgr.get(app).supports_hardware_target(self.hw_target):
incompatible.append(app)
if len(incompatible):
raise AppBuilderException(
f"Apps incompatible with target {self.hw_target}: {', '.join(incompatible)}"
)
[FL-3097] fbt, faploader: minimal app module implementation (#2420) * fbt, faploader: minimal app module implementation * faploader, libs: moved API hashtable core to flipper_application * example: compound api * lib: flipper_application: naming fixes, doxygen comments * fbt: changed `requires` manifest field behavior for app extensions * examples: refactored plugin apps; faploader: changed new API naming; fbt: changed PLUGIN app type meaning * loader: dropped support for debug apps & plugin menus * moved applications/plugins -> applications/external * Restored x bit on chiplist_convert.py * git: fixed free-dap submodule path * pvs: updated submodule paths * examples: example_advanced_plugins.c: removed potential memory leak on errors * examples: example_plugins: refined requires * fbt: not deploying app modules for debug/sample apps; extra validation for .PLUGIN-type apps * apps: removed cdefines for external apps * fbt: moved ext app path definition * fbt: reworked fap_dist handling; f18: synced api_symbols.csv * fbt: removed resources_paths for extapps * scripts: reworked storage * scripts: reworked runfap.py & selfupdate.py to use new api * wip: fal runner * fbt: moved file packaging into separate module * scripts: storage: fixes * scripts: storage: minor fixes for new api * fbt: changed internal artifact storage details for external apps * scripts: storage: additional fixes and better error reporting; examples: using APP_DATA_PATH() * fbt, scripts: reworked launch_app to deploy plugins; moved old runfap.py to distfap.py * fbt: extra check for plugins descriptors * fbt: additional checks in emitter * fbt: better info message on SDK rebuild * scripts: removed requirements.txt * loader: removed remnants of plugins & debug menus * post-review fixes
2023-03-14 14:29:28 +00:00
def _group_plugins(self):
known_extensions = self.get_apps_of_type(FlipperAppType.PLUGIN, all_known=True)
for extension_app in known_extensions:
for parent_app_id in extension_app.requires:
try:
parent_app = self.appmgr.get(parent_app_id)
parent_app._plugins.append(extension_app)
except FlipperManifestException:
[FL-3097] fbt, faploader: minimal app module implementation (#2420) * fbt, faploader: minimal app module implementation * faploader, libs: moved API hashtable core to flipper_application * example: compound api * lib: flipper_application: naming fixes, doxygen comments * fbt: changed `requires` manifest field behavior for app extensions * examples: refactored plugin apps; faploader: changed new API naming; fbt: changed PLUGIN app type meaning * loader: dropped support for debug apps & plugin menus * moved applications/plugins -> applications/external * Restored x bit on chiplist_convert.py * git: fixed free-dap submodule path * pvs: updated submodule paths * examples: example_advanced_plugins.c: removed potential memory leak on errors * examples: example_plugins: refined requires * fbt: not deploying app modules for debug/sample apps; extra validation for .PLUGIN-type apps * apps: removed cdefines for external apps * fbt: moved ext app path definition * fbt: reworked fap_dist handling; f18: synced api_symbols.csv * fbt: removed resources_paths for extapps * scripts: reworked storage * scripts: reworked runfap.py & selfupdate.py to use new api * wip: fal runner * fbt: moved file packaging into separate module * scripts: storage: fixes * scripts: storage: minor fixes for new api * fbt: changed internal artifact storage details for external apps * scripts: storage: additional fixes and better error reporting; examples: using APP_DATA_PATH() * fbt, scripts: reworked launch_app to deploy plugins; moved old runfap.py to distfap.py * fbt: extra check for plugins descriptors * fbt: additional checks in emitter * fbt: better info message on SDK rebuild * scripts: removed requirements.txt * loader: removed remnants of plugins & debug menus * post-review fixes
2023-03-14 14:29:28 +00:00
self._writer(
f"Module {extension_app.appid} has unknown parent {parent_app_id}"
)
def get_apps_cdefs(self):
cdefs = set()
for app in self._apps:
cdefs.update(app.cdefines)
return sorted(list(cdefs))
def get_sdk_headers(self):
sdk_headers = []
for app in self._apps:
2023-07-17 07:51:15 +00:00
sdk_headers.extend(
[
src._appdir.File(header)
for src in [app, *app._plugins]
2023-07-17 07:51:15 +00:00
for header in src.sdk_headers
]
)
return sdk_headers
def get_apps_of_type(self, apptype: FlipperAppType, all_known: bool = False):
"""Looks up apps of given type in current app set. If all_known is true,
ignores app set and checks all loaded apps' manifests."""
return sorted(
filter(
lambda app: app.apptype == apptype,
self.appmgr.known_apps.values()
if all_known
else map(self.appmgr.get, self.appnames),
),
key=lambda app: app.order,
)
def get_builtin_apps(self):
return list(
filter(lambda app: app.apptype in self.BUILTIN_APP_TYPES, self._apps)
)
def get_builtin_app_folders(self):
return sorted(
set(
(app._appdir, source_type)
for app in self.get_builtin_apps()
for source_type in app.sources
)
)