76d38e832e
* fbt: reproducible manifest builds, less rebuild on small updates; scripts: assets: using timestamp from commandline af available * fbt: added app import validation for launch_app & single app build targets * fbt: COMSTR for app imports validation * docs: minor fixes * docs: markdown fix * vscode: comments for RTOS startup Co-authored-by: あく <alleteam@gmail.com>
166 lines
5.0 KiB
Python
166 lines
5.0 KiB
Python
from SCons.Builder import Builder
|
|
from SCons.Action import Action
|
|
from SCons.Errors import UserError
|
|
import SCons.Warnings
|
|
|
|
import os
|
|
import pathlib
|
|
from fbt.elfmanifest import assemble_manifest_data
|
|
from fbt.sdk import SdkCache
|
|
import itertools
|
|
|
|
|
|
def BuildAppElf(env, app):
|
|
work_dir = env.subst("$EXT_APPS_WORK_DIR")
|
|
|
|
app_alias = f"{env['FIRMWARE_BUILD_CFG']}_{app.appid}"
|
|
app_original_elf = os.path.join(work_dir, f"{app.appid}_d")
|
|
app_sources = list(
|
|
itertools.chain.from_iterable(
|
|
env.GlobRecursive(source_type, os.path.join(work_dir, app._appdir.relpath))
|
|
for source_type in app.sources
|
|
)
|
|
)
|
|
app_elf_raw = env.Program(
|
|
app_original_elf,
|
|
app_sources,
|
|
APP_ENTRY=app.entry_point,
|
|
LIBS=env["LIBS"] + app.fap_libs,
|
|
)
|
|
|
|
app_elf_dump = env.ObjDump(app_elf_raw)
|
|
env.Alias(f"{app_alias}_list", app_elf_dump)
|
|
|
|
app_elf_augmented = env.EmbedAppMetadata(
|
|
os.path.join(env.subst("$PLUGIN_ELF_DIR"), app.appid),
|
|
app_elf_raw,
|
|
APP=app,
|
|
)
|
|
|
|
manifest_vals = vars(app)
|
|
manifest_vals = {
|
|
k: v for k, v in manifest_vals.items() if k not in ("_appdir", "_apppath")
|
|
}
|
|
|
|
env.Depends(
|
|
app_elf_augmented,
|
|
[env["SDK_DEFINITION"], env.Value(manifest_vals)],
|
|
)
|
|
if app.fap_icon:
|
|
env.Depends(
|
|
app_elf_augmented,
|
|
env.File(f"{app._apppath}/{app.fap_icon}"),
|
|
)
|
|
env.Alias(app_alias, app_elf_augmented)
|
|
|
|
app_elf_import_validator = env.ValidateAppImports(app_elf_augmented)
|
|
env.AlwaysBuild(app_elf_import_validator)
|
|
env.Alias(app_alias, app_elf_import_validator)
|
|
return (app_elf_augmented, app_elf_raw, app_elf_import_validator)
|
|
|
|
|
|
def prepare_app_metadata(target, source, env):
|
|
sdk_cache = SdkCache(env.subst("$SDK_DEFINITION"), load_version_only=True)
|
|
|
|
if not sdk_cache.is_buildable():
|
|
raise UserError(
|
|
"SDK version is not finalized, please review changes and re-run operation"
|
|
)
|
|
|
|
app = env["APP"]
|
|
meta_file_name = source[0].path + ".meta"
|
|
with open(meta_file_name, "wb") as f:
|
|
# f.write(f"hello this is {app}")
|
|
f.write(
|
|
assemble_manifest_data(
|
|
app_manifest=app,
|
|
hardware_target=int(env.subst("$TARGET_HW")),
|
|
sdk_version=sdk_cache.version.as_int(),
|
|
)
|
|
)
|
|
|
|
|
|
def validate_app_imports(target, source, env):
|
|
sdk_cache = SdkCache(env.subst("$SDK_DEFINITION"), load_version_only=False)
|
|
app_syms = set()
|
|
with open(target[0].path, "rt") as f:
|
|
for line in f:
|
|
app_syms.add(line.split()[0])
|
|
unresolved_syms = app_syms - sdk_cache.get_valid_names()
|
|
if unresolved_syms:
|
|
SCons.Warnings.warn(
|
|
SCons.Warnings.LinkWarning,
|
|
f"{source[0].path}: app won't run. Unresolved symbols: {unresolved_syms}",
|
|
)
|
|
|
|
|
|
def GetExtAppFromPath(env, app_dir):
|
|
if not app_dir:
|
|
raise UserError("APPSRC= not set")
|
|
|
|
appmgr = env["APPMGR"]
|
|
|
|
app = None
|
|
for dir_part in reversed(pathlib.Path(app_dir).parts):
|
|
if app := appmgr.find_by_appdir(dir_part):
|
|
break
|
|
if not app:
|
|
raise UserError(f"Failed to resolve application for given APPSRC={app_dir}")
|
|
|
|
app_elf = env["_extapps"]["compact"].get(app.appid, None)
|
|
if not app_elf:
|
|
raise UserError(
|
|
f"Application {app.appid} is not configured for building as external"
|
|
)
|
|
|
|
app_validator = env["_extapps"]["validators"].get(app.appid, None)
|
|
|
|
return (app, app_elf[0], app_validator[0])
|
|
|
|
|
|
def generate(env, **kw):
|
|
env.SetDefault(EXT_APPS_WORK_DIR=kw.get("EXT_APPS_WORK_DIR"))
|
|
env.VariantDir(env.subst("$EXT_APPS_WORK_DIR"), env.Dir("#"), duplicate=False)
|
|
|
|
env.AddMethod(BuildAppElf)
|
|
env.AddMethod(GetExtAppFromPath)
|
|
env.Append(
|
|
BUILDERS={
|
|
"EmbedAppMetadata": Builder(
|
|
action=[
|
|
Action(prepare_app_metadata, "$APPMETA_COMSTR"),
|
|
Action(
|
|
"${OBJCOPY} "
|
|
"--remove-section .ARM.attributes "
|
|
"--add-section .fapmeta=${SOURCE}.meta "
|
|
"--set-section-flags .fapmeta=contents,noload,readonly,data "
|
|
"--strip-debug --strip-unneeded "
|
|
"--add-gnu-debuglink=${SOURCE} "
|
|
"${SOURCES} ${TARGET}",
|
|
"$APPMETAEMBED_COMSTR",
|
|
),
|
|
],
|
|
suffix=".fap",
|
|
src_suffix=".elf",
|
|
),
|
|
"ValidateAppImports": Builder(
|
|
action=[
|
|
Action(
|
|
"@${NM} -P -u ${SOURCE} > ${TARGET}",
|
|
None, # "$APPDUMP_COMSTR",
|
|
),
|
|
Action(
|
|
validate_app_imports,
|
|
"$APPCHECK_COMSTR",
|
|
),
|
|
],
|
|
suffix=".impsyms",
|
|
src_suffix=".fap",
|
|
),
|
|
}
|
|
)
|
|
|
|
|
|
def exists(env):
|
|
return True
|