SConstruct 8.1 KB

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