Просмотр исходного кода

fbt fixes for mfbt pt2 (#1951)

* fbt: split sdk management code
* scripts: fixed import handling
* fbt: sdk: reformatted paths
* scrips: dist: bundling libs as a build artifact
* fbt: sdk: better path management
* typo fix
* fbt: sdk: minor path handling fixes
* toolchain: fixed windows toolchain download
* fbt: minor refactorin
* fbt: moved sdk management code to extapps.scons
* fbt: fixed sdk symbols header path; disabled -fstack-usage
* fbt: changed pathing for .py scripts
* fbt: changed SDK_HEADERS pathing; added libusb to SDK; added icon_i.h to SDK; added hw target to SDK meta
* fbt: added libusb headers to SDK
* picopass: include cleanup; api: added subghz/registry.h; api: added mbedtls to exported headers
* picopass: fixed formatting
* fbt: fixed COPRO_ASSETS_SCRIPT
* sdk: added basic infrared apis
* toolchain: added ufbt to list of legal fbtenv callers; updated error messages
* fbt: changed manifest collection & icon processing code
* fbt: simpler srcdir lookup
* toolchain: path management fixes; fbt: fixes for fap private libs paths
* scripts: toolchain: reworked download on Windows
* toolchain: v17
* scripts: added colorlog for logging
* Github: fix unit tests

Co-authored-by: あく <alleteam@gmail.com>
hedger 3 лет назад
Родитель
Сommit
ebc2b66372
40 измененных файлов с 458 добавлено и 234 удалено
  1. 6 2
      .github/workflows/unit_tests.yml
  2. 3 7
      SConstruct
  3. BIN
      applications/plugins/picopass/125_10px.png
  4. 1 1
      applications/plugins/picopass/application.fam
  5. 0 2
      applications/plugins/picopass/picopass_worker_i.h
  6. 3 4
      applications/plugins/picopass/rfal_picopass.c
  7. 1 0
      applications/services/gui/application.fam
  8. 26 40
      firmware.scons
  9. 3 4
      firmware/SConscript
  10. 123 1
      firmware/targets/f7/api_symbols.csv
  11. 6 6
      lib/SConscript
  12. 1 1
      lib/STM32CubeWB.scons
  13. 1 1
      lib/flipper_application/SConscript
  14. 2 2
      lib/flipper_format/SConscript
  15. 5 0
      lib/infrared/SConscript
  16. 6 6
      lib/lfrfid/SConscript
  17. 4 0
      lib/libusb_stm32.scons
  18. 4 0
      lib/mbedtls.scons
  19. 1 1
      lib/misc.scons
  20. 1 1
      lib/print/SConscript
  21. 13 12
      lib/subghz/SConscript
  22. 8 0
      lib/subghz/registry.h
  23. 17 17
      lib/toolbox/SConscript
  24. 13 2
      scripts/fbt/util.py
  25. 15 0
      scripts/fbt_tools/crosscc.py
  26. 20 18
      scripts/fbt_tools/fbt_apps.py
  27. 8 12
      scripts/fbt_tools/fbt_assets.py
  28. 6 3
      scripts/fbt_tools/fbt_dist.py
  29. 17 7
      scripts/fbt_tools/fbt_extapps.py
  30. 29 17
      scripts/fbt_tools/fbt_sdk.py
  31. 4 1
      scripts/fbt_tools/fbt_version.py
  32. 2 1
      scripts/fbt_tools/fwbin.py
  33. 12 4
      scripts/flipper/app.py
  34. 4 2
      scripts/sconsdist.py
  35. 8 5
      scripts/testing/await_flipper.py
  36. 12 12
      scripts/toolchain/fbtenv.sh
  37. 24 12
      scripts/toolchain/windows-toolchain-download.ps1
  38. 2 3
      site_scons/cc.scons
  39. 12 24
      site_scons/environ.scons
  40. 35 3
      site_scons/extapps.scons

+ 6 - 2
.github/workflows/unit_tests.yml

@@ -31,22 +31,26 @@ jobs:
         id: connect
         if: steps.compile.outcome == 'success'
         run: |
-          python3 ./scripts/testing/await_flipper.py ${{steps.device.outputs.flipper}}
+          . scripts/toolchain/fbtenv.sh
+          ./scripts/testing/await_flipper.py ${{steps.device.outputs.flipper}}
 
       - name: 'Format flipper SD card'
         id: format
         if: steps.connect.outcome == 'success'
         run: |
+          . scripts/toolchain/fbtenv.sh
           ./scripts/storage.py -p ${{steps.device.outputs.flipper}} format_ext
 
       - name: 'Copy assets and unit tests data to flipper'
         id: copy
         if: steps.format.outcome == 'success'
         run: |
+          . scripts/toolchain/fbtenv.sh
           ./scripts/storage.py -p ${{steps.device.outputs.flipper}} send assets/resources /ext
           ./scripts/storage.py -p ${{steps.device.outputs.flipper}} send assets/unit_tests /ext/unit_tests
 
       - name: 'Run units and validate results'
         if: steps.copy.outcome == 'success'
         run: |
-          python3 ./scripts/testing/units.py ${{steps.device.outputs.flipper}}
+          . scripts/toolchain/fbtenv.sh
+          ./scripts/testing/units.py ${{steps.device.outputs.flipper}}

+ 3 - 7
SConstruct

@@ -33,10 +33,6 @@ coreenv = SConscript(
 )
 SConscript("site_scons/cc.scons", exports={"ENV": coreenv})
 
-# Store root dir in environment for certain tools
-coreenv["ROOT_DIR"] = Dir(".")
-
-
 # Create a separate "dist" environment and add construction envs to it
 distenv = coreenv.Clone(
     tools=[
@@ -233,13 +229,13 @@ distenv.PhonyTarget(
 # Linter
 distenv.PhonyTarget(
     "lint",
-    "${PYTHON3} scripts/lint.py check ${LINT_SOURCES}",
+    "${PYTHON3} ${FBT_SCRIPT_DIR}/lint.py check ${LINT_SOURCES}",
     LINT_SOURCES=firmware_env["LINT_SOURCES"],
 )
 
 distenv.PhonyTarget(
     "format",
-    "${PYTHON3} scripts/lint.py format ${LINT_SOURCES}",
+    "${PYTHON3} ${FBT_SCRIPT_DIR}/lint.py format ${LINT_SOURCES}",
     LINT_SOURCES=firmware_env["LINT_SOURCES"],
 )
 
@@ -280,7 +276,7 @@ distenv.PhonyTarget(
 )
 
 # Start Flipper CLI via PySerial's miniterm
-distenv.PhonyTarget("cli", "${PYTHON3} scripts/serial_cli.py")
+distenv.PhonyTarget("cli", "${PYTHON3} ${FBT_SCRIPT_DIR}/serial_cli.py")
 
 
 # Find blackmagic probe

BIN
applications/plugins/picopass/125_10px.png


+ 1 - 1
applications/plugins/picopass/application.fam

@@ -9,7 +9,7 @@ App(
     ],
     stack_size=4 * 1024,
     order=30,
-    fap_icon="../../../assets/icons/Archive/125_10px.png",
+    fap_icon="125_10px.png",
     fap_category="Tools",
     fap_libs=["mbedtls"],
     fap_private_libs=[

+ 0 - 2
applications/plugins/picopass/picopass_worker_i.h

@@ -9,8 +9,6 @@
 #include <furi_hal.h>
 
 #include <stdlib.h>
-#include <st25r3916.h>
-#include <rfal_analogConfig.h>
 #include <rfal_rf.h>
 
 #include <platform.h>

+ 3 - 4
applications/plugins/picopass/rfal_picopass.c

@@ -1,5 +1,4 @@
 #include "rfal_picopass.h"
-#include "utils.h"
 
 #define RFAL_PICOPASS_TXRX_FLAGS                                                    \
     (FURI_HAL_NFC_LL_TXRX_FLAGS_CRC_TX_MANUAL | FURI_HAL_NFC_LL_TXRX_FLAGS_AGC_ON | \
@@ -97,7 +96,7 @@ FuriHalNfcReturn rfalPicoPassPollerSelect(uint8_t* csn, rfalPicoPassSelectRes* s
 
     rfalPicoPassSelectReq selReq;
     selReq.CMD = RFAL_PICOPASS_CMD_SELECT;
-    ST_MEMCPY(selReq.CSN, csn, RFAL_PICOPASS_UID_LEN);
+    memcpy(selReq.CSN, csn, RFAL_PICOPASS_UID_LEN);
     uint16_t recvLen = 0;
     uint32_t flags = RFAL_PICOPASS_TXRX_FLAGS;
     uint32_t fwt = furi_hal_nfc_ll_ms2fc(20);
@@ -146,8 +145,8 @@ FuriHalNfcReturn rfalPicoPassPollerCheck(uint8_t* mac, rfalPicoPassCheckRes* chk
     FuriHalNfcReturn ret;
     rfalPicoPassCheckReq chkReq;
     chkReq.CMD = RFAL_PICOPASS_CMD_CHECK;
-    ST_MEMCPY(chkReq.mac, mac, 4);
-    ST_MEMSET(chkReq.null, 0, 4);
+    memcpy(chkReq.mac, mac, 4);
+    memset(chkReq.null, 0, 4);
     uint16_t recvLen = 0;
     uint32_t flags = RFAL_PICOPASS_TXRX_FLAGS;
     uint32_t fwt = furi_hal_nfc_ll_ms2fc(20);

+ 1 - 0
applications/services/gui/application.fam

@@ -12,6 +12,7 @@ App(
     order=70,
     sdk_headers=[
         "gui.h",
+        "icon_i.h",
         "elements.h",
         "view_dispatcher.h",
         "view_stack.h",

+ 26 - 40
firmware.scons

@@ -1,6 +1,7 @@
 Import("ENV", "fw_build_meta")
 
 from SCons.Errors import UserError
+from SCons.Node import FS
 import itertools
 
 from fbt_extra.util import (
@@ -14,7 +15,6 @@ env = ENV.Clone(
         ("compilation_db", {"COMPILATIONDB_COMSTR": "\tCDB\t${TARGET}"}),
         "fwbin",
         "fbt_apps",
-        "fbt_sdk",
     ],
     COMPILATIONDB_USE_ABSPATH=False,
     BUILD_DIR=fw_build_meta["build_dir"],
@@ -112,7 +112,9 @@ lib_targets = env.BuildModules(
 
 
 # Now, env is fully set up with everything to build apps
-fwenv = env.Clone()
+fwenv = env.Clone(FW_ARTIFACTS=[])
+
+fw_artifacts = fwenv["FW_ARTIFACTS"]
 
 # Set up additional app-specific build flags
 SConscript("site_scons/firmwareopts.scons", exports={"ENV": fwenv})
@@ -130,7 +132,14 @@ if extra_int_apps := GetOption("extra_int_apps"):
 if fwenv["FAP_EXAMPLES"]:
     fwenv.Append(APPDIRS=[("applications/examples", False)])
 
-fwenv.LoadApplicationManifests()
+for app_dir, _ in env["APPDIRS"]:
+    app_dir_node = env.Dir("#").Dir(app_dir)
+
+    for entry in app_dir_node.glob("*"):
+        if isinstance(entry, FS.Dir) and not str(entry).startswith("."):
+            fwenv.LoadAppManifest(entry)
+
+
 fwenv.PrepareApplicationsBuild()
 
 # Build external apps
@@ -138,6 +147,7 @@ if env["IS_BASE_FIRMWARE"]:
     extapps = fwenv["FW_EXTAPPS"] = SConscript(
         "site_scons/extapps.scons", exports={"ENV": fwenv}
     )
+    fw_artifacts.append(extapps["sdk_tree"])
 
 
 # Add preprocessor definitions for current set of apps
@@ -220,7 +230,10 @@ Depends(fwelf, lib_targets)
 AddPostAction(fwelf, fwenv["APPBUILD_DUMP"])
 AddPostAction(
     fwelf,
-    Action('${PYTHON3} "${ROOT_DIR}/scripts/fwsize.py" elf ${TARGET}', "Firmware size"),
+    Action(
+        '${PYTHON3} "${BIN_SIZE_SCRIPT}" elf ${TARGET}',
+        "Firmware size",
+    ),
 )
 
 # Produce extra firmware files
@@ -228,7 +241,7 @@ fwhex = fwenv["FW_HEX"] = fwenv.HEXBuilder("${FIRMWARE_BUILD_CFG}")
 fwbin = fwenv["FW_BIN"] = fwenv.BINBuilder("${FIRMWARE_BUILD_CFG}")
 AddPostAction(
     fwbin,
-    Action('@${PYTHON3} "${ROOT_DIR}/scripts/fwsize.py" bin ${TARGET}'),
+    Action('@${PYTHON3} "${BIN_SIZE_SCRIPT}" bin ${TARGET}'),
 )
 
 fwdfu = fwenv["FW_DFU"] = fwenv.DFUBuilder("${FIRMWARE_BUILD_CFG}")
@@ -238,12 +251,14 @@ fwdump = fwenv.ObjDump("${FIRMWARE_BUILD_CFG}")
 Alias(fwenv["FIRMWARE_BUILD_CFG"] + "_list", fwdump)
 
 
-fw_artifacts = fwenv["FW_ARTIFACTS"] = [
-    fwhex,
-    fwbin,
-    fwdfu,
-    fwenv["FW_VERSION_JSON"],
-]
+fw_artifacts.extend(
+    [
+        fwhex,
+        fwbin,
+        fwdfu,
+        fwenv["FW_VERSION_JSON"],
+    ]
+)
 
 
 fwcdb = fwenv.CompilationDatabase()
@@ -272,34 +287,5 @@ if should_gen_cdb_and_link_dir(fwenv, BUILD_TARGETS):
 
 Alias(fwenv["FIRMWARE_BUILD_CFG"] + "_all", fw_artifacts)
 
-if fwenv["IS_BASE_FIRMWARE"]:
-    sdk_source = fwenv.SDKPrebuilder(
-        "sdk_origin",
-        # Deps on root SDK headers and generated files
-        (fwenv["SDK_HEADERS"], fwenv["FW_ASSETS_HEADERS"]),
-    )
-    # Extra deps on headers included in deeper levels
-    Depends(sdk_source, fwenv.ProcessSdkDepends("sdk_origin.d"))
-
-    fwenv["SDK_DIR"] = fwenv.Dir("sdk")
-    sdk_tree = fwenv.SDKTree(fwenv["SDK_DIR"], "sdk_origin")
-    fw_artifacts.append(sdk_tree)
-    # AlwaysBuild(sdk_tree)
-    Alias("sdk_tree", sdk_tree)
-
-    sdk_apicheck = fwenv.SDKSymUpdater(fwenv["SDK_DEFINITION"], "sdk_origin")
-    Precious(sdk_apicheck)
-    NoClean(sdk_apicheck)
-    AlwaysBuild(sdk_apicheck)
-    Alias("sdk_check", sdk_apicheck)
-
-    sdk_apisyms = fwenv.SDKSymGenerator(
-        "assets/compiled/symbols.h", fwenv["SDK_DEFINITION"]
-    )
-    Alias("api_syms", sdk_apisyms)
-
-    if fwenv["FORCE"]:
-        fwenv.AlwaysBuild(sdk_source, sdk_tree, sdk_apicheck, sdk_apisyms)
-
 
 Return("fwenv")

+ 3 - 4
firmware/SConscript

@@ -2,11 +2,10 @@ Import("env")
 
 env.Append(
     LINT_SOURCES=["firmware"],
-    # SDK_HEADERS=[env.File("#/firmware/targets/furi_hal_include/furi_hal.h")],
     SDK_HEADERS=[
-        *env.GlobRecursive("*.h", "#/firmware/targets/furi_hal_include", "*_i.h"),
-        *env.GlobRecursive("*.h", "#/firmware/targets/f${TARGET_HW}/furi_hal", "*_i.h"),
-        File("#/firmware/targets/f7/platform_specific/intrinsic_export.h"),
+        *env.GlobRecursive("*.h", "targets/furi_hal_include", "*_i.h"),
+        *env.GlobRecursive("*.h", "targets/f${TARGET_HW}/furi_hal", "*_i.h"),
+        File("targets/f7/platform_specific/intrinsic_export.h"),
     ],
 )
 

+ 123 - 1
firmware/targets/f7/api_symbols.csv

@@ -1,5 +1,5 @@
 entry,status,name,type,params
-Version,+,7.0,,
+Version,+,7.2,,
 Header,+,applications/services/bt/bt_service/bt.h,,
 Header,+,applications/services/cli/cli.h,,
 Header,+,applications/services/cli/cli_vcp.h,,
@@ -7,6 +7,7 @@ Header,+,applications/services/dialogs/dialogs.h,,
 Header,+,applications/services/dolphin/dolphin.h,,
 Header,+,applications/services/gui/elements.h,,
 Header,+,applications/services/gui/gui.h,,
+Header,+,applications/services/gui/icon_i.h,,
 Header,+,applications/services/gui/modules/button_menu.h,,
 Header,+,applications/services/gui/modules/button_panel.h,,
 Header,+,applications/services/gui/modules/byte_input.h,,
@@ -110,12 +111,42 @@ Header,+,lib/STM32CubeWB/Drivers/STM32WBxx_HAL_Driver/Inc/stm32wbxx_ll_wwdg.h,,
 Header,+,lib/flipper_application/flipper_application.h,,
 Header,+,lib/flipper_format/flipper_format.h,,
 Header,+,lib/flipper_format/flipper_format_i.h,,
+Header,+,lib/infrared/encoder_decoder/infrared.h,,
+Header,+,lib/infrared/worker/infrared_transmit.h,,
+Header,+,lib/infrared/worker/infrared_worker.h,,
 Header,+,lib/lfrfid/lfrfid_dict_file.h,,
 Header,+,lib/lfrfid/lfrfid_raw_file.h,,
 Header,+,lib/lfrfid/lfrfid_raw_worker.h,,
 Header,+,lib/lfrfid/lfrfid_worker.h,,
 Header,+,lib/lfrfid/protocols/lfrfid_protocols.h,,
 Header,+,lib/lfrfid/tools/bit_lib.h,,
+Header,+,lib/libusb_stm32/inc/hid_usage_button.h,,
+Header,+,lib/libusb_stm32/inc/hid_usage_consumer.h,,
+Header,+,lib/libusb_stm32/inc/hid_usage_desktop.h,,
+Header,+,lib/libusb_stm32/inc/hid_usage_device.h,,
+Header,+,lib/libusb_stm32/inc/hid_usage_game.h,,
+Header,+,lib/libusb_stm32/inc/hid_usage_keyboard.h,,
+Header,+,lib/libusb_stm32/inc/hid_usage_led.h,,
+Header,+,lib/libusb_stm32/inc/hid_usage_ordinal.h,,
+Header,+,lib/libusb_stm32/inc/hid_usage_power.h,,
+Header,+,lib/libusb_stm32/inc/hid_usage_simulation.h,,
+Header,+,lib/libusb_stm32/inc/hid_usage_sport.h,,
+Header,+,lib/libusb_stm32/inc/hid_usage_telephony.h,,
+Header,+,lib/libusb_stm32/inc/hid_usage_vr.h,,
+Header,+,lib/libusb_stm32/inc/usb.h,,
+Header,+,lib/libusb_stm32/inc/usb_cdc.h,,
+Header,+,lib/libusb_stm32/inc/usb_cdca.h,,
+Header,+,lib/libusb_stm32/inc/usb_cdce.h,,
+Header,+,lib/libusb_stm32/inc/usb_cdci.h,,
+Header,+,lib/libusb_stm32/inc/usb_cdcp.h,,
+Header,+,lib/libusb_stm32/inc/usb_cdcw.h,,
+Header,+,lib/libusb_stm32/inc/usb_dfu.h,,
+Header,+,lib/libusb_stm32/inc/usb_hid.h,,
+Header,+,lib/libusb_stm32/inc/usb_std.h,,
+Header,+,lib/libusb_stm32/inc/usb_tmc.h,,
+Header,+,lib/libusb_stm32/inc/usbd_core.h,,
+Header,+,lib/mbedtls/include/mbedtls/des.h,,
+Header,+,lib/mbedtls/include/mbedtls/sha1.h,,
 Header,+,lib/micro-ecc/uECC.h,,
 Header,+,lib/one_wire/ibutton/ibutton_worker.h,,
 Header,+,lib/one_wire/maxim_crc.h,,
@@ -132,6 +163,7 @@ Header,+,lib/subghz/blocks/math.h,,
 Header,+,lib/subghz/environment.h,,
 Header,+,lib/subghz/protocols/raw.h,,
 Header,+,lib/subghz/receiver.h,,
+Header,+,lib/subghz/registry.h,,
 Header,+,lib/subghz/subghz_setting.h,,
 Header,+,lib/subghz/subghz_tx_rx_worker.h,,
 Header,+,lib/subghz/subghz_worker.h,,
@@ -407,6 +439,7 @@ Function,-,_system_r,int,"_reent*, const char*"
 Function,-,_tempnam_r,char*,"_reent*, const char*, const char*"
 Function,-,_tmpfile_r,FILE*,_reent*
 Function,-,_tmpnam_r,char*,"_reent*, char*"
+Function,-,_tzset_r,void,_reent*
 Function,-,_ungetc_r,int,"_reent*, int, FILE*"
 Function,-,_unsetenv_r,int,"_reent*, const char*"
 Function,-,_vasiprintf_r,int,"_reent*, char**, const char*, __gnuc_va_list"
@@ -454,6 +487,8 @@ Function,+,args_read_hex_bytes,_Bool,"FuriString*, uint8_t*, size_t"
 Function,+,args_read_int_and_trim,_Bool,"FuriString*, int*"
 Function,+,args_read_probably_quoted_string_and_trim,_Bool,"FuriString*, FuriString*"
 Function,+,args_read_string_and_trim,_Bool,"FuriString*, FuriString*"
+Function,-,asctime,char*,const tm*
+Function,-,asctime_r,char*,"const tm*, char*"
 Function,-,asin,double,double
 Function,-,asinf,float,float
 Function,-,asinh,double,double
@@ -621,6 +656,7 @@ Function,+,cli_read_timeout,size_t,"Cli*, uint8_t*, size_t, uint32_t"
 Function,+,cli_session_close,void,Cli*
 Function,+,cli_session_open,void,"Cli*, void*"
 Function,+,cli_write,void,"Cli*, const uint8_t*, size_t"
+Function,-,clock,clock_t,
 Function,-,copysign,double,"double, double"
 Function,-,copysignf,float,"float, float"
 Function,-,copysignl,long double,"long double, long double"
@@ -633,6 +669,8 @@ Function,-,cosl,long double,long double
 Function,+,crc32_calc_buffer,uint32_t,"uint32_t, const void*, size_t"
 Function,+,crc32_calc_file,uint32_t,"File*, const FileCrcProgressCb, void*"
 Function,-,ctermid,char*,char*
+Function,-,ctime,char*,const time_t*
+Function,-,ctime_r,char*,"const time_t*, char*"
 Function,-,cuserid,char*,char*
 Function,+,delete_mutex,_Bool,ValueMutex*
 Function,+,dialog_ex_alloc,DialogEx*,
@@ -659,6 +697,7 @@ Function,+,dialog_message_set_icon,void,"DialogMessage*, const Icon*, uint8_t, u
 Function,+,dialog_message_set_text,void,"DialogMessage*, const char*, uint8_t, uint8_t, Align, Align"
 Function,+,dialog_message_show,DialogMessageButton,"DialogsApp*, const DialogMessage*"
 Function,+,dialog_message_show_storage_error,void,"DialogsApp*, const char*"
+Function,-,difftime,double,"time_t, time_t"
 Function,-,digital_signal_alloc,DigitalSignal*,uint32_t
 Function,-,digital_signal_append,_Bool,"DigitalSignal*, DigitalSignal*"
 Function,-,digital_signal_free,void,DigitalSignal*
@@ -1475,6 +1514,8 @@ Function,-,getenv,char*,const char*
 Function,-,gets,char*,char*
 Function,-,getsubopt,int,"char**, char**, char**"
 Function,-,getw,int,FILE*
+Function,-,gmtime,tm*,const time_t*
+Function,-,gmtime_r,tm*,"const time_t*, tm*"
 Function,+,gui_add_framebuffer_callback,void,"Gui*, GuiCanvasCommitCallback, void*"
 Function,+,gui_add_view_port,void,"Gui*, ViewPort*, GuiLayer"
 Function,+,gui_get_framebuffer_size,size_t,Gui*
@@ -1535,6 +1576,42 @@ Function,-,ilogbl,int,long double
 Function,-,index,char*,"const char*, int"
 Function,-,infinity,double,
 Function,-,infinityf,float,
+Function,+,infrared_alloc_decoder,InfraredDecoderHandler*,
+Function,+,infrared_alloc_encoder,InfraredEncoderHandler*,
+Function,+,infrared_check_decoder_ready,const InfraredMessage*,InfraredDecoderHandler*
+Function,+,infrared_decode,const InfraredMessage*,"InfraredDecoderHandler*, _Bool, uint32_t"
+Function,+,infrared_encode,InfraredStatus,"InfraredEncoderHandler*, uint32_t*, _Bool*"
+Function,+,infrared_free_decoder,void,InfraredDecoderHandler*
+Function,+,infrared_free_encoder,void,InfraredEncoderHandler*
+Function,+,infrared_get_protocol_address_length,uint8_t,InfraredProtocol
+Function,+,infrared_get_protocol_by_name,InfraredProtocol,const char*
+Function,+,infrared_get_protocol_command_length,uint8_t,InfraredProtocol
+Function,+,infrared_get_protocol_duty_cycle,float,InfraredProtocol
+Function,+,infrared_get_protocol_frequency,uint32_t,InfraredProtocol
+Function,+,infrared_get_protocol_name,const char*,InfraredProtocol
+Function,+,infrared_is_protocol_valid,_Bool,InfraredProtocol
+Function,+,infrared_reset_decoder,void,InfraredDecoderHandler*
+Function,+,infrared_reset_encoder,void,"InfraredEncoderHandler*, const InfraredMessage*"
+Function,+,infrared_send,void,"const InfraredMessage*, int"
+Function,+,infrared_send_raw,void,"const uint32_t[], uint32_t, _Bool"
+Function,+,infrared_send_raw_ext,void,"const uint32_t[], uint32_t, _Bool, uint32_t, float"
+Function,+,infrared_worker_alloc,InfraredWorker*,
+Function,+,infrared_worker_free,void,InfraredWorker*
+Function,+,infrared_worker_get_decoded_signal,const InfraredMessage*,const InfraredWorkerSignal*
+Function,+,infrared_worker_get_raw_signal,void,"const InfraredWorkerSignal*, const uint32_t**, size_t*"
+Function,+,infrared_worker_rx_enable_blink_on_receiving,void,"InfraredWorker*, _Bool"
+Function,+,infrared_worker_rx_enable_signal_decoding,void,"InfraredWorker*, _Bool"
+Function,+,infrared_worker_rx_set_received_signal_callback,void,"InfraredWorker*, InfraredWorkerReceivedSignalCallback, void*"
+Function,+,infrared_worker_rx_start,void,InfraredWorker*
+Function,+,infrared_worker_rx_stop,void,InfraredWorker*
+Function,+,infrared_worker_set_decoded_signal,void,"InfraredWorker*, const InfraredMessage*"
+Function,+,infrared_worker_set_raw_signal,void,"InfraredWorker*, const uint32_t*, size_t"
+Function,+,infrared_worker_signal_is_decoded,_Bool,const InfraredWorkerSignal*
+Function,+,infrared_worker_tx_get_signal_steady_callback,InfraredWorkerGetSignalResponse,"void*, InfraredWorker*"
+Function,+,infrared_worker_tx_set_get_signal_callback,void,"InfraredWorker*, InfraredWorkerGetSignalCallback, void*"
+Function,+,infrared_worker_tx_set_signal_sent_callback,void,"InfraredWorker*, InfraredWorkerMessageSentCallback, void*"
+Function,+,infrared_worker_tx_start,void,InfraredWorker*
+Function,+,infrared_worker_tx_stop,void,InfraredWorker*
 Function,+,init_mutex,_Bool,"ValueMutex*, void*, size_t"
 Function,-,initstate,char*,"unsigned, char*, size_t"
 Function,+,input_get_key_name,const char*,InputKey
@@ -1634,6 +1711,8 @@ Function,+,loader_update_menu,void,
 Function,+,loading_alloc,Loading*,
 Function,+,loading_free,void,Loading*
 Function,+,loading_get_view,View*,Loading*
+Function,-,localtime,tm*,const time_t*
+Function,-,localtime_r,tm*,"const time_t*, tm*"
 Function,-,log,double,double
 Function,-,log10,double,double
 Function,-,log10f,float,float
@@ -1662,6 +1741,36 @@ Function,+,manchester_encoder_advance,_Bool,"ManchesterEncoderState*, const _Boo
 Function,+,manchester_encoder_finish,ManchesterEncoderResult,ManchesterEncoderState*
 Function,+,manchester_encoder_reset,void,ManchesterEncoderState*
 Function,+,maxim_crc8,uint8_t,"const uint8_t*, const uint8_t, const uint8_t"
+Function,-,mbedtls_des3_crypt_cbc,int,"mbedtls_des3_context*, int, size_t, unsigned char[8], const unsigned char*, unsigned char*"
+Function,-,mbedtls_des3_crypt_ecb,int,"mbedtls_des3_context*, const unsigned char[8], unsigned char[8]"
+Function,-,mbedtls_des3_free,void,mbedtls_des3_context*
+Function,-,mbedtls_des3_init,void,mbedtls_des3_context*
+Function,-,mbedtls_des3_set2key_dec,int,"mbedtls_des3_context*, const unsigned char[8 * 2]"
+Function,-,mbedtls_des3_set2key_enc,int,"mbedtls_des3_context*, const unsigned char[8 * 2]"
+Function,-,mbedtls_des3_set3key_dec,int,"mbedtls_des3_context*, const unsigned char[8 * 3]"
+Function,-,mbedtls_des3_set3key_enc,int,"mbedtls_des3_context*, const unsigned char[8 * 3]"
+Function,-,mbedtls_des_crypt_cbc,int,"mbedtls_des_context*, int, size_t, unsigned char[8], const unsigned char*, unsigned char*"
+Function,-,mbedtls_des_crypt_ecb,int,"mbedtls_des_context*, const unsigned char[8], unsigned char[8]"
+Function,-,mbedtls_des_free,void,mbedtls_des_context*
+Function,-,mbedtls_des_init,void,mbedtls_des_context*
+Function,-,mbedtls_des_key_check_key_parity,int,const unsigned char[8]
+Function,-,mbedtls_des_key_check_weak,int,const unsigned char[8]
+Function,-,mbedtls_des_key_set_parity,void,unsigned char[8]
+Function,-,mbedtls_des_self_test,int,int
+Function,-,mbedtls_des_setkey,void,"uint32_t[32], const unsigned char[8]"
+Function,-,mbedtls_des_setkey_dec,int,"mbedtls_des_context*, const unsigned char[8]"
+Function,-,mbedtls_des_setkey_enc,int,"mbedtls_des_context*, const unsigned char[8]"
+Function,-,mbedtls_internal_sha1_process,int,"mbedtls_sha1_context*, const unsigned char[64]"
+Function,-,mbedtls_platform_gmtime_r,tm*,"const mbedtls_time_t*, tm*"
+Function,-,mbedtls_platform_zeroize,void,"void*, size_t"
+Function,-,mbedtls_sha1,int,"const unsigned char*, size_t, unsigned char[20]"
+Function,-,mbedtls_sha1_clone,void,"mbedtls_sha1_context*, const mbedtls_sha1_context*"
+Function,-,mbedtls_sha1_finish,int,"mbedtls_sha1_context*, unsigned char[20]"
+Function,-,mbedtls_sha1_free,void,mbedtls_sha1_context*
+Function,-,mbedtls_sha1_init,void,mbedtls_sha1_context*
+Function,-,mbedtls_sha1_self_test,int,int
+Function,-,mbedtls_sha1_starts,int,mbedtls_sha1_context*
+Function,-,mbedtls_sha1_update,int,"mbedtls_sha1_context*, const unsigned char*, size_t"
 Function,-,mblen,int,"const char*, size_t"
 Function,-,mbstowcs,size_t,"wchar_t*, const char*, size_t"
 Function,-,mbtowc,int,"wchar_t*, const char*, size_t"
@@ -1702,6 +1811,7 @@ Function,-,mkostemps,int,"char*, int, int"
 Function,-,mkstemp,int,char*
 Function,-,mkstemps,int,"char*, int"
 Function,-,mktemp,char*,char*
+Function,-,mktime,time_t,tm*
 Function,-,modf,double,"double, double*"
 Function,-,modff,float,"float, float*"
 Function,-,modfl,long double,"long double, long double*"
@@ -2210,6 +2320,8 @@ Function,+,stream_write_vaformat,size_t,"Stream*, const char*, va_list"
 Function,-,strerror,char*,int
 Function,-,strerror_l,char*,"int, locale_t"
 Function,-,strerror_r,char*,"int, char*, size_t"
+Function,-,strftime,size_t,"char*, size_t, const char*, const tm*"
+Function,-,strftime_l,size_t,"char*, size_t, const char*, const tm*, locale_t"
 Function,+,string_stream_alloc,Stream*,
 Function,-,strlcat,size_t,"char*, const char*, size_t"
 Function,+,strlcpy,size_t,"char*, const char*, size_t"
@@ -2224,6 +2336,8 @@ Function,-,strndup,char*,"const char*, size_t"
 Function,-,strnlen,size_t,"const char*, size_t"
 Function,-,strnstr,char*,"const char*, const char*, size_t"
 Function,-,strpbrk,char*,"const char*, const char*"
+Function,-,strptime,char*,"const char*, const char*, tm*"
+Function,-,strptime_l,char*,"const char*, const char*, tm*, locale_t"
 Function,+,strrchr,char*,"const char*, int"
 Function,-,strsep,char*,"char**, const char*"
 Function,-,strsignal,char*,int
@@ -2313,6 +2427,9 @@ Function,+,subghz_protocol_raw_get_sample_write,size_t,SubGhzProtocolDecoderRAW*
 Function,+,subghz_protocol_raw_save_to_file_init,_Bool,"SubGhzProtocolDecoderRAW*, const char*, SubGhzRadioPreset*"
 Function,+,subghz_protocol_raw_save_to_file_pause,void,"SubGhzProtocolDecoderRAW*, _Bool"
 Function,+,subghz_protocol_raw_save_to_file_stop,void,SubGhzProtocolDecoderRAW*
+Function,+,subghz_protocol_registry_count,size_t,const SubGhzProtocolRegistry*
+Function,+,subghz_protocol_registry_get_by_index,const SubGhzProtocol*,"const SubGhzProtocolRegistry*, size_t"
+Function,+,subghz_protocol_registry_get_by_name,const SubGhzProtocol*,"const SubGhzProtocolRegistry*, const char*"
 Function,+,subghz_receiver_alloc_init,SubGhzReceiver*,SubGhzEnvironment*
 Function,+,subghz_receiver_decode,void,"SubGhzReceiver*, _Bool, uint32_t"
 Function,+,subghz_receiver_free,void,SubGhzReceiver*
@@ -2411,6 +2528,7 @@ Function,+,text_input_set_validator,void,"TextInput*, TextInputValidatorCallback
 Function,-,tgamma,double,double
 Function,-,tgammaf,float,float
 Function,-,tgammal,long double,long double
+Function,-,time,time_t,time_t*
 Function,+,timerCalculateTimer,uint32_t,uint16_t
 Function,-,timerDelay,void,uint16_t
 Function,+,timerIsExpired,_Bool,uint32_t
@@ -2429,6 +2547,7 @@ Function,-,toupper_l,int,"int, locale_t"
 Function,-,trunc,double,double
 Function,-,truncf,float,float
 Function,-,truncl,long double,long double
+Function,-,tzset,void,
 Function,-,uECC_compress,void,"const uint8_t*, uint8_t*, uECC_Curve"
 Function,+,uECC_compute_public_key,int,"const uint8_t*, uint8_t*, uECC_Curve"
 Function,-,uECC_curve_private_key_size,int,uECC_Curve
@@ -2666,10 +2785,13 @@ Variable,-,MSIRangeTable,const uint32_t[16],
 Variable,-,SmpsPrescalerTable,const uint32_t[4][6],
 Variable,+,SystemCoreClock,uint32_t,
 Variable,+,_ctype_,const char[],
+Variable,-,_daylight,int,
 Variable,+,_global_impure_ptr,_reent*,
 Variable,+,_impure_ptr,_reent*,
 Variable,-,_sys_errlist,const char*[],
 Variable,-,_sys_nerr,int,
+Variable,-,_timezone,long,
+Variable,-,_tzname,char*[2],
 Variable,+,cli_vcp,CliSession,
 Variable,+,furi_hal_i2c_bus_external,FuriHalI2cBus,
 Variable,+,furi_hal_i2c_bus_power,FuriHalI2cBus,

+ 6 - 6
lib/SConscript

@@ -17,12 +17,12 @@ env.Append(
         "lib/print",
     ],
     SDK_HEADERS=[
-        File("#/lib/one_wire/one_wire_host_timing.h"),
-        File("#/lib/one_wire/one_wire_host.h"),
-        File("#/lib/one_wire/one_wire_slave.h"),
-        File("#/lib/one_wire/one_wire_device.h"),
-        File("#/lib/one_wire/ibutton/ibutton_worker.h"),
-        File("#/lib/one_wire/maxim_crc.h"),
+        File("one_wire/one_wire_host_timing.h"),
+        File("one_wire/one_wire_host.h"),
+        File("one_wire/one_wire_slave.h"),
+        File("one_wire/one_wire_device.h"),
+        File("one_wire/ibutton/ibutton_worker.h"),
+        File("one_wire/maxim_crc.h"),
     ],
 )
 

+ 1 - 1
lib/STM32CubeWB.scons

@@ -15,7 +15,7 @@ env.Append(
     ],
     SDK_HEADERS=env.GlobRecursive(
         "*_ll_*.h",
-        "#/lib/STM32CubeWB/Drivers/STM32WBxx_HAL_Driver/Inc/",
+        Dir("STM32CubeWB/Drivers/STM32WBxx_HAL_Driver/Inc/"),
         exclude="*usb.h",
     ),
 )

+ 1 - 1
lib/flipper_application/SConscript

@@ -5,7 +5,7 @@ env.Append(
         "#/lib/flipper_application",
     ],
     SDK_HEADERS=[
-        File("#/lib/flipper_application/flipper_application.h"),
+        File("flipper_application.h"),
     ],
 )
 

+ 2 - 2
lib/flipper_format/SConscript

@@ -5,8 +5,8 @@ env.Append(
         "#/lib/flipper_format",
     ],
     SDK_HEADERS=[
-        File("#/lib/flipper_format/flipper_format.h"),
-        File("#/lib/flipper_format/flipper_format_i.h"),
+        File("flipper_format.h"),
+        File("flipper_format_i.h"),
     ],
 )
 

+ 5 - 0
lib/infrared/SConscript

@@ -5,6 +5,11 @@ env.Append(
         "#/lib/infrared/encoder_decoder",
         "#/lib/infrared/worker",
     ],
+    SDK_HEADERS=[
+        File("encoder_decoder/infrared.h"),
+        File("worker/infrared_worker.h"),
+        File("worker/infrared_transmit.h"),
+    ],
 )
 
 

+ 6 - 6
lib/lfrfid/SConscript

@@ -8,12 +8,12 @@ env.Append(
         "#/lib/lfrfid",
     ],
     SDK_HEADERS=[
-        File("#/lib/lfrfid/lfrfid_worker.h"),
-        File("#/lib/lfrfid/lfrfid_raw_worker.h"),
-        File("#/lib/lfrfid/lfrfid_raw_file.h"),
-        File("#/lib/lfrfid/lfrfid_dict_file.h"),
-        File("#/lib/lfrfid/tools/bit_lib.h"),
-        File("#/lib/lfrfid/protocols/lfrfid_protocols.h"),
+        File("lfrfid_worker.h"),
+        File("lfrfid_raw_worker.h"),
+        File("lfrfid_raw_file.h"),
+        File("lfrfid_dict_file.h"),
+        File("tools/bit_lib.h"),
+        File("protocols/lfrfid_protocols.h"),
     ],
 )
 

+ 4 - 0
lib/libusb_stm32.scons

@@ -7,6 +7,10 @@ env.Append(
     CPPDEFINES=[
         ("USB_PMASIZE", "0x400"),
     ],
+    SDK_HEADERS=env.GlobRecursive(
+        "*.h",
+        Dir("libusb_stm32/inc"),
+    ),
 )
 
 

+ 4 - 0
lib/mbedtls.scons

@@ -5,6 +5,10 @@ env.Append(
         "#/lib/mbedtls",
         "#/lib/mbedtls/include",
     ],
+    SDK_HEADERS=[
+        File("mbedtls/include/mbedtls/des.h"),
+        File("mbedtls/include/mbedtls/sha1.h"),
+    ],
 )
 
 

+ 1 - 1
lib/misc.scons

@@ -13,7 +13,7 @@ env.Append(
         "PB_ENABLE_MALLOC",
     ],
     SDK_HEADERS=[
-        File("#/lib/micro-ecc/uECC.h"),
+        File("micro-ecc/uECC.h"),
     ],
 )
 

+ 1 - 1
lib/print/SConscript

@@ -98,7 +98,7 @@ for wrapped_fn in wrapped_fn_list:
 
 env.Append(
     SDK_HEADERS=[
-        File("#/lib/print/wrappers.h"),
+        File("wrappers.h"),
     ],
 )
 

+ 13 - 12
lib/subghz/SConscript

@@ -5,18 +5,19 @@ env.Append(
         "#/lib/subghz",
     ],
     SDK_HEADERS=[
-        File("#/lib/subghz/environment.h"),
-        File("#/lib/subghz/receiver.h"),
-        File("#/lib/subghz/subghz_worker.h"),
-        File("#/lib/subghz/subghz_tx_rx_worker.h"),
-        File("#/lib/subghz/transmitter.h"),
-        File("#/lib/subghz/protocols/raw.h"),
-        File("#/lib/subghz/blocks/const.h"),
-        File("#/lib/subghz/blocks/decoder.h"),
-        File("#/lib/subghz/blocks/encoder.h"),
-        File("#/lib/subghz/blocks/generic.h"),
-        File("#/lib/subghz/blocks/math.h"),
-        File("#/lib/subghz/subghz_setting.h"),
+        File("environment.h"),
+        File("receiver.h"),
+        File("registry.h"),
+        File("subghz_worker.h"),
+        File("subghz_tx_rx_worker.h"),
+        File("transmitter.h"),
+        File("protocols/raw.h"),
+        File("blocks/const.h"),
+        File("blocks/decoder.h"),
+        File("blocks/encoder.h"),
+        File("blocks/generic.h"),
+        File("blocks/math.h"),
+        File("subghz_setting.h"),
     ],
 )
 

+ 8 - 0
lib/subghz/registry.h

@@ -2,6 +2,10 @@
 
 #include "types.h"
 
+#ifdef __cplusplus
+extern "C" {
+#endif
+
 typedef struct SubGhzEnvironment SubGhzEnvironment;
 
 typedef struct SubGhzProtocolRegistry SubGhzProtocolRegistry;
@@ -37,3 +41,7 @@ const SubGhzProtocol* subghz_protocol_registry_get_by_index(
  * @return Number of protocols
  */
 size_t subghz_protocol_registry_count(const SubGhzProtocolRegistry* protocol_registry);
+
+#ifdef __cplusplus
+}
+#endif

+ 17 - 17
lib/toolbox/SConscript

@@ -8,23 +8,23 @@ env.Append(
         "#/lib/toolbox",
     ],
     SDK_HEADERS=[
-        File("#/lib/toolbox/manchester_decoder.h"),
-        File("#/lib/toolbox/manchester_encoder.h"),
-        File("#/lib/toolbox/path.h"),
-        File("#/lib/toolbox/random_name.h"),
-        File("#/lib/toolbox/hmac_sha256.h"),
-        File("#/lib/toolbox/crc32_calc.h"),
-        File("#/lib/toolbox/dir_walk.h"),
-        File("#/lib/toolbox/md5.h"),
-        File("#/lib/toolbox/args.h"),
-        File("#/lib/toolbox/saved_struct.h"),
-        File("#/lib/toolbox/version.h"),
-        File("#/lib/toolbox/tar/tar_archive.h"),
-        File("#/lib/toolbox/stream/stream.h"),
-        File("#/lib/toolbox/stream/file_stream.h"),
-        File("#/lib/toolbox/stream/string_stream.h"),
-        File("#/lib/toolbox/stream/buffered_file_stream.h"),
-        File("#/lib/toolbox/protocols/protocol_dict.h"),
+        File("manchester_decoder.h"),
+        File("manchester_encoder.h"),
+        File("path.h"),
+        File("random_name.h"),
+        File("hmac_sha256.h"),
+        File("crc32_calc.h"),
+        File("dir_walk.h"),
+        File("md5.h"),
+        File("args.h"),
+        File("saved_struct.h"),
+        File("version.h"),
+        File("tar/tar_archive.h"),
+        File("stream/stream.h"),
+        File("stream/file_stream.h"),
+        File("stream/string_stream.h"),
+        File("stream/buffered_file_stream.h"),
+        File("protocols/protocol_dict.h"),
     ],
 )
 

+ 13 - 2
scripts/fbt/util.py

@@ -1,11 +1,11 @@
 import SCons
 from SCons.Subst import quote_spaces
 from SCons.Errors import StopError
+from SCons.Node.FS import _my_normcase
 
 import re
 import os
-import random
-import string
+
 
 WINPATHSEP_RE = re.compile(r"\\([^\"'\\]|$)")
 
@@ -41,3 +41,14 @@ def link_dir(target_path, source_path, is_windows):
 
 def single_quote(arg_list):
     return " ".join(f"'{arg}'" if " " in arg else str(arg) for arg in arg_list)
+
+
+def extract_abs_dir_path(node):
+    if isinstance(node, SCons.Node.FS.EntryProxy):
+        node = node.get()
+
+    for repo_dir in node.get_all_rdirs():
+        if os.path.exists(repo_dir.abspath):
+            return repo_dir.abspath
+
+    raise StopError(f"Can't find absolute path for {node.name}")

+ 15 - 0
scripts/fbt_tools/crosscc.py

@@ -37,6 +37,21 @@ def _get_tool_version(env, tool):
 
 
 def generate(env, **kw):
+    if not env.get("VERBOSE", False):
+        env.SetDefault(
+            CCCOMSTR="\tCC\t${SOURCE}",
+            CXXCOMSTR="\tCPP\t${SOURCE}",
+            ASCOMSTR="\tASM\t${SOURCE}",
+            ARCOMSTR="\tAR\t${TARGET}",
+            RANLIBCOMSTR="\tRANLIB\t${TARGET}",
+            LINKCOMSTR="\tLINK\t${TARGET}",
+            INSTALLSTR="\tINSTALL\t${TARGET}",
+            APPSCOMSTR="\tAPPS\t${TARGET}",
+            VERSIONCOMSTR="\tVERSION\t${TARGET}",
+            STRIPCOMSTR="\tSTRIP\t${TARGET}",
+            OBJDUMPCOMSTR="\tOBJDUMP\t${TARGET}",
+        )
+
     for orig_tool in (asm, gcc, gxx, ar, gnulink, strip, gdb, objdump):
         orig_tool.generate(env)
     env.SetDefault(

+ 20 - 18
scripts/fbt_tools/fbt_apps.py

@@ -2,7 +2,7 @@ from SCons.Builder import Builder
 from SCons.Action import Action
 from SCons.Warnings import warn, WarningOnByDefault
 import SCons
-import os.path
+from ansi.color import fg
 
 from fbt.appmanifest import (
     FlipperAppType,
@@ -16,21 +16,20 @@ from fbt.appmanifest import (
 #  AppBuildset env["APPBUILD"] - contains subset of apps, filtered for current config
 
 
-def LoadApplicationManifests(env):
-    appmgr = env["APPMGR"] = AppManager()
-    for app_dir, _ in env["APPDIRS"]:
-        app_dir_node = env.Dir("#").Dir(app_dir)
+def LoadAppManifest(env, entry):
+    try:
+        APP_MANIFEST_NAME = "application.fam"
+        manifest_glob = entry.glob(APP_MANIFEST_NAME)
+        if len(manifest_glob) == 0:
+            raise FlipperManifestException(
+                f"Folder {entry}: manifest {APP_MANIFEST_NAME} is missing"
+            )
 
-        for entry in app_dir_node.glob("*", ondisk=True, source=True):
-            if isinstance(entry, SCons.Node.FS.Dir) and not str(entry).startswith("."):
-                try:
-                    app_manifest_file_path = os.path.join(
-                        entry.abspath, "application.fam"
-                    )
-                    appmgr.load_manifest(app_manifest_file_path, entry)
-                    env.Append(PY_LINT_SOURCES=[app_manifest_file_path])
-                except FlipperManifestException as e:
-                    warn(WarningOnByDefault, str(e))
+        app_manifest_file_path = manifest_glob[0].rfile().abspath
+        env["APPMGR"].load_manifest(app_manifest_file_path, entry)
+        env.Append(PY_LINT_SOURCES=[app_manifest_file_path])
+    except FlipperManifestException as e:
+        warn(WarningOnByDefault, str(e))
 
 
 def PrepareApplicationsBuild(env):
@@ -46,12 +45,12 @@ def PrepareApplicationsBuild(env):
 
 def DumpApplicationConfig(target, source, env):
     print(f"Loaded {len(env['APPMGR'].known_apps)} app definitions.")
-    print("Firmware modules configuration:")
+    print(fg.boldgreen("Firmware modules configuration:"))
     for apptype in FlipperAppType:
         app_sublist = env["APPBUILD"].get_apps_of_type(apptype)
         if app_sublist:
             print(
-                f"{apptype.value}:\n\t",
+                fg.green(f"{apptype.value}:\n\t"),
                 ", ".join(app.appid for app in app_sublist),
             )
 
@@ -65,8 +64,11 @@ def build_apps_c(target, source, env):
 
 
 def generate(env):
-    env.AddMethod(LoadApplicationManifests)
+    env.AddMethod(LoadAppManifest)
     env.AddMethod(PrepareApplicationsBuild)
+    env.SetDefault(
+        APPMGR=AppManager(),
+    )
 
     env.Append(
         BUILDERS={

+ 8 - 12
scripts/fbt_tools/fbt_assets.py

@@ -1,11 +1,10 @@
-import SCons
-
 from SCons.Builder import Builder
 from SCons.Action import Action
-from SCons.Node.FS import File
+from SCons.Errors import SConsEnvironmentError
 
 import os
 import subprocess
+from ansi.color import fg
 
 
 def icons_emitter(target, source, env):
@@ -13,7 +12,6 @@ def icons_emitter(target, source, env):
         target[0].File(env.subst("${ICON_FILE_NAME}.c")),
         target[0].File(env.subst("${ICON_FILE_NAME}.h")),
     ]
-    source = env.GlobRecursive("*.*", env["ICON_SRC_DIR"])
     return target, source
 
 
@@ -86,7 +84,7 @@ def proto_ver_generator(target, source, env):
         )
     except (subprocess.CalledProcessError, EnvironmentError) as e:
         # Not great, not terrible
-        print("Git: fetch failed")
+        print(fg.boldred("Git: fetch failed"))
 
     try:
         git_describe = _invoke_git(
@@ -94,10 +92,8 @@ def proto_ver_generator(target, source, env):
             source_dir=src_dir,
         )
     except (subprocess.CalledProcessError, EnvironmentError) as e:
-        print("Git: describe failed")
-        Exit("git error")
+        raise SConsEnvironmentError("Git: describe failed")
 
-    # print("describe=", git_describe)
     git_major, git_minor = git_describe.split(".")
     version_file_data = (
         "#pragma once",
@@ -116,7 +112,7 @@ def CompileIcons(env, target_dir, source_dir, *, icon_bundle_name="assets_icons"
 
     icons = env.IconBuilder(
         target_dir,
-        ICON_SRC_DIR=source_dir,
+        source_dir,
         ICON_FILE_NAME=icon_bundle_name,
     )
     env.Depends(icons, icons_src)
@@ -125,8 +121,8 @@ def CompileIcons(env, target_dir, source_dir, *, icon_bundle_name="assets_icons"
 
 def generate(env):
     env.SetDefault(
-        ASSETS_COMPILER="${ROOT_DIR.abspath}/scripts/assets.py",
-        NANOPB_COMPILER="${ROOT_DIR.abspath}/lib/nanopb/generator/nanopb_generator.py",
+        ASSETS_COMPILER="${FBT_SCRIPT_DIR}/assets.py",
+        NANOPB_COMPILER="${ROOT_DIR}/lib/nanopb/generator/nanopb_generator.py",
     )
     env.AddMethod(CompileIcons)
 
@@ -143,7 +139,7 @@ def generate(env):
         BUILDERS={
             "IconBuilder": Builder(
                 action=Action(
-                    '${PYTHON3} "${ASSETS_COMPILER}" icons ${ICON_SRC_DIR} ${TARGET.dir} --filename ${ICON_FILE_NAME}',
+                    '${PYTHON3} "${ASSETS_COMPILER}" icons ${ABSPATHGETTERFUNC(SOURCE)} ${TARGET.dir} --filename ${ICON_FILE_NAME}',
                     "${ICONSCOMSTR}",
                 ),
                 emitter=icons_emitter,

+ 6 - 3
scripts/fbt_tools/fbt_dist.py

@@ -103,7 +103,7 @@ def DistCommand(env, name, source, **kw):
     command = env.Command(
         target,
         source,
-        '@${PYTHON3} "${ROOT_DIR.abspath}/scripts/sconsdist.py" copy -p ${DIST_PROJECTS} -s "${DIST_SUFFIX}" ${DIST_EXTRA}',
+        '@${PYTHON3} "${DIST_SCRIPT}" copy -p ${DIST_PROJECTS} -s "${DIST_SUFFIX}" ${DIST_EXTRA}',
         **kw,
     )
     env.Pseudo(target)
@@ -121,6 +121,9 @@ def generate(env):
 
     env.SetDefault(
         COPRO_MCU_FAMILY="STM32WB5x",
+        SELFUPDATE_SCRIPT="${FBT_SCRIPT_DIR}/selfupdate.py",
+        DIST_SCRIPT="${FBT_SCRIPT_DIR}/sconsdist.py",
+        COPRO_ASSETS_SCRIPT="${FBT_SCRIPT_DIR}/assets.py",
     )
 
     env.Append(
@@ -128,7 +131,7 @@ def generate(env):
             "UsbInstall": Builder(
                 action=[
                     Action(
-                        '${PYTHON3} "${ROOT_DIR.abspath}/scripts/selfupdate.py" dist/${DIST_DIR}/f${TARGET_HW}-update-${DIST_SUFFIX}/update.fuf'
+                        '${PYTHON3} "${SELFUPDATE_SCRIPT}" dist/${DIST_DIR}/f${TARGET_HW}-update-${DIST_SUFFIX}/update.fuf'
                     ),
                     Touch("${TARGET}"),
                 ]
@@ -136,7 +139,7 @@ def generate(env):
             "CoproBuilder": Builder(
                 action=Action(
                     [
-                        '${PYTHON3} "${ROOT_DIR.abspath}/scripts/assets.py" '
+                        '${PYTHON3} "${COPRO_ASSETS_SCRIPT}" '
                         "copro ${COPRO_CUBE_DIR} "
                         "${TARGET} ${COPRO_MCU_FAMILY} "
                         "--cube_ver=${COPRO_CUBE_VERSION} "

+ 17 - 7
scripts/fbt_tools/fbt_extapps.py

@@ -1,15 +1,18 @@
-import shutil
 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.appmanifest import FlipperApplication, FlipperManifestException
 from fbt.sdk.cache import SdkCache
+from fbt.util import extract_abs_dir_path
+
+import os
+import pathlib
 import itertools
+import shutil
+
 from ansi.color import fg
 
 
@@ -62,7 +65,7 @@ def BuildAppElf(env, app):
         lib_src_root_path = os.path.join(app_work_dir, "lib", lib_def.name)
         app_env.AppendUnique(
             CPPPATH=list(
-                app_env.Dir(lib_src_root_path).Dir(incpath).srcnode()
+                app_env.Dir(lib_src_root_path).Dir(incpath).srcnode().rfile().abspath
                 for incpath in lib_def.fap_include_paths
             ),
         )
@@ -82,7 +85,12 @@ def BuildAppElf(env, app):
                 *lib_def.cflags,
             ],
             CPPDEFINES=lib_def.cdefines,
-            CPPPATH=list(map(app._appdir.Dir, lib_def.cincludes)),
+            CPPPATH=list(
+                map(
+                    lambda cpath: extract_abs_dir_path(app._appdir.Dir(cpath)),
+                    lib_def.cincludes,
+                )
+            ),
         )
 
         lib = private_lib_env.StaticLibrary(
@@ -157,7 +165,6 @@ def prepare_app_metadata(target, source, env):
     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,
@@ -236,7 +243,10 @@ def fap_dist_action(target, source, env):
 
 
 def generate(env, **kw):
-    env.SetDefault(EXT_APPS_WORK_DIR=kw.get("EXT_APPS_WORK_DIR"))
+    env.SetDefault(
+        EXT_APPS_WORK_DIR=kw.get("EXT_APPS_WORK_DIR"),
+        APP_RUN_SCRIPT="${FBT_SCRIPT_DIR}/runfap.py",
+    )
 
     if not env["VERBOSE"]:
         env.SetDefault(

+ 29 - 17
scripts/fbt_tools/fbt_sdk.py

@@ -46,7 +46,9 @@ def prebuild_sdk_emitter(target, source, env):
 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"]))
+        sdk_c.write(
+            "\n".join(f"#include <{h.srcnode().path}>" for h in env["SDK_HEADERS"])
+        )
 
 
 class SdkMeta:
@@ -62,18 +64,25 @@ class SdkMeta:
             "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}"),
+            "linker_libs": self.env.subst("${LIBS}"),
+            "app_ep_subst": self.env.subst("${APP_ENTRY}"),
+            "sdk_path_subst": self.env.subst("${SDK_DIR_SUBST}"),
+            "hardware": self.env.subst("${TARGET_HW}"),
         }
         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"))
+        expanded_vars = self.env.subst(
+            vars,
+            target=Entry("dummy"),
+        )
         return expanded_vars.replace("\\", "/")
 
 
 class SdkTreeBuilder:
     SDK_DIR_SUBST = "SDK_ROOT_DIR"
+    SDK_APP_EP_SUBST = "SDK_APP_EP_SUBST"
 
     def __init__(self, env, target, source) -> None:
         self.env = env
@@ -87,6 +96,11 @@ class SdkTreeBuilder:
         self.sdk_root_dir = target[0].Dir(".")
         self.sdk_deploy_dir = self.sdk_root_dir.Dir(self.target_sdk_dir_name)
 
+        self.sdk_env = self.env.Clone(
+            APP_ENTRY=self.SDK_APP_EP_SUBST,
+            SDK_DIR_SUBST=self.SDK_DIR_SUBST,
+        )
+
     def _parse_sdk_depends(self):
         deps_file = self.source[0]
         with open(deps_file.path, "rt") as deps_f:
@@ -95,38 +109,36 @@ class SdkTreeBuilder:
             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_depends.append(self.sdk_env.subst("${LINKER_SCRIPT_PATH}"))
+            self.header_depends.append(self.sdk_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]
+        filtered_paths = ["."]
         full_fw_paths = list(
             map(
                 os.path.normpath,
-                (self.env.Dir(inc_dir).relpath for inc_dir in self.env["CPPPATH"]),
+                (
+                    self.sdk_env.Dir(inc_dir).relpath
+                    for inc_dir in self.sdk_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),
-            )
+            filter(lambda path: path in sdk_dirs, full_fw_paths),
         )
+        filtered_paths = list(map(self.build_sdk_file_path, filtered_paths))
 
-        sdk_env = self.env.Clone()
-        sdk_env.Replace(
+        self.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 = SdkMeta(self.sdk_env, self)
         meta.save_to(self.target[0].path)
 
     def build_sdk_file_path(self, orig_path: str) -> str:
@@ -211,7 +223,7 @@ def validate_sdk_cache(source, target, env):
     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())
+        current_sdk.add_header_to_sdk(pathlib.Path(h.srcnode().path).as_posix())
 
     sdk_cache = SdkCache(target[0].path)
     sdk_cache.validate_api(current_sdk.get_api())

+ 4 - 1
scripts/fbt_tools/fbt_version.py

@@ -12,11 +12,14 @@ def version_emitter(target, source, env):
 
 
 def generate(env):
+    env.SetDefault(
+        VERSION_SCRIPT="${FBT_SCRIPT_DIR}/version.py",
+    )
     env.Append(
         BUILDERS={
             "VersionBuilder": Builder(
                 action=Action(
-                    '${PYTHON3} "${ROOT_DIR.abspath}/scripts/version.py" generate -t ${TARGET_HW} -o ${TARGET.dir.posix} --dir "${ROOT_DIR}"',
+                    '${PYTHON3} "${VERSION_SCRIPT}" generate -t ${TARGET_HW} -o ${TARGET.dir.posix} --dir "${ROOT_DIR}"',
                     "${VERSIONCOMSTR}",
                 ),
                 emitter=version_emitter,

+ 2 - 1
scripts/fbt_tools/fwbin.py

@@ -8,7 +8,8 @@ __NM_ARM_BIN = "arm-none-eabi-nm"
 
 def generate(env):
     env.SetDefault(
-        BIN2DFU="${ROOT_DIR.abspath}/scripts/bin2dfu.py",
+        BIN2DFU="${FBT_SCRIPT_DIR}/bin2dfu.py",
+        BIN_SIZE_SCRIPT="${FBT_SCRIPT_DIR}/fwsize.py",
         OBJCOPY=__OBJCOPY_ARM_BIN,  # FIXME
         NM=__NM_ARM_BIN,  # FIXME
     )

+ 12 - 4
scripts/flipper/app.py

@@ -1,6 +1,7 @@
 import logging
 import argparse
 import sys
+import colorlog
 
 
 class App:
@@ -10,7 +11,7 @@ class App:
         self.parser = argparse.ArgumentParser()
         self.parser.add_argument("-d", "--debug", action="store_true", help="Debug")
         # Logging
-        self.logger = logging.getLogger()
+        self.logger = colorlog.getLogger()
         # Application specific initialization
         self.init()
 
@@ -21,10 +22,17 @@ class App:
         self.log_level = logging.DEBUG if self.args.debug else logging.INFO
         self.logger.setLevel(self.log_level)
         if not self.logger.hasHandlers():
-            self.handler = logging.StreamHandler(sys.stdout)
+            self.handler = colorlog.StreamHandler(sys.stdout)
             self.handler.setLevel(self.log_level)
-            self.formatter = logging.Formatter(
-                "%(asctime)s [%(levelname)s] %(message)s"
+            self.formatter = colorlog.ColoredFormatter(
+                "%(log_color)s%(asctime)s [%(levelname)s] %(message)s",
+                log_colors={
+                    "DEBUG": "cyan",
+                    # "INFO": "white",
+                    "WARNING": "yellow",
+                    "ERROR": "red",
+                    "CRITICAL": "red,bg_white",
+                },
             )
             self.handler.setFormatter(self.formatter)
             self.logger.addHandler(self.handler)

+ 4 - 2
scripts/sconsdist.py

@@ -131,7 +131,9 @@ class Main(App):
             self.copy_single_project(project)
 
         self.logger.info(
-            fg.green(f"Firmware binaries can be found at:\n\t{self.output_dir_path}")
+            fg.boldgreen(
+                f"Firmware binaries can be found at:\n\t{self.output_dir_path}"
+            )
         )
 
         if self.args.version:
@@ -167,7 +169,7 @@ class Main(App):
 
             if (bundle_result := UpdateMain(no_exit=True)(bundle_args)) == 0:
                 self.logger.info(
-                    fg.green(
+                    fg.boldgreen(
                         f"Use this directory to self-update your Flipper:\n\t{bundle_dir}"
                     )
                 )

+ 8 - 5
scripts/testing/await_flipper.py

@@ -13,19 +13,22 @@ if not [%FBT_NOENV%] == [] (
     exit /b 0
 )
 
-set "FLIPPER_TOOLCHAIN_VERSION=16"
-set "FBT_TOOLCHAIN_ROOT=%FBT_ROOT%\toolchain\x86_64-windows"
+set "FLIPPER_TOOLCHAIN_VERSION=17"
 
+if [%FBT_TOOLCHAIN_ROOT%] == [] (
+    set "FBT_TOOLCHAIN_ROOT=%FBT_ROOT%\toolchain\x86_64-windows"
+)
 
 if not exist "%FBT_TOOLCHAIN_ROOT%" (
-    powershell -ExecutionPolicy Bypass -File "%FBT_ROOT%\scripts\toolchain\windows-toolchain-download.ps1" "%flipper_toolchain_version%"
+    powershell -ExecutionPolicy Bypass -File "%FBT_ROOT%\scripts\toolchain\windows-toolchain-download.ps1" "%flipper_toolchain_version%" "%FBT_TOOLCHAIN_ROOT%"
 )
 if not exist "%FBT_TOOLCHAIN_ROOT%\VERSION" (
-    powershell -ExecutionPolicy Bypass -File "%FBT_ROOT%\scripts\toolchain\windows-toolchain-download.ps1" "%flipper_toolchain_version%"
+    powershell -ExecutionPolicy Bypass -File "%FBT_ROOT%\scripts\toolchain\windows-toolchain-download.ps1" "%flipper_toolchain_version%" "%FBT_TOOLCHAIN_ROOT%"
 )
+
 set /p REAL_TOOLCHAIN_VERSION=<"%FBT_TOOLCHAIN_ROOT%\VERSION"
 if not "%REAL_TOOLCHAIN_VERSION%" == "%FLIPPER_TOOLCHAIN_VERSION%" (
-    powershell -ExecutionPolicy Bypass -File "%FBT_ROOT%\scripts\toolchain\windows-toolchain-download.ps1" "%flipper_toolchain_version%"
+    powershell -ExecutionPolicy Bypass -File "%FBT_ROOT%\scripts\toolchain\windows-toolchain-download.ps1" "%flipper_toolchain_version%" "%FBT_TOOLCHAIN_ROOT%"
 )
 
 

+ 12 - 12
scripts/toolchain/fbtenv.sh

@@ -5,7 +5,7 @@
 # public variables
 DEFAULT_SCRIPT_PATH="$(pwd -P)";
 SCRIPT_PATH="${SCRIPT_PATH:-$DEFAULT_SCRIPT_PATH}";
-FBT_TOOLCHAIN_VERSION="${FBT_TOOLCHAIN_VERSION:-"16"}";
+FBT_TOOLCHAIN_VERSION="${FBT_TOOLCHAIN_VERSION:-"17"}";
 FBT_TOOLCHAIN_PATH="${FBT_TOOLCHAIN_PATH:-$SCRIPT_PATH}";
 
 fbtenv_show_usage()
@@ -62,7 +62,7 @@ fbtenv_check_sourced()
         fbtenv_show_usage;
         return 1;
     fi
-    case ${0##*/} in dash|-dash|bash|-bash|ksh|-ksh|sh|-sh|*.sh|fbt)
+    case ${0##*/} in dash|-dash|bash|-bash|ksh|-ksh|sh|-sh|*.sh|fbt|ufbt)
         return 0;;
     esac
     fbtenv_show_usage;
@@ -76,8 +76,8 @@ fbtenv_chck_many_source()
             return 0;
         fi
     fi
-    echo "Warning! FBT environment script sourced more than once!";
-    echo "This may signal that you are making mistakes, please open a new shell!";
+    echo "Warning! FBT environment script was sourced more than once!";
+    echo "You might be doing things wrong, please open a new shell!";
     return 1;
 }
 
@@ -93,8 +93,8 @@ fbtenv_set_shell_prompt()
 
 fbtenv_check_script_path()
 {
-    if [ ! -x "$SCRIPT_PATH/fbt" ]; then
-        echo "Please source this script being into flipperzero-firmware root directory, or specify 'SCRIPT_PATH' manually";
+    if [ ! -x "$SCRIPT_PATH/fbt" ] && [ ! -x "$SCRIPT_PATH/ufbt" ] ; then
+        echo "Please source this script from [u]fbt root directory, or specify 'SCRIPT_PATH' variable manually";
         echo "Example:";
         printf "\tSCRIPT_PATH=lang/c/flipperzero-firmware source lang/c/flipperzero-firmware/scripts/fbtenv.sh\n";
         echo "If current directory is right, type 'unset SCRIPT_PATH' and try again"
@@ -108,7 +108,7 @@ fbtenv_get_kernel_type()
     SYS_TYPE="$(uname -s)";
     ARCH_TYPE="$(uname -m)";
     if [ "$ARCH_TYPE" != "x86_64" ] && [ "$SYS_TYPE" != "Darwin" ]; then
-        echo "Now we provide toolchain only for x86_64 arhitecture, sorry..";
+        echo "We only provide toolchain for x86_64 CPUs, sorry..";
         return 1;
     fi
     if [ "$SYS_TYPE" = "Darwin" ]; then
@@ -119,10 +119,10 @@ fbtenv_get_kernel_type()
         TOOLCHAIN_ARCH_DIR="$FBT_TOOLCHAIN_PATH/toolchain/x86_64-linux";
         TOOLCHAIN_URL="https://update.flipperzero.one/builds/toolchain/gcc-arm-none-eabi-10.3-x86_64-linux-flipper-$FBT_TOOLCHAIN_VERSION.tar.gz";
     elif echo "$SYS_TYPE" | grep -q "MINGW"; then
-        echo "In MinGW shell use \"fbt.cmd\" instead of \"fbt\"";
+        echo "In MinGW shell use \"[u]fbt.cmd\" instead of \"[u]fbt\"";
         return 1;
     else
-        echo "Your system is not recognized. Sorry.. Please report us your configuration.";
+        echo "Your system configuration is not supported. Sorry.. Please report us your configuration.";
         return 1;
     fi
     return 0;
@@ -142,7 +142,7 @@ fbtenv_check_rosetta()
 
 fbtenv_check_tar()
 {
-    printf "Checking tar..";
+    printf "Checking for tar..";
     if ! tar --version > /dev/null 2>&1; then
         echo "no";
         return 1;
@@ -153,7 +153,7 @@ fbtenv_check_tar()
 
 fbtenv_check_downloaded_toolchain()
 {
-    printf "Checking downloaded toolchain tgz..";
+    printf "Checking if downloaded toolchain tgz exists..";
     if [ ! -f "$FBT_TOOLCHAIN_PATH/toolchain/$TOOLCHAIN_TAR" ]; then
         echo "no";
         return 1;
@@ -204,7 +204,7 @@ fbtenv_unpack_toolchain()
 
 fbtenv_clearing()
 {
-    printf "Clearing..";
+    printf "Cleaning up..";
     if [ -n "${FBT_TOOLCHAIN_PATH:-""}" ]; then
         rm -rf "${FBT_TOOLCHAIN_PATH:?}/toolchain/"*.tar.gz;
         rm -rf "${FBT_TOOLCHAIN_PATH:?}/toolchain/"*.part;

+ 24 - 12
scripts/toolchain/windows-toolchain-download.ps1

@@ -1,34 +1,46 @@
 Set-StrictMode -Version 2.0
 $ErrorActionPreference = "Stop"
 [Net.ServicePointManager]::SecurityProtocol = "tls12, tls11, tls"
-$repo_root = (Get-Item "$PSScriptRoot\..\..").FullName
+# TODO: fix
+$download_dir = (Get-Item "$PSScriptRoot\..\..").FullName
 $toolchain_version = $args[0]
+$toolchain_target_path = $args[1]
+
 $toolchain_url = "https://update.flipperzero.one/builds/toolchain/gcc-arm-none-eabi-10.3-x86_64-windows-flipper-$toolchain_version.zip"
-$toolchain_zip = "gcc-arm-none-eabi-10.3-x86_64-windows-flipper-$toolchain_version.zip"
-$toolchain_dir = "gcc-arm-none-eabi-10.3-x86_64-windows-flipper"
+$toolchain_dist_folder = "gcc-arm-none-eabi-10.3-x86_64-windows-flipper"
+$toolchain_zip = "$toolchain_dist_folder-$toolchain_version.zip"
+
+$toolchain_zip_temp_path = "$download_dir\$toolchain_zip"
+$toolchain_dist_temp_path = "$download_dir\$toolchain_dist_folder"
 
-if (Test-Path -LiteralPath "$repo_root\toolchain\x86_64-windows") {
+if (Test-Path -LiteralPath "$toolchain_target_path") {
 	Write-Host -NoNewline "Removing old Windows toolchain.."
-	Remove-Item -LiteralPath "$repo_root\toolchain\x86_64-windows" -Force -Recurse
+	Remove-Item -LiteralPath "$toolchain_target_path" -Force -Recurse
 	Write-Host "done!"
 }
-if (!(Test-Path -Path "$repo_root\$toolchain_zip" -PathType Leaf)) {
+if (!(Test-Path -Path "$toolchain_zip_temp_path" -PathType Leaf)) {
     Write-Host -NoNewline "Downloading Windows toolchain.."
     $wc = New-Object net.webclient
-    $wc.Downloadfile("$toolchain_url", "$repo_root\$toolchain_zip")
+    $wc.Downloadfile("$toolchain_url", "$toolchain_zip_temp_path")
     Write-Host "done!"
 }
 
-if (!(Test-Path -LiteralPath "$repo_root\toolchain")) {
-    New-Item "$repo_root\toolchain" -ItemType Directory
+if (!(Test-Path -LiteralPath "$toolchain_target_path\..")) {
+    New-Item "$toolchain_target_path\.." -ItemType Directory -Force
 }
 
 Write-Host -NoNewline "Extracting Windows toolchain.."
+# This is faster than Expand-Archive
 Add-Type -Assembly "System.IO.Compression.Filesystem"
-[System.IO.Compression.ZipFile]::ExtractToDirectory("$repo_root\$toolchain_zip", "$repo_root\")
-Move-Item -Path "$repo_root\$toolchain_dir" -Destination "$repo_root\toolchain\x86_64-windows"
+[System.IO.Compression.ZipFile]::ExtractToDirectory("$toolchain_zip_temp_path", "$download_dir")
+# Expand-Archive -LiteralPath "$toolchain_zip_temp_path" -DestinationPath "$download_dir"
+
+Write-Host -NoNewline "moving.."
+Move-Item -LiteralPath "$toolchain_dist_temp_path" -Destination "$toolchain_target_path"
 Write-Host "done!"
 
 Write-Host -NoNewline "Cleaning up temporary files.."
-Remove-Item -LiteralPath "$repo_root\$toolchain_zip" -Force
+Remove-Item -LiteralPath "$toolchain_zip_temp_path" -Force
 Write-Host "done!"
+
+# dasdasd

+ 2 - 3
site_scons/cc.scons

@@ -30,10 +30,9 @@ ENV.AppendUnique(
         "-ffunction-sections",
         "-fsingle-precision-constant",
         "-fno-math-errno",
-        "-fstack-usage",
+        # Generates .su files with stack usage information
+        # "-fstack-usage",
         "-g",
-        # "-Wno-stringop-overread",
-        # "-Wno-stringop-overflow",
     ],
     CPPDEFINES=[
         "_GNU_SOURCE",

+ 12 - 24
site_scons/environ.scons

@@ -1,5 +1,10 @@
 from SCons.Platform import TempFileMunge
-from fbt.util import tempfile_arg_esc_func, single_quote, wrap_tempfile
+from fbt.util import (
+    tempfile_arg_esc_func,
+    single_quote,
+    wrap_tempfile,
+    extract_abs_dir_path,
+)
 
 import os
 import multiprocessing
@@ -52,6 +57,12 @@ coreenv = VAR_ENV.Clone(
     MAXLINELENGTH=2048,
     PROGSUFFIX=".elf",
     ENV=forward_os_env,
+    SINGLEQUOTEFUNC=single_quote,
+    ABSPATHGETTERFUNC=extract_abs_dir_path,
+    # Setting up temp file parameters - to overcome command line length limits
+    TEMPFILEARGESCFUNC=tempfile_arg_esc_func,
+    FBT_SCRIPT_DIR=Dir("#/scripts"),
+    ROOT_DIR=Dir("#"),
 )
 
 # If DIST_SUFFIX is set in environment, is has precedence (set by CI)
@@ -60,24 +71,6 @@ if os_suffix := os.environ.get("DIST_SUFFIX", None):
         DIST_SUFFIX=os_suffix,
     )
 
-# print(coreenv.Dump())
-if not coreenv["VERBOSE"]:
-    coreenv.SetDefault(
-        CCCOMSTR="\tCC\t${SOURCE}",
-        CXXCOMSTR="\tCPP\t${SOURCE}",
-        ASCOMSTR="\tASM\t${SOURCE}",
-        ARCOMSTR="\tAR\t${TARGET}",
-        RANLIBCOMSTR="\tRANLIB\t${TARGET}",
-        LINKCOMSTR="\tLINK\t${TARGET}",
-        INSTALLSTR="\tINSTALL\t${TARGET}",
-        APPSCOMSTR="\tAPPS\t${TARGET}",
-        VERSIONCOMSTR="\tVERSION\t${TARGET}",
-        STRIPCOMSTR="\tSTRIP\t${TARGET}",
-        OBJDUMPCOMSTR="\tOBJDUMP\t${TARGET}",
-        # GDBCOMSTR="\tGDB\t${SOURCE}",
-        # GDBPYCOMSTR="\tGDB-PY\t${SOURCE}",
-    )
-
 # Default value for commandline options
 
 SetOption("num_jobs", multiprocessing.cpu_count())
@@ -90,12 +83,7 @@ SetOption("max_drift", 1)
 # Random task queue - to discover isses with build logic faster
 # SetOption("random", 1)
 
-
-# Setting up temp file parameters - to overcome command line length limits
-coreenv["TEMPFILEARGESCFUNC"] = tempfile_arg_esc_func
 wrap_tempfile(coreenv, "LINKCOM")
 wrap_tempfile(coreenv, "ARCOM")
 
-coreenv["SINGLEQUOTEFUNC"] = single_quote
-
 Return("coreenv")

+ 35 - 3
site_scons/extapps.scons

@@ -3,10 +3,9 @@ from SCons.Errors import UserError
 
 Import("ENV")
 
-
 from fbt.appmanifest import FlipperAppType
 
-appenv = ENV.Clone(
+appenv = ENV["APPENV"] = ENV.Clone(
     tools=[
         (
             "fbt_extapps",
@@ -17,6 +16,7 @@ appenv = ENV.Clone(
             },
         ),
         "fbt_assets",
+        "fbt_sdk",
     ]
 )
 
@@ -66,6 +66,7 @@ extapps = appenv["_extapps"] = {
     "validators": {},
     "dist": {},
     "resources_dist": None,
+    "sdk_tree": None,
 }
 
 
@@ -115,10 +116,41 @@ if appsrc := appenv.subst("$APPSRC"):
     app_manifest, fap_file, app_validator = appenv.GetExtAppFromPath(appsrc)
     appenv.PhonyTarget(
         "launch_app",
-        '${PYTHON3} scripts/runfap.py ${SOURCE} --fap_dst_dir "/ext/apps/${FAP_CATEGORY}"',
+        '${PYTHON3} "${APP_RUN_SCRIPT}" ${SOURCE} --fap_dst_dir "/ext/apps/${FAP_CATEGORY}"',
         source=fap_file,
         FAP_CATEGORY=app_manifest.fap_category,
     )
     appenv.Alias("launch_app", app_validator)
 
+# SDK management
+
+sdk_origin_path = "${BUILD_DIR}/sdk_origin"
+sdk_source = appenv.SDKPrebuilder(
+    sdk_origin_path,
+    # Deps on root SDK headers and generated files
+    (appenv["SDK_HEADERS"], appenv["FW_ASSETS_HEADERS"]),
+)
+# Extra deps on headers included in deeper levels
+Depends(sdk_source, appenv.ProcessSdkDepends(f"{sdk_origin_path}.d"))
+
+appenv["SDK_DIR"] = appenv.Dir("${BUILD_DIR}/sdk")
+sdk_tree = extapps["sdk_tree"] = appenv.SDKTree(appenv["SDK_DIR"], sdk_origin_path)
+# AlwaysBuild(sdk_tree)
+Alias("sdk_tree", sdk_tree)
+
+sdk_apicheck = appenv.SDKSymUpdater(appenv["SDK_DEFINITION"], sdk_origin_path)
+Precious(sdk_apicheck)
+NoClean(sdk_apicheck)
+AlwaysBuild(sdk_apicheck)
+Alias("sdk_check", sdk_apicheck)
+
+sdk_apisyms = appenv.SDKSymGenerator(
+    "${BUILD_DIR}/assets/compiled/symbols.h", appenv["SDK_DEFINITION"]
+)
+Alias("api_syms", sdk_apisyms)
+
+if appenv["FORCE"]:
+    appenv.AlwaysBuild(sdk_source, sdk_tree, sdk_apicheck, sdk_apisyms)
+
+
 Return("extapps")