fbt_sdk.py 6.0 KB

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