Przeglądaj źródła

[FL-3174] Dolphin builder in ufbt; minor ufbt/fbt improvements (#2601)

* ufbt: added "dolphin_ext" target (expects "external" subfolder in cwd with dolphin assets); cleaned up unused code
* ufbt: codestyle fixes
* scripts: fixed style according to ruff linter
* scripts: additional cleanup & codestyle fixes
* github: pass target hw code when installing local SDK with ufbt
* ufbt: added error message for missing folder in dolphin builder
* scripts: more linter fixes
* sdk: added flipper_format_stream; ufbt: support for --extra-define
* fbt: reduced amount of global defines
* scripts, fbt: rearranged imports

Co-authored-by: Aleksandr Kutuzov <alleteam@gmail.com>
hedger 2 lat temu
rodzic
commit
c3ececcf96
73 zmienionych plików z 306 dodań i 377 usunięć
  1. 2 0
      .github/workflows/build.yml
  2. 2 2
      assets/SConscript
  3. 2 2
      firmware.scons
  4. 7 1
      firmware/targets/f18/api_symbols.csv
  5. 7 1
      firmware/targets/f7/api_symbols.csv
  6. 1 0
      lib/flipper_format/SConscript
  7. 0 3
      lib/freertos.scons
  8. 5 3
      lib/libusb_stm32.scons
  9. 5 3
      lib/littlefs.scons
  10. 2 2
      lib/misc.scons
  11. 2 2
      lib/toolbox/SConscript
  12. 16 16
      scripts/assets.py
  13. 3 3
      scripts/distfap.py
  14. 5 5
      scripts/fbt/appmanifest.py
  15. 1 3
      scripts/fbt/elfmanifest.py
  16. 1 1
      scripts/fbt/fapassets.py
  17. 5 12
      scripts/fbt/sdk/cache.py
  18. 4 5
      scripts/fbt/util.py
  19. 1 1
      scripts/fbt/version.py
  20. 1 1
      scripts/fbt_tools/ccache.py
  21. 5 9
      scripts/fbt_tools/crosscc.py
  22. 6 7
      scripts/fbt_tools/fbt_apps.py
  23. 7 7
      scripts/fbt_tools/fbt_assets.py
  24. 0 2
      scripts/fbt_tools/fbt_debugopts.py
  25. 1 2
      scripts/fbt_tools/fbt_dist.py
  26. 7 11
      scripts/fbt_tools/fbt_extapps.py
  27. 0 2
      scripts/fbt_tools/fbt_hwtarget.py
  28. 9 10
      scripts/fbt_tools/fbt_sdk.py
  29. 5 7
      scripts/fbt_tools/fbt_tweaks.py
  30. 1 1
      scripts/fbt_tools/fbt_version.py
  31. 2 2
      scripts/fbt_tools/fwbin.py
  32. 0 4
      scripts/fbt_tools/gdb.py
  33. 1 1
      scripts/fbt_tools/objdump.py
  34. 2 2
      scripts/fbt_tools/openocd.py
  35. 7 6
      scripts/fbt_tools/pvsstudio.py
  36. 2 1
      scripts/fbt_tools/sconsmodular.py
  37. 1 1
      scripts/fbt_tools/sconsrecursiveglob.py
  38. 1 1
      scripts/fbt_tools/strip.py
  39. 23 27
      scripts/flash.py
  40. 1 1
      scripts/flipper/app.py
  41. 4 4
      scripts/flipper/assets/copro.py
  42. 2 1
      scripts/flipper/assets/coprobin.py
  43. 5 7
      scripts/flipper/assets/dolphin.py
  44. 3 9
      scripts/flipper/assets/icon.py
  45. 2 3
      scripts/flipper/assets/manifest.py
  46. 1 3
      scripts/flipper/assets/obdata.py
  47. 4 4
      scripts/flipper/cube.py
  48. 2 2
      scripts/flipper/storage.py
  49. 0 1
      scripts/flipper/utils/__init__.py
  50. 5 5
      scripts/flipper/utils/programmer_openocd.py
  51. 3 3
      scripts/flipper/utils/stm32wb55.py
  52. 2 2
      scripts/flipper/utils/templite.py
  53. 4 3
      scripts/fwsize.py
  54. 5 5
      scripts/get_env.py
  55. 5 6
      scripts/lint.py
  56. 2 1
      scripts/merge_report_qa.py
  57. 2 1
      scripts/meta.py
  58. 2 2
      scripts/ob.py
  59. 17 20
      scripts/otp.py
  60. 7 7
      scripts/program.py
  61. 5 7
      scripts/runfap.py
  62. 1 1
      scripts/sconsdist.py
  63. 5 7
      scripts/selfupdate.py
  64. 3 2
      scripts/serial_cli.py
  65. 6 6
      scripts/storage.py
  66. 25 12
      scripts/ufbt/SConstruct
  67. 9 23
      scripts/ufbt/commandline.scons
  68. 3 3
      scripts/ufbt/site_init.py
  69. 3 4
      scripts/ufbt/site_tools/ufbt_state.py
  70. 0 37
      scripts/ufbt/update.scons
  71. 10 10
      scripts/update.py
  72. 4 4
      scripts/version.py
  73. 2 2
      site_scons/extapps.scons

+ 2 - 0
.github/workflows/build.yml

@@ -193,12 +193,14 @@ jobs:
           TARGET="$(echo '${{ matrix.target }}' | sed 's/f//')"; \
           ./fbt TARGET_HW=$TARGET DEBUG=0 COMPACT=1 fap_dist updater_package
           echo "sdk-file=$(ls dist/${{ matrix.target }}-*/flipper-z-${{ matrix.target }}-sdk-*.zip)" >> $GITHUB_OUTPUT
+          echo "hw-target-code=$TARGET" >> $GITHUB_OUTPUT
 
       - name: Deploy uFBT with SDK
         uses: flipperdevices/flipperzero-ufbt-action@v0.1.0
         with:
           task: setup
           sdk-file: ${{ steps.build-fw.outputs.sdk-file }}
+          sdk-hw-target: ${{ steps.build-fw.outputs.hw-target-code }}
 
       - name: Build test app with SDK
         run: |

+ 2 - 2
assets/SConscript

@@ -1,7 +1,7 @@
-Import("env")
-
 from fbt.version import get_git_commit_unix_timestamp
 
+Import("env")
+
 assetsenv = env.Clone(
     tools=["fbt_assets"],
     FW_LIB_NAME="assets",

+ 2 - 2
firmware.scons

@@ -1,5 +1,3 @@
-Import("ENV", "fw_build_meta")
-
 from SCons.Errors import UserError
 from SCons.Node import FS
 
@@ -10,6 +8,8 @@ from fbt_extra.util import (
     link_elf_dir_as_latest,
 )
 
+Import("ENV", "fw_build_meta")
+
 # Building initial C environment for libs
 env = ENV.Clone(
     tools=[

+ 7 - 1
firmware/targets/f18/api_symbols.csv

@@ -1,5 +1,5 @@
 entry,status,name,type,params
-Version,+,23.1,,
+Version,+,23.3,,
 Header,+,applications/services/bt/bt_service/bt.h,,
 Header,+,applications/services/cli/cli.h,,
 Header,+,applications/services/cli/cli_vcp.h,,
@@ -112,6 +112,7 @@ Header,+,lib/flipper_application/plugins/composite_resolver.h,,
 Header,+,lib/flipper_application/plugins/plugin_manager.h,,
 Header,+,lib/flipper_format/flipper_format.h,,
 Header,+,lib/flipper_format/flipper_format_i.h,,
+Header,+,lib/flipper_format/flipper_format_stream.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,,
@@ -755,6 +756,11 @@ Function,+,flipper_format_read_uint32,_Bool,"FlipperFormat*, const char*, uint32
 Function,+,flipper_format_rewind,_Bool,FlipperFormat*
 Function,+,flipper_format_seek_to_end,_Bool,FlipperFormat*
 Function,+,flipper_format_set_strict_mode,void,"FlipperFormat*, _Bool"
+Function,+,flipper_format_stream_delete_key_and_write,_Bool,"Stream*, FlipperStreamWriteData*, _Bool"
+Function,+,flipper_format_stream_get_value_count,_Bool,"Stream*, const char*, uint32_t*, _Bool"
+Function,+,flipper_format_stream_read_value_line,_Bool,"Stream*, const char*, FlipperStreamValue, void*, size_t, _Bool"
+Function,+,flipper_format_stream_write_comment_cstr,_Bool,"Stream*, const char*"
+Function,+,flipper_format_stream_write_value_line,_Bool,"Stream*, FlipperStreamWriteData*"
 Function,+,flipper_format_string_alloc,FlipperFormat*,
 Function,+,flipper_format_update_bool,_Bool,"FlipperFormat*, const char*, const _Bool*, const uint16_t"
 Function,+,flipper_format_update_float,_Bool,"FlipperFormat*, const char*, const float*, const uint16_t"

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

@@ -1,5 +1,5 @@
 entry,status,name,type,params
-Version,+,23.1,,
+Version,+,23.3,,
 Header,+,applications/services/bt/bt_service/bt.h,,
 Header,+,applications/services/cli/cli.h,,
 Header,+,applications/services/cli/cli_vcp.h,,
@@ -118,6 +118,7 @@ Header,+,lib/flipper_application/plugins/composite_resolver.h,,
 Header,+,lib/flipper_application/plugins/plugin_manager.h,,
 Header,+,lib/flipper_format/flipper_format.h,,
 Header,+,lib/flipper_format/flipper_format_i.h,,
+Header,+,lib/flipper_format/flipper_format_stream.h,,
 Header,+,lib/ibutton/ibutton_key.h,,
 Header,+,lib/ibutton/ibutton_protocols.h,,
 Header,+,lib/ibutton/ibutton_worker.h,,
@@ -918,6 +919,11 @@ Function,+,flipper_format_read_uint32,_Bool,"FlipperFormat*, const char*, uint32
 Function,+,flipper_format_rewind,_Bool,FlipperFormat*
 Function,+,flipper_format_seek_to_end,_Bool,FlipperFormat*
 Function,+,flipper_format_set_strict_mode,void,"FlipperFormat*, _Bool"
+Function,+,flipper_format_stream_delete_key_and_write,_Bool,"Stream*, FlipperStreamWriteData*, _Bool"
+Function,+,flipper_format_stream_get_value_count,_Bool,"Stream*, const char*, uint32_t*, _Bool"
+Function,+,flipper_format_stream_read_value_line,_Bool,"Stream*, const char*, FlipperStreamValue, void*, size_t, _Bool"
+Function,+,flipper_format_stream_write_comment_cstr,_Bool,"Stream*, const char*"
+Function,+,flipper_format_stream_write_value_line,_Bool,"Stream*, FlipperStreamWriteData*"
 Function,+,flipper_format_string_alloc,FlipperFormat*,
 Function,+,flipper_format_update_bool,_Bool,"FlipperFormat*, const char*, const _Bool*, const uint16_t"
 Function,+,flipper_format_update_float,_Bool,"FlipperFormat*, const char*, const float*, const uint16_t"

+ 1 - 0
lib/flipper_format/SConscript

@@ -7,6 +7,7 @@ env.Append(
     SDK_HEADERS=[
         File("flipper_format.h"),
         File("flipper_format_i.h"),
+        File("flipper_format_stream.h"),
     ],
 )
 

+ 0 - 3
lib/freertos.scons

@@ -7,9 +7,6 @@ env.Append(
         "#/lib/FreeRTOS-Kernel/portable/GCC/ARM_CM4F",
         "#/lib/FreeRTOS-glue",
     ],
-    CPPDEFINES=[
-        "HAVE_FREERTOS",
-    ],
 )
 
 

+ 5 - 3
lib/libusb_stm32.scons

@@ -4,9 +4,6 @@ env.Append(
     CPPPATH=[
         "#/lib/libusb_stm32/inc",
     ],
-    CPPDEFINES=[
-        ("USB_PMASIZE", "0x400"),
-    ],
     SDK_HEADERS=env.GlobRecursive(
         "*.h",
         Dir("libusb_stm32/inc"),
@@ -16,6 +13,11 @@ env.Append(
 
 libenv = env.Clone(FW_LIB_NAME="usb_stm32")
 libenv.ApplyLibFlags()
+libenv.Append(
+    CPPDEFINES=[
+        ("USB_PMASIZE", "0x400"),
+    ],
+)
 
 
 sources = [

+ 5 - 3
lib/littlefs.scons

@@ -4,14 +4,16 @@ env.Append(
     CPPPATH=[
         "#/lib/littlefs",
     ],
-    CPPDEFINES=[
-        ("LFS_CONFIG", "lfs_config.h"),
-    ],
 )
 
 
 libenv = env.Clone(FW_LIB_NAME="littlefs")
 libenv.ApplyLibFlags()
+libenv.Append(
+    CPPDEFINES=[
+        ("LFS_CONFIG", "lfs_config.h"),
+    ],
+)
 
 sources = Glob("littlefs/*.c", source=True)
 

+ 2 - 2
lib/misc.scons

@@ -1,7 +1,7 @@
-Import("env")
-
 from fbt.util import GLOB_FILE_EXCLUSION
 
+Import("env")
+
 env.Append(
     CPPPATH=[
         "#/lib/digital_signal",

+ 2 - 2
lib/toolbox/SConscript

@@ -1,8 +1,8 @@
-Import("env")
-
 from fbt.version import get_fast_git_version_id
 
 
+Import("env")
+
 env.Append(
     CPPPATH=[
         "#/lib/toolbox",

+ 16 - 16
scripts/assets.py

@@ -1,10 +1,10 @@
 #!/usr/bin/env python3
 
+import os
+
 from flipper.app import App
 from flipper.assets.icon import file2image
 
-import os
-
 ICONS_SUPPORTED_FORMATS = ["png"]
 
 ICONS_TEMPLATE_H_HEADER = """#pragma once
@@ -127,7 +127,7 @@ class Main(App):
             if not filenames:
                 continue
             if "frame_rate" in filenames:
-                self.logger.debug(f"Folder contains animation")
+                self.logger.debug("Folder contains animation")
                 icon_name = "A_" + os.path.split(dirpath)[1].replace("-", "_")
                 width = height = None
                 frame_count = 0
@@ -186,7 +186,7 @@ class Main(App):
                     icons_c.write("\n")
                     icons.append((icon_name, width, height, 0, 1))
         # Create array of images:
-        self.logger.debug(f"Finalizing source file")
+        self.logger.debug("Finalizing source file")
         for name, width, height, frame_rate, frame_count in icons:
             icons_c.write(
                 ICONS_TEMPLATE_C_ICONS.format(
@@ -201,7 +201,7 @@ class Main(App):
         icons_c.close()
 
         # Create Public Header
-        self.logger.debug(f"Creating header")
+        self.logger.debug("Creating header")
         icons_h = open(
             os.path.join(self.args.output_directory, f"{self.args.filename}.h"),
             "w",
@@ -211,7 +211,7 @@ class Main(App):
         for name, width, height, frame_rate, frame_count in icons:
             icons_h.write(ICONS_TEMPLATE_H_ICON_NAME.format(name=name))
         icons_h.close()
-        self.logger.debug(f"Done")
+        self.logger.debug("Done")
         return 0
 
     def manifest(self):
@@ -232,7 +232,7 @@ class Main(App):
         new_manifest = Manifest(self.args.timestamp)
         new_manifest.create(directory_path)
 
-        self.logger.info(f"Comparing new manifest with existing")
+        self.logger.info("Comparing new manifest with existing")
         only_in_old, changed, only_in_new = Manifest.compare(old_manifest, new_manifest)
         for record in only_in_old:
             self.logger.info(f"Only in old: {record}")
@@ -246,38 +246,38 @@ class Main(App):
         else:
             self.logger.info("Manifest is up-to-date!")
 
-        self.logger.info(f"Complete")
+        self.logger.info("Complete")
 
         return 0
 
     def copro(self):
         from flipper.assets.copro import Copro
 
-        self.logger.info(f"Bundling coprocessor binaries")
+        self.logger.info("Bundling coprocessor binaries")
         copro = Copro(self.args.mcu)
-        self.logger.info(f"Loading CUBE info")
+        self.logger.info("Loading CUBE info")
         copro.loadCubeInfo(self.args.cube_dir, self.args.cube_ver)
-        self.logger.info(f"Bundling")
+        self.logger.info("Bundling")
         copro.bundle(
             self.args.output_dir,
             self.args.stack_file,
             self.args.stack_type,
             self.args.stack_addr,
         )
-        self.logger.info(f"Complete")
+        self.logger.info("Complete")
 
         return 0
 
     def dolphin(self):
         from flipper.assets.dolphin import Dolphin
 
-        self.logger.info(f"Processing Dolphin sources")
+        self.logger.info("Processing Dolphin sources")
         dolphin = Dolphin()
-        self.logger.info(f"Loading data")
+        self.logger.info("Loading data")
         dolphin.load(self.args.input_directory)
-        self.logger.info(f"Packing")
+        self.logger.info("Packing")
         dolphin.pack(self.args.output_directory, self.args.symbol_name)
-        self.logger.info(f"Complete")
+        self.logger.info("Complete")
 
         return 0
 

+ 3 - 3
scripts/distfap.py

@@ -1,12 +1,12 @@
 #!/usr/bin/env python3
 
+import os
+import posixpath
+
 from flipper.app import App
 from flipper.storage import FlipperStorage, FlipperStorageOperations
 from flipper.utils.cdc import resolve_port
 
-import os
-import posixpath
-
 
 class Main(App):
     def init(self):

+ 5 - 5
scripts/fbt/appmanifest.py

@@ -1,7 +1,7 @@
+import os
 from dataclasses import dataclass, field
-from typing import List, Optional, Tuple, Callable
 from enum import Enum
-import os
+from typing import Callable, List, Optional, Tuple
 
 
 class FlipperManifestException(Exception):
@@ -93,7 +93,7 @@ class AppManager:
     def get(self, appname: str):
         try:
             return self.known_apps[appname]
-        except KeyError as _:
+        except KeyError:
             raise FlipperManifestException(
                 f"Missing application manifest for '{appname}'"
             )
@@ -223,6 +223,7 @@ class AppBuildset:
         return self.appmgr.get(app_name).supports_hardware_target(self.hw_target)
 
     def _get_app_depends(self, app_name: str) -> List[str]:
+        app_def = self.appmgr.get(app_name)
         # Skip app if its target is not supported by the target we are building for
         if not self._check_if_app_target_supported(app_name):
             self._writer(
@@ -230,7 +231,6 @@ class AppBuildset:
             )
             return []
 
-        app_def = self.appmgr.get(app_name)
         return list(
             filter(
                 self._check_if_app_target_supported,
@@ -296,7 +296,7 @@ class AppBuildset:
                 try:
                     parent_app = self.appmgr.get(parent_app_id)
                     parent_app._plugins.append(extension_app)
-                except FlipperManifestException as e:
+                except FlipperManifestException:
                     self._writer(
                         f"Module {extension_app.appid} has unknown parent {parent_app_id}"
                     )

+ 1 - 3
scripts/fbt/elfmanifest.py

@@ -1,12 +1,10 @@
-from dataclasses import dataclass
 import os
-
 import struct
 from dataclasses import dataclass, field
 
-from .appmanifest import FlipperApplication
 from flipper.assets.icon import file2image
 
+from .appmanifest import FlipperApplication
 
 _MANIFEST_MAGIC = 0x52474448
 

+ 1 - 1
scripts/fbt/fapassets.py

@@ -1,5 +1,5 @@
-import os
 import hashlib
+import os
 import struct
 from typing import TypedDict
 

+ 5 - 12
scripts/fbt/sdk/cache.py

@@ -1,20 +1,13 @@
-import operator
-import os
 import csv
 import operator
-
-from enum import Enum, auto
-from typing import Set, ClassVar, Any
+import os
 from dataclasses import dataclass
+from enum import Enum, auto
+from typing import Any, ClassVar, Set
 
 from ansi.color import fg
 
-from . import (
-    ApiEntries,
-    ApiEntryFunction,
-    ApiEntryVariable,
-    ApiHeader,
-)
+from . import ApiEntries, ApiEntryFunction, ApiEntryVariable, ApiHeader
 
 
 @dataclass(frozen=True)
@@ -137,7 +130,7 @@ class SdkCache:
                     f"API version is still WIP: {self.version}. Review the changes and re-run command."
                 )
             )
-            print(f"CSV file entries to mark up:")
+            print("CSV file entries to mark up:")
             print(
                 fg.yellow(
                     "\n".join(

+ 4 - 5
scripts/fbt/util.py

@@ -1,10 +1,9 @@
-import SCons
-from SCons.Subst import quote_spaces
-from SCons.Errors import StopError
-
-import re
 import os
+import re
 
+import SCons
+from SCons.Errors import StopError
+from SCons.Subst import quote_spaces
 
 WINPATHSEP_RE = re.compile(r"\\([^\"'\\]|$)")
 

+ 1 - 1
scripts/fbt/version.py

@@ -1,5 +1,5 @@
-import subprocess
 import datetime
+import subprocess
 from functools import cache
 
 

+ 1 - 1
scripts/fbt_tools/ccache.py

@@ -3,7 +3,7 @@ def exists():
 
 
 def generate(env):
-    if ccache := env.WhereIs("ccache"):
+    if env.WhereIs("ccache"):
         env["CCACHE"] = "ccache"
         env["CC_NOCACHE"] = env["CC"]
         env["CC"] = "$CCACHE $CC_NOCACHE"

+ 5 - 9
scripts/fbt_tools/crosscc.py

@@ -1,15 +1,11 @@
-from SCons.Errors import StopError
-from SCons.Tool import asm
-from SCons.Tool import gcc
-from SCons.Tool import gxx
-from SCons.Tool import ar
-from SCons.Tool import gnulink
-import strip
+import subprocess
+
 import gdb
 import objdump
-
+import strip
 from SCons.Action import _subproc
-import subprocess
+from SCons.Errors import StopError
+from SCons.Tool import ar, asm, gcc, gnulink, gxx
 
 
 def prefix_commands(env, command_prefix, cmd_list):

+ 6 - 7
scripts/fbt_tools/fbt_apps.py

@@ -1,15 +1,14 @@
-from SCons.Builder import Builder
-from SCons.Action import Action
-from SCons.Errors import StopError
-from SCons.Warnings import warn, WarningOnByDefault
 from ansi.color import fg
-
 from fbt.appmanifest import (
-    FlipperAppType,
-    AppManager,
     ApplicationsCGenerator,
+    AppManager,
+    FlipperAppType,
     FlipperManifestException,
 )
+from SCons.Action import Action
+from SCons.Builder import Builder
+from SCons.Errors import StopError
+from SCons.Warnings import WarningOnByDefault, warn
 
 # Adding objects for application management to env
 #  AppManager env["APPMGR"] - loads all manifests; manages list of known apps

+ 7 - 7
scripts/fbt_tools/fbt_assets.py

@@ -1,10 +1,10 @@
-from SCons.Builder import Builder
-from SCons.Action import Action
-from SCons.Errors import StopError
-
 import os
 import subprocess
+
 from ansi.color import fg
+from SCons.Action import Action
+from SCons.Builder import Builder
+from SCons.Errors import StopError
 
 
 def icons_emitter(target, source, env):
@@ -76,11 +76,11 @@ def proto_ver_generator(target, source, env):
     target_file = target[0]
     src_dir = source[0].dir.abspath
     try:
-        git_fetch = _invoke_git(
+        _invoke_git(
             ["fetch", "--tags"],
             source_dir=src_dir,
         )
-    except (subprocess.CalledProcessError, EnvironmentError) as e:
+    except (subprocess.CalledProcessError, EnvironmentError):
         # Not great, not terrible
         print(fg.boldred("Git: fetch failed"))
 
@@ -89,7 +89,7 @@ def proto_ver_generator(target, source, env):
             ["describe", "--tags", "--abbrev=0"],
             source_dir=src_dir,
         )
-    except (subprocess.CalledProcessError, EnvironmentError) as e:
+    except (subprocess.CalledProcessError, EnvironmentError):
         raise StopError("Git: describe failed")
 
     git_major, git_minor = git_describe.split(".")

+ 0 - 2
scripts/fbt_tools/fbt_debugopts.py

@@ -1,5 +1,3 @@
-from re import search
-
 from SCons.Errors import UserError
 
 

+ 1 - 2
scripts/fbt_tools/fbt_dist.py

@@ -1,6 +1,5 @@
-from SCons.Builder import Builder
 from SCons.Action import Action
-from SCons.Script import Mkdir
+from SCons.Builder import Builder
 from SCons.Defaults import Touch
 
 

+ 7 - 11
scripts/fbt_tools/fbt_extapps.py

@@ -3,23 +3,19 @@ import os
 import pathlib
 import shutil
 from dataclasses import dataclass, field
-from typing import Optional, TypedDict
-
-from ansi.color import fg
+from typing import Optional
 
 import SCons.Warnings
-from SCons.Action import Action
-from SCons.Builder import Builder
-from SCons.Errors import UserError
-from SCons.Node import NodeList
-from SCons.Node.FS import File, Entry
-
+from ansi.color import fg
 from fbt.appmanifest import FlipperApplication, FlipperAppType, FlipperManifestException
 from fbt.elfmanifest import assemble_manifest_data
 from fbt.fapassets import FileBundler
 from fbt.sdk.cache import SdkCache
 from fbt.util import extract_abs_dir_path
-
+from SCons.Action import Action
+from SCons.Builder import Builder
+from SCons.Errors import UserError
+from SCons.Node.FS import Entry, File
 
 _FAP_META_SECTION = ".fapmeta"
 _FAP_FILEASSETS_SECTION = ".fapassets"
@@ -289,7 +285,7 @@ def GetExtAppByIdOrPath(env, app_dir):
     try:
         # Maybe user passed an appid?
         app = appmgr.get(app_dir)
-    except FlipperManifestException as _:
+    except FlipperManifestException:
         # Look up path components in known app dirs
         for dir_part in reversed(pathlib.Path(app_dir).parts):
             if app := appmgr.find_by_appdir(dir_part):

+ 0 - 2
scripts/fbt_tools/fbt_hwtarget.py

@@ -1,5 +1,3 @@
-from SCons.Builder import Builder
-from SCons.Action import Action
 import json
 
 

+ 9 - 10
scripts/fbt_tools/fbt_sdk.py

@@ -1,21 +1,20 @@
+import json
+import os.path
+import pathlib
+import posixpath
 import shutil
-from SCons.Builder import Builder
+
+from fbt.sdk.cache import SdkCache
+from fbt.sdk.collector import SdkCollector
+from fbt.util import path_as_posix
 from SCons.Action import Action
+from SCons.Builder import Builder
 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
-from fbt.util import path_as_posix
-
 
 def ProcessSdkDepends(env, filename):
     try:

+ 5 - 7
scripts/fbt_tools/fbt_tweaks.py

@@ -1,15 +1,13 @@
+import os
+import sys
+import traceback
+
 import SCons.Warnings as Warnings
+from ansi.color import fg
 from SCons.Errors import UserError
 
-
 # from SCons.Script.Main import find_deepest_user_frame
 
-from ansi.color import fg, bg, fx
-
-import traceback
-import sys
-import os
-
 
 def find_deepest_user_frame(tb):
     tb.reverse()

+ 1 - 1
scripts/fbt_tools/fbt_version.py

@@ -1,5 +1,5 @@
-from SCons.Builder import Builder
 from SCons.Action import Action
+from SCons.Builder import Builder
 
 
 def version_emitter(target, source, env):

+ 2 - 2
scripts/fbt_tools/fwbin.py

@@ -1,6 +1,6 @@
-from SCons.Builder import Builder
-from SCons.Action import Action
 import SCons
+from SCons.Action import Action
+from SCons.Builder import Builder
 
 __OBJCOPY_ARM_BIN = "arm-none-eabi-objcopy"
 __NM_ARM_BIN = "arm-none-eabi-nm"

+ 0 - 4
scripts/fbt_tools/gdb.py

@@ -1,7 +1,3 @@
-from SCons.Builder import Builder
-from SCons.Action import Action
-
-
 def generate(env):
     env.SetDefault(
         GDB="gdb",

+ 1 - 1
scripts/fbt_tools/objdump.py

@@ -1,5 +1,5 @@
-from SCons.Builder import Builder
 from SCons.Action import Action
+from SCons.Builder import Builder
 
 
 def generate(env):

+ 2 - 2
scripts/fbt_tools/openocd.py

@@ -1,7 +1,7 @@
-from SCons.Builder import Builder
+import SCons
 from SCons.Action import Action
+from SCons.Builder import Builder
 from SCons.Defaults import Touch
-import SCons
 
 __OPENOCD_BIN = "openocd"
 

+ 7 - 6
scripts/fbt_tools/pvsstudio.py

@@ -1,11 +1,12 @@
-from SCons.Builder import Builder
-from SCons.Action import Action
-from SCons.Script import Delete, Mkdir, GetBuildFailures, Flatten
-import multiprocessing
-import webbrowser
 import atexit
-import sys
+import multiprocessing
 import subprocess
+import sys
+import webbrowser
+
+from SCons.Action import Action
+from SCons.Builder import Builder
+from SCons.Script import Delete, Flatten, GetBuildFailures, Mkdir
 
 __no_browser = False
 

+ 2 - 1
scripts/fbt_tools/sconsmodular.py

@@ -1,5 +1,6 @@
-import posixpath
 import os
+import posixpath
+
 from SCons.Errors import UserError
 
 

+ 1 - 1
scripts/fbt_tools/sconsrecursiveglob.py

@@ -1,6 +1,6 @@
 import SCons
-from SCons.Script import Flatten
 from fbt.util import GLOB_FILE_EXCLUSION
+from SCons.Script import Flatten
 
 
 def GlobRecursive(env, pattern, node=".", exclude=[]):

+ 1 - 1
scripts/fbt_tools/strip.py

@@ -1,5 +1,5 @@
-from SCons.Builder import Builder
 from SCons.Action import Action
+from SCons.Builder import Builder
 
 
 def generate(env):

+ 23 - 27
scripts/flash.py

@@ -1,13 +1,9 @@
 #!/usr/bin/env python3
 
-import logging
-import argparse
-import sys
-import os
 
 from flipper.app import App
-from flipper.cube import CubeProgrammer
 from flipper.assets.coprobin import CoproBinary
+from flipper.cube import CubeProgrammer
 
 STATEMENT = "AGREE_TO_LOSE_FLIPPER_FEATURES_THAT_USE_CRYPTO_ENCLAVE"
 
@@ -94,59 +90,59 @@ class Main(App):
         }
 
     def wipe(self):
-        self.logger.info(f"Wiping flash")
+        self.logger.info("Wiping flash")
         cp = CubeProgrammer(self._getCubeParams())
-        self.logger.info(f"Setting RDP to 0xBB")
+        self.logger.info("Setting RDP to 0xBB")
         cp.setOptionBytes({"RDP": ("0xBB", "rw")})
-        self.logger.info(f"Verifying RDP")
+        self.logger.info("Verifying RDP")
         r = cp.checkOptionBytes({"RDP": ("0xBB", "rw")})
-        assert r == True
+        assert r is True
         self.logger.info(f"Result: {r}")
-        self.logger.info(f"Setting RDP to 0xAA")
+        self.logger.info("Setting RDP to 0xAA")
         cp.setOptionBytes({"RDP": ("0xAA", "rw")})
-        self.logger.info(f"Verifying RDP")
+        self.logger.info("Verifying RDP")
         r = cp.checkOptionBytes({"RDP": ("0xAA", "rw")})
-        assert r == True
+        assert r is True
         self.logger.info(f"Result: {r}")
-        self.logger.info(f"Complete")
+        self.logger.info("Complete")
         return 0
 
     def core1bootloader(self):
-        self.logger.info(f"Flashing bootloader")
+        self.logger.info("Flashing bootloader")
         cp = CubeProgrammer(self._getCubeParams())
         cp.flashBin("0x08000000", self.args.bootloader)
-        self.logger.info(f"Complete")
+        self.logger.info("Complete")
         cp.resetTarget()
         return 0
 
     def core1firmware(self):
-        self.logger.info(f"Flashing firmware")
+        self.logger.info("Flashing firmware")
         cp = CubeProgrammer(self._getCubeParams())
         cp.flashBin("0x08008000", self.args.firmware)
-        self.logger.info(f"Complete")
+        self.logger.info("Complete")
         cp.resetTarget()
         return 0
 
     def core1(self):
-        self.logger.info(f"Flashing bootloader")
+        self.logger.info("Flashing bootloader")
         cp = CubeProgrammer(self._getCubeParams())
         cp.flashBin("0x08000000", self.args.bootloader)
-        self.logger.info(f"Flashing firmware")
+        self.logger.info("Flashing firmware")
         cp.flashBin("0x08008000", self.args.firmware)
         cp.resetTarget()
-        self.logger.info(f"Complete")
+        self.logger.info("Complete")
         return 0
 
     def core2fus(self):
         if self.args.statement != STATEMENT:
             self.logger.error(
-                f"PLEASE DON'T. THIS FEATURE INTENDED ONLY FOR FACTORY FLASHING"
+                "PLEASE DON'T. THIS FEATURE INTENDED ONLY FOR FACTORY FLASHING"
             )
             return 1
-        self.logger.info(f"Flashing Firmware Update Service")
+        self.logger.info("Flashing Firmware Update Service")
         cp = CubeProgrammer(self._getCubeParams())
         cp.flashCore2(self.args.fus_address, self.args.fus)
-        self.logger.info(f"Complete")
+        self.logger.info("Complete")
         return 0
 
     def core2radio(self):
@@ -163,15 +159,15 @@ class Main(App):
                 f"Radio address not provided, guessed as 0x{radio_address:X}"
             )
         if radio_address > 0x080E0000:
-            self.logger.error(f"I KNOW WHAT YOU DID LAST SUMMER")
+            self.logger.error("I KNOW WHAT YOU DID LAST SUMMER")
             return 1
 
         cp = CubeProgrammer(self._getCubeParams())
-        self.logger.info(f"Removing Current Radio Stack")
+        self.logger.info("Removing Current Radio Stack")
         cp.deleteCore2RadioStack()
-        self.logger.info(f"Flashing Radio Stack")
+        self.logger.info("Flashing Radio Stack")
         cp.flashCore2(radio_address, self.args.radio)
-        self.logger.info(f"Complete")
+        self.logger.info("Complete")
         return 0
 
 

+ 1 - 1
scripts/flipper/app.py

@@ -44,7 +44,7 @@ class App:
         if isinstance(return_code, int):
             return self._exit(return_code)
         else:
-            self.logger.error(f"Missing return code")
+            self.logger.error("Missing return code")
             return self._exit(255)
 
     def _exit(self, code):

+ 4 - 4
scripts/flipper/assets/copro.py

@@ -6,7 +6,7 @@ import xml.etree.ElementTree as ET
 import posixpath
 import os
 
-from flipper.utils import *
+from flipper.utils import file_sha256, timestamp
 from flipper.assets.coprobin import CoproBinary, get_stack_type
 
 
@@ -45,13 +45,13 @@ class Copro:
         cube_manifest = ET.parse(cube_manifest_file)
         cube_package = cube_manifest.find("PackDescription")
         if not cube_package:
-            raise Exception(f"Unknown Cube manifest format")
+            raise Exception("Unknown Cube manifest format")
         cube_version = cube_package.get("Patch") or cube_package.get("Release")
         if not cube_version or not cube_version.startswith("FW.WB"):
-            raise Exception(f"Incorrect Cube package or version info")
+            raise Exception("Incorrect Cube package or version info")
         cube_version = cube_version.replace("FW.WB.", "", 1)
         if cube_version != reference_cube_version:
-            raise Exception(f"Unsupported cube version")
+            raise Exception("Unsupported cube version")
         self.version = cube_version
 
     def _getFileName(self, name):

+ 2 - 1
scripts/flipper/assets/coprobin.py

@@ -1,6 +1,7 @@
 import struct
 import math
-import os, os.path
+import os
+import os.path
 import sys
 
 

+ 5 - 7
scripts/flipper/assets/dolphin.py

@@ -1,13 +1,11 @@
 import multiprocessing
 import logging
 import os
-import sys
-import shutil
 from collections import Counter
 
-from flipper.utils.fff import *
-from flipper.utils.templite import *
-from .icon import *
+from flipper.utils.fff import FlipperFormatFile
+from flipper.utils.templite import Templite
+from .icon import ImageTools, file2image
 
 
 def _convert_image_to_bm(pair: set):
@@ -121,7 +119,7 @@ class DolphinBubbleAnimation:
                 self.meta["Passive frames"] + self.meta["Active frames"]
                 == ordered_frames_count
             )
-        except EOFError as e:
+        except EOFError:
             raise Exception("Invalid meta file: too short")
         except AssertionError as e:
             self.logger.exception(e)
@@ -158,7 +156,7 @@ class DolphinBubbleAnimation:
             except AssertionError as e:
                 self.logger.exception(e)
                 self.logger.error(
-                    f"Animation {self.name} bubble slot {bubble_slot} got incorrect data: {bubble}"
+                    f"Animation {self.name} bubble slot {bubble['Slot']} got incorrect data: {bubble}"
                 )
                 raise Exception("Meta file is invalid: incorrect bubble data")
             except EOFError:

+ 3 - 9
scripts/flipper/assets/icon.py

@@ -1,9 +1,6 @@
 import logging
-import argparse
 import subprocess
 import io
-import os
-import sys
 
 ICONS_SUPPORTED_FORMATS = ["png"]
 
@@ -36,11 +33,8 @@ class ImageTools:
     @staticmethod
     def is_processing_slow():
         try:
-            from PIL import Image, ImageOps
-            import heatshrink2
-
             return False
-        except ImportError as e:
+        except ImportError:
             return True
 
     def __init__(self):
@@ -52,7 +46,7 @@ class ImageTools:
 
         try:
             from PIL import Image, ImageOps
-        except ImportError as e:
+        except ImportError:
             self.__pil_unavailable = True
             self.logger.info("pillow module is missing, using convert cli util")
             return self.png2xbm(file)
@@ -72,7 +66,7 @@ class ImageTools:
 
         try:
             import heatshrink2
-        except ImportError as e:
+        except ImportError:
             self.__hs2_unavailable = True
             self.logger.info("heatshrink2 module is missing, using heatshrink cli util")
             return self.xbm2hs(data)

+ 2 - 3
scripts/flipper/assets/manifest.py

@@ -1,11 +1,10 @@
-import datetime
 import logging
 import os
 import posixpath
 from pathlib import Path
 
-from flipper.utils import *
-from flipper.utils.fstree import *
+from flipper.utils import timestamp, file_md5
+from flipper.utils.fstree import FsNode, compare_fs_trees
 
 MANIFEST_VERSION = 0
 

+ 1 - 3
scripts/flipper/assets/obdata.py

@@ -1,7 +1,5 @@
 #!/usr/bin/env python3
 
-import logging
-import struct
 
 from enum import Enum
 from dataclasses import dataclass
@@ -181,7 +179,7 @@ class OptionBytesData:
 
     def gen_values(self):
         obref = ObReferenceValuesGenerator()
-        converted_refs = list(obref.apply(ob) for ob in self.obs)
+        list(obref.apply(ob) for ob in self.obs)
         return obref
 
 

+ 4 - 4
scripts/flipper/cube.py

@@ -14,7 +14,7 @@ class CubeProgrammer:
         if "port" in config and config["port"]:
             connect.append(f"port={config['port']}")
         else:
-            connect.append(f"port=swd")
+            connect.append("port=swd")
         if "serial" in config and config["serial"]:
             connect.append(f"sn={config['serial']}")
         self.params.append("-c " + " ".join(connect))
@@ -43,20 +43,20 @@ class CubeProgrammer:
         return output.decode()
 
     def getVersion(self):
-        output = self._execute(["--version"])
+        self._execute(["--version"])
 
     def checkOptionBytes(self, option_bytes):
         output = self._execute(["-ob displ"])
         ob_correct = True
         for line in output.split("\n"):
             line = line.strip()
-            if not ":" in line:
+            if ":" not in line:
                 self.logger.debug(f"Skipping line: {line}")
                 continue
             key, data = line.split(":", 1)
             key = key.strip()
             data = data.strip()
-            if not key in option_bytes.keys():
+            if key not in option_bytes.keys():
                 self.logger.debug(f"Skipping key: {key}")
                 continue
             self.logger.debug(f"Processing key: {key} {data}")

+ 2 - 2
scripts/flipper/storage.py

@@ -151,7 +151,7 @@ class FlipperStorage:
             try:
                 # TODO: better decoding, considering non-ascii characters
                 line = line.decode("ascii")
-            except:
+            except Exception:
                 continue
 
             line = line.strip()
@@ -194,7 +194,7 @@ class FlipperStorage:
             try:
                 # TODO: better decoding, considering non-ascii characters
                 line = line.decode("ascii")
-            except:
+            except Exception:
                 continue
 
             line = line.strip()

+ 0 - 1
scripts/flipper/utils/__init__.py

@@ -1,6 +1,5 @@
 import datetime
 import hashlib
-import os
 
 
 def timestamp():

+ 5 - 5
scripts/flipper/utils/programmer_openocd.py

@@ -31,13 +31,13 @@ class OpenOCDProgrammer(Programmer):
         config["interface"] = interface
         config["target"] = "target/stm32wbx.cfg"
 
-        if not serial is None:
+        if serial is not None:
             if interface == "interface/cmsis-dap.cfg":
                 config["serial"] = f"cmsis_dap_serial {serial}"
             elif "stlink" in interface:
                 config["serial"] = f"stlink_serial {serial}"
 
-        if not port_base is None:
+        if port_base is not None:
             config["port_base"] = port_base
 
         self.openocd = OpenOCD(config)
@@ -59,7 +59,7 @@ class OpenOCDProgrammer(Programmer):
             raise Exception(f"File {file_path} not found")
 
         self.openocd.start()
-        self.openocd.send_tcl(f"init")
+        self.openocd.send_tcl("init")
         self.openocd.send_tcl(
             f"program {file_path} 0x{address:08x}{' verify' if verify else ''} reset exit"
         )
@@ -196,7 +196,7 @@ class OpenOCDProgrammer(Programmer):
         if ob_need_to_apply:
             stm32.option_bytes_apply(self.openocd)
         else:
-            self.logger.info(f"Option Bytes are already correct")
+            self.logger.info("Option Bytes are already correct")
 
         # Load Option Bytes
         # That will reset and also lock the Option Bytes and the Flash
@@ -256,7 +256,7 @@ class OpenOCDProgrammer(Programmer):
                     already_written = False
 
             if already_written:
-                self.logger.info(f"OTP memory is already written with the given data")
+                self.logger.info("OTP memory is already written with the given data")
                 return OpenOCDProgrammerResult.Success
 
             self.reset(self.RunMode.Stop)

+ 3 - 3
scripts/flipper/utils/stm32wb55.py

@@ -123,7 +123,7 @@ class STM32WB55:
     def clear_flash_errors(self, oocd: OpenOCD):
         # Errata 2.2.9: Flash OPTVERR flag is always set after system reset
         # And also clear all other flash error flags
-        self.logger.debug(f"Resetting flash errors")
+        self.logger.debug("Resetting flash errors")
         self.FLASH_SR.load(oocd)
         self.FLASH_SR.OP_ERR = 1
         self.FLASH_SR.PROG_ERR = 1
@@ -218,7 +218,7 @@ class STM32WB55:
             raise Exception("Flash lock failed")
 
     def option_bytes_apply(self, oocd: OpenOCD):
-        self.logger.debug(f"Applying Option Bytes")
+        self.logger.debug("Applying Option Bytes")
 
         self.FLASH_CR.load(oocd)
         self.FLASH_CR.OPT_STRT = 1
@@ -228,7 +228,7 @@ class STM32WB55:
         self.flash_wait_for_operation(oocd)
 
     def option_bytes_load(self, oocd: OpenOCD):
-        self.logger.debug(f"Loading Option Bytes")
+        self.logger.debug("Loading Option Bytes")
         self.FLASH_CR.load(oocd)
         self.FLASH_CR.OBL_LAUNCH = 1
         self.FLASH_CR.store(oocd)

+ 2 - 2
scripts/flipper/utils/templite.py

@@ -77,8 +77,8 @@ class TempliteCompiler:
             return
 
         lines = self.block.splitlines()
-        margin = min(len(l) - len(l.lstrip()) for l in lines if l.strip())
-        self.block = "\n".join("\t" * self.offset + l[margin:] for l in lines)
+        margin = min(len(line) - len(line.lstrip()) for line in lines if line.strip())
+        self.block = "\n".join("\t" * self.offset + line[margin:] for line in lines)
         self.blocks.append(self.block)
         if self.block.endswith(":"):
             self.offset += 1

+ 4 - 3
scripts/fwsize.py

@@ -1,10 +1,11 @@
 #!/usr/bin/env python3
 
-from flipper.app import App
-import subprocess
-import os
 import math
+import os
+import subprocess
+
 from ansi.color import fg
+from flipper.app import App
 
 
 class Main(App):

+ 5 - 5
scripts/get_env.py

@@ -1,14 +1,14 @@
 #!/usr/bin/env python3
 
-import ssl
+import argparse
+import datetime
 import json
 import os
-import shlex
+import random
 import re
+import shlex
+import ssl
 import string
-import random
-import argparse
-import datetime
 import urllib.request
 
 

+ 5 - 6
scripts/lint.py

@@ -1,14 +1,13 @@
 #!/usr/bin/env python3
 
+import multiprocessing
 import os
 import re
 import shutil
 import subprocess
-import multiprocessing
 
 from flipper.app import App
 
-
 SOURCE_CODE_FILE_EXTENSIONS = [".h", ".c", ".cpp", ".cxx", ".hpp"]
 SOURCE_CODE_FILE_PATTERN = r"^[0-9A-Za-z_]+\.[a-z]+$"
 SOURCE_CODE_DIR_PATTERN = r"^[0-9A-Za-z_]+$"
@@ -59,7 +58,7 @@ class Main(App):
                         show_message = True
         if show_message:
             self.logger.warning(
-                f"Folders are not renamed automatically, please fix it by yourself"
+                "Folders are not renamed automatically, please fix it by yourself"
             )
 
     def _find_sources(self, folders: list):
@@ -70,7 +69,7 @@ class Main(App):
 
                 for filename in filenames:
                     ext = os.path.splitext(filename.lower())[1]
-                    if not ext in SOURCE_CODE_FILE_EXTENSIONS:
+                    if ext not in SOURCE_CODE_FILE_EXTENSIONS:
                         continue
                     output.append(os.path.join(dirpath, filename))
         return output
@@ -80,7 +79,7 @@ class Main(App):
         try:
             subprocess.check_call(task)
             return True
-        except subprocess.CalledProcessError as e:
+        except subprocess.CalledProcessError:
             return False
 
     def _format_sources(self, sources: list, dry_run: bool = False):
@@ -144,7 +143,7 @@ class Main(App):
 
     def _apply_file_permissions(self, sources: list, dry_run: bool = False):
         execute_permissions = 0o111
-        pattern = re.compile(SOURCE_CODE_FILE_PATTERN)
+        re.compile(SOURCE_CODE_FILE_PATTERN)
         good = []
         bad = []
         # Check sources for unexpected execute permissions

+ 2 - 1
scripts/merge_report_qa.py

@@ -1,9 +1,10 @@
 #!/usr/bin/env python3
 
+import argparse
 import os
 import re
 import sys
-import argparse
+
 from slack_sdk import WebClient
 from slack_sdk.errors import SlackApiError
 

+ 2 - 1
scripts/meta.py

@@ -1,8 +1,9 @@
 #!/usr/bin/env python3
 
-from flipper.app import App
 import json
 
+from flipper.app import App
+
 
 class Main(App):
     def init(self):

+ 2 - 2
scripts/ob.py

@@ -44,7 +44,7 @@ class Main(App):
         )
 
     def check(self):
-        self.logger.info(f"Checking Option Bytes")
+        self.logger.info("Checking Option Bytes")
 
         # OpenOCD
         openocd = OpenOCDProgrammer(
@@ -60,7 +60,7 @@ class Main(App):
         return return_code
 
     def set(self):
-        self.logger.info(f"Setting Option Bytes")
+        self.logger.info("Setting Option Bytes")
 
         # OpenOCD
         openocd = OpenOCDProgrammer(

+ 17 - 20
scripts/otp.py

@@ -1,13 +1,13 @@
 #!/usr/bin/env python3
 
+import datetime
 import logging
-import argparse
-import subprocess
 import os
-import sys
 import re
 import struct
-import datetime
+
+from flipper.app import App
+from flipper.utils.programmer_openocd import OpenOCDProgrammer, OpenOCDProgrammerResult
 
 OTP_MAGIC = 0xBABE
 OTP_VERSION = 0x02
@@ -33,9 +33,6 @@ OTP_DISPLAYS = {
     "mgg": 0x02,
 }
 
-from flipper.app import App
-from flipper.utils.programmer_openocd import OpenOCDProgrammer, OpenOCDProgrammerResult
-
 
 class OTPException(Exception):
     def __init__(self, message: str, result: OpenOCDProgrammerResult):
@@ -158,7 +155,7 @@ class Main(App):
         )
 
     def generate_all(self):
-        self.logger.info(f"Generating OTP")
+        self.logger.info("Generating OTP")
         self._processFirstArgs()
         self._processSecondArgs()
         with open(f"{self.args.file}_first.bin", "wb") as file:
@@ -172,18 +169,18 @@ class Main(App):
         return 0
 
     def flash_first(self):
-        self.logger.info(f"Flashing first block of OTP")
+        self.logger.info("Flashing first block of OTP")
 
         self._processFirstArgs()
 
         filename = f"otp_unknown_first_{self.timestamp}.bin"
 
         try:
-            self.logger.info(f"Packing binary data")
+            self.logger.info("Packing binary data")
             with open(filename, "wb") as file:
                 file.write(self._packFirst())
 
-            self.logger.info(f"Flashing OTP")
+            self.logger.info("Flashing OTP")
 
             openocd = OpenOCDProgrammer(
                 self.args.interface,
@@ -195,7 +192,7 @@ class Main(App):
             if programmer_result != OpenOCDProgrammerResult.Success:
                 raise OTPException("Failed to flash OTP", programmer_result)
 
-            self.logger.info(f"Flashed Successfully")
+            self.logger.info("Flashed Successfully")
         except OTPException as e:
             self.logger.exception(e)
             return e.get_exit_code()
@@ -205,18 +202,18 @@ class Main(App):
         return 0
 
     def flash_second(self):
-        self.logger.info(f"Flashing second block of OTP")
+        self.logger.info("Flashing second block of OTP")
 
         self._processSecondArgs()
 
         filename = f"otp_{self.args.name}_second_{self.timestamp}.bin"
 
         try:
-            self.logger.info(f"Packing binary data")
+            self.logger.info("Packing binary data")
             with open(filename, "wb") as file:
                 file.write(self._packSecond())
 
-            self.logger.info(f"Flashing OTP")
+            self.logger.info("Flashing OTP")
 
             openocd = OpenOCDProgrammer(
                 self.args.interface,
@@ -228,7 +225,7 @@ class Main(App):
             if programmer_result != OpenOCDProgrammerResult.Success:
                 raise OTPException("Failed to flash OTP", programmer_result)
 
-            self.logger.info(f"Flashed Successfully")
+            self.logger.info("Flashed Successfully")
         except OTPException as e:
             self.logger.exception(e)
             return e.get_exit_code()
@@ -238,7 +235,7 @@ class Main(App):
         return 0
 
     def flash_all(self):
-        self.logger.info(f"Flashing OTP")
+        self.logger.info("Flashing OTP")
 
         self._processFirstArgs()
         self._processSecondArgs()
@@ -246,12 +243,12 @@ class Main(App):
         filename = f"otp_{self.args.name}_whole_{self.timestamp}.bin"
 
         try:
-            self.logger.info(f"Packing binary data")
+            self.logger.info("Packing binary data")
             with open(filename, "wb") as file:
                 file.write(self._packFirst())
                 file.write(self._packSecond())
 
-            self.logger.info(f"Flashing OTP")
+            self.logger.info("Flashing OTP")
 
             openocd = OpenOCDProgrammer(
                 self.args.interface,
@@ -263,7 +260,7 @@ class Main(App):
             if programmer_result != OpenOCDProgrammerResult.Success:
                 raise OTPException("Failed to flash OTP", programmer_result)
 
-            self.logger.info(f"Flashed Successfully")
+            self.logger.info("Flashed Successfully")
         except OTPException as e:
             self.logger.exception(e)
             return e.get_exit_code()

+ 7 - 7
scripts/program.py

@@ -1,13 +1,13 @@
 #!/usr/bin/env python3
-import typing
-import subprocess
 import logging
-import time
 import os
 import socket
-
+import subprocess
+import time
+import typing
 from abc import ABC, abstractmethod
 from dataclasses import dataclass
+
 from flipper.app import App
 
 
@@ -223,7 +223,7 @@ class BlackmagicProgrammer(Programmer):
         try:
             socket.inet_aton(address)
             return True
-        except:
+        except Exception:
             return False
 
     def set_serial(self, serial: str):
@@ -415,12 +415,12 @@ class Main(App):
             if len(interfaces) == 0:
                 interfaces = [p for p in network_programmers if p.get_name() == i_name]
         else:
-            self.logger.info(f"Probing for interfaces...")
+            self.logger.info("Probing for interfaces...")
             interfaces = self._search_interface(self.args.serial)
 
             if len(interfaces) == 0:
                 # Probe network blackmagic
-                self.logger.info(f"Probing for network interfaces...")
+                self.logger.info("Probing for network interfaces...")
                 interfaces = self._search_network_interface(self.args.serial)
 
             if len(interfaces) == 0:

+ 5 - 7
scripts/runfap.py

@@ -1,14 +1,12 @@
 #!/usr/bin/env python3
 
+import operator
+from functools import reduce
+
 from flipper.app import App
 from flipper.storage import FlipperStorage, FlipperStorageOperations
 from flipper.utils.cdc import resolve_port
 
-import os
-import posixpath
-from functools import reduce
-import operator
-
 
 class Main(App):
     def init(self):
@@ -38,8 +36,8 @@ class Main(App):
         self.parser.set_defaults(func=self.install)
 
     @staticmethod
-    def flatten(l):
-        return reduce(operator.concat, l, [])
+    def flatten(item_list):
+        return reduce(operator.concat, item_list, [])
 
     def install(self):
         self.args.sources = self.flatten(self.args.sources)

+ 1 - 1
scripts/sconsdist.py

@@ -5,7 +5,7 @@ import shutil
 import tarfile
 import zipfile
 from os import makedirs, walk
-from os.path import exists, join, relpath, basename, split
+from os.path import basename, exists, join, relpath
 
 from ansi.color import fg
 from flipper.app import App

+ 5 - 7
scripts/selfupdate.py

@@ -1,14 +1,12 @@
 #!/usr/bin/env python3
 
-from typing import final
-from flipper.app import App
-from flipper.storage import FlipperStorage, FlipperStorageOperations
-from flipper.utils.cdc import resolve_port
-
 import logging
 import os
 import pathlib
-import serial.tools.list_ports as list_ports
+
+from flipper.app import App
+from flipper.storage import FlipperStorage, FlipperStorageOperations
+from flipper.utils.cdc import resolve_port
 
 
 class Main(App):
@@ -54,7 +52,7 @@ class Main(App):
                     f"update install {flipper_update_path}/{manifest_name}\r"
                 )
                 result = storage.read.until(storage.CLI_EOL)
-                if not b"Verifying" in result:
+                if b"Verifying" not in result:
                     self.logger.error(f"Unexpected response: {result.decode('ascii')}")
                     return 3
                 result = storage.read.until(storage.CLI_EOL)

+ 3 - 2
scripts/serial_cli.py

@@ -1,9 +1,10 @@
 import logging
-import subprocess
-from flipper.utils.cdc import resolve_port
 import os
+import subprocess
 import sys
 
+from flipper.utils.cdc import resolve_port
+
 
 def main():
     logger = logging.getLogger()

+ 6 - 6
scripts/storage.py

@@ -1,14 +1,14 @@
 #!/usr/bin/env python3
 
-from flipper.app import App
-from flipper.storage import FlipperStorage, FlipperStorageOperations
-from flipper.utils.cdc import resolve_port
-
-import os
 import binascii
 import filecmp
+import os
 import tempfile
 
+from flipper.app import App
+from flipper.storage import FlipperStorage, FlipperStorageOperations
+from flipper.utils.cdc import resolve_port
+
 
 def WrapStorageOp(func):
     def wrapper(*args, **kwargs):
@@ -122,7 +122,7 @@ class Main(App):
             try:
                 print("Text data:")
                 print(data.decode())
-            except:
+            except Exception:
                 print("Binary hexadecimal data:")
                 print(binascii.hexlify(data).decode())
 

+ 25 - 12
scripts/ufbt/SConstruct

@@ -1,7 +1,6 @@
 from SCons.Platform import TempFileMunge
 from SCons.Node import FS
 from SCons.Errors import UserError
-from SCons.Warnings import warn, WarningOnByDefault
 
 
 import os
@@ -14,6 +13,7 @@ SetOption("max_drift", 1)
 
 ufbt_state_dir = Dir(os.environ.get("UFBT_STATE_DIR", "#.ufbt"))
 ufbt_script_dir = Dir(os.environ.get("UFBT_SCRIPT_DIR"))
+ufbt_build_dir = ufbt_state_dir.Dir("build")
 
 ufbt_current_sdk_dir = ufbt_state_dir.Dir("current")
 
@@ -63,16 +63,7 @@ core_env = Environment(
     ],
 )
 
-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)
+core_env.Append(CPPDEFINES=GetOption("extra_defines"))
 
 # Now we can import stuff bundled with SDK - it was added to sys.path by ufbt_state
 
@@ -109,7 +100,7 @@ env = core_env.Clone(
         "fbt_assets",
         ("compilation_db", {"COMPILATIONDB_COMSTR": "\tCDB\t${TARGET}"}),
     ],
-    FBT_FAP_DEBUG_ELF_ROOT=ufbt_state_dir.Dir("build"),
+    FBT_FAP_DEBUG_ELF_ROOT=ufbt_build_dir,
     TEMPFILE=TempFileMunge,
     MAXLINELENGTH=2048,
     PROGSUFFIX=".elf",
@@ -427,3 +418,25 @@ dist_env.PhonyTarget(
     "get_apiversion",
     "@echo $( ${UFBT_API_VERSION} $)",
 )
+
+# Dolphin animation builder. Expects "external" directory in current dir
+# with animation sources & manifests. Builds & uploads them to connected Flipper
+dolphin_src_dir = original_app_dir.Dir("external")
+if dolphin_src_dir.exists():
+    dolphin_dir = ufbt_build_dir.Dir("dolphin")
+    dolphin_external = dist_env.DolphinExtBuilder(
+        ufbt_build_dir.Dir("dolphin"),
+        original_app_dir,
+        DOLPHIN_RES_TYPE="external",
+    )
+    dist_env.PhonyTarget(
+        "dolphin_ext",
+        '${PYTHON3} ${FBT_SCRIPT_DIR}/storage.py send "${SOURCE}" /ext/dolphin',
+        source=ufbt_build_dir.Dir("dolphin"),
+    )
+else:
+
+    def missing_dolphin_folder(**kw):
+        raise UserError(f"Dolphin folder not found: {dolphin_src_dir}")
+
+    dist_env.PhonyTarget("dolphin_ext", Action(missing_dolphin_folder, None))

+ 9 - 23
scripts/ufbt/commandline.scons

@@ -1,32 +1,18 @@
 AddOption(
-    "--proxy-env",
-    action="store",
-    dest="proxy_env",
-    default="",
-    help="Comma-separated list of additional environment variables to pass to child SCons processes",
+    "--extra-define",
+    action="append",
+    dest="extra_defines",
+    default=[],
+    help="Extra global define that will be passed to C/C++ compiler, can be specified multiple times",
 )
 
 AddOption(
-    "--channel",
+    "--proxy-env",
     action="store",
-    dest="sdk_channel",
-    choices=["dev", "rc", "release"],
+    dest="proxy_env",
     default="",
-    help="Release channel to use for SDK",
-)
-
-AddOption(
-    "--branch",
-    action="store",
-    dest="sdk_branch",
-    help="Custom main repo branch to use for SDK",
-)
-
-AddOption(
-    "--hw-target",
-    action="store",
-    dest="sdk_target",
-    help="SDK Hardware target",
+    help="Comma-separated list of additional environment variables to pass to "
+    "child SCons processes",
 )
 
 vars = Variables("ufbt_options.py", ARGUMENTS)

+ 3 - 3
scripts/ufbt/site_init.py

@@ -1,8 +1,8 @@
-from SCons.Script import GetBuildFailures
-import SCons.Errors
-
 import atexit
+
+import SCons.Errors
 from ansi.color import fg, fx
+from SCons.Script import GetBuildFailures
 
 
 def bf_to_str(bf):

+ 3 - 4
scripts/ufbt/site_tools/ufbt_state.py

@@ -1,12 +1,11 @@
-from SCons.Errors import StopError
-from SCons.Warnings import warn, WarningOnByDefault
-
 import json
 import os
-import sys
 import pathlib
+import sys
 from functools import reduce
 
+from SCons.Errors import StopError
+
 
 def _load_sdk_data(sdk_root):
     split_vars = {

+ 0 - 37
scripts/ufbt/update.scons

@@ -1,37 +0,0 @@
-from SCons.Errors import StopError
-
-Import("core_env")
-
-update_env = core_env.Clone(
-    toolpath=[core_env["FBT_SCRIPT_DIR"].Dir("fbt_tools")],
-    tools=["python3"],
-)
-print("Updating SDK...")
-ufbt_state = update_env["UFBT_STATE"]
-
-update_args = [
-    "--ufbt-dir",
-    f'"{update_env["UFBT_STATE_DIR"]}"',
-]
-
-if branch_name := GetOption("sdk_branch"):
-    update_args.extend(["--branch", branch_name])
-elif channel_name := GetOption("sdk_channel"):
-    update_args.extend(["--channel", channel_name])
-elif branch_name := ufbt_state.get("branch", None):
-    update_args.extend(["--branch", branch_name])
-elif channel_name := ufbt_state.get("channel", None):
-    update_args.extend(["--channel", channel_name])
-else:
-    raise StopError("No branch or channel specified for SDK update")
-
-if hw_target := GetOption("sdk_target"):
-    update_args.extend(["--hw-target", hw_target])
-else:
-    update_args.extend(["--hw-target", ufbt_state["hw_target"]])
-
-update_env.Replace(UPDATE_ARGS=update_args)
-result = update_env.Execute(
-    update_env.subst('$PYTHON3 "$UFBT_BOOTSTRAP_SCRIPT" $UPDATE_ARGS'),
-)
-Exit(result)

+ 10 - 10
scripts/update.py

@@ -1,16 +1,16 @@
 #!/usr/bin/env python3
 
-from flipper.app import App
-from flipper.utils.fff import FlipperFormatFile
-from flipper.assets.coprobin import CoproBinary, get_stack_type
-from flipper.assets.obdata import OptionBytesData, ObReferenceValues
-from os.path import basename, join, exists
+import math
 import os
 import shutil
-import zlib
 import tarfile
-import math
+import zlib
+from os.path import exists, join
 
+from flipper.app import App
+from flipper.assets.coprobin import CoproBinary, get_stack_type
+from flipper.assets.obdata import ObReferenceValues, OptionBytesData
+from flipper.utils.fff import FlipperFormatFile
 from slideshow import Main as SlideshowMain
 
 
@@ -267,9 +267,9 @@ class Main(App):
 
     @staticmethod
     def batch(iterable, n=1):
-        l = len(iterable)
-        for ndx in range(0, l, n):
-            yield iterable[ndx : min(ndx + n, l)]
+        iterable_len = len(iterable)
+        for ndx in range(0, iterable_len, n):
+            yield iterable[ndx : min(ndx + n, iterable_len)]
 
 
 if __name__ == "__main__":

+ 4 - 4
scripts/version.py

@@ -1,12 +1,12 @@
 #!/usb/bin/env python3
 
-from flipper.app import App
-
-import subprocess
-import os
 import json
+import os
+import subprocess
 from datetime import date, datetime
 
+from flipper.app import App
+
 
 class GitVersion:
     REVISION_SUFFIX_LENGTH = 8

+ 2 - 2
site_scons/extapps.scons

@@ -1,12 +1,12 @@
 from dataclasses import dataclass, field
+from fbt.appmanifest import FlipperAppType
 
 from SCons.Node import NodeList
 from SCons.Warnings import warn, WarningOnByDefault
-from SCons.Errors import UserError
+
 
 Import("ENV")
 
-from fbt.appmanifest import FlipperAppType
 
 appenv = ENV["APPENV"] = ENV.Clone(
     tools=[