SConstruct 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355
  1. #
  2. # Main Flipper Build System entry point
  3. #
  4. # This file is evaluated by scons (the build system) every time fbt is invoked.
  5. # Scons constructs all referenced environments & their targets' dependency
  6. # trees on startup. So, to keep startup time as low as possible, we're hiding
  7. # construction of certain targets behind command-line options.
  8. import os
  9. from fbt.util import path_as_posix
  10. DefaultEnvironment(tools=[])
  11. EnsurePythonVersion(3, 8)
  12. # Progress(["OwO\r", "owo\r", "uwu\r", "owo\r"], interval=15)
  13. # This environment is created only for loading options & validating file/dir existence
  14. fbt_variables = SConscript("site_scons/commandline.scons")
  15. cmd_environment = Environment(
  16. toolpath=["#/scripts/fbt_tools"],
  17. tools=[
  18. ("fbt_help", {"vars": fbt_variables}),
  19. ],
  20. variables=fbt_variables,
  21. )
  22. # Building basic environment - tools, utility methods, cross-compilation
  23. # settings, gcc flags for Cortex-M4, basic builders and more
  24. coreenv = SConscript(
  25. "site_scons/environ.scons",
  26. exports={"VAR_ENV": cmd_environment},
  27. toolpath=["#/scripts/fbt_tools"],
  28. )
  29. SConscript("site_scons/cc.scons", exports={"ENV": coreenv})
  30. # Create a separate "dist" environment and add construction envs to it
  31. distenv = coreenv.Clone(
  32. tools=[
  33. "fbt_dist",
  34. "fbt_debugopts",
  35. "openocd",
  36. "blackmagic",
  37. "jflash",
  38. ],
  39. ENV=os.environ,
  40. UPDATE_BUNDLE_DIR="dist/${DIST_DIR}/f${TARGET_HW}-update-${DIST_SUFFIX}",
  41. )
  42. firmware_env = distenv.AddFwProject(
  43. base_env=coreenv,
  44. fw_type="firmware",
  45. fw_env_key="FW_ENV",
  46. )
  47. # If enabled, initialize updater-related targets
  48. if GetOption("fullenv") or any(
  49. filter(lambda target: "updater" in target or "flash_usb" in target, BUILD_TARGETS)
  50. ):
  51. updater_env = distenv.AddFwProject(
  52. base_env=coreenv,
  53. fw_type="updater",
  54. fw_env_key="UPD_ENV",
  55. )
  56. # Target for self-update package
  57. dist_basic_arguments = [
  58. "--bundlever",
  59. '"${UPDATE_VERSION_STRING}"',
  60. ]
  61. dist_radio_arguments = [
  62. "--radio",
  63. '"${ROOT_DIR.abspath}/${COPRO_STACK_BIN_DIR}/${COPRO_STACK_BIN}"',
  64. "--radiotype",
  65. "${COPRO_STACK_TYPE}",
  66. "${COPRO_DISCLAIMER}",
  67. "--obdata",
  68. '"${ROOT_DIR.abspath}/${COPRO_OB_DATA}"',
  69. ]
  70. dist_resource_arguments = [
  71. "-r",
  72. '"${ROOT_DIR.abspath}/assets/resources"',
  73. ]
  74. dist_splash_arguments = (
  75. [
  76. "--splash",
  77. distenv.subst("assets/slideshow/$UPDATE_SPLASH"),
  78. ]
  79. if distenv["UPDATE_SPLASH"]
  80. else []
  81. )
  82. selfupdate_dist = distenv.DistCommand(
  83. "updater_package",
  84. (distenv["DIST_DEPENDS"], firmware_env["FW_RESOURCES"]),
  85. DIST_EXTRA=[
  86. *dist_basic_arguments,
  87. *dist_radio_arguments,
  88. *dist_resource_arguments,
  89. *dist_splash_arguments,
  90. ],
  91. )
  92. selfupdate_min_dist = distenv.DistCommand(
  93. "updater_minpackage",
  94. distenv["DIST_DEPENDS"],
  95. DIST_EXTRA=dist_basic_arguments,
  96. )
  97. # Updater debug
  98. distenv.PhonyTarget(
  99. "updater_debug",
  100. "${GDBPYCOM}",
  101. source=updater_env["FW_ELF"],
  102. GDBREMOTE="${OPENOCD_GDB_PIPE}",
  103. )
  104. distenv.PhonyTarget(
  105. "updater_blackmagic",
  106. "${GDBPYCOM}",
  107. source=updater_env["FW_ELF"],
  108. GDBOPTS=distenv.subst("$GDBOPTS_BLACKMAGIC"),
  109. GDBREMOTE="${BLACKMAGIC_ADDR}",
  110. )
  111. # Installation over USB & CLI
  112. usb_update_package = distenv.AddUsbFlashTarget(
  113. "#build/usbinstall.flag", (firmware_env["FW_RESOURCES"], selfupdate_dist)
  114. )
  115. distenv.Alias("flash_usb_full", usb_update_package)
  116. usb_minupdate_package = distenv.AddUsbFlashTarget(
  117. "#build/minusbinstall.flag", (selfupdate_min_dist,)
  118. )
  119. distenv.Alias("flash_usb", usb_minupdate_package)
  120. # Target for copying & renaming binaries to dist folder
  121. basic_dist = distenv.DistCommand("fw_dist", distenv["DIST_DEPENDS"])
  122. distenv.Default(basic_dist)
  123. dist_dir_name = distenv.GetProjetDirName()
  124. dist_dir = distenv.Dir(f"#/dist/{dist_dir_name}")
  125. external_apps_artifacts = firmware_env["FW_EXTAPPS"]
  126. external_app_list = external_apps_artifacts.application_map.values()
  127. fap_dist = [
  128. distenv.Install(
  129. dist_dir.Dir("debug_elf"),
  130. list(app_artifact.debug for app_artifact in external_app_list),
  131. ),
  132. *(
  133. distenv.Install(
  134. dist_dir.File(dist_entry[1]).dir,
  135. app_artifact.compact,
  136. )
  137. for app_artifact in external_app_list
  138. for dist_entry in app_artifact.dist_entries
  139. ),
  140. ]
  141. Depends(
  142. fap_dist,
  143. list(app_artifact.validator for app_artifact in external_app_list),
  144. )
  145. Alias("fap_dist", fap_dist)
  146. # distenv.Default(fap_dist)
  147. distenv.Depends(firmware_env["FW_RESOURCES"], external_apps_artifacts.resources_dist)
  148. # Copy all faps to device
  149. fap_deploy = distenv.PhonyTarget(
  150. "fap_deploy",
  151. "${PYTHON3} ${ROOT_DIR}/scripts/storage.py send ${SOURCE} /ext/apps",
  152. source=Dir("#/assets/resources/apps"),
  153. )
  154. # Target for bundling core2 package for qFlipper
  155. copro_dist = distenv.CoproBuilder(
  156. "#/build/core2_firmware.tgz",
  157. [],
  158. )
  159. distenv.AlwaysBuild(copro_dist)
  160. distenv.Alias("copro_dist", copro_dist)
  161. firmware_flash = distenv.AddOpenOCDFlashTarget(firmware_env)
  162. distenv.Alias("flash", firmware_flash)
  163. firmware_jflash = distenv.AddJFlashTarget(firmware_env)
  164. distenv.Alias("jflash", firmware_jflash)
  165. firmware_bm_flash = distenv.PhonyTarget(
  166. "flash_blackmagic",
  167. "$GDB $GDBOPTS $SOURCES $GDBFLASH",
  168. source=firmware_env["FW_ELF"],
  169. GDBOPTS="${GDBOPTS_BASE} ${GDBOPTS_BLACKMAGIC}",
  170. GDBREMOTE="${BLACKMAGIC_ADDR}",
  171. GDBFLASH=[
  172. "-ex",
  173. "load",
  174. "-ex",
  175. "quit",
  176. ],
  177. )
  178. gdb_backtrace_all_threads = distenv.PhonyTarget(
  179. "gdb_trace_all",
  180. "$GDB $GDBOPTS $SOURCES $GDBFLASH",
  181. source=firmware_env["FW_ELF"],
  182. GDBOPTS="${GDBOPTS_BASE}",
  183. GDBREMOTE="${OPENOCD_GDB_PIPE}",
  184. GDBFLASH=[
  185. "-ex",
  186. "thread apply all bt",
  187. "-ex",
  188. "quit",
  189. ],
  190. )
  191. # Debugging firmware
  192. firmware_debug = distenv.PhonyTarget(
  193. "debug",
  194. "${GDBPYCOM}",
  195. source=firmware_env["FW_ELF"],
  196. GDBOPTS="${GDBOPTS_BASE}",
  197. GDBREMOTE="${OPENOCD_GDB_PIPE}",
  198. FBT_FAP_DEBUG_ELF_ROOT=path_as_posix(firmware_env.subst("$FBT_FAP_DEBUG_ELF_ROOT")),
  199. )
  200. distenv.Depends(firmware_debug, firmware_flash)
  201. distenv.PhonyTarget(
  202. "blackmagic",
  203. "${GDBPYCOM}",
  204. source=firmware_env["FW_ELF"],
  205. GDBOPTS="${GDBOPTS_BASE} ${GDBOPTS_BLACKMAGIC}",
  206. GDBREMOTE="${BLACKMAGIC_ADDR}",
  207. FBT_FAP_DEBUG_ELF_ROOT=path_as_posix(firmware_env.subst("$FBT_FAP_DEBUG_ELF_ROOT")),
  208. )
  209. # Debug alien elf
  210. debug_other_opts = [
  211. "-ex",
  212. "source ${FBT_DEBUG_DIR}/PyCortexMDebug/PyCortexMDebug.py",
  213. # "-ex",
  214. # "source ${FBT_DEBUG_DIR}/FreeRTOS/FreeRTOS.py",
  215. "-ex",
  216. "source ${FBT_DEBUG_DIR}/flipperversion.py",
  217. "-ex",
  218. "fw-version",
  219. ]
  220. distenv.PhonyTarget(
  221. "debug_other",
  222. "${GDBPYCOM}",
  223. GDBOPTS="${GDBOPTS_BASE}",
  224. GDBREMOTE="${OPENOCD_GDB_PIPE}",
  225. GDBPYOPTS=debug_other_opts,
  226. )
  227. distenv.PhonyTarget(
  228. "debug_other_blackmagic",
  229. "${GDBPYCOM}",
  230. GDBOPTS="${GDBOPTS_BASE} ${GDBOPTS_BLACKMAGIC}",
  231. GDBREMOTE="${BLACKMAGIC_ADDR}",
  232. GDBPYOPTS=debug_other_opts,
  233. )
  234. # Just start OpenOCD
  235. distenv.PhonyTarget(
  236. "openocd",
  237. "${OPENOCDCOM}",
  238. )
  239. # Linter
  240. distenv.PhonyTarget(
  241. "lint",
  242. "${PYTHON3} ${FBT_SCRIPT_DIR}/lint.py check ${LINT_SOURCES}",
  243. LINT_SOURCES=[n.srcnode() for n in firmware_env["LINT_SOURCES"]],
  244. )
  245. distenv.PhonyTarget(
  246. "format",
  247. "${PYTHON3} ${FBT_SCRIPT_DIR}/lint.py format ${LINT_SOURCES}",
  248. LINT_SOURCES=[n.srcnode() for n in firmware_env["LINT_SOURCES"]],
  249. )
  250. # PY_LINT_SOURCES contains recursively-built modules' SConscript files + application manifests
  251. # Here we add additional Python files residing in repo root
  252. firmware_env.Append(
  253. PY_LINT_SOURCES=[
  254. # Py code folders
  255. "site_scons",
  256. "scripts",
  257. # Extra files
  258. "SConstruct",
  259. "firmware.scons",
  260. "fbt_options.py",
  261. ]
  262. )
  263. black_commandline = "@${PYTHON3} -m black ${PY_BLACK_ARGS} ${PY_LINT_SOURCES}"
  264. black_base_args = ["--include", '"\\.scons|\\.py|SConscript|SConstruct"']
  265. distenv.PhonyTarget(
  266. "lint_py",
  267. black_commandline,
  268. PY_BLACK_ARGS=[
  269. "--check",
  270. "--diff",
  271. *black_base_args,
  272. ],
  273. PY_LINT_SOURCES=firmware_env["PY_LINT_SOURCES"],
  274. )
  275. distenv.PhonyTarget(
  276. "format_py",
  277. black_commandline,
  278. PY_BLACK_ARGS=black_base_args,
  279. PY_LINT_SOURCES=firmware_env["PY_LINT_SOURCES"],
  280. )
  281. # Start Flipper CLI via PySerial's miniterm
  282. distenv.PhonyTarget("cli", "${PYTHON3} ${FBT_SCRIPT_DIR}/serial_cli.py")
  283. # Find blackmagic probe
  284. distenv.PhonyTarget(
  285. "get_blackmagic",
  286. "@echo $( ${BLACKMAGIC_ADDR} $)",
  287. )
  288. # Find STLink probe ids
  289. distenv.PhonyTarget(
  290. "get_stlink",
  291. distenv.Action(
  292. lambda **kw: distenv.GetDevices(),
  293. None,
  294. ),
  295. )
  296. # Prepare vscode environment
  297. vscode_dist = distenv.Install("#.vscode", distenv.Glob("#.vscode/example/*"))
  298. distenv.Precious(vscode_dist)
  299. distenv.NoClean(vscode_dist)
  300. distenv.Alias("vscode_dist", vscode_dist)
  301. # Configure shell with build tools
  302. distenv.PhonyTarget(
  303. "env",
  304. "@echo $( ${FBT_SCRIPT_DIR}/toolchain/fbtenv.sh $)",
  305. )