| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302 |
- import shutil
- from SCons.Builder import Builder
- from SCons.Action import Action
- from SCons.Errors import UserError
- # from SCons.Scanner import C
- from SCons.Script import Entry
- from SCons.Util import LogicalLines
- import os.path
- import posixpath
- import pathlib
- import json
- from fbt.sdk.collector import SdkCollector
- from fbt.sdk.cache import SdkCache
- def ProcessSdkDepends(env, filename):
- try:
- with open(filename, "r") as fin:
- lines = LogicalLines(fin).readlines()
- except IOError:
- return []
- _, depends = lines[0].split(":", 1)
- depends = depends.split()
- depends.pop(0) # remove the .c file
- depends = list(
- # Don't create dependency on non-existing files
- # (e.g. when they were renamed since last build)
- filter(
- lambda file: file.exists(),
- (env.File(f"#{path}") for path in depends),
- )
- )
- return depends
- def prebuild_sdk_emitter(target, source, env):
- target.append(env.ChangeFileExtension(target[0], ".d"))
- target.append(env.ChangeFileExtension(target[0], ".i.c"))
- return target, source
- def prebuild_sdk_create_origin_file(target, source, env):
- mega_file = env.subst("${TARGET}.c", target=target[0])
- with open(mega_file, "wt") as sdk_c:
- sdk_c.write("\n".join(f"#include <{h.path}>" for h in env["SDK_HEADERS"]))
- class SdkMeta:
- def __init__(self, env, tree_builder: "SdkTreeBuilder"):
- self.env = env
- self.treebuilder = tree_builder
- def save_to(self, json_manifest_path: str):
- meta_contents = {
- "sdk_symbols": self.treebuilder.build_sdk_file_path(
- self.env["SDK_DEFINITION"].path
- ),
- "cc_args": self._wrap_scons_vars("$CCFLAGS $_CCCOMCOM"),
- "cpp_args": self._wrap_scons_vars("$CXXFLAGS $CCFLAGS $_CCCOMCOM"),
- "linker_args": self._wrap_scons_vars("$LINKFLAGS"),
- "linker_script": self.env.subst("${LINKER_SCRIPT_PATH}"),
- }
- with open(json_manifest_path, "wt") as f:
- json.dump(meta_contents, f, indent=4)
- def _wrap_scons_vars(self, vars: str):
- expanded_vars = self.env.subst(vars, target=Entry("dummy"))
- return expanded_vars.replace("\\", "/")
- class SdkTreeBuilder:
- SDK_DIR_SUBST = "SDK_ROOT_DIR"
- def __init__(self, env, target, source) -> None:
- self.env = env
- self.target = target
- self.source = source
- self.header_depends = []
- self.header_dirs = []
- self.target_sdk_dir_name = env.subst("f${TARGET_HW}_sdk")
- self.sdk_root_dir = target[0].Dir(".")
- self.sdk_deploy_dir = self.sdk_root_dir.Dir(self.target_sdk_dir_name)
- def _parse_sdk_depends(self):
- deps_file = self.source[0]
- with open(deps_file.path, "rt") as deps_f:
- lines = LogicalLines(deps_f).readlines()
- _, depends = lines[0].split(":", 1)
- self.header_depends = list(
- filter(lambda fname: fname.endswith(".h"), depends.split()),
- )
- self.header_depends.append(self.env.subst("${LINKER_SCRIPT_PATH}"))
- self.header_depends.append(self.env.subst("${SDK_DEFINITION}"))
- self.header_dirs = sorted(
- set(map(os.path.normpath, map(os.path.dirname, self.header_depends)))
- )
- def _generate_sdk_meta(self):
- filtered_paths = [self.target_sdk_dir_name]
- full_fw_paths = list(
- map(
- os.path.normpath,
- (self.env.Dir(inc_dir).relpath for inc_dir in self.env["CPPPATH"]),
- )
- )
- sdk_dirs = ", ".join(f"'{dir}'" for dir in self.header_dirs)
- filtered_paths.extend(
- map(
- self.build_sdk_file_path,
- filter(lambda path: path in sdk_dirs, full_fw_paths),
- )
- )
- sdk_env = self.env.Clone()
- sdk_env.Replace(
- CPPPATH=filtered_paths,
- LINKER_SCRIPT=self.env.subst("${APP_LINKER_SCRIPT}"),
- ORIG_LINKER_SCRIPT_PATH=self.env["LINKER_SCRIPT_PATH"],
- LINKER_SCRIPT_PATH=self.build_sdk_file_path("${ORIG_LINKER_SCRIPT_PATH}"),
- )
- meta = SdkMeta(sdk_env, self)
- meta.save_to(self.target[0].path)
- def build_sdk_file_path(self, orig_path: str) -> str:
- return posixpath.normpath(
- posixpath.join(
- self.SDK_DIR_SUBST,
- self.target_sdk_dir_name,
- orig_path,
- )
- ).replace("\\", "/")
- def emitter(self, target, source, env):
- target_folder = target[0]
- target = [target_folder.File("sdk.opts")]
- return target, source
- def _run_deploy_commands(self):
- dirs_to_create = set(
- self.sdk_deploy_dir.Dir(dirpath).path for dirpath in self.header_dirs
- )
- shutil.rmtree(self.sdk_root_dir.path, ignore_errors=False)
- for sdkdir in dirs_to_create:
- os.makedirs(sdkdir, exist_ok=True)
- for header in self.header_depends:
- shutil.copy2(header, self.sdk_deploy_dir.File(header).path)
- def deploy_action(self):
- self._parse_sdk_depends()
- self._run_deploy_commands()
- self._generate_sdk_meta()
- def deploy_sdk_tree_action(target, source, env):
- sdk_tree = SdkTreeBuilder(env, target, source)
- return sdk_tree.deploy_action()
- def deploy_sdk_tree_emitter(target, source, env):
- sdk_tree = SdkTreeBuilder(env, target, source)
- return sdk_tree.emitter(target, source, env)
- def gen_sdk_data(sdk_cache: SdkCache):
- api_def = []
- api_def.extend(
- (f"#include <{h.name}>" for h in sdk_cache.get_headers()),
- )
- api_def.append(f"const int elf_api_version = {sdk_cache.version.as_int()};")
- api_def.append(
- "static constexpr auto elf_api_table = sort(create_array_t<sym_entry>("
- )
- api_lines = []
- for fun_def in sdk_cache.get_functions():
- api_lines.append(
- f"API_METHOD({fun_def.name}, {fun_def.returns}, ({fun_def.params}))"
- )
- for var_def in sdk_cache.get_variables():
- api_lines.append(f"API_VARIABLE({var_def.name}, {var_def.var_type })")
- api_def.append(",\n".join(api_lines))
- api_def.append("));")
- return api_def
- def _check_sdk_is_up2date(sdk_cache: SdkCache):
- if not sdk_cache.is_buildable():
- raise UserError(
- "SDK version is not finalized, please review changes and re-run operation"
- )
- def validate_sdk_cache(source, target, env):
- # print(f"Generating SDK for {source[0]} to {target[0]}")
- current_sdk = SdkCollector()
- current_sdk.process_source_file_for_sdk(source[0].path)
- for h in env["SDK_HEADERS"]:
- current_sdk.add_header_to_sdk(pathlib.Path(h.path).as_posix())
- sdk_cache = SdkCache(target[0].path)
- sdk_cache.validate_api(current_sdk.get_api())
- sdk_cache.save()
- _check_sdk_is_up2date(sdk_cache)
- def generate_sdk_symbols(source, target, env):
- sdk_cache = SdkCache(source[0].path)
- _check_sdk_is_up2date(sdk_cache)
- api_def = gen_sdk_data(sdk_cache)
- with open(target[0].path, "wt") as f:
- f.write("\n".join(api_def))
- def generate(env, **kw):
- if not env["VERBOSE"]:
- env.SetDefault(
- SDK_PREGEN_COMSTR="\tPREGEN\t${TARGET}",
- SDK_COMSTR="\tSDKSRC\t${TARGET}",
- SDKSYM_UPDATER_COMSTR="\tSDKCHK\t${TARGET}",
- SDKSYM_GENERATOR_COMSTR="\tSDKSYM\t${TARGET}",
- SDKDEPLOY_COMSTR="\tSDKTREE\t${TARGET}",
- )
- # Filtering out things cxxheaderparser cannot handle
- env.SetDefault(
- SDK_PP_FLAGS=[
- '-D"_Static_assert(x,y)="',
- '-D"__asm__(x)="',
- '-D"__attribute__(x)="',
- "-Drestrict=",
- "-D_Noreturn=",
- "-D__restrict=",
- "-D__extension__=",
- "-D__inline=inline",
- "-D__inline__=inline",
- ]
- )
- env.AddMethod(ProcessSdkDepends)
- env.Append(
- BUILDERS={
- "SDKPrebuilder": Builder(
- emitter=prebuild_sdk_emitter,
- action=[
- Action(
- prebuild_sdk_create_origin_file,
- "$SDK_PREGEN_COMSTR",
- ),
- Action(
- "$CC -o $TARGET -E -P $CCFLAGS $_CCCOMCOM $SDK_PP_FLAGS -MMD ${TARGET}.c",
- "$SDK_COMSTR",
- ),
- ],
- suffix=".i",
- ),
- "SDKTree": Builder(
- action=Action(
- deploy_sdk_tree_action,
- "$SDKDEPLOY_COMSTR",
- ),
- emitter=deploy_sdk_tree_emitter,
- src_suffix=".d",
- ),
- "SDKSymUpdater": Builder(
- action=Action(
- validate_sdk_cache,
- "$SDKSYM_UPDATER_COMSTR",
- ),
- suffix=".csv",
- src_suffix=".i",
- ),
- "SDKSymGenerator": Builder(
- action=Action(
- generate_sdk_symbols,
- "$SDKSYM_GENERATOR_COMSTR",
- ),
- suffix=".h",
- src_suffix=".csv",
- ),
- }
- )
- def exists(env):
- return True
|