from dataclasses import dataclass, field from os.path import dirname from SCons.Node import NodeList from SCons.Warnings import warn, WarningOnByDefault from SCons.Errors import UserError Import("ENV") from fbt.appmanifest import FlipperAppType appenv = ENV["APPENV"] = ENV.Clone( tools=[ "fbt_extapps", "fbt_assets", "fbt_sdk", ], RESOURCES_ROOT=ENV.Dir("#/assets/resources"), ) appenv.Replace( LINKER_SCRIPT_PATH=appenv["APP_LINKER_SCRIPT_PATH"], ) appenv.AppendUnique( CCFLAGS=[ "-ggdb3", "-mword-relocations", "-mlong-calls", "-fno-common", "-nostdlib", "-fvisibility=hidden", ], LINKFLAGS=[ "-Ur", "-Wl,-Ur", # "-Wl,--orphan-handling=error", "-Bsymbolic", "-nostartfiles", "-mlong-calls", "-fno-common", "-nostdlib", "-Wl,--gc-sections", "-Wl,--no-export-dynamic", "-fvisibility=hidden", "-Wl,-e${APP_ENTRY}", "-Xlinker", "-Map=${TARGET}.map", "-specs=nano.specs", "-specs=nosys.specs", ], LIBS=[ "m", "gcc", "stdc++", "supc++", ], ) @dataclass class FlipperExtAppBuildArtifacts: application_map: dict = field(default_factory=dict) resources_dist: NodeList = field(default_factory=NodeList) sdk_tree: NodeList = field(default_factory=NodeList) apps_to_build_as_faps = [ FlipperAppType.PLUGIN, FlipperAppType.EXTERNAL, FlipperAppType.DEBUG, ] known_extapps = [ app for apptype in apps_to_build_as_faps for app in appenv["APPBUILD"].get_apps_of_type(apptype, True) ] # Ugly access to global option if extra_app_list := GetOption("extra_ext_apps"): known_extapps.extend(map(appenv["APPMGR"].get, extra_app_list.split(","))) incompatible_apps = [] for app in known_extapps: if not app.supports_hardware_target(appenv.subst("f${TARGET_HW}")): incompatible_apps.append(app) continue appenv.BuildAppElf(app) extapps = FlipperExtAppBuildArtifacts() extapps.application_map = appenv["EXT_APPS"] if incompatible_apps: warn( WarningOnByDefault, f"Skipping build of {len(incompatible_apps)} incompatible app(s): " + ", ".join(f"'{app.name}' (id '{app.appid}')" for app in incompatible_apps), ) if appenv["FORCE"]: appenv.AlwaysBuild( list(app_artifact.compact for app_artifact in extapps.application_map.values()) ) Alias( "faps", list(app_artifact.validator for app_artifact in extapps.application_map.values()), ) extapps.resources_dist = appenv.FapDist(appenv["RESOURCES_ROOT"], []) if appsrc := appenv.subst("$APPSRC"): deploy_sources, flipp_dist_paths, validators = [], [], [] run_script_extra_ars = "" def _add_dist_targets(app_artifacts): validators.append(app_artifacts.validator) for _, ext_path in app_artifacts.dist_entries: deploy_sources.append(app_artifacts.compact) flipp_dist_paths.append(f"/ext/{ext_path}") return app_artifacts def _add_host_app_to_targets(host_app): artifacts_app_to_run = appenv["EXT_APPS"].get(host_app.appid, None) _add_dist_targets(artifacts_app_to_run) for plugin in host_app._plugins: _add_dist_targets(appenv["EXT_APPS"].get(plugin.appid, None)) artifacts_app_to_run = appenv.GetExtAppByIdOrPath(appsrc) if artifacts_app_to_run.app.apptype == FlipperAppType.PLUGIN: # We deploy host app instead host_app = appenv["APPMGR"].get(artifacts_app_to_run.app.requires[0]) if host_app: if host_app.apptype == FlipperAppType.EXTERNAL: _add_host_app_to_targets(host_app) else: # host app is a built-in app run_script_extra_ars = f"-a {host_app.name}" _add_dist_targets(artifacts_app_to_run) else: raise UserError("Host app is unknown") else: _add_host_app_to_targets(artifacts_app_to_run.app) # print(deploy_sources, flipp_dist_paths) appenv.PhonyTarget( "launch_app", '${PYTHON3} "${APP_RUN_SCRIPT}" ${EXTRA_ARGS} -s ${SOURCES} -t ${FLIPPER_FILE_TARGETS}', source=deploy_sources, FLIPPER_FILE_TARGETS=flipp_dist_paths, EXTRA_ARGS=run_script_extra_ars, ) appenv.Alias("launch_app", validators) # SDK management sdk_origin_path = "${BUILD_DIR}/sdk_origin" sdk_source = appenv.SDKPrebuilder( sdk_origin_path, # Deps on root SDK headers and generated files (appenv["SDK_HEADERS"], appenv["FW_ASSETS_HEADERS"]), ) # Extra deps on headers included in deeper levels # Available on second and subsequent builds Depends(sdk_source, appenv.ProcessSdkDepends(f"{sdk_origin_path}.d")) appenv["SDK_DIR"] = appenv.Dir("${BUILD_DIR}/sdk") sdk_tree = appenv.SDKTree(appenv["SDK_DIR"], sdk_origin_path) # AlwaysBuild(sdk_tree) Alias("sdk_tree", sdk_tree) extapps.sdk_tree = sdk_tree sdk_apicheck = appenv.SDKSymUpdater(appenv["SDK_DEFINITION"], sdk_origin_path) Precious(sdk_apicheck) NoClean(sdk_apicheck) AlwaysBuild(sdk_apicheck) Alias("sdk_check", sdk_apicheck) sdk_apisyms = appenv.SDKSymGenerator( "${BUILD_DIR}/assets/compiled/symbols.h", appenv["SDK_DEFINITION"] ) Alias("api_syms", sdk_apisyms) ENV.Replace( SDK_APISYMS=sdk_apisyms, _APP_ICONS=appenv["_APP_ICONS"], ) if appenv["FORCE"]: appenv.AlwaysBuild(sdk_source, sdk_tree, sdk_apicheck, sdk_apisyms) Return("extapps")