diff --git a/.gitattributes b/.gitattributes index 6313b56c578..bd82ecaa960 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1 +1,4 @@ * text=auto eol=lf +*.bat eol=crlf +*.ps1 eol=crlf +*.cmd eol=crlf diff --git a/.gitignore b/.gitignore index 2d66413bbcf..21f391743ec 100644 --- a/.gitignore +++ b/.gitignore @@ -46,4 +46,7 @@ dist build/ # Toolchain -toolchain*/ \ No newline at end of file +/toolchain + +# openocd output file +openocd.log diff --git a/SConstruct b/SConstruct index 9c17a82a174..7cdf88fca72 100644 --- a/SConstruct +++ b/SConstruct @@ -8,6 +8,8 @@ import os +EnsurePythonVersion(3, 8) + DefaultEnvironment(tools=[]) # Progress(["OwO\r", "owo\r", "uwu\r", "owo\r"], interval=15) @@ -32,7 +34,7 @@ coreenv["ROOT_DIR"] = Dir(".") # Create a separate "dist" environment and add construction envs to it distenv = coreenv.Clone( tools=["fbt_dist", "openocd", "blackmagic"], - OPENOCD_GDB_PIPE=["|openocd -c 'gdb_port pipe' ${[SINGLEQUOTEFUNC(OPENOCD_OPTS)]}"], + OPENOCD_GDB_PIPE=["|openocd -c 'gdb_port pipe; log_output debug/openocd.log' ${[SINGLEQUOTEFUNC(OPENOCD_OPTS)]}"], GDBOPTS_BASE=[ "-ex", "target extended-remote ${GDBREMOTE}", diff --git a/applications/about/about.c b/applications/about/about.c index 6f8ebdf34cd..4d12ed0246a 100644 --- a/applications/about/about.c +++ b/applications/about/about.c @@ -206,4 +206,4 @@ int32_t about_settings_app(void* p) { furi_record_close("gui"); return 0; -} +} \ No newline at end of file diff --git a/applications/extapps.scons b/applications/extapps.scons index 7b1cae7d211..a53620b93fc 100644 --- a/applications/extapps.scons +++ b/applications/extapps.scons @@ -3,7 +3,6 @@ Import("ENV") from fbt.appmanifest import FlipperAppType - appenv = ENV.Clone(tools=["fbt_extapps"]) appenv.Replace( diff --git a/applications/gui/elements.h b/applications/gui/elements.h index 2b8b8d3f2b7..2329ca27b32 100644 --- a/applications/gui/elements.h +++ b/applications/gui/elements.h @@ -204,7 +204,7 @@ void elements_string_fit_width(Canvas* canvas, string_t string, uint8_t width); * @param[in] text Formatted text. The following formats are available: * "\e#Bold text\e#" - bold font is used * "\e*Monospaced text\e*" - monospaced font is used - * "\e#Inversed text\e#" - white text on black background + * "\e!Inversed text\e!" - white text on black background * @param strip_to_dots Strip text to ... if does not fit to width */ void elements_text_box( diff --git a/applications/storage/storage.h b/applications/storage/storage.h index 1e6ced23b84..dcb8deee83f 100644 --- a/applications/storage/storage.h +++ b/applications/storage/storage.h @@ -190,6 +190,14 @@ FS_Error storage_common_rename(Storage* storage, const char* old_path, const cha */ FS_Error storage_common_copy(Storage* storage, const char* old_path, const char* new_path); +/** Copy one folder contents into another with rename of all conflicting files + * @param app pointer to the api + * @param old_path old path + * @param new_path new path + * @return FS_Error operation result + */ +FS_Error storage_common_merge(Storage* storage, const char* old_path, const char* new_path); + /** Creates a directory * @param app pointer to the api * @param path directory path diff --git a/applications/storage/storage_external_api.c b/applications/storage/storage_external_api.c index 77bb6550b26..dc29faa6792 100644 --- a/applications/storage/storage_external_api.c +++ b/applications/storage/storage_external_api.c @@ -1,3 +1,4 @@ +#include "furi/log.h" #include #include #include "storage.h" @@ -5,8 +6,10 @@ #include "storage_message.h" #include #include +#include "toolbox/path.h" #define MAX_NAME_LENGTH 256 +#define MAX_EXT_LEN 16 #define TAG "StorageAPI" @@ -436,6 +439,131 @@ FS_Error storage_common_copy(Storage* storage, const char* old_path, const char* return error; } +static FS_Error + storage_merge_recursive(Storage* storage, const char* old_path, const char* new_path) { + FS_Error error = storage_common_mkdir(storage, new_path); + DirWalk* dir_walk = dir_walk_alloc(storage); + string_t path; + string_t tmp_new_path; + string_t tmp_old_path; + FileInfo fileinfo; + string_init(path); + string_init(tmp_new_path); + string_init(tmp_old_path); + + do { + if((error != FSE_OK) && (error != FSE_EXIST)) break; + + if(!dir_walk_open(dir_walk, old_path)) { + error = dir_walk_get_error(dir_walk); + break; + } + + while(1) { + DirWalkResult res = dir_walk_read(dir_walk, path, &fileinfo); + + if(res == DirWalkError) { + error = dir_walk_get_error(dir_walk); + break; + } else if(res == DirWalkLast) { + break; + } else { + string_set(tmp_old_path, path); + string_right(path, strlen(old_path)); + string_printf(tmp_new_path, "%s%s", new_path, string_get_cstr(path)); + + if(fileinfo.flags & FSF_DIRECTORY) { + if(storage_common_stat(storage, string_get_cstr(tmp_new_path), &fileinfo) == + FSE_OK) { + if(fileinfo.flags & FSF_DIRECTORY) { + error = storage_common_mkdir(storage, string_get_cstr(tmp_new_path)); + } + } + } else { + error = storage_common_merge( + storage, string_get_cstr(tmp_old_path), string_get_cstr(tmp_new_path)); + } + + if(error != FSE_OK) break; + } + } + + } while(false); + + string_clear(tmp_new_path); + string_clear(tmp_old_path); + string_clear(path); + dir_walk_free(dir_walk); + return error; +} + +FS_Error storage_common_merge(Storage* storage, const char* old_path, const char* new_path) { + FS_Error error; + const char* new_path_tmp; + string_t new_path_next; + string_init(new_path_next); + + FileInfo fileinfo; + error = storage_common_stat(storage, old_path, &fileinfo); + + if(error == FSE_OK) { + if(fileinfo.flags & FSF_DIRECTORY) { + error = storage_merge_recursive(storage, old_path, new_path); + } else { + error = storage_common_stat(storage, new_path, &fileinfo); + if(error == FSE_OK) { + string_set_str(new_path_next, new_path); + string_t dir_path; + string_t filename; + char extension[MAX_EXT_LEN]; + + string_init(dir_path); + string_init(filename); + + path_extract_filename(new_path_next, filename, true); + path_extract_dirname(new_path, dir_path); + path_extract_extension(new_path_next, extension, MAX_EXT_LEN); + + storage_get_next_filename( + storage, + string_get_cstr(dir_path), + string_get_cstr(filename), + extension, + new_path_next, + 255); + string_cat_printf(dir_path, "/%s%s", string_get_cstr(new_path_next), extension); + string_set(new_path_next, dir_path); + + string_clear(dir_path); + string_clear(filename); + new_path_tmp = string_get_cstr(new_path_next); + } else { + new_path_tmp = new_path; + } + Stream* stream_from = file_stream_alloc(storage); + Stream* stream_to = file_stream_alloc(storage); + + do { + if(!file_stream_open(stream_from, old_path, FSAM_READ, FSOM_OPEN_EXISTING)) break; + if(!file_stream_open(stream_to, new_path_tmp, FSAM_WRITE, FSOM_CREATE_NEW)) break; + stream_copy_full(stream_from, stream_to); + } while(false); + + error = file_stream_get_error(stream_from); + if(error == FSE_OK) { + error = file_stream_get_error(stream_to); + } + + stream_free(stream_from); + stream_free(stream_to); + } + } + + string_clear(new_path_next); + + return error; +} + FS_Error storage_common_mkdir(Storage* storage, const char* path) { S_API_PROLOGUE; S_API_DATA_PATH; diff --git a/applications/storage_move_to_sd/application.fam b/applications/storage_move_to_sd/application.fam new file mode 100644 index 00000000000..60a6d9876bb --- /dev/null +++ b/applications/storage_move_to_sd/application.fam @@ -0,0 +1,19 @@ +App( + appid="storage_move_to_sd", + name="StorageMoveToSd", + apptype=FlipperAppType.SYSTEM, + entry_point="storage_move_to_sd_app", + requires=["gui","storage"], + provides=["storage_move_to_sd_start"], + stack_size=2 * 1024, + order=30, +) + +App( + appid="storage_move_to_sd_start", + apptype=FlipperAppType.STARTUP, + entry_point="storage_move_to_sd_start", + requires=["storage"], + order=120, +) + diff --git a/applications/storage_move_to_sd/scenes/storage_move_to_sd_scene.c b/applications/storage_move_to_sd/scenes/storage_move_to_sd_scene.c new file mode 100644 index 00000000000..011bf47df95 --- /dev/null +++ b/applications/storage_move_to_sd/scenes/storage_move_to_sd_scene.c @@ -0,0 +1,30 @@ +#include "storage_move_to_sd_scene.h" + +// Generate scene on_enter handlers array +#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_enter, +void (*const storage_move_to_sd_on_enter_handlers[])(void*) = { +#include "storage_move_to_sd_scene_config.h" +}; +#undef ADD_SCENE + +// Generate scene on_event handlers array +#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_event, +bool (*const storage_move_to_sd_on_event_handlers[])(void* context, SceneManagerEvent event) = { +#include "storage_move_to_sd_scene_config.h" +}; +#undef ADD_SCENE + +// Generate scene on_exit handlers array +#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_exit, +void (*const storage_move_to_sd_on_exit_handlers[])(void* context) = { +#include "storage_move_to_sd_scene_config.h" +}; +#undef ADD_SCENE + +// Initialize scene handlers configuration structure +const SceneManagerHandlers storage_move_to_sd_scene_handlers = { + .on_enter_handlers = storage_move_to_sd_on_enter_handlers, + .on_event_handlers = storage_move_to_sd_on_event_handlers, + .on_exit_handlers = storage_move_to_sd_on_exit_handlers, + .scene_num = StorageMoveToSdSceneNum, +}; diff --git a/applications/storage_move_to_sd/scenes/storage_move_to_sd_scene.h b/applications/storage_move_to_sd/scenes/storage_move_to_sd_scene.h new file mode 100644 index 00000000000..bdeb4a84337 --- /dev/null +++ b/applications/storage_move_to_sd/scenes/storage_move_to_sd_scene.h @@ -0,0 +1,29 @@ +#pragma once + +#include + +// Generate scene id and total number +#define ADD_SCENE(prefix, name, id) StorageMoveToSd##id, +typedef enum { +#include "storage_move_to_sd_scene_config.h" + StorageMoveToSdSceneNum, +} StorageMoveToSdScene; +#undef ADD_SCENE + +extern const SceneManagerHandlers storage_move_to_sd_scene_handlers; + +// Generate scene on_enter handlers declaration +#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_enter(void*); +#include "storage_move_to_sd_scene_config.h" +#undef ADD_SCENE + +// Generate scene on_event handlers declaration +#define ADD_SCENE(prefix, name, id) \ + bool prefix##_scene_##name##_on_event(void* context, SceneManagerEvent event); +#include "storage_move_to_sd_scene_config.h" +#undef ADD_SCENE + +// Generate scene on_exit handlers declaration +#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_exit(void* context); +#include "storage_move_to_sd_scene_config.h" +#undef ADD_SCENE diff --git a/applications/storage_move_to_sd/scenes/storage_move_to_sd_scene_config.h b/applications/storage_move_to_sd/scenes/storage_move_to_sd_scene_config.h new file mode 100644 index 00000000000..1d7b2d25b83 --- /dev/null +++ b/applications/storage_move_to_sd/scenes/storage_move_to_sd_scene_config.h @@ -0,0 +1,2 @@ +ADD_SCENE(storage_move_to_sd, confirm, Confirm) +ADD_SCENE(storage_move_to_sd, progress, Progress) diff --git a/applications/storage_move_to_sd/scenes/storage_move_to_sd_scene_confirm.c b/applications/storage_move_to_sd/scenes/storage_move_to_sd_scene_confirm.c new file mode 100644 index 00000000000..71ce5a7c3fc --- /dev/null +++ b/applications/storage_move_to_sd/scenes/storage_move_to_sd_scene_confirm.c @@ -0,0 +1,70 @@ +#include "../storage_move_to_sd.h" +#include "gui/canvas.h" +#include "gui/modules/widget_elements/widget_element_i.h" +#include "storage/storage.h" + +static void storage_move_to_sd_scene_confirm_widget_callback( + GuiButtonType result, + InputType type, + void* context) { + StorageMoveToSd* app = context; + furi_assert(app); + if(type == InputTypeShort) { + if(result == GuiButtonTypeRight) { + view_dispatcher_send_custom_event(app->view_dispatcher, MoveToSdCustomEventConfirm); + } else if(result == GuiButtonTypeLeft) { + view_dispatcher_send_custom_event(app->view_dispatcher, MoveToSdCustomEventExit); + } + } +} + +void storage_move_to_sd_scene_confirm_on_enter(void* context) { + StorageMoveToSd* app = context; + + widget_add_button_element( + app->widget, + GuiButtonTypeLeft, + "Cancel", + storage_move_to_sd_scene_confirm_widget_callback, + app); + widget_add_button_element( + app->widget, + GuiButtonTypeRight, + "Confirm", + storage_move_to_sd_scene_confirm_widget_callback, + app); + + widget_add_string_element( + app->widget, 64, 10, AlignCenter, AlignCenter, FontPrimary, "SD card inserted"); + widget_add_string_multiline_element( + app->widget, + 64, + 32, + AlignCenter, + AlignCenter, + FontSecondary, + "Move data from\ninternal storage to SD card?"); + + view_dispatcher_switch_to_view(app->view_dispatcher, StorageMoveToSdViewWidget); +} + +bool storage_move_to_sd_scene_confirm_on_event(void* context, SceneManagerEvent event) { + StorageMoveToSd* app = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == MoveToSdCustomEventConfirm) { + scene_manager_next_scene(app->scene_manager, StorageMoveToSdProgress); + consumed = true; + } else if(event.event == MoveToSdCustomEventExit) { + view_dispatcher_stop(app->view_dispatcher); + } + } + + return consumed; +} + +void storage_move_to_sd_scene_confirm_on_exit(void* context) { + StorageMoveToSd* app = context; + widget_reset(app->widget); +} diff --git a/applications/storage_move_to_sd/scenes/storage_move_to_sd_scene_progress.c b/applications/storage_move_to_sd/scenes/storage_move_to_sd_scene_progress.c new file mode 100644 index 00000000000..d44b25c38f8 --- /dev/null +++ b/applications/storage_move_to_sd/scenes/storage_move_to_sd_scene_progress.c @@ -0,0 +1,32 @@ +#include "../storage_move_to_sd.h" +#include "cmsis_os2.h" + +void storage_move_to_sd_scene_progress_on_enter(void* context) { + StorageMoveToSd* app = context; + + widget_add_string_element( + app->widget, 64, 10, AlignCenter, AlignCenter, FontPrimary, "Moving..."); + + view_dispatcher_switch_to_view(app->view_dispatcher, StorageMoveToSdViewWidget); + + storage_move_to_sd_perform(); + view_dispatcher_send_custom_event(app->view_dispatcher, MoveToSdCustomEventExit); +} + +bool storage_move_to_sd_scene_progress_on_event(void* context, SceneManagerEvent event) { + StorageMoveToSd* app = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + view_dispatcher_stop(app->view_dispatcher); + } else if(event.type == SceneManagerEventTypeBack) { + consumed = true; + } + + return consumed; +} + +void storage_move_to_sd_scene_progress_on_exit(void* context) { + StorageMoveToSd* app = context; + widget_reset(app->widget); +} diff --git a/applications/storage_move_to_sd/storage_move_to_sd.c b/applications/storage_move_to_sd/storage_move_to_sd.c new file mode 100644 index 00000000000..f703321d306 --- /dev/null +++ b/applications/storage_move_to_sd/storage_move_to_sd.c @@ -0,0 +1,177 @@ +#include "storage_move_to_sd.h" +#include "cmsis_os2.h" +#include "furi/common_defines.h" +#include "furi/log.h" +#include "loader/loader.h" +#include "m-string.h" +#include + +#define TAG "MoveToSd" + +#define MOVE_SRC "/int" +#define MOVE_DST "/ext" + +static const char* app_dirs[] = { + "subghz", + "lfrfid", + "nfc", + "infrared", + "ibutton", + "badusb", +}; + +bool storage_move_to_sd_perform(void) { + Storage* storage = furi_record_open("storage"); + string_t path_src; + string_t path_dst; + string_init(path_src); + string_init(path_dst); + + for(uint32_t i = 0; i < COUNT_OF(app_dirs); i++) { + string_printf(path_src, "%s/%s", MOVE_SRC, app_dirs[i]); + string_printf(path_dst, "%s/%s", MOVE_DST, app_dirs[i]); + storage_common_merge(storage, string_get_cstr(path_src), string_get_cstr(path_dst)); + storage_simply_remove_recursive(storage, string_get_cstr(path_src)); + } + + string_clear(path_src); + string_clear(path_dst); + + furi_record_close("storage"); + + return false; +} + +static bool storage_move_to_sd_check(void) { + Storage* storage = furi_record_open("storage"); + + FileInfo file_info; + bool state = false; + string_t path; + string_init(path); + + for(uint32_t i = 0; i < COUNT_OF(app_dirs); i++) { + string_printf(path, "%s/%s", MOVE_SRC, app_dirs[i]); + if(storage_common_stat(storage, string_get_cstr(path), &file_info) == FSE_OK) { + if((file_info.flags & FSF_DIRECTORY) != 0) { + state = true; + break; + } + } + } + + string_clear(path); + + furi_record_close("storage"); + + return state; +} + +static bool storage_move_to_sd_custom_event_callback(void* context, uint32_t event) { + furi_assert(context); + StorageMoveToSd* app = context; + return scene_manager_handle_custom_event(app->scene_manager, event); +} + +static bool storage_move_to_sd_back_event_callback(void* context) { + furi_assert(context); + StorageMoveToSd* app = context; + return scene_manager_handle_back_event(app->scene_manager); +} + +static void storage_move_to_sd_unmount_callback(const void* message, void* context) { + StorageMoveToSd* app = context; + furi_assert(app); + const StorageEvent* storage_event = message; + + if((storage_event->type == StorageEventTypeCardUnmount) || + (storage_event->type == StorageEventTypeCardMountError)) { + view_dispatcher_send_custom_event(app->view_dispatcher, MoveToSdCustomEventExit); + } +} + +static StorageMoveToSd* storage_move_to_sd_alloc() { + StorageMoveToSd* app = malloc(sizeof(StorageMoveToSd)); + + app->gui = furi_record_open("gui"); + app->notifications = furi_record_open("notification"); + + app->view_dispatcher = view_dispatcher_alloc(); + app->scene_manager = scene_manager_alloc(&storage_move_to_sd_scene_handlers, app); + + view_dispatcher_enable_queue(app->view_dispatcher); + view_dispatcher_set_event_callback_context(app->view_dispatcher, app); + + view_dispatcher_set_custom_event_callback( + app->view_dispatcher, storage_move_to_sd_custom_event_callback); + view_dispatcher_set_navigation_event_callback( + app->view_dispatcher, storage_move_to_sd_back_event_callback); + + view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen); + + app->widget = widget_alloc(); + view_dispatcher_add_view( + app->view_dispatcher, StorageMoveToSdViewWidget, widget_get_view(app->widget)); + + scene_manager_next_scene(app->scene_manager, StorageMoveToSdConfirm); + + Storage* storage = furi_record_open("storage"); + app->sub = furi_pubsub_subscribe( + storage_get_pubsub(storage), storage_move_to_sd_unmount_callback, app); + furi_record_close("storage"); + + return app; +} + +static void storage_move_to_sd_free(StorageMoveToSd* app) { + Storage* storage = furi_record_open("storage"); + furi_pubsub_unsubscribe(storage_get_pubsub(storage), app->sub); + furi_record_close("storage"); + furi_record_close("notification"); + + view_dispatcher_remove_view(app->view_dispatcher, StorageMoveToSdViewWidget); + widget_free(app->widget); + view_dispatcher_free(app->view_dispatcher); + scene_manager_free(app->scene_manager); + + furi_record_close("gui"); + + free(app); +} + +int32_t storage_move_to_sd_app(void* p) { + UNUSED(p); + + if(storage_move_to_sd_check()) { + StorageMoveToSd* app = storage_move_to_sd_alloc(); + notification_message(app->notifications, &sequence_display_backlight_on); + view_dispatcher_run(app->view_dispatcher); + storage_move_to_sd_free(app); + } else { + FURI_LOG_I(TAG, "Nothing to move"); + } + + return 0; +} + +static void storage_move_to_sd_mount_callback(const void* message, void* context) { + UNUSED(context); + + const StorageEvent* storage_event = message; + + if(storage_event->type == StorageEventTypeCardMount) { + Loader* loader = furi_record_open("loader"); + loader_start(loader, "StorageMoveToSd", NULL); + furi_record_close("loader"); + } +} + +int32_t storage_move_to_sd_start(void* p) { + UNUSED(p); + Storage* storage = furi_record_open("storage"); + + furi_pubsub_subscribe(storage_get_pubsub(storage), storage_move_to_sd_mount_callback, NULL); + + furi_record_close("storage"); + return 0; +} diff --git a/applications/storage_move_to_sd/storage_move_to_sd.h b/applications/storage_move_to_sd/storage_move_to_sd.h new file mode 100644 index 00000000000..dc1d669b519 --- /dev/null +++ b/applications/storage_move_to_sd/storage_move_to_sd.h @@ -0,0 +1,49 @@ +#pragma once +#include "gui/modules/widget_elements/widget_element_i.h" +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include + +#include "scenes/storage_move_to_sd_scene.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef enum { + MoveToSdCustomEventExit, + MoveToSdCustomEventConfirm, +} MoveToSdCustomEvent; + +typedef struct { + // records + Gui* gui; + Widget* widget; + NotificationApp* notifications; + + // view managment + SceneManager* scene_manager; + ViewDispatcher* view_dispatcher; + + FuriPubSubSubscription* sub; + +} StorageMoveToSd; + +typedef enum { + StorageMoveToSdViewWidget, +} StorageMoveToSdView; + +bool storage_move_to_sd_perform(void); + +#ifdef __cplusplus +} +#endif diff --git a/fbt b/fbt index b3d2ca354ee..0ea572b1221 100755 --- a/fbt +++ b/fbt @@ -1,18 +1,22 @@ -#!/bin/bash +#!/bin/sh -set -e +# shellcheck disable=SC2086 source=/dev/null +# unofficial strict mode +set -eu; -SCRIPTDIR="$( dirname -- "$0"; )"; -SCONS_EP=${SCRIPTDIR}/lib/scons/scripts/scons.py +SCONS_DEFAULT_FLAGS="-Q --warn=target-not-built"; +SCRIPT_PATH="$(cd "$(dirname "$0")" && pwd -P)"; -if [[ -z "${FBT_NO_SYNC:-}" ]] ; then - if [[ -d .git ]]; then - git submodule update --init - else - echo Not in a git repo, please clone with git clone --recursive - exit 1 +if [ -z "${FBT_NOENV:-}" ]; then + . "$SCRIPT_PATH/scripts/toolchain/fbtenv.sh"; +fi + +if [ -z "${FBT_NO_SYNC:-}" ]; then + if [ ! -d "$SCRIPT_PATH/.git" ]; then + echo "\".git\" directory not found, please clone repo via \"git clone --recursive\""; + exit 1; fi + git submodule update --init; fi -SCONS_DEFAULT_FLAGS="-Q --warn=target-not-built" -python3 ${SCONS_EP} ${SCONS_DEFAULT_FLAGS} "$@" +python3 "$SCRIPT_PATH/lib/scons/scripts/scons.py" $SCONS_DEFAULT_FLAGS "$@" \ No newline at end of file diff --git a/fbt.cmd b/fbt.cmd index 5edecd210d6..2339eaaa1ec 100644 --- a/fbt.cmd +++ b/fbt.cmd @@ -1,15 +1,17 @@ @echo off +call %~dp0scripts\toolchain\fbtenv.cmd env set SCONS_EP=%~dp0\lib\scons\scripts\scons.py if [%FBT_NO_SYNC%] == [] ( - if exist ".git" ( - git submodule update --init - ) else ( - echo Not in a git repo, please clone with git clone --recursive - exit /b 1 - ) + if exist ".git" ( + git submodule update --init + ) else ( + echo Not in a git repo, please clone with git clone --recursive + exit /b 1 + ) ) +git submodule update --init set "SCONS_DEFAULT_FLAGS=-Q --warn=target-not-built" -python %SCONS_EP% %SCONS_DEFAULT_FLAGS% %* +python lib\scons\scripts\scons.py %SCONS_DEFAULT_FLAGS% %* \ No newline at end of file diff --git a/fbt_options.py b/fbt_options.py index 1c9f9c82b52..fb2a0d366e1 100644 --- a/fbt_options.py +++ b/fbt_options.py @@ -10,8 +10,8 @@ ## Optimize for debugging experience DEBUG = 1 -# Suffix to add to files when building distribution. -# If OS environment has DIST_SUFFIX set, it will be used instead.. +# Suffix to add to files when building distribution +# If OS environment has DIST_SUFFIX set, it will be used instead DIST_SUFFIX = "local" # Coprocessor firmware @@ -27,7 +27,7 @@ # Firmware also supports "ble_full", but it might not fit into debug builds COPRO_STACK_TYPE = "ble_light" -# Leave 0 to lets scripts automatically calculate it +# Leave 0 to let scripts automatically calculate it COPRO_STACK_ADDR = "0x0" # If you override COPRO_CUBE_DIR on commandline, override this aswell @@ -56,7 +56,7 @@ SVD_FILE = "debug/STM32WB55_CM4.svd" -# Look for blackmagic probe on serial ports +# Look for blackmagic probe on serial ports and local network BLACKMAGIC = "auto" FIRMWARE_APPS = { @@ -67,6 +67,7 @@ # Apps "basic_apps", "updater_app", + "storage_move_to_sd", "archive", # Settings "passport", diff --git a/firmware.scons b/firmware.scons index 7cc67f436e1..2ad29c21cb7 100644 --- a/firmware.scons +++ b/firmware.scons @@ -201,7 +201,7 @@ fwelf = fwenv["FW_ELF"] = fwenv.Program( ], ) - +# Make it depend on everything child builders returned # Firmware depends on everything child builders returned Depends(fwelf, lib_targets) # Output extra details after building firmware diff --git a/lib/infrared/worker/infrared_worker.h b/lib/infrared/worker/infrared_worker.h index f54b3e01d6e..c6617e501e3 100644 --- a/lib/infrared/worker/infrared_worker.h +++ b/lib/infrared/worker/infrared_worker.h @@ -7,7 +7,7 @@ extern "C" { #endif -#define MAX_TIMINGS_AMOUNT 512 +#define MAX_TIMINGS_AMOUNT 1024 /** Interface struct of infrared worker */ typedef struct InfraredWorker InfraredWorker; diff --git a/scripts/toolchain/fbtenv.cmd b/scripts/toolchain/fbtenv.cmd new file mode 100644 index 00000000000..34adb12c133 --- /dev/null +++ b/scripts/toolchain/fbtenv.cmd @@ -0,0 +1,45 @@ +@echo off + +if not [%FBT_ROOT%] == [] ( + goto already_set +) + +set "FBT_ROOT=%~dp0\..\..\" +pushd %FBT_ROOT% +set "FBT_ROOT=%cd%" +popd + +if not [%FBT_NOENV%] == [] ( + exit /b 0 +) + +set "FLIPPER_TOOLCHAIN_VERSION=3" +set "FBT_TOOLCHAIN_ROOT=%FBT_ROOT%\toolchain\i686-windows" + + +if not exist "%FBT_TOOLCHAIN_ROOT%" ( + powershell -ExecutionPolicy Bypass -File %FBT_ROOT%\scripts\toolchain\windows-toolchain-download.ps1 "%flipper_toolchain_version%" +) +if not exist "%FBT_TOOLCHAIN_ROOT%\VERSION" ( + powershell -ExecutionPolicy Bypass -File %FBT_ROOT%\scripts\toolchain\windows-toolchain-download.ps1 "%flipper_toolchain_version%" +) +set /p REAL_TOOLCHAIN_VERSION=<%FBT_TOOLCHAIN_ROOT%\VERSION +if not "%REAL_TOOLCHAIN_VERSION%" == "%FLIPPER_TOOLCHAIN_VERSION%" ( + powershell -ExecutionPolicy Bypass -File %FBT_ROOT%\scripts\toolchain\windows-toolchain-download.ps1 "%flipper_toolchain_version%" +) + + +set "HOME=%USERPROFILE%" +set "PYTHONHOME=%FBT_TOOLCHAIN_ROOT%\python" +set "PATH=%FBT_TOOLCHAIN_ROOT%\python;%FBT_TOOLCHAIN_ROOT%\bin;%FBT_TOOLCHAIN_ROOT%\protoc\bin;%FBT_TOOLCHAIN_ROOT%\openocd\bin;%PATH%" +set "PROMPT=(fbt) %PROMPT%" + +:already_set + +if not "%1" == "env" ( + echo ********************************* + echo * fbt build environment * + echo ********************************* + cd %FBT_ROOT% + cmd /k +) diff --git a/scripts/toolchain/fbtenv.sh b/scripts/toolchain/fbtenv.sh new file mode 100755 index 00000000000..df2b2457643 --- /dev/null +++ b/scripts/toolchain/fbtenv.sh @@ -0,0 +1,54 @@ +#!/bin/sh + +# unofficial strict mode +set -eu; + +FLIPPER_TOOLCHAIN_VERSION="3"; + +get_kernel_type() +{ + SYS_TYPE="$(uname -s)" + if [ "$SYS_TYPE" = "Darwin" ]; then + TOOLCHAIN_PATH="toolchain/x86_64-darwin"; + elif [ "$SYS_TYPE" = "Linux" ]; then + TOOLCHAIN_PATH="toolchain/x86_64-linux"; + elif echo "$SYS_TYPE" | grep -q "MINGW"; then + echo "In MinGW shell use \"fbt.cmd\" instead of \"fbt\""; + exit 1; + else + echo "Your system is not supported. Sorry. Please report us your configuration."; + exit 1; + fi +} + +check_download_toolchain() +{ + if [ ! -d "$SCRIPT_PATH/$TOOLCHAIN_PATH" ]; then + download_toolchain; + elif [ ! -f "$SCRIPT_PATH/$TOOLCHAIN_PATH/VERSION" ]; then + download_toolchain; + elif [ "$(cat "$SCRIPT_PATH/$TOOLCHAIN_PATH/VERSION")" -ne "$FLIPPER_TOOLCHAIN_VERSION" ]; then + download_toolchain; + fi +} + +download_toolchain() +{ + chmod 755 "$SCRIPT_PATH/scripts/toolchain/unix-toolchain-download.sh"; + "$SCRIPT_PATH/scripts/toolchain/unix-toolchain-download.sh" "$FLIPPER_TOOLCHAIN_VERSION" || exit 1; +} + +main() +{ + if [ -z "${SCRIPT_PATH:-}" ]; then + echo "Mannual running this script is now allowed."; + exit 1; + fi + get_kernel_type; # sets TOOLCHAIN_PATH + check_download_toolchain; + PATH="$SCRIPT_PATH/$TOOLCHAIN_PATH/python/bin:$PATH"; + PATH="$SCRIPT_PATH/$TOOLCHAIN_PATH/bin:$PATH"; + PATH="$SCRIPT_PATH/$TOOLCHAIN_PATH/protobuf/bin:$PATH"; + PATH="$SCRIPT_PATH/$TOOLCHAIN_PATH/openocd/bin:$PATH"; +} +main; diff --git a/scripts/toolchain/unix-toolchain-download.sh b/scripts/toolchain/unix-toolchain-download.sh new file mode 100755 index 00000000000..386be2a3ca9 --- /dev/null +++ b/scripts/toolchain/unix-toolchain-download.sh @@ -0,0 +1,135 @@ +#!/bin/sh +# shellcheck disable=SC2086,SC2034 + +# unofficial strict mode +set -eu; + +check_system() +{ + VER="$1"; # toolchain version + printf "Checking kernel type.."; + SYS_TYPE="$(uname -s)" + if [ "$SYS_TYPE" = "Darwin" ]; then + echo "darwin"; + TOOLCHAIN_URL="https://update.flipperzero.one/builds/toolchain/gcc-arm-none-eabi-10.3-x86_64-darwin-flipper-$VER.tar.gz"; + TOOLCHAIN_PATH="toolchain/x86_64-darwin"; + elif [ "$SYS_TYPE" = "Linux" ]; then + echo "linux"; + TOOLCHAIN_URL="https://update.flipperzero.one/builds/toolchain/gcc-arm-none-eabi-10.3-x86_64-linux-flipper-$VER.tar.gz"; + TOOLCHAIN_PATH="toolchain/x86_64-linux"; + else + echo "unsupported."; + echo "Your system is unsupported.. sorry.."; + exit 1; + fi +} + +check_tar() +{ + printf "Checking tar.."; + if ! tar --version > /dev/null 2>&1; then + echo "no"; + exit 1; + fi + echo "yes"; +} + + +curl_wget_check() +{ + printf "Checking curl.."; + if ! curl --version > /dev/null 2>&1; then + echo "no"; + printf "Checking wget.."; + if ! wget --version > /dev/null 2>&1; then + echo "no"; + echo "No curl or wget found in your PATH."; + echo "Please provide it or download this file:"; + echo; + echo "$TOOLCHAIN_URL"; + echo; + echo "And place in repo root dir mannualy."; + exit 1; + fi + echo "yes" + DOWNLOADER="wget"; + DOWNLOADER_ARGS="--show-progress --progress=bar:force -qO"; + return; + fi + echo "yes" + DOWNLOADER="curl"; + DOWNLOADER_ARGS="--progress-bar -SLo"; +} + +check_downloaded_toolchain() +{ + printf "Checking downloaded toolchain tgz.."; + if [ -f "$REPO_ROOT/$TOOLCHAIN_TAR" ]; then + echo "yes"; + return 0; + fi + echo "no"; + return 1; +} + +download_toolchain() +{ + echo "Downloading toolchain:"; + "$DOWNLOADER" $DOWNLOADER_ARGS "$REPO_ROOT/$TOOLCHAIN_TAR" "$TOOLCHAIN_URL"; + echo "done"; +} + +remove_old_tooclhain() +{ + printf "Removing old toolchain (if exist).."; + rm -rf "${REPO_ROOT:?}/$TOOLCHAIN_PATH"; + echo "done"; +} + +show_unpack_percentage() +{ + LINE=0; + while read -r line; do + LINE=$(( LINE + 1 )); + if [ $(( LINE % 300 )) -eq 0 ]; then + printf "#"; + fi + done + echo " 100.0%"; +} + +unpack_toolchain() +{ + echo "Unpacking toolchain:"; + tar -xvf "$REPO_ROOT/$TOOLCHAIN_TAR" -C "$REPO_ROOT/" 2>&1 | show_unpack_percentage; + mkdir -p "$REPO_ROOT/toolchain"; + mv "$REPO_ROOT/$TOOLCHAIN_DIR" "$REPO_ROOT/$TOOLCHAIN_PATH/"; + echo "done"; +} + +clearing() +{ + printf "Clearing.."; + rm -rf "${REPO_ROOT:?}/$TOOLCHAIN_TAR"; + echo "done"; +} + +main() +{ + SCRIPT_PATH="$(cd "$(dirname "$0")" && pwd -P)" + REPO_ROOT="$(cd "$SCRIPT_PATH/../../" && pwd)"; + check_system "$1"; # recives TOOLCHAIN_VERSION, defines TOOLCHAIN_URL and TOOLCHAIN_PATH + check_tar; + TOOLCHAIN_TAR="$(basename "$TOOLCHAIN_URL")"; + TOOLCHAIN_DIR="$(echo "$TOOLCHAIN_TAR" | sed "s/-$VER.tar.gz//g")"; + if ! check_downloaded_toolchain; then + curl_wget_check; + download_toolchain; + fi + remove_old_tooclhain; + unpack_toolchain; +} + +trap clearing EXIT; +trap clearing 2; # SIGINT not coverable by EXIT +main "$1"; # toochain version diff --git a/scripts/toolchain/windows-toolchain-download.ps1 b/scripts/toolchain/windows-toolchain-download.ps1 new file mode 100644 index 00000000000..bcb8f998f25 --- /dev/null +++ b/scripts/toolchain/windows-toolchain-download.ps1 @@ -0,0 +1,34 @@ +Set-StrictMode -Version 2.0 +$ErrorActionPreference = "Stop" +[Net.ServicePointManager]::SecurityProtocol = "tls12, tls11, tls" +$repo_root = (Get-Item "$PSScriptRoot\..\..").FullName +$toolchain_version = $args[0] +$toolchain_url = "https://update.flipperzero.one/builds/toolchain/gcc-arm-none-eabi-10.3-i686-windows-flipper-$toolchain_version.zip" +$toolchain_zip = "gcc-arm-none-eabi-10.3-i686-windows-flipper-$toolchain_version.zip" +$toolchain_dir = "gcc-arm-none-eabi-10.3-i686-windows-flipper" + +if (Test-Path -LiteralPath "$repo_root\toolchain\i686-windows") { + Write-Host -NoNewline "Removing old Windows toolchain.." + Remove-Item -LiteralPath "$repo_root\toolchain\i686-windows" -Force -Recurse + Write-Host "done!" +} +if (!(Test-Path -Path "$repo_root\$toolchain_zip" -PathType Leaf)) { + Write-Host -NoNewline "Downloading Windows toolchain.." + $wc = New-Object net.webclient + $wc.Downloadfile("$toolchain_url", "$repo_root\$toolchain_zip") + Write-Host "done!" +} + +if (!(Test-Path -LiteralPath "$repo_root\toolchain")) { + New-Item "$repo_root\toolchain" -ItemType Directory +} + +Write-Host -NoNewline "Unziping Windows toolchain.." +Add-Type -Assembly "System.IO.Compression.Filesystem" +[System.IO.Compression.ZipFile]::ExtractToDirectory("$toolchain_zip", "$repo_root\") +Move-Item -Path "$repo_root\$toolchain_dir" -Destination "$repo_root\toolchain\i686-windows" +Write-Host "done!" + +Write-Host -NoNewline "Clearing temporary files.." +Remove-Item -LiteralPath "$repo_root\$toolchain_zip" -Force +Write-Host "done!" diff --git a/site_scons/fbt/appmanifest.py b/site_scons/fbt/appmanifest.py index 218b139f3ed..990bd5b3b0c 100644 --- a/site_scons/fbt/appmanifest.py +++ b/site_scons/fbt/appmanifest.py @@ -63,8 +63,13 @@ def App(*args, **kw): nonlocal app_manifests app_manifests.append(FlipperApplication(*args, **kw, _appdir=app_dir_name)) - with open(app_manifest_path, "rt") as manifest_file: - exec(manifest_file.read()) + try: + with open(app_manifest_path, "rt") as manifest_file: + exec(manifest_file.read()) + except Exception as e: + raise FlipperManifestException( + f"Failed parsing manifest '{app_manifest_path}' : {e}" + ) if len(app_manifests) == 0: raise FlipperManifestException( diff --git a/site_scons/fbt/util.py b/site_scons/fbt/util.py index a6bc4c063b8..8d8af518328 100644 --- a/site_scons/fbt/util.py +++ b/site_scons/fbt/util.py @@ -1,5 +1,6 @@ import SCons from SCons.Subst import quote_spaces +from SCons.Errors import StopError import re import os @@ -30,7 +31,7 @@ def link_dir(target_path, source_path, is_windows): import _winapi if not os.path.isdir(source_path): - raise Exception(f"Source directory {source_path} is not a directory") + raise StopError(f"Source directory {source_path} is not a directory") if not os.path.exists(target_path): _winapi.CreateJunction(source_path, target_path) diff --git a/site_scons/site_tools/crosscc.py b/site_scons/site_tools/crosscc.py index 8d6b4a61879..dbedf5c0795 100644 --- a/site_scons/site_tools/crosscc.py +++ b/site_scons/site_tools/crosscc.py @@ -1,3 +1,4 @@ +from SCons.Errors import StopError from SCons.Tool import asm from SCons.Tool import gcc from SCons.Tool import gxx @@ -65,7 +66,7 @@ def generate(env, **kw): # print("CC version =", cc_version) # print(list(filter(lambda v: v in cc_version, whitelisted_versions))) if not any(filter(lambda v: v in cc_version, whitelisted_versions)): - raise Exception( + raise StopError( f"Toolchain version is not supported. Allowed: {whitelisted_versions}, toolchain: {cc_version} " ) diff --git a/site_scons/site_tools/fbt_apps.py b/site_scons/site_tools/fbt_apps.py index bdccccf7b5c..1c2e0167aae 100644 --- a/site_scons/site_tools/fbt_apps.py +++ b/site_scons/site_tools/fbt_apps.py @@ -1,8 +1,14 @@ from SCons.Builder import Builder from SCons.Action import Action +from SCons.Errors import UserError import SCons -from fbt.appmanifest import FlipperAppType, AppManager, ApplicationsCGenerator +from fbt.appmanifest import ( + FlipperAppType, + AppManager, + ApplicationsCGenerator, + FlipperManifestException, +) # Adding objects for application management to env # AppManager env["APPMGR"] - loads all manifests; manages list of known apps @@ -13,7 +19,10 @@ def LoadApplicationManifests(env): appmgr = env["APPMGR"] = AppManager() for entry in env.Glob("#/applications/*"): if isinstance(entry, SCons.Node.FS.Dir) and not str(entry).startswith("."): - appmgr.load_manifest(entry.File("application.fam").abspath, entry.name) + try: + appmgr.load_manifest(entry.File("application.fam").abspath, entry.name) + except FlipperManifestException as e: + raise UserError(e) def PrepareApplicationsBuild(env):