fbt: improvements (#3217)

* fbt: changed cdefines & lib handling for external apps; added extra checks for app manifest fields; moved around AppsC generator
* fbt: commandline fixes for spaces in paths
* fbt: fixed stringification for FAP_VERSION
* fbt: Removed excessive quoting for gdb
* docs: update for cdefines; fbt: typo fix
* fbt: enforcing at least 2 components in app version=

Co-authored-by: あく <alleteam@gmail.com>
This commit is contained in:
hedger 2023-11-15 20:27:35 +04:00 committed by GitHub
parent 457aa5331f
commit 98d5718ec9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
31 changed files with 271 additions and 166 deletions

View File

@ -172,17 +172,19 @@ Alias("fap_dist", fap_dist)
fap_deploy = distenv.PhonyTarget(
"fap_deploy",
[
Action(
[
"${PYTHON3}",
"${FBT_SCRIPT_DIR}/storage.py",
"-p",
"${FLIP_PORT}",
"send",
"${SOURCE}",
"/ext/apps",
[
"${PYTHON3}",
"${FBT_SCRIPT_DIR}/storage.py",
"-p",
"${FLIP_PORT}",
"send",
"${SOURCE}",
"/ext/apps",
]
]
],
),
source=firmware_env.Dir(("${RESOURCES_ROOT}/apps")),
)
Depends(fap_deploy, firmware_env["FW_RESOURCES_MANIFEST"])
@ -261,7 +263,7 @@ distenv.PhonyTarget(
distenv.PhonyTarget(
"debug_other_blackmagic",
"${GDBPYCOM}",
GDBOPTS="${GDBOPTS_BASE} ${GDBOPTS_BLACKMAGIC}",
GDBOPTS="${GDBOPTS_BASE} ${GDBOPTS_BLACKMAGIC}",
GDBREMOTE="${BLACKMAGIC_ADDR}",
GDBPYOPTS=debug_other_opts,
)
@ -276,13 +278,13 @@ distenv.PhonyTarget(
# Linter
distenv.PhonyTarget(
"lint",
"${PYTHON3} ${FBT_SCRIPT_DIR}/lint.py check ${LINT_SOURCES}",
[["${PYTHON3}", "${FBT_SCRIPT_DIR}/lint.py", "check", "${LINT_SOURCES}"]],
LINT_SOURCES=[n.srcnode() for n in firmware_env["LINT_SOURCES"]],
)
distenv.PhonyTarget(
"format",
"${PYTHON3} ${FBT_SCRIPT_DIR}/lint.py format ${LINT_SOURCES}",
[["${PYTHON3}", "${FBT_SCRIPT_DIR}/lint.py", "format", "${LINT_SOURCES}"]],
LINT_SOURCES=[n.srcnode() for n in firmware_env["LINT_SOURCES"]],
)
@ -323,10 +325,14 @@ distenv.PhonyTarget(
)
# Start Flipper CLI via PySerial's miniterm
distenv.PhonyTarget("cli", "${PYTHON3} ${FBT_SCRIPT_DIR}/serial_cli.py -p ${FLIP_PORT}")
distenv.PhonyTarget(
"cli", [["${PYTHON3}", "${FBT_SCRIPT_DIR}/serial_cli.py", "-p", "${FLIP_PORT}"]]
)
# Update WiFi devboard firmware
distenv.PhonyTarget("devboard_flash", "${PYTHON3} ${FBT_SCRIPT_DIR}/wifi_board.py")
distenv.PhonyTarget(
"devboard_flash", [["${PYTHON3}", "${FBT_SCRIPT_DIR}/wifi_board.py"]]
)
# Find blackmagic probe
@ -361,5 +367,5 @@ distenv.Alias("vscode_dist", vscode_dist)
# Configure shell with build tools
distenv.PhonyTarget(
"env",
"@echo $( ${FBT_SCRIPT_DIR}/toolchain/fbtenv.sh $)",
"@echo $( ${FBT_SCRIPT_DIR.abspath}/toolchain/fbtenv.sh $)",
)

View File

@ -4,7 +4,6 @@ App(
apptype=FlipperAppType.DEBUG,
targets=["f7"],
entry_point="accessor_app",
cdefines=["APP_ACCESSOR"],
requires=["gui"],
stack_size=4 * 1024,
order=40,

View File

@ -3,7 +3,6 @@ App(
name="Battery Test",
apptype=FlipperAppType.DEBUG,
entry_point="battery_test_app",
cdefines=["APP_BATTERY_TEST"],
requires=[
"gui",
"power",

View File

@ -3,7 +3,6 @@ App(
name="Blink Test",
apptype=FlipperAppType.DEBUG,
entry_point="blink_test_app",
cdefines=["APP_BLINK"],
requires=["gui"],
stack_size=1 * 1024,
order=10,

View File

@ -3,7 +3,6 @@ App(
name="CCID Debug",
apptype=FlipperAppType.DEBUG,
entry_point="ccid_test_app",
cdefines=["CCID_TEST"],
requires=[
"gui",
],

View File

@ -3,7 +3,6 @@ App(
name="Crash Test",
apptype=FlipperAppType.DEBUG,
entry_point="crash_test_app",
cdefines=["APP_CRASH_TEST"],
requires=["gui"],
stack_size=1 * 1024,
fap_category="Debug",

View File

@ -3,7 +3,6 @@ App(
name="Display Test",
apptype=FlipperAppType.DEBUG,
entry_point="display_test_app",
cdefines=["APP_DISPLAY_TEST"],
requires=["gui"],
fap_libs=["misc"],
stack_size=1 * 1024,

View File

@ -3,7 +3,6 @@ App(
name="File Browser Test",
apptype=FlipperAppType.DEBUG,
entry_point="file_browser_app",
cdefines=["APP_FILE_BROWSER_TEST"],
requires=["gui"],
stack_size=2 * 1024,
order=150,

View File

@ -3,7 +3,6 @@ App(
name="Keypad Test",
apptype=FlipperAppType.DEBUG,
entry_point="keypad_test_app",
cdefines=["APP_KEYPAD_TEST"],
requires=["gui"],
stack_size=1 * 1024,
order=30,

View File

@ -3,7 +3,6 @@ App(
name="Locale Test",
apptype=FlipperAppType.DEBUG,
entry_point="locale_test_app",
cdefines=["APP_LOCALE"],
requires=["gui", "locale"],
stack_size=2 * 1024,
order=70,

View File

@ -3,7 +3,6 @@ App(
name="Text Box Test",
apptype=FlipperAppType.DEBUG,
entry_point="text_box_test_app",
cdefines=["APP_TEXT_BOX_TEST"],
requires=["gui"],
stack_size=1 * 1024,
order=140,

View File

@ -3,7 +3,6 @@ App(
name="UART Echo",
apptype=FlipperAppType.DEBUG,
entry_point="uart_echo_app",
cdefines=["APP_UART_ECHO"],
requires=["gui"],
stack_size=2 * 1024,
order=70,

View File

@ -3,7 +3,6 @@ App(
name="USB Mouse Demo",
apptype=FlipperAppType.DEBUG,
entry_point="usb_mouse_app",
cdefines=["APP_USB_MOUSE"],
requires=["gui"],
stack_size=1 * 1024,
order=60,

View File

@ -3,7 +3,6 @@ App(
name="USB Test",
apptype=FlipperAppType.DEBUG,
entry_point="usb_test_app",
cdefines=["APP_USB_TEST"],
requires=["gui"],
stack_size=1 * 1024,
order=50,

View File

@ -3,7 +3,6 @@ App(
name="Vibro Test",
apptype=FlipperAppType.DEBUG,
entry_point="vibro_test_app",
cdefines=["APP_VIBRO_TEST"],
requires=["gui"],
stack_size=1 * 1024,
order=20,

View File

@ -32,7 +32,7 @@ Only two parameters are mandatory: **_appid_** and **_apptype_**. Others are opt
- **name**: name displayed in menus.
- **entry_point**: C function to be used as the application's entry point. Note that C++ function names are mangled, so you need to wrap them in `extern "C"` to use them as entry points.
- **flags**: internal flags for system apps. Do not use.
- **cdefines**: C preprocessor definitions to declare globally for other apps when the current application is included in the active build configuration.
- **cdefines**: C preprocessor definitions to declare globally for other apps when the current application is included in the active build configuration. **For external applications**: specified definitions are used when building the application itself.
- **requires**: list of application IDs to include in the build configuration when the current application is referenced in the list of applications to build.
- **conflicts**: list of application IDs with which the current application conflicts. If any of them is found in the constructed application list, **`fbt`** will abort the firmware build process.
- **provides**: functionally identical to **_requires_** field.

View File

@ -219,7 +219,7 @@ AddPostAction(fwelf, fwenv["APPBUILD_DUMP"])
AddPostAction(
fwelf,
Action(
'${PYTHON3} "${BIN_SIZE_SCRIPT}" elf ${TARGET}',
[["${PYTHON3}", "${BIN_SIZE_SCRIPT}", "elf", "${TARGET}"]],
"Firmware size",
),
)
@ -229,7 +229,7 @@ fwhex = fwenv["FW_HEX"] = fwenv.HEXBuilder("${FIRMWARE_BUILD_CFG}")
fwbin = fwenv["FW_BIN"] = fwenv.BINBuilder("${FIRMWARE_BUILD_CFG}")
AddPostAction(
fwbin,
Action('@${PYTHON3} "${BIN_SIZE_SCRIPT}" bin ${TARGET}'),
Action([["@${PYTHON3}", "${BIN_SIZE_SCRIPT}", "bin", "${TARGET}"]]),
)
fwdfu = fwenv["FW_DFU"] = fwenv.DFUBuilder("${FIRMWARE_BUILD_CFG}")

View File

@ -33,6 +33,8 @@ class FlipperAppType(Enum):
@dataclass
class FlipperApplication:
APP_ID_REGEX: ClassVar[re.Pattern] = re.compile(r"^[a-z0-9_]+$")
PRIVATE_FIELD_PREFIX: ClassVar[str] = "_"
APP_MANIFEST_DEFAULT_NAME: ClassVar[str] = "application.fam"
@dataclass
class ExternallyBuiltFile:
@ -48,8 +50,6 @@ class FlipperApplication:
cdefines: List[str] = field(default_factory=list)
cincludes: List[str] = field(default_factory=list)
PRIVATE_FIELD_PREFIX = "_"
appid: str
apptype: FlipperAppType
name: Optional[str] = ""
@ -117,8 +117,10 @@ class FlipperApplication:
self.fap_version = tuple(int(v) for v in self.fap_version.split("."))
except ValueError:
raise FlipperManifestException(
f"Invalid version string '{self.fap_version}'. Must be in the form 'major.minor'"
f"Invalid version '{self.fap_version}'. Must be in the form 'major.minor'"
)
if len(self.fap_version) < 2:
raise ValueError("Not enough version components")
class AppManager:
@ -155,11 +157,20 @@ class AppManager:
raise FlipperManifestException(
f"App {kw.get('appid')} cannot have fal_embedded set"
)
# Harmless - cdefines for external apps are meaningless
# if apptype == FlipperAppType.EXTERNAL and kw.get("cdefines"):
# raise FlipperManifestException(
# f"External app {kw.get('appid')} must not have 'cdefines' in manifest"
# )
if apptype in AppBuildset.dist_app_types:
# For distributing .fap's resources, there's "fap_file_assets"
for app_property in ("resources",):
if kw.get(app_property):
raise FlipperManifestException(
f"App {kw.get('appid')} of type {apptype} cannot have '{app_property}' in manifest"
)
else:
for app_property in ("fap_extbuild", "fap_private_libs", "fap_icon_assets"):
if kw.get(app_property):
raise FlipperManifestException(
f"App {kw.get('appid')} of type {apptype} must not have '{app_property}' in manifest"
)
def load_manifest(self, app_manifest_path: str, app_dir_node: object):
if not os.path.exists(app_manifest_path):
@ -241,12 +252,21 @@ class AppBuildset:
FlipperAppType.STARTUP,
)
EXTERNAL_APP_TYPES_MAP = {
# AppType -> bool: true if always deploy, false if obey app set
FlipperAppType.EXTERNAL: True,
FlipperAppType.PLUGIN: True,
FlipperAppType.DEBUG: True,
FlipperAppType.MENUEXTERNAL: False,
}
@classmethod
@property
def dist_app_types(cls):
"""Applications that are installed on SD card"""
return list(
entry[0] for entry in cls.EXTERNAL_APP_TYPES_MAP.items() if entry[1]
)
@staticmethod
def print_writer(message):
print(message)
@ -432,96 +452,3 @@ class AppBuildset:
for source_type in app.sources
)
)
class ApplicationsCGenerator:
APP_TYPE_MAP = {
FlipperAppType.SERVICE: ("FlipperInternalApplication", "FLIPPER_SERVICES"),
FlipperAppType.SYSTEM: ("FlipperInternalApplication", "FLIPPER_SYSTEM_APPS"),
FlipperAppType.APP: ("FlipperInternalApplication", "FLIPPER_APPS"),
FlipperAppType.DEBUG: ("FlipperInternalApplication", "FLIPPER_DEBUG_APPS"),
FlipperAppType.SETTINGS: (
"FlipperInternalApplication",
"FLIPPER_SETTINGS_APPS",
),
FlipperAppType.STARTUP: (
"FlipperInternalOnStartHook",
"FLIPPER_ON_SYSTEM_START",
),
}
APP_EXTERNAL_TYPE = (
"FlipperExternalApplication",
"FLIPPER_EXTERNAL_APPS",
)
def __init__(self, buildset: AppBuildset, autorun_app: str = ""):
self.buildset = buildset
self.autorun = autorun_app
def get_app_ep_forward(self, app: FlipperApplication):
if app.apptype == FlipperAppType.STARTUP:
return f"extern void {app.entry_point}();"
return f"extern int32_t {app.entry_point}(void* p);"
def get_app_descr(self, app: FlipperApplication):
if app.apptype == FlipperAppType.STARTUP:
return app.entry_point
return f"""
{{.app = {app.entry_point},
.name = "{app.name}",
.appid = "{app.appid}",
.stack_size = {app.stack_size},
.icon = {f"&{app.icon}" if app.icon else "NULL"},
.flags = {'|'.join(f"FlipperInternalApplicationFlag{flag}" for flag in app.flags)} }}"""
def get_external_app_descr(self, app: FlipperApplication):
app_path = "/ext/apps"
if app.fap_category:
app_path += f"/{app.fap_category}"
app_path += f"/{app.appid}.fap"
return f"""
{{
.name = "{app.name}",
.icon = {f"&{app.icon}" if app.icon else "NULL"},
.path = "{app_path}" }}"""
def generate(self):
contents = [
'#include "applications.h"',
"#include <assets_icons.h>",
f'const char* FLIPPER_AUTORUN_APP_NAME = "{self.autorun}";',
]
for apptype in self.APP_TYPE_MAP:
contents.extend(
map(self.get_app_ep_forward, self.buildset.get_apps_of_type(apptype))
)
entry_type, entry_block = self.APP_TYPE_MAP[apptype]
contents.append(f"const {entry_type} {entry_block}[] = {{")
contents.append(
",\n".join(
map(self.get_app_descr, self.buildset.get_apps_of_type(apptype))
)
)
contents.append("};")
contents.append(
f"const size_t {entry_block}_COUNT = COUNT_OF({entry_block});"
)
archive_app = self.buildset.get_apps_of_type(FlipperAppType.ARCHIVE)
if archive_app:
contents.extend(
[
self.get_app_ep_forward(archive_app[0]),
f"const FlipperInternalApplication FLIPPER_ARCHIVE = {self.get_app_descr(archive_app[0])};",
]
)
entry_type, entry_block = self.APP_EXTERNAL_TYPE
external_apps = self.buildset.get_apps_of_type(FlipperAppType.MENUEXTERNAL)
contents.append(f"const {entry_type} {entry_block}[] = {{")
contents.append(",\n".join(map(self.get_external_app_descr, external_apps)))
contents.append("};")
contents.append(f"const size_t {entry_block}_COUNT = COUNT_OF({entry_block});")
return "\n".join(contents)

View File

@ -1,25 +1,118 @@
from ansi.color import fg
from fbt.appmanifest import (
ApplicationsCGenerator,
AppManager,
AppBuildset,
FlipperApplication,
FlipperAppType,
FlipperManifestException,
)
from SCons.Action import Action
from SCons.Builder import Builder
from SCons.Errors import StopError
from SCons.Warnings import WarningOnByDefault, warn
from SCons.Script import GetOption
from SCons.Warnings import WarningOnByDefault, warn
# Adding objects for application management to env
# AppManager env["APPMGR"] - loads all manifests; manages list of known apps
# AppBuildset env["APPBUILD"] - contains subset of apps, filtered for current config
class ApplicationsCGenerator:
APP_TYPE_MAP = {
FlipperAppType.SERVICE: ("FlipperInternalApplication", "FLIPPER_SERVICES"),
FlipperAppType.SYSTEM: ("FlipperInternalApplication", "FLIPPER_SYSTEM_APPS"),
FlipperAppType.APP: ("FlipperInternalApplication", "FLIPPER_APPS"),
FlipperAppType.DEBUG: ("FlipperInternalApplication", "FLIPPER_DEBUG_APPS"),
FlipperAppType.SETTINGS: (
"FlipperInternalApplication",
"FLIPPER_SETTINGS_APPS",
),
FlipperAppType.STARTUP: (
"FlipperInternalOnStartHook",
"FLIPPER_ON_SYSTEM_START",
),
}
APP_EXTERNAL_TYPE = (
"FlipperExternalApplication",
"FLIPPER_EXTERNAL_APPS",
)
def __init__(self, buildset: AppBuildset, autorun_app: str = ""):
self.buildset = buildset
self.autorun = autorun_app
def get_app_ep_forward(self, app: FlipperApplication):
if app.apptype == FlipperAppType.STARTUP:
return f"extern void {app.entry_point}();"
return f"extern int32_t {app.entry_point}(void* p);"
def get_app_descr(self, app: FlipperApplication):
if app.apptype == FlipperAppType.STARTUP:
return app.entry_point
return f"""
{{.app = {app.entry_point},
.name = "{app.name}",
.appid = "{app.appid}",
.stack_size = {app.stack_size},
.icon = {f"&{app.icon}" if app.icon else "NULL"},
.flags = {'|'.join(f"FlipperInternalApplicationFlag{flag}" for flag in app.flags)} }}"""
def get_external_app_descr(self, app: FlipperApplication):
app_path = "/ext/apps"
if app.fap_category:
app_path += f"/{app.fap_category}"
app_path += f"/{app.appid}.fap"
return f"""
{{
.name = "{app.name}",
.icon = {f"&{app.icon}" if app.icon else "NULL"},
.path = "{app_path}" }}"""
def generate(self):
contents = [
'#include "applications.h"',
"#include <assets_icons.h>",
f'const char* FLIPPER_AUTORUN_APP_NAME = "{self.autorun}";',
]
for apptype in self.APP_TYPE_MAP:
contents.extend(
map(self.get_app_ep_forward, self.buildset.get_apps_of_type(apptype))
)
entry_type, entry_block = self.APP_TYPE_MAP[apptype]
contents.append(f"const {entry_type} {entry_block}[] = {{")
contents.append(
",\n".join(
map(self.get_app_descr, self.buildset.get_apps_of_type(apptype))
)
)
contents.append("};")
contents.append(
f"const size_t {entry_block}_COUNT = COUNT_OF({entry_block});"
)
archive_app = self.buildset.get_apps_of_type(FlipperAppType.ARCHIVE)
if archive_app:
contents.extend(
[
self.get_app_ep_forward(archive_app[0]),
f"const FlipperInternalApplication FLIPPER_ARCHIVE = {self.get_app_descr(archive_app[0])};",
]
)
entry_type, entry_block = self.APP_EXTERNAL_TYPE
external_apps = self.buildset.get_apps_of_type(FlipperAppType.MENUEXTERNAL)
contents.append(f"const {entry_type} {entry_block}[] = {{")
contents.append(",\n".join(map(self.get_external_app_descr, external_apps)))
contents.append("};")
contents.append(f"const size_t {entry_block}_COUNT = COUNT_OF({entry_block});")
return "\n".join(contents)
def LoadAppManifest(env, entry):
try:
APP_MANIFEST_NAME = "application.fam"
manifest_glob = entry.glob(APP_MANIFEST_NAME)
manifest_glob = entry.glob(FlipperApplication.APP_MANIFEST_DEFAULT_NAME)
if len(manifest_glob) == 0:
try:
disk_node = next(filter(lambda d: d.exists(), entry.get_all_rdirs()))
@ -27,7 +120,7 @@ def LoadAppManifest(env, entry):
disk_node = entry
raise FlipperManifestException(
f"App folder '{disk_node.abspath}': missing manifest ({APP_MANIFEST_NAME})"
f"App folder '{disk_node.abspath}': missing manifest ({FlipperApplication.APP_MANIFEST_DEFAULT_NAME})"
)
app_manifest_file_path = manifest_glob[0].rfile().abspath

View File

@ -58,7 +58,8 @@ class AppBuilder:
)
self.app_env.Append(
CPPDEFINES=[
("FAP_VERSION", f'"{".".join(map(str, self.app.fap_version))}"')
("FAP_VERSION", f'\\"{".".join(map(str, self.app.fap_version))}\\"'),
*self.app.cdefines,
],
)
self.app_env.VariantDir(self.app_work_dir, self.app._appdir, duplicate=False)
@ -143,8 +144,8 @@ class AppBuilder:
self.app._assets_dirs = [self.app._appdir.Dir(self.app.fap_file_assets)]
self.app_env.Append(
LIBS=[*self.app.fap_libs, *self.private_libs],
CPPPATH=[self.app_work_dir, self.app._appdir],
LIBS=[*self.app.fap_libs, *self.private_libs, *self.app.fap_libs],
CPPPATH=[self.app_env.Dir(self.app_work_dir), self.app._appdir],
)
app_sources = self.app_env.GatherSources(
@ -472,7 +473,19 @@ def AddAppLaunchTarget(env, appname, launch_target_name):
components = _gather_app_components(env, appname)
target = env.PhonyTarget(
launch_target_name,
'${PYTHON3} "${APP_RUN_SCRIPT}" -p ${FLIP_PORT} ${EXTRA_ARGS} -s ${SOURCES} -t ${FLIPPER_FILE_TARGETS}',
[
[
"${PYTHON3}",
"${APP_RUN_SCRIPT}",
"-p",
"${FLIP_PORT}",
"${EXTRA_ARGS}",
"-s",
"${SOURCES}",
"-t",
"${FLIPPER_FILE_TARGETS}",
]
],
source=components.deploy_sources.values(),
FLIPPER_FILE_TARGETS=components.deploy_sources.keys(),
EXTRA_ARGS=components.extra_launch_args,

View File

@ -285,7 +285,20 @@ def generate(env, **kw):
"$SDK_AMALGAMATE_HEADER_COMSTR",
),
Action(
"$CC -o $TARGET -E -P $CCFLAGS $_CCCOMCOM $SDK_PP_FLAGS -MMD ${TARGET}.c",
[
[
"$CC",
"-o",
"$TARGET",
"-E",
"-P",
"$CCFLAGS",
"$_CCCOMCOM",
"$SDK_PP_FLAGS",
"-MMD",
"${TARGET}.c",
]
],
"$SDK_AMALGAMATE_PP_COMSTR",
),
],

View File

@ -19,10 +19,22 @@ def generate(env):
BUILDERS={
"VersionBuilder": Builder(
action=Action(
'${PYTHON3} "${VERSION_SCRIPT}" generate '
"-t ${TARGET_HW} -fw-origin ${FIRMWARE_ORIGIN} "
'-o ${TARGET.dir.posix} --dir "${ROOT_DIR}"',
"${VERSIONCOMSTR}",
[
[
"${PYTHON3}",
"${VERSION_SCRIPT}",
"generate",
"-t",
"${TARGET_HW}",
"--fw-origin",
"${FIRMWARE_ORIGIN}",
"-o",
"${TARGET.dir.posix}",
"--dir",
"${ROOT_DIR}",
"${VERSIONCOMSTR}",
]
]
),
emitter=_version_emitter,
),

View File

@ -25,7 +25,7 @@ def generate(env):
BUILDERS={
"HEXBuilder": Builder(
action=Action(
'${OBJCOPY} -O ihex "${SOURCE}" "${TARGET}"',
[["${OBJCOPY}", "-O", "ihex", "${SOURCE}", "${TARGET}"]],
"${HEXCOMSTR}",
),
suffix=".hex",
@ -33,7 +33,7 @@ def generate(env):
),
"BINBuilder": Builder(
action=Action(
'${OBJCOPY} -O binary -S "${SOURCE}" "${TARGET}"',
[["${OBJCOPY}", "-O", "binary", "-S", "${SOURCE}", "${TARGET}"]],
"${BINCOMSTR}",
),
suffix=".bin",
@ -41,7 +41,20 @@ def generate(env):
),
"DFUBuilder": Builder(
action=Action(
'${PYTHON3} "${BIN2DFU}" -i "${SOURCE}" -o "${TARGET}" -a ${IMAGE_BASE_ADDRESS} -l "Flipper Zero F${TARGET_HW}"',
[
[
"${PYTHON3}",
"${BIN2DFU}",
"-i",
"${SOURCE}",
"-o",
"${TARGET}",
"-a",
"${IMAGE_BASE_ADDRESS}",
"-l",
"Flipper Zero F${TARGET_HW}",
]
],
"${DFUCOMSTR}",
),
suffix=".dfu",

View File

@ -1,5 +1,6 @@
from SCons.Builder import Builder
from SCons.Defaults import Touch
from SCons.Action import Action
def generate(env):
@ -9,13 +10,21 @@ def generate(env):
"-auto",
"-exit",
],
JFLASHCOM="${JFLASH} -openprj${JFLASHPROJECT} -open${SOURCE},${JFLASHADDR} ${JFLASHFLAGS}",
)
env.Append(
BUILDERS={
"JFlash": Builder(
action=[
"${JFLASHCOM}",
Action(
[
[
"${JFLASH}",
"-openprj${JFLASHPROJECT}",
"-open${SOURCE},${JFLASHADDR}",
"${JFLASHFLAGS}",
]
]
),
Touch("${TARGET}"),
],
),

View File

@ -6,13 +6,12 @@ def generate(env):
env.SetDefault(
OBJDUMP="objdump",
OBJDUMPFLAGS=[],
OBJDUMPCOM="$OBJDUMP $OBJDUMPFLAGS -S $SOURCES > $TARGET",
)
env.Append(
BUILDERS={
"ObjDump": Builder(
action=Action(
"${OBJDUMPCOM}",
[["$OBJDUMP", "$OBJDUMPFLAGS", "-S", "$SOURCES", ">", "$TARGET"]],
"${OBJDUMPCOMSTR}",
),
suffix=".lst",

View File

@ -5,6 +5,7 @@ from SCons.Defaults import Touch
__OPENOCD_BIN = "openocd"
# TODO: FL-3663: rework argument passing to lists
_oocd_action = Action(
"${OPENOCD} ${OPENOCD_OPTS} ${OPENOCD_COMMAND}",
"${OPENOCDCOMSTR}",

View File

@ -79,7 +79,17 @@ def generate(env):
BUILDERS={
"PVSCheck": Builder(
action=Action(
'${PVSCHECKBIN} analyze ${PVSOPTIONS} -f "${SOURCE}" -o "${TARGET}"',
[
[
"${PVSCHECKBIN}",
"analyze",
"${PVSOPTIONS}",
"-f",
"${SOURCE}",
"-o",
"${TARGET}",
]
],
"${PVSCHECKCOMSTR}",
),
suffix=".log",
@ -92,7 +102,17 @@ def generate(env):
# PlogConverter.exe and plog-converter have different behavior
Mkdir("${TARGET.dir}") if env["PLATFORM"] == "win32" else None,
Action(_set_browser_action, None),
'${PVSCONVBIN} ${PVSCONVOPTIONS} "${SOURCE}" -o "${REPORT_DIR}"',
Action(
[
[
"${PVSCONVBIN}",
"${PVSCONVOPTIONS}",
"${SOURCE}",
"-o",
"${REPORT_DIR}",
]
]
),
],
"${PVSCONVCOMSTR}",
),

View File

@ -6,13 +6,12 @@ def generate(env):
env.SetDefault(
STRIP="strip",
STRIPFLAGS=[],
STRIPCOM="$STRIP $STRIPFLAGS $SOURCES -o $TARGET",
)
env.Append(
BUILDERS={
"ELFStripper": Builder(
action=Action(
"${STRIPCOM}",
[["$STRIP", "$STRIPFLAGS", "$SOURCES", "-o", "$TARGET"]],
"${STRIPCOMSTR}",
),
suffix=".elf",

View File

@ -325,24 +325,26 @@ else:
appenv.PhonyTarget(
"cli",
'${PYTHON3} "${FBT_SCRIPT_DIR}/serial_cli.py" -p ${FLIP_PORT}',
[["${PYTHON3}", "${FBT_SCRIPT_DIR}/serial_cli.py", "-p", "${FLIP_PORT}"]],
)
# Update WiFi devboard firmware
dist_env.PhonyTarget("devboard_flash", "${PYTHON3} ${FBT_SCRIPT_DIR}/wifi_board.py")
dist_env.PhonyTarget(
"devboard_flash", [["${PYTHON3}", "${FBT_SCRIPT_DIR}/wifi_board.py"]]
)
# Linter
dist_env.PhonyTarget(
"lint",
"${PYTHON3} ${FBT_SCRIPT_DIR}/lint.py check ${LINT_SOURCES}",
[["${PYTHON3}", "${FBT_SCRIPT_DIR}/lint.py", "check", "${LINT_SOURCES}"]],
source=original_app_dir.File(".clang-format"),
LINT_SOURCES=[original_app_dir],
)
dist_env.PhonyTarget(
"format",
"${PYTHON3} ${FBT_SCRIPT_DIR}/lint.py format ${LINT_SOURCES}",
[["${PYTHON3}", "${FBT_SCRIPT_DIR}/lint.py", "format", "${LINT_SOURCES}"]],
source=original_app_dir.File(".clang-format"),
LINT_SOURCES=[original_app_dir],
)
@ -455,7 +457,17 @@ if dolphin_src_dir.exists():
)
dist_env.PhonyTarget(
"dolphin_ext",
'${PYTHON3} ${FBT_SCRIPT_DIR}/storage.py -p ${FLIP_PORT} send "${SOURCE}" /ext/dolphin',
[
[
"${PYTHON3}",
"${FBT_SCRIPT_DIR}/storage.py",
"-p",
"${FLIP_PORT}",
"send",
"${SOURCE}",
"/ext/dolphin",
]
],
source=ufbt_build_dir.Dir("dolphin"),
)
else:
@ -467,7 +479,7 @@ else:
dist_env.PhonyTarget(
"env",
"@echo $( ${FBT_SCRIPT_DIR}/toolchain/fbtenv.sh $)",
"@echo $( ${FBT_SCRIPT_DIR.abspath}/toolchain/fbtenv.sh $)",
)
dist_env.PostConfigureUfbtEnvionment()

View File

@ -101,7 +101,7 @@ class Main(App):
required=True,
)
self.parser_generate.add_argument(
"-fw-origin",
"--fw-origin",
dest="firmware_origin",
type=str,
help="firmware origin",

View File

@ -27,6 +27,8 @@ variables_to_forward = [
"PYTHONNOUSERSITE",
"TMP",
"TEMP",
# ccache
"CCACHE_DISABLE",
# Colors for tools
"TERM",
]
@ -62,7 +64,7 @@ coreenv = VAR_ENV.Clone(
# Setting up temp file parameters - to overcome command line length limits
TEMPFILEARGESCFUNC=tempfile_arg_esc_func,
ROOT_DIR=Dir("#"),
FBT_SCRIPT_DIR="${ROOT_DIR}/scripts",
FBT_SCRIPT_DIR=Dir("#/scripts"),
)
# If DIST_SUFFIX is set in environment, is has precedence (set by CI)