fbt_assets.py 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179
  1. from SCons.Builder import Builder
  2. from SCons.Action import Action
  3. from SCons.Errors import StopError
  4. import os
  5. import subprocess
  6. from ansi.color import fg
  7. def icons_emitter(target, source, env):
  8. target = [
  9. target[0].File(env.subst("${ICON_FILE_NAME}.c")),
  10. target[0].File(env.subst("${ICON_FILE_NAME}.h")),
  11. ]
  12. return target, source
  13. def proto_emitter(target, source, env):
  14. target = []
  15. for src in source:
  16. basename = os.path.splitext(src.name)[0]
  17. target.append(env.File(f"compiled/{basename}.pb.c"))
  18. target.append(env.File(f"compiled/{basename}.pb.h"))
  19. return target, source
  20. def dolphin_emitter(target, source, env):
  21. res_root_dir = source[0].Dir(env["DOLPHIN_RES_TYPE"])
  22. source = [res_root_dir]
  23. source.extend(env.GlobRecursive("*.*", res_root_dir.srcnode()))
  24. target_base_dir = target[0]
  25. env.Replace(_DOLPHIN_OUT_DIR=target[0])
  26. if env["DOLPHIN_RES_TYPE"] == "external":
  27. target = [target_base_dir.File("manifest.txt")]
  28. ## A detailed list of files to be generated
  29. ## works better if we just leave target the folder
  30. # target = []
  31. # target.extend(
  32. # map(
  33. # lambda node: target_base_dir.File(
  34. # res_root_dir.rel_path(node).replace(".png", ".bm")
  35. # ),
  36. # filter(lambda node: isinstance(node, SCons.Node.FS.File), source),
  37. # )
  38. # )
  39. else:
  40. asset_basename = f"assets_dolphin_{env['DOLPHIN_RES_TYPE']}"
  41. target = [
  42. target_base_dir.File(asset_basename + ".c"),
  43. target_base_dir.File(asset_basename + ".h"),
  44. ]
  45. # Debug output
  46. # print(
  47. # f"Dolphin res type: {env['DOLPHIN_RES_TYPE']},\ntarget files:",
  48. # list(f.path for f in target),
  49. # f"\nsource files:",
  50. # list(f.path for f in source),
  51. # )
  52. return target, source
  53. def _invoke_git(args, source_dir):
  54. cmd = ["git"]
  55. cmd.extend(args)
  56. return (
  57. subprocess.check_output(cmd, cwd=source_dir, stderr=subprocess.STDOUT)
  58. .strip()
  59. .decode()
  60. )
  61. def proto_ver_generator(target, source, env):
  62. target_file = target[0]
  63. src_dir = source[0].dir.abspath
  64. try:
  65. git_fetch = _invoke_git(
  66. ["fetch", "--tags"],
  67. source_dir=src_dir,
  68. )
  69. except (subprocess.CalledProcessError, EnvironmentError) as e:
  70. # Not great, not terrible
  71. print(fg.boldred("Git: fetch failed"))
  72. try:
  73. git_describe = _invoke_git(
  74. ["describe", "--tags", "--abbrev=0"],
  75. source_dir=src_dir,
  76. )
  77. except (subprocess.CalledProcessError, EnvironmentError) as e:
  78. raise StopError("Git: describe failed")
  79. git_major, git_minor = git_describe.split(".")
  80. version_file_data = (
  81. "#pragma once",
  82. f"#define PROTOBUF_MAJOR_VERSION {git_major}",
  83. f"#define PROTOBUF_MINOR_VERSION {git_minor}",
  84. "",
  85. )
  86. with open(str(target_file), "wt") as file:
  87. file.write("\n".join(version_file_data))
  88. def CompileIcons(env, target_dir, source_dir, *, icon_bundle_name="assets_icons"):
  89. # Gathering icons sources
  90. icons_src = env.GlobRecursive("*.png", source_dir)
  91. icons_src += env.GlobRecursive("frame_rate", source_dir)
  92. icons = env.IconBuilder(
  93. target_dir,
  94. source_dir,
  95. ICON_FILE_NAME=icon_bundle_name,
  96. )
  97. env.Depends(icons, icons_src)
  98. return icons
  99. def generate(env):
  100. env.SetDefault(
  101. ASSETS_COMPILER="${FBT_SCRIPT_DIR}/assets.py",
  102. NANOPB_COMPILER="${ROOT_DIR}/lib/nanopb/generator/nanopb_generator.py",
  103. )
  104. env.AddMethod(CompileIcons)
  105. if not env["VERBOSE"]:
  106. env.SetDefault(
  107. ICONSCOMSTR="\tICONS\t${TARGET}",
  108. PROTOCOMSTR="\tPROTO\t${SOURCE}",
  109. DOLPHINCOMSTR="\tDOLPHIN\t${DOLPHIN_RES_TYPE}",
  110. RESMANIFESTCOMSTR="\tMANIFEST\t${TARGET}",
  111. PBVERCOMSTR="\tPBVER\t${TARGET}",
  112. )
  113. env.Append(
  114. BUILDERS={
  115. "IconBuilder": Builder(
  116. action=Action(
  117. '${PYTHON3} "${ASSETS_COMPILER}" icons "${ABSPATHGETTERFUNC(SOURCE)}" "${TARGET.dir}" --filename ${ICON_FILE_NAME}',
  118. "${ICONSCOMSTR}",
  119. ),
  120. emitter=icons_emitter,
  121. ),
  122. "ProtoBuilder": Builder(
  123. action=Action(
  124. '${PYTHON3} "${NANOPB_COMPILER}" -q -I${SOURCE.dir.posix} -D${TARGET.dir.posix} ${SOURCES.posix}',
  125. "${PROTOCOMSTR}",
  126. ),
  127. emitter=proto_emitter,
  128. suffix=".pb.c",
  129. src_suffix=".proto",
  130. ),
  131. "DolphinSymBuilder": Builder(
  132. action=Action(
  133. '${PYTHON3} "${ASSETS_COMPILER}" dolphin -s dolphin_${DOLPHIN_RES_TYPE} "${SOURCE}" "${_DOLPHIN_OUT_DIR}"',
  134. "${DOLPHINCOMSTR}",
  135. ),
  136. emitter=dolphin_emitter,
  137. ),
  138. "DolphinExtBuilder": Builder(
  139. action=Action(
  140. '${PYTHON3} "${ASSETS_COMPILER}" dolphin "${SOURCE}" "${_DOLPHIN_OUT_DIR}"',
  141. "${DOLPHINCOMSTR}",
  142. ),
  143. emitter=dolphin_emitter,
  144. ),
  145. "ProtoVerBuilder": Builder(
  146. action=Action(
  147. proto_ver_generator,
  148. "${PBVERCOMSTR}",
  149. ),
  150. ),
  151. }
  152. )
  153. def exists(env):
  154. return True