extapps.scons 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197
  1. from dataclasses import dataclass, field
  2. from os.path import dirname
  3. from SCons.Node import NodeList
  4. from SCons.Warnings import warn, WarningOnByDefault
  5. from SCons.Errors import UserError
  6. Import("ENV")
  7. from fbt.appmanifest import FlipperAppType
  8. appenv = ENV["APPENV"] = ENV.Clone(
  9. tools=[
  10. "fbt_extapps",
  11. "fbt_assets",
  12. "fbt_sdk",
  13. ],
  14. RESOURCES_ROOT=ENV.Dir("#/assets/resources"),
  15. )
  16. appenv.Replace(
  17. LINKER_SCRIPT_PATH=appenv["APP_LINKER_SCRIPT_PATH"],
  18. )
  19. appenv.AppendUnique(
  20. CCFLAGS=[
  21. "-ggdb3",
  22. "-mword-relocations",
  23. "-mlong-calls",
  24. "-fno-common",
  25. "-nostdlib",
  26. "-fvisibility=hidden",
  27. ],
  28. LINKFLAGS=[
  29. "-Ur",
  30. "-Wl,-Ur",
  31. # "-Wl,--orphan-handling=error",
  32. "-Bsymbolic",
  33. "-nostartfiles",
  34. "-mlong-calls",
  35. "-fno-common",
  36. "-nostdlib",
  37. "-Wl,--gc-sections",
  38. "-Wl,--no-export-dynamic",
  39. "-fvisibility=hidden",
  40. "-Wl,-e${APP_ENTRY}",
  41. "-Xlinker",
  42. "-Map=${TARGET}.map",
  43. "-specs=nano.specs",
  44. "-specs=nosys.specs",
  45. ],
  46. LIBS=[
  47. "m",
  48. "gcc",
  49. "stdc++",
  50. "supc++",
  51. ],
  52. )
  53. @dataclass
  54. class FlipperExtAppBuildArtifacts:
  55. application_map: dict = field(default_factory=dict)
  56. resources_dist: NodeList = field(default_factory=NodeList)
  57. sdk_tree: NodeList = field(default_factory=NodeList)
  58. apps_to_build_as_faps = [
  59. FlipperAppType.PLUGIN,
  60. FlipperAppType.EXTERNAL,
  61. FlipperAppType.DEBUG,
  62. ]
  63. known_extapps = [
  64. app
  65. for apptype in apps_to_build_as_faps
  66. for app in appenv["APPBUILD"].get_apps_of_type(apptype, True)
  67. ]
  68. # Ugly access to global option
  69. if extra_app_list := GetOption("extra_ext_apps"):
  70. known_extapps.extend(map(appenv["APPMGR"].get, extra_app_list.split(",")))
  71. incompatible_apps = []
  72. for app in known_extapps:
  73. if not app.supports_hardware_target(appenv.subst("f${TARGET_HW}")):
  74. incompatible_apps.append(app)
  75. continue
  76. appenv.BuildAppElf(app)
  77. extapps = FlipperExtAppBuildArtifacts()
  78. extapps.application_map = appenv["EXT_APPS"]
  79. if incompatible_apps:
  80. warn(
  81. WarningOnByDefault,
  82. f"Skipping build of {len(incompatible_apps)} incompatible app(s): "
  83. + ", ".join(f"'{app.name}' (id '{app.appid}')" for app in incompatible_apps),
  84. )
  85. if appenv["FORCE"]:
  86. appenv.AlwaysBuild(
  87. list(app_artifact.compact for app_artifact in extapps.application_map.values())
  88. )
  89. Alias(
  90. "faps",
  91. list(app_artifact.validator for app_artifact in extapps.application_map.values()),
  92. )
  93. extapps.resources_dist = appenv.FapDist(appenv["RESOURCES_ROOT"], [])
  94. if appsrc := appenv.subst("$APPSRC"):
  95. deploy_sources, flipp_dist_paths, validators = [], [], []
  96. run_script_extra_ars = ""
  97. def _add_dist_targets(app_artifacts):
  98. validators.append(app_artifacts.validator)
  99. for _, ext_path in app_artifacts.dist_entries:
  100. deploy_sources.append(app_artifacts.compact)
  101. flipp_dist_paths.append(f"/ext/{ext_path}")
  102. return app_artifacts
  103. def _add_host_app_to_targets(host_app):
  104. artifacts_app_to_run = appenv["EXT_APPS"].get(host_app.appid, None)
  105. _add_dist_targets(artifacts_app_to_run)
  106. for plugin in host_app._plugins:
  107. _add_dist_targets(appenv["EXT_APPS"].get(plugin.appid, None))
  108. artifacts_app_to_run = appenv.GetExtAppByIdOrPath(appsrc)
  109. if artifacts_app_to_run.app.apptype == FlipperAppType.PLUGIN:
  110. # We deploy host app instead
  111. host_app = appenv["APPMGR"].get(artifacts_app_to_run.app.requires[0])
  112. if host_app:
  113. if host_app.apptype == FlipperAppType.EXTERNAL:
  114. _add_host_app_to_targets(host_app)
  115. else:
  116. # host app is a built-in app
  117. run_script_extra_ars = f"-a {host_app.name}"
  118. _add_dist_targets(artifacts_app_to_run)
  119. else:
  120. raise UserError("Host app is unknown")
  121. else:
  122. _add_host_app_to_targets(artifacts_app_to_run.app)
  123. # print(deploy_sources, flipp_dist_paths)
  124. appenv.PhonyTarget(
  125. "launch_app",
  126. '${PYTHON3} "${APP_RUN_SCRIPT}" ${EXTRA_ARGS} -s ${SOURCES} -t ${FLIPPER_FILE_TARGETS}',
  127. source=deploy_sources,
  128. FLIPPER_FILE_TARGETS=flipp_dist_paths,
  129. EXTRA_ARGS=run_script_extra_ars,
  130. )
  131. appenv.Alias("launch_app", validators)
  132. # SDK management
  133. sdk_origin_path = "${BUILD_DIR}/sdk_origin"
  134. sdk_source = appenv.SDKPrebuilder(
  135. sdk_origin_path,
  136. # Deps on root SDK headers and generated files
  137. (appenv["SDK_HEADERS"], appenv["FW_ASSETS_HEADERS"]),
  138. )
  139. # Extra deps on headers included in deeper levels
  140. # Available on second and subsequent builds
  141. Depends(sdk_source, appenv.ProcessSdkDepends(f"{sdk_origin_path}.d"))
  142. appenv["SDK_DIR"] = appenv.Dir("${BUILD_DIR}/sdk")
  143. sdk_tree = appenv.SDKTree(appenv["SDK_DIR"], sdk_origin_path)
  144. # AlwaysBuild(sdk_tree)
  145. Alias("sdk_tree", sdk_tree)
  146. extapps.sdk_tree = sdk_tree
  147. sdk_apicheck = appenv.SDKSymUpdater(appenv["SDK_DEFINITION"], sdk_origin_path)
  148. Precious(sdk_apicheck)
  149. NoClean(sdk_apicheck)
  150. AlwaysBuild(sdk_apicheck)
  151. Alias("sdk_check", sdk_apicheck)
  152. sdk_apisyms = appenv.SDKSymGenerator(
  153. "${BUILD_DIR}/assets/compiled/symbols.h", appenv["SDK_DEFINITION"]
  154. )
  155. Alias("api_syms", sdk_apisyms)
  156. ENV.Replace(
  157. SDK_APISYMS=sdk_apisyms,
  158. _APP_ICONS=appenv["_APP_ICONS"],
  159. )
  160. if appenv["FORCE"]:
  161. appenv.AlwaysBuild(sdk_source, sdk_tree, sdk_apicheck, sdk_apisyms)
  162. Return("extapps")