Merge branch 'fz-dev' into dev
63
SConstruct
@ -7,7 +7,6 @@
|
|||||||
# construction of certain targets behind command-line options.
|
# construction of certain targets behind command-line options.
|
||||||
|
|
||||||
import os
|
import os
|
||||||
import subprocess
|
|
||||||
|
|
||||||
DefaultEnvironment(tools=[])
|
DefaultEnvironment(tools=[])
|
||||||
|
|
||||||
@ -15,17 +14,22 @@ EnsurePythonVersion(3, 8)
|
|||||||
|
|
||||||
# Progress(["OwO\r", "owo\r", "uwu\r", "owo\r"], interval=15)
|
# Progress(["OwO\r", "owo\r", "uwu\r", "owo\r"], interval=15)
|
||||||
|
|
||||||
|
|
||||||
# This environment is created only for loading options & validating file/dir existence
|
# This environment is created only for loading options & validating file/dir existence
|
||||||
fbt_variables = SConscript("site_scons/commandline.scons")
|
fbt_variables = SConscript("site_scons/commandline.scons")
|
||||||
cmd_environment = Environment(tools=[], variables=fbt_variables)
|
cmd_environment = Environment(
|
||||||
Help(fbt_variables.GenerateHelpText(cmd_environment))
|
toolpath=["#/scripts/fbt_tools"],
|
||||||
|
tools=[
|
||||||
|
("fbt_help", {"vars": fbt_variables}),
|
||||||
|
],
|
||||||
|
variables=fbt_variables,
|
||||||
|
)
|
||||||
|
|
||||||
# Building basic environment - tools, utility methods, cross-compilation
|
# Building basic environment - tools, utility methods, cross-compilation
|
||||||
# settings, gcc flags for Cortex-M4, basic builders and more
|
# settings, gcc flags for Cortex-M4, basic builders and more
|
||||||
coreenv = SConscript(
|
coreenv = SConscript(
|
||||||
"site_scons/environ.scons",
|
"site_scons/environ.scons",
|
||||||
exports={"VAR_ENV": cmd_environment},
|
exports={"VAR_ENV": cmd_environment},
|
||||||
|
toolpath=["#/scripts/fbt_tools"],
|
||||||
)
|
)
|
||||||
SConscript("site_scons/cc.scons", exports={"ENV": coreenv})
|
SConscript("site_scons/cc.scons", exports={"ENV": coreenv})
|
||||||
|
|
||||||
@ -35,41 +39,13 @@ coreenv["ROOT_DIR"] = Dir(".")
|
|||||||
|
|
||||||
# Create a separate "dist" environment and add construction envs to it
|
# Create a separate "dist" environment and add construction envs to it
|
||||||
distenv = coreenv.Clone(
|
distenv = coreenv.Clone(
|
||||||
tools=["fbt_dist", "openocd", "blackmagic", "jflash"],
|
tools=[
|
||||||
OPENOCD_GDB_PIPE=[
|
"fbt_dist",
|
||||||
"|openocd -c 'gdb_port pipe; log_output debug/openocd.log' ${[SINGLEQUOTEFUNC(OPENOCD_OPTS)]}"
|
"fbt_debugopts",
|
||||||
|
"openocd",
|
||||||
|
"blackmagic",
|
||||||
|
"jflash",
|
||||||
],
|
],
|
||||||
GDBOPTS_BASE=[
|
|
||||||
"-ex",
|
|
||||||
"target extended-remote ${GDBREMOTE}",
|
|
||||||
"-ex",
|
|
||||||
"set confirm off",
|
|
||||||
"-ex",
|
|
||||||
"set pagination off",
|
|
||||||
],
|
|
||||||
GDBOPTS_BLACKMAGIC=[
|
|
||||||
"-ex",
|
|
||||||
"monitor swdp_scan",
|
|
||||||
"-ex",
|
|
||||||
"monitor debug_bmp enable",
|
|
||||||
"-ex",
|
|
||||||
"attach 1",
|
|
||||||
"-ex",
|
|
||||||
"set mem inaccessible-by-default off",
|
|
||||||
],
|
|
||||||
GDBPYOPTS=[
|
|
||||||
"-ex",
|
|
||||||
"source debug/FreeRTOS/FreeRTOS.py",
|
|
||||||
"-ex",
|
|
||||||
"source debug/flipperapps.py",
|
|
||||||
"-ex",
|
|
||||||
"source debug/PyCortexMDebug/PyCortexMDebug.py",
|
|
||||||
"-ex",
|
|
||||||
"svd_load ${SVD_FILE}",
|
|
||||||
"-ex",
|
|
||||||
"compare-sections",
|
|
||||||
],
|
|
||||||
JFLASHPROJECT="${ROOT_DIR.abspath}/debug/fw.jflash",
|
|
||||||
ENV=os.environ,
|
ENV=os.environ,
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -166,7 +142,7 @@ basic_dist = distenv.DistCommand("fw_dist", distenv["DIST_DEPENDS"])
|
|||||||
distenv.Default(basic_dist)
|
distenv.Default(basic_dist)
|
||||||
|
|
||||||
dist_dir = distenv.GetProjetDirName()
|
dist_dir = distenv.GetProjetDirName()
|
||||||
plugin_dist = [
|
fap_dist = [
|
||||||
distenv.Install(
|
distenv.Install(
|
||||||
f"#/dist/{dist_dir}/apps/debug_elf",
|
f"#/dist/{dist_dir}/apps/debug_elf",
|
||||||
firmware_env["FW_EXTAPPS"]["debug"].values(),
|
firmware_env["FW_EXTAPPS"]["debug"].values(),
|
||||||
@ -176,9 +152,9 @@ plugin_dist = [
|
|||||||
for dist_entry in firmware_env["FW_EXTAPPS"]["dist"].values()
|
for dist_entry in firmware_env["FW_EXTAPPS"]["dist"].values()
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
Depends(plugin_dist, firmware_env["FW_EXTAPPS"]["validators"].values())
|
Depends(fap_dist, firmware_env["FW_EXTAPPS"]["validators"].values())
|
||||||
Alias("plugin_dist", plugin_dist)
|
Alias("fap_dist", fap_dist)
|
||||||
# distenv.Default(plugin_dist)
|
# distenv.Default(fap_dist)
|
||||||
|
|
||||||
plugin_resources_dist = list(
|
plugin_resources_dist = list(
|
||||||
distenv.Install(f"#/assets/resources/apps/{dist_entry[0]}", dist_entry[1])
|
distenv.Install(f"#/assets/resources/apps/{dist_entry[0]}", dist_entry[1])
|
||||||
@ -189,9 +165,10 @@ distenv.Depends(firmware_env["FW_RESOURCES"], plugin_resources_dist)
|
|||||||
|
|
||||||
# Target for bundling core2 package for qFlipper
|
# Target for bundling core2 package for qFlipper
|
||||||
copro_dist = distenv.CoproBuilder(
|
copro_dist = distenv.CoproBuilder(
|
||||||
distenv.Dir("assets/core2_firmware"),
|
"#/build/core2_firmware.tgz",
|
||||||
[],
|
[],
|
||||||
)
|
)
|
||||||
|
distenv.AlwaysBuild(copro_dist)
|
||||||
distenv.Alias("copro_dist", copro_dist)
|
distenv.Alias("copro_dist", copro_dist)
|
||||||
|
|
||||||
firmware_flash = distenv.AddOpenOCDFlashTarget(firmware_env)
|
firmware_flash = distenv.AddOpenOCDFlashTarget(firmware_env)
|
||||||
|
@ -4,6 +4,8 @@
|
|||||||
#include <gui/gui.h>
|
#include <gui/gui.h>
|
||||||
#include <input/input.h>
|
#include <input/input.h>
|
||||||
|
|
||||||
|
/* Magic happens here -- this file is generated by fbt.
|
||||||
|
* Just set fap_icon_assets in application.fam and #include {APPID}_icons.h */
|
||||||
#include "example_images_icons.h"
|
#include "example_images_icons.h"
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
|
@ -237,7 +237,8 @@ static uint16_t ducky_get_keycode(BadUsbScript* bad_usb, const char* param, bool
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int32_t ducky_parse_line(BadUsbScript* bad_usb, FuriString* line) {
|
static int32_t
|
||||||
|
ducky_parse_line(BadUsbScript* bad_usb, FuriString* line, char* error, size_t error_len) {
|
||||||
uint32_t line_len = furi_string_size(line);
|
uint32_t line_len = furi_string_size(line);
|
||||||
const char* line_tmp = furi_string_get_cstr(line);
|
const char* line_tmp = furi_string_get_cstr(line);
|
||||||
bool state = false;
|
bool state = false;
|
||||||
@ -270,6 +271,9 @@ static int32_t ducky_parse_line(BadUsbScript* bad_usb, FuriString* line) {
|
|||||||
if((state) && (delay_val > 0)) {
|
if((state) && (delay_val > 0)) {
|
||||||
return (int32_t)delay_val;
|
return (int32_t)delay_val;
|
||||||
}
|
}
|
||||||
|
if(error != NULL) {
|
||||||
|
snprintf(error, error_len, "Invalid number %s", line_tmp);
|
||||||
|
}
|
||||||
return SCRIPT_STATE_ERROR;
|
return SCRIPT_STATE_ERROR;
|
||||||
} else if(
|
} else if(
|
||||||
(strncmp(line_tmp, ducky_cmd_defdelay_1, strlen(ducky_cmd_defdelay_1)) == 0) ||
|
(strncmp(line_tmp, ducky_cmd_defdelay_1, strlen(ducky_cmd_defdelay_1)) == 0) ||
|
||||||
@ -277,17 +281,26 @@ static int32_t ducky_parse_line(BadUsbScript* bad_usb, FuriString* line) {
|
|||||||
// DEFAULT_DELAY
|
// DEFAULT_DELAY
|
||||||
line_tmp = &line_tmp[ducky_get_command_len(line_tmp) + 1];
|
line_tmp = &line_tmp[ducky_get_command_len(line_tmp) + 1];
|
||||||
state = ducky_get_number(line_tmp, &bad_usb->defdelay);
|
state = ducky_get_number(line_tmp, &bad_usb->defdelay);
|
||||||
|
if(!state && error != NULL) {
|
||||||
|
snprintf(error, error_len, "Invalid number %s", line_tmp);
|
||||||
|
}
|
||||||
return (state) ? (0) : SCRIPT_STATE_ERROR;
|
return (state) ? (0) : SCRIPT_STATE_ERROR;
|
||||||
} else if(strncmp(line_tmp, ducky_cmd_string, strlen(ducky_cmd_string)) == 0) {
|
} else if(strncmp(line_tmp, ducky_cmd_string, strlen(ducky_cmd_string)) == 0) {
|
||||||
// STRING
|
// STRING
|
||||||
line_tmp = &line_tmp[ducky_get_command_len(line_tmp) + 1];
|
line_tmp = &line_tmp[ducky_get_command_len(line_tmp) + 1];
|
||||||
state = ducky_string(bad_usb, line_tmp);
|
state = ducky_string(bad_usb, line_tmp);
|
||||||
|
if(!state && error != NULL) {
|
||||||
|
snprintf(error, error_len, "Invalid string %s", line_tmp);
|
||||||
|
}
|
||||||
return (state) ? (0) : SCRIPT_STATE_ERROR;
|
return (state) ? (0) : SCRIPT_STATE_ERROR;
|
||||||
} else if(strncmp(line_tmp, ducky_cmd_altchar, strlen(ducky_cmd_altchar)) == 0) {
|
} else if(strncmp(line_tmp, ducky_cmd_altchar, strlen(ducky_cmd_altchar)) == 0) {
|
||||||
// ALTCHAR
|
// ALTCHAR
|
||||||
line_tmp = &line_tmp[ducky_get_command_len(line_tmp) + 1];
|
line_tmp = &line_tmp[ducky_get_command_len(line_tmp) + 1];
|
||||||
ducky_numlock_on();
|
ducky_numlock_on();
|
||||||
state = ducky_altchar(line_tmp);
|
state = ducky_altchar(line_tmp);
|
||||||
|
if(!state && error != NULL) {
|
||||||
|
snprintf(error, error_len, "Invalid altchar %s", line_tmp);
|
||||||
|
}
|
||||||
return (state) ? (0) : SCRIPT_STATE_ERROR;
|
return (state) ? (0) : SCRIPT_STATE_ERROR;
|
||||||
} else if(
|
} else if(
|
||||||
(strncmp(line_tmp, ducky_cmd_altstr_1, strlen(ducky_cmd_altstr_1)) == 0) ||
|
(strncmp(line_tmp, ducky_cmd_altstr_1, strlen(ducky_cmd_altstr_1)) == 0) ||
|
||||||
@ -296,11 +309,17 @@ static int32_t ducky_parse_line(BadUsbScript* bad_usb, FuriString* line) {
|
|||||||
line_tmp = &line_tmp[ducky_get_command_len(line_tmp) + 1];
|
line_tmp = &line_tmp[ducky_get_command_len(line_tmp) + 1];
|
||||||
ducky_numlock_on();
|
ducky_numlock_on();
|
||||||
state = ducky_altstring(line_tmp);
|
state = ducky_altstring(line_tmp);
|
||||||
|
if(!state && error != NULL) {
|
||||||
|
snprintf(error, error_len, "Invalid altstring %s", line_tmp);
|
||||||
|
}
|
||||||
return (state) ? (0) : SCRIPT_STATE_ERROR;
|
return (state) ? (0) : SCRIPT_STATE_ERROR;
|
||||||
} else if(strncmp(line_tmp, ducky_cmd_repeat, strlen(ducky_cmd_repeat)) == 0) {
|
} else if(strncmp(line_tmp, ducky_cmd_repeat, strlen(ducky_cmd_repeat)) == 0) {
|
||||||
// REPEAT
|
// REPEAT
|
||||||
line_tmp = &line_tmp[ducky_get_command_len(line_tmp) + 1];
|
line_tmp = &line_tmp[ducky_get_command_len(line_tmp) + 1];
|
||||||
state = ducky_get_number(line_tmp, &bad_usb->repeat_cnt);
|
state = ducky_get_number(line_tmp, &bad_usb->repeat_cnt);
|
||||||
|
if(!state && error != NULL) {
|
||||||
|
snprintf(error, error_len, "Invalid number %s", line_tmp);
|
||||||
|
}
|
||||||
return (state) ? (0) : SCRIPT_STATE_ERROR;
|
return (state) ? (0) : SCRIPT_STATE_ERROR;
|
||||||
} else if(strncmp(line_tmp, ducky_cmd_sysrq, strlen(ducky_cmd_sysrq)) == 0) {
|
} else if(strncmp(line_tmp, ducky_cmd_sysrq, strlen(ducky_cmd_sysrq)) == 0) {
|
||||||
// SYSRQ
|
// SYSRQ
|
||||||
@ -313,7 +332,12 @@ static int32_t ducky_parse_line(BadUsbScript* bad_usb, FuriString* line) {
|
|||||||
} else {
|
} else {
|
||||||
// Special keys + modifiers
|
// Special keys + modifiers
|
||||||
uint16_t key = ducky_get_keycode(bad_usb, line_tmp, false);
|
uint16_t key = ducky_get_keycode(bad_usb, line_tmp, false);
|
||||||
if(key == HID_KEYBOARD_NONE) return SCRIPT_STATE_ERROR;
|
if(key == HID_KEYBOARD_NONE) {
|
||||||
|
if(error != NULL) {
|
||||||
|
snprintf(error, error_len, "No keycode defined for %s", line_tmp);
|
||||||
|
}
|
||||||
|
return SCRIPT_STATE_ERROR;
|
||||||
|
}
|
||||||
if((key & 0xFF00) != 0) {
|
if((key & 0xFF00) != 0) {
|
||||||
// It's a modifier key
|
// It's a modifier key
|
||||||
line_tmp = &line_tmp[ducky_get_command_len(line_tmp) + 1];
|
line_tmp = &line_tmp[ducky_get_command_len(line_tmp) + 1];
|
||||||
@ -323,6 +347,9 @@ static int32_t ducky_parse_line(BadUsbScript* bad_usb, FuriString* line) {
|
|||||||
furi_hal_hid_kb_release(key);
|
furi_hal_hid_kb_release(key);
|
||||||
return (0);
|
return (0);
|
||||||
}
|
}
|
||||||
|
if(error != NULL) {
|
||||||
|
strncpy(error, "Unknown error", error_len);
|
||||||
|
}
|
||||||
return SCRIPT_STATE_ERROR;
|
return SCRIPT_STATE_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -401,7 +428,8 @@ static int32_t ducky_script_execute_next(BadUsbScript* bad_usb, File* script_fil
|
|||||||
|
|
||||||
if(bad_usb->repeat_cnt > 0) {
|
if(bad_usb->repeat_cnt > 0) {
|
||||||
bad_usb->repeat_cnt--;
|
bad_usb->repeat_cnt--;
|
||||||
delay_val = ducky_parse_line(bad_usb, bad_usb->line_prev);
|
delay_val = ducky_parse_line(
|
||||||
|
bad_usb, bad_usb->line_prev, bad_usb->st.error, sizeof(bad_usb->st.error));
|
||||||
if(delay_val == SCRIPT_STATE_NEXT_LINE) { // Empty line
|
if(delay_val == SCRIPT_STATE_NEXT_LINE) { // Empty line
|
||||||
return 0;
|
return 0;
|
||||||
} else if(delay_val < 0) { // Script error
|
} else if(delay_val < 0) { // Script error
|
||||||
@ -435,7 +463,9 @@ static int32_t ducky_script_execute_next(BadUsbScript* bad_usb, File* script_fil
|
|||||||
bad_usb->st.line_cur++;
|
bad_usb->st.line_cur++;
|
||||||
bad_usb->buf_len = bad_usb->buf_len + bad_usb->buf_start - (i + 1);
|
bad_usb->buf_len = bad_usb->buf_len + bad_usb->buf_start - (i + 1);
|
||||||
bad_usb->buf_start = i + 1;
|
bad_usb->buf_start = i + 1;
|
||||||
delay_val = ducky_parse_line(bad_usb, bad_usb->line);
|
delay_val = ducky_parse_line(
|
||||||
|
bad_usb, bad_usb->line, bad_usb->st.error, sizeof(bad_usb->st.error));
|
||||||
|
|
||||||
if(delay_val < 0) {
|
if(delay_val < 0) {
|
||||||
bad_usb->st.error_line = bad_usb->st.line_cur;
|
bad_usb->st.error_line = bad_usb->st.line_cur;
|
||||||
FURI_LOG_E(WORKER_TAG, "Unknown command at line %u", bad_usb->st.line_cur);
|
FURI_LOG_E(WORKER_TAG, "Unknown command at line %u", bad_usb->st.line_cur);
|
||||||
@ -618,6 +648,7 @@ BadUsbScript* bad_usb_script_open(FuriString* file_path) {
|
|||||||
bad_usb_script_set_default_keyboard_layout(bad_usb);
|
bad_usb_script_set_default_keyboard_layout(bad_usb);
|
||||||
|
|
||||||
bad_usb->st.state = BadUsbStateInit;
|
bad_usb->st.state = BadUsbStateInit;
|
||||||
|
bad_usb->st.error[0] = '\0';
|
||||||
|
|
||||||
bad_usb->thread = furi_thread_alloc();
|
bad_usb->thread = furi_thread_alloc();
|
||||||
furi_thread_set_name(bad_usb->thread, "BadUsbWorker");
|
furi_thread_set_name(bad_usb->thread, "BadUsbWorker");
|
||||||
|
@ -25,6 +25,7 @@ typedef struct {
|
|||||||
uint16_t line_nb;
|
uint16_t line_nb;
|
||||||
uint32_t delay_remain;
|
uint32_t delay_remain;
|
||||||
uint16_t error_line;
|
uint16_t error_line;
|
||||||
|
char error[64];
|
||||||
} BadUsbState;
|
} BadUsbState;
|
||||||
|
|
||||||
BadUsbScript* bad_usb_script_open(FuriString* file_path);
|
BadUsbScript* bad_usb_script_open(FuriString* file_path);
|
||||||
|
@ -74,6 +74,7 @@ static void bad_usb_draw_callback(Canvas* canvas, void* _model) {
|
|||||||
canvas_draw_str_aligned(
|
canvas_draw_str_aligned(
|
||||||
canvas, 127, 46, AlignRight, AlignBottom, furi_string_get_cstr(disp_str));
|
canvas, 127, 46, AlignRight, AlignBottom, furi_string_get_cstr(disp_str));
|
||||||
furi_string_reset(disp_str);
|
furi_string_reset(disp_str);
|
||||||
|
canvas_draw_str_aligned(canvas, 127, 56, AlignRight, AlignBottom, model->state.error);
|
||||||
} else if(model->state.state == BadUsbStateIdle) {
|
} else if(model->state.state == BadUsbStateIdle) {
|
||||||
canvas_draw_icon(canvas, 4, 26, &I_Smile_18x18);
|
canvas_draw_icon(canvas, 4, 26, &I_Smile_18x18);
|
||||||
canvas_set_font(canvas, FontBigNumbers);
|
canvas_set_font(canvas, FontBigNumbers);
|
||||||
|
BIN
assets/dolphin/external/L1_Painting_128x64/frame_0.png
vendored
Normal file
After Width: | Height: | Size: 1.6 KiB |
BIN
assets/dolphin/external/L1_Painting_128x64/frame_1.png
vendored
Normal file
After Width: | Height: | Size: 1.6 KiB |
BIN
assets/dolphin/external/L1_Painting_128x64/frame_10.png
vendored
Normal file
After Width: | Height: | Size: 1.6 KiB |
BIN
assets/dolphin/external/L1_Painting_128x64/frame_11.png
vendored
Normal file
After Width: | Height: | Size: 1.5 KiB |
BIN
assets/dolphin/external/L1_Painting_128x64/frame_2.png
vendored
Normal file
After Width: | Height: | Size: 1.6 KiB |
BIN
assets/dolphin/external/L1_Painting_128x64/frame_3.png
vendored
Normal file
After Width: | Height: | Size: 1.5 KiB |
BIN
assets/dolphin/external/L1_Painting_128x64/frame_4.png
vendored
Normal file
After Width: | Height: | Size: 1.6 KiB |
BIN
assets/dolphin/external/L1_Painting_128x64/frame_5.png
vendored
Normal file
After Width: | Height: | Size: 1.6 KiB |
BIN
assets/dolphin/external/L1_Painting_128x64/frame_6.png
vendored
Normal file
After Width: | Height: | Size: 1.6 KiB |
BIN
assets/dolphin/external/L1_Painting_128x64/frame_7.png
vendored
Normal file
After Width: | Height: | Size: 1.6 KiB |
BIN
assets/dolphin/external/L1_Painting_128x64/frame_8.png
vendored
Normal file
After Width: | Height: | Size: 1.6 KiB |
BIN
assets/dolphin/external/L1_Painting_128x64/frame_9.png
vendored
Normal file
After Width: | Height: | Size: 1.6 KiB |
32
assets/dolphin/external/L1_Painting_128x64/meta.txt
vendored
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
Filetype: Flipper Animation
|
||||||
|
Version: 1
|
||||||
|
|
||||||
|
Width: 128
|
||||||
|
Height: 64
|
||||||
|
Passive frames: 9
|
||||||
|
Active frames: 13
|
||||||
|
Frames order: 0 1 2 3 4 5 2 3 4 10 6 7 8 7 8 7 8 7 8 9 10 11
|
||||||
|
Active cycles: 1
|
||||||
|
Frame rate: 2
|
||||||
|
Duration: 3600
|
||||||
|
Active cooldown: 7
|
||||||
|
|
||||||
|
Bubble slots: 1
|
||||||
|
|
||||||
|
Slot: 0
|
||||||
|
X: 57
|
||||||
|
Y: 24
|
||||||
|
Text: No mistakes,
|
||||||
|
AlignH: Left
|
||||||
|
AlignV: Center
|
||||||
|
StartFrame: 11
|
||||||
|
EndFrame: 14
|
||||||
|
|
||||||
|
Slot: 0
|
||||||
|
X: 57
|
||||||
|
Y: 21
|
||||||
|
Text: only happy\n accidents
|
||||||
|
AlignH: Left
|
||||||
|
AlignV: Center
|
||||||
|
StartFrame: 15
|
||||||
|
EndFrame: 18
|
7
assets/dolphin/external/manifest.txt
vendored
@ -85,6 +85,13 @@ Min level: 1
|
|||||||
Max level: 3
|
Max level: 3
|
||||||
Weight: 3
|
Weight: 3
|
||||||
|
|
||||||
|
Name: L1_Painting_128x64
|
||||||
|
Min butthurt: 0
|
||||||
|
Max butthurt: 7
|
||||||
|
Min level: 1
|
||||||
|
Max level: 3
|
||||||
|
Weight: 6
|
||||||
|
|
||||||
Name: L3_Hijack_radio_128x64
|
Name: L3_Hijack_radio_128x64
|
||||||
Min butthurt: 0
|
Min butthurt: 0
|
||||||
Max butthurt: 8
|
Max butthurt: 8
|
||||||
|
BIN
assets/resources/dolphin/L1_Painting_128x64/frame_0.bm
Normal file
BIN
assets/resources/dolphin/L1_Painting_128x64/frame_1.bm
Normal file
BIN
assets/resources/dolphin/L1_Painting_128x64/frame_10.bm
Normal file
BIN
assets/resources/dolphin/L1_Painting_128x64/frame_11.bm
Normal file
BIN
assets/resources/dolphin/L1_Painting_128x64/frame_2.bm
Normal file
BIN
assets/resources/dolphin/L1_Painting_128x64/frame_3.bm
Normal file
BIN
assets/resources/dolphin/L1_Painting_128x64/frame_4.bm
Normal file
BIN
assets/resources/dolphin/L1_Painting_128x64/frame_5.bm
Normal file
BIN
assets/resources/dolphin/L1_Painting_128x64/frame_6.bm
Normal file
BIN
assets/resources/dolphin/L1_Painting_128x64/frame_7.bm
Normal file
BIN
assets/resources/dolphin/L1_Painting_128x64/frame_8.bm
Normal file
BIN
assets/resources/dolphin/L1_Painting_128x64/frame_9.bm
Normal file
32
assets/resources/dolphin/L1_Painting_128x64/meta.txt
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
Filetype: Flipper Animation
|
||||||
|
Version: 1
|
||||||
|
|
||||||
|
Width: 128
|
||||||
|
Height: 64
|
||||||
|
Passive frames: 9
|
||||||
|
Active frames: 13
|
||||||
|
Frames order: 0 1 2 3 4 5 2 3 4 10 6 7 8 7 8 7 8 7 8 9 10 11
|
||||||
|
Active cycles: 1
|
||||||
|
Frame rate: 2
|
||||||
|
Duration: 3600
|
||||||
|
Active cooldown: 7
|
||||||
|
|
||||||
|
Bubble slots: 1
|
||||||
|
|
||||||
|
Slot: 0
|
||||||
|
X: 57
|
||||||
|
Y: 24
|
||||||
|
Text: No mistakes,
|
||||||
|
AlignH: Left
|
||||||
|
AlignV: Center
|
||||||
|
StartFrame: 11
|
||||||
|
EndFrame: 14
|
||||||
|
|
||||||
|
Slot: 0
|
||||||
|
X: 57
|
||||||
|
Y: 21
|
||||||
|
Text: only happy\n accidents
|
||||||
|
AlignH: Left
|
||||||
|
AlignV: Center
|
||||||
|
StartFrame: 15
|
||||||
|
EndFrame: 18
|
@ -85,6 +85,13 @@ Min level: 1
|
|||||||
Max level: 3
|
Max level: 3
|
||||||
Weight: 3
|
Weight: 3
|
||||||
|
|
||||||
|
Name: L1_Painting_128x64
|
||||||
|
Min butthurt: 0
|
||||||
|
Max butthurt: 7
|
||||||
|
Min level: 1
|
||||||
|
Max level: 3
|
||||||
|
Weight: 6
|
||||||
|
|
||||||
Name: L3_Hijack_radio_128x64
|
Name: L3_Hijack_radio_128x64
|
||||||
Min butthurt: 0
|
Min butthurt: 0
|
||||||
Max butthurt: 8
|
Max butthurt: 8
|
||||||
|
@ -30,7 +30,7 @@ Only 2 parameters are mandatory: ***appid*** and ***apptype***, others are optio
|
|||||||
| METAPACKAGE | Does not define any code to be run, used for declaring dependencies and application bundles |
|
| METAPACKAGE | Does not define any code to be run, used for declaring dependencies and application bundles |
|
||||||
|
|
||||||
* **name**: Name that is displayed in menus.
|
* **name**: Name that is displayed in menus.
|
||||||
* **entry_point**: C function to be used as application's entry point.
|
* **entry_point**: C function to be used as application's entry point. Note that C++ function names are mangled, so you need to wrap them in `extern "C"` in order to use them as entry points.
|
||||||
* **flags**: Internal flags for system apps. Do not use.
|
* **flags**: Internal flags for system apps. Do not use.
|
||||||
* **cdefines**: C preprocessor definitions to declare globally for other apps when current application is included in active build configuration.
|
* **cdefines**: C preprocessor definitions to declare globally for other apps when current application is included in active build configuration.
|
||||||
* **requires**: List of application IDs to also include in build configuration, when current application is referenced in list of applications to build.
|
* **requires**: List of application IDs to also include in build configuration, when current application is referenced in list of applications to build.
|
||||||
@ -55,7 +55,7 @@ The following parameters are used only for [FAPs](./AppsOnSDCard.md):
|
|||||||
* **fap_author**: string, may be empty. Application's author.
|
* **fap_author**: string, may be empty. Application's author.
|
||||||
* **fap_weburl**: string, may be empty. Application's homepage.
|
* **fap_weburl**: string, may be empty. Application's homepage.
|
||||||
* **fap_icon_assets**: string. If present, defines a folder name to be used for gathering image assets for this application. These images will be preprocessed and built alongside the application. See [FAP assets](./AppsOnSDCard.md#fap-assets) for details.
|
* **fap_icon_assets**: string. If present, defines a folder name to be used for gathering image assets for this application. These images will be preprocessed and built alongside the application. See [FAP assets](./AppsOnSDCard.md#fap-assets) for details.
|
||||||
* **fap_extbuild**: provides support for parts of application sources to be build by external tools. Contains a list of `ExtFile(path="file name", command="shell command")` definitions. **`fbt`** will run the specified command for each file in the list.
|
* **fap_extbuild**: provides support for parts of application sources to be built by external tools. Contains a list of `ExtFile(path="file name", command="shell command")` definitions. **`fbt`** will run the specified command for each file in the list.
|
||||||
Note that commands are executed at the firmware root folder's root, and all intermediate files must be placed in a application's temporary build folder. For that, you can use pattern expansion by **`fbt`**: `${FAP_WORK_DIR}` will be replaced with the path to the application's temporary build folder, and `${FAP_SRC_DIR}` will be replaced with the path to the application's source folder. You can also use other variables defined internally by **`fbt`**.
|
Note that commands are executed at the firmware root folder's root, and all intermediate files must be placed in a application's temporary build folder. For that, you can use pattern expansion by **`fbt`**: `${FAP_WORK_DIR}` will be replaced with the path to the application's temporary build folder, and `${FAP_SRC_DIR}` will be replaced with the path to the application's source folder. You can also use other variables defined internally by **`fbt`**.
|
||||||
|
|
||||||
Example for building an app from Rust sources:
|
Example for building an app from Rust sources:
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
[fbt](./fbt.md) has support for building applications as FAP files. FAP are essentially .elf executables with extra metadata and resources bundled in.
|
[fbt](./fbt.md) has support for building applications as FAP files. FAP are essentially .elf executables with extra metadata and resources bundled in.
|
||||||
|
|
||||||
FAPs are built with `faps` **`fbt`** target. They can also be deployed to `dist` folder with `plugin_dist` **`fbt`** target.
|
FAPs are built with `faps` target. They can also be deployed to `dist` folder with `fap_dist` target.
|
||||||
|
|
||||||
FAPs do not depend on being run on a specific firmware version. Compatibility is determined by the FAP's metadata, which includes the required [API version](#api-versioning).
|
FAPs do not depend on being run on a specific firmware version. Compatibility is determined by the FAP's metadata, which includes the required [API version](#api-versioning).
|
||||||
|
|
||||||
@ -15,7 +15,7 @@ To build your application as a FAP, just create a folder with your app's source
|
|||||||
|
|
||||||
* To build your application, run `./fbt fap_{APPID}`, where APPID is your application's ID in its manifest.
|
* To build your application, run `./fbt fap_{APPID}`, where APPID is your application's ID in its manifest.
|
||||||
* To build your app, then upload it over USB & run it on Flipper, use `./fbt launch_app APPSRC=applications/path/to/app`. This command is configured in default [VSCode profile](../.vscode/ReadMe.md) as "Launch App on Flipper" build action (Ctrl+Shift+B menu).
|
* To build your app, then upload it over USB & run it on Flipper, use `./fbt launch_app APPSRC=applications/path/to/app`. This command is configured in default [VSCode profile](../.vscode/ReadMe.md) as "Launch App on Flipper" build action (Ctrl+Shift+B menu).
|
||||||
* To build all FAPs, run `./fbt plugin_dist`.
|
* To build all FAPs, run `./fbt faps` or `./fbt fap_dist`.
|
||||||
|
|
||||||
|
|
||||||
## FAP assets
|
## FAP assets
|
||||||
|
@ -43,7 +43,7 @@ To run cleanup (think of `make clean`) for specified targets, add `-c` option.
|
|||||||
### High-level (what you most likely need)
|
### High-level (what you most likely need)
|
||||||
|
|
||||||
- `fw_dist` - build & publish firmware to `dist` folder. This is a default target, when no other are specified
|
- `fw_dist` - build & publish firmware to `dist` folder. This is a default target, when no other are specified
|
||||||
- `plugin_dist` - build external plugins & publish to `dist` folder
|
- `fap_dist` - build external plugins & publish to `dist` folder
|
||||||
- `updater_package`, `updater_minpackage` - build self-update package. Minimal version only inclues firmware's DFU file; full version also includes radio stack & resources for SD card
|
- `updater_package`, `updater_minpackage` - build self-update package. Minimal version only inclues firmware's DFU file; full version also includes radio stack & resources for SD card
|
||||||
- `copro_dist` - bundle Core2 FUS+stack binaries for qFlipper
|
- `copro_dist` - bundle Core2 FUS+stack binaries for qFlipper
|
||||||
- `flash` - flash attached device with OpenOCD over ST-Link
|
- `flash` - flash attached device with OpenOCD over ST-Link
|
||||||
@ -56,6 +56,7 @@ To run cleanup (think of `make clean`) for specified targets, add `-c` option.
|
|||||||
- `get_blackmagic` - output blackmagic address in gdb remote format. Useful for IDE integration
|
- `get_blackmagic` - output blackmagic address in gdb remote format. Useful for IDE integration
|
||||||
- `lint`, `format` - run clang-format on C source code to check and reformat it according to `.clang-format` specs
|
- `lint`, `format` - run clang-format on C source code to check and reformat it according to `.clang-format` specs
|
||||||
- `lint_py`, `format_py` - run [black](https://black.readthedocs.io/en/stable/index.html) on Python source code, build system files & application manifests
|
- `lint_py`, `format_py` - run [black](https://black.readthedocs.io/en/stable/index.html) on Python source code, build system files & application manifests
|
||||||
|
- `cli` - start Flipper CLI session over USB
|
||||||
|
|
||||||
### Firmware targets
|
### Firmware targets
|
||||||
|
|
||||||
|
@ -3,7 +3,7 @@ Import("ENV", "fw_build_meta")
|
|||||||
from SCons.Errors import UserError
|
from SCons.Errors import UserError
|
||||||
import itertools
|
import itertools
|
||||||
|
|
||||||
from fbt.util import (
|
from fbt_extra.util import (
|
||||||
should_gen_cdb_and_link_dir,
|
should_gen_cdb_and_link_dir,
|
||||||
link_elf_dir_as_latest,
|
link_elf_dir_as_latest,
|
||||||
)
|
)
|
||||||
@ -141,6 +141,10 @@ else:
|
|||||||
if extra_int_apps := GetOption("extra_int_apps"):
|
if extra_int_apps := GetOption("extra_int_apps"):
|
||||||
fwenv.Append(APPS=extra_int_apps.split(","))
|
fwenv.Append(APPS=extra_int_apps.split(","))
|
||||||
|
|
||||||
|
|
||||||
|
if fwenv["FAP_EXAMPLES"]:
|
||||||
|
fwenv.Append(APPDIRS=[("applications/examples", False)])
|
||||||
|
|
||||||
fwenv.LoadApplicationManifests()
|
fwenv.LoadApplicationManifests()
|
||||||
fwenv.PrepareApplicationsBuild()
|
fwenv.PrepareApplicationsBuild()
|
||||||
|
|
||||||
@ -316,10 +320,13 @@ if fwenv["IS_BASE_FIRMWARE"]:
|
|||||||
"-D__inline__=inline",
|
"-D__inline__=inline",
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
Depends(sdk_source, (fwenv["SDK_HEADERS"], fwenv["FW_ASSETS_HEADERS"]))
|
# Depends(sdk_source, (fwenv["SDK_HEADERS"], fwenv["FW_ASSETS_HEADERS"]))
|
||||||
|
Depends(sdk_source, fwenv.ProcessSdkDepends("sdk_origin.d"))
|
||||||
|
|
||||||
sdk_tree = fwenv.SDKTree("sdk/sdk.opts", "sdk_origin")
|
fwenv["SDK_DIR"] = fwenv.Dir("sdk")
|
||||||
AlwaysBuild(sdk_tree)
|
sdk_tree = fwenv.SDKTree(fwenv["SDK_DIR"], "sdk_origin")
|
||||||
|
fw_artifacts.append(sdk_tree)
|
||||||
|
# AlwaysBuild(sdk_tree)
|
||||||
Alias("sdk_tree", sdk_tree)
|
Alias("sdk_tree", sdk_tree)
|
||||||
|
|
||||||
sdk_apicheck = fwenv.SDKSymUpdater(fwenv.subst("$SDK_DEFINITION"), "sdk_origin")
|
sdk_apicheck = fwenv.SDKSymUpdater(fwenv.subst("$SDK_DEFINITION"), "sdk_origin")
|
||||||
@ -329,7 +336,7 @@ if fwenv["IS_BASE_FIRMWARE"]:
|
|||||||
Alias("sdk_check", sdk_apicheck)
|
Alias("sdk_check", sdk_apicheck)
|
||||||
|
|
||||||
sdk_apisyms = fwenv.SDKSymGenerator(
|
sdk_apisyms = fwenv.SDKSymGenerator(
|
||||||
"assets/compiled/symbols.h", fwenv.subst("$SDK_DEFINITION")
|
"assets/compiled/symbols.h", fwenv["SDK_DEFINITION"]
|
||||||
)
|
)
|
||||||
Alias("api_syms", sdk_apisyms)
|
Alias("api_syms", sdk_apisyms)
|
||||||
|
|
||||||
|
@ -207,7 +207,7 @@ bool protocol_awid_write_data(ProtocolAwid* protocol, void* data) {
|
|||||||
|
|
||||||
// Fix incorrect length byte
|
// Fix incorrect length byte
|
||||||
if(protocol->data[0] != 26 && protocol->data[0] != 50 && protocol->data[0] != 37 &&
|
if(protocol->data[0] != 26 && protocol->data[0] != 50 && protocol->data[0] != 37 &&
|
||||||
protocol->data[0] != 34 && protocol->data[0] != 36) {
|
protocol->data[0] != 34 && protocol->data[0] != 36 ) {
|
||||||
protocol->data[0] = 26;
|
protocol->data[0] = 26;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -232,7 +232,7 @@ bool protocol_awid_write_data(ProtocolAwid* protocol, void* data) {
|
|||||||
|
|
||||||
const ProtocolBase protocol_awid = {
|
const ProtocolBase protocol_awid = {
|
||||||
.name = "AWID",
|
.name = "AWID",
|
||||||
.manufacturer = "AWIG",
|
.manufacturer = "AWID",
|
||||||
.data_size = AWID_DECODED_DATA_SIZE,
|
.data_size = AWID_DECODED_DATA_SIZE,
|
||||||
.features = LFRFIDFeatureASK,
|
.features = LFRFIDFeatureASK,
|
||||||
.validate_count = 3,
|
.validate_count = 3,
|
||||||
|
@ -41,25 +41,3 @@ def link_dir(target_path, source_path, is_windows):
|
|||||||
|
|
||||||
def single_quote(arg_list):
|
def single_quote(arg_list):
|
||||||
return " ".join(f"'{arg}'" if " " in arg else str(arg) for arg in arg_list)
|
return " ".join(f"'{arg}'" if " " in arg else str(arg) for arg in arg_list)
|
||||||
|
|
||||||
|
|
||||||
def link_elf_dir_as_latest(env, elf_node):
|
|
||||||
elf_dir = elf_node.Dir(".")
|
|
||||||
latest_dir = env.Dir("#build/latest")
|
|
||||||
print(f"Setting {elf_dir} as latest built dir (./build/latest/)")
|
|
||||||
return link_dir(latest_dir.abspath, elf_dir.abspath, env["PLATFORM"] == "win32")
|
|
||||||
|
|
||||||
|
|
||||||
def should_gen_cdb_and_link_dir(env, requested_targets):
|
|
||||||
explicitly_building_updater = False
|
|
||||||
# Hacky way to check if updater-related targets were requested
|
|
||||||
for build_target in requested_targets:
|
|
||||||
if "updater" in str(build_target):
|
|
||||||
explicitly_building_updater = True
|
|
||||||
|
|
||||||
is_updater = not env["IS_BASE_FIRMWARE"]
|
|
||||||
# If updater is explicitly requested, link to the latest updater
|
|
||||||
# Otherwise, link to firmware
|
|
||||||
return (is_updater and explicitly_building_updater) or (
|
|
||||||
not is_updater and not explicitly_building_updater
|
|
||||||
)
|
|
41
scripts/fbt_tools/fbt_debugopts.py
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
def generate(env, **kw):
|
||||||
|
env.SetDefault(
|
||||||
|
OPENOCD_GDB_PIPE=[
|
||||||
|
"|openocd -c 'gdb_port pipe; log_output debug/openocd.log' ${[SINGLEQUOTEFUNC(OPENOCD_OPTS)]}"
|
||||||
|
],
|
||||||
|
GDBOPTS_BASE=[
|
||||||
|
"-ex",
|
||||||
|
"target extended-remote ${GDBREMOTE}",
|
||||||
|
"-ex",
|
||||||
|
"set confirm off",
|
||||||
|
"-ex",
|
||||||
|
"set pagination off",
|
||||||
|
],
|
||||||
|
GDBOPTS_BLACKMAGIC=[
|
||||||
|
"-ex",
|
||||||
|
"monitor swdp_scan",
|
||||||
|
"-ex",
|
||||||
|
"monitor debug_bmp enable",
|
||||||
|
"-ex",
|
||||||
|
"attach 1",
|
||||||
|
"-ex",
|
||||||
|
"set mem inaccessible-by-default off",
|
||||||
|
],
|
||||||
|
GDBPYOPTS=[
|
||||||
|
"-ex",
|
||||||
|
"source debug/FreeRTOS/FreeRTOS.py",
|
||||||
|
"-ex",
|
||||||
|
"source debug/flipperapps.py",
|
||||||
|
"-ex",
|
||||||
|
"source debug/PyCortexMDebug/PyCortexMDebug.py",
|
||||||
|
"-ex",
|
||||||
|
"svd_load ${SVD_FILE}",
|
||||||
|
"-ex",
|
||||||
|
"compare-sections",
|
||||||
|
],
|
||||||
|
JFLASHPROJECT="${ROOT_DIR.abspath}/debug/fw.jflash",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def exists(env):
|
||||||
|
return True
|
@ -136,7 +136,6 @@ def generate(env):
|
|||||||
"CoproBuilder": Builder(
|
"CoproBuilder": Builder(
|
||||||
action=Action(
|
action=Action(
|
||||||
[
|
[
|
||||||
Mkdir("$TARGET"),
|
|
||||||
'${PYTHON3} "${ROOT_DIR.abspath}/scripts/assets.py" '
|
'${PYTHON3} "${ROOT_DIR.abspath}/scripts/assets.py" '
|
||||||
"copro ${COPRO_CUBE_DIR} "
|
"copro ${COPRO_CUBE_DIR} "
|
||||||
"${TARGET} ${COPRO_MCU_FAMILY} "
|
"${TARGET} ${COPRO_MCU_FAMILY} "
|
||||||
@ -145,7 +144,7 @@ def generate(env):
|
|||||||
'--stack_file="${COPRO_STACK_BIN}" '
|
'--stack_file="${COPRO_STACK_BIN}" '
|
||||||
"--stack_addr=${COPRO_STACK_ADDR} ",
|
"--stack_addr=${COPRO_STACK_ADDR} ",
|
||||||
],
|
],
|
||||||
"",
|
"\tCOPRO\t${TARGET}",
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
}
|
}
|
@ -6,12 +6,10 @@ import SCons.Warnings
|
|||||||
import os
|
import os
|
||||||
import pathlib
|
import pathlib
|
||||||
from fbt.elfmanifest import assemble_manifest_data
|
from fbt.elfmanifest import assemble_manifest_data
|
||||||
from fbt.appmanifest import FlipperManifestException
|
from fbt.appmanifest import FlipperApplication, FlipperManifestException
|
||||||
from fbt.sdk import SdkCache
|
from fbt.sdk import SdkCache
|
||||||
import itertools
|
import itertools
|
||||||
|
|
||||||
from site_scons.fbt.appmanifest import FlipperApplication
|
|
||||||
|
|
||||||
|
|
||||||
def BuildAppElf(env, app):
|
def BuildAppElf(env, app):
|
||||||
ext_apps_work_dir = env.subst("$EXT_APPS_WORK_DIR")
|
ext_apps_work_dir = env.subst("$EXT_APPS_WORK_DIR")
|
||||||
@ -180,7 +178,7 @@ def validate_app_imports(target, source, env):
|
|||||||
if unresolved_syms:
|
if unresolved_syms:
|
||||||
SCons.Warnings.warn(
|
SCons.Warnings.warn(
|
||||||
SCons.Warnings.LinkWarning,
|
SCons.Warnings.LinkWarning,
|
||||||
f"{source[0].path}: app won't run. Unresolved symbols: {unresolved_syms}",
|
f"\033[93m{source[0].path}: app won't run. Unresolved symbols: \033[95m{unresolved_syms}\033[0m",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
44
scripts/fbt_tools/fbt_help.py
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
targets_help = """Configuration variables:
|
||||||
|
"""
|
||||||
|
|
||||||
|
tail_help = """
|
||||||
|
|
||||||
|
TASKS:
|
||||||
|
Building:
|
||||||
|
firmware_all, fw_dist:
|
||||||
|
Build firmware; create distribution package
|
||||||
|
faps, fap_dist:
|
||||||
|
Build all FAP apps
|
||||||
|
fap_{APPID}, launch_app APPSRC={APPID}:
|
||||||
|
Build FAP app with appid={APPID}; upload & start it over USB
|
||||||
|
|
||||||
|
Flashing & debugging:
|
||||||
|
flash, flash_blackmagic, jflash:
|
||||||
|
Flash firmware to target using debug probe
|
||||||
|
flash_usb, flash_usb_full:
|
||||||
|
Install firmware using self-update package
|
||||||
|
debug, debug_other, blackmagic:
|
||||||
|
Start GDB
|
||||||
|
|
||||||
|
Other:
|
||||||
|
cli:
|
||||||
|
Open a Flipper CLI session over USB
|
||||||
|
firmware_cdb, updater_cdb:
|
||||||
|
Generate сompilation_database.json
|
||||||
|
lint, lint_py:
|
||||||
|
run linters
|
||||||
|
format, format_py:
|
||||||
|
run code formatters
|
||||||
|
|
||||||
|
For more targets & info, see documentation/fbt.md
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
def generate(env, **kw):
|
||||||
|
vars = kw["vars"]
|
||||||
|
basic_help = vars.GenerateHelpText(env)
|
||||||
|
env.Help(targets_help + basic_help + tail_help)
|
||||||
|
|
||||||
|
|
||||||
|
def exists(env):
|
||||||
|
return True
|
@ -9,10 +9,32 @@ from SCons.Util import LogicalLines
|
|||||||
import os.path
|
import os.path
|
||||||
import posixpath
|
import posixpath
|
||||||
import pathlib
|
import pathlib
|
||||||
|
import json
|
||||||
|
|
||||||
from fbt.sdk import SdkCollector, SdkCache
|
from fbt.sdk import SdkCollector, SdkCache
|
||||||
|
|
||||||
|
|
||||||
|
def ProcessSdkDepends(env, filename):
|
||||||
|
try:
|
||||||
|
with open(filename, "r") as fin:
|
||||||
|
lines = LogicalLines(fin).readlines()
|
||||||
|
except IOError:
|
||||||
|
return []
|
||||||
|
|
||||||
|
_, depends = lines[0].split(":", 1)
|
||||||
|
depends = depends.split()
|
||||||
|
depends.pop(0) # remove the .c file
|
||||||
|
depends = list(
|
||||||
|
# Don't create dependency on non-existing files
|
||||||
|
# (e.g. when they were renamed since last build)
|
||||||
|
filter(
|
||||||
|
lambda file: file.exists(),
|
||||||
|
(env.File(f"#{path}") for path in depends),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
return depends
|
||||||
|
|
||||||
|
|
||||||
def prebuild_sdk_emitter(target, source, env):
|
def prebuild_sdk_emitter(target, source, env):
|
||||||
target.append(env.ChangeFileExtension(target[0], ".d"))
|
target.append(env.ChangeFileExtension(target[0], ".d"))
|
||||||
target.append(env.ChangeFileExtension(target[0], ".i.c"))
|
target.append(env.ChangeFileExtension(target[0], ".i.c"))
|
||||||
@ -25,6 +47,25 @@ def prebuild_sdk_create_origin_file(target, source, env):
|
|||||||
sdk_c.write("\n".join(f"#include <{h.path}>" for h in env["SDK_HEADERS"]))
|
sdk_c.write("\n".join(f"#include <{h.path}>" for h in env["SDK_HEADERS"]))
|
||||||
|
|
||||||
|
|
||||||
|
class SdkMeta:
|
||||||
|
def __init__(self, env):
|
||||||
|
self.env = env
|
||||||
|
|
||||||
|
def save_to(self, json_manifest_path: str):
|
||||||
|
meta_contents = {
|
||||||
|
"sdk_symbols": self.env["SDK_DEFINITION"].name,
|
||||||
|
"cc_args": self._wrap_scons_vars("$CCFLAGS $_CCCOMCOM"),
|
||||||
|
"cpp_args": self._wrap_scons_vars("$CXXFLAGS $CCFLAGS $_CCCOMCOM"),
|
||||||
|
"linker_args": self._wrap_scons_vars("$LINKFLAGS"),
|
||||||
|
}
|
||||||
|
with open(json_manifest_path, "wt") as f:
|
||||||
|
json.dump(meta_contents, f, indent=4)
|
||||||
|
|
||||||
|
def _wrap_scons_vars(self, vars: str):
|
||||||
|
expanded_vars = self.env.subst(vars, target=Entry("dummy"))
|
||||||
|
return expanded_vars.replace("\\", "/")
|
||||||
|
|
||||||
|
|
||||||
class SdkTreeBuilder:
|
class SdkTreeBuilder:
|
||||||
def __init__(self, env, target, source) -> None:
|
def __init__(self, env, target, source) -> None:
|
||||||
self.env = env
|
self.env = env
|
||||||
@ -34,8 +75,9 @@ class SdkTreeBuilder:
|
|||||||
self.header_depends = []
|
self.header_depends = []
|
||||||
self.header_dirs = []
|
self.header_dirs = []
|
||||||
|
|
||||||
self.target_sdk_dir = env.subst("f${TARGET_HW}_sdk")
|
self.target_sdk_dir_name = env.subst("f${TARGET_HW}_sdk")
|
||||||
self.sdk_deploy_dir = target[0].Dir(self.target_sdk_dir)
|
self.sdk_root_dir = target[0].Dir(".")
|
||||||
|
self.sdk_deploy_dir = self.sdk_root_dir.Dir(self.target_sdk_dir_name)
|
||||||
|
|
||||||
def _parse_sdk_depends(self):
|
def _parse_sdk_depends(self):
|
||||||
deps_file = self.source[0]
|
deps_file = self.source[0]
|
||||||
@ -50,7 +92,7 @@ class SdkTreeBuilder:
|
|||||||
)
|
)
|
||||||
|
|
||||||
def _generate_sdk_meta(self):
|
def _generate_sdk_meta(self):
|
||||||
filtered_paths = [self.target_sdk_dir]
|
filtered_paths = [self.target_sdk_dir_name]
|
||||||
full_fw_paths = list(
|
full_fw_paths = list(
|
||||||
map(
|
map(
|
||||||
os.path.normpath,
|
os.path.normpath,
|
||||||
@ -62,17 +104,18 @@ class SdkTreeBuilder:
|
|||||||
for dir in full_fw_paths:
|
for dir in full_fw_paths:
|
||||||
if dir in sdk_dirs:
|
if dir in sdk_dirs:
|
||||||
filtered_paths.append(
|
filtered_paths.append(
|
||||||
posixpath.normpath(posixpath.join(self.target_sdk_dir, dir))
|
posixpath.normpath(posixpath.join(self.target_sdk_dir_name, dir))
|
||||||
)
|
)
|
||||||
|
|
||||||
sdk_env = self.env.Clone()
|
sdk_env = self.env.Clone()
|
||||||
sdk_env.Replace(CPPPATH=filtered_paths)
|
sdk_env.Replace(CPPPATH=filtered_paths)
|
||||||
with open(self.target[0].path, "wt") as f:
|
meta = SdkMeta(sdk_env)
|
||||||
cmdline_options = sdk_env.subst(
|
meta.save_to(self.target[0].path)
|
||||||
"$CCFLAGS $_CCCOMCOM", target=Entry("dummy")
|
|
||||||
)
|
def emitter(self, target, source, env):
|
||||||
f.write(cmdline_options.replace("\\", "/"))
|
target_folder = target[0]
|
||||||
f.write("\n")
|
target = [target_folder.File("sdk.opts")]
|
||||||
|
return target, source
|
||||||
|
|
||||||
def _create_deploy_commands(self):
|
def _create_deploy_commands(self):
|
||||||
dirs_to_create = set(
|
dirs_to_create = set(
|
||||||
@ -81,13 +124,17 @@ class SdkTreeBuilder:
|
|||||||
actions = [
|
actions = [
|
||||||
Delete(self.sdk_deploy_dir),
|
Delete(self.sdk_deploy_dir),
|
||||||
Mkdir(self.sdk_deploy_dir),
|
Mkdir(self.sdk_deploy_dir),
|
||||||
|
Copy(
|
||||||
|
self.sdk_root_dir,
|
||||||
|
self.env["SDK_DEFINITION"],
|
||||||
|
),
|
||||||
]
|
]
|
||||||
actions += [Mkdir(d) for d in dirs_to_create]
|
actions += [Mkdir(d) for d in dirs_to_create]
|
||||||
|
|
||||||
actions += [
|
actions += [
|
||||||
Copy(
|
Action(
|
||||||
self.sdk_deploy_dir.File(h).path,
|
Copy(self.sdk_deploy_dir.File(h).path, h),
|
||||||
h,
|
# f"Copy {h} to {self.sdk_deploy_dir}",
|
||||||
)
|
)
|
||||||
for h in self.header_depends
|
for h in self.header_depends
|
||||||
]
|
]
|
||||||
@ -108,6 +155,11 @@ def deploy_sdk_tree(target, source, env, for_signature):
|
|||||||
return sdk_tree.generate_actions()
|
return sdk_tree.generate_actions()
|
||||||
|
|
||||||
|
|
||||||
|
def deploy_sdk_tree_emitter(target, source, env):
|
||||||
|
sdk_tree = SdkTreeBuilder(env, target, source)
|
||||||
|
return sdk_tree.emitter(target, source, env)
|
||||||
|
|
||||||
|
|
||||||
def gen_sdk_data(sdk_cache: SdkCache):
|
def gen_sdk_data(sdk_cache: SdkCache):
|
||||||
api_def = []
|
api_def = []
|
||||||
api_def.extend(
|
api_def.extend(
|
||||||
@ -165,6 +217,7 @@ def generate_sdk_symbols(source, target, env):
|
|||||||
|
|
||||||
|
|
||||||
def generate(env, **kw):
|
def generate(env, **kw):
|
||||||
|
env.AddMethod(ProcessSdkDepends)
|
||||||
env.Append(
|
env.Append(
|
||||||
BUILDERS={
|
BUILDERS={
|
||||||
"SDKPrebuilder": Builder(
|
"SDKPrebuilder": Builder(
|
||||||
@ -183,6 +236,7 @@ def generate(env, **kw):
|
|||||||
),
|
),
|
||||||
"SDKTree": Builder(
|
"SDKTree": Builder(
|
||||||
generator=deploy_sdk_tree,
|
generator=deploy_sdk_tree,
|
||||||
|
emitter=deploy_sdk_tree_emitter,
|
||||||
src_suffix=".d",
|
src_suffix=".d",
|
||||||
),
|
),
|
||||||
"SDKSymUpdater": Builder(
|
"SDKSymUpdater": Builder(
|
@ -1,10 +1,9 @@
|
|||||||
import logging
|
import logging
|
||||||
import datetime
|
|
||||||
import shutil
|
|
||||||
import json
|
import json
|
||||||
from os.path import basename
|
from io import BytesIO
|
||||||
|
import tarfile
|
||||||
import xml.etree.ElementTree as ET
|
import xml.etree.ElementTree as ET
|
||||||
|
|
||||||
from flipper.utils import *
|
from flipper.utils import *
|
||||||
from flipper.assets.coprobin import CoproBinary, get_stack_type
|
from flipper.assets.coprobin import CoproBinary, get_stack_type
|
||||||
|
|
||||||
@ -51,20 +50,19 @@ class Copro:
|
|||||||
raise Exception(f"Unsupported cube version")
|
raise Exception(f"Unsupported cube version")
|
||||||
self.version = cube_version
|
self.version = cube_version
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _getFileName(name):
|
||||||
|
return os.path.join("core2_firmware", name)
|
||||||
|
|
||||||
def addFile(self, array, filename, **kwargs):
|
def addFile(self, array, filename, **kwargs):
|
||||||
source_file = os.path.join(self.mcu_copro, filename)
|
source_file = os.path.join(self.mcu_copro, filename)
|
||||||
destination_file = os.path.join(self.output_dir, filename)
|
self.output_tar.add(source_file, arcname=self._getFileName(filename))
|
||||||
shutil.copyfile(source_file, destination_file)
|
array.append({"name": filename, "sha256": file_sha256(source_file), **kwargs})
|
||||||
array.append(
|
|
||||||
{"name": filename, "sha256": file_sha256(destination_file), **kwargs}
|
def bundle(self, output_file, stack_file_name, stack_type, stack_addr=None):
|
||||||
)
|
self.output_tar = tarfile.open(output_file, "w:gz")
|
||||||
|
|
||||||
def bundle(self, output_dir, stack_file_name, stack_type, stack_addr=None):
|
|
||||||
if not os.path.isdir(output_dir):
|
|
||||||
raise Exception(f'"{output_dir}" doesn\'t exists')
|
|
||||||
self.output_dir = output_dir
|
|
||||||
stack_file = os.path.join(self.mcu_copro, stack_file_name)
|
stack_file = os.path.join(self.mcu_copro, stack_file_name)
|
||||||
manifest_file = os.path.join(self.output_dir, "Manifest.json")
|
|
||||||
# Form Manifest
|
# Form Manifest
|
||||||
manifest = dict(MANIFEST_TEMPLATE)
|
manifest = dict(MANIFEST_TEMPLATE)
|
||||||
manifest["manifest"]["timestamp"] = timestamp()
|
manifest["manifest"]["timestamp"] = timestamp()
|
||||||
@ -105,6 +103,10 @@ class Copro:
|
|||||||
stack_file_name,
|
stack_file_name,
|
||||||
address=f"0x{stack_addr:X}",
|
address=f"0x{stack_addr:X}",
|
||||||
)
|
)
|
||||||
# Save manifest to
|
|
||||||
with open(manifest_file, "w", newline="\n") as file:
|
# Save manifest
|
||||||
json.dump(manifest, file)
|
manifest_data = json.dumps(manifest, indent=4).encode("utf-8")
|
||||||
|
info = tarfile.TarInfo(self._getFileName("Manifest.json"))
|
||||||
|
info.size = len(manifest_data)
|
||||||
|
self.output_tar.addfile(info, BytesIO(manifest_data))
|
||||||
|
self.output_tar.close()
|
||||||
|
@ -17,7 +17,7 @@ class Main(App):
|
|||||||
async def rebuild(self, line):
|
async def rebuild(self, line):
|
||||||
self.clearConsole()
|
self.clearConsole()
|
||||||
self.logger.info(f"Triggered by: {line}")
|
self.logger.info(f"Triggered by: {line}")
|
||||||
proc = await asyncio.create_subprocess_exec("make")
|
proc = await asyncio.create_subprocess_exec("./fbt")
|
||||||
await proc.wait()
|
await proc.wait()
|
||||||
await asyncio.sleep(1)
|
await asyncio.sleep(1)
|
||||||
self.is_building = False
|
self.is_building = False
|
||||||
|
@ -1,10 +1,12 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
from flipper.app import App
|
from flipper.app import App
|
||||||
from os.path import join, exists
|
from os.path import join, exists, relpath
|
||||||
from os import makedirs, environ
|
from os import makedirs, walk, environ
|
||||||
from update import Main as UpdateMain
|
from update import Main as UpdateMain
|
||||||
import shutil
|
import shutil
|
||||||
|
import zipfile
|
||||||
|
import tarfile
|
||||||
|
|
||||||
|
|
||||||
class ProjectDir:
|
class ProjectDir:
|
||||||
@ -17,6 +19,8 @@ class ProjectDir:
|
|||||||
|
|
||||||
|
|
||||||
class Main(App):
|
class Main(App):
|
||||||
|
DIST_FILE_PREFIX = "flipper-z-"
|
||||||
|
|
||||||
def init(self):
|
def init(self):
|
||||||
self.subparsers = self.parser.add_subparsers(help="sub-command help")
|
self.subparsers = self.parser.add_subparsers(help="sub-command help")
|
||||||
|
|
||||||
@ -45,9 +49,13 @@ class Main(App):
|
|||||||
def get_project_filename(self, project, filetype):
|
def get_project_filename(self, project, filetype):
|
||||||
# Temporary fix
|
# Temporary fix
|
||||||
project_name = project.project
|
project_name = project.project
|
||||||
if project_name == "firmware" and filetype != "elf":
|
if project_name == "firmware":
|
||||||
project_name = "full"
|
if filetype == "zip":
|
||||||
return f"flipper-z-{self.target}-{project_name}-{self.args.suffix}.{filetype}"
|
project_name = "sdk"
|
||||||
|
elif filetype != "elf":
|
||||||
|
project_name = "full"
|
||||||
|
|
||||||
|
return f"{self.DIST_FILE_PREFIX}{self.target}-{project_name}-{self.args.suffix}.{filetype}"
|
||||||
|
|
||||||
def get_dist_filepath(self, filename):
|
def get_dist_filepath(self, filename):
|
||||||
return join(self.output_dir_path, filename)
|
return join(self.output_dir_path, filename)
|
||||||
@ -56,10 +64,28 @@ class Main(App):
|
|||||||
obj_directory = join("build", project.dir)
|
obj_directory = join("build", project.dir)
|
||||||
|
|
||||||
for filetype in ("elf", "bin", "dfu", "json"):
|
for filetype in ("elf", "bin", "dfu", "json"):
|
||||||
shutil.copyfile(
|
if exists(src_file := join(obj_directory, f"{project.project}.{filetype}")):
|
||||||
join(obj_directory, f"{project.project}.{filetype}"),
|
shutil.copyfile(
|
||||||
self.get_dist_filepath(self.get_project_filename(project, filetype)),
|
src_file,
|
||||||
)
|
self.get_dist_filepath(
|
||||||
|
self.get_project_filename(project, filetype)
|
||||||
|
),
|
||||||
|
)
|
||||||
|
if exists(sdk_folder := join(obj_directory, "sdk")):
|
||||||
|
with zipfile.ZipFile(
|
||||||
|
self.get_dist_filepath(self.get_project_filename(project, "zip")),
|
||||||
|
"w",
|
||||||
|
zipfile.ZIP_DEFLATED,
|
||||||
|
) as zf:
|
||||||
|
for root, dirs, files in walk(sdk_folder):
|
||||||
|
for file in files:
|
||||||
|
zf.write(
|
||||||
|
join(root, file),
|
||||||
|
relpath(
|
||||||
|
join(root, file),
|
||||||
|
sdk_folder,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
def copy(self):
|
def copy(self):
|
||||||
self.projects = dict(
|
self.projects = dict(
|
||||||
@ -103,9 +129,8 @@ class Main(App):
|
|||||||
)
|
)
|
||||||
|
|
||||||
if self.args.version:
|
if self.args.version:
|
||||||
bundle_dir = join(
|
bundle_dir_name = f"{self.target}-update-{self.args.suffix}"
|
||||||
self.output_dir_path, f"{self.target}-update-{self.args.suffix}"
|
bundle_dir = join(self.output_dir_path, bundle_dir_name)
|
||||||
)
|
|
||||||
bundle_args = [
|
bundle_args = [
|
||||||
"generate",
|
"generate",
|
||||||
"-d",
|
"-d",
|
||||||
@ -131,9 +156,6 @@ class Main(App):
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
bundle_args.extend(self.other_args)
|
bundle_args.extend(self.other_args)
|
||||||
self.logger.info(
|
|
||||||
f"Use this directory to self-update your Flipper:\n\t{bundle_dir}"
|
|
||||||
)
|
|
||||||
log_custom_fz_name = (
|
log_custom_fz_name = (
|
||||||
environ.get("CUSTOM_FLIPPER_NAME", None)
|
environ.get("CUSTOM_FLIPPER_NAME", None)
|
||||||
or ""
|
or ""
|
||||||
@ -143,7 +165,23 @@ class Main(App):
|
|||||||
f"Flipper Custom Name is set:\n\tName: {log_custom_fz_name} : length - {len(log_custom_fz_name)} chars"
|
f"Flipper Custom Name is set:\n\tName: {log_custom_fz_name} : length - {len(log_custom_fz_name)} chars"
|
||||||
)
|
)
|
||||||
|
|
||||||
return UpdateMain(no_exit=True)(bundle_args)
|
if (bundle_result := UpdateMain(no_exit=True)(bundle_args)) == 0:
|
||||||
|
self.logger.info(
|
||||||
|
f"Use this directory to self-update your Flipper:\n\t{bundle_dir}"
|
||||||
|
)
|
||||||
|
|
||||||
|
# Create tgz archive
|
||||||
|
with tarfile.open(
|
||||||
|
join(
|
||||||
|
self.output_dir_path,
|
||||||
|
f"{self.DIST_FILE_PREFIX}{bundle_dir_name}.tgz",
|
||||||
|
),
|
||||||
|
"w:gz",
|
||||||
|
compresslevel=9,
|
||||||
|
) as tar:
|
||||||
|
tar.add(bundle_dir, arcname=bundle_dir_name)
|
||||||
|
|
||||||
|
return bundle_result
|
||||||
|
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
|
@ -81,52 +81,46 @@ vars.AddVariables(
|
|||||||
help="Enable debug tools to be built",
|
help="Enable debug tools to be built",
|
||||||
default=False,
|
default=False,
|
||||||
),
|
),
|
||||||
)
|
BoolVariable(
|
||||||
|
"FAP_EXAMPLES",
|
||||||
vars.Add(
|
help="Enable example applications to be built",
|
||||||
"DIST_SUFFIX",
|
default=False,
|
||||||
help="Suffix for binaries in build output for dist targets",
|
),
|
||||||
default="local",
|
(
|
||||||
)
|
"DIST_SUFFIX",
|
||||||
|
"Suffix for binaries in build output for dist targets",
|
||||||
vars.Add(
|
"local",
|
||||||
"CUSTOM_FLIPPER_NAME",
|
),
|
||||||
help="Replaces OTP flipper name with custom string of 8 chars",
|
(
|
||||||
default="",
|
"UPDATE_VERSION_STRING",
|
||||||
)
|
"Version string for updater package",
|
||||||
|
"${DIST_SUFFIX}",
|
||||||
vars.Add(
|
),
|
||||||
"UPDATE_VERSION_STRING",
|
(
|
||||||
help="Version string for updater package",
|
"CUSTOM_FLIPPER_NAME",
|
||||||
default="${DIST_SUFFIX}",
|
"Replaces OTP flipper name with custom string of 8 chars",
|
||||||
)
|
"",
|
||||||
|
),
|
||||||
|
(
|
||||||
vars.Add(
|
"COPRO_CUBE_VERSION",
|
||||||
"COPRO_CUBE_VERSION",
|
"Cube version",
|
||||||
help="Cube version",
|
"",
|
||||||
default="",
|
),
|
||||||
)
|
(
|
||||||
|
"COPRO_STACK_ADDR",
|
||||||
vars.Add(
|
"Core2 Firmware address",
|
||||||
"COPRO_STACK_ADDR",
|
"0",
|
||||||
help="Core2 Firmware address",
|
),
|
||||||
default="0",
|
(
|
||||||
)
|
"COPRO_STACK_BIN",
|
||||||
|
"Core2 Firmware file name",
|
||||||
vars.Add(
|
"",
|
||||||
"COPRO_STACK_BIN",
|
),
|
||||||
help="Core2 Firmware file name",
|
(
|
||||||
default="",
|
"COPRO_DISCLAIMER",
|
||||||
)
|
"Value to pass to bundling script to confirm dangerous operations",
|
||||||
|
"",
|
||||||
vars.Add(
|
),
|
||||||
"COPRO_DISCLAIMER",
|
|
||||||
help="Value to pass to bundling script to confirm dangerous operations",
|
|
||||||
default="",
|
|
||||||
)
|
|
||||||
|
|
||||||
vars.AddVariables(
|
|
||||||
PathVariable(
|
PathVariable(
|
||||||
"COPRO_OB_DATA",
|
"COPRO_OB_DATA",
|
||||||
help="Path to OB reference data",
|
help="Path to OB reference data",
|
||||||
@ -167,86 +161,75 @@ vars.AddVariables(
|
|||||||
validator=PathVariable.PathAccept,
|
validator=PathVariable.PathAccept,
|
||||||
default="",
|
default="",
|
||||||
),
|
),
|
||||||
)
|
(
|
||||||
|
"FBT_TOOLCHAIN_VERSIONS",
|
||||||
vars.Add(
|
"Whitelisted toolchain versions (leave empty for no check)",
|
||||||
"FBT_TOOLCHAIN_VERSIONS",
|
tuple(),
|
||||||
help="Whitelisted toolchain versions (leave empty for no check)",
|
),
|
||||||
default=tuple(),
|
(
|
||||||
)
|
"OPENOCD_OPTS",
|
||||||
|
"Options to pass to OpenOCD",
|
||||||
vars.Add(
|
"",
|
||||||
"OPENOCD_OPTS",
|
),
|
||||||
help="Options to pass to OpenOCD",
|
(
|
||||||
default="",
|
"BLACKMAGIC",
|
||||||
)
|
"Blackmagic probe location",
|
||||||
|
"auto",
|
||||||
vars.Add(
|
),
|
||||||
"BLACKMAGIC",
|
(
|
||||||
help="Blackmagic probe location",
|
"UPDATE_SPLASH",
|
||||||
default="auto",
|
"Directory name with slideshow frames to render after installing update package",
|
||||||
)
|
"update_default",
|
||||||
|
),
|
||||||
vars.Add(
|
(
|
||||||
"UPDATE_SPLASH",
|
"LOADER_AUTOSTART",
|
||||||
help="Directory name with slideshow frames to render after installing update package",
|
"Application name to automatically run on Flipper boot",
|
||||||
default="update_default",
|
"",
|
||||||
)
|
),
|
||||||
|
(
|
||||||
vars.Add(
|
"FIRMWARE_APPS",
|
||||||
"LOADER_AUTOSTART",
|
"Map of (configuration_name->application_list)",
|
||||||
help="Application name to automatically run on Flipper boot",
|
{
|
||||||
default="",
|
"default": (
|
||||||
)
|
# Svc
|
||||||
|
"basic_services",
|
||||||
|
# Apps
|
||||||
vars.Add(
|
"main_apps",
|
||||||
"FIRMWARE_APPS",
|
"system_apps",
|
||||||
help="Map of (configuration_name->application_list)",
|
# Settings
|
||||||
default={
|
"settings_apps",
|
||||||
"default": (
|
# Plugins
|
||||||
# Svc
|
# "basic_plugins",
|
||||||
"basic_services",
|
# Debug
|
||||||
# Apps
|
# "debug_apps",
|
||||||
"main_apps",
|
)
|
||||||
"system_apps",
|
},
|
||||||
# Settings
|
),
|
||||||
"settings_apps",
|
(
|
||||||
# Plugins
|
"FIRMWARE_APP_SET",
|
||||||
# "basic_plugins",
|
"Application set to use from FIRMWARE_APPS",
|
||||||
# Debug
|
"default",
|
||||||
# "debug_apps",
|
),
|
||||||
)
|
(
|
||||||
},
|
"APPSRC",
|
||||||
)
|
"Application source directory for app to build & upload",
|
||||||
|
"",
|
||||||
vars.Add(
|
),
|
||||||
"FIRMWARE_APP_SET",
|
# List of tuples (directory, add_to_global_include_path)
|
||||||
help="Application set to use from FIRMWARE_APPS",
|
(
|
||||||
default="default",
|
"APPDIRS",
|
||||||
)
|
"Directories to search for firmware components & external apps",
|
||||||
|
[
|
||||||
vars.Add(
|
("applications", False),
|
||||||
"APPSRC",
|
("applications/services", True),
|
||||||
help="Application source directory for app to build & upload",
|
("applications/main", True),
|
||||||
default="",
|
("applications/settings", False),
|
||||||
)
|
("applications/system", False),
|
||||||
|
("applications/debug", False),
|
||||||
# List of tuples (directory, add_to_global_include_path)
|
("applications/plugins", False),
|
||||||
vars.Add(
|
("applications_user", False),
|
||||||
"APPDIRS",
|
],
|
||||||
help="Directories to search for firmware components & external apps",
|
),
|
||||||
default=[
|
|
||||||
("applications", False),
|
|
||||||
("applications/services", True),
|
|
||||||
("applications/main", True),
|
|
||||||
("applications/settings", False),
|
|
||||||
("applications/system", False),
|
|
||||||
("applications/debug", False),
|
|
||||||
("applications/plugins", False),
|
|
||||||
#("applications/examples", False),
|
|
||||||
("applications_user", False),
|
|
||||||
],
|
|
||||||
)
|
)
|
||||||
|
|
||||||
Return("vars")
|
Return("vars")
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
import SCons
|
|
||||||
from SCons.Platform import TempFileMunge
|
from SCons.Platform import TempFileMunge
|
||||||
from fbt import util
|
from fbt.util import tempfile_arg_esc_func, single_quote, wrap_tempfile
|
||||||
|
|
||||||
import os
|
import os
|
||||||
import multiprocessing
|
import multiprocessing
|
||||||
@ -13,15 +12,19 @@ forward_os_env = {
|
|||||||
}
|
}
|
||||||
# Proxying CI environment to child processes & scripts
|
# Proxying CI environment to child processes & scripts
|
||||||
variables_to_forward = [
|
variables_to_forward = [
|
||||||
|
# CI/CD variables
|
||||||
"WORKFLOW_BRANCH_OR_TAG",
|
"WORKFLOW_BRANCH_OR_TAG",
|
||||||
"DIST_SUFFIX",
|
"DIST_SUFFIX",
|
||||||
"CUSTOM_FLIPPER_NAME",
|
"CUSTOM_FLIPPER_NAME",
|
||||||
|
# Python & other tools
|
||||||
"HOME",
|
"HOME",
|
||||||
"APPDATA",
|
"APPDATA",
|
||||||
"PYTHONHOME",
|
"PYTHONHOME",
|
||||||
"PYTHONNOUSERSITE",
|
"PYTHONNOUSERSITE",
|
||||||
"TMP",
|
"TMP",
|
||||||
"TEMP",
|
"TEMP",
|
||||||
|
# Colors for tools
|
||||||
|
"TERM",
|
||||||
]
|
]
|
||||||
if proxy_env := GetOption("proxy_env"):
|
if proxy_env := GetOption("proxy_env"):
|
||||||
variables_to_forward.extend(proxy_env.split(","))
|
variables_to_forward.extend(proxy_env.split(","))
|
||||||
@ -80,7 +83,7 @@ if not coreenv["VERBOSE"]:
|
|||||||
SetOption("num_jobs", multiprocessing.cpu_count())
|
SetOption("num_jobs", multiprocessing.cpu_count())
|
||||||
# Avoiding re-scan of all sources on every startup
|
# Avoiding re-scan of all sources on every startup
|
||||||
SetOption("implicit_cache", True)
|
SetOption("implicit_cache", True)
|
||||||
SetOption("implicit_deps_unchanged", True)
|
# SetOption("implicit_deps_unchanged", True)
|
||||||
# More aggressive caching
|
# More aggressive caching
|
||||||
SetOption("max_drift", 1)
|
SetOption("max_drift", 1)
|
||||||
# Random task queue - to discover isses with build logic faster
|
# Random task queue - to discover isses with build logic faster
|
||||||
@ -88,10 +91,10 @@ SetOption("max_drift", 1)
|
|||||||
|
|
||||||
|
|
||||||
# Setting up temp file parameters - to overcome command line length limits
|
# Setting up temp file parameters - to overcome command line length limits
|
||||||
coreenv["TEMPFILEARGESCFUNC"] = util.tempfile_arg_esc_func
|
coreenv["TEMPFILEARGESCFUNC"] = tempfile_arg_esc_func
|
||||||
util.wrap_tempfile(coreenv, "LINKCOM")
|
wrap_tempfile(coreenv, "LINKCOM")
|
||||||
util.wrap_tempfile(coreenv, "ARCOM")
|
wrap_tempfile(coreenv, "ARCOM")
|
||||||
|
|
||||||
coreenv["SINGLEQUOTEFUNC"] = util.single_quote
|
coreenv["SINGLEQUOTEFUNC"] = single_quote
|
||||||
|
|
||||||
Return("coreenv")
|
Return("coreenv")
|
||||||
|
@ -21,7 +21,7 @@ appenv = ENV.Clone(
|
|||||||
)
|
)
|
||||||
|
|
||||||
appenv.Replace(
|
appenv.Replace(
|
||||||
LINKER_SCRIPT="application-ext",
|
LINKER_SCRIPT="application_ext",
|
||||||
)
|
)
|
||||||
|
|
||||||
appenv.AppendUnique(
|
appenv.AppendUnique(
|
||||||
@ -106,6 +106,7 @@ appenv.PhonyTarget("firmware_extapps", appenv.Action(legacy_app_build_stub, None
|
|||||||
|
|
||||||
|
|
||||||
Alias("faps", extapps["compact"].values())
|
Alias("faps", extapps["compact"].values())
|
||||||
|
Alias("faps", extapps["validators"].values())
|
||||||
|
|
||||||
if appsrc := appenv.subst("$APPSRC"):
|
if appsrc := appenv.subst("$APPSRC"):
|
||||||
app_manifest, fap_file, app_validator = appenv.GetExtAppFromPath(appsrc)
|
app_manifest, fap_file, app_validator = appenv.GetExtAppFromPath(appsrc)
|
||||||
|
23
site_scons/fbt_extra/util.py
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
from fbt.util import link_dir
|
||||||
|
|
||||||
|
|
||||||
|
def link_elf_dir_as_latest(env, elf_node):
|
||||||
|
elf_dir = elf_node.Dir(".")
|
||||||
|
latest_dir = env.Dir("#build/latest")
|
||||||
|
print(f"Setting {elf_dir} as latest built dir (./build/latest/)")
|
||||||
|
return link_dir(latest_dir.abspath, elf_dir.abspath, env["PLATFORM"] == "win32")
|
||||||
|
|
||||||
|
|
||||||
|
def should_gen_cdb_and_link_dir(env, requested_targets):
|
||||||
|
explicitly_building_updater = False
|
||||||
|
# Hacky way to check if updater-related targets were requested
|
||||||
|
for build_target in requested_targets:
|
||||||
|
if "updater" in str(build_target):
|
||||||
|
explicitly_building_updater = True
|
||||||
|
|
||||||
|
is_updater = not env["IS_BASE_FIRMWARE"]
|
||||||
|
# If updater is explicitly requested, link to the latest updater
|
||||||
|
# Otherwise, link to firmware
|
||||||
|
return (is_updater and explicitly_building_updater) or (
|
||||||
|
not is_updater and not explicitly_building_updater
|
||||||
|
)
|