| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393 |
- from SCons.Platform import TempFileMunge
- from SCons.Node import FS
- from SCons.Errors import UserError
- import os
- import multiprocessing
- import pathlib
- SetOption("num_jobs", multiprocessing.cpu_count())
- SetOption("max_drift", 1)
- # SetOption("silent", False)
- ufbt_state_dir = Dir(os.environ.get("UFBT_STATE_DIR", "#.ufbt"))
- ufbt_script_dir = Dir(os.environ.get("UFBT_SCRIPT_DIR"))
- ufbt_current_sdk_dir = ufbt_state_dir.Dir("current")
- SConsignFile(ufbt_state_dir.File(".sconsign.dblite").abspath)
- ufbt_variables = SConscript("commandline.scons")
- forward_os_env = {
- # Import PATH from OS env - scons doesn't do that by default
- "PATH": os.environ["PATH"],
- }
- # Proxying environment to child processes & scripts
- variables_to_forward = [
- # CI/CD variables
- "WORKFLOW_BRANCH_OR_TAG",
- "DIST_SUFFIX",
- # Python & other tools
- "HOME",
- "APPDATA",
- "PYTHONHOME",
- "PYTHONNOUSERSITE",
- "TMP",
- "TEMP",
- # Colors for tools
- "TERM",
- ]
- if proxy_env := GetOption("proxy_env"):
- variables_to_forward.extend(proxy_env.split(","))
- for env_value_name in variables_to_forward:
- if environ_value := os.environ.get(env_value_name, None):
- forward_os_env[env_value_name] = environ_value
- # Core environment init - loads SDK state, sets up paths, etc.
- core_env = Environment(
- variables=ufbt_variables,
- ENV=forward_os_env,
- UFBT_STATE_DIR=ufbt_state_dir,
- UFBT_CURRENT_SDK_DIR=ufbt_current_sdk_dir,
- UFBT_SCRIPT_DIR=ufbt_script_dir,
- toolpath=[ufbt_current_sdk_dir.Dir("scripts/ufbt/site_tools")],
- tools=[
- "ufbt_state",
- ("ufbt_help", {"vars": ufbt_variables}),
- ],
- )
- if "update" in BUILD_TARGETS:
- SConscript(
- "update.scons",
- exports={"core_env": core_env},
- )
- if "purge" in BUILD_TARGETS:
- core_env.Execute(Delete(ufbt_state_dir))
- print("uFBT state purged")
- Exit(0)
- # Now we can import stuff bundled with SDK - it was added to sys.path by ufbt_state
- from fbt.util import (
- tempfile_arg_esc_func,
- single_quote,
- extract_abs_dir,
- extract_abs_dir_path,
- wrap_tempfile,
- path_as_posix,
- )
- from fbt.appmanifest import FlipperAppType
- from fbt.sdk.cache import SdkCache
- # Base environment with all tools loaded from SDK
- env = core_env.Clone(
- toolpath=[core_env["FBT_SCRIPT_DIR"].Dir("fbt_tools")],
- tools=[
- "fbt_tweaks",
- (
- "crosscc",
- {
- "toolchain_prefix": "arm-none-eabi-",
- "versions": (" 10.3",),
- },
- ),
- "fwbin",
- "python3",
- "sconsrecursiveglob",
- "sconsmodular",
- "ccache",
- "fbt_apps",
- "fbt_extapps",
- "fbt_assets",
- ("compilation_db", {"COMPILATIONDB_COMSTR": "\tCDB\t${TARGET}"}),
- ],
- FBT_FAP_DEBUG_ELF_ROOT=ufbt_state_dir.Dir("build"),
- TEMPFILE=TempFileMunge,
- MAXLINELENGTH=2048,
- PROGSUFFIX=".elf",
- TEMPFILEARGESCFUNC=tempfile_arg_esc_func,
- SINGLEQUOTEFUNC=single_quote,
- ABSPATHGETTERFUNC=extract_abs_dir_path,
- APPS=[],
- UFBT_API_VERSION=SdkCache(
- core_env.subst("$SDK_DEFINITION"), load_version_only=True
- ).version,
- APPCHECK_COMSTR="\tAPPCHK\t${SOURCE}\n\t\tTarget: ${TARGET_HW}, API: ${UFBT_API_VERSION}",
- )
- wrap_tempfile(env, "LINKCOM")
- wrap_tempfile(env, "ARCOM")
- # print(env.Dump())
- # Dist env
- dist_env = env.Clone(
- tools=[
- "fbt_dist",
- "fbt_debugopts",
- "openocd",
- "blackmagic",
- "jflash",
- "textfile",
- ],
- ENV=os.environ,
- OPENOCD_OPTS=[
- "-f",
- "interface/stlink.cfg",
- "-c",
- "transport select hla_swd",
- "-f",
- "${FBT_DEBUG_DIR}/stm32wbx.cfg",
- "-c",
- "stm32wbx.cpu configure -rtos auto",
- ],
- )
- openocd_target = dist_env.OpenOCDFlash(
- dist_env["UFBT_STATE_DIR"].File("flash"),
- dist_env["FW_BIN"],
- OPENOCD_COMMAND=[
- "-c",
- "program ${SOURCE.posix} reset exit 0x08000000",
- ],
- )
- dist_env.Alias("firmware_flash", openocd_target)
- dist_env.Alias("flash", openocd_target)
- if env["FORCE"]:
- env.AlwaysBuild(openocd_target)
- firmware_debug = dist_env.PhonyTarget(
- "debug",
- "${GDBPYCOM}",
- source=dist_env["FW_ELF"],
- GDBOPTS="${GDBOPTS_BASE}",
- GDBREMOTE="${OPENOCD_GDB_PIPE}",
- FBT_FAP_DEBUG_ELF_ROOT=path_as_posix(dist_env.subst("$FBT_FAP_DEBUG_ELF_ROOT")),
- )
- dist_env.PhonyTarget(
- "blackmagic",
- "${GDBPYCOM}",
- source=dist_env["FW_ELF"],
- GDBOPTS="${GDBOPTS_BASE} ${GDBOPTS_BLACKMAGIC}",
- GDBREMOTE="${BLACKMAGIC_ADDR}",
- FBT_FAP_DEBUG_ELF_ROOT=path_as_posix(dist_env.subst("$FBT_FAP_DEBUG_ELF_ROOT")),
- )
- dist_env.PhonyTarget(
- "flash_blackmagic",
- "$GDB $GDBOPTS $SOURCES $GDBFLASH",
- source=dist_env["FW_ELF"],
- GDBOPTS="${GDBOPTS_BASE} ${GDBOPTS_BLACKMAGIC}",
- GDBREMOTE="${BLACKMAGIC_ADDR}",
- GDBFLASH=[
- "-ex",
- "load",
- "-ex",
- "quit",
- ],
- )
- flash_usb_full = dist_env.UsbInstall(
- dist_env["UFBT_STATE_DIR"].File("usbinstall"),
- [],
- )
- dist_env.AlwaysBuild(flash_usb_full)
- dist_env.Alias("flash_usb", flash_usb_full)
- dist_env.Alias("flash_usb_full", flash_usb_full)
- # App build environment
- appenv = env.Clone(
- CCCOM=env["CCCOM"].replace("$CFLAGS", "$CFLAGS_APP $CFLAGS"),
- CXXCOM=env["CXXCOM"].replace("$CXXFLAGS", "$CXXFLAGS_APP $CXXFLAGS"),
- LINKCOM=env["LINKCOM"].replace("$LINKFLAGS", "$LINKFLAGS_APP $LINKFLAGS"),
- COMPILATIONDB_USE_ABSPATH=True,
- )
- original_app_dir = Dir(appenv.subst("$UFBT_APP_DIR"))
- app_mount_point = Dir("#/app/")
- app_mount_point.addRepository(original_app_dir)
- appenv.LoadAppManifest(app_mount_point)
- appenv.PrepareApplicationsBuild()
- #######################
- apps_artifacts = appenv["EXT_APPS"]
- apps_to_build_as_faps = [
- FlipperAppType.PLUGIN,
- FlipperAppType.EXTERNAL,
- ]
- known_extapps = [
- app
- for apptype in apps_to_build_as_faps
- for app in appenv["APPBUILD"].get_apps_of_type(apptype, True)
- ]
- for app in known_extapps:
- app_artifacts = appenv.BuildAppElf(app)
- app_src_dir = extract_abs_dir(app_artifacts.app._appdir)
- app_artifacts.installer = [
- appenv.Install(app_src_dir.Dir("dist"), app_artifacts.compact),
- appenv.Install(app_src_dir.Dir("dist").Dir("debug"), app_artifacts.debug),
- ]
- if appenv["FORCE"]:
- appenv.AlwaysBuild([extapp.compact for extapp in apps_artifacts.values()])
- # Final steps - target aliases
- install_and_check = [
- (extapp.installer, extapp.validator) for extapp in apps_artifacts.values()
- ]
- Alias(
- "faps",
- install_and_check,
- )
- Default(install_and_check)
- # Compilation database
- fwcdb = appenv.CompilationDatabase(
- original_app_dir.Dir(".vscode").File("compile_commands.json")
- )
- AlwaysBuild(fwcdb)
- Precious(fwcdb)
- NoClean(fwcdb)
- if len(apps_artifacts):
- Default(fwcdb)
- # launch handler
- runnable_apps = appenv["APPBUILD"].get_apps_of_type(FlipperAppType.EXTERNAL, True)
- app_to_launch = None
- if len(runnable_apps) == 1:
- app_to_launch = runnable_apps[0].appid
- elif len(runnable_apps) > 1:
- # more than 1 app - try to find one with matching id
- app_to_launch = appenv.subst("$APPID")
- def ambiguous_app_call(**kw):
- raise UserError(
- f"More than one app is runnable: {', '.join(app.appid for app in runnable_apps)}. Please specify an app with APPID=..."
- )
- if app_to_launch:
- appenv.AddAppLaunchTarget(app_to_launch, "launch")
- else:
- dist_env.PhonyTarget("launch", Action(ambiguous_app_call, None))
- # cli handler
- appenv.PhonyTarget(
- "cli",
- '${PYTHON3} "${FBT_SCRIPT_DIR}/serial_cli.py"',
- )
- # Linter
- dist_env.PhonyTarget(
- "lint",
- "${PYTHON3} ${FBT_SCRIPT_DIR}/lint.py check ${LINT_SOURCES}",
- source=original_app_dir.File(".clang-format"),
- LINT_SOURCES=[original_app_dir],
- )
- dist_env.PhonyTarget(
- "format",
- "${PYTHON3} ${FBT_SCRIPT_DIR}/lint.py format ${LINT_SOURCES}",
- source=original_app_dir.File(".clang-format"),
- LINT_SOURCES=[original_app_dir],
- )
- # Prepare vscode environment
- def _path_as_posix(path):
- return pathlib.Path(path).as_posix()
- vscode_dist = []
- project_template_dir = dist_env["UFBT_SCRIPT_ROOT"].Dir("project_template")
- for template_file in project_template_dir.Dir(".vscode").glob("*"):
- vscode_dist.append(
- dist_env.Substfile(
- original_app_dir.Dir(".vscode").File(template_file.name),
- template_file,
- SUBST_DICT={
- "@UFBT_VSCODE_PATH_SEP@": os.path.pathsep,
- "@UFBT_TOOLCHAIN_ARM_TOOLCHAIN_DIR@": pathlib.Path(
- dist_env.WhereIs("arm-none-eabi-gcc")
- ).parent.as_posix(),
- "@UFBT_TOOLCHAIN_GCC@": _path_as_posix(
- dist_env.WhereIs("arm-none-eabi-gcc")
- ),
- "@UFBT_TOOLCHAIN_GDB_PY@": _path_as_posix(
- dist_env.WhereIs("arm-none-eabi-gdb-py")
- ),
- "@UFBT_TOOLCHAIN_OPENOCD@": _path_as_posix(dist_env.WhereIs("openocd")),
- "@UFBT_APP_DIR@": _path_as_posix(original_app_dir.abspath),
- "@UFBT_ROOT_DIR@": _path_as_posix(Dir("#").abspath),
- "@UFBT_DEBUG_DIR@": dist_env["FBT_DEBUG_DIR"],
- "@UFBT_DEBUG_ELF_DIR@": _path_as_posix(
- dist_env["FBT_FAP_DEBUG_ELF_ROOT"].abspath
- ),
- "@UFBT_FIRMWARE_ELF@": _path_as_posix(dist_env["FW_ELF"].abspath),
- },
- )
- )
- for config_file in project_template_dir.glob(".*"):
- if isinstance(config_file, FS.Dir):
- continue
- vscode_dist.append(dist_env.Install(original_app_dir, config_file))
- dist_env.Precious(vscode_dist)
- dist_env.NoClean(vscode_dist)
- dist_env.Alias("vscode_dist", vscode_dist)
- # Creating app from base template
- dist_env.SetDefault(FBT_APPID=appenv.subst("$APPID") or "template")
- app_template_dist = []
- for template_file in project_template_dir.Dir("app_template").glob("*"):
- dist_file_name = dist_env.subst(template_file.name)
- if template_file.name.endswith(".png"):
- app_template_dist.append(
- dist_env.InstallAs(original_app_dir.File(dist_file_name), template_file)
- )
- else:
- app_template_dist.append(
- dist_env.Substfile(
- original_app_dir.File(dist_file_name),
- template_file,
- SUBST_DICT={
- "@FBT_APPID@": dist_env.subst("$FBT_APPID"),
- },
- )
- )
- AddPostAction(
- app_template_dist[-1],
- [
- Mkdir(original_app_dir.Dir("images")),
- Touch(original_app_dir.Dir("images").File(".gitkeep")),
- ],
- )
- dist_env.Precious(app_template_dist)
- dist_env.NoClean(app_template_dist)
- dist_env.Alias("create", app_template_dist)
|