Merge branch 'fz-dev' into dev
|
@ -51,10 +51,10 @@ static void dap_main_view_draw_callback(Canvas* canvas, void* _model) {
|
|||
canvas_set_color(canvas, ColorBlack);
|
||||
if(model->dap_active) {
|
||||
canvas_draw_icon(canvas, 14, 16, &I_ArrowUpFilled_12x18);
|
||||
canvas_draw_icon(canvas, 28, 16, &I_ArrowDownFilled_12x18);
|
||||
canvas_draw_icon_ex(canvas, 28, 16, &I_ArrowUpFilled_12x18, IconRotation180);
|
||||
} else {
|
||||
canvas_draw_icon(canvas, 14, 16, &I_ArrowUpEmpty_12x18);
|
||||
canvas_draw_icon(canvas, 28, 16, &I_ArrowDownEmpty_12x18);
|
||||
canvas_draw_icon_ex(canvas, 28, 16, &I_ArrowUpEmpty_12x18, IconRotation180);
|
||||
}
|
||||
|
||||
switch(model->mode) {
|
||||
|
@ -76,9 +76,9 @@ static void dap_main_view_draw_callback(Canvas* canvas, void* _model) {
|
|||
}
|
||||
|
||||
if(model->rx_active) {
|
||||
canvas_draw_icon(canvas, 101, 16, &I_ArrowDownFilled_12x18);
|
||||
canvas_draw_icon_ex(canvas, 101, 16, &I_ArrowUpFilled_12x18, IconRotation180);
|
||||
} else {
|
||||
canvas_draw_icon(canvas, 101, 16, &I_ArrowDownEmpty_12x18);
|
||||
canvas_draw_icon_ex(canvas, 101, 16, &I_ArrowUpEmpty_12x18, IconRotation180);
|
||||
}
|
||||
|
||||
canvas_draw_str_aligned(canvas, 100, 38, AlignCenter, AlignTop, "UART");
|
||||
|
|
Before Width: | Height: | Size: 160 B |
Before Width: | Height: | Size: 168 B |
|
@ -80,9 +80,9 @@ static void gpio_usb_uart_draw_callback(Canvas* canvas, void* _model) {
|
|||
canvas_draw_icon(canvas, 48, 14, &I_ArrowUpEmpty_14x15);
|
||||
|
||||
if(model->rx_active)
|
||||
canvas_draw_icon(canvas, 48, 34, &I_ArrowDownFilled_14x15);
|
||||
canvas_draw_icon_ex(canvas, 48, 34, &I_ArrowUpFilled_14x15, IconRotation180);
|
||||
else
|
||||
canvas_draw_icon(canvas, 48, 34, &I_ArrowDownEmpty_14x15);
|
||||
canvas_draw_icon_ex(canvas, 48, 34, &I_ArrowUpEmpty_14x15, IconRotation180);
|
||||
}
|
||||
|
||||
static bool gpio_usb_uart_input_callback(InputEvent* event, void* context) {
|
||||
|
|
|
@ -37,7 +37,7 @@ static void desktop_loader_callback(const void* message, void* context) {
|
|||
static void desktop_lock_icon_draw_callback(Canvas* canvas, void* context) {
|
||||
UNUSED(context);
|
||||
furi_assert(canvas);
|
||||
canvas_draw_icon(canvas, 0, 0, &I_Lock_8x8);
|
||||
canvas_draw_icon(canvas, 0, 0, &I_Lock_7x8);
|
||||
}
|
||||
|
||||
static void desktop_dummy_mode_icon_draw_callback(Canvas* canvas, void* context) {
|
||||
|
@ -233,7 +233,7 @@ Desktop* desktop_alloc() {
|
|||
|
||||
// Lock icon
|
||||
desktop->lock_icon_viewport = view_port_alloc();
|
||||
view_port_set_width(desktop->lock_icon_viewport, icon_get_width(&I_Lock_8x8));
|
||||
view_port_set_width(desktop->lock_icon_viewport, icon_get_width(&I_Lock_7x8));
|
||||
view_port_draw_callback_set(
|
||||
desktop->lock_icon_viewport, desktop_lock_icon_draw_callback, desktop);
|
||||
view_port_enabled_set(desktop->lock_icon_viewport, false);
|
||||
|
|
|
@ -115,16 +115,18 @@ static void desktop_view_pin_input_draw_cells(Canvas* canvas, DesktopViewPinInpu
|
|||
} else {
|
||||
switch(model->pin.data[i]) {
|
||||
case InputKeyDown:
|
||||
canvas_draw_icon(canvas, x + 3, y + 2, &I_Pin_arrow_down_7x9);
|
||||
canvas_draw_icon_ex(
|
||||
canvas, x + 3, y + 2, &I_Pin_arrow_up_7x9, IconRotation180);
|
||||
break;
|
||||
case InputKeyUp:
|
||||
canvas_draw_icon(canvas, x + 3, y + 2, &I_Pin_arrow_up_7x9);
|
||||
canvas_draw_icon_ex(canvas, x + 3, y + 2, &I_Pin_arrow_up_7x9, IconRotation0);
|
||||
break;
|
||||
case InputKeyLeft:
|
||||
canvas_draw_icon(canvas, x + 2, y + 3, &I_Pin_arrow_left_9x7);
|
||||
canvas_draw_icon_ex(
|
||||
canvas, x + 2, y + 3, &I_Pin_arrow_up_7x9, IconRotation270);
|
||||
break;
|
||||
case InputKeyRight:
|
||||
canvas_draw_icon(canvas, x + 2, y + 3, &I_Pin_arrow_right_9x7);
|
||||
canvas_draw_icon_ex(canvas, x + 2, y + 3, &I_Pin_arrow_up_7x9, IconRotation90);
|
||||
break;
|
||||
default:
|
||||
furi_assert(0);
|
||||
|
@ -147,7 +149,8 @@ static void desktop_view_pin_input_draw(Canvas* canvas, void* context) {
|
|||
desktop_view_pin_input_draw_cells(canvas, model);
|
||||
|
||||
if((model->pin.length > 0) && !model->locked_input) {
|
||||
canvas_draw_icon(canvas, 4, 53, &I_Pin_back_full_40x8);
|
||||
canvas_draw_icon(canvas, 4, 53, &I_Pin_back_arrow_10x8);
|
||||
canvas_draw_str(canvas, 16, 60, "= clear");
|
||||
}
|
||||
|
||||
if(model->button_label && ((model->pin.length >= MIN_PIN_SIZE) || model->locked_input)) {
|
||||
|
|
|
@ -224,7 +224,7 @@ void canvas_draw_bitmap(
|
|||
y += canvas->offset_y;
|
||||
uint8_t* bitmap_data = NULL;
|
||||
compress_icon_decode(canvas->compress_icon, compressed_bitmap_data, &bitmap_data);
|
||||
u8g2_DrawXBM(&canvas->fb, x, y, width, height, bitmap_data);
|
||||
canvas_draw_u8g2_bitmap(&canvas->fb, x, y, width, height, bitmap_data, IconRotation0);
|
||||
}
|
||||
|
||||
void canvas_draw_icon_animation(
|
||||
|
@ -240,13 +240,138 @@ void canvas_draw_icon_animation(
|
|||
uint8_t* icon_data = NULL;
|
||||
compress_icon_decode(
|
||||
canvas->compress_icon, icon_animation_get_data(icon_animation), &icon_data);
|
||||
u8g2_DrawXBM(
|
||||
canvas_draw_u8g2_bitmap(
|
||||
&canvas->fb,
|
||||
x,
|
||||
y,
|
||||
icon_animation_get_width(icon_animation),
|
||||
icon_animation_get_height(icon_animation),
|
||||
icon_data);
|
||||
icon_data,
|
||||
IconRotation0);
|
||||
}
|
||||
|
||||
static void canvas_draw_u8g2_bitmap_int(
|
||||
u8g2_t* u8g2,
|
||||
u8g2_uint_t x,
|
||||
u8g2_uint_t y,
|
||||
u8g2_uint_t w,
|
||||
u8g2_uint_t h,
|
||||
bool mirror,
|
||||
bool rotation,
|
||||
const uint8_t* bitmap) {
|
||||
u8g2_uint_t blen;
|
||||
blen = w;
|
||||
blen += 7;
|
||||
blen >>= 3;
|
||||
|
||||
if(rotation && !mirror) {
|
||||
x += w + 1;
|
||||
} else if(mirror && !rotation) {
|
||||
y += h - 1;
|
||||
}
|
||||
|
||||
while(h > 0) {
|
||||
const uint8_t* b = bitmap;
|
||||
uint16_t len = w;
|
||||
uint16_t x0 = x;
|
||||
uint16_t y0 = y;
|
||||
uint8_t mask;
|
||||
uint8_t color = u8g2->draw_color;
|
||||
uint8_t ncolor = (color == 0 ? 1 : 0);
|
||||
mask = 1;
|
||||
|
||||
while(len > 0) {
|
||||
if(u8x8_pgm_read(b) & mask) {
|
||||
u8g2->draw_color = color;
|
||||
u8g2_DrawHVLine(u8g2, x0, y0, 1, 0);
|
||||
} else if(u8g2->bitmap_transparency == 0) {
|
||||
u8g2->draw_color = ncolor;
|
||||
u8g2_DrawHVLine(u8g2, x0, y0, 1, 0);
|
||||
}
|
||||
|
||||
if(rotation) {
|
||||
y0++;
|
||||
} else {
|
||||
x0++;
|
||||
}
|
||||
|
||||
mask <<= 1;
|
||||
if(mask == 0) {
|
||||
mask = 1;
|
||||
b++;
|
||||
}
|
||||
len--;
|
||||
}
|
||||
|
||||
u8g2->draw_color = color;
|
||||
bitmap += blen;
|
||||
|
||||
if(mirror) {
|
||||
if(rotation) {
|
||||
x++;
|
||||
} else {
|
||||
y--;
|
||||
}
|
||||
} else {
|
||||
if(rotation) {
|
||||
x--;
|
||||
} else {
|
||||
y++;
|
||||
}
|
||||
}
|
||||
h--;
|
||||
}
|
||||
}
|
||||
|
||||
void canvas_draw_u8g2_bitmap(
|
||||
u8g2_t* u8g2,
|
||||
u8g2_uint_t x,
|
||||
u8g2_uint_t y,
|
||||
u8g2_uint_t w,
|
||||
u8g2_uint_t h,
|
||||
const uint8_t* bitmap,
|
||||
IconRotation rotation) {
|
||||
u8g2_uint_t blen;
|
||||
blen = w;
|
||||
blen += 7;
|
||||
blen >>= 3;
|
||||
#ifdef U8G2_WITH_INTERSECTION
|
||||
if(u8g2_IsIntersection(u8g2, x, y, x + w, y + h) == 0) return;
|
||||
#endif /* U8G2_WITH_INTERSECTION */
|
||||
|
||||
switch(rotation) {
|
||||
case IconRotation0:
|
||||
canvas_draw_u8g2_bitmap_int(u8g2, x, y, w, h, 0, 0, bitmap);
|
||||
break;
|
||||
case IconRotation90:
|
||||
canvas_draw_u8g2_bitmap_int(u8g2, x, y, w, h, 0, 1, bitmap);
|
||||
break;
|
||||
case IconRotation180:
|
||||
canvas_draw_u8g2_bitmap_int(u8g2, x, y, w, h, 1, 0, bitmap);
|
||||
break;
|
||||
case IconRotation270:
|
||||
canvas_draw_u8g2_bitmap_int(u8g2, x, y, w, h, 1, 1, bitmap);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void canvas_draw_icon_ex(
|
||||
Canvas* canvas,
|
||||
uint8_t x,
|
||||
uint8_t y,
|
||||
const Icon* icon,
|
||||
IconRotation rotation) {
|
||||
furi_assert(canvas);
|
||||
furi_assert(icon);
|
||||
|
||||
x += canvas->offset_x;
|
||||
y += canvas->offset_y;
|
||||
uint8_t* icon_data = NULL;
|
||||
compress_icon_decode(canvas->compress_icon, icon_get_data(icon), &icon_data);
|
||||
canvas_draw_u8g2_bitmap(
|
||||
&canvas->fb, x, y, icon_get_width(icon), icon_get_height(icon), icon_data, rotation);
|
||||
}
|
||||
|
||||
void canvas_draw_icon(Canvas* canvas, uint8_t x, uint8_t y, const Icon* icon) {
|
||||
|
@ -257,7 +382,8 @@ void canvas_draw_icon(Canvas* canvas, uint8_t x, uint8_t y, const Icon* icon) {
|
|||
y += canvas->offset_y;
|
||||
uint8_t* icon_data = NULL;
|
||||
compress_icon_decode(canvas->compress_icon, icon_get_data(icon), &icon_data);
|
||||
u8g2_DrawXBM(&canvas->fb, x, y, icon_get_width(icon), icon_get_height(icon), icon_data);
|
||||
canvas_draw_u8g2_bitmap(
|
||||
&canvas->fb, x, y, icon_get_width(icon), icon_get_height(icon), icon_data, IconRotation0);
|
||||
}
|
||||
|
||||
void canvas_draw_dot(Canvas* canvas, uint8_t x, uint8_t y) {
|
||||
|
@ -367,7 +493,7 @@ void canvas_draw_xbm(
|
|||
furi_assert(canvas);
|
||||
x += canvas->offset_x;
|
||||
y += canvas->offset_y;
|
||||
u8g2_DrawXBM(&canvas->fb, x, y, w, h, bitmap);
|
||||
canvas_draw_u8g2_bitmap(&canvas->fb, x, y, w, h, bitmap, IconRotation0);
|
||||
}
|
||||
|
||||
void canvas_draw_glyph(Canvas* canvas, uint8_t x, uint8_t y, uint16_t ch) {
|
||||
|
|
|
@ -65,6 +65,22 @@ typedef struct {
|
|||
uint8_t descender;
|
||||
} CanvasFontParameters;
|
||||
|
||||
/** Icon flip */
|
||||
typedef enum {
|
||||
IconFlipNone,
|
||||
IconFlipHorizontal,
|
||||
IconFlipVertical,
|
||||
IconFlipBoth,
|
||||
} IconFlip;
|
||||
|
||||
/** Icon rotation */
|
||||
typedef enum {
|
||||
IconRotation0,
|
||||
IconRotation90,
|
||||
IconRotation180,
|
||||
IconRotation270,
|
||||
} IconRotation;
|
||||
|
||||
/** Canvas anonymous structure */
|
||||
typedef struct Canvas Canvas;
|
||||
|
||||
|
@ -218,6 +234,22 @@ void canvas_draw_bitmap(
|
|||
uint8_t height,
|
||||
const uint8_t* compressed_bitmap_data);
|
||||
|
||||
/** Draw icon at position defined by x,y with rotation and flip.
|
||||
*
|
||||
* @param canvas Canvas instance
|
||||
* @param x x coordinate
|
||||
* @param y y coordinate
|
||||
* @param icon Icon instance
|
||||
* @param flip IconFlip
|
||||
* @param rotation IconRotation
|
||||
*/
|
||||
void canvas_draw_icon_ex(
|
||||
Canvas* canvas,
|
||||
uint8_t x,
|
||||
uint8_t y,
|
||||
const Icon* icon,
|
||||
IconRotation rotation);
|
||||
|
||||
/** Draw animation at position defined by x,y.
|
||||
*
|
||||
* @param canvas Canvas instance
|
||||
|
|
|
@ -83,6 +83,25 @@ void canvas_set_orientation(Canvas* canvas, CanvasOrientation orientation);
|
|||
*/
|
||||
CanvasOrientation canvas_get_orientation(const Canvas* canvas);
|
||||
|
||||
/** Draw a u8g2 bitmap
|
||||
*
|
||||
* @param canvas Canvas instance
|
||||
* @param x x coordinate
|
||||
* @param y y coordinate
|
||||
* @param width width
|
||||
* @param height height
|
||||
* @param bitmap bitmap
|
||||
* @param rotation rotation
|
||||
*/
|
||||
void canvas_draw_u8g2_bitmap(
|
||||
u8g2_t* u8g2,
|
||||
uint8_t x,
|
||||
uint8_t y,
|
||||
uint8_t width,
|
||||
uint8_t height,
|
||||
const uint8_t* bitmap,
|
||||
uint8_t rotation);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
#include <flipper_application/api_hashtable/compilesort.hpp>
|
||||
|
||||
/* Generated table */
|
||||
#include <symbols.h>
|
||||
#include <firmware_api_table.h>
|
||||
|
||||
static_assert(!has_hash_collisions(elf_api_table), "Detected API method hash collision!");
|
||||
|
||||
|
|
Before Width: | Height: | Size: 654 B |
Before Width: | Height: | Size: 669 B |
Before Width: | Height: | Size: 3.5 KiB |
Before Width: | Height: | Size: 3.5 KiB |
Before Width: | Height: | Size: 3.5 KiB |
Before Width: | Height: | Size: 3.6 KiB |
Before Width: | Height: | Size: 303 B |
|
@ -75,12 +75,12 @@ Example for building an app from Rust sources:
|
|||
Library sources must be placed in a subfolder of the `lib` folder within the application's source folder.
|
||||
Each library is defined as a call to the `Lib()` function, accepting the following parameters:
|
||||
|
||||
- **name**: name of the library's folder. Required.
|
||||
- **fap_include_paths**: list of the library's relative paths to add to the parent fap's include path list. The default value is `["."]`, meaning the library's source root.
|
||||
- **sources**: list of filename masks to be used for gathering include files for this library. Paths are relative to the library's source root. The default value is `["*.c*"]`.
|
||||
- **cflags**: list of additional compiler flags to be used for building this library. The default value is `[]`.
|
||||
- **cdefines**: list of additional preprocessor definitions to be used for building this library. The default value is `[]`.
|
||||
- **cincludes**: list of additional include paths to be used for building this library. Paths are relative to the application's root. This can be used for providing external search paths for this library's code — for configuration headers. The default value is `[]`.
|
||||
- **name**: name of the library's folder. Required.
|
||||
- **fap_include_paths**: list of the library's relative paths to add to the parent fap's include path list. The default value is `["."]`, meaning the library's source root.
|
||||
- **sources**: list of filename masks to be used for gathering include files for this library. Paths are relative to the library's source root. The default value is `["*.c*"]`.
|
||||
- **cflags**: list of additional compiler flags to be used for building this library. The default value is `[]`.
|
||||
- **cdefines**: list of additional preprocessor definitions to be used for building this library. The default value is `[]`.
|
||||
- **cincludes**: list of additional include paths to be used for building this library. Paths are relative to the application's root. This can be used for providing external search paths for this library's code — for configuration headers. The default value is `[]`.
|
||||
|
||||
Example for building an app with a private library:
|
||||
|
||||
|
|
|
@ -3,6 +3,8 @@
|
|||
FBT is the entry point for firmware-related commands and utilities.
|
||||
It is invoked by `./fbt` in the firmware project root directory. Internally, it is a wrapper around [scons](https://scons.org/) build system.
|
||||
|
||||
If you don't need all features of `fbt` - like building the whole firmware - and only want to build and debug a single application, you can use [ufbt](https://pypi.org/project/ufbt/).
|
||||
|
||||
## Environment
|
||||
|
||||
To use `fbt`, you only need `git` installed in your system.
|
||||
|
|
|
@ -68,7 +68,7 @@ env = ENV.Clone(
|
|||
],
|
||||
},
|
||||
},
|
||||
SDK_APISYMS=None,
|
||||
FW_API_TABLE=None,
|
||||
_APP_ICONS=None,
|
||||
)
|
||||
|
||||
|
@ -241,7 +241,7 @@ Depends(
|
|||
[
|
||||
fwenv["FW_VERSION_JSON"],
|
||||
fwenv["FW_ASSETS_HEADERS"],
|
||||
fwenv["SDK_APISYMS"],
|
||||
fwenv["FW_API_TABLE"],
|
||||
fwenv["_APP_ICONS"],
|
||||
],
|
||||
)
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
entry,status,name,type,params
|
||||
Version,+,20.0,,
|
||||
Version,+,20.1,,
|
||||
Header,+,applications/services/bt/bt_service/bt.h,,
|
||||
Header,+,applications/services/cli/cli.h,,
|
||||
Header,+,applications/services/cli/cli_vcp.h,,
|
||||
|
@ -540,6 +540,7 @@ Function,+,canvas_draw_frame,void,"Canvas*, uint8_t, uint8_t, uint8_t, uint8_t"
|
|||
Function,+,canvas_draw_glyph,void,"Canvas*, uint8_t, uint8_t, uint16_t"
|
||||
Function,+,canvas_draw_icon,void,"Canvas*, uint8_t, uint8_t, const Icon*"
|
||||
Function,+,canvas_draw_icon_animation,void,"Canvas*, uint8_t, uint8_t, IconAnimation*"
|
||||
Function,+,canvas_draw_icon_ex,void,"Canvas*, uint8_t, uint8_t, const Icon*, IconRotation"
|
||||
Function,+,canvas_draw_line,void,"Canvas*, uint8_t, uint8_t, uint8_t, uint8_t"
|
||||
Function,+,canvas_draw_rbox,void,"Canvas*, uint8_t, uint8_t, uint8_t, uint8_t, uint8_t"
|
||||
Function,+,canvas_draw_rframe,void,"Canvas*, uint8_t, uint8_t, uint8_t, uint8_t, uint8_t"
|
||||
|
@ -876,12 +877,6 @@ Function,-,furi_hal_clock_resume_tick,void,
|
|||
Function,-,furi_hal_clock_suspend_tick,void,
|
||||
Function,-,furi_hal_clock_switch_to_hsi,void,
|
||||
Function,-,furi_hal_clock_switch_to_pll,void,
|
||||
Function,-,compress_alloc,Compress*,uint16_t
|
||||
Function,-,compress_decode,_Bool,"Compress*, uint8_t*, size_t, uint8_t*, size_t, size_t*"
|
||||
Function,-,compress_encode,_Bool,"Compress*, uint8_t*, size_t, uint8_t*, size_t, size_t*"
|
||||
Function,-,compress_free,void,Compress*
|
||||
Function,-,compress_icon_decode,void,"const uint8_t*, uint8_t**"
|
||||
Function,-,compress_icon_init,void,
|
||||
Function,+,furi_hal_console_disable,void,
|
||||
Function,+,furi_hal_console_enable,void,
|
||||
Function,+,furi_hal_console_init,void,
|
||||
|
|
|
|
@ -631,6 +631,7 @@ Function,+,canvas_draw_glyph,void,"Canvas*, uint8_t, uint8_t, uint16_t"
|
|||
Function,+,canvas_draw_icon,void,"Canvas*, uint8_t, uint8_t, const Icon*"
|
||||
Function,+,canvas_draw_icon_animation,void,"Canvas*, uint8_t, uint8_t, IconAnimation*"
|
||||
Function,+,canvas_draw_icon_bitmap,void,"Canvas*, uint8_t, uint8_t, int16_t, int16_t, const Icon*"
|
||||
Function,+,canvas_draw_icon_ex,void,"Canvas*, uint8_t, uint8_t, const Icon*, IconRotation"
|
||||
Function,+,canvas_draw_line,void,"Canvas*, uint8_t, uint8_t, uint8_t, uint8_t"
|
||||
Function,+,canvas_draw_rbox,void,"Canvas*, uint8_t, uint8_t, uint8_t, uint8_t, uint8_t"
|
||||
Function,+,canvas_draw_rframe,void,"Canvas*, uint8_t, uint8_t, uint8_t, uint8_t, uint8_t"
|
||||
|
|
|
|
@ -2,16 +2,14 @@
|
|||
#include <furi_hal.h>
|
||||
#include "sector_cache.h"
|
||||
|
||||
static volatile DSTATUS Stat = STA_NOINIT;
|
||||
|
||||
static DSTATUS driver_check_status(BYTE lun) {
|
||||
UNUSED(lun);
|
||||
Stat = STA_NOINIT;
|
||||
if(sd_get_card_state() == SdSpiStatusOK) {
|
||||
Stat &= ~STA_NOINIT;
|
||||
DSTATUS status = 0;
|
||||
if(sd_get_card_state() != SdSpiStatusOK) {
|
||||
status = STA_NOINIT;
|
||||
}
|
||||
|
||||
return Stat;
|
||||
return status;
|
||||
}
|
||||
|
||||
static DSTATUS driver_initialize(BYTE pdrv);
|
||||
|
@ -107,6 +105,16 @@ static bool sd_device_write(uint32_t* buff, uint32_t sector, uint32_t count) {
|
|||
* @retval DSTATUS: Operation status
|
||||
*/
|
||||
static DSTATUS driver_initialize(BYTE pdrv) {
|
||||
UNUSED(pdrv);
|
||||
return RES_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gets Disk Status
|
||||
* @param pdrv: Physical drive number (0..)
|
||||
* @retval DSTATUS: Operation status
|
||||
*/
|
||||
static DSTATUS driver_status(BYTE pdrv) {
|
||||
furi_hal_spi_acquire(&furi_hal_spi_bus_handle_sd_fast);
|
||||
furi_hal_sd_spi_handle = &furi_hal_spi_bus_handle_sd_fast;
|
||||
|
||||
|
@ -118,16 +126,6 @@ static DSTATUS driver_initialize(BYTE pdrv) {
|
|||
return status;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gets Disk Status
|
||||
* @param pdrv: Physical drive number (0..)
|
||||
* @retval DSTATUS: Operation status
|
||||
*/
|
||||
static DSTATUS driver_status(BYTE pdrv) {
|
||||
UNUSED(pdrv);
|
||||
return Stat;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Reads Sector(s)
|
||||
* @param pdrv: Physical drive number (0..)
|
||||
|
@ -224,15 +222,15 @@ static DRESULT driver_write(BYTE pdrv, const BYTE* buff, DWORD sector, UINT coun
|
|||
* @retval DRESULT: Operation result
|
||||
*/
|
||||
static DRESULT driver_ioctl(BYTE pdrv, BYTE cmd, void* buff) {
|
||||
UNUSED(pdrv);
|
||||
DRESULT res = RES_ERROR;
|
||||
SD_CardInfo CardInfo;
|
||||
|
||||
if(Stat & STA_NOINIT) return RES_NOTRDY;
|
||||
|
||||
furi_hal_spi_acquire(&furi_hal_spi_bus_handle_sd_fast);
|
||||
furi_hal_sd_spi_handle = &furi_hal_spi_bus_handle_sd_fast;
|
||||
|
||||
DSTATUS status = driver_check_status(pdrv);
|
||||
if(status & STA_NOINIT) return RES_NOTRDY;
|
||||
|
||||
switch(cmd) {
|
||||
/* Make sure that no pending write process */
|
||||
case CTRL_SYNC:
|
||||
|
|
|
@ -53,8 +53,8 @@ void furi_hal_gpio_init(
|
|||
const GpioPull pull,
|
||||
const GpioSpeed speed) {
|
||||
// we cannot set alternate mode in this function
|
||||
furi_assert(mode != GpioModeAltFunctionPushPull);
|
||||
furi_assert(mode != GpioModeAltFunctionOpenDrain);
|
||||
furi_check(mode != GpioModeAltFunctionPushPull);
|
||||
furi_check(mode != GpioModeAltFunctionOpenDrain);
|
||||
|
||||
furi_hal_gpio_init_ex(gpio, mode, pull, speed, GpioAltFnUnused);
|
||||
}
|
||||
|
@ -178,7 +178,7 @@ void furi_hal_gpio_add_int_callback(const GpioPin* gpio, GpioExtiCallback cb, vo
|
|||
|
||||
FURI_CRITICAL_ENTER();
|
||||
uint8_t pin_num = furi_hal_gpio_get_pin_num(gpio);
|
||||
furi_assert(gpio_interrupt[pin_num].callback == NULL);
|
||||
furi_check(gpio_interrupt[pin_num].callback == NULL);
|
||||
gpio_interrupt[pin_num].callback = cb;
|
||||
gpio_interrupt[pin_num].context = ctx;
|
||||
gpio_interrupt[pin_num].ready = true;
|
||||
|
|
|
@ -2,29 +2,24 @@
|
|||
#include <furi_hal.h>
|
||||
#include <flipper.h>
|
||||
#include <alt_boot.h>
|
||||
#include <u8g2_glue.h>
|
||||
#include <assets_icons.h>
|
||||
#include <toolbox/compress.h>
|
||||
#include <gui/canvas.h>
|
||||
#include <gui/canvas_i.h>
|
||||
|
||||
void flipper_boot_dfu_show_splash() {
|
||||
// Initialize
|
||||
CompressIcon* compress_icon = compress_icon_alloc();
|
||||
Canvas* canvas = canvas_init();
|
||||
|
||||
u8g2_t* fb = malloc(sizeof(u8g2_t));
|
||||
memset(fb, 0, sizeof(u8g2_t));
|
||||
u8g2_Setup_st756x_flipper(fb, U8G2_R0, u8x8_hw_spi_stm32, u8g2_gpio_and_delay_stm32);
|
||||
u8g2_InitDisplay(fb);
|
||||
u8g2_SetDrawColor(fb, 0x01);
|
||||
uint8_t* splash_data = NULL;
|
||||
compress_icon_decode(compress_icon, icon_get_data(&I_DFU_128x50), &splash_data);
|
||||
u8g2_DrawXBM(fb, 0, 64 - 50, 128, 50, splash_data);
|
||||
u8g2_SetFont(fb, u8g2_font_helvB08_tr);
|
||||
u8g2_DrawStr(fb, 2, 8, "Update & Recovery Mode");
|
||||
u8g2_DrawStr(fb, 2, 21, "DFU Started");
|
||||
u8g2_SetPowerSave(fb, 0);
|
||||
u8g2_SendBuffer(fb);
|
||||
canvas_set_color(canvas, ColorBlack);
|
||||
canvas_set_font(canvas, FontPrimary);
|
||||
|
||||
compress_icon_free(compress_icon);
|
||||
canvas_draw_icon(canvas, 0, 64 - 50, &I_DFU_128x50);
|
||||
canvas_draw_str(canvas, 2, 8, "Update & Recovery Mode");
|
||||
canvas_draw_str(canvas, 2, 21, "DFU Started");
|
||||
canvas_commit(canvas);
|
||||
|
||||
canvas_free(canvas);
|
||||
}
|
||||
|
||||
void flipper_boot_dfu_exec() {
|
||||
|
|
|
@ -2,44 +2,43 @@
|
|||
#include <furi_hal.h>
|
||||
#include <flipper.h>
|
||||
#include <alt_boot.h>
|
||||
#include <u8g2_glue.h>
|
||||
#include <assets_icons.h>
|
||||
#include <toolbox/compress.h>
|
||||
#include <gui/canvas.h>
|
||||
#include <gui/canvas_i.h>
|
||||
|
||||
#define COUNTER_VALUE (136U)
|
||||
|
||||
static void flipper_boot_recovery_draw_splash(u8g2_t* fb, size_t progress) {
|
||||
static void flipper_boot_recovery_draw_progress(Canvas* canvas, size_t progress) {
|
||||
if(progress < COUNTER_VALUE) {
|
||||
// Fill the progress bar while the progress is going down
|
||||
u8g2_SetDrawColor(fb, 0x01);
|
||||
u8g2_DrawRFrame(fb, 59, 41, 69, 8, 2);
|
||||
canvas_draw_rframe(canvas, 59, 41, 69, 8, 2);
|
||||
size_t width = (COUNTER_VALUE - progress) * 68 / COUNTER_VALUE;
|
||||
u8g2_DrawBox(fb, 60, 42, width, 6);
|
||||
canvas_draw_box(canvas, 60, 42, width, 6);
|
||||
} else {
|
||||
u8g2_SetDrawColor(fb, 0x00);
|
||||
u8g2_DrawRBox(fb, 59, 41, 69, 8, 2);
|
||||
canvas_draw_rframe(canvas, 59, 41, 69, 8, 2);
|
||||
canvas_set_color(canvas, ColorWhite);
|
||||
canvas_draw_box(canvas, 60, 42, 67, 6);
|
||||
canvas_set_color(canvas, ColorBlack);
|
||||
}
|
||||
|
||||
u8g2_SendBuffer(fb);
|
||||
canvas_commit(canvas);
|
||||
}
|
||||
|
||||
void flipper_boot_recovery_draw_splash(Canvas* canvas) {
|
||||
canvas_set_color(canvas, ColorBlack);
|
||||
canvas_set_font(canvas, FontPrimary);
|
||||
|
||||
canvas_draw_icon(canvas, 0, 0, &I_Erase_pin_128x64);
|
||||
|
||||
canvas_commit(canvas);
|
||||
}
|
||||
|
||||
void flipper_boot_recovery_exec() {
|
||||
u8g2_t* fb = malloc(sizeof(u8g2_t));
|
||||
u8g2_Setup_st756x_flipper(fb, U8G2_R0, u8x8_hw_spi_stm32, u8g2_gpio_and_delay_stm32);
|
||||
u8g2_InitDisplay(fb);
|
||||
Canvas* canvas = canvas_init();
|
||||
|
||||
CompressIcon* compress_icon = compress_icon_alloc();
|
||||
uint8_t* splash_data = NULL;
|
||||
compress_icon_decode(compress_icon, icon_get_data(&I_Erase_pin_128x64), &splash_data);
|
||||
|
||||
u8g2_ClearBuffer(fb);
|
||||
u8g2_SetDrawColor(fb, 0x01);
|
||||
|
||||
// Draw the recovery picture
|
||||
u8g2_DrawXBM(fb, 0, 0, 128, 64, splash_data);
|
||||
u8g2_SendBuffer(fb);
|
||||
u8g2_SetPowerSave(fb, 0);
|
||||
compress_icon_free(compress_icon);
|
||||
// Show recovery splashscreen
|
||||
flipper_boot_recovery_draw_splash(canvas);
|
||||
|
||||
size_t counter = COUNTER_VALUE;
|
||||
while(counter) {
|
||||
|
@ -53,7 +52,7 @@ void flipper_boot_recovery_exec() {
|
|||
counter = COUNTER_VALUE;
|
||||
}
|
||||
|
||||
flipper_boot_recovery_draw_splash(fb, counter);
|
||||
flipper_boot_recovery_draw_progress(canvas, counter);
|
||||
}
|
||||
|
||||
if(!counter) {
|
||||
|
|
|
@ -45,16 +45,19 @@ void platformDisableIrqCallback() {
|
|||
|
||||
void platformSetIrqCallback(PlatformIrqCallback callback) {
|
||||
rfal_platform.callback = callback;
|
||||
rfal_platform.thread =
|
||||
furi_thread_alloc_ex("RfalIrqDriver", 1024, rfal_platform_irq_thread, NULL);
|
||||
furi_thread_mark_as_service(rfal_platform.thread);
|
||||
furi_thread_set_priority(rfal_platform.thread, FuriThreadPriorityIsr);
|
||||
furi_thread_start(rfal_platform.thread);
|
||||
|
||||
furi_hal_gpio_add_int_callback(&gpio_nfc_irq_rfid_pull, nfc_isr, NULL);
|
||||
// Disable interrupt callback as the pin is shared between 2 apps
|
||||
// It is enabled in rfalLowPowerModeStop()
|
||||
furi_hal_gpio_disable_int_callback(&gpio_nfc_irq_rfid_pull);
|
||||
if(!rfal_platform.thread) {
|
||||
rfal_platform.thread =
|
||||
furi_thread_alloc_ex("RfalIrqDriver", 1024, rfal_platform_irq_thread, NULL);
|
||||
furi_thread_mark_as_service(rfal_platform.thread);
|
||||
furi_thread_set_priority(rfal_platform.thread, FuriThreadPriorityIsr);
|
||||
furi_thread_start(rfal_platform.thread);
|
||||
|
||||
furi_hal_gpio_add_int_callback(&gpio_nfc_irq_rfid_pull, nfc_isr, NULL);
|
||||
// Disable interrupt callback as the pin is shared between 2 apps
|
||||
// It is enabled in rfalLowPowerModeStop()
|
||||
furi_hal_gpio_disable_int_callback(&gpio_nfc_irq_rfid_pull);
|
||||
}
|
||||
}
|
||||
|
||||
bool platformSpiTxRx(const uint8_t* txBuf, uint8_t* rxBuf, uint16_t len) {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
from SCons.Builder import Builder
|
||||
from SCons.Action import Action
|
||||
from SCons.Errors import SConsEnvironmentError
|
||||
from SCons.Errors import StopError
|
||||
|
||||
import os
|
||||
import subprocess
|
||||
|
@ -90,7 +90,7 @@ def proto_ver_generator(target, source, env):
|
|||
source_dir=src_dir,
|
||||
)
|
||||
except (subprocess.CalledProcessError, EnvironmentError) as e:
|
||||
raise SConsEnvironmentError("Git: describe failed")
|
||||
raise StopError("Git: describe failed")
|
||||
|
||||
git_major, git_minor = git_describe.split(".")
|
||||
version_file_data = (
|
||||
|
|
|
@ -21,6 +21,10 @@ from fbt.sdk.cache import SdkCache
|
|||
from fbt.util import extract_abs_dir_path
|
||||
|
||||
|
||||
_FAP_META_SECTION = ".fapmeta"
|
||||
_FAP_FILEASSETS_SECTION = ".fapassets"
|
||||
|
||||
|
||||
@dataclass
|
||||
class FlipperExternalAppInfo:
|
||||
app: FlipperApplication
|
||||
|
@ -234,6 +238,8 @@ def BuildAppElf(env, app):
|
|||
|
||||
|
||||
def prepare_app_metadata(target, source, env):
|
||||
metadata_node = next(filter(lambda t: t.name.endswith(_FAP_META_SECTION), target))
|
||||
|
||||
sdk_cache = SdkCache(env["SDK_DEFINITION"].path, load_version_only=True)
|
||||
|
||||
if not sdk_cache.is_buildable():
|
||||
|
@ -242,8 +248,7 @@ def prepare_app_metadata(target, source, env):
|
|||
)
|
||||
|
||||
app = env["APP"]
|
||||
meta_file_name = source[0].path + ".meta"
|
||||
with open(meta_file_name, "wb") as f:
|
||||
with open(metadata_node.abspath, "wb") as f:
|
||||
f.write(
|
||||
assemble_manifest_data(
|
||||
app_manifest=app,
|
||||
|
@ -337,24 +342,26 @@ def embed_app_metadata_emitter(target, source, env):
|
|||
if app.apptype == FlipperAppType.PLUGIN:
|
||||
target[0].name = target[0].name.replace(".fap", ".fal")
|
||||
|
||||
meta_file_name = source[0].path + ".meta"
|
||||
target.append("#" + meta_file_name)
|
||||
target.append(env.File(source[0].abspath + _FAP_META_SECTION))
|
||||
|
||||
if app.fap_file_assets:
|
||||
files_section = source[0].path + ".files.section"
|
||||
target.append("#" + files_section)
|
||||
target.append(env.File(source[0].abspath + _FAP_FILEASSETS_SECTION))
|
||||
|
||||
return (target, source)
|
||||
|
||||
|
||||
def prepare_app_files(target, source, env):
|
||||
files_section_node = next(
|
||||
filter(lambda t: t.name.endswith(_FAP_FILEASSETS_SECTION), target)
|
||||
)
|
||||
|
||||
app = env["APP"]
|
||||
directory = app._appdir.Dir(app.fap_file_assets)
|
||||
directory = env.Dir(app._apppath).Dir(app.fap_file_assets)
|
||||
if not directory.exists():
|
||||
raise UserError(f"File asset directory {directory} does not exist")
|
||||
|
||||
bundler = FileBundler(directory.abspath)
|
||||
bundler.export(source[0].path + ".files.section")
|
||||
bundler.export(files_section_node.abspath)
|
||||
|
||||
|
||||
def generate_embed_app_metadata_actions(source, target, env, for_signature):
|
||||
|
@ -367,15 +374,15 @@ def generate_embed_app_metadata_actions(source, target, env, for_signature):
|
|||
objcopy_str = (
|
||||
"${OBJCOPY} "
|
||||
"--remove-section .ARM.attributes "
|
||||
"--add-section .fapmeta=${SOURCE}.meta "
|
||||
"--add-section ${_FAP_META_SECTION}=${SOURCE}${_FAP_META_SECTION} "
|
||||
)
|
||||
|
||||
if app.fap_file_assets:
|
||||
actions.append(Action(prepare_app_files, "$APPFILE_COMSTR"))
|
||||
objcopy_str += "--add-section .fapassets=${SOURCE}.files.section "
|
||||
objcopy_str += "--add-section ${_FAP_FILEASSETS_SECTION}=${SOURCE}${_FAP_FILEASSETS_SECTION} "
|
||||
|
||||
objcopy_str += (
|
||||
"--set-section-flags .fapmeta=contents,noload,readonly,data "
|
||||
"--set-section-flags ${_FAP_META_SECTION}=contents,noload,readonly,data "
|
||||
"--strip-debug --strip-unneeded "
|
||||
"--add-gnu-debuglink=${SOURCE} "
|
||||
"${SOURCES} ${TARGET}"
|
||||
|
@ -391,6 +398,51 @@ def generate_embed_app_metadata_actions(source, target, env, for_signature):
|
|||
return Action(actions)
|
||||
|
||||
|
||||
def AddAppLaunchTarget(env, appname, launch_target_name):
|
||||
deploy_sources, flipp_dist_paths, validators = [], [], []
|
||||
run_script_extra_ars = ""
|
||||
|
||||
def _add_dist_targets(app_artifacts):
|
||||
validators.append(app_artifacts.validator)
|
||||
for _, ext_path in app_artifacts.dist_entries:
|
||||
deploy_sources.append(app_artifacts.compact)
|
||||
flipp_dist_paths.append(f"/ext/{ext_path}")
|
||||
return app_artifacts
|
||||
|
||||
def _add_host_app_to_targets(host_app):
|
||||
artifacts_app_to_run = env["EXT_APPS"].get(host_app.appid, None)
|
||||
_add_dist_targets(artifacts_app_to_run)
|
||||
for plugin in host_app._plugins:
|
||||
_add_dist_targets(env["EXT_APPS"].get(plugin.appid, None))
|
||||
|
||||
artifacts_app_to_run = env.GetExtAppByIdOrPath(appname)
|
||||
if artifacts_app_to_run.app.apptype == FlipperAppType.PLUGIN:
|
||||
# We deploy host app instead
|
||||
host_app = env["APPMGR"].get(artifacts_app_to_run.app.requires[0])
|
||||
|
||||
if host_app:
|
||||
if host_app.apptype == FlipperAppType.EXTERNAL:
|
||||
_add_host_app_to_targets(host_app)
|
||||
else:
|
||||
# host app is a built-in app
|
||||
run_script_extra_ars = f"-a {host_app.name}"
|
||||
_add_dist_targets(artifacts_app_to_run)
|
||||
else:
|
||||
raise UserError("Host app is unknown")
|
||||
else:
|
||||
_add_host_app_to_targets(artifacts_app_to_run.app)
|
||||
|
||||
# print(deploy_sources, flipp_dist_paths)
|
||||
env.PhonyTarget(
|
||||
launch_target_name,
|
||||
'${PYTHON3} "${APP_RUN_SCRIPT}" ${EXTRA_ARGS} -s ${SOURCES} -t ${FLIPPER_FILE_TARGETS}',
|
||||
source=deploy_sources,
|
||||
FLIPPER_FILE_TARGETS=flipp_dist_paths,
|
||||
EXTRA_ARGS=run_script_extra_ars,
|
||||
)
|
||||
env.Alias(launch_target_name, validators)
|
||||
|
||||
|
||||
def generate(env, **kw):
|
||||
env.SetDefault(
|
||||
EXT_APPS_WORK_DIR="${FBT_FAP_DEBUG_ELF_ROOT}",
|
||||
|
@ -410,10 +462,14 @@ def generate(env, **kw):
|
|||
EXT_APPS={}, # appid -> FlipperExternalAppInfo
|
||||
EXT_LIBS={},
|
||||
_APP_ICONS=[],
|
||||
_FAP_META_SECTION=_FAP_META_SECTION,
|
||||
_FAP_FILEASSETS_SECTION=_FAP_FILEASSETS_SECTION,
|
||||
)
|
||||
|
||||
env.AddMethod(BuildAppElf)
|
||||
env.AddMethod(GetExtAppByIdOrPath)
|
||||
env.AddMethod(AddAppLaunchTarget)
|
||||
|
||||
env.Append(
|
||||
BUILDERS={
|
||||
"FapDist": Builder(
|
||||
|
|
|
@ -38,13 +38,13 @@ def ProcessSdkDepends(env, filename):
|
|||
return depends
|
||||
|
||||
|
||||
def prebuild_sdk_emitter(target, source, env):
|
||||
def api_amalgam_emitter(target, source, env):
|
||||
target.append(env.ChangeFileExtension(target[0], ".d"))
|
||||
target.append(env.ChangeFileExtension(target[0], ".i.c"))
|
||||
return target, source
|
||||
|
||||
|
||||
def prebuild_sdk_create_origin_file(target, source, env):
|
||||
def api_amalgam_gen_origin_header(target, source, env):
|
||||
mega_file = env.subst("${TARGET}.c", target=target[0])
|
||||
with open(mega_file, "wt") as sdk_c:
|
||||
sdk_c.write(
|
||||
|
@ -87,6 +87,7 @@ class SdkMeta:
|
|||
class SdkTreeBuilder:
|
||||
SDK_DIR_SUBST = "SDK_ROOT_DIR"
|
||||
SDK_APP_EP_SUBST = "SDK_APP_EP_SUBST"
|
||||
HEADER_EXTENSIONS = [".h", ".hpp"]
|
||||
|
||||
def __init__(self, env, target, source) -> None:
|
||||
self.env = env
|
||||
|
@ -111,7 +112,10 @@ class SdkTreeBuilder:
|
|||
lines = LogicalLines(deps_f).readlines()
|
||||
_, depends = lines[0].split(":", 1)
|
||||
self.header_depends = list(
|
||||
filter(lambda fname: fname.endswith(".h"), depends.split()),
|
||||
filter(
|
||||
lambda fname: any(map(fname.endswith, self.HEADER_EXTENSIONS)),
|
||||
depends.split(),
|
||||
),
|
||||
)
|
||||
self.header_depends.append(self.sdk_env.subst("${LINKER_SCRIPT_PATH}"))
|
||||
self.header_depends.append(self.sdk_env.subst("${SDK_DEFINITION}"))
|
||||
|
@ -180,12 +184,12 @@ class SdkTreeBuilder:
|
|||
self._generate_sdk_meta()
|
||||
|
||||
|
||||
def deploy_sdk_tree_action(target, source, env):
|
||||
def deploy_sdk_header_tree_action(target, source, env):
|
||||
sdk_tree = SdkTreeBuilder(env, target, source)
|
||||
return sdk_tree.deploy_action()
|
||||
|
||||
|
||||
def deploy_sdk_tree_emitter(target, source, env):
|
||||
def deploy_sdk_header_tree_emitter(target, source, env):
|
||||
sdk_tree = SdkTreeBuilder(env, target, source)
|
||||
return sdk_tree.emitter(target, source, env)
|
||||
|
||||
|
@ -224,7 +228,7 @@ def _check_sdk_is_up2date(sdk_cache: SdkCache):
|
|||
)
|
||||
|
||||
|
||||
def validate_sdk_cache(source, target, env):
|
||||
def validate_api_cache(source, target, env):
|
||||
# print(f"Generating SDK for {source[0]} to {target[0]}")
|
||||
current_sdk = SdkCollector()
|
||||
current_sdk.process_source_file_for_sdk(source[0].path)
|
||||
|
@ -237,7 +241,7 @@ def validate_sdk_cache(source, target, env):
|
|||
_check_sdk_is_up2date(sdk_cache)
|
||||
|
||||
|
||||
def generate_sdk_symbols(source, target, env):
|
||||
def generate_api_table(source, target, env):
|
||||
sdk_cache = SdkCache(source[0].path)
|
||||
_check_sdk_is_up2date(sdk_cache)
|
||||
|
||||
|
@ -249,11 +253,11 @@ def generate_sdk_symbols(source, target, env):
|
|||
def generate(env, **kw):
|
||||
if not env["VERBOSE"]:
|
||||
env.SetDefault(
|
||||
SDK_PREGEN_COMSTR="\tPREGEN\t${TARGET}",
|
||||
SDK_COMSTR="\tSDKSRC\t${TARGET}",
|
||||
SDK_AMALGAMATE_HEADER_COMSTR="\tAPIPREP\t${TARGET}",
|
||||
SDK_AMALGAMATE_PP_COMSTR="\tAPIPP\t${TARGET}",
|
||||
SDKSYM_UPDATER_COMSTR="\tSDKCHK\t${TARGET}",
|
||||
SDKSYM_GENERATOR_COMSTR="\tSDKSYM\t${TARGET}",
|
||||
SDKDEPLOY_COMSTR="\tSDKTREE\t${TARGET}",
|
||||
APITABLE_GENERATOR_COMSTR="\tAPITBL\t${TARGET}",
|
||||
SDKTREE_COMSTR="\tSDKTREE\t${TARGET}",
|
||||
)
|
||||
|
||||
# Filtering out things cxxheaderparser cannot handle
|
||||
|
@ -274,40 +278,40 @@ def generate(env, **kw):
|
|||
env.AddMethod(ProcessSdkDepends)
|
||||
env.Append(
|
||||
BUILDERS={
|
||||
"SDKPrebuilder": Builder(
|
||||
emitter=prebuild_sdk_emitter,
|
||||
"ApiAmalgamator": Builder(
|
||||
emitter=api_amalgam_emitter,
|
||||
action=[
|
||||
Action(
|
||||
prebuild_sdk_create_origin_file,
|
||||
"$SDK_PREGEN_COMSTR",
|
||||
api_amalgam_gen_origin_header,
|
||||
"$SDK_AMALGAMATE_HEADER_COMSTR",
|
||||
),
|
||||
Action(
|
||||
"$CC -o $TARGET -E -P $CCFLAGS $_CCCOMCOM $SDK_PP_FLAGS -MMD ${TARGET}.c",
|
||||
"$SDK_COMSTR",
|
||||
"$SDK_AMALGAMATE_PP_COMSTR",
|
||||
),
|
||||
],
|
||||
suffix=".i",
|
||||
),
|
||||
"SDKTree": Builder(
|
||||
"SDKHeaderTreeExtractor": Builder(
|
||||
action=Action(
|
||||
deploy_sdk_tree_action,
|
||||
"$SDKDEPLOY_COMSTR",
|
||||
deploy_sdk_header_tree_action,
|
||||
"$SDKTREE_COMSTR",
|
||||
),
|
||||
emitter=deploy_sdk_tree_emitter,
|
||||
emitter=deploy_sdk_header_tree_emitter,
|
||||
src_suffix=".d",
|
||||
),
|
||||
"SDKSymUpdater": Builder(
|
||||
"ApiTableValidator": Builder(
|
||||
action=Action(
|
||||
validate_sdk_cache,
|
||||
validate_api_cache,
|
||||
"$SDKSYM_UPDATER_COMSTR",
|
||||
),
|
||||
suffix=".csv",
|
||||
src_suffix=".i",
|
||||
),
|
||||
"SDKSymGenerator": Builder(
|
||||
"ApiSymbolTable": Builder(
|
||||
action=Action(
|
||||
generate_sdk_symbols,
|
||||
"$SDKSYM_GENERATOR_COMSTR",
|
||||
generate_api_table,
|
||||
"$APITABLE_GENERATOR_COMSTR",
|
||||
),
|
||||
suffix=".h",
|
||||
src_suffix=".csv",
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
import SCons.Warnings as Warnings
|
||||
from SCons.Errors import UserError
|
||||
|
||||
|
||||
# from SCons.Script.Main import find_deepest_user_frame
|
||||
|
||||
|
@ -36,6 +38,11 @@ def fbt_warning(e):
|
|||
|
||||
|
||||
def generate(env):
|
||||
if env.get("UFBT_WORK_DIR"):
|
||||
raise UserError(
|
||||
"You're trying to use a new format SDK on a legacy ufbt version. "
|
||||
"Please update ufbt to a version from PyPI: https://pypi.org/project/ufbt/"
|
||||
)
|
||||
Warnings._warningOut = fbt_warning
|
||||
|
||||
|
||||
|
|
|
@ -56,11 +56,11 @@ class StorageErrorCode(enum.Enum):
|
|||
|
||||
|
||||
class FlipperStorageException(Exception):
|
||||
def __init__(self, message):
|
||||
super().__init__(f"Storage error: {message}")
|
||||
|
||||
def __init__(self, path: str, error_code: StorageErrorCode):
|
||||
super().__init__(f"Storage error: path '{path}': {error_code.value}")
|
||||
@staticmethod
|
||||
def from_error_code(path: str, error_code: StorageErrorCode):
|
||||
return FlipperStorageException(
|
||||
f"Storage error: path '{path}': {error_code.value}"
|
||||
)
|
||||
|
||||
|
||||
class BufferedRead:
|
||||
|
@ -247,7 +247,9 @@ class FlipperStorage:
|
|||
if self.has_error(answer):
|
||||
last_error = self.get_error(answer)
|
||||
self.read.until(self.CLI_PROMPT)
|
||||
raise FlipperStorageException(filename_to, last_error)
|
||||
raise FlipperStorageException.from_error_code(
|
||||
filename_to, last_error
|
||||
)
|
||||
|
||||
self.port.write(filedata)
|
||||
self.read.until(self.CLI_PROMPT)
|
||||
|
@ -319,7 +321,7 @@ class FlipperStorage:
|
|||
StorageErrorCode.INVALID_NAME,
|
||||
):
|
||||
return False
|
||||
raise FlipperStorageException(path, error_code)
|
||||
raise FlipperStorageException.from_error_code(path, error_code)
|
||||
|
||||
return True
|
||||
|
||||
|
@ -333,7 +335,7 @@ class FlipperStorage:
|
|||
|
||||
def _check_no_error(self, response, path=None):
|
||||
if self.has_error(response):
|
||||
raise FlipperStorageException(self.get_error(response))
|
||||
raise FlipperStorageException.from_error_code(self.get_error(response))
|
||||
|
||||
def size(self, path: str):
|
||||
"""file size on Flipper"""
|
||||
|
|
|
@ -31,9 +31,10 @@ def parse_args():
|
|||
|
||||
def get_commit_json(event):
|
||||
context = ssl._create_unverified_context()
|
||||
with urllib.request.urlopen(
|
||||
event["pull_request"]["_links"]["commits"]["href"], context=context
|
||||
) as commit_file:
|
||||
commit_url = event["pull_request"]["base"]["repo"]["commits_url"].replace(
|
||||
"{/sha}", f"/{event['after']}"
|
||||
)
|
||||
with urllib.request.urlopen(commit_url, context=context) as commit_file:
|
||||
commit_json = json.loads(commit_file.read().decode("utf-8"))
|
||||
return commit_json
|
||||
|
||||
|
@ -43,8 +44,8 @@ def get_details(event, args):
|
|||
current_time = datetime.datetime.utcnow().date()
|
||||
if args.type == "pull":
|
||||
commit_json = get_commit_json(event)
|
||||
data["commit_comment"] = shlex.quote(commit_json[-1]["commit"]["message"])
|
||||
data["commit_hash"] = commit_json[-1]["sha"]
|
||||
data["commit_comment"] = shlex.quote(commit_json["commit"]["message"])
|
||||
data["commit_hash"] = commit_json["sha"]
|
||||
ref = event["pull_request"]["head"]["ref"]
|
||||
data["pull_id"] = event["pull_request"]["number"]
|
||||
data["pull_name"] = shlex.quote(event["pull_request"]["title"])
|
||||
|
|
|
@ -1,13 +1,15 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
from flipper.app import App
|
||||
from os.path import join, exists, relpath
|
||||
from os import makedirs, walk, environ
|
||||
from update import Main as UpdateMain
|
||||
import json
|
||||
import shutil
|
||||
import zipfile
|
||||
import tarfile
|
||||
import zipfile
|
||||
from os import makedirs, walk, environ
|
||||
from os.path import exists, join, relpath, basename, split
|
||||
|
||||
from ansi.color import fg
|
||||
from flipper.app import App
|
||||
from update import Main as UpdateMain
|
||||
|
||||
|
||||
class ProjectDir:
|
||||
|
@ -54,12 +56,19 @@ class Main(App):
|
|||
if project_name == "firmware" and filetype != "elf":
|
||||
project_name = "full"
|
||||
|
||||
return self.get_dist_file_name(project_name, filetype)
|
||||
dist_target_path = self.get_dist_file_name(project_name, filetype)
|
||||
self.note_dist_component(
|
||||
project_name, filetype, self.get_dist_path(dist_target_path)
|
||||
)
|
||||
return dist_target_path
|
||||
|
||||
def note_dist_component(self, component: str, extension: str, srcpath: str) -> None:
|
||||
self._dist_components[f"{component}.{extension}"] = srcpath
|
||||
|
||||
def get_dist_file_name(self, dist_artifact_type: str, filetype: str) -> str:
|
||||
return f"{self.DIST_FILE_PREFIX}{self.target}-{dist_artifact_type}-{self.args.suffix}.{filetype}"
|
||||
|
||||
def get_dist_file_path(self, filename: str) -> str:
|
||||
def get_dist_path(self, filename: str) -> str:
|
||||
return join(self.output_dir_path, filename)
|
||||
|
||||
def copy_single_project(self, project: ProjectDir) -> None:
|
||||
|
@ -69,17 +78,15 @@ class Main(App):
|
|||
if exists(src_file := join(obj_directory, f"{project.project}.{filetype}")):
|
||||
shutil.copyfile(
|
||||
src_file,
|
||||
self.get_dist_file_path(
|
||||
self.get_project_file_name(project, filetype)
|
||||
),
|
||||
self.get_dist_path(self.get_project_file_name(project, filetype)),
|
||||
)
|
||||
for foldertype in ("sdk", "lib"):
|
||||
for foldertype in ("sdk_headers", "lib"):
|
||||
if exists(sdk_folder := join(obj_directory, foldertype)):
|
||||
self.package_zip(foldertype, sdk_folder)
|
||||
self.note_dist_component(foldertype, "dir", sdk_folder)
|
||||
|
||||
def package_zip(self, foldertype, sdk_folder):
|
||||
with zipfile.ZipFile(
|
||||
self.get_dist_file_path(self.get_dist_file_name(foldertype, "zip")),
|
||||
self.get_dist_path(self.get_dist_file_name(foldertype, "zip")),
|
||||
"w",
|
||||
zipfile.ZIP_DEFLATED,
|
||||
) as zf:
|
||||
|
@ -94,7 +101,8 @@ class Main(App):
|
|||
)
|
||||
|
||||
def copy(self) -> int:
|
||||
self.projects = dict(
|
||||
self._dist_components: dict[str, str] = dict()
|
||||
self.projects: dict[str, ProjectDir] = dict(
|
||||
map(
|
||||
lambda pd: (pd.project, pd),
|
||||
map(ProjectDir, self.args.project),
|
||||
|
@ -122,12 +130,18 @@ class Main(App):
|
|||
try:
|
||||
shutil.rmtree(self.output_dir_path)
|
||||
except Exception as ex:
|
||||
pass
|
||||
self.logger.warn(f"Failed to clean output directory: {ex}")
|
||||
|
||||
if not exists(self.output_dir_path):
|
||||
self.logger.debug(f"Creating output directory {self.output_dir_path}")
|
||||
makedirs(self.output_dir_path)
|
||||
|
||||
for folder in ("debug", "scripts"):
|
||||
if exists(folder):
|
||||
self.note_dist_component(folder, "dir", folder)
|
||||
|
||||
for project in self.projects.values():
|
||||
self.logger.debug(f"Copying {project.project} for {project.target}")
|
||||
self.copy_single_project(project)
|
||||
|
||||
self.logger.info(
|
||||
|
@ -137,67 +151,141 @@ class Main(App):
|
|||
)
|
||||
|
||||
if self.args.version:
|
||||
bundle_dir_name = f"{self.target}-update-{self.args.suffix}"[
|
||||
: self.DIST_FOLDER_MAX_NAME_LENGTH
|
||||
]
|
||||
bundle_dir = join(self.output_dir_path, bundle_dir_name)
|
||||
bundle_args = [
|
||||
"generate",
|
||||
"-d",
|
||||
bundle_dir,
|
||||
"-v",
|
||||
self.args.version,
|
||||
"-t",
|
||||
self.target,
|
||||
"--dfu",
|
||||
self.get_dist_file_path(
|
||||
self.get_project_file_name(self.projects["firmware"], "dfu")
|
||||
),
|
||||
"--stage",
|
||||
self.get_dist_file_path(
|
||||
self.get_project_file_name(self.projects["updater"], "bin")
|
||||
),
|
||||
]
|
||||
if self.args.resources:
|
||||
bundle_args.extend(
|
||||
(
|
||||
"-r",
|
||||
self.args.resources,
|
||||
)
|
||||
)
|
||||
bundle_args.extend(self.other_args)
|
||||
log_custom_fz_name = (
|
||||
environ.get("CUSTOM_FLIPPER_NAME", None)
|
||||
or ""
|
||||
if bundle_result := self.bundle_update_package():
|
||||
return bundle_result
|
||||
|
||||
required_components = ("firmware.elf", "full.bin", "update.dir")
|
||||
if all(
|
||||
map(
|
||||
lambda c: c in self._dist_components,
|
||||
required_components,
|
||||
)
|
||||
if (log_custom_fz_name != "") and (len(log_custom_fz_name) <= 8) and (log_custom_fz_name.isalnum()) and (log_custom_fz_name.isascii()):
|
||||
self.logger.info(
|
||||
f"Flipper Custom Name is set:\n\tName: {log_custom_fz_name} : length - {len(log_custom_fz_name)} chars"
|
||||
)
|
||||
|
||||
if (bundle_result := UpdateMain(no_exit=True)(bundle_args)) == 0:
|
||||
self.logger.info(
|
||||
fg.boldgreen(
|
||||
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,
|
||||
format=tarfile.USTAR_FORMAT,
|
||||
) as tar:
|
||||
tar.add(bundle_dir, arcname=bundle_dir_name)
|
||||
|
||||
return bundle_result
|
||||
):
|
||||
self.bundle_sdk()
|
||||
|
||||
return 0
|
||||
|
||||
def bundle_sdk(self):
|
||||
self.logger.info("Bundling SDK")
|
||||
components_paths = dict()
|
||||
|
||||
sdk_components_keys = (
|
||||
"full.bin",
|
||||
"firmware.elf",
|
||||
"update.dir",
|
||||
"sdk_headers.dir",
|
||||
"lib.dir",
|
||||
"debug.dir",
|
||||
"scripts.dir",
|
||||
)
|
||||
|
||||
with zipfile.ZipFile(
|
||||
self.get_dist_path(self.get_dist_file_name("sdk", "zip")),
|
||||
"w",
|
||||
zipfile.ZIP_DEFLATED,
|
||||
) as zf:
|
||||
for component_key in sdk_components_keys:
|
||||
component_path = self._dist_components.get(component_key)
|
||||
components_paths[component_key] = basename(component_path)
|
||||
|
||||
if component_key.endswith(".dir"):
|
||||
for root, dirnames, files in walk(component_path):
|
||||
if "__pycache__" in dirnames:
|
||||
dirnames.remove("__pycache__")
|
||||
for file in files:
|
||||
zf.write(
|
||||
join(root, file),
|
||||
join(
|
||||
components_paths[component_key],
|
||||
relpath(
|
||||
join(root, file),
|
||||
component_path,
|
||||
),
|
||||
),
|
||||
)
|
||||
else:
|
||||
zf.write(component_path, basename(component_path))
|
||||
|
||||
zf.writestr(
|
||||
"components.json",
|
||||
json.dumps(
|
||||
{
|
||||
"meta": {
|
||||
"hw_target": self.target,
|
||||
"flavor": self.flavor,
|
||||
"version": self.args.version,
|
||||
},
|
||||
"components": components_paths,
|
||||
}
|
||||
),
|
||||
)
|
||||
|
||||
def bundle_update_package(self):
|
||||
self.logger.debug(
|
||||
f"Generating update bundle with version {self.args.version} for {self.target}"
|
||||
)
|
||||
bundle_dir_name = f"{self.target}-update-{self.args.suffix}"[
|
||||
: self.DIST_FOLDER_MAX_NAME_LENGTH
|
||||
]
|
||||
bundle_dir = self.get_dist_path(bundle_dir_name)
|
||||
bundle_args = [
|
||||
"generate",
|
||||
"-d",
|
||||
bundle_dir,
|
||||
"-v",
|
||||
self.args.version,
|
||||
"-t",
|
||||
self.target,
|
||||
"--dfu",
|
||||
self.get_dist_path(
|
||||
self.get_project_file_name(self.projects["firmware"], "dfu")
|
||||
),
|
||||
"--stage",
|
||||
self.get_dist_path(
|
||||
self.get_project_file_name(self.projects["updater"], "bin")
|
||||
),
|
||||
]
|
||||
if self.args.resources:
|
||||
bundle_args.extend(
|
||||
(
|
||||
"-r",
|
||||
self.args.resources,
|
||||
)
|
||||
)
|
||||
bundle_args.extend(self.other_args)
|
||||
|
||||
log_custom_fz_name = (
|
||||
environ.get("CUSTOM_FLIPPER_NAME", None)
|
||||
or ""
|
||||
)
|
||||
if (log_custom_fz_name != "") and (len(log_custom_fz_name) <= 8) and (log_custom_fz_name.isalnum()) and (log_custom_fz_name.isascii()):
|
||||
self.logger.info(f"Flipper Custom Name is set:\n\tName: {log_custom_fz_name} : length - {len(log_custom_fz_name)} chars"
|
||||
)
|
||||
|
||||
if (bundle_result := UpdateMain(no_exit=True)(bundle_args)) == 0:
|
||||
self.note_dist_component("update", "dir", bundle_dir)
|
||||
self.logger.info(
|
||||
fg.boldgreen(
|
||||
f"Use this directory to self-update your Flipper:\n\t{bundle_dir}"
|
||||
)
|
||||
)
|
||||
|
||||
# Create tgz archive
|
||||
with tarfile.open(
|
||||
join(
|
||||
self.output_dir_path,
|
||||
bundle_tgz := f"{self.DIST_FILE_PREFIX}{bundle_dir_name}.tgz",
|
||||
),
|
||||
"w:gz",
|
||||
compresslevel=9,
|
||||
format=tarfile.USTAR_FORMAT,
|
||||
) as tar:
|
||||
self.note_dist_component(
|
||||
"update", "tgz", self.get_dist_path(bundle_tgz)
|
||||
)
|
||||
tar.add(bundle_dir, arcname=bundle_dir_name)
|
||||
return bundle_result
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
Main()()
|
||||
|
|
|
@ -8,7 +8,7 @@ import sys
|
|||
def main():
|
||||
logger = logging.getLogger()
|
||||
if not (port := resolve_port(logger, "auto")):
|
||||
logger.error("Is Flipper connected over USB and isn't in DFU mode?")
|
||||
logger.error("Is Flipper connected over USB and is it not in DFU mode?")
|
||||
return 1
|
||||
subprocess.call(
|
||||
[
|
||||
|
|
|
@ -15,10 +15,12 @@ if not ["%FBT_NOENV%"] == [""] (
|
|||
|
||||
set "FLIPPER_TOOLCHAIN_VERSION=21"
|
||||
|
||||
if ["%FBT_TOOLCHAIN_ROOT%"] == [""] (
|
||||
set "FBT_TOOLCHAIN_ROOT=%FBT_ROOT%\toolchain\x86_64-windows"
|
||||
if ["%FBT_TOOLCHAIN_PATH%"] == [""] (
|
||||
set "FBT_TOOLCHAIN_PATH=%FBT_ROOT%"
|
||||
)
|
||||
|
||||
set "FBT_TOOLCHAIN_ROOT=%FBT_TOOLCHAIN_PATH%\toolchain\x86_64-windows"
|
||||
|
||||
set "FBT_TOOLCHAIN_VERSION_FILE=%FBT_TOOLCHAIN_ROOT%\VERSION"
|
||||
|
||||
if not exist "%FBT_TOOLCHAIN_ROOT%" (
|
||||
|
|
|
@ -4,9 +4,15 @@
|
|||
|
||||
# public variables
|
||||
DEFAULT_SCRIPT_PATH="$(pwd -P)";
|
||||
SCRIPT_PATH="${SCRIPT_PATH:-$DEFAULT_SCRIPT_PATH}";
|
||||
FBT_TOOLCHAIN_VERSION="${FBT_TOOLCHAIN_VERSION:-"21"}";
|
||||
FBT_TOOLCHAIN_PATH="${FBT_TOOLCHAIN_PATH:-$SCRIPT_PATH}";
|
||||
|
||||
if [ -z ${FBT_TOOLCHAIN_PATH+x} ] ; then
|
||||
FBT_TOOLCHAIN_PATH_WAS_SET=0;
|
||||
else
|
||||
FBT_TOOLCHAIN_PATH_WAS_SET=1;
|
||||
fi
|
||||
|
||||
FBT_TOOLCHAIN_PATH="${FBT_TOOLCHAIN_PATH:-$DEFAULT_SCRIPT_PATH}";
|
||||
FBT_VERBOSE="${FBT_VERBOSE:-""}";
|
||||
|
||||
fbtenv_show_usage()
|
||||
|
@ -60,7 +66,6 @@ fbtenv_restore_env()
|
|||
unset SAVED_PYTHONPATH;
|
||||
unset SAVED_PYTHONHOME;
|
||||
|
||||
unset SCRIPT_PATH;
|
||||
unset FBT_TOOLCHAIN_VERSION;
|
||||
unset FBT_TOOLCHAIN_PATH;
|
||||
}
|
||||
|
@ -104,13 +109,14 @@ fbtenv_set_shell_prompt()
|
|||
return 0; # all other shells
|
||||
}
|
||||
|
||||
fbtenv_check_script_path()
|
||||
fbtenv_check_env_vars()
|
||||
{
|
||||
if [ ! -x "$SCRIPT_PATH/fbt" ] && [ ! -x "$SCRIPT_PATH/ufbt" ] ; then
|
||||
echo "Please source this script from [u]fbt root directory, or specify 'SCRIPT_PATH' variable manually";
|
||||
# Return error if FBT_TOOLCHAIN_PATH is not set before script is sourced or if fbt executable is not in DEFAULT_SCRIPT_PATH
|
||||
if [ "$FBT_TOOLCHAIN_PATH_WAS_SET" -eq 0 ] && [ ! -x "$DEFAULT_SCRIPT_PATH/fbt" ] && [ ! -x "$DEFAULT_SCRIPT_PATH/ufbt" ] ; then
|
||||
echo "Please source this script from [u]fbt root directory, or specify 'FBT_TOOLCHAIN_PATH' variable manually";
|
||||
echo "Example:";
|
||||
printf "\tSCRIPT_PATH=lang/c/flipperzero-firmware source lang/c/flipperzero-firmware/scripts/fbtenv.sh\n";
|
||||
echo "If current directory is right, type 'unset SCRIPT_PATH' and try again"
|
||||
printf "\tFBT_TOOLCHAIN_PATH=lang/c/flipperzero-firmware source lang/c/flipperzero-firmware/scripts/fbtenv.sh\n";
|
||||
echo "If current directory is right, type 'unset FBT_TOOLCHAIN_PATH' and try again"
|
||||
return 1;
|
||||
fi
|
||||
return 0;
|
||||
|
@ -217,7 +223,7 @@ fbtenv_show_unpack_percentage()
|
|||
|
||||
fbtenv_unpack_toolchain()
|
||||
{
|
||||
echo "Unpacking toolchain:";
|
||||
echo "Unpacking toolchain to '$FBT_TOOLCHAIN_PATH/toolchain':";
|
||||
tar -xvf "$FBT_TOOLCHAIN_PATH/toolchain/$TOOLCHAIN_TAR" -C "$FBT_TOOLCHAIN_PATH/toolchain" 2>&1 | fbtenv_show_unpack_percentage;
|
||||
mkdir -p "$FBT_TOOLCHAIN_PATH/toolchain" || return 1;
|
||||
mv "$FBT_TOOLCHAIN_PATH/toolchain/$TOOLCHAIN_DIR" "$TOOLCHAIN_ARCH_DIR" || return 1;
|
||||
|
@ -225,7 +231,7 @@ fbtenv_unpack_toolchain()
|
|||
return 0;
|
||||
}
|
||||
|
||||
fbtenv_clearing()
|
||||
fbtenv_cleanup()
|
||||
{
|
||||
printf "Cleaning up..";
|
||||
if [ -n "${FBT_TOOLCHAIN_PATH:-""}" ]; then
|
||||
|
@ -280,14 +286,14 @@ fbtenv_download_toolchain()
|
|||
fbtenv_check_tar || return 1;
|
||||
TOOLCHAIN_TAR="$(basename "$TOOLCHAIN_URL")";
|
||||
TOOLCHAIN_DIR="$(echo "$TOOLCHAIN_TAR" | sed "s/-$FBT_TOOLCHAIN_VERSION.tar.gz//g")";
|
||||
trap fbtenv_clearing 2; # trap will be restored in fbtenv_clearing
|
||||
trap fbtenv_cleanup 2; # trap will be restored in fbtenv_cleanup
|
||||
if ! fbtenv_check_downloaded_toolchain; then
|
||||
fbtenv_curl_wget_check || return 1;
|
||||
fbtenv_download_toolchain_tar || return 1;
|
||||
fi
|
||||
fbtenv_remove_old_tooclhain;
|
||||
fbtenv_unpack_toolchain || return 1;
|
||||
fbtenv_clearing;
|
||||
fbtenv_cleanup;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -306,8 +312,8 @@ fbtenv_main()
|
|||
fbtenv_restore_env;
|
||||
return 0;
|
||||
fi
|
||||
fbtenv_check_if_sourced_multiple_times; # many source it's just a warning
|
||||
fbtenv_check_script_path || return 1;
|
||||
fbtenv_check_if_sourced_multiple_times;
|
||||
fbtenv_check_env_vars || return 1;
|
||||
fbtenv_check_download_toolchain || return 1;
|
||||
fbtenv_set_shell_prompt;
|
||||
fbtenv_print_version;
|
||||
|
|
393
scripts/ufbt/SConstruct
Normal file
|
@ -0,0 +1,393 @@
|
|||
from SCons.Platform import TempFileMunge
|
||||
from SCons.Node import FS
|
||||
from SCons.Errors import UserError
|
||||
|
||||
import os
|
||||
import multiprocessing
|
||||
import pathlib
|
||||
|
||||
SetOption("num_jobs", multiprocessing.cpu_count())
|
||||
SetOption("max_drift", 1)
|
||||
# SetOption("silent", False)
|
||||
|
||||
ufbt_state_dir = Dir(os.environ.get("UFBT_STATE_DIR", "#.ufbt"))
|
||||
ufbt_script_dir = Dir(os.environ.get("UFBT_SCRIPT_DIR"))
|
||||
|
||||
ufbt_current_sdk_dir = ufbt_state_dir.Dir("current")
|
||||
|
||||
SConsignFile(ufbt_state_dir.File(".sconsign.dblite").abspath)
|
||||
|
||||
ufbt_variables = SConscript("commandline.scons")
|
||||
|
||||
forward_os_env = {
|
||||
# Import PATH from OS env - scons doesn't do that by default
|
||||
"PATH": os.environ["PATH"],
|
||||
}
|
||||
|
||||
# Proxying environment to child processes & scripts
|
||||
variables_to_forward = [
|
||||
# CI/CD variables
|
||||
"WORKFLOW_BRANCH_OR_TAG",
|
||||
"DIST_SUFFIX",
|
||||
# Python & other tools
|
||||
"HOME",
|
||||
"APPDATA",
|
||||
"PYTHONHOME",
|
||||
"PYTHONNOUSERSITE",
|
||||
"TMP",
|
||||
"TEMP",
|
||||
# Colors for tools
|
||||
"TERM",
|
||||
]
|
||||
|
||||
if proxy_env := GetOption("proxy_env"):
|
||||
variables_to_forward.extend(proxy_env.split(","))
|
||||
|
||||
for env_value_name in variables_to_forward:
|
||||
if environ_value := os.environ.get(env_value_name, None):
|
||||
forward_os_env[env_value_name] = environ_value
|
||||
|
||||
# Core environment init - loads SDK state, sets up paths, etc.
|
||||
core_env = Environment(
|
||||
variables=ufbt_variables,
|
||||
ENV=forward_os_env,
|
||||
UFBT_STATE_DIR=ufbt_state_dir,
|
||||
UFBT_CURRENT_SDK_DIR=ufbt_current_sdk_dir,
|
||||
UFBT_SCRIPT_DIR=ufbt_script_dir,
|
||||
toolpath=[ufbt_current_sdk_dir.Dir("scripts/ufbt/site_tools")],
|
||||
tools=[
|
||||
"ufbt_state",
|
||||
("ufbt_help", {"vars": ufbt_variables}),
|
||||
],
|
||||
)
|
||||
|
||||
if "update" in BUILD_TARGETS:
|
||||
SConscript(
|
||||
"update.scons",
|
||||
exports={"core_env": core_env},
|
||||
)
|
||||
|
||||
if "purge" in BUILD_TARGETS:
|
||||
core_env.Execute(Delete(ufbt_state_dir))
|
||||
print("uFBT state purged")
|
||||
Exit(0)
|
||||
|
||||
# Now we can import stuff bundled with SDK - it was added to sys.path by ufbt_state
|
||||
|
||||
from fbt.util import (
|
||||
tempfile_arg_esc_func,
|
||||
single_quote,
|
||||
extract_abs_dir,
|
||||
extract_abs_dir_path,
|
||||
wrap_tempfile,
|
||||
path_as_posix,
|
||||
)
|
||||
from fbt.appmanifest import FlipperAppType
|
||||
from fbt.sdk.cache import SdkCache
|
||||
|
||||
# Base environment with all tools loaded from SDK
|
||||
env = core_env.Clone(
|
||||
toolpath=[core_env["FBT_SCRIPT_DIR"].Dir("fbt_tools")],
|
||||
tools=[
|
||||
"fbt_tweaks",
|
||||
(
|
||||
"crosscc",
|
||||
{
|
||||
"toolchain_prefix": "arm-none-eabi-",
|
||||
"versions": (" 10.3",),
|
||||
},
|
||||
),
|
||||
"fwbin",
|
||||
"python3",
|
||||
"sconsrecursiveglob",
|
||||
"sconsmodular",
|
||||
"ccache",
|
||||
"fbt_apps",
|
||||
"fbt_extapps",
|
||||
"fbt_assets",
|
||||
("compilation_db", {"COMPILATIONDB_COMSTR": "\tCDB\t${TARGET}"}),
|
||||
],
|
||||
FBT_FAP_DEBUG_ELF_ROOT=ufbt_state_dir.Dir("build"),
|
||||
TEMPFILE=TempFileMunge,
|
||||
MAXLINELENGTH=2048,
|
||||
PROGSUFFIX=".elf",
|
||||
TEMPFILEARGESCFUNC=tempfile_arg_esc_func,
|
||||
SINGLEQUOTEFUNC=single_quote,
|
||||
ABSPATHGETTERFUNC=extract_abs_dir_path,
|
||||
APPS=[],
|
||||
UFBT_API_VERSION=SdkCache(
|
||||
core_env.subst("$SDK_DEFINITION"), load_version_only=True
|
||||
).version,
|
||||
APPCHECK_COMSTR="\tAPPCHK\t${SOURCE}\n\t\tTarget: ${TARGET_HW}, API: ${UFBT_API_VERSION}",
|
||||
)
|
||||
|
||||
wrap_tempfile(env, "LINKCOM")
|
||||
wrap_tempfile(env, "ARCOM")
|
||||
|
||||
# print(env.Dump())
|
||||
|
||||
# Dist env
|
||||
|
||||
dist_env = env.Clone(
|
||||
tools=[
|
||||
"fbt_dist",
|
||||
"fbt_debugopts",
|
||||
"openocd",
|
||||
"blackmagic",
|
||||
"jflash",
|
||||
"textfile",
|
||||
],
|
||||
ENV=os.environ,
|
||||
OPENOCD_OPTS=[
|
||||
"-f",
|
||||
"interface/stlink.cfg",
|
||||
"-c",
|
||||
"transport select hla_swd",
|
||||
"-f",
|
||||
"${FBT_DEBUG_DIR}/stm32wbx.cfg",
|
||||
"-c",
|
||||
"stm32wbx.cpu configure -rtos auto",
|
||||
],
|
||||
)
|
||||
|
||||
openocd_target = dist_env.OpenOCDFlash(
|
||||
dist_env["UFBT_STATE_DIR"].File("flash"),
|
||||
dist_env["FW_BIN"],
|
||||
OPENOCD_COMMAND=[
|
||||
"-c",
|
||||
"program ${SOURCE.posix} reset exit 0x08000000",
|
||||
],
|
||||
)
|
||||
dist_env.Alias("firmware_flash", openocd_target)
|
||||
dist_env.Alias("flash", openocd_target)
|
||||
if env["FORCE"]:
|
||||
env.AlwaysBuild(openocd_target)
|
||||
|
||||
firmware_debug = dist_env.PhonyTarget(
|
||||
"debug",
|
||||
"${GDBPYCOM}",
|
||||
source=dist_env["FW_ELF"],
|
||||
GDBOPTS="${GDBOPTS_BASE}",
|
||||
GDBREMOTE="${OPENOCD_GDB_PIPE}",
|
||||
FBT_FAP_DEBUG_ELF_ROOT=path_as_posix(dist_env.subst("$FBT_FAP_DEBUG_ELF_ROOT")),
|
||||
)
|
||||
|
||||
dist_env.PhonyTarget(
|
||||
"blackmagic",
|
||||
"${GDBPYCOM}",
|
||||
source=dist_env["FW_ELF"],
|
||||
GDBOPTS="${GDBOPTS_BASE} ${GDBOPTS_BLACKMAGIC}",
|
||||
GDBREMOTE="${BLACKMAGIC_ADDR}",
|
||||
FBT_FAP_DEBUG_ELF_ROOT=path_as_posix(dist_env.subst("$FBT_FAP_DEBUG_ELF_ROOT")),
|
||||
)
|
||||
|
||||
dist_env.PhonyTarget(
|
||||
"flash_blackmagic",
|
||||
"$GDB $GDBOPTS $SOURCES $GDBFLASH",
|
||||
source=dist_env["FW_ELF"],
|
||||
GDBOPTS="${GDBOPTS_BASE} ${GDBOPTS_BLACKMAGIC}",
|
||||
GDBREMOTE="${BLACKMAGIC_ADDR}",
|
||||
GDBFLASH=[
|
||||
"-ex",
|
||||
"load",
|
||||
"-ex",
|
||||
"quit",
|
||||
],
|
||||
)
|
||||
|
||||
flash_usb_full = dist_env.UsbInstall(
|
||||
dist_env["UFBT_STATE_DIR"].File("usbinstall"),
|
||||
[],
|
||||
)
|
||||
dist_env.AlwaysBuild(flash_usb_full)
|
||||
dist_env.Alias("flash_usb", flash_usb_full)
|
||||
dist_env.Alias("flash_usb_full", flash_usb_full)
|
||||
|
||||
# App build environment
|
||||
|
||||
appenv = env.Clone(
|
||||
CCCOM=env["CCCOM"].replace("$CFLAGS", "$CFLAGS_APP $CFLAGS"),
|
||||
CXXCOM=env["CXXCOM"].replace("$CXXFLAGS", "$CXXFLAGS_APP $CXXFLAGS"),
|
||||
LINKCOM=env["LINKCOM"].replace("$LINKFLAGS", "$LINKFLAGS_APP $LINKFLAGS"),
|
||||
COMPILATIONDB_USE_ABSPATH=True,
|
||||
)
|
||||
|
||||
|
||||
original_app_dir = Dir(appenv.subst("$UFBT_APP_DIR"))
|
||||
app_mount_point = Dir("#/app/")
|
||||
app_mount_point.addRepository(original_app_dir)
|
||||
|
||||
appenv.LoadAppManifest(app_mount_point)
|
||||
appenv.PrepareApplicationsBuild()
|
||||
|
||||
#######################
|
||||
|
||||
apps_artifacts = appenv["EXT_APPS"]
|
||||
|
||||
apps_to_build_as_faps = [
|
||||
FlipperAppType.PLUGIN,
|
||||
FlipperAppType.EXTERNAL,
|
||||
]
|
||||
|
||||
known_extapps = [
|
||||
app
|
||||
for apptype in apps_to_build_as_faps
|
||||
for app in appenv["APPBUILD"].get_apps_of_type(apptype, True)
|
||||
]
|
||||
for app in known_extapps:
|
||||
app_artifacts = appenv.BuildAppElf(app)
|
||||
app_src_dir = extract_abs_dir(app_artifacts.app._appdir)
|
||||
app_artifacts.installer = [
|
||||
appenv.Install(app_src_dir.Dir("dist"), app_artifacts.compact),
|
||||
appenv.Install(app_src_dir.Dir("dist").Dir("debug"), app_artifacts.debug),
|
||||
]
|
||||
|
||||
if appenv["FORCE"]:
|
||||
appenv.AlwaysBuild([extapp.compact for extapp in apps_artifacts.values()])
|
||||
|
||||
# Final steps - target aliases
|
||||
|
||||
install_and_check = [
|
||||
(extapp.installer, extapp.validator) for extapp in apps_artifacts.values()
|
||||
]
|
||||
Alias(
|
||||
"faps",
|
||||
install_and_check,
|
||||
)
|
||||
Default(install_and_check)
|
||||
|
||||
# Compilation database
|
||||
|
||||
fwcdb = appenv.CompilationDatabase(
|
||||
original_app_dir.Dir(".vscode").File("compile_commands.json")
|
||||
)
|
||||
|
||||
AlwaysBuild(fwcdb)
|
||||
Precious(fwcdb)
|
||||
NoClean(fwcdb)
|
||||
if len(apps_artifacts):
|
||||
Default(fwcdb)
|
||||
|
||||
|
||||
# launch handler
|
||||
runnable_apps = appenv["APPBUILD"].get_apps_of_type(FlipperAppType.EXTERNAL, True)
|
||||
|
||||
app_to_launch = None
|
||||
if len(runnable_apps) == 1:
|
||||
app_to_launch = runnable_apps[0].appid
|
||||
elif len(runnable_apps) > 1:
|
||||
# more than 1 app - try to find one with matching id
|
||||
app_to_launch = appenv.subst("$APPID")
|
||||
|
||||
|
||||
def ambiguous_app_call(**kw):
|
||||
raise UserError(
|
||||
f"More than one app is runnable: {', '.join(app.appid for app in runnable_apps)}. Please specify an app with APPID=..."
|
||||
)
|
||||
|
||||
|
||||
if app_to_launch:
|
||||
appenv.AddAppLaunchTarget(app_to_launch, "launch")
|
||||
else:
|
||||
dist_env.PhonyTarget("launch", Action(ambiguous_app_call, None))
|
||||
|
||||
# cli handler
|
||||
|
||||
appenv.PhonyTarget(
|
||||
"cli",
|
||||
'${PYTHON3} "${FBT_SCRIPT_DIR}/serial_cli.py"',
|
||||
)
|
||||
|
||||
# Linter
|
||||
|
||||
dist_env.PhonyTarget(
|
||||
"lint",
|
||||
"${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}",
|
||||
source=original_app_dir.File(".clang-format"),
|
||||
LINT_SOURCES=[original_app_dir],
|
||||
)
|
||||
|
||||
|
||||
# Prepare vscode environment
|
||||
def _path_as_posix(path):
|
||||
return pathlib.Path(path).as_posix()
|
||||
|
||||
|
||||
vscode_dist = []
|
||||
project_template_dir = dist_env["UFBT_SCRIPT_ROOT"].Dir("project_template")
|
||||
for template_file in project_template_dir.Dir(".vscode").glob("*"):
|
||||
vscode_dist.append(
|
||||
dist_env.Substfile(
|
||||
original_app_dir.Dir(".vscode").File(template_file.name),
|
||||
template_file,
|
||||
SUBST_DICT={
|
||||
"@UFBT_VSCODE_PATH_SEP@": os.path.pathsep,
|
||||
"@UFBT_TOOLCHAIN_ARM_TOOLCHAIN_DIR@": pathlib.Path(
|
||||
dist_env.WhereIs("arm-none-eabi-gcc")
|
||||
).parent.as_posix(),
|
||||
"@UFBT_TOOLCHAIN_GCC@": _path_as_posix(
|
||||
dist_env.WhereIs("arm-none-eabi-gcc")
|
||||
),
|
||||
"@UFBT_TOOLCHAIN_GDB_PY@": _path_as_posix(
|
||||
dist_env.WhereIs("arm-none-eabi-gdb-py")
|
||||
),
|
||||
"@UFBT_TOOLCHAIN_OPENOCD@": _path_as_posix(dist_env.WhereIs("openocd")),
|
||||
"@UFBT_APP_DIR@": _path_as_posix(original_app_dir.abspath),
|
||||
"@UFBT_ROOT_DIR@": _path_as_posix(Dir("#").abspath),
|
||||
"@UFBT_DEBUG_DIR@": dist_env["FBT_DEBUG_DIR"],
|
||||
"@UFBT_DEBUG_ELF_DIR@": _path_as_posix(
|
||||
dist_env["FBT_FAP_DEBUG_ELF_ROOT"].abspath
|
||||
),
|
||||
"@UFBT_FIRMWARE_ELF@": _path_as_posix(dist_env["FW_ELF"].abspath),
|
||||
},
|
||||
)
|
||||
)
|
||||
|
||||
for config_file in project_template_dir.glob(".*"):
|
||||
if isinstance(config_file, FS.Dir):
|
||||
continue
|
||||
vscode_dist.append(dist_env.Install(original_app_dir, config_file))
|
||||
|
||||
dist_env.Precious(vscode_dist)
|
||||
dist_env.NoClean(vscode_dist)
|
||||
dist_env.Alias("vscode_dist", vscode_dist)
|
||||
|
||||
|
||||
# Creating app from base template
|
||||
|
||||
dist_env.SetDefault(FBT_APPID=appenv.subst("$APPID") or "template")
|
||||
app_template_dist = []
|
||||
for template_file in project_template_dir.Dir("app_template").glob("*"):
|
||||
dist_file_name = dist_env.subst(template_file.name)
|
||||
if template_file.name.endswith(".png"):
|
||||
app_template_dist.append(
|
||||
dist_env.InstallAs(original_app_dir.File(dist_file_name), template_file)
|
||||
)
|
||||
else:
|
||||
app_template_dist.append(
|
||||
dist_env.Substfile(
|
||||
original_app_dir.File(dist_file_name),
|
||||
template_file,
|
||||
SUBST_DICT={
|
||||
"@FBT_APPID@": dist_env.subst("$FBT_APPID"),
|
||||
},
|
||||
)
|
||||
)
|
||||
|
||||
AddPostAction(
|
||||
app_template_dist[-1],
|
||||
[
|
||||
Mkdir(original_app_dir.Dir("images")),
|
||||
Touch(original_app_dir.Dir("images").File(".gitkeep")),
|
||||
],
|
||||
)
|
||||
dist_env.Precious(app_template_dist)
|
||||
dist_env.NoClean(app_template_dist)
|
||||
dist_env.Alias("create", app_template_dist)
|
90
scripts/ufbt/commandline.scons
Normal file
|
@ -0,0 +1,90 @@
|
|||
AddOption(
|
||||
"--proxy-env",
|
||||
action="store",
|
||||
dest="proxy_env",
|
||||
default="",
|
||||
help="Comma-separated list of additional environment variables to pass to child SCons processes",
|
||||
)
|
||||
|
||||
AddOption(
|
||||
"--channel",
|
||||
action="store",
|
||||
dest="sdk_channel",
|
||||
choices=["dev", "rc", "release"],
|
||||
default="",
|
||||
help="Release channel to use for SDK",
|
||||
)
|
||||
|
||||
AddOption(
|
||||
"--branch",
|
||||
action="store",
|
||||
dest="sdk_branch",
|
||||
help="Custom main repo branch to use for SDK",
|
||||
)
|
||||
|
||||
AddOption(
|
||||
"--hw-target",
|
||||
action="store",
|
||||
dest="sdk_target",
|
||||
help="SDK Hardware target",
|
||||
)
|
||||
|
||||
vars = Variables("ufbt_options.py", ARGUMENTS)
|
||||
|
||||
vars.AddVariables(
|
||||
BoolVariable(
|
||||
"VERBOSE",
|
||||
help="Print full commands",
|
||||
default=False,
|
||||
),
|
||||
BoolVariable(
|
||||
"FORCE",
|
||||
help="Force target action (for supported targets)",
|
||||
default=False,
|
||||
),
|
||||
# These 2 are inherited from SDK
|
||||
# BoolVariable(
|
||||
# "DEBUG",
|
||||
# help="Enable debug build",
|
||||
# default=True,
|
||||
# ),
|
||||
# BoolVariable(
|
||||
# "COMPACT",
|
||||
# help="Optimize for size",
|
||||
# default=False,
|
||||
# ),
|
||||
PathVariable(
|
||||
"OTHER_ELF",
|
||||
help="Path to prebuilt ELF file to debug",
|
||||
validator=PathVariable.PathAccept,
|
||||
default="",
|
||||
),
|
||||
(
|
||||
"OPENOCD_OPTS",
|
||||
"Options to pass to OpenOCD",
|
||||
"",
|
||||
),
|
||||
(
|
||||
"BLACKMAGIC",
|
||||
"Blackmagic probe location",
|
||||
"auto",
|
||||
),
|
||||
(
|
||||
"OPENOCD_ADAPTER_SERIAL",
|
||||
"OpenOCD adapter serial number",
|
||||
"auto",
|
||||
),
|
||||
(
|
||||
"APPID",
|
||||
"Application id",
|
||||
"",
|
||||
),
|
||||
PathVariable(
|
||||
"UFBT_APP_DIR",
|
||||
help="Application dir to work with",
|
||||
validator=PathVariable.PathIsDir,
|
||||
default="",
|
||||
),
|
||||
)
|
||||
|
||||
Return("vars")
|
191
scripts/ufbt/project_template/.clang-format
Normal file
|
@ -0,0 +1,191 @@
|
|||
---
|
||||
Language: Cpp
|
||||
AccessModifierOffset: -4
|
||||
AlignAfterOpenBracket: AlwaysBreak
|
||||
AlignArrayOfStructures: None
|
||||
AlignConsecutiveMacros: None
|
||||
AlignConsecutiveAssignments: None
|
||||
AlignConsecutiveBitFields: None
|
||||
AlignConsecutiveDeclarations: None
|
||||
AlignEscapedNewlines: Left
|
||||
AlignOperands: Align
|
||||
AlignTrailingComments: false
|
||||
AllowAllArgumentsOnNextLine: true
|
||||
AllowAllParametersOfDeclarationOnNextLine: false
|
||||
AllowShortEnumsOnASingleLine: true
|
||||
AllowShortBlocksOnASingleLine: Never
|
||||
AllowShortCaseLabelsOnASingleLine: false
|
||||
AllowShortFunctionsOnASingleLine: None
|
||||
AllowShortLambdasOnASingleLine: All
|
||||
AllowShortIfStatementsOnASingleLine: WithoutElse
|
||||
AllowShortLoopsOnASingleLine: true
|
||||
AlwaysBreakAfterDefinitionReturnType: None
|
||||
AlwaysBreakAfterReturnType: None
|
||||
AlwaysBreakBeforeMultilineStrings: false
|
||||
AlwaysBreakTemplateDeclarations: Yes
|
||||
AttributeMacros:
|
||||
- __capability
|
||||
BinPackArguments: false
|
||||
BinPackParameters: false
|
||||
BraceWrapping:
|
||||
AfterCaseLabel: false
|
||||
AfterClass: false
|
||||
AfterControlStatement: Never
|
||||
AfterEnum: false
|
||||
AfterFunction: false
|
||||
AfterNamespace: false
|
||||
AfterObjCDeclaration: false
|
||||
AfterStruct: false
|
||||
AfterUnion: false
|
||||
AfterExternBlock: false
|
||||
BeforeCatch: false
|
||||
BeforeElse: false
|
||||
BeforeLambdaBody: false
|
||||
BeforeWhile: false
|
||||
IndentBraces: false
|
||||
SplitEmptyFunction: true
|
||||
SplitEmptyRecord: true
|
||||
SplitEmptyNamespace: true
|
||||
BreakBeforeBinaryOperators: None
|
||||
BreakBeforeConceptDeclarations: true
|
||||
BreakBeforeBraces: Attach
|
||||
BreakBeforeInheritanceComma: false
|
||||
BreakInheritanceList: BeforeColon
|
||||
BreakBeforeTernaryOperators: false
|
||||
BreakConstructorInitializersBeforeComma: false
|
||||
BreakConstructorInitializers: BeforeComma
|
||||
BreakAfterJavaFieldAnnotations: false
|
||||
BreakStringLiterals: false
|
||||
ColumnLimit: 99
|
||||
CommentPragmas: '^ IWYU pragma:'
|
||||
QualifierAlignment: Leave
|
||||
CompactNamespaces: false
|
||||
ConstructorInitializerIndentWidth: 4
|
||||
ContinuationIndentWidth: 4
|
||||
Cpp11BracedListStyle: true
|
||||
DeriveLineEnding: true
|
||||
DerivePointerAlignment: false
|
||||
DisableFormat: false
|
||||
EmptyLineAfterAccessModifier: Never
|
||||
EmptyLineBeforeAccessModifier: LogicalBlock
|
||||
ExperimentalAutoDetectBinPacking: false
|
||||
PackConstructorInitializers: BinPack
|
||||
BasedOnStyle: ''
|
||||
ConstructorInitializerAllOnOneLineOrOnePerLine: false
|
||||
AllowAllConstructorInitializersOnNextLine: true
|
||||
FixNamespaceComments: false
|
||||
ForEachMacros:
|
||||
- foreach
|
||||
- Q_FOREACH
|
||||
- BOOST_FOREACH
|
||||
IfMacros:
|
||||
- KJ_IF_MAYBE
|
||||
IncludeBlocks: Preserve
|
||||
IncludeCategories:
|
||||
- Regex: '.*'
|
||||
Priority: 1
|
||||
SortPriority: 0
|
||||
CaseSensitive: false
|
||||
- Regex: '^(<|"(gtest|gmock|isl|json)/)'
|
||||
Priority: 3
|
||||
SortPriority: 0
|
||||
CaseSensitive: false
|
||||
- Regex: '.*'
|
||||
Priority: 1
|
||||
SortPriority: 0
|
||||
CaseSensitive: false
|
||||
IncludeIsMainRegex: '(Test)?$'
|
||||
IncludeIsMainSourceRegex: ''
|
||||
IndentAccessModifiers: false
|
||||
IndentCaseLabels: false
|
||||
IndentCaseBlocks: false
|
||||
IndentGotoLabels: true
|
||||
IndentPPDirectives: None
|
||||
IndentExternBlock: AfterExternBlock
|
||||
IndentRequires: false
|
||||
IndentWidth: 4
|
||||
IndentWrappedFunctionNames: true
|
||||
InsertTrailingCommas: None
|
||||
JavaScriptQuotes: Leave
|
||||
JavaScriptWrapImports: true
|
||||
KeepEmptyLinesAtTheStartOfBlocks: false
|
||||
LambdaBodyIndentation: Signature
|
||||
MacroBlockBegin: ''
|
||||
MacroBlockEnd: ''
|
||||
MaxEmptyLinesToKeep: 1
|
||||
NamespaceIndentation: None
|
||||
ObjCBinPackProtocolList: Auto
|
||||
ObjCBlockIndentWidth: 4
|
||||
ObjCBreakBeforeNestedBlockParam: true
|
||||
ObjCSpaceAfterProperty: true
|
||||
ObjCSpaceBeforeProtocolList: true
|
||||
PenaltyBreakAssignment: 10
|
||||
PenaltyBreakBeforeFirstCallParameter: 30
|
||||
PenaltyBreakComment: 10
|
||||
PenaltyBreakFirstLessLess: 0
|
||||
PenaltyBreakOpenParenthesis: 0
|
||||
PenaltyBreakString: 10
|
||||
PenaltyBreakTemplateDeclaration: 10
|
||||
PenaltyExcessCharacter: 100
|
||||
PenaltyReturnTypeOnItsOwnLine: 60
|
||||
PenaltyIndentedWhitespace: 0
|
||||
PointerAlignment: Left
|
||||
PPIndentWidth: -1
|
||||
ReferenceAlignment: Pointer
|
||||
ReflowComments: false
|
||||
RemoveBracesLLVM: false
|
||||
SeparateDefinitionBlocks: Leave
|
||||
ShortNamespaceLines: 1
|
||||
SortIncludes: Never
|
||||
SortJavaStaticImport: Before
|
||||
SortUsingDeclarations: false
|
||||
SpaceAfterCStyleCast: false
|
||||
SpaceAfterLogicalNot: false
|
||||
SpaceAfterTemplateKeyword: true
|
||||
SpaceBeforeAssignmentOperators: true
|
||||
SpaceBeforeCaseColon: false
|
||||
SpaceBeforeCpp11BracedList: false
|
||||
SpaceBeforeCtorInitializerColon: true
|
||||
SpaceBeforeInheritanceColon: true
|
||||
SpaceBeforeParens: Never
|
||||
SpaceBeforeParensOptions:
|
||||
AfterControlStatements: false
|
||||
AfterForeachMacros: false
|
||||
AfterFunctionDefinitionName: false
|
||||
AfterFunctionDeclarationName: false
|
||||
AfterIfMacros: false
|
||||
AfterOverloadedOperator: false
|
||||
BeforeNonEmptyParentheses: false
|
||||
SpaceAroundPointerQualifiers: Default
|
||||
SpaceBeforeRangeBasedForLoopColon: true
|
||||
SpaceInEmptyBlock: false
|
||||
SpaceInEmptyParentheses: false
|
||||
SpacesBeforeTrailingComments: 1
|
||||
SpacesInAngles: Never
|
||||
SpacesInConditionalStatement: false
|
||||
SpacesInContainerLiterals: false
|
||||
SpacesInCStyleCastParentheses: false
|
||||
SpacesInLineCommentPrefix:
|
||||
Minimum: 1
|
||||
Maximum: -1
|
||||
SpacesInParentheses: false
|
||||
SpacesInSquareBrackets: false
|
||||
SpaceBeforeSquareBrackets: false
|
||||
BitFieldColonSpacing: Both
|
||||
Standard: c++03
|
||||
StatementAttributeLikeMacros:
|
||||
- Q_EMIT
|
||||
StatementMacros:
|
||||
- Q_UNUSED
|
||||
- QT_REQUIRE_VERSION
|
||||
TabWidth: 4
|
||||
UseCRLF: false
|
||||
UseTab: Never
|
||||
WhitespaceSensitiveMacros:
|
||||
- STRINGIZE
|
||||
- PP_STRINGIZE
|
||||
- BOOST_PP_STRINGIZE
|
||||
- NS_SWIFT_NAME
|
||||
- CF_SWIFT_NAME
|
||||
...
|
||||
|
13
scripts/ufbt/project_template/.editorconfig
Normal file
|
@ -0,0 +1,13 @@
|
|||
root = true
|
||||
|
||||
[*]
|
||||
end_of_line = lf
|
||||
insert_final_newline = true
|
||||
charset = utf-8
|
||||
|
||||
[*.{cpp,h,c,py,sh}]
|
||||
indent_style = space
|
||||
indent_size = 4
|
||||
|
||||
[{Makefile,*.mk}]
|
||||
indent_size = tab
|
4
scripts/ufbt/project_template/.gitignore
vendored
Normal file
|
@ -0,0 +1,4 @@
|
|||
dist/*
|
||||
.vscode
|
||||
.clang-format
|
||||
.editorconfig
|
14
scripts/ufbt/project_template/.vscode/c_cpp_properties.json
vendored
Normal file
|
@ -0,0 +1,14 @@
|
|||
{
|
||||
"configurations": [
|
||||
{
|
||||
"name": "main",
|
||||
"compilerPath": "@UFBT_TOOLCHAIN_GCC@",
|
||||
"intelliSenseMode": "gcc-arm",
|
||||
"compileCommands": "${workspaceFolder}/.vscode/compile_commands.json",
|
||||
"configurationProvider": "ms-vscode.cpptools",
|
||||
"cStandard": "gnu17",
|
||||
"cppStandard": "c++17"
|
||||
},
|
||||
],
|
||||
"version": 4
|
||||
}
|
18
scripts/ufbt/project_template/.vscode/extensions.json
vendored
Normal file
|
@ -0,0 +1,18 @@
|
|||
{
|
||||
// See https://go.microsoft.com/fwlink/?LinkId=827846 to learn about workspace recommendations.
|
||||
// Extension identifier format: ${publisher}.${name}. Example: vscode.csharp
|
||||
// List of extensions which should be recommended for users of this workspace.
|
||||
"recommendations": [
|
||||
"ms-python.black-formatter",
|
||||
"ms-vscode.cpptools",
|
||||
"amiralizadeh9480.cpp-helper",
|
||||
"marus25.cortex-debug",
|
||||
"zxh404.vscode-proto3",
|
||||
"augustocdias.tasks-shell-input"
|
||||
],
|
||||
// List of extensions recommended by VS Code that should not be recommended for users of this workspace.
|
||||
"unwantedRecommendations": [
|
||||
"twxs.cmake",
|
||||
"ms-vscode.cmake-tools"
|
||||
]
|
||||
}
|
98
scripts/ufbt/project_template/.vscode/launch.json
vendored
Normal file
|
@ -0,0 +1,98 @@
|
|||
{
|
||||
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
||||
"version": "0.2.0",
|
||||
"inputs": [
|
||||
// {
|
||||
// "id": "BLACKMAGIC",
|
||||
// "type": "command",
|
||||
// "command": "shellCommand.execute",
|
||||
// "args": {
|
||||
// "useSingleResult": true,
|
||||
// "env": {
|
||||
// "PATH": "${workspaceFolder};${env:PATH}"
|
||||
// },
|
||||
// "command": "./fbt get_blackmagic",
|
||||
// "description": "Get Blackmagic device",
|
||||
// }
|
||||
// },
|
||||
],
|
||||
"configurations": [
|
||||
{
|
||||
"name": "Attach FW (ST-Link)",
|
||||
"cwd": "${workspaceFolder}",
|
||||
"executable": "@UFBT_FIRMWARE_ELF@",
|
||||
"request": "attach",
|
||||
"type": "cortex-debug",
|
||||
"servertype": "openocd",
|
||||
"device": "stlink",
|
||||
"svdFile": "@UFBT_DEBUG_DIR@/STM32WB55_CM4.svd",
|
||||
"rtos": "FreeRTOS",
|
||||
"configFiles": [
|
||||
"interface/stlink.cfg",
|
||||
"@UFBT_DEBUG_DIR@/stm32wbx.cfg"
|
||||
],
|
||||
"postAttachCommands": [
|
||||
"source @UFBT_DEBUG_DIR@/flipperapps.py",
|
||||
"fap-set-debug-elf-root @UFBT_DEBUG_ELF_DIR@"
|
||||
],
|
||||
// "showDevDebugOutput": "raw",
|
||||
},
|
||||
{
|
||||
"name": "Attach FW (DAP)",
|
||||
"cwd": "${workspaceFolder}",
|
||||
"executable": "@UFBT_FIRMWARE_ELF@",
|
||||
"request": "attach",
|
||||
"type": "cortex-debug",
|
||||
"servertype": "openocd",
|
||||
"device": "cmsis-dap",
|
||||
"svdFile": "@UFBT_DEBUG_DIR@/STM32WB55_CM4.svd",
|
||||
"rtos": "FreeRTOS",
|
||||
"configFiles": [
|
||||
"interface/cmsis-dap.cfg",
|
||||
"@UFBT_DEBUG_DIR@/stm32wbx.cfg"
|
||||
],
|
||||
"postAttachCommands": [
|
||||
"source @UFBT_DEBUG_DIR@/flipperapps.py",
|
||||
"fap-set-debug-elf-root @UFBT_DEBUG_ELF_DIR@"
|
||||
],
|
||||
// "showDevDebugOutput": "raw",
|
||||
},
|
||||
// {
|
||||
// "name": "Attach FW (blackmagic)",
|
||||
// "cwd": "${workspaceFolder}",
|
||||
// "executable": "@UFBT_FIRMWARE_ELF@",
|
||||
// "request": "attach",
|
||||
// "type": "cortex-debug",
|
||||
// "servertype": "external",
|
||||
// "gdbTarget": "${input:BLACKMAGIC}",
|
||||
// "svdFile": "@UFBT_DEBUG_DIR@/STM32WB55_CM4.svd",
|
||||
// "rtos": "FreeRTOS",
|
||||
// "postAttachCommands": [
|
||||
// "monitor swdp_scan",
|
||||
// "attach 1",
|
||||
// "set confirm off",
|
||||
// "set mem inaccessible-by-default off",
|
||||
// "source @UFBT_DEBUG_DIR@/flipperapps.py",
|
||||
// "fap-set-debug-elf-root @UFBT_DEBUG_ELF_DIR@"
|
||||
// ]
|
||||
// // "showDevDebugOutput": "raw",
|
||||
// },
|
||||
{
|
||||
"name": "Attach FW (JLink)",
|
||||
"cwd": "${workspaceFolder}",
|
||||
"executable": "@UFBT_FIRMWARE_ELF@",
|
||||
"request": "attach",
|
||||
"type": "cortex-debug",
|
||||
"servertype": "jlink",
|
||||
"interface": "swd",
|
||||
"device": "STM32WB55RG",
|
||||
"svdFile": "@UFBT_DEBUG_DIR@/STM32WB55_CM4.svd",
|
||||
"rtos": "FreeRTOS",
|
||||
"postAttachCommands": [
|
||||
"source @UFBT_DEBUG_DIR@/flipperapps.py",
|
||||
"fap-set-debug-elf-root @UFBT_DEBUG_ELF_DIR@"
|
||||
]
|
||||
// "showDevDebugOutput": "raw",
|
||||
},
|
||||
]
|
||||
}
|
20
scripts/ufbt/project_template/.vscode/settings.json
vendored
Normal file
|
@ -0,0 +1,20 @@
|
|||
{
|
||||
"cortex-debug.enableTelemetry": false,
|
||||
"cortex-debug.variableUseNaturalFormat": false,
|
||||
"cortex-debug.showRTOS": true,
|
||||
"cortex-debug.armToolchainPath": "@UFBT_TOOLCHAIN_ARM_TOOLCHAIN_DIR@",
|
||||
"cortex-debug.openocdPath": "@UFBT_TOOLCHAIN_OPENOCD@",
|
||||
"cortex-debug.gdbPath": "@UFBT_TOOLCHAIN_GDB_PY@",
|
||||
"editor.formatOnSave": true,
|
||||
"files.associations": {
|
||||
"*.scons": "python",
|
||||
"SConscript": "python",
|
||||
"SConstruct": "python",
|
||||
"*.fam": "python"
|
||||
},
|
||||
"cortex-debug.registerUseNaturalFormat": false,
|
||||
"python.analysis.typeCheckingMode": "off",
|
||||
"[python]": {
|
||||
"editor.defaultFormatter": "ms-python.black-formatter"
|
||||
}
|
||||
}
|
54
scripts/ufbt/project_template/.vscode/tasks.json
vendored
Normal file
|
@ -0,0 +1,54 @@
|
|||
{
|
||||
// See https://go.microsoft.com/fwlink/?LinkId=733558
|
||||
// for the documentation about the tasks.json format
|
||||
"version": "2.0.0",
|
||||
"options": {
|
||||
"env": {
|
||||
"PATH": "${workspaceFolder}@UFBT_VSCODE_PATH_SEP@${env:PATH}@UFBT_VSCODE_PATH_SEP@@UFBT_ROOT_DIR@"
|
||||
}
|
||||
},
|
||||
"tasks": [
|
||||
{
|
||||
"label": "Launch App on Flipper",
|
||||
"group": "build",
|
||||
"type": "shell",
|
||||
"command": "ufbt launch"
|
||||
},
|
||||
{
|
||||
"label": "Build",
|
||||
"group": "build",
|
||||
"type": "shell",
|
||||
"command": "ufbt"
|
||||
},
|
||||
{
|
||||
"label": "Flash FW (ST-Link)",
|
||||
"group": "build",
|
||||
"type": "shell",
|
||||
"command": "ufbt FORCE=1 flash"
|
||||
},
|
||||
// {
|
||||
// "label": "[NOTIMPL] Flash FW (blackmagic)",
|
||||
// "group": "build",
|
||||
// "type": "shell",
|
||||
// "command": "ufbt flash_blackmagic"
|
||||
// },
|
||||
// {
|
||||
// "label": "[NOTIMPL] Flash FW (JLink)",
|
||||
// "group": "build",
|
||||
// "type": "shell",
|
||||
// "command": "ufbt FORCE=1 jflash"
|
||||
// },
|
||||
{
|
||||
"label": "Flash FW (USB, with resources)",
|
||||
"group": "build",
|
||||
"type": "shell",
|
||||
"command": "ufbt FORCE=1 flash_usb"
|
||||
},
|
||||
{
|
||||
"label": "Update uFBT SDK",
|
||||
"group": "build",
|
||||
"type": "shell",
|
||||
"command": "ufbt update"
|
||||
}
|
||||
]
|
||||
}
|
12
scripts/ufbt/project_template/app_template/${FBT_APPID}.c
Normal file
|
@ -0,0 +1,12 @@
|
|||
#include <furi.h>
|
||||
|
||||
/* generated by fbt from .png files in images folder */
|
||||
#include <@FBT_APPID@_icons.h>
|
||||
|
||||
int32_t @FBT_APPID@_app(void* p) {
|
||||
UNUSED(p);
|
||||
FURI_LOG_I("TEST", "Hello world");
|
||||
FURI_LOG_I("TEST", "I'm @FBT_APPID@!");
|
||||
|
||||
return 0;
|
||||
}
|
BIN
scripts/ufbt/project_template/app_template/${FBT_APPID}.png
Normal file
After Width: | Height: | Size: 220 B |
17
scripts/ufbt/project_template/app_template/application.fam
Normal file
|
@ -0,0 +1,17 @@
|
|||
# For details & more options, see documentation/AppManifests.md in firmware repo
|
||||
|
||||
App(
|
||||
appid="@FBT_APPID@", # Must be unique
|
||||
name="App @FBT_APPID@", # Displayed in menus
|
||||
apptype=FlipperAppType.EXTERNAL,
|
||||
entry_point="@FBT_APPID@_app",
|
||||
stack_size=2 * 1024,
|
||||
fap_category="Misc",
|
||||
# Optional values
|
||||
# fap_version=(0, 1), # (major, minor)
|
||||
fap_icon="@FBT_APPID@.png", # 10x10 1-bit PNG
|
||||
# fap_description="A simple app",
|
||||
# fap_author="J. Doe",
|
||||
# fap_weburl="https://github.com/user/@FBT_APPID@",
|
||||
fap_icon_assets="images", # Image assets to compile for this application
|
||||
)
|
36
scripts/ufbt/site_init.py
Normal file
|
@ -0,0 +1,36 @@
|
|||
from SCons.Script import GetBuildFailures
|
||||
import SCons.Errors
|
||||
|
||||
import atexit
|
||||
from ansi.color import fg, fx
|
||||
|
||||
|
||||
def bf_to_str(bf):
|
||||
"""Convert an element of GetBuildFailures() to a string
|
||||
in a useful way."""
|
||||
|
||||
if bf is None: # unknown targets product None in list
|
||||
return "(unknown tgt)"
|
||||
elif isinstance(bf, SCons.Errors.StopError):
|
||||
return fg.yellow(str(bf))
|
||||
elif bf.node:
|
||||
return fg.yellow(str(bf.node)) + ": " + bf.errstr
|
||||
elif bf.filename:
|
||||
return fg.yellow(bf.filename) + ": " + bf.errstr
|
||||
return fg.yellow("unknown failure: ") + bf.errstr
|
||||
|
||||
|
||||
def display_build_status():
|
||||
"""Display the build status. Called by atexit.
|
||||
Here you could do all kinds of complicated things."""
|
||||
bf = GetBuildFailures()
|
||||
if bf:
|
||||
# bf is normally a list of build failures; if an element is None,
|
||||
# it's because of a target that scons doesn't know anything about.
|
||||
failures_message = "\n".join([bf_to_str(x) for x in bf if x is not None])
|
||||
print()
|
||||
print(fg.brightred(fx.bold("*" * 10 + " FBT ERRORS " + "*" * 10)))
|
||||
print(failures_message)
|
||||
|
||||
|
||||
atexit.register(display_build_status)
|
53
scripts/ufbt/site_tools/ufbt_help.py
Normal file
|
@ -0,0 +1,53 @@
|
|||
targets_help = """Configuration variables:
|
||||
"""
|
||||
|
||||
tail_help = """
|
||||
|
||||
TASKS:
|
||||
(* - not supported yet)
|
||||
|
||||
launch:
|
||||
Upload and start application over USB
|
||||
vscode_dist:
|
||||
Configure application in current directory for development in VSCode.
|
||||
create:
|
||||
Copy application template to current directory. Set APPID=myapp to create an app with id 'myapp'.
|
||||
|
||||
Building:
|
||||
faps:
|
||||
Build all FAP apps
|
||||
fap_{APPID}, launch 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
|
||||
lint:
|
||||
run linter for C code
|
||||
format:
|
||||
reformat C code
|
||||
|
||||
How to create a new application:
|
||||
1. Create a new directory for your application and cd into it.
|
||||
2. Run `ufbt vscode_dist create APPID=myapp`
|
||||
3. In VSCode, open the folder and start editing.
|
||||
4. Run `ufbt launch` to build and upload your application.
|
||||
"""
|
||||
|
||||
|
||||
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
|
117
scripts/ufbt/site_tools/ufbt_state.py
Normal file
|
@ -0,0 +1,117 @@
|
|||
from SCons.Errors import StopError
|
||||
from SCons.Warnings import warn, WarningOnByDefault
|
||||
|
||||
import json
|
||||
import os
|
||||
import sys
|
||||
import pathlib
|
||||
from functools import reduce
|
||||
|
||||
|
||||
def _load_sdk_data(sdk_root):
|
||||
split_vars = {
|
||||
"cc_args",
|
||||
"cpp_args",
|
||||
"linker_args",
|
||||
"linker_libs",
|
||||
}
|
||||
subst_vars = split_vars | {
|
||||
"sdk_symbols",
|
||||
}
|
||||
sdk_data = {}
|
||||
with open(os.path.join(sdk_root, "sdk.opts")) as f:
|
||||
sdk_json_data = json.load(f)
|
||||
replacements = {
|
||||
sdk_json_data["app_ep_subst"]: "${APP_ENTRY}",
|
||||
sdk_json_data["sdk_path_subst"]: sdk_root.replace("\\", "/"),
|
||||
sdk_json_data["map_file_subst"]: "${TARGET}",
|
||||
}
|
||||
|
||||
def do_value_substs(src_value):
|
||||
if isinstance(src_value, str):
|
||||
return reduce(
|
||||
lambda acc, kv: acc.replace(*kv), replacements.items(), src_value
|
||||
)
|
||||
elif isinstance(src_value, list):
|
||||
return [do_value_substs(v) for v in src_value]
|
||||
else:
|
||||
return src_value
|
||||
|
||||
for key, value in sdk_json_data.items():
|
||||
if key in split_vars:
|
||||
value = value.split()
|
||||
if key in subst_vars:
|
||||
value = do_value_substs(value)
|
||||
sdk_data[key] = value
|
||||
|
||||
return sdk_data
|
||||
|
||||
|
||||
def _load_state_file(state_dir_node, filename: str) -> dict:
|
||||
state_path = os.path.join(state_dir_node.abspath, filename)
|
||||
if not os.path.exists(state_path):
|
||||
raise StopError(f"State file {state_path} not found")
|
||||
|
||||
with open(state_path, "r") as f:
|
||||
return json.load(f)
|
||||
|
||||
|
||||
def generate(env, **kw):
|
||||
sdk_current_sdk_dir_node = env["UFBT_CURRENT_SDK_DIR"]
|
||||
|
||||
sdk_components_filename = kw.get("SDK_COMPONENTS", "components.json")
|
||||
ufbt_state_filename = kw.get("UFBT_STATE", "ufbt_state.json")
|
||||
|
||||
sdk_state = _load_state_file(sdk_current_sdk_dir_node, sdk_components_filename)
|
||||
ufbt_state = _load_state_file(sdk_current_sdk_dir_node, ufbt_state_filename)
|
||||
|
||||
if not (sdk_components := sdk_state.get("components", {})):
|
||||
raise StopError("SDK state file doesn't contain components data")
|
||||
|
||||
sdk_data = _load_sdk_data(
|
||||
sdk_current_sdk_dir_node.Dir(sdk_components["sdk_headers.dir"]).abspath
|
||||
)
|
||||
|
||||
if not sdk_state["meta"]["hw_target"].endswith(sdk_data["hardware"]):
|
||||
raise StopError("SDK state file doesn't match hardware target")
|
||||
|
||||
if sdk_state["meta"]["version"] != ufbt_state["version"]:
|
||||
warn(
|
||||
WarningOnByDefault,
|
||||
f"Version mismatch: SDK state vs uFBT: {sdk_state['meta']['version']} vs {ufbt_state['version']}",
|
||||
)
|
||||
|
||||
scripts_dir = sdk_current_sdk_dir_node.Dir(sdk_components["scripts.dir"])
|
||||
env.SetDefault(
|
||||
# Paths
|
||||
SDK_DEFINITION=env.File(sdk_data["sdk_symbols"]),
|
||||
FBT_DEBUG_DIR=pathlib.Path(
|
||||
sdk_current_sdk_dir_node.Dir(sdk_components["debug.dir"]).abspath
|
||||
).as_posix(),
|
||||
FBT_SCRIPT_DIR=scripts_dir,
|
||||
LIBPATH=sdk_current_sdk_dir_node.Dir(sdk_components["lib.dir"]),
|
||||
FW_ELF=sdk_current_sdk_dir_node.File(sdk_components["firmware.elf"]),
|
||||
FW_BIN=sdk_current_sdk_dir_node.File(sdk_components["full.bin"]),
|
||||
UPDATE_BUNDLE_DIR=sdk_current_sdk_dir_node.Dir(sdk_components["update.dir"]),
|
||||
SVD_FILE="${FBT_DEBUG_DIR}/STM32WB55_CM4.svd",
|
||||
# Build variables
|
||||
ROOT_DIR=env.Dir("#"),
|
||||
FIRMWARE_BUILD_CFG="firmware",
|
||||
TARGET_HW=int(sdk_data["hardware"]),
|
||||
CFLAGS_APP=sdk_data["cc_args"],
|
||||
CXXFLAGS_APP=sdk_data["cpp_args"],
|
||||
LINKFLAGS_APP=sdk_data["linker_args"],
|
||||
LIBS=sdk_data["linker_libs"],
|
||||
# ufbt state
|
||||
# UFBT_STATE_DIR=ufbt_state_dir_node,
|
||||
# UFBT_CURRENT_SDK_DIR=sdk_current_sdk_dir_node,
|
||||
UFBT_STATE=ufbt_state,
|
||||
UFBT_BOOTSTRAP_SCRIPT="${UFBT_SCRIPT_DIR}/bootstrap.py",
|
||||
UFBT_SCRIPT_ROOT=scripts_dir.Dir("ufbt"),
|
||||
)
|
||||
|
||||
sys.path.insert(0, env["FBT_SCRIPT_DIR"].abspath)
|
||||
|
||||
|
||||
def exists(env):
|
||||
return True
|
37
scripts/ufbt/update.scons
Normal file
|
@ -0,0 +1,37 @@
|
|||
from SCons.Errors import StopError
|
||||
|
||||
Import("core_env")
|
||||
|
||||
update_env = core_env.Clone(
|
||||
toolpath=[core_env["FBT_SCRIPT_DIR"].Dir("fbt_tools")],
|
||||
tools=["python3"],
|
||||
)
|
||||
print("Updating SDK...")
|
||||
ufbt_state = update_env["UFBT_STATE"]
|
||||
|
||||
update_args = [
|
||||
"--ufbt-dir",
|
||||
f'"{update_env["UFBT_STATE_DIR"]}"',
|
||||
]
|
||||
|
||||
if branch_name := GetOption("sdk_branch"):
|
||||
update_args.extend(["--branch", branch_name])
|
||||
elif channel_name := GetOption("sdk_channel"):
|
||||
update_args.extend(["--channel", channel_name])
|
||||
elif branch_name := ufbt_state.get("branch", None):
|
||||
update_args.extend(["--branch", branch_name])
|
||||
elif channel_name := ufbt_state.get("channel", None):
|
||||
update_args.extend(["--channel", channel_name])
|
||||
else:
|
||||
raise StopError("No branch or channel specified for SDK update")
|
||||
|
||||
if hw_target := GetOption("sdk_target"):
|
||||
update_args.extend(["--hw-target", hw_target])
|
||||
else:
|
||||
update_args.extend(["--hw-target", ufbt_state["hw_target"]])
|
||||
|
||||
update_env.Replace(UPDATE_ARGS=update_args)
|
||||
result = update_env.Execute(
|
||||
update_env.subst('$PYTHON3 "$UFBT_BOOTSTRAP_SCRIPT" $UPDATE_ARGS'),
|
||||
)
|
||||
Exit(result)
|
|
@ -112,86 +112,47 @@ Alias(
|
|||
|
||||
extapps.resources_dist = appenv.FapDist(appenv["RESOURCES_ROOT"], [])
|
||||
|
||||
|
||||
if appsrc := appenv.subst("$APPSRC"):
|
||||
deploy_sources, flipp_dist_paths, validators = [], [], []
|
||||
run_script_extra_ars = ""
|
||||
appenv.AddAppLaunchTarget(appsrc, "launch_app")
|
||||
|
||||
def _add_dist_targets(app_artifacts):
|
||||
validators.append(app_artifacts.validator)
|
||||
for _, ext_path in app_artifacts.dist_entries:
|
||||
deploy_sources.append(app_artifacts.compact)
|
||||
flipp_dist_paths.append(f"/ext/{ext_path}")
|
||||
return app_artifacts
|
||||
|
||||
def _add_host_app_to_targets(host_app):
|
||||
artifacts_app_to_run = appenv["EXT_APPS"].get(host_app.appid, None)
|
||||
_add_dist_targets(artifacts_app_to_run)
|
||||
for plugin in host_app._plugins:
|
||||
_add_dist_targets(appenv["EXT_APPS"].get(plugin.appid, None))
|
||||
|
||||
artifacts_app_to_run = appenv.GetExtAppByIdOrPath(appsrc)
|
||||
if artifacts_app_to_run.app.apptype == FlipperAppType.PLUGIN:
|
||||
# We deploy host app instead
|
||||
host_app = appenv["APPMGR"].get(artifacts_app_to_run.app.requires[0])
|
||||
|
||||
if host_app:
|
||||
if host_app.apptype == FlipperAppType.EXTERNAL:
|
||||
_add_host_app_to_targets(host_app)
|
||||
else:
|
||||
# host app is a built-in app
|
||||
run_script_extra_ars = f"-a {host_app.name}"
|
||||
_add_dist_targets(artifacts_app_to_run)
|
||||
else:
|
||||
raise UserError("Host app is unknown")
|
||||
else:
|
||||
_add_host_app_to_targets(artifacts_app_to_run.app)
|
||||
|
||||
# print(deploy_sources, flipp_dist_paths)
|
||||
appenv.PhonyTarget(
|
||||
"launch_app",
|
||||
'${PYTHON3} "${APP_RUN_SCRIPT}" ${EXTRA_ARGS} -s ${SOURCES} -t ${FLIPPER_FILE_TARGETS}',
|
||||
source=deploy_sources,
|
||||
FLIPPER_FILE_TARGETS=flipp_dist_paths,
|
||||
EXTRA_ARGS=run_script_extra_ars,
|
||||
)
|
||||
appenv.Alias("launch_app", validators)
|
||||
|
||||
# SDK management
|
||||
|
||||
sdk_origin_path = "${BUILD_DIR}/sdk_origin"
|
||||
sdk_source = appenv.SDKPrebuilder(
|
||||
sdk_origin_path,
|
||||
amalgamated_api = "${BUILD_DIR}/sdk_origin"
|
||||
sdk_source = appenv.ApiAmalgamator(
|
||||
amalgamated_api,
|
||||
# Deps on root SDK headers and generated files
|
||||
(appenv["SDK_HEADERS"], appenv["FW_ASSETS_HEADERS"]),
|
||||
)
|
||||
# Extra deps on headers included in deeper levels
|
||||
# Available on second and subsequent builds
|
||||
Depends(sdk_source, appenv.ProcessSdkDepends(f"{sdk_origin_path}.d"))
|
||||
Depends(sdk_source, appenv.ProcessSdkDepends(f"{amalgamated_api}.d"))
|
||||
|
||||
appenv["SDK_DIR"] = appenv.Dir("${BUILD_DIR}/sdk")
|
||||
sdk_tree = appenv.SDKTree(appenv["SDK_DIR"], sdk_origin_path)
|
||||
appenv["SDK_DIR"] = appenv.Dir("${BUILD_DIR}/sdk_headers")
|
||||
sdk_header_tree = appenv.SDKHeaderTreeExtractor(appenv["SDK_DIR"], amalgamated_api)
|
||||
# AlwaysBuild(sdk_tree)
|
||||
Alias("sdk_tree", sdk_tree)
|
||||
extapps.sdk_tree = sdk_tree
|
||||
Alias("sdk_tree", sdk_header_tree)
|
||||
extapps.sdk_tree = sdk_header_tree
|
||||
|
||||
sdk_apicheck = appenv.SDKSymUpdater(appenv["SDK_DEFINITION"], sdk_origin_path)
|
||||
Precious(sdk_apicheck)
|
||||
NoClean(sdk_apicheck)
|
||||
AlwaysBuild(sdk_apicheck)
|
||||
Alias("sdk_check", sdk_apicheck)
|
||||
api_check = appenv.ApiTableValidator(appenv["SDK_DEFINITION"], amalgamated_api)
|
||||
Precious(api_check)
|
||||
NoClean(api_check)
|
||||
AlwaysBuild(api_check)
|
||||
Alias("api_check", api_check)
|
||||
|
||||
sdk_apisyms = appenv.SDKSymGenerator(
|
||||
"${BUILD_DIR}/assets/compiled/symbols.h", appenv["SDK_DEFINITION"]
|
||||
firmware_apitable = appenv.ApiSymbolTable(
|
||||
"${BUILD_DIR}/assets/compiled/firmware_api_table.h", appenv["SDK_DEFINITION"]
|
||||
)
|
||||
Alias("api_syms", sdk_apisyms)
|
||||
Alias("api_table", firmware_apitable)
|
||||
ENV.Replace(
|
||||
SDK_APISYMS=sdk_apisyms,
|
||||
FW_API_TABLE=firmware_apitable,
|
||||
_APP_ICONS=appenv["_APP_ICONS"],
|
||||
)
|
||||
|
||||
|
||||
if appenv["FORCE"]:
|
||||
appenv.AlwaysBuild(sdk_source, sdk_tree, sdk_apicheck, sdk_apisyms)
|
||||
appenv.AlwaysBuild(sdk_source, sdk_header_tree, api_check, firmware_apitable)
|
||||
|
||||
|
||||
Return("extapps")
|
||||
|
|