拓冰建站拓冰建站
首页 / 资讯中心 / 正文

Flipper Zero 固件应用清单(FAM)完全指南:从 application.fam 到构建系统

Flipper Zero 固件应用清单FAM完全指南从 application.fam 到构建系统【免费下载链接】unleashed-firmwareFlipper Zero Unleashed Firmware项目地址: https://gitcode.com/GitHub_Trending/un/unleashed-firmwareFAMFlipper App ManifestFlipper 应用清单是 Unleashed 固件中每个组件——系统服务、用户应用、系统设置乃至插件——在构建系统内的身份证。本文以 documentation/AppManifests.md 为骨架结合仓库中真实.fam文件与构建工具 scripts/fbt/appmanifest.py 的实现细节系统讲解App()的全部参数、FAP 外部应用专属配置、私有库与外部构建扩展帮助你从零写出一份可被fbt正确解析、参与固件编译的应用清单。FAM 是什么每个组件的构建契约Unleashed 固件的所有组成部分——服务services、用户应用user applications、系统设置system settings——都是独立开发的。每个组件目录下都有一份名为application.fam的清单文件用 Python 语法声明该组件的基本属性及其与系统其他部分的关系。当构建固件时fbtFlipper Build Tool会收集所有应用清单manifest处理它们之间的依赖关系requires/provides/conflicts只构建当前构建配置中引用的那些组件。关于构建配置如COMPACT、DEBUG、FIRMWARE_APP_SET等选项的细节参见 FBT 文档。清单解析的实际入口在 scripts/fbt/appmanifest.pyfbt读取.fam文件内容并exec执行其中预置了App()、ExtFile()、Lib()三个可调用对象如果解析失败或文件内没有任何App()定义会抛出FlipperManifestException。App 定义与 App() 函数一个固件组件的属性通过一段 Python 代码片段声明即调用App()函数并传入各种参数。只有两个参数是强制性的appid和apptype其余均为可选且可能只对特定 app 类型有意义。App()在源码中对应FlipperApplication数据类见 scripts/fbt/appmanifest.py其中还隐藏着一些默认值纪律例如PLUGIN类型的应用会被强制将stack_size置为 0因为它不独立运行线程而appid必须匹配正则^[a-z0-9_]$否则直接报错。appid应用唯一标识字符串类型构建系统内的应用 ID。它用于在构建配置中指定要包含哪个应用解析依赖requires/provides检测冲突conflicts。注意命名规范appid只能包含小写字母、数字和下划线且不能以数字开头由 appmanifest.py 中的APP_ID_REGEX强制校验重复声明同一appid也会被拒绝见 appmanifest.py。apptype组件类型apptype是FlipperAppType.*枚举的一个成员枚举定义在 scripts/fbt/appmanifest.py。各取值含义如下枚举成员固件组件类型SERVICE系统服务在系统启动早期创建SYSTEM不显示在任何菜单中的应用可由其他应用或 CLI 启动APP主菜单中的常规应用PLUGIN作为固件一部分构建、放入 Plugins 菜单的应用DEBUG仅在启用调试模式后于 Debug 菜单中显示的应用ARCHIVE唯一且仅有的 Archive 应用SETTINGS放入系统设置菜单的应用STARTUP系统启动时运行的回调函数不定义独立应用EXTERNAL构建为.fap插件的外部应用METAPACKAGE不定义任何可运行代码仅用于声明依赖和应用捆绑包从源码看枚举中实际还包含MENUEXTERNAL作为主菜单外部应用构建和EXTSETTINGS作为设置菜单外部应用构建两个成员它们被归类到外部应用类型映射中见 appmanifest.py。apptype还决定了清单参数校验规则见 appmanifest.pyPLUGIN不能设置stack_size提示did you mean FlipperAppType.EXTERNAL?且必须声明requires非PLUGIN类型不能设置fal_embedded外部分发类型EXTERNAL、PLUGIN、DEBUG不能设置resources参数内置类型不能使用fap_extbuild、fap_private_libs等 FAP 专属参数。通用参数详解以下参数适用于所有 app 类型name菜单中显示的名称字符串可空。entry_point作为应用入口点的 C 函数名。注意 C 函数名会被 name mangling 处理因此要作为入口点必须用extern C包裹。类型为STARTUP时对应启动回调。flags系统应用的内部标志不要使用。cdefines当当前应用被包含进构建配置时全局声明给其他应用的 C 预处理器定义列表。对外部应用这些定义在构建该应用自身时使用。例如 applications/services/bt/application.fam 中cdefines[SRV_BT]。requires应用 ID 列表。当当前应用被引用时这些应用也会被加入构建配置依赖。conflicts与当前应用冲突的应用 ID 列表。如果其中任何一个出现在构建的应用列表中fbt会中止固件构建见 appmanifest.py 的_check_conflicts。provides功能上与requires字段完全相同的字段。二者均会被纳入依赖解析见 appmanifest.py。stack_size应用启动时分配的栈大小字节。栈分配过小会导致系统因栈溢出崩溃过大则会减少应用处理数据可用的堆内存。可用top和freeCLI 命令分析应用内存占用。源码中默认值为 2048 字节appmanifest.py仓库常见写法如4 * 1024。icon内置资源中的动画图标名用于将应用构建为固件一部分时显示。例如 applications/main/nfc/application.fam 中的iconA_NFC_14。order应用在所在分组内的排序位置值越小越靠近列表开头。用于启动钩子STARTUP和菜单项的排序。从源码看get_apps_of_type会按order对同类型应用排序appmanifest.py。sdk_headers本应用代码中要包含进外部应用 API 定义的 C 头文件列表。例如bt服务声明了bt_service/bt.h、bt_settings.h等。这些头文件会被收集进 SDK 定义appmanifest.py。targets该应用兼容的字符串和目标名列表。不指定则对所有目标构建默认值为[all]。例如accessor应用声明targets[f7]applications/debug/accessor/application.fam。构建时若目标不匹配fbt会跳过该应用并打印提示appmanifest.py。resources应用源文件夹内用于打包 SD 卡资源的子文件夹名。仅当应用被包含进构建配置时才会打包。默认值为不打包。例如 applications/main/nfc/application.fam 中的resourcesresources。外部应用FAP专属参数以下参数仅用于 FAPFlipper Application Package即构建为.fap外部插件时sources字符串列表用于在应用文件夹内收集源码的文件名掩码。默认值为[*.c*]即包含 C 和 C 源文件。应用不能使用lib文件夹存放自己的源码该文件夹保留给fap_private_libs。以!开头的路径会被从源码列表中排除同时支持通配符和目录名。例如[*.c*, !plugins]会包含应用文件夹内所有 C/C 源文件但排除plugins和lib文件夹。不含通配符*、?的路径按完整字面路径处理包含与排除均如此。fap_version字符串应用版本。默认值0.1。也可用(x, y)二元组指定版本还可以追加更多点分部分如补丁号但构建出的.fap只存储主版本和次版本号。源码中fap_version会被解析为整数元组appmanifest.py。fap_icon.png文件名要求 1-bit 色深、10x10 像素嵌入.fap文件内。fap_libs额外链接的库列表可访问主固件未导出为 API 的额外函数代价是增加.fap文件体积和 RAM 占用。仓库实例fap_libs[assets, mbedtls]NFC 应用、fap_libs[assets]JS 应用。fap_category字符串可空。应用子类别同时决定 FAP 在文件系统 apps 文件夹中的路径。仓库实例fap_categoryNFC、fap_categoryassets、fap_categoryDebug。fap_description字符串可空。简短应用描述。fap_author字符串可空。应用作者。fap_weburl字符串可空。应用主页。fap_icon_assets字符串。若存在定义用于收集该应用图片资源的文件夹名这些图片会被预处理并随应用一同构建。详见 FAP assets。fap_extbuild支持应用源码的某部分由外部工具构建。包含ExtFile(pathfile name, commandshell command)定义列表fbt会为列表中的每个文件运行指定命令。fal_embedded布尔值默认False。仅适用于PLUGIN类型。若为True插件会作为资源嵌入宿主应用的.fap文件中并在其启动时解压到apps_assets/APPID文件夹从而允许插件随宿主应用一起分发。仓库中 NFC 的各协议插件如nfc_iso14443_3a均设置fal_embeddedTrue见 applications/main/nfc/application.fam。fap_exclude_libs排除链接的工具链库源码中还有一个文档表格之外的 FAP 参数值得一提fap_exclude_libs。它用于声明不要静态链接进本应用的库这些符号保持未定义改在加载时从固件 API 表解析。例如fap_exclude_libs[gcc]可以避免每个.fal都携带一份双精度软浮点辅助函数副本见 appmanifest.py 的注释仓库中绝大多数 FAP 都使用了该参数。fap_extbuild用外部工具链构建部分源码fap_extbuild中所有命令都在固件根目录执行所有中间文件必须放入应用的临时构建文件夹。为此fbt提供模式展开${FAP_WORK_DIR}替换为应用临时构建文件夹路径${FAP_SRC_DIR}替换为应用源文件夹路径也可以使用fbt内部定义的其他变量。用 Rust 源码构建应用的示例sources[target/thumbv7em-none-eabihf/release/libhello_rust.a], fap_extbuild( ExtFile( path${FAP_WORK_DIR}/target/thumbv7em-none-eabihf/release/libhello_rust.a, commandcargo build --release --verbose --target thumbv7em-none-eabihf --target-dir ${FAP_WORK_DIR}/target --manifest-path ${FAP_SRC_DIR}/Cargo.toml, ), ),这里ExtFile.path指向外部工具cargo产出的目标文件command是生成它的 shell 命令fbt会先执行命令再将该文件纳入应用链接。fap_private_libs随应用源码分发的私有库fap_private_libs是随应用一起以源码形式分发的额外库列表这些库会作为应用构建流程的一部分被编译。库源码必须放在应用源文件夹的lib子文件夹中。每个库通过调用Lib()函数定义参数如下name库文件夹名。必填。fap_include_paths要加入父 fap 包含路径列表的库相对路径列表。默认值[.]即库源码根目录。sources用于收集该库包含文件的文件名掩码列表。路径相对库源码根目录。默认值[*.c*]。cflags构建该库时使用的额外编译器标志列表。默认值[]。cdefines构建该库时使用的额外预处理器定义列表。默认值[]。cincludes构建该库时使用的额外包含路径列表。路径相对应用根目录可用于为库代码提供外部搜索路径如配置头文件。默认值[]。Lib()与ExtFile()一样是清单执行环境的预置对象对应FlipperApplication.Library数据类appmanifest.py。带私有库的应用构建示例fap_private_libs[ Lib( namembedtls, fap_include_paths[include], sources[ library/des.c, library/sha1.c, library/platform_util.c, ], cdefines[MBEDTLS_ERROR_C], ), Lib( nameloclass, cflags[-Wno-error], ), ],对该片段fbt会构建 2 个库一个来自lib/mbedtls文件夹的源码另一个来自lib/loclass文件夹的源码。对mbedtlsfbt会把lib/mbedtls/include加入应用的包含路径列表只编译sources列表中指定的文件并为mbedtls源码启用MBEDTLS_ERROR_C预处理器定义对loclassfbt会把lib/loclass加入应用包含路径并构建该文件夹下所有源码。同时通过cflags[-Wno-error]禁用将警告视为错误这在编译大型第三方代码库时非常有用。两个库最终都会与应用链接在一起。依赖解析与冲突检测fbt 的内部工作流理解清单参数如何参与构建有助于写出正确的清单。在 scripts/fbt/appmanifest.py 中AppBuildset的构造过程依次执行_process_deps()appmanifest.py迭代地把所有已选应用的provides requires中缺失的依赖且目标兼容加入构建集直到没有新增依赖为止_process_ext_apps()根据EXTERNAL_APP_TYPES_MAP收集外部应用并按硬件目标兼容性分流_check_conflicts()若两个互斥应用同时出现在应用列表中抛出App conflicts for ...异常并中止构建_check_unsatisfied()检查requires中是否有未满足的依赖_check_target_match()校验所有应用与当前硬件目标如f7兼容_group_plugins()把PLUGIN类型应用挂到其宿主应用requires指向的应用名下支持fal_embedded内嵌分发。因此conflicts会在构建早期以硬失败方式生效而requires/provides会被自动补全——这也是provides与requires功能等同的原因依赖方向由构建集闭合过程统一处理。.fam 文件内容一份清单可声明多个应用.fam文件可包含一个或多个应用定义。例如 applications/services/bt/application.fam 中的一部分App( appidbt, nameBtSrv, apptypeFlipperAppType.SERVICE, entry_pointbt_srv, cdefines[SRV_BT], requires[ cli, dialogs, ], provides[ bt_start, bt_settings, ], stack_size1 * 1024, order20, sdk_headers[ bt_service/bt.h, bt_service/bt_keys_storage.h, bt_settings.h, bt_service/bt_settings_api_i.h, ], ) App( appidbt_start, apptypeFlipperAppType.STARTUP, entry_pointbt_on_system_start, order40, )这个真实例子展示了多个关键点同一个.fam可以包含 SERVICE、STARTUP 等多个App()调用bt服务通过provides声明它提供了bt_start与bt_settings两个应用任何requires[bt_start]或requires[bt_settings]的应用都能自动把bt拉进构建集sdk_headers把bt_service/bt.h等头文件暴露给外部应用作为 API。仓库中的典型用法参考编写自己的清单前可以对照这些真实案例DEBUG 应用applications/debug/accessor/application.fam 展示了apptypeFlipperAppType.DEBUG、targets[f7]、requires[gui]、fap_categoryDebug的组合。EXTERNAL 应用与 PLUGIN 集群applications/system/js_app/application.fam 是一个典型示例js_app本体是EXTERNAL带fap_icon、fap_categoryassets、fap_libs[assets]、明确的sources列表而cli_js、js_event_loop、js_gui等一批PLUGIN都requires[js_app]形成宿主 插件结构js_vgm甚至用sources[modules/js_vgm/*.c, modules/js_vgm/ICM42688P/*.c]演示了带目录的通配符收集。MENUEXTERNAL 内嵌插件applications/main/nfc/application.fam 展示了FlipperAppType.MENUEXTERNAL与fal_embeddedTrue的配合以及用!plugins、!cli、!mosgortrans、!gallagher、!nfc_emv_parser.c、!*_extra_scenes.c排除子目录/文件的sources写法注意排除规则匹配目录/文件名本身而非完整路径。构建集配置fbt_options.py 中的FIRMWARE_APPS定义了default与unit_tests两个预设按 appid 引用应用集合FIRMWARE_APP_SET选择生效的预设。这也解释了appid作为构建系统主键的作用。编写清单的常见错误与排查结合源码校验逻辑以下几点最容易踩坑appid不合规必须匹配^[a-z0-9_]$否则抛出Invalid appid ...重复appid整个固件中 appid 必须唯一否则报Duplicate app declarationPLUGIN 设置stack_size会提示改为FlipperAppType.EXTERNALPLUGIN还必须有requires内置应用使用 FAP 专属参数如非外部类型设置fap_extbuild/fap_private_libs会被拒绝外部类型设置resources同样会被拒绝.fam为空文件执行后没有任何App()定义会被视为 malformedC 入口点entry_point指定的 C 函数必须用extern C包裹否则链接时因名字修饰找不到符号栈大小权衡stack_size过小导致栈溢出崩溃过大浪费堆内存可用top/freeCLI 实测后再定。掌握 FAM 语法与校验规则后你就可以为固件新增组件、把应用打包成 FAP、组织插件集群并放心地让fbt在构建时自动解析依赖、检测冲突最终生成你想要的固件形态。【免费下载链接】unleashed-firmwareFlipper Zero Unleashed Firmware项目地址: https://gitcode.com/GitHub_Trending/un/unleashed-firmware创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门