SConstruct 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321
  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. import subprocess
  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(tools=[], variables=fbt_variables)
  16. Help(fbt_variables.GenerateHelpText(cmd_environment))
  17. # Building basic environment - tools, utility methods, cross-compilation
  18. # settings, gcc flags for Cortex-M4, basic builders and more
  19. coreenv = SConscript(
  20. "site_scons/environ.scons",
  21. exports={"VAR_ENV": cmd_environment},
  22. )
  23. SConscript("site_scons/cc.scons", exports={"ENV": coreenv})
  24. # Store root dir in environment for certain tools
  25. coreenv["ROOT_DIR"] = Dir(".")
  26. # Create a separate "dist" environment and add construction envs to it
  27. distenv = coreenv.Clone(
  28. tools=["fbt_dist", "openocd", "blackmagic", "jflash"],
  29. OPENOCD_GDB_PIPE=[
  30. "|openocd -c 'gdb_port pipe; log_output debug/openocd.log' ${[SINGLEQUOTEFUNC(OPENOCD_OPTS)]}"
  31. ],
  32. GDBOPTS_BASE=[
  33. "-ex",
  34. "target extended-remote ${GDBREMOTE}",
  35. "-ex",
  36. "set confirm off",
  37. "-ex",
  38. "set pagination off",
  39. ],
  40. GDBOPTS_BLACKMAGIC=[
  41. "-ex",
  42. "monitor swdp_scan",
  43. "-ex",
  44. "monitor debug_bmp enable",
  45. "-ex",
  46. "attach 1",
  47. "-ex",
  48. "set mem inaccessible-by-default off",
  49. ],
  50. GDBPYOPTS=[
  51. "-ex",
  52. "source debug/FreeRTOS/FreeRTOS.py",
  53. "-ex",
  54. "source debug/flipperapps.py",
  55. "-ex",
  56. "source debug/PyCortexMDebug/PyCortexMDebug.py",
  57. "-ex",
  58. "svd_load ${SVD_FILE}",
  59. "-ex",
  60. "compare-sections",
  61. ],
  62. JFLASHPROJECT="${ROOT_DIR.abspath}/debug/fw.jflash",
  63. ENV=os.environ,
  64. )
  65. firmware_env = distenv.AddFwProject(
  66. base_env=coreenv,
  67. fw_type="firmware",
  68. fw_env_key="FW_ENV",
  69. )
  70. # If enabled, initialize updater-related targets
  71. if GetOption("fullenv") or any(
  72. filter(lambda target: "updater" in target or "flash_usb" in target, BUILD_TARGETS)
  73. ):
  74. updater_env = distenv.AddFwProject(
  75. base_env=coreenv,
  76. fw_type="updater",
  77. fw_env_key="UPD_ENV",
  78. )
  79. # Target for self-update package
  80. dist_basic_arguments = [
  81. "--bundlever",
  82. '"${UPDATE_VERSION_STRING}"',
  83. ]
  84. dist_radio_arguments = [
  85. "--radio",
  86. '"${ROOT_DIR.abspath}/${COPRO_STACK_BIN_DIR}/${COPRO_STACK_BIN}"',
  87. "--radiotype",
  88. "${COPRO_STACK_TYPE}",
  89. "${COPRO_DISCLAIMER}",
  90. "--obdata",
  91. '"${ROOT_DIR.abspath}/${COPRO_OB_DATA}"',
  92. ]
  93. dist_resource_arguments = [
  94. "-r",
  95. '"${ROOT_DIR.abspath}/assets/resources"',
  96. ]
  97. dist_splash_arguments = (
  98. [
  99. "--splash",
  100. distenv.subst("assets/slideshow/$UPDATE_SPLASH"),
  101. ]
  102. if distenv["UPDATE_SPLASH"]
  103. else []
  104. )
  105. selfupdate_dist = distenv.DistCommand(
  106. "updater_package",
  107. (distenv["DIST_DEPENDS"], firmware_env["FW_RESOURCES"]),
  108. DIST_EXTRA=[
  109. *dist_basic_arguments,
  110. *dist_radio_arguments,
  111. *dist_resource_arguments,
  112. *dist_splash_arguments,
  113. ],
  114. )
  115. selfupdate_min_dist = distenv.DistCommand(
  116. "updater_minpackage",
  117. distenv["DIST_DEPENDS"],
  118. DIST_EXTRA=dist_basic_arguments,
  119. )
  120. # Updater debug
  121. distenv.PhonyTarget(
  122. "updater_debug",
  123. "${GDBPYCOM}",
  124. source=updater_env["FW_ELF"],
  125. GDBREMOTE="${OPENOCD_GDB_PIPE}",
  126. )
  127. distenv.PhonyTarget(
  128. "updater_blackmagic",
  129. "${GDBPYCOM}",
  130. source=updater_env["FW_ELF"],
  131. GDBOPTS=distenv.subst("$GDBOPTS_BLACKMAGIC"),
  132. GDBREMOTE="${BLACKMAGIC_ADDR}",
  133. )
  134. # Installation over USB & CLI
  135. usb_update_package = distenv.AddUsbFlashTarget(
  136. "#build/usbinstall.flag", (firmware_env["FW_RESOURCES"], selfupdate_dist)
  137. )
  138. distenv.Alias("flash_usb_full", usb_update_package)
  139. usb_minupdate_package = distenv.AddUsbFlashTarget(
  140. "#build/minusbinstall.flag", (selfupdate_min_dist,)
  141. )
  142. distenv.Alias("flash_usb", usb_minupdate_package)
  143. # Target for copying & renaming binaries to dist folder
  144. basic_dist = distenv.DistCommand("fw_dist", distenv["DIST_DEPENDS"])
  145. distenv.Default(basic_dist)
  146. dist_dir = distenv.GetProjetDirName()
  147. plugin_dist = [
  148. distenv.Install(
  149. f"#/dist/{dist_dir}/apps/debug_elf",
  150. firmware_env["FW_EXTAPPS"]["debug"].values(),
  151. ),
  152. *(
  153. distenv.Install(f"#/dist/{dist_dir}/apps/{dist_entry[0]}", dist_entry[1])
  154. for dist_entry in firmware_env["FW_EXTAPPS"]["dist"].values()
  155. ),
  156. ]
  157. Depends(plugin_dist, firmware_env["FW_EXTAPPS"]["validators"].values())
  158. Alias("plugin_dist", plugin_dist)
  159. # distenv.Default(plugin_dist)
  160. plugin_resources_dist = list(
  161. distenv.Install(f"#/assets/resources/apps/{dist_entry[0]}", dist_entry[1])
  162. for dist_entry in firmware_env["FW_EXTAPPS"]["dist"].values()
  163. )
  164. distenv.Depends(firmware_env["FW_RESOURCES"], plugin_resources_dist)
  165. # Target for bundling core2 package for qFlipper
  166. copro_dist = distenv.CoproBuilder(
  167. distenv.Dir("assets/core2_firmware"),
  168. [],
  169. )
  170. distenv.Alias("copro_dist", copro_dist)
  171. firmware_flash = distenv.AddOpenOCDFlashTarget(firmware_env)
  172. distenv.Alias("flash", firmware_flash)
  173. firmware_jflash = distenv.AddJFlashTarget(firmware_env)
  174. distenv.Alias("jflash", firmware_jflash)
  175. firmware_bm_flash = distenv.PhonyTarget(
  176. "flash_blackmagic",
  177. "$GDB $GDBOPTS $SOURCES $GDBFLASH",
  178. source=firmware_env["FW_ELF"],
  179. GDBOPTS="${GDBOPTS_BASE} ${GDBOPTS_BLACKMAGIC}",
  180. GDBREMOTE="${BLACKMAGIC_ADDR}",
  181. GDBFLASH=[
  182. "-ex",
  183. "load",
  184. "-ex",
  185. "quit",
  186. ],
  187. )
  188. # Debugging firmware
  189. firmware_debug = distenv.PhonyTarget(
  190. "debug",
  191. "${GDBPYCOM}",
  192. source=firmware_env["FW_ELF"],
  193. GDBOPTS="${GDBOPTS_BASE}",
  194. GDBREMOTE="${OPENOCD_GDB_PIPE}",
  195. )
  196. distenv.Depends(firmware_debug, firmware_flash)
  197. distenv.PhonyTarget(
  198. "blackmagic",
  199. "${GDBPYCOM}",
  200. source=firmware_env["FW_ELF"],
  201. GDBOPTS="${GDBOPTS_BASE} ${GDBOPTS_BLACKMAGIC}",
  202. GDBREMOTE="${BLACKMAGIC_ADDR}",
  203. )
  204. # Debug alien elf
  205. distenv.PhonyTarget(
  206. "debug_other",
  207. "${GDBPYCOM}",
  208. GDBOPTS="${GDBOPTS_BASE}",
  209. GDBREMOTE="${OPENOCD_GDB_PIPE}",
  210. GDBPYOPTS='-ex "source debug/PyCortexMDebug/PyCortexMDebug.py" ',
  211. )
  212. distenv.PhonyTarget(
  213. "debug_other_blackmagic",
  214. "${GDBPYCOM}",
  215. GDBOPTS="${GDBOPTS_BASE} ${GDBOPTS_BLACKMAGIC}",
  216. GDBREMOTE="$${BLACKMAGIC_ADDR}",
  217. )
  218. # Just start OpenOCD
  219. distenv.PhonyTarget(
  220. "openocd",
  221. "${OPENOCDCOM}",
  222. )
  223. # Linter
  224. distenv.PhonyTarget(
  225. "lint",
  226. "${PYTHON3} scripts/lint.py check ${LINT_SOURCES}",
  227. LINT_SOURCES=firmware_env["LINT_SOURCES"],
  228. )
  229. distenv.PhonyTarget(
  230. "format",
  231. "${PYTHON3} scripts/lint.py format ${LINT_SOURCES}",
  232. LINT_SOURCES=firmware_env["LINT_SOURCES"],
  233. )
  234. # PY_LINT_SOURCES contains recursively-built modules' SConscript files + application manifests
  235. # Here we add additional Python files residing in repo root
  236. firmware_env.Append(
  237. PY_LINT_SOURCES=[
  238. # Py code folders
  239. "site_scons",
  240. "scripts",
  241. # Extra files
  242. "SConstruct",
  243. "firmware.scons",
  244. "fbt_options.py",
  245. ]
  246. )
  247. black_commandline = "@${PYTHON3} -m black ${PY_BLACK_ARGS} ${PY_LINT_SOURCES}"
  248. black_base_args = ["--include", '"\\.scons|\\.py|SConscript|SConstruct"']
  249. distenv.PhonyTarget(
  250. "lint_py",
  251. black_commandline,
  252. PY_BLACK_ARGS=[
  253. "--check",
  254. "--diff",
  255. *black_base_args,
  256. ],
  257. PY_LINT_SOURCES=firmware_env["PY_LINT_SOURCES"],
  258. )
  259. distenv.PhonyTarget(
  260. "format_py",
  261. black_commandline,
  262. PY_BLACK_ARGS=black_base_args,
  263. PY_LINT_SOURCES=firmware_env["PY_LINT_SOURCES"],
  264. )
  265. # Start Flipper CLI via PySerial's miniterm
  266. distenv.PhonyTarget("cli", "${PYTHON3} scripts/serial_cli.py")
  267. # Find blackmagic probe
  268. distenv.PhonyTarget(
  269. "get_blackmagic",
  270. "@echo $( ${BLACKMAGIC_ADDR} $)",
  271. )
  272. # Prepare vscode environment
  273. vscode_dist = distenv.Install("#.vscode", distenv.Glob("#.vscode/example/*"))
  274. distenv.Precious(vscode_dist)
  275. distenv.NoClean(vscode_dist)
  276. distenv.Alias("vscode_dist", vscode_dist)