Explorar el Código

fbt: 'target' field for apps; lib debugging support (#1995)

* fbt: added 'target' field to application manifest
* fbt: earlier pagination setup for gdb
* fbt: added LIB_DEBUG flag
* fbt: sdk: added SDK_MAP_FILE_SUBST
hedger hace 3 años
padre
commit
a959fa32bc

+ 3 - 6
SConstruct

@@ -7,6 +7,7 @@
 # construction of certain targets behind command-line options.
 
 import os
+from fbt.util import path_as_posix
 
 DefaultEnvironment(tools=[])
 
@@ -200,9 +201,7 @@ firmware_debug = distenv.PhonyTarget(
     source=firmware_env["FW_ELF"],
     GDBOPTS="${GDBOPTS_BASE}",
     GDBREMOTE="${OPENOCD_GDB_PIPE}",
-    FBT_FAP_DEBUG_ELF_ROOT=firmware_env.subst("$FBT_FAP_DEBUG_ELF_ROOT").replace(
-        "\\", "/"
-    ),
+    FBT_FAP_DEBUG_ELF_ROOT=path_as_posix(firmware_env.subst("$FBT_FAP_DEBUG_ELF_ROOT")),
 )
 distenv.Depends(firmware_debug, firmware_flash)
 
@@ -212,9 +211,7 @@ distenv.PhonyTarget(
     source=firmware_env["FW_ELF"],
     GDBOPTS="${GDBOPTS_BASE} ${GDBOPTS_BLACKMAGIC}",
     GDBREMOTE="${BLACKMAGIC_ADDR}",
-    FBT_FAP_DEBUG_ELF_ROOT=firmware_env.subst("$FBT_FAP_DEBUG_ELF_ROOT").replace(
-        "\\", "/"
-    ),
+    FBT_FAP_DEBUG_ELF_ROOT=path_as_posix(firmware_env.subst("$FBT_FAP_DEBUG_ELF_ROOT")),
 )
 
 # Debug alien elf

+ 1 - 0
documentation/AppManifests.md

@@ -40,6 +40,7 @@ Only 2 parameters are mandatory: ***appid*** and ***apptype***, others are optio
 * **icon**: Animated icon name from built-in assets to be used when building app as a part of firmware.
 * **order**: Order of an application within its group when sorting entries in it. The lower the order is, the closer to the start of the list the item is placed. *Used for ordering startup hooks and menu entries.* 
 * **sdk_headers**: List of C header files from this app's code to include in API definitions for external applications.
+* **targets**: list of strings, target names, which this application is compatible with. If not specified, application is built for all targets. Default value is `["all"]`.
 
 
 #### Parameters for external applications

+ 2 - 2
firmware.scons

@@ -40,11 +40,11 @@ env = ENV.Clone(
     FW_LIB_OPTS={
         "Default": {
             "CCFLAGS": [
-                "-Os",
+                "-Og" if ENV["LIB_DEBUG"] else "-Os",
             ],
             "CPPDEFINES": [
                 "NDEBUG",
-                "FURI_NDEBUG",
+                "FURI_DEBUG" if ENV["LIB_DEBUG"] else "FURI_NDEBUG",
             ],
             # You can add other entries named after libraries
             # If they are present, they have precedence over Default

+ 17 - 3
scripts/fbt/appmanifest.py

@@ -52,6 +52,8 @@ class FlipperApplication:
     icon: Optional[str] = None
     order: int = 0
     sdk_headers: List[str] = field(default_factory=list)
+    targets: List[str] = field(default_factory=lambda: ["all"])
+
     # .fap-specific
     sources: List[str] = field(default_factory=lambda: ["*.c*"])
     fap_version: Tuple[int] = field(default_factory=lambda: (0, 1))
@@ -135,8 +137,8 @@ class AppManager:
             raise FlipperManifestException(f"Duplicate app declaration: {app.appid}")
         self.known_apps[app.appid] = app
 
-    def filter_apps(self, applist: List[str]):
-        return AppBuildset(self, applist)
+    def filter_apps(self, applist: List[str], hw_target: str):
+        return AppBuildset(self, applist, hw_target)
 
 
 class AppBuilderException(Exception):
@@ -155,11 +157,13 @@ class AppBuildset:
         FlipperAppType.STARTUP,
     )
 
-    def __init__(self, appmgr: AppManager, appnames: List[str]):
+    def __init__(self, appmgr: AppManager, appnames: List[str], hw_target: str):
         self.appmgr = appmgr
         self.appnames = set(appnames)
+        self.hw_target = hw_target
         self._orig_appnames = appnames
         self._process_deps()
+        self._filter_by_target()
         self._check_conflicts()
         self._check_unsatisfied()  # unneeded?
         self.apps = sorted(
@@ -170,6 +174,16 @@ class AppBuildset:
     def _is_missing_dep(self, dep_name: str):
         return dep_name not in self.appnames
 
+    def _filter_by_target(self):
+        for appname in self.appnames.copy():
+            app = self.appmgr.get(appname)
+            # if app.apptype not in self.BUILTIN_APP_TYPES:
+            if not any(map(lambda t: t in app.targets, ["all", self.hw_target])):
+                print(
+                    f"Removing {appname} due to target mismatch (building for {self.hw_target}, app supports {app.targets}"
+                )
+                self.appnames.remove(appname)
+
     def _process_deps(self):
         while True:
             provided = []

+ 6 - 1
scripts/fbt/util.py

@@ -1,7 +1,6 @@
 import SCons
 from SCons.Subst import quote_spaces
 from SCons.Errors import StopError
-from SCons.Node.FS import _my_normcase
 
 import re
 import os
@@ -58,3 +57,9 @@ def extract_abs_dir_path(node):
         raise StopError(f"Can't find absolute path for {node.name}")
 
     return abs_dir_node.abspath
+
+
+def path_as_posix(path):
+    if SCons.Platform.platform_default() == "win32":
+        return path.replace(os.path.sep, os.path.altsep)
+    return path

+ 7 - 6
scripts/fbt_tools/fbt_apps.py

@@ -1,7 +1,6 @@
 from SCons.Builder import Builder
 from SCons.Action import Action
 from SCons.Warnings import warn, WarningOnByDefault
-import SCons
 from ansi.color import fg
 
 from fbt.appmanifest import (
@@ -33,14 +32,12 @@ def LoadAppManifest(env, entry):
 
 
 def PrepareApplicationsBuild(env):
-    appbuild = env["APPBUILD"] = env["APPMGR"].filter_apps(env["APPS"])
+    appbuild = env["APPBUILD"] = env["APPMGR"].filter_apps(
+        env["APPS"], env.subst("f${TARGET_HW}")
+    )
     env.Append(
         SDK_HEADERS=appbuild.get_sdk_headers(),
     )
-    env["APPBUILD_DUMP"] = env.Action(
-        DumpApplicationConfig,
-        "\tINFO\t",
-    )
 
 
 def DumpApplicationConfig(target, source, env):
@@ -68,6 +65,10 @@ def generate(env):
     env.AddMethod(PrepareApplicationsBuild)
     env.SetDefault(
         APPMGR=AppManager(),
+        APPBUILD_DUMP=env.Action(
+            DumpApplicationConfig,
+            "\tINFO\t",
+        ),
     )
 
     env.Append(

+ 2 - 2
scripts/fbt_tools/fbt_debugopts.py

@@ -41,12 +41,12 @@ def generate(env, **kw):
             "|openocd -c 'gdb_port pipe; log_output ${FBT_DEBUG_DIR}/openocd.log' ${[SINGLEQUOTEFUNC(OPENOCD_OPTS)]}"
         ],
         GDBOPTS_BASE=[
+            "-ex",
+            "set pagination off",
             "-ex",
             "target extended-remote ${GDBREMOTE}",
             "-ex",
             "set confirm off",
-            "-ex",
-            "set pagination off",
         ],
         GDBOPTS_BLACKMAGIC=[
             "-ex",

+ 14 - 8
scripts/fbt_tools/fbt_sdk.py

@@ -14,6 +14,7 @@ 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):
@@ -52,6 +53,8 @@ def prebuild_sdk_create_origin_file(target, source, env):
 
 
 class SdkMeta:
+    MAP_FILE_SUBST = "SDK_MAP_FILE_SUBST"
+
     def __init__(self, env, tree_builder: "SdkTreeBuilder"):
         self.env = env
         self.treebuilder = tree_builder
@@ -67,6 +70,7 @@ class SdkMeta:
             "linker_libs": self.env.subst("${LIBS}"),
             "app_ep_subst": self.env.subst("${APP_ENTRY}"),
             "sdk_path_subst": self.env.subst("${SDK_DIR_SUBST}"),
+            "map_file_subst": self.MAP_FILE_SUBST,
             "hardware": self.env.subst("${TARGET_HW}"),
         }
         with open(json_manifest_path, "wt") as f:
@@ -75,9 +79,9 @@ class SdkMeta:
     def _wrap_scons_vars(self, vars: str):
         expanded_vars = self.env.subst(
             vars,
-            target=Entry("dummy"),
+            target=Entry(self.MAP_FILE_SUBST),
         )
-        return expanded_vars.replace("\\", "/")
+        return path_as_posix(expanded_vars)
 
 
 class SdkTreeBuilder:
@@ -142,13 +146,15 @@ class SdkTreeBuilder:
         meta.save_to(self.target[0].path)
 
     def build_sdk_file_path(self, orig_path: str) -> str:
-        return posixpath.normpath(
-            posixpath.join(
-                self.SDK_DIR_SUBST,
-                self.target_sdk_dir_name,
-                orig_path,
+        return path_as_posix(
+            posixpath.normpath(
+                posixpath.join(
+                    self.SDK_DIR_SUBST,
+                    self.target_sdk_dir_name,
+                    orig_path,
+                )
             )
-        ).replace("\\", "/")
+        )
 
     def emitter(self, target, source, env):
         target_folder = target[0]

+ 5 - 0
site_scons/commandline.scons

@@ -63,6 +63,11 @@ vars.AddVariables(
         help="Enable debug build",
         default=True,
     ),
+    BoolVariable(
+        "LIB_DEBUG",
+        help="Enable debug build for libraries",
+        default=False,
+    ),
     BoolVariable(
         "COMPACT",
         help="Optimize for size",

+ 9 - 1
site_scons/extapps.scons

@@ -1,6 +1,6 @@
 from dataclasses import dataclass, field
-from SCons.Errors import UserError
 from SCons.Node import NodeList
+from SCons.Warnings import warn, WarningOnByDefault
 
 
 Import("ENV")
@@ -80,6 +80,14 @@ if extra_app_list := GetOption("extra_ext_apps"):
     known_extapps.extend(map(appenv["APPMGR"].get, extra_app_list.split(",")))
 
 for app in known_extapps:
+    if not any(map(lambda t: t in app.targets, ["all", appenv.subst("f${TARGET_HW}")])):
+        warn(
+            WarningOnByDefault,
+            f"Can't build '{app.name}' (id '{app.appid}'): target mismatch"
+            f" (building for {appenv.subst('f${TARGET_HW}')}, app supports {app.targets}",
+        )
+        continue
+
     appenv.BuildAppElf(app)