fbt_sdk.py 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209
  1. from SCons.Builder import Builder
  2. from SCons.Action import Action
  3. from SCons.Errors import UserError
  4. # from SCons.Scanner import C
  5. from SCons.Script import Mkdir, Copy, Delete, Entry
  6. from SCons.Util import LogicalLines
  7. import os.path
  8. import posixpath
  9. import pathlib
  10. from fbt.sdk import SdkCollector, SdkCache
  11. def prebuild_sdk_emitter(target, source, env):
  12. target.append(env.ChangeFileExtension(target[0], ".d"))
  13. target.append(env.ChangeFileExtension(target[0], ".i.c"))
  14. return target, source
  15. def prebuild_sdk_create_origin_file(target, source, env):
  16. mega_file = env.subst("${TARGET}.c", target=target[0])
  17. with open(mega_file, "wt") as sdk_c:
  18. sdk_c.write("\n".join(f"#include <{h.path}>" for h in env["SDK_HEADERS"]))
  19. class SdkTreeBuilder:
  20. def __init__(self, env, target, source) -> None:
  21. self.env = env
  22. self.target = target
  23. self.source = source
  24. self.header_depends = []
  25. self.header_dirs = []
  26. self.target_sdk_dir = env.subst("f${TARGET_HW}_sdk")
  27. self.sdk_deploy_dir = target[0].Dir(self.target_sdk_dir)
  28. def _parse_sdk_depends(self):
  29. deps_file = self.source[0]
  30. with open(deps_file.path, "rt") as deps_f:
  31. lines = LogicalLines(deps_f).readlines()
  32. _, depends = lines[0].split(":", 1)
  33. self.header_depends = list(
  34. filter(lambda fname: fname.endswith(".h"), depends.split()),
  35. )
  36. self.header_dirs = sorted(
  37. set(map(os.path.normpath, map(os.path.dirname, self.header_depends)))
  38. )
  39. def _generate_sdk_meta(self):
  40. filtered_paths = [self.target_sdk_dir]
  41. full_fw_paths = list(
  42. map(
  43. os.path.normpath,
  44. (self.env.Dir(inc_dir).relpath for inc_dir in self.env["CPPPATH"]),
  45. )
  46. )
  47. sdk_dirs = ", ".join(f"'{dir}'" for dir in self.header_dirs)
  48. for dir in full_fw_paths:
  49. if dir in sdk_dirs:
  50. filtered_paths.append(
  51. posixpath.normpath(posixpath.join(self.target_sdk_dir, dir))
  52. )
  53. sdk_env = self.env.Clone()
  54. sdk_env.Replace(CPPPATH=filtered_paths)
  55. with open(self.target[0].path, "wt") as f:
  56. cmdline_options = sdk_env.subst(
  57. "$CCFLAGS $_CCCOMCOM", target=Entry("dummy")
  58. )
  59. f.write(cmdline_options.replace("\\", "/"))
  60. f.write("\n")
  61. def _create_deploy_commands(self):
  62. dirs_to_create = set(
  63. self.sdk_deploy_dir.Dir(dirpath) for dirpath in self.header_dirs
  64. )
  65. actions = [
  66. Delete(self.sdk_deploy_dir),
  67. Mkdir(self.sdk_deploy_dir),
  68. ]
  69. actions += [Mkdir(d) for d in dirs_to_create]
  70. actions += [
  71. Copy(
  72. self.sdk_deploy_dir.File(h).path,
  73. h,
  74. )
  75. for h in self.header_depends
  76. ]
  77. return actions
  78. def generate_actions(self):
  79. self._parse_sdk_depends()
  80. self._generate_sdk_meta()
  81. return self._create_deploy_commands()
  82. def deploy_sdk_tree(target, source, env, for_signature):
  83. if for_signature:
  84. return []
  85. sdk_tree = SdkTreeBuilder(env, target, source)
  86. return sdk_tree.generate_actions()
  87. def gen_sdk_data(sdk_cache: SdkCache):
  88. api_def = []
  89. api_def.extend(
  90. (f"#include <{h.name}>" for h in sdk_cache.get_headers()),
  91. )
  92. api_def.append(f"const int elf_api_version = {sdk_cache.version.as_int()};")
  93. api_def.append(
  94. "static constexpr auto elf_api_table = sort(create_array_t<sym_entry>("
  95. )
  96. api_lines = []
  97. for fun_def in sdk_cache.get_functions():
  98. api_lines.append(
  99. f"API_METHOD({fun_def.name}, {fun_def.returns}, ({fun_def.params}))"
  100. )
  101. for var_def in sdk_cache.get_variables():
  102. api_lines.append(f"API_VARIABLE({var_def.name}, {var_def.var_type })")
  103. api_def.append(",\n".join(api_lines))
  104. api_def.append("));")
  105. return api_def
  106. def _check_sdk_is_up2date(sdk_cache: SdkCache):
  107. if not sdk_cache.is_buildable():
  108. raise UserError(
  109. "SDK version is not finalized, please review changes and re-run operation"
  110. )
  111. def validate_sdk_cache(source, target, env):
  112. # print(f"Generating SDK for {source[0]} to {target[0]}")
  113. current_sdk = SdkCollector()
  114. current_sdk.process_source_file_for_sdk(source[0].path)
  115. for h in env["SDK_HEADERS"]:
  116. current_sdk.add_header_to_sdk(pathlib.Path(h.path).as_posix())
  117. sdk_cache = SdkCache(target[0].path)
  118. sdk_cache.validate_api(current_sdk.get_api())
  119. sdk_cache.save()
  120. _check_sdk_is_up2date(sdk_cache)
  121. def generate_sdk_symbols(source, target, env):
  122. sdk_cache = SdkCache(source[0].path)
  123. _check_sdk_is_up2date(sdk_cache)
  124. api_def = gen_sdk_data(sdk_cache)
  125. with open(target[0].path, "wt") as f:
  126. f.write("\n".join(api_def))
  127. def generate(env, **kw):
  128. env.Append(
  129. BUILDERS={
  130. "SDKPrebuilder": Builder(
  131. emitter=prebuild_sdk_emitter,
  132. action=[
  133. Action(
  134. prebuild_sdk_create_origin_file,
  135. "$SDK_PREGEN_COMSTR",
  136. ),
  137. Action(
  138. "$CC -o $TARGET -E -P $CCFLAGS $_CCCOMCOM $SDK_PP_FLAGS -MMD ${TARGET}.c",
  139. "$SDK_COMSTR",
  140. ),
  141. ],
  142. suffix=".i",
  143. ),
  144. "SDKTree": Builder(
  145. generator=deploy_sdk_tree,
  146. src_suffix=".d",
  147. ),
  148. "SDKSymUpdater": Builder(
  149. action=Action(
  150. validate_sdk_cache,
  151. "$SDKSYM_UPDATER_COMSTR",
  152. ),
  153. suffix=".csv",
  154. src_suffix=".i",
  155. ),
  156. "SDKSymGenerator": Builder(
  157. action=Action(
  158. generate_sdk_symbols,
  159. "$SDKSYM_GENERATOR_COMSTR",
  160. ),
  161. suffix=".h",
  162. src_suffix=".csv",
  163. ),
  164. }
  165. )
  166. def exists(env):
  167. return True