fbt_assets.py 5.5 KB

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