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:
		
							parent
							
								
									457aa5331f
								
							
						
					
					
						commit
						98d5718ec9
					
				
							
								
								
									
										18
									
								
								SConstruct
									
									
									
									
									
								
							
							
						
						
									
										18
									
								
								SConstruct
									
									
									
									
									
								
							| @ -172,6 +172,7 @@ Alias("fap_dist", fap_dist) | ||||
| 
 | ||||
| fap_deploy = distenv.PhonyTarget( | ||||
|     "fap_deploy", | ||||
|     Action( | ||||
|         [ | ||||
|             [ | ||||
|                 "${PYTHON3}", | ||||
| @ -182,7 +183,8 @@ fap_deploy = distenv.PhonyTarget( | ||||
|                 "${SOURCE}", | ||||
|                 "/ext/apps", | ||||
|             ] | ||||
|     ], | ||||
|         ] | ||||
|     ), | ||||
|     source=firmware_env.Dir(("${RESOURCES_ROOT}/apps")), | ||||
| ) | ||||
| Depends(fap_deploy, firmware_env["FW_RESOURCES_MANIFEST"]) | ||||
| @ -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"]], | ||||
| ) | ||||
| 
 | ||||
| @ -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 | ||||
| @ -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 $)", | ||||
| ) | ||||
|  | ||||
| @ -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, | ||||
|  | ||||
| @ -3,7 +3,6 @@ App( | ||||
|     name="Battery Test", | ||||
|     apptype=FlipperAppType.DEBUG, | ||||
|     entry_point="battery_test_app", | ||||
|     cdefines=["APP_BATTERY_TEST"], | ||||
|     requires=[ | ||||
|         "gui", | ||||
|         "power", | ||||
|  | ||||
| @ -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, | ||||
|  | ||||
| @ -3,7 +3,6 @@ App( | ||||
|     name="CCID Debug", | ||||
|     apptype=FlipperAppType.DEBUG, | ||||
|     entry_point="ccid_test_app", | ||||
|     cdefines=["CCID_TEST"], | ||||
|     requires=[ | ||||
|         "gui", | ||||
|     ], | ||||
|  | ||||
| @ -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", | ||||
|  | ||||
| @ -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, | ||||
|  | ||||
| @ -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, | ||||
|  | ||||
| @ -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, | ||||
|  | ||||
| @ -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, | ||||
|  | ||||
| @ -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, | ||||
|  | ||||
| @ -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, | ||||
|  | ||||
| @ -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, | ||||
|  | ||||
| @ -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, | ||||
|  | ||||
| @ -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, | ||||
|  | ||||
| @ -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. | ||||
|  | ||||
| @ -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", | ||||
|     ), | ||||
| ) | ||||
| @ -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}") | ||||
|  | ||||
| @ -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: | ||||
| @ -48,8 +50,6 @@ class FlipperApplication: | ||||
|         cdefines: List[str] = field(default_factory=list) | ||||
|         cincludes: List[str] = field(default_factory=list) | ||||
| 
 | ||||
|     PRIVATE_FIELD_PREFIX = "_" | ||||
| 
 | ||||
|     appid: str | ||||
|     apptype: FlipperAppType | ||||
|     name: Optional[str] = "" | ||||
| @ -117,8 +117,10 @@ class FlipperApplication: | ||||
|                 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: | ||||
| @ -155,11 +157,20 @@ class AppManager: | ||||
|                 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): | ||||
| @ -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) | ||||
| @ -432,96 +452,3 @@ class AppBuildset: | ||||
|                 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) | ||||
|  | ||||
| @ -1,25 +1,118 @@ | ||||
| from ansi.color import fg | ||||
| from fbt.appmanifest import ( | ||||
|     ApplicationsCGenerator, | ||||
|     AppManager, | ||||
|     AppBuildset, | ||||
|     FlipperApplication, | ||||
|     FlipperAppType, | ||||
|     FlipperManifestException, | ||||
| ) | ||||
| from SCons.Action import Action | ||||
| from SCons.Builder import Builder | ||||
| from SCons.Errors import StopError | ||||
| from SCons.Warnings import WarningOnByDefault, warn | ||||
| from SCons.Script import GetOption | ||||
| from SCons.Warnings import WarningOnByDefault, warn | ||||
| 
 | ||||
| # Adding objects for application management to env | ||||
| #  AppManager env["APPMGR"] - loads all manifests; manages list of known apps | ||||
| #  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): | ||||
|     try: | ||||
|         APP_MANIFEST_NAME = "application.fam" | ||||
|         manifest_glob = entry.glob(APP_MANIFEST_NAME) | ||||
|         manifest_glob = entry.glob(FlipperApplication.APP_MANIFEST_DEFAULT_NAME) | ||||
|         if len(manifest_glob) == 0: | ||||
|             try: | ||||
|                 disk_node = next(filter(lambda d: d.exists(), entry.get_all_rdirs())) | ||||
| @ -27,7 +120,7 @@ def LoadAppManifest(env, entry): | ||||
|                 disk_node = entry | ||||
| 
 | ||||
|             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 | ||||
|  | ||||
| @ -58,7 +58,8 @@ class AppBuilder: | ||||
|         ) | ||||
|         self.app_env.Append( | ||||
|             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) | ||||
| @ -143,8 +144,8 @@ class AppBuilder: | ||||
|             self.app._assets_dirs = [self.app._appdir.Dir(self.app.fap_file_assets)] | ||||
| 
 | ||||
|         self.app_env.Append( | ||||
|             LIBS=[*self.app.fap_libs, *self.private_libs], | ||||
|             CPPPATH=[self.app_work_dir, self.app._appdir], | ||||
|             LIBS=[*self.app.fap_libs, *self.private_libs, *self.app.fap_libs], | ||||
|             CPPPATH=[self.app_env.Dir(self.app_work_dir), self.app._appdir], | ||||
|         ) | ||||
| 
 | ||||
|         app_sources = self.app_env.GatherSources( | ||||
| @ -472,7 +473,19 @@ def AddAppLaunchTarget(env, appname, launch_target_name): | ||||
|     components = _gather_app_components(env, appname) | ||||
|     target = env.PhonyTarget( | ||||
|         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(), | ||||
|         FLIPPER_FILE_TARGETS=components.deploy_sources.keys(), | ||||
|         EXTRA_ARGS=components.extra_launch_args, | ||||
|  | ||||
| @ -285,7 +285,20 @@ def generate(env, **kw): | ||||
|                         "$SDK_AMALGAMATE_HEADER_COMSTR", | ||||
|                     ), | ||||
|                     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", | ||||
|                     ), | ||||
|                 ], | ||||
|  | ||||
| @ -19,10 +19,22 @@ def generate(env): | ||||
|         BUILDERS={ | ||||
|             "VersionBuilder": Builder( | ||||
|                 action=Action( | ||||
|                     '${PYTHON3} "${VERSION_SCRIPT}" generate ' | ||||
|                     "-t ${TARGET_HW} -fw-origin ${FIRMWARE_ORIGIN} " | ||||
|                     '-o ${TARGET.dir.posix} --dir "${ROOT_DIR}"', | ||||
|                     [ | ||||
|                         [ | ||||
|                             "${PYTHON3}", | ||||
|                             "${VERSION_SCRIPT}", | ||||
|                             "generate", | ||||
|                             "-t", | ||||
|                             "${TARGET_HW}", | ||||
|                             "--fw-origin", | ||||
|                             "${FIRMWARE_ORIGIN}", | ||||
|                             "-o", | ||||
|                             "${TARGET.dir.posix}", | ||||
|                             "--dir", | ||||
|                             "${ROOT_DIR}", | ||||
|                             "${VERSIONCOMSTR}", | ||||
|                         ] | ||||
|                     ] | ||||
|                 ), | ||||
|                 emitter=_version_emitter, | ||||
|             ), | ||||
|  | ||||
| @ -25,7 +25,7 @@ def generate(env): | ||||
|         BUILDERS={ | ||||
|             "HEXBuilder": Builder( | ||||
|                 action=Action( | ||||
|                     '${OBJCOPY} -O ihex "${SOURCE}" "${TARGET}"', | ||||
|                     [["${OBJCOPY}", "-O", "ihex", "${SOURCE}", "${TARGET}"]], | ||||
|                     "${HEXCOMSTR}", | ||||
|                 ), | ||||
|                 suffix=".hex", | ||||
| @ -33,7 +33,7 @@ def generate(env): | ||||
|             ), | ||||
|             "BINBuilder": Builder( | ||||
|                 action=Action( | ||||
|                     '${OBJCOPY} -O binary -S "${SOURCE}" "${TARGET}"', | ||||
|                     [["${OBJCOPY}", "-O", "binary", "-S", "${SOURCE}", "${TARGET}"]], | ||||
|                     "${BINCOMSTR}", | ||||
|                 ), | ||||
|                 suffix=".bin", | ||||
| @ -41,7 +41,20 @@ def generate(env): | ||||
|             ), | ||||
|             "DFUBuilder": Builder( | ||||
|                 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}", | ||||
|                 ), | ||||
|                 suffix=".dfu", | ||||
|  | ||||
| @ -1,5 +1,6 @@ | ||||
| from SCons.Builder import Builder | ||||
| from SCons.Defaults import Touch | ||||
| from SCons.Action import Action | ||||
| 
 | ||||
| 
 | ||||
| def generate(env): | ||||
| @ -9,13 +10,21 @@ def generate(env): | ||||
|             "-auto", | ||||
|             "-exit", | ||||
|         ], | ||||
|         JFLASHCOM="${JFLASH} -openprj${JFLASHPROJECT} -open${SOURCE},${JFLASHADDR} ${JFLASHFLAGS}", | ||||
|     ) | ||||
|     env.Append( | ||||
|         BUILDERS={ | ||||
|             "JFlash": Builder( | ||||
|                 action=[ | ||||
|                     "${JFLASHCOM}", | ||||
|                     Action( | ||||
|                         [ | ||||
|                             [ | ||||
|                                 "${JFLASH}", | ||||
|                                 "-openprj${JFLASHPROJECT}", | ||||
|                                 "-open${SOURCE},${JFLASHADDR}", | ||||
|                                 "${JFLASHFLAGS}", | ||||
|                             ] | ||||
|                         ] | ||||
|                     ), | ||||
|                     Touch("${TARGET}"), | ||||
|                 ], | ||||
|             ), | ||||
|  | ||||
| @ -6,13 +6,12 @@ def generate(env): | ||||
|     env.SetDefault( | ||||
|         OBJDUMP="objdump", | ||||
|         OBJDUMPFLAGS=[], | ||||
|         OBJDUMPCOM="$OBJDUMP $OBJDUMPFLAGS -S $SOURCES > $TARGET", | ||||
|     ) | ||||
|     env.Append( | ||||
|         BUILDERS={ | ||||
|             "ObjDump": Builder( | ||||
|                 action=Action( | ||||
|                     "${OBJDUMPCOM}", | ||||
|                     [["$OBJDUMP", "$OBJDUMPFLAGS", "-S", "$SOURCES", ">", "$TARGET"]], | ||||
|                     "${OBJDUMPCOMSTR}", | ||||
|                 ), | ||||
|                 suffix=".lst", | ||||
|  | ||||
| @ -5,6 +5,7 @@ from SCons.Defaults import Touch | ||||
| 
 | ||||
| __OPENOCD_BIN = "openocd" | ||||
| 
 | ||||
| # TODO: FL-3663: rework argument passing to lists | ||||
| _oocd_action = Action( | ||||
|     "${OPENOCD} ${OPENOCD_OPTS} ${OPENOCD_COMMAND}", | ||||
|     "${OPENOCDCOMSTR}", | ||||
|  | ||||
| @ -79,7 +79,17 @@ def generate(env): | ||||
|         BUILDERS={ | ||||
|             "PVSCheck": Builder( | ||||
|                 action=Action( | ||||
|                     '${PVSCHECKBIN} analyze ${PVSOPTIONS} -f "${SOURCE}" -o "${TARGET}"', | ||||
|                     [ | ||||
|                         [ | ||||
|                             "${PVSCHECKBIN}", | ||||
|                             "analyze", | ||||
|                             "${PVSOPTIONS}", | ||||
|                             "-f", | ||||
|                             "${SOURCE}", | ||||
|                             "-o", | ||||
|                             "${TARGET}", | ||||
|                         ] | ||||
|                     ], | ||||
|                     "${PVSCHECKCOMSTR}", | ||||
|                 ), | ||||
|                 suffix=".log", | ||||
| @ -92,7 +102,17 @@ def generate(env): | ||||
|                         # PlogConverter.exe and plog-converter have different behavior | ||||
|                         Mkdir("${TARGET.dir}") if env["PLATFORM"] == "win32" else None, | ||||
|                         Action(_set_browser_action, None), | ||||
|                         '${PVSCONVBIN} ${PVSCONVOPTIONS} "${SOURCE}" -o "${REPORT_DIR}"', | ||||
|                         Action( | ||||
|                             [ | ||||
|                                 [ | ||||
|                                     "${PVSCONVBIN}", | ||||
|                                     "${PVSCONVOPTIONS}", | ||||
|                                     "${SOURCE}", | ||||
|                                     "-o", | ||||
|                                     "${REPORT_DIR}", | ||||
|                                 ] | ||||
|                             ] | ||||
|                         ), | ||||
|                     ], | ||||
|                     "${PVSCONVCOMSTR}", | ||||
|                 ), | ||||
|  | ||||
| @ -6,13 +6,12 @@ def generate(env): | ||||
|     env.SetDefault( | ||||
|         STRIP="strip", | ||||
|         STRIPFLAGS=[], | ||||
|         STRIPCOM="$STRIP $STRIPFLAGS $SOURCES -o $TARGET", | ||||
|     ) | ||||
|     env.Append( | ||||
|         BUILDERS={ | ||||
|             "ELFStripper": Builder( | ||||
|                 action=Action( | ||||
|                     "${STRIPCOM}", | ||||
|                     [["$STRIP", "$STRIPFLAGS", "$SOURCES", "-o", "$TARGET"]], | ||||
|                     "${STRIPCOMSTR}", | ||||
|                 ), | ||||
|                 suffix=".elf", | ||||
|  | ||||
| @ -325,24 +325,26 @@ else: | ||||
| 
 | ||||
| appenv.PhonyTarget( | ||||
|     "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 | ||||
| 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 | ||||
| 
 | ||||
| dist_env.PhonyTarget( | ||||
|     "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"), | ||||
|     LINT_SOURCES=[original_app_dir], | ||||
| ) | ||||
| 
 | ||||
| dist_env.PhonyTarget( | ||||
|     "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"), | ||||
|     LINT_SOURCES=[original_app_dir], | ||||
| ) | ||||
| @ -455,7 +457,17 @@ if dolphin_src_dir.exists(): | ||||
|     ) | ||||
|     dist_env.PhonyTarget( | ||||
|         "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"), | ||||
|     ) | ||||
| else: | ||||
| @ -467,7 +479,7 @@ else: | ||||
| 
 | ||||
| dist_env.PhonyTarget( | ||||
|     "env", | ||||
|     "@echo $( ${FBT_SCRIPT_DIR}/toolchain/fbtenv.sh $)", | ||||
|     "@echo $( ${FBT_SCRIPT_DIR.abspath}/toolchain/fbtenv.sh $)", | ||||
| ) | ||||
| 
 | ||||
| dist_env.PostConfigureUfbtEnvionment() | ||||
|  | ||||
| @ -101,7 +101,7 @@ class Main(App): | ||||
|             required=True, | ||||
|         ) | ||||
|         self.parser_generate.add_argument( | ||||
|             "-fw-origin", | ||||
|             "--fw-origin", | ||||
|             dest="firmware_origin", | ||||
|             type=str, | ||||
|             help="firmware origin", | ||||
|  | ||||
| @ -27,6 +27,8 @@ variables_to_forward = [ | ||||
|     "PYTHONNOUSERSITE", | ||||
|     "TMP", | ||||
|     "TEMP", | ||||
|     # ccache | ||||
|     "CCACHE_DISABLE", | ||||
|     # Colors for tools | ||||
|     "TERM", | ||||
| ] | ||||
| @ -62,7 +64,7 @@ coreenv = VAR_ENV.Clone( | ||||
|     # Setting up temp file parameters - to overcome command line length limits | ||||
|     TEMPFILEARGESCFUNC=tempfile_arg_esc_func, | ||||
|     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) | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 hedger
						hedger