diff --git a/.gitmodules b/.gitmodules index 898e6456..bd243b02 100644 --- a/.gitmodules +++ b/.gitmodules @@ -6,4 +6,4 @@ url = https://github.com/Flipper-Zero/STM32CubeWB.git [submodule "applications/floopper-bloopper"] path = applications/floopper-bloopper - url = https://github.com/glitchcore/floopper-bloopper.git + url = https://github.com/Flipper-Zero/floopper-bloopper.git diff --git a/applications/app-loader/app-loader.c b/applications/app-loader/app-loader.c index 3b824157..8332dc13 100644 --- a/applications/app-loader/app-loader.c +++ b/applications/app-loader/app-loader.c @@ -1,6 +1,5 @@ #include #include -#include #include "menu/menu.h" #include "menu/menu_item.h" #include "applications.h" @@ -9,125 +8,85 @@ typedef struct { FuriThread* thread; - ViewPort* view_port; const FlipperApplication* current_app; Cli* cli; - Gui* gui; } AppLoaderState; -typedef struct { - AppLoaderState* state; - const FlipperApplication* app; -} AppLoaderContext; +static AppLoaderState state; // TODO add mutex for contex -static void app_loader_render_callback(Canvas* canvas, void* _ctx) { - AppLoaderState* ctx = (AppLoaderState*)_ctx; - - canvas_clear(canvas); - canvas_set_color(canvas, ColorBlack); - canvas_set_font(canvas, FontPrimary); - canvas_draw_str(canvas, 2, 32, ctx->current_app->name); - - canvas_set_font(canvas, FontSecondary); - canvas_draw_str(canvas, 2, 44, "press back to exit"); -} - -static void app_loader_input_callback(InputEvent* input_event, void* _ctx) { - AppLoaderState* ctx = (AppLoaderState*)_ctx; - - if(input_event->type == InputTypeShort && input_event->key == InputKeyBack) { - furi_thread_terminate(ctx->thread); - } -} - static void app_loader_menu_callback(void* _ctx) { - AppLoaderContext* ctx = (AppLoaderContext*)_ctx; - - if(ctx->app->app == NULL) return; - - view_port_enabled_set(ctx->state->view_port, true); - + furi_assert(_ctx); + const FlipperApplication* flipper_app = (FlipperApplication*)_ctx; + furi_assert(flipper_app->app); + furi_assert(flipper_app->name); api_hal_power_insomnia_enter(); - ctx->state->current_app = ctx->app; + state.current_app = flipper_app; - furi_thread_set_name(ctx->state->thread, ctx->app->name); - furi_thread_set_stack_size(ctx->state->thread, ctx->app->stack_size); - furi_thread_set_callback(ctx->state->thread, ctx->app->app); - furi_thread_start(ctx->state->thread); + furi_thread_set_name(state.thread, flipper_app->name); + furi_thread_set_stack_size(state.thread, flipper_app->stack_size); + furi_thread_set_callback(state.thread, flipper_app->app); + furi_thread_start(state.thread); } static void app_loader_cli_callback(string_t args, void* _ctx) { - AppLoaderContext* ctx = (AppLoaderContext*)_ctx; + furi_assert(_ctx); + const FlipperApplication* flipper_app = (FlipperApplication*)_ctx; + furi_assert(flipper_app->app); + furi_assert(flipper_app->name); - if(ctx->app->app == NULL) return; - - printf("Starting furi application\r\n"); + if(!(furi_thread_get_state(state.thread) == FuriThreadStateStopped)) { + printf("Can't start, furi application is running"); + return; + } + printf("Starting furi application %s", flipper_app->name); api_hal_power_insomnia_enter(); - - furi_thread_set_name(ctx->state->thread, ctx->app->name); - furi_thread_set_stack_size(ctx->state->thread, ctx->app->stack_size); - furi_thread_set_callback(ctx->state->thread, ctx->app->app); - furi_thread_start(ctx->state->thread); - - printf("Press any key to kill application"); - - cli_getc(ctx->state->cli); - - furi_thread_terminate(ctx->state->thread); + furi_thread_set_name(state.thread, flipper_app->name); + furi_thread_set_stack_size(state.thread, flipper_app->stack_size); + furi_thread_set_callback(state.thread, flipper_app->app); + furi_thread_start(state.thread); } void app_loader_thread_state_callback(FuriThreadState state, void* context) { furi_assert(context); - AppLoaderState* app_loader_state = context; if(state == FuriThreadStateStopped) { - view_port_enabled_set(app_loader_state->view_port, false); api_hal_power_insomnia_exit(); } } int32_t app_loader(void* p) { - AppLoaderState state; state.thread = furi_thread_alloc(); furi_thread_set_state_context(state.thread, &state); furi_thread_set_state_callback(state.thread, app_loader_thread_state_callback); - state.view_port = view_port_alloc(); - view_port_enabled_set(state.view_port, false); - view_port_draw_callback_set(state.view_port, app_loader_render_callback, &state); - view_port_input_callback_set(state.view_port, app_loader_input_callback, &state); - ValueMutex* menu_mutex = furi_record_open("menu"); state.cli = furi_record_open("cli"); - state.gui = furi_record_open("gui"); - - gui_add_view_port(state.gui, state.view_port, GuiLayerFullscreen); // Main menu with_value_mutex( menu_mutex, (Menu * menu) { for(size_t i = 0; i < FLIPPER_APPS_COUNT; i++) { - AppLoaderContext* ctx = furi_alloc(sizeof(AppLoaderContext)); - ctx->state = &state; - ctx->app = &FLIPPER_APPS[i]; - + // Add menu item menu_item_add( menu, menu_item_alloc_function( FLIPPER_APPS[i].name, assets_icons_get(FLIPPER_APPS[i].icon), app_loader_menu_callback, - ctx)); + (void*)&FLIPPER_APPS[i])); // Add cli command string_t cli_name; string_init_set_str(cli_name, "app_"); string_cat_str(cli_name, FLIPPER_APPS[i].name); cli_add_command( - state.cli, string_get_cstr(cli_name), app_loader_cli_callback, ctx); + state.cli, + string_get_cstr(cli_name), + app_loader_cli_callback, + (void*)&FLIPPER_APPS[i]); string_clear(cli_name); } }); @@ -157,24 +116,24 @@ int32_t app_loader(void* p) { menu_item_alloc_menu("Plugins", assets_icons_get(A_Plugins_14)); for(size_t i = 0; i < FLIPPER_PLUGINS_COUNT; i++) { - AppLoaderContext* ctx = furi_alloc(sizeof(AppLoaderContext)); - ctx->state = &state; - ctx->app = &FLIPPER_PLUGINS[i]; - + // Add menu item menu_item_subitem_add( menu_plugins, menu_item_alloc_function( FLIPPER_PLUGINS[i].name, assets_icons_get(FLIPPER_PLUGINS[i].icon), app_loader_menu_callback, - ctx)); + (void*)&FLIPPER_PLUGINS[i])); // Add cli command string_t cli_name; string_init_set_str(cli_name, "app_"); string_cat_str(cli_name, FLIPPER_PLUGINS[i].name); cli_add_command( - state.cli, string_get_cstr(cli_name), app_loader_cli_callback, ctx); + state.cli, + string_get_cstr(cli_name), + app_loader_cli_callback, + (void*)&FLIPPER_PLUGINS[i]); string_clear(cli_name); } diff --git a/applications/applications.c b/applications/applications.c index a7aa6d3b..55e5c7cb 100644 --- a/applications/applications.c +++ b/applications/applications.c @@ -209,10 +209,7 @@ const FlipperApplication FLIPPER_PLUGINS[] = { #endif #ifdef BUILD_VIBRO_DEMO - {.app = application_vibro, - .name = "application_vibro", - .stack_size = 1024, - .icon = A_Plugins_14}, + {.app = application_vibro, .name = "vibro", .stack_size = 1024, .icon = A_Plugins_14}, #endif #ifdef BUILD_MUSIC_PLAYER diff --git a/applications/coreglitch_demo_0/coreglitch_demo_0.c b/applications/coreglitch_demo_0/coreglitch_demo_0.c index 3034e3c8..0bf93d79 100644 --- a/applications/coreglitch_demo_0/coreglitch_demo_0.c +++ b/applications/coreglitch_demo_0/coreglitch_demo_0.c @@ -1,13 +1,44 @@ #include +#include +#include #include #include "u8g2/u8g2.h" extern TIM_HandleTypeDef SPEAKER_TIM; +bool exit_app; + +static void event_cb(const void* value, void* ctx) { + furi_assert(value); + const InputEvent* event = value; + if(event->key == InputKeyBack && event->type == InputTypeShort) { + exit_app = true; + } +} + +void coreglitch_draw_callback(Canvas* canvas, void* ctx) { + canvas_clear(canvas); + canvas_set_font(canvas, FontPrimary); + canvas_draw_str(canvas, 2, 10, "Coreglitch demo application"); +} + int32_t coreglitch_demo_0(void* p) { printf("coreglitch demo!\r\n"); + exit_app = false; + PubSub* event_record = furi_record_open("input_events"); + PubSubItem* event_pubsub = subscribe_pubsub(event_record, event_cb, NULL); + + // Configure view port + ViewPort* view_port = view_port_alloc(); + furi_check(view_port); + view_port_draw_callback_set(view_port, coreglitch_draw_callback, NULL); + + // Register view port in GUI + Gui* gui = furi_record_open("gui"); + gui_add_view_port(gui, view_port, GuiLayerFullscreen); + float notes[] = { 0.0, 330.0, @@ -29,7 +60,7 @@ int32_t coreglitch_demo_0(void* p) { uint8_t cnt = 0; while(1) { - for(size_t note_idx = 0; note_idx < 400; note_idx++) { + for(size_t note_idx = 0; (note_idx < 400) && (!exit_app); note_idx++) { float scale = scales[((cnt + note_idx) / 16) % 4]; float freq = notes[(note_idx + cnt / 2) % 8] * scale; @@ -45,10 +76,17 @@ int32_t coreglitch_demo_0(void* p) { // delay(1); cnt++; - delay(100); } + if(exit_app) { + break; + } } + hal_pwm_stop(&SPEAKER_TIM, SPEAKER_CH); + view_port_enabled_set(view_port, false); + gui_remove_view_port(gui, view_port); + view_port_free(view_port); + unsubscribe_pubsub(event_pubsub); return 0; } diff --git a/applications/examples/blink.c b/applications/examples/blink.c index 4f3d6e13..ea6babaf 100644 --- a/applications/examples/blink.c +++ b/applications/examples/blink.c @@ -1,30 +1,96 @@ #include #include +#include +#include + +typedef enum { + EventTypeTick, + EventTypeKey, +} EventType; + +typedef struct { + EventType type; + InputEvent input; +} BlinkEvent; + void rgb_set(bool r, bool g, bool b) { api_hal_light_set(LightRed, r ? 0xFF : 0x00); api_hal_light_set(LightGreen, g ? 0xFF : 0x00); api_hal_light_set(LightBlue, b ? 0xFF : 0x00); } +void blink_update(void* ctx) { + furi_assert(ctx); + osMessageQueueId_t event_queue = ctx; + + BlinkEvent event = {.type = EventTypeTick}; + osMessageQueuePut(event_queue, &event, 0, 0); +} + +void blink_draw_callback(Canvas* canvas, void* ctx) { + canvas_clear(canvas); + canvas_set_font(canvas, FontPrimary); + canvas_draw_str(canvas, 2, 10, "Blink application"); +} + +void blink_input_callback(InputEvent* input_event, void* ctx) { + furi_assert(ctx); + osMessageQueueId_t event_queue = ctx; + + BlinkEvent event = {.type = EventTypeKey, .input = *input_event}; + osMessageQueuePut(event_queue, &event, 0, 0); +} + int32_t application_blink(void* p) { + osMessageQueueId_t event_queue = osMessageQueueNew(8, sizeof(BlinkEvent), NULL); + + // Configure view port + ViewPort* view_port = view_port_alloc(); + furi_check(view_port); + view_port_draw_callback_set(view_port, blink_draw_callback, NULL); + view_port_input_callback_set(view_port, blink_input_callback, event_queue); + osTimerId_t timer = osTimerNew(blink_update, osTimerPeriodic, event_queue, NULL); + osTimerStart(timer, 500); + + // Register view port in GUI + Gui* gui = furi_record_open("gui"); + gui_add_view_port(gui, view_port, GuiLayerFullscreen); + + bool blink_color[][3] = { + {1, 0, 0}, + {0, 1, 0}, + {0, 0, 1}, + {1, 1, 0}, + {0, 1, 1}, + {1, 0, 1}, + {1, 1, 1}, + {0, 0, 0}, + }; + uint8_t state = 0; + BlinkEvent event; + while(1) { - rgb_set(1, 0, 0); - delay(500); - rgb_set(0, 1, 0); - delay(500); - rgb_set(0, 0, 1); - delay(500); - rgb_set(1, 1, 0); - delay(500); - rgb_set(0, 1, 1); - delay(500); - rgb_set(1, 0, 1); - delay(500); - rgb_set(1, 1, 1); - delay(500); - rgb_set(0, 0, 0); - delay(500); + furi_check(osMessageQueueGet(event_queue, &event, NULL, osWaitForever) == osOK); + if(event.type == EventTypeKey) { + if((event.input.type == InputTypeShort) && (event.input.key == InputKeyBack)) { + rgb_set(0, 0, 0); + view_port_enabled_set(view_port, false); + gui_remove_view_port(gui, view_port); + view_port_free(view_port); + osMessageQueueDelete(event_queue); + osTimerDelete(timer); + + return 0; + } + } else { + if(state < sizeof(blink_color) / sizeof(blink_color[0])) { + state++; + } else { + state = 0; + } + rgb_set(blink_color[state][0], blink_color[state][1], blink_color[state][2]); + } } return 0; diff --git a/applications/examples/input_dump.c b/applications/examples/input_dump.c index 19bd1c82..9644f746 100644 --- a/applications/examples/input_dump.c +++ b/applications/examples/input_dump.c @@ -1,28 +1,57 @@ #include #include #include +#include #include -typedef union { - unsigned int packed; - InputType state; -} InputDump; +typedef struct { + InputEvent input; +} InputDumpEvent; -static void event_cb(const void* value, void* ctx) { - const InputEvent* event = value; +void input_dump_draw_callback(Canvas* canvas, void* ctx) { + canvas_clear(canvas); + canvas_set_font(canvas, FontPrimary); + canvas_draw_str(canvas, 2, 10, "Input dump application"); + canvas_set_font(canvas, FontSecondary); + canvas_draw_str(canvas, 2, 22, "Press long back to exit"); +} - printf("event: %02x %s\r\n", event->key, event->type ? "pressed" : "released"); +void input_dump_input_callback(InputEvent* input_event, void* ctx) { + furi_assert(ctx); + osMessageQueueId_t event_queue = ctx; + InputDumpEvent event = {.input = *input_event}; + osMessageQueuePut(event_queue, &event, 0, 0); } int32_t application_input_dump(void* p) { - // open record - PubSub* event_record = furi_record_open("input_events"); - subscribe_pubsub(event_record, event_cb, NULL); + osMessageQueueId_t event_queue = osMessageQueueNew(8, sizeof(InputDumpEvent), NULL); + + // Configure view port + ViewPort* view_port = view_port_alloc(); + furi_check(view_port); + view_port_draw_callback_set(view_port, input_dump_draw_callback, NULL); + view_port_input_callback_set(view_port, input_dump_input_callback, event_queue); + + // Register view port in GUI + Gui* gui = furi_record_open("gui"); + gui_add_view_port(gui, view_port, GuiLayerFullscreen); printf("Example app [input dump]\r\n"); + InputDumpEvent event; - for(;;) { - delay(100); + while(1) { + furi_check(osMessageQueueGet(event_queue, &event, NULL, osWaitForever) == osOK); + if(event.input.type == InputTypeLong && event.input.key == InputKeyBack) { + view_port_enabled_set(view_port, false); + gui_remove_view_port(gui, view_port); + view_port_free(view_port); + osMessageQueueDelete(event_queue); + + return 0; + } else if(event.input.type == InputTypePress || event.input.type == InputTypeRelease) { + printf( + "event: %02x %s\r\n", event.input.key, event.input.type ? "pressed" : "released"); + } } return 0; diff --git a/applications/examples/vibro.c b/applications/examples/vibro.c index 175bb05b..70bae8a9 100644 --- a/applications/examples/vibro.c +++ b/applications/examples/vibro.c @@ -1,40 +1,70 @@ #include #include +#include #include typedef struct { - GpioPin* vibro; -} Ctx; + InputEvent input; +} VibroEvent; -static void button_handler(const void* value, void* _ctx) { - const InputEvent* event = value; - Ctx* ctx = (Ctx*)_ctx; +void vibro_draw_callback(Canvas* canvas, void* ctx) { + canvas_clear(canvas); + canvas_set_font(canvas, FontPrimary); + canvas_draw_str(canvas, 2, 10, "Vibro application"); + canvas_set_font(canvas, FontSecondary); + canvas_draw_str(canvas, 2, 22, "Press OK turns on vibro"); + canvas_set_font(canvas, FontSecondary); + canvas_draw_str(canvas, 2, 34, "Release OK turns off vibro"); +} - if(event->key != InputKeyOk) return; +void vibro_input_callback(InputEvent* input_event, void* ctx) { + furi_assert(ctx); + osMessageQueueId_t event_queue = ctx; - if(event->type == InputTypePress) { - api_hal_light_set(LightGreen, 0xFF); - gpio_write(ctx->vibro, true); - } else if(event->type == InputTypeRelease) { - api_hal_light_set(LightGreen, 0x00); - gpio_write(ctx->vibro, false); - } + VibroEvent event = {.input = *input_event}; + osMessageQueuePut(event_queue, &event, 0, 0); } int32_t application_vibro(void* p) { - Ctx ctx = {.vibro = (GpioPin*)&vibro_gpio}; + GpioPin* gpio = (GpioPin*)&vibro_gpio; + osMessageQueueId_t event_queue = osMessageQueueNew(8, sizeof(VibroEvent), NULL); - gpio_init(ctx.vibro, GpioModeOutputPushPull); - gpio_write(ctx.vibro, false); + // Configure view port + ViewPort* view_port = view_port_alloc(); + furi_check(view_port); + view_port_draw_callback_set(view_port, vibro_draw_callback, NULL); + view_port_input_callback_set(view_port, vibro_input_callback, event_queue); - // subscribe on buttons - PubSub* event_record = furi_record_open("input_events"); - furi_check(event_record); - subscribe_pubsub(event_record, button_handler, &ctx); + // Register view port in GUI + Gui* gui = furi_record_open("gui"); + gui_add_view_port(gui, view_port, GuiLayerFullscreen); + + gpio_init(gpio, GpioModeOutputPushPull); + gpio_write(gpio, false); + VibroEvent event; while(1) { - osDelay(osWaitForever); + furi_check(osMessageQueueGet(event_queue, &event, NULL, osWaitForever) == osOK); + if(event.input.type == InputTypeShort && event.input.key == InputKeyBack) { + gpio_write(gpio, false); + api_hal_light_set(LightGreen, 0); + view_port_enabled_set(view_port, false); + gui_remove_view_port(gui, view_port); + view_port_free(view_port); + osMessageQueueDelete(event_queue); + + return 0; + } + if(event.input.key == InputKeyOk) { + if(event.input.type == InputTypePress) { + gpio_write(gpio, true); + api_hal_light_set(LightGreen, 255); + } else if(event.input.type == InputTypeRelease) { + gpio_write(gpio, false); + api_hal_light_set(LightGreen, 0); + } + } } return 0; diff --git a/applications/floopper-bloopper b/applications/floopper-bloopper index a68a7eb2..82be9fcd 160000 --- a/applications/floopper-bloopper +++ b/applications/floopper-bloopper @@ -1 +1 @@ -Subproject commit a68a7eb200712b24c16a62713a5d137152805cd1 +Subproject commit 82be9fcd35c2057a3aee799be3172fa4415ef069 diff --git a/applications/gui-test/gui-test.c b/applications/gui-test/gui-test.c index b0acf819..5ca2cd7f 100644 --- a/applications/gui-test/gui-test.c +++ b/applications/gui-test/gui-test.c @@ -10,6 +10,8 @@ #include #include +#define GUI_TEST_FLAG_EXIT 0x00000001U + typedef enum { GuiTesterViewTextInput = 0, GuiTesterViewSubmenu, @@ -69,6 +71,25 @@ static GuiTester* gui_test_alloc(void) { return gui_tester; } +static void gui_test_free(GuiTester* gui_tester) { + furi_assert(gui_tester); + view_dispatcher_remove_view(gui_tester->view_dispatcher, GuiTesterViewDialog); + dialog_free(gui_tester->dialog); + view_dispatcher_remove_view(gui_tester->view_dispatcher, GuiTesterViewDialogEx); + dialog_ex_free(gui_tester->dialog_ex); + view_dispatcher_remove_view(gui_tester->view_dispatcher, GuiTesterViewSubmenu); + submenu_free(gui_tester->submenu); + view_dispatcher_remove_view(gui_tester->view_dispatcher, GuiTesterViewTextInput); + text_input_free(gui_tester->text_input); + view_dispatcher_remove_view(gui_tester->view_dispatcher, GuiTesterViewPopup); + popup_free(gui_tester->popup); + view_dispatcher_remove_view(gui_tester->view_dispatcher, GuiTesterViewByteInput); + byte_input_free(gui_tester->byte_input); + + view_dispatcher_free(gui_tester->view_dispatcher); + free(gui_tester); +} + static void next_view(void* context) { furi_assert(context); GuiTester* gui_tester = context; @@ -105,8 +126,19 @@ static void byte_input_callback(void* context, uint8_t* bytes, uint8_t bytes_cou next_view(context); } +static void event_cb(const void* value, void* ctx) { + furi_assert(value); + furi_assert(ctx); + const InputEvent* event = value; + if(event->key == InputKeyBack && event->type == InputTypeLong) { + osThreadFlagsSet((osThreadId_t)ctx, GUI_TEST_FLAG_EXIT); + } +} + int32_t gui_test(void* param) { (void)param; + PubSub* event_record = furi_record_open("input_events"); + PubSubItem* event_pubsub = subscribe_pubsub(event_record, event_cb, (void*)osThreadGetId()); GuiTester* gui_tester = gui_test_alloc(); Gui* gui = furi_record_open("gui"); @@ -194,8 +226,13 @@ int32_t gui_test(void* param) { view_dispatcher_switch_to_view(gui_tester->view_dispatcher, gui_tester->view_index); while(1) { - osDelay(1000); + if(osThreadFlagsWait(GUI_TEST_FLAG_EXIT, osFlagsWaitAny, osWaitForever)) { + break; + } } + unsubscribe_pubsub(event_pubsub); + free(text_input_text); + gui_test_free(gui_tester); return 0; } \ No newline at end of file diff --git a/applications/music-player/music-player.c b/applications/music-player/music-player.c index b4bac7f1..7e065b1f 100644 --- a/applications/music-player/music-player.c +++ b/applications/music-player/music-player.c @@ -408,8 +408,16 @@ int32_t music_player(void* p) { if(event_status == osOK) { if(event.type == EventTypeKey) { // press events - if(event.value.input.type == InputTypePress && + if(event.value.input.type == InputTypeShort && event.value.input.key == InputKeyBack) { + osThreadTerminate(player); + hal_pwm_stop(&SPEAKER_TIM, SPEAKER_CH); + view_port_enabled_set(view_port, false); + gui_remove_view_port(gui, view_port); + view_port_free(view_port); + osMessageQueueDelete(event_queue); + + return 0; } if(event.value.input.type == InputTypePress && diff --git a/core/furi/thread.c b/core/furi/thread.c index 81c0eeed..fd679fa7 100644 --- a/core/furi/thread.c +++ b/core/furi/thread.c @@ -92,6 +92,11 @@ void furi_thread_set_state_context(FuriThread* thread, void* context) { thread->state_context = context; } +FuriThreadState furi_thread_get_state(FuriThread* thread) { + furi_assert(thread); + return thread->state; +} + bool furi_thread_start(FuriThread* thread) { furi_assert(thread); furi_assert(thread->callback); diff --git a/core/furi/thread.h b/core/furi/thread.h index 6d502c21..d73141c2 100644 --- a/core/furi/thread.h +++ b/core/furi/thread.h @@ -77,6 +77,12 @@ void furi_thread_set_state_callback(FuriThread* thread, FuriThreadStateCallback */ void furi_thread_set_state_context(FuriThread* thread, void* context); +/* Get FuriThread state + * @param thread - FuriThread instance + * @return thread state from FuriThreadState + */ +FuriThreadState furi_thread_get_state(FuriThread* thread); + /* Start FuriThread * @param thread - FuriThread instance * @return true on success diff --git a/make/rules.mk b/make/rules.mk index a29cac3c..85e0413a 100644 --- a/make/rules.mk +++ b/make/rules.mk @@ -27,6 +27,7 @@ $(info $(shell $(BUILD_FLAGS_SHELL))) CHECK_AND_REINIT_SUBMODULES_SHELL=\ if git submodule status | egrep -q '^[-]|^[+]' ; then \ echo "INFO: Need to reinitialize git submodules"; \ + git submodule sync; \ git submodule update --init; \ fi $(info $(shell $(CHECK_AND_REINIT_SUBMODULES_SHELL)))