diff --git a/.github/actions/build-app/action.yml b/.github/actions/build-app/action.yml index 4d9d1b7..173aea2 100644 --- a/.github/actions/build-app/action.yml +++ b/.github/actions/build-app/action.yml @@ -18,7 +18,7 @@ runs: path: . # The export reset prevents an error when building other targets # The default environment variable is set to "esp32" by the GitHub Action - command: 'export IDF_TARGET= && python tactility.py Apps/${{ inputs.app_name }} build' + command: 'export IDF_TARGET= && python tactility.py build -p Apps/${{ inputs.app_name }}' - name: 'Upload Artifact' uses: actions/upload-artifact@v4 with: diff --git a/Apps/Brainfuck/CMakeLists.txt b/Apps/Brainfuck/CMakeLists.txt index 606d3fb..dd2ced2 100644 --- a/Apps/Brainfuck/CMakeLists.txt +++ b/Apps/Brainfuck/CMakeLists.txt @@ -8,6 +8,6 @@ endif() get_filename_component(TACTILITY_SDK_PATH "$ENV{TACTILITY_SDK_PATH}" ABSOLUTE) include("${TACTILITY_SDK_PATH}/TactilitySDK.cmake") -tactility_project_pre(tactility.brainfuck) -project(tactility.brainfuck) -tactility_project_post(tactility.brainfuck) +tactility_project_pre(brainfuck) +project(brainfuck) +tactility_project_post(brainfuck) diff --git a/Apps/Brainfuck/manifest.properties b/Apps/Brainfuck/manifest.properties index c42c8ad..b80f1f4 100644 --- a/Apps/Brainfuck/manifest.properties +++ b/Apps/Brainfuck/manifest.properties @@ -1,8 +1,11 @@ -manifest.version=0.2 +manifest.version=0.3 target.sdk=0.8.0-dev target.platforms=esp32,esp32s3,esp32c6,esp32p4 -app.id=tactility.brainfuck -app.version.name=0.11.0 -app.version.code=11 -app.name=Brainfuck interpreter -app.description=Brainfuck esoteric language interpreter +id=tactility.brainfuck +version.name=0.12.0 +version.code=12 +name=Brainfuck +description=Brainfuck esoteric language interpreter +app.0.id=tactility.brainfuck +app.0.name=Brainfuck interpreter +app.0.binary=brainfuck diff --git a/Apps/Breakout/CMakeLists.txt b/Apps/Breakout/CMakeLists.txt index a5e23d7..366ebdf 100644 --- a/Apps/Breakout/CMakeLists.txt +++ b/Apps/Breakout/CMakeLists.txt @@ -8,6 +8,6 @@ endif() get_filename_component(TACTILITY_SDK_PATH "$ENV{TACTILITY_SDK_PATH}" ABSOLUTE) include("${TACTILITY_SDK_PATH}/TactilitySDK.cmake") -tactility_project_pre(tactility.breakout) -project(tactility.breakout) -tactility_project_post(tactility.breakout) +tactility_project_pre(breakout) +project(breakout) +tactility_project_post(breakout) diff --git a/Apps/Breakout/manifest.properties b/Apps/Breakout/manifest.properties index 1595a9d..c8a61d6 100644 --- a/Apps/Breakout/manifest.properties +++ b/Apps/Breakout/manifest.properties @@ -1,8 +1,11 @@ -manifest.version=0.2 +manifest.version=0.3 target.sdk=0.8.0-dev target.platforms=esp32,esp32s3,esp32c6,esp32p4 -app.id=tactility.breakout -app.version.name=0.14.0 -app.version.code=14 -app.name=Breakout -app.description=Classic brick-breaking arcade game +id=tactility.breakout +version.name=0.15.0 +version.code=15 +name=Breakout +description=Classic brick-breaking arcade game +app.0.id=tactility.breakout +app.0.name=Breakout +app.0.binary=breakout diff --git a/Apps/Calculator/CMakeLists.txt b/Apps/Calculator/CMakeLists.txt index e6cd874..9d9076d 100644 --- a/Apps/Calculator/CMakeLists.txt +++ b/Apps/Calculator/CMakeLists.txt @@ -8,6 +8,6 @@ endif() get_filename_component(TACTILITY_SDK_PATH "$ENV{TACTILITY_SDK_PATH}" ABSOLUTE) include("${TACTILITY_SDK_PATH}/TactilitySDK.cmake") -tactility_project_pre(tactility.calculator) -project(tactility.calculator) -tactility_project_post(tactility.calculator) +tactility_project_pre(calculator) +project(calculator) +tactility_project_post(calculator) diff --git a/Apps/Calculator/manifest.properties b/Apps/Calculator/manifest.properties index 0898279..8fdbaae 100644 --- a/Apps/Calculator/manifest.properties +++ b/Apps/Calculator/manifest.properties @@ -1,7 +1,10 @@ -manifest.version=0.2 +manifest.version=0.3 target.sdk=0.8.0-dev target.platforms=esp32,esp32s3,esp32c6,esp32p4 -app.id=tactility.calculator -app.version.name=0.12.0 -app.version.code=12 -app.name=Calculator +id=tactility.calculator +version.name=0.13.0 +version.code=13 +name=Calculator +app.0.id=tactility.calculator +app.0.name=Calculator +app.0.binary=calculator diff --git a/Apps/CoreUtils/date/CMakeLists.txt b/Apps/CoreUtils/date/CMakeLists.txt new file mode 100644 index 0000000..d8e46bd --- /dev/null +++ b/Apps/CoreUtils/date/CMakeLists.txt @@ -0,0 +1,13 @@ +# tactility-cmakelists-version: 1 +cmake_minimum_required(VERSION 3.20) + +if (NOT DEFINED ENV{TACTILITY_SDK_PATH}) + message(FATAL_ERROR "TACTILITY_SDK_PATH environment variable is not set") +endif() + +get_filename_component(TACTILITY_SDK_PATH "$ENV{TACTILITY_SDK_PATH}" ABSOLUTE) +include("${TACTILITY_SDK_PATH}/TactilitySDK.cmake") + +tactility_project_pre(coreutils.date) +project(coreutils.date) +tactility_project_post(coreutils.date) diff --git a/Apps/CoreUtils/date/main/CMakeLists.txt b/Apps/CoreUtils/date/main/CMakeLists.txt new file mode 100644 index 0000000..545b36c --- /dev/null +++ b/Apps/CoreUtils/date/main/CMakeLists.txt @@ -0,0 +1,4 @@ +include("$ENV{TACTILITY_SDK_PATH}/TactilitySDK.cmake") + +file(GLOB_RECURSE SOURCE_FILES Source/*.c) +tactility_component_register(SRCS ${SOURCE_FILES}) diff --git a/Apps/CoreUtils/date/main/Source/main.c b/Apps/CoreUtils/date/main/Source/main.c new file mode 100644 index 0000000..7bdf5f1 --- /dev/null +++ b/Apps/CoreUtils/date/main/Source/main.c @@ -0,0 +1,12 @@ +#include +#include + +int main(int argc, char* argv[]) { + time_t now = time(NULL); + char buffer[64]; + struct tm local_time; + localtime_r(&now, &local_time); + strftime(buffer, sizeof(buffer), "%a %b %e %H:%M:%S %Z %Y", &local_time); + printf("%s\n", buffer); + return 0; +} diff --git a/Apps/CoreUtils/echo/CMakeLists.txt b/Apps/CoreUtils/echo/CMakeLists.txt new file mode 100644 index 0000000..ca189b9 --- /dev/null +++ b/Apps/CoreUtils/echo/CMakeLists.txt @@ -0,0 +1,13 @@ +# tactility-cmakelists-version: 1 +cmake_minimum_required(VERSION 3.20) + +if (NOT DEFINED ENV{TACTILITY_SDK_PATH}) + message(FATAL_ERROR "TACTILITY_SDK_PATH environment variable is not set") +endif() + +get_filename_component(TACTILITY_SDK_PATH "$ENV{TACTILITY_SDK_PATH}" ABSOLUTE) +include("${TACTILITY_SDK_PATH}/TactilitySDK.cmake") + +tactility_project_pre(coreutils.echo) +project(coreutils.echo) +tactility_project_post(coreutils.echo) diff --git a/Apps/CoreUtils/echo/main/CMakeLists.txt b/Apps/CoreUtils/echo/main/CMakeLists.txt new file mode 100644 index 0000000..545b36c --- /dev/null +++ b/Apps/CoreUtils/echo/main/CMakeLists.txt @@ -0,0 +1,4 @@ +include("$ENV{TACTILITY_SDK_PATH}/TactilitySDK.cmake") + +file(GLOB_RECURSE SOURCE_FILES Source/*.c) +tactility_component_register(SRCS ${SOURCE_FILES}) diff --git a/Apps/CoreUtils/echo/main/Source/main.c b/Apps/CoreUtils/echo/main/Source/main.c new file mode 100644 index 0000000..b56d923 --- /dev/null +++ b/Apps/CoreUtils/echo/main/Source/main.c @@ -0,0 +1,12 @@ +#include + +int main(int argc, char* argv[]) { + for (int i = 1; i < argc; i++) { + if (i > 1) { + printf(" "); + } + printf("%s", argv[i]); + } + printf("\n"); + return 0; +} diff --git a/Apps/CoreUtils/manifest.properties b/Apps/CoreUtils/manifest.properties new file mode 100644 index 0000000..feeb7a0 --- /dev/null +++ b/Apps/CoreUtils/manifest.properties @@ -0,0 +1,16 @@ +manifest.version=0.3 +target.sdk=0.8.0-dev +target.platforms=posix-x86_64,esp32s3 +id=tactility.coreutils +version.name=0.2.0 +version.code=2 +name=CoreUtils +description=Commandline utilities +app.0.id=tactility.coreutils.echo +app.0.name=Echo +app.0.binary=echo +app.0.hidden=true +app.1.id=tactility.coreutils.date +app.1.name=Date +app.1.binary=date +app.1.hidden=true diff --git a/Apps/Diceware/CMakeLists.txt b/Apps/Diceware/CMakeLists.txt index 56515ee..4dc314d 100644 --- a/Apps/Diceware/CMakeLists.txt +++ b/Apps/Diceware/CMakeLists.txt @@ -8,6 +8,6 @@ endif() get_filename_component(TACTILITY_SDK_PATH "$ENV{TACTILITY_SDK_PATH}" ABSOLUTE) include("${TACTILITY_SDK_PATH}/TactilitySDK.cmake") -tactility_project_pre(tactility.diceware) -project(tactility.diceware) -tactility_project_post(tactility.diceware) +tactility_project_pre(diceware) +project(diceware) +tactility_project_post(diceware) diff --git a/Apps/Diceware/main/Source/Diceware.cpp b/Apps/Diceware/main/Source/Diceware.cpp index 28f9100..47ae366 100644 --- a/Apps/Diceware/main/Source/Diceware.cpp +++ b/Apps/Diceware/main/Source/Diceware.cpp @@ -1,6 +1,6 @@ #include "Diceware.h" -#include +#include #include #include #include @@ -115,7 +115,7 @@ static void onHelpClicked(lv_event_t* e) { "The hardware random number generator can use the Wi-Fi radio to improve randomness. There's no need to connect to a Wi-Fi network for this to work.", "OK", }; - app_manager_start_for_result("AlertDialog", ctx->appInstanceId, 3, argv, &ctx->pendingHelpDialogId); + app_start_for_result("AlertDialog", 3, argv, ctx->appInstanceId, &ctx->pendingHelpDialogId); } void dicewareCreateWidgets(lv_obj_t* parent, void* userData) { diff --git a/Apps/Diceware/manifest.properties b/Apps/Diceware/manifest.properties index a379ff3..4e9903e 100644 --- a/Apps/Diceware/manifest.properties +++ b/Apps/Diceware/manifest.properties @@ -1,7 +1,11 @@ -manifest.version=0.2 +manifest.version=0.3 target.sdk=0.8.0-dev target.platforms=esp32,esp32s3,esp32c6,esp32p4 -app.id=tactility.diceware -app.version.name=0.14.0 -app.version.code=14 -app.name=Diceware +id=tactility.diceware +version.name=0.15.0 +version.code=15 +name=Diceware +description=Password generator +app.0.id=tactility.diceware +app.0.name=Diceware +app.0.binary=diceware diff --git a/Apps/Doom/CMakeLists.txt b/Apps/Doom/CMakeLists.txt index 354c8fb..5e2da40 100644 --- a/Apps/Doom/CMakeLists.txt +++ b/Apps/Doom/CMakeLists.txt @@ -8,6 +8,6 @@ endif() get_filename_component(TACTILITY_SDK_PATH "$ENV{TACTILITY_SDK_PATH}" ABSOLUTE) include("${TACTILITY_SDK_PATH}/TactilitySDK.cmake") -tactility_project_pre(tactility.doom) -project(tactility.doom) -tactility_project_post(tactility.doom) +tactility_project_pre(doom) +project(doom) +tactility_project_post(doom) diff --git a/Apps/Doom/main/Source/main.cpp b/Apps/Doom/main/Source/main.cpp index a07e29d..686d166 100644 --- a/Apps/Doom/main/Source/main.cpp +++ b/Apps/Doom/main/Source/main.cpp @@ -5,6 +5,7 @@ #include #include #include +#include #include #include @@ -22,7 +23,7 @@ constexpr auto* TAG = "Doom"; static void showErrorAndWait(AppInstanceId appInstanceId, const char* message) { const char* argv[] = { "Error", message, "OK" }; uint32_t dialogInstanceId = 0; - app_manager_start_for_result("tactility.alertdialog", appInstanceId, 3, argv, &dialogInstanceId); + app_start_for_result("tactility.alertdialog", 3, argv, appInstanceId, &dialogInstanceId); if (dialogInstanceId == 0) { return; } diff --git a/Apps/Doom/manifest.properties b/Apps/Doom/manifest.properties index 6a28a36..5c48198 100644 --- a/Apps/Doom/manifest.properties +++ b/Apps/Doom/manifest.properties @@ -1,9 +1,12 @@ -manifest.version=0.2 +manifest.version=0.3 target.sdk=0.8.0-dev target.platforms=esp32p4 -app.id=tactility.doom -app.version.name=0.5.0 -app.version.code=5 -app.name=Doom -app.description=Classic DOOM. Tab5 only, keyboard required to play. +id=tactility.doom +version.name=0.6.0 +version.code=6 +name=Doom +description=Classic DOOM. Tab5 only, keyboard required to play. +app.0.id=tactility.doom +app.0.name=Doom +app.0.binary=doom requires.device.id=m5stack-tab5 diff --git a/Apps/EpubReader/CMakeLists.txt b/Apps/EpubReader/CMakeLists.txt index 31c6298..6a683c6 100644 --- a/Apps/EpubReader/CMakeLists.txt +++ b/Apps/EpubReader/CMakeLists.txt @@ -8,6 +8,6 @@ endif() get_filename_component(TACTILITY_SDK_PATH "$ENV{TACTILITY_SDK_PATH}" ABSOLUTE) include("${TACTILITY_SDK_PATH}/TactilitySDK.cmake") -tactility_project_pre(tactility.epubreader) -project(tactility.epubreader) -tactility_project_post(tactility.epubreader) +tactility_project_pre(epubreader) +project(epubreader) +tactility_project_post(epubreader) diff --git a/Apps/EpubReader/main/Source/EpubReader.cpp b/Apps/EpubReader/main/Source/EpubReader.cpp index 279c5ca..bbce5a0 100644 --- a/Apps/EpubReader/main/Source/EpubReader.cpp +++ b/Apps/EpubReader/main/Source/EpubReader.cpp @@ -1,8 +1,8 @@ #include "EpubReader.h" #include "HtmlStrip.h" // stripHtmlToText -#include #include +#include #include #include #include @@ -192,9 +192,10 @@ void openTocDialog(Context* ctx) { for (const auto& item : toc) { argv.push_back(item.title.c_str()); } - app_manager_start_for_result( - "SelectionDialog", ctx->appInstanceId, - (int)argv.size(), argv.data(), &ctx->tocDialogId_ + app_start_for_result( + "SelectionDialog", + (int)argv.size(), argv.data(), + ctx->appInstanceId, &ctx->tocDialogId_ ); } @@ -215,8 +216,8 @@ void epubReaderCreateWidgets(lv_obj_t* parent, void* userData) { "Epub Reader requires a device with PSRAM and cannot run on this hardware.", "OK", }; - app_manager_start_for_result( - "AlertDialog", ctx->appInstanceId, 3, argv, &ctx->psramAlertId_ + app_start_for_result( + "AlertDialog", 3, argv, ctx->appInstanceId, &ctx->psramAlertId_ ); } return; diff --git a/Apps/EpubReader/manifest.properties b/Apps/EpubReader/manifest.properties index cf84f8c..a8a42f5 100644 --- a/Apps/EpubReader/manifest.properties +++ b/Apps/EpubReader/manifest.properties @@ -1,8 +1,11 @@ -manifest.version=0.2 +manifest.version=0.3 target.sdk=0.8.0-dev target.platforms=esp32s3,esp32p4 -app.id=tactility.epubreader -app.version.name=0.11.0 -app.version.code=11 -app.name=Epub Reader -app.description=Epub and text file reader. Requires PSRAM! +id=tactility.epubreader +version.name=0.12.0 +version.code=12 +name=ePub Reader +description=ePub and text file reader. Requires PSRAM! +app.0.id=tactility.epubreader +app.0.name=ePub Reader +app.0.binary=epubreader diff --git a/Apps/EspNowBridge/CMakeLists.txt b/Apps/EspNowBridge/CMakeLists.txt index 528a4cb..9093a63 100644 --- a/Apps/EspNowBridge/CMakeLists.txt +++ b/Apps/EspNowBridge/CMakeLists.txt @@ -8,6 +8,6 @@ endif() get_filename_component(TACTILITY_SDK_PATH "$ENV{TACTILITY_SDK_PATH}" ABSOLUTE) include("${TACTILITY_SDK_PATH}/TactilitySDK.cmake") -tactility_project_pre(tactility.espnowbridge) -project(tactility.espnowbridge) -tactility_project_post(tactility.espnowbridge) +tactility_project_pre(espnowbridge) +project(espnowbridge) +tactility_project_post(espnowbridge) diff --git a/Apps/EspNowBridge/main/Source/EspNowBridge.cpp b/Apps/EspNowBridge/main/Source/EspNowBridge.cpp index bfb6345..7c04ae9 100644 --- a/Apps/EspNowBridge/main/Source/EspNowBridge.cpp +++ b/Apps/EspNowBridge/main/Source/EspNowBridge.cpp @@ -1,8 +1,8 @@ #include "EspNowBridge.h" -#include #include #include +#include #include #include #include @@ -550,11 +550,6 @@ static void startUpdateTask(Context* ctx, const std::string& filePath) { } } -// Matches Tactility's own built-in file-selection system app (Tactility/Source/app/fileselection/ -// FileSelection.cpp) - its manifest id and argv convention aren't part of any public app-module -// header (that app isn't generic app-module framework, just one particular app shipped by -// Tactility), so external apps reach it by calling app_manager_start_for_result_with_streams() -// against these directly, the same way Tactility's own built-in apps (e.g. Notes) do internally. static constexpr auto* FILE_SELECTION_APP_ID = "tactility.fileselection"; static constexpr auto* FILE_SELECTION_MODE_EXISTING = "--existing"; @@ -578,7 +573,7 @@ static void onUpdateButtonClicked(lv_event_t* /*event*/) { .event_group = ctx->eventGroup, }; uint32_t instanceId = 0; - if (app_manager_start_for_result_with_streams(FILE_SELECTION_APP_ID, ctx->appInstanceId, 1, argv, &binding, 1, &instanceId) == ERROR_NONE) { + if (app_start_for_result_with_streams(FILE_SELECTION_APP_ID, 1, argv, &binding, 1, ctx->appInstanceId, &instanceId) == ERROR_NONE) { ctx->pickFileLaunchId = instanceId; } } diff --git a/Apps/EspNowBridge/manifest.properties b/Apps/EspNowBridge/manifest.properties index 33debe6..e97b8a1 100644 --- a/Apps/EspNowBridge/manifest.properties +++ b/Apps/EspNowBridge/manifest.properties @@ -1,8 +1,11 @@ -manifest.version=0.2 +manifest.version=0.3 target.sdk=0.8.0-dev target.platforms=esp32p4 -app.id=tactility.espnowbridge -app.version.name=0.9.0 -app.version.code=9 -app.name=ESP-NOW Bridge -app.description=Companion app for updating P4 device C6 co-processor firmware to enable ESP-NOW bridge support. +id=tactility.espnowbridge +version.name=0.10.0 +version.code=10 +name=ESP-NOW Bridge +description=Companion app for updating P4 device C6 co-processor firmware to enable ESP-NOW bridge support. +app.0.id=tactility.espnowbridge +app.0.name=ESP-NOW Bridge +app.0.binary=espnowbridge diff --git a/Apps/GPIO/CMakeLists.txt b/Apps/GPIO/CMakeLists.txt index 4ae68e8..7363674 100644 --- a/Apps/GPIO/CMakeLists.txt +++ b/Apps/GPIO/CMakeLists.txt @@ -8,6 +8,6 @@ endif() get_filename_component(TACTILITY_SDK_PATH "$ENV{TACTILITY_SDK_PATH}" ABSOLUTE) include("${TACTILITY_SDK_PATH}/TactilitySDK.cmake") -tactility_project_pre(tactility.gpio) -project(tactility.gpio) -tactility_project_post(tactility.gpio) +tactility_project_pre(gpio) +project(gpio) +tactility_project_post(gpio) diff --git a/Apps/GPIO/manifest.properties b/Apps/GPIO/manifest.properties index 3cb4d4a..4bc0e27 100644 --- a/Apps/GPIO/manifest.properties +++ b/Apps/GPIO/manifest.properties @@ -1,7 +1,11 @@ -manifest.version=0.2 +manifest.version=0.3 target.sdk=0.8.0-dev target.platforms=esp32,esp32s3,esp32c6,esp32p4 -app.id=tactility.gpio -app.version.name=0.14.0 -app.version.code=14 -app.name=GPIO +id=tactility.gpio +version.name=0.15.0 +version.code=15 +name=GPIO +description=Shows the live state of all GPIO pins +app.0.id=tactility.gpio +app.0.name=GPIO +app.0.binary=gpio diff --git a/Apps/GraphicsDemo/CMakeLists.txt b/Apps/GraphicsDemo/CMakeLists.txt index 65ebd93..6e45c21 100644 --- a/Apps/GraphicsDemo/CMakeLists.txt +++ b/Apps/GraphicsDemo/CMakeLists.txt @@ -8,6 +8,6 @@ endif() get_filename_component(TACTILITY_SDK_PATH "$ENV{TACTILITY_SDK_PATH}" ABSOLUTE) include("${TACTILITY_SDK_PATH}/TactilitySDK.cmake") -tactility_project_pre(tactility.graphicsdemo) -project(tactility.graphicsdemo) -tactility_project_post(tactility.graphicsdemo) +tactility_project_pre(graphicsdemo) +project(graphicsdemo) +tactility_project_post(graphicsdemo) diff --git a/Apps/GraphicsDemo/main/Source/Main.cpp b/Apps/GraphicsDemo/main/Source/Main.cpp index 1b788b5..6df7e5c 100644 --- a/Apps/GraphicsDemo/main/Source/Main.cpp +++ b/Apps/GraphicsDemo/main/Source/Main.cpp @@ -5,7 +5,7 @@ #include #include -#include +#include #include #include @@ -24,7 +24,7 @@ constexpr auto TAG = "Main"; static void showErrorAndWait(AppInstanceId appInstanceId, const char* message) { const char* argv[] = { "Error", message, "OK" }; uint32_t dialogInstanceId = 0; - app_manager_start_for_result("AlertDialog", appInstanceId, 3, argv, &dialogInstanceId); + app_start_for_result("AlertDialog", 3, argv, appInstanceId, &dialogInstanceId); if (dialogInstanceId == 0) { return; } diff --git a/Apps/GraphicsDemo/manifest.properties b/Apps/GraphicsDemo/manifest.properties index 87db999..e50cabe 100644 --- a/Apps/GraphicsDemo/manifest.properties +++ b/Apps/GraphicsDemo/manifest.properties @@ -1,7 +1,10 @@ -manifest.version=0.2 +manifest.version=0.3 target.sdk=0.8.0-dev target.platforms=esp32,esp32s3,esp32c6,esp32p4 -app.id=tactility.graphicsdemo -app.version.name=0.12.0 -app.version.code=12 -app.name=Graphics Demo +id=tactility.graphicsdemo +version.name=0.13.0 +version.code=13 +name=Graphics Demo +app.0.id=tactility.graphicsdemo +app.0.name=Graphics Demo +app.0.binary=graphicsdemo diff --git a/Apps/HelloWorld/CMakeLists.txt b/Apps/HelloWorld/CMakeLists.txt index 70a314b..0ea34d4 100644 --- a/Apps/HelloWorld/CMakeLists.txt +++ b/Apps/HelloWorld/CMakeLists.txt @@ -8,6 +8,6 @@ endif() get_filename_component(TACTILITY_SDK_PATH "$ENV{TACTILITY_SDK_PATH}" ABSOLUTE) include("${TACTILITY_SDK_PATH}/TactilitySDK.cmake") -tactility_project_pre(tactility.helloworld) -project(tactility.helloworld) -tactility_project_post(tactility.helloworld) +tactility_project_pre(helloworld) +project(helloworld) +tactility_project_post(helloworld) diff --git a/Apps/HelloWorld/manifest.properties b/Apps/HelloWorld/manifest.properties index 396c002..c3bbc94 100644 --- a/Apps/HelloWorld/manifest.properties +++ b/Apps/HelloWorld/manifest.properties @@ -1,7 +1,10 @@ -manifest.version=0.2 +manifest.version=0.3 target.sdk=0.8.0-dev target.platforms=esp32,esp32s3,esp32c6,esp32p4,posix-x86_64 -app.id=tactility.helloworld -app.version.name=0.12.0 -app.version.code=12 -app.name=Hello World +id=tactility.helloworld +version.name=0.13.0 +version.code=13 +name=Hello World +app.0.id=tactility.helloworld +app.0.name=Hello World +app.0.binary=helloworld diff --git a/Apps/M5UnitTest/CMakeLists.txt b/Apps/M5UnitTest/CMakeLists.txt index e15563e..df95a41 100644 --- a/Apps/M5UnitTest/CMakeLists.txt +++ b/Apps/M5UnitTest/CMakeLists.txt @@ -8,6 +8,6 @@ endif() get_filename_component(TACTILITY_SDK_PATH "$ENV{TACTILITY_SDK_PATH}" ABSOLUTE) include("${TACTILITY_SDK_PATH}/TactilitySDK.cmake") -tactility_project_pre(tactility.m5unittest) -project(tactility.m5unittest) -tactility_project_post(tactility.m5unittest) +tactility_project_pre(m5unittest) +project(m5unittest) +tactility_project_post(m5unittest) diff --git a/Apps/M5UnitTest/manifest.properties b/Apps/M5UnitTest/manifest.properties index a3d606b..12d7cfb 100644 --- a/Apps/M5UnitTest/manifest.properties +++ b/Apps/M5UnitTest/manifest.properties @@ -1,7 +1,10 @@ -manifest.version=0.2 +manifest.version=0.3 target.sdk=0.8.0-dev target.platforms=esp32s3,esp32p4 -app.id=tactility.m5unittest -app.version.name=0.10.0 -app.version.code=10 -app.name=M5 Unit Test +id=tactility.m5unittest +version.name=0.11.0 +version.code=11 +name=M5 Unit Test +app.0.id=tactility.m5unittest +app.0.name=M5 Unit Test +app.0.binary=m5unittest diff --git a/Apps/Magic8Ball/CMakeLists.txt b/Apps/Magic8Ball/CMakeLists.txt index f082795..e03f850 100644 --- a/Apps/Magic8Ball/CMakeLists.txt +++ b/Apps/Magic8Ball/CMakeLists.txt @@ -8,6 +8,6 @@ endif() get_filename_component(TACTILITY_SDK_PATH "$ENV{TACTILITY_SDK_PATH}" ABSOLUTE) include("${TACTILITY_SDK_PATH}/TactilitySDK.cmake") -tactility_project_pre(tactility.magic8ball) -project(tactility.magic8ball) -tactility_project_post(tactility.magic8ball) +tactility_project_pre(magic8ball) +project(magic8ball) +tactility_project_post(magic8ball) diff --git a/Apps/Magic8Ball/manifest.properties b/Apps/Magic8Ball/manifest.properties index bfc52cd..d7ed29e 100644 --- a/Apps/Magic8Ball/manifest.properties +++ b/Apps/Magic8Ball/manifest.properties @@ -1,7 +1,10 @@ -manifest.version=0.2 +manifest.version=0.3 target.sdk=0.8.0-dev target.platforms=esp32,esp32s3,esp32c6,esp32p4 -app.id=tactility.magic8ball -app.version.name=0.11.0 -app.version.code=11 -app.name=Magic 8-Ball +id=tactility.magic8ball +version.name=0.12.0 +version.code=12 +name=Magic 8-Ball +app.0.id=tactility.magic8ball +app.0.name=Magic 8-Ball +app.0.binary=magic8ball diff --git a/Apps/MediaKeys/CMakeLists.txt b/Apps/MediaKeys/CMakeLists.txt index 9efe14a..e0990f0 100644 --- a/Apps/MediaKeys/CMakeLists.txt +++ b/Apps/MediaKeys/CMakeLists.txt @@ -8,6 +8,6 @@ endif() get_filename_component(TACTILITY_SDK_PATH "$ENV{TACTILITY_SDK_PATH}" ABSOLUTE) include("${TACTILITY_SDK_PATH}/TactilitySDK.cmake") -tactility_project_pre(tactility.mediakeys) -project(tactility.mediakeys) -tactility_project_post(tactility.mediakeys) +tactility_project_pre(mediakeys) +project(mediakeys) +tactility_project_post(mediakeys) diff --git a/Apps/MediaKeys/manifest.properties b/Apps/MediaKeys/manifest.properties index cd6f956..08a00ad 100644 --- a/Apps/MediaKeys/manifest.properties +++ b/Apps/MediaKeys/manifest.properties @@ -1,8 +1,11 @@ -manifest.version=0.2 +manifest.version=0.3 target.sdk=0.8.0-dev target.platforms=esp32s3,esp32p4 -app.id=tactility.mediakeys -app.version.name=0.12.0 -app.version.code=12 -app.name=Media Keys -app.description=Bluetooth media keys. Touch or Physical Keyboard control\nB - previous, P - play/pause, N - next, M - mute, D - volume down, U - volume up.\nQ or ESC to exit focus. +id=tactility.mediakeys +version.name=0.13.0 +version.code=13 +name=Media Keys +description=Bluetooth media keys. Touch or Physical Keyboard control\nB - previous, P - play/pause, N - next, M - mute, D - volume down, U - volume up.\nQ or ESC to exit focus. +app.0.id=tactility.mediakeys +app.0.name=Media Keys +app.0.binary=mediakeys diff --git a/Apps/MystifyDemo/CMakeLists.txt b/Apps/MystifyDemo/CMakeLists.txt index ea4379a..c558e44 100644 --- a/Apps/MystifyDemo/CMakeLists.txt +++ b/Apps/MystifyDemo/CMakeLists.txt @@ -8,6 +8,6 @@ endif() get_filename_component(TACTILITY_SDK_PATH "$ENV{TACTILITY_SDK_PATH}" ABSOLUTE) include("${TACTILITY_SDK_PATH}/TactilitySDK.cmake") -tactility_project_pre(tactility.mystifydemo) -project(tactility.mystifydemo) -tactility_project_post(tactility.mystifydemo) +tactility_project_pre(mystifydemo) +project(mystifydemo) +tactility_project_post(mystifydemo) diff --git a/Apps/MystifyDemo/main/Source/Main.cpp b/Apps/MystifyDemo/main/Source/Main.cpp index 1b788b5..e9c1a76 100644 --- a/Apps/MystifyDemo/main/Source/Main.cpp +++ b/Apps/MystifyDemo/main/Source/Main.cpp @@ -5,8 +5,8 @@ #include #include -#include #include +#include #include #include @@ -24,7 +24,7 @@ constexpr auto TAG = "Main"; static void showErrorAndWait(AppInstanceId appInstanceId, const char* message) { const char* argv[] = { "Error", message, "OK" }; uint32_t dialogInstanceId = 0; - app_manager_start_for_result("AlertDialog", appInstanceId, 3, argv, &dialogInstanceId); + app_start_for_result("AlertDialog", 3, argv, appInstanceId, &dialogInstanceId); if (dialogInstanceId == 0) { return; } diff --git a/Apps/MystifyDemo/manifest.properties b/Apps/MystifyDemo/manifest.properties index 7e36cf5..937b4dd 100644 --- a/Apps/MystifyDemo/manifest.properties +++ b/Apps/MystifyDemo/manifest.properties @@ -1,7 +1,11 @@ -manifest.version=0.2 +manifest.version=0.3 target.sdk=0.8.0-dev target.platforms=esp32,esp32s3,esp32c6,esp32p4 -app.id=tactility.mystifydemo -app.version.name=0.13.0 -app.version.code=13 -app.name=Mystify Demo +id=tactility.mystifydemo +version.name=0.14.0 +version.code=14 +name=Mystify Demo +description=A graphics demo that is inspired by a Windows 95 screensaver. +app.0.id=tactility.mystifydemo +app.0.name=Mystify Demo +app.0.binary=mystifydemo diff --git a/Apps/SerialConsole/CMakeLists.txt b/Apps/SerialConsole/CMakeLists.txt index c01c4f2..e9a2a97 100644 --- a/Apps/SerialConsole/CMakeLists.txt +++ b/Apps/SerialConsole/CMakeLists.txt @@ -8,6 +8,6 @@ endif() get_filename_component(TACTILITY_SDK_PATH "$ENV{TACTILITY_SDK_PATH}" ABSOLUTE) include("${TACTILITY_SDK_PATH}/TactilitySDK.cmake") -tactility_project_pre(tactility.serialconsole) -project(tactility.serialconsole) -tactility_project_post(tactility.serialconsole) +tactility_project_pre(serialconsole) +project(serialconsole) +tactility_project_post(serialconsole) diff --git a/Apps/SerialConsole/main/Source/ConnectView.cpp b/Apps/SerialConsole/main/Source/ConnectView.cpp index b1c083d..994a61e 100644 --- a/Apps/SerialConsole/main/Source/ConnectView.cpp +++ b/Apps/SerialConsole/main/Source/ConnectView.cpp @@ -5,8 +5,8 @@ #include #include -#include #include +#include #include #include #include @@ -53,7 +53,7 @@ lv_obj_t* createRowWrapper(lv_obj_t* parent) { void showError(Context* app, const char* message) { const char* argv[] = { "Error", message, "OK" }; uint32_t dialogInstanceId = 0; - app_manager_start_for_result("AlertDialog", app->appInstanceId, 3, argv, &dialogInstanceId); + app_start_for_result("AlertDialog", 3, argv, app->appInstanceId, &dialogInstanceId); } void onConnect(Context* app) { diff --git a/Apps/SerialConsole/manifest.properties b/Apps/SerialConsole/manifest.properties index cfbdb43..d5c1c67 100644 --- a/Apps/SerialConsole/manifest.properties +++ b/Apps/SerialConsole/manifest.properties @@ -1,7 +1,10 @@ -manifest.version=0.2 +manifest.version=0.3 target.sdk=0.8.0-dev target.platforms=esp32,esp32s3,esp32c6,esp32p4 -app.id=tactility.serialconsole -app.version.name=0.14.0 -app.version.code=14 -app.name=Serial Console +id=tactility.serialconsole +version.name=0.15.0 +version.code=15 +name=Serial Console +app.0.id=tactility.serialconsole +app.0.name=Serial Console +app.0.binary=serialconsole diff --git a/Apps/Snake/CMakeLists.txt b/Apps/Snake/CMakeLists.txt index 952cc55..159903f 100644 --- a/Apps/Snake/CMakeLists.txt +++ b/Apps/Snake/CMakeLists.txt @@ -8,6 +8,6 @@ endif() get_filename_component(TACTILITY_SDK_PATH "$ENV{TACTILITY_SDK_PATH}" ABSOLUTE) include("${TACTILITY_SDK_PATH}/TactilitySDK.cmake") -tactility_project_pre(tactility.snake) -project(tactility.snake) -tactility_project_post(tactility.snake) +tactility_project_pre(snake) +project(snake) +tactility_project_post(snake) diff --git a/Apps/Snake/main/Source/Snake.cpp b/Apps/Snake/main/Source/Snake.cpp index 9be7aaf..13cf5f9 100644 --- a/Apps/Snake/main/Source/Snake.cpp +++ b/Apps/Snake/main/Source/Snake.cpp @@ -9,8 +9,8 @@ #include #include -#include #include +#include #include #include @@ -128,7 +128,7 @@ void snakeEventCb(lv_event_t* e) { score, length, getHighScore(ctx, ctx->currentDifficulty)); } const char* argv[] = { alertTitle, message, "OK" }; - app_manager_start_for_result("AlertDialog", ctx->appInstanceId, 3, argv, &ctx->gameOverDialogId); + app_start_for_result("AlertDialog", 3, argv, ctx->appInstanceId, &ctx->gameOverDialogId); } else { // Update score display lv_label_set_text_fmt(ctx->scoreLabel, "SCORE: %u", snake_get_score(ctx->gameObject)); @@ -264,7 +264,7 @@ void snakeTeardown(Context* ctx) { void snakeShowSelectionDialog(Context* ctx) { const char* argv[] = { "Snake", "How to Play", "Easy", "Medium", "Hard", "Hell" }; - app_manager_start_for_result("tactility.selectiondialog", ctx->appInstanceId, 6, argv, &ctx->selectionDialogId); + app_start_for_result("tactility.selectiondialog", 6, argv, ctx->appInstanceId, &ctx->selectionDialogId); } void snakeShowHelpDialog(Context* ctx) { @@ -275,7 +275,7 @@ void snakeShowHelpDialog(Context* ctx) { "Don't hit yourself!", "OK", }; - app_manager_start_for_result("tactility.alertdialog", ctx->appInstanceId, 3, argv, &ctx->helpDialogId); + app_start_for_result("tactility.alertdialog", 3, argv, ctx->appInstanceId, &ctx->helpDialogId); } void snakeClearGame(Context* ctx) { diff --git a/Apps/Snake/manifest.properties b/Apps/Snake/manifest.properties index 75bb565..326903e 100644 --- a/Apps/Snake/manifest.properties +++ b/Apps/Snake/manifest.properties @@ -1,8 +1,11 @@ -manifest.version=0.2 +manifest.version=0.3 target.sdk=0.8.0-dev target.platforms=esp32,esp32s3,esp32c6,esp32p4 -app.id=tactility.snake -app.version.name=0.15.0 -app.version.code=15 -app.name=Snake -app.description=Classic Snake game +id=tactility.snake +version.name=0.16.0 +version.code=16 +name=Snake +description=Classic Snake game +app.0.id=tactility.snake +app.0.name=Snake +app.0.binary=snake diff --git a/Apps/TamaTac/CMakeLists.txt b/Apps/TamaTac/CMakeLists.txt index 1ec190b..e370e56 100644 --- a/Apps/TamaTac/CMakeLists.txt +++ b/Apps/TamaTac/CMakeLists.txt @@ -8,6 +8,6 @@ endif() get_filename_component(TACTILITY_SDK_PATH "$ENV{TACTILITY_SDK_PATH}" ABSOLUTE) include("${TACTILITY_SDK_PATH}/TactilitySDK.cmake") -tactility_project_pre(tactility.tamatac) -project(tactility.tamatac) -tactility_project_post(tactility.tamatac) +tactility_project_pre(tamatac) +project(tamatac) +tactility_project_post(tamatac) diff --git a/Apps/TamaTac/main/Source/TamaTac.cpp b/Apps/TamaTac/main/Source/TamaTac.cpp index a9f26b5..d80fcb1 100644 --- a/Apps/TamaTac/main/Source/TamaTac.cpp +++ b/Apps/TamaTac/main/Source/TamaTac.cpp @@ -7,7 +7,7 @@ #include "SpriteData.h" #include #include -#include +#include #include #include #include @@ -113,7 +113,7 @@ void onResetClicked([[maybe_unused]] lv_event_t* e) { "Reset", "Cancel", }; - app_manager_start_for_result("AlertDialog", ctx->appInstanceId, 4, argv, &ctx->resetDialogId); + app_start_for_result("AlertDialog", 4, argv, ctx->appInstanceId, &ctx->resetDialogId); } void onTimerUpdate(TimerHandle_t timer) { diff --git a/Apps/TamaTac/manifest.properties b/Apps/TamaTac/manifest.properties index 46684df..c79ace9 100644 --- a/Apps/TamaTac/manifest.properties +++ b/Apps/TamaTac/manifest.properties @@ -1,8 +1,11 @@ -manifest.version=0.2 +manifest.version=0.3 target.sdk=0.8.0-dev target.platforms=esp32,esp32s3,esp32c6,esp32p4 -app.id=tactility.tamatac -app.version.name=0.10.0 -app.version.code=10 -app.name=TamaTac -app.description=Virtual pet inspired by Tamagotchi. Only runs on devices with PSRAM. +id=tactility.tamatac +version.name=0.11.0 +version.code=11 +name=TamaTac +description=Virtual pet inspired by Tamagotchi. Only runs on devices with PSRAM. +app.0.id=tactility.tamatac +app.0.name=TamaTac +app.0.binary=tamatac diff --git a/Apps/TodoList/CMakeLists.txt b/Apps/TodoList/CMakeLists.txt index f92028f..b22457f 100644 --- a/Apps/TodoList/CMakeLists.txt +++ b/Apps/TodoList/CMakeLists.txt @@ -8,6 +8,6 @@ endif() get_filename_component(TACTILITY_SDK_PATH "$ENV{TACTILITY_SDK_PATH}" ABSOLUTE) include("${TACTILITY_SDK_PATH}/TactilitySDK.cmake") -tactility_project_pre(tactility.todolist) -project(tactility.todolist) -tactility_project_post(tactility.todolist) +tactility_project_pre(todolist) +project(todolist) +tactility_project_post(todolist) diff --git a/Apps/TodoList/manifest.properties b/Apps/TodoList/manifest.properties index 5a7f803..0469ba6 100644 --- a/Apps/TodoList/manifest.properties +++ b/Apps/TodoList/manifest.properties @@ -1,8 +1,11 @@ -manifest.version=0.2 +manifest.version=0.3 target.sdk=0.8.0-dev target.platforms=esp32,esp32s3,esp32c6,esp32p4 -app.id=tactility.todolist -app.version.name=0.13.0 -app.version.code=13 -app.name=Todo List -app.description=Simple task list manager +id=tactility.todolist +version.name=0.14.0 +version.code=14 +name=Todo List +description=Simple task list manager +app.0.id=tactility.todolist +app.0.name=Todo List +app.0.binary=todolist diff --git a/Apps/TwoEleven/CMakeLists.txt b/Apps/TwoEleven/CMakeLists.txt index 5808671..71a3ef0 100644 --- a/Apps/TwoEleven/CMakeLists.txt +++ b/Apps/TwoEleven/CMakeLists.txt @@ -8,6 +8,6 @@ endif() get_filename_component(TACTILITY_SDK_PATH "$ENV{TACTILITY_SDK_PATH}" ABSOLUTE) include("${TACTILITY_SDK_PATH}/TactilitySDK.cmake") -tactility_project_pre(tactility.twoeleven) -project(tactility.twoeleven) -tactility_project_post(tactility.twoeleven) +tactility_project_pre(twoeleven) +project(twoeleven) +tactility_project_post(twoeleven) diff --git a/Apps/TwoEleven/main/Source/TwoEleven.cpp b/Apps/TwoEleven/main/Source/TwoEleven.cpp index 8210bdb..11b3e72 100644 --- a/Apps/TwoEleven/main/Source/TwoEleven.cpp +++ b/Apps/TwoEleven/main/Source/TwoEleven.cpp @@ -8,8 +8,8 @@ #include #include #include -#include #include +#include #include #include #include @@ -117,7 +117,7 @@ void twoElevenEventCb(lv_event_t* e) { snprintf(message, sizeof(message), "YOU WIN!\n\nSCORE: %" PRId32 "\nBEST: %" PRId32, score, getHighScore(ctx, ctx->currentGridSize)); } const char* argv[] = { title, message, "OK" }; - app_manager_start_for_result("tactility.alertdialog", ctx->appInstanceId, 3, argv, &ctx->gameOverDialogId); + app_start_for_result("tactility.alertdialog", 3, argv, ctx->appInstanceId, &ctx->gameOverDialogId); } else if (ctx->gameOverDialogId == 0 && twoeleven_get_status(ctx->gameObject)) { int32_t prevHighScore = getHighScore(ctx, ctx->currentGridSize); bool isNewHighScore = score > prevHighScore; @@ -137,7 +137,7 @@ void twoElevenEventCb(lv_event_t* e) { snprintf(message, sizeof(message), "GAME OVER!\n\nSCORE: %" PRId32 "\nBEST: %" PRId32, score, getHighScore(ctx, ctx->currentGridSize)); } const char* argv[] = { title, message, "OK" }; - app_manager_start_for_result("tactility.alertdialog", ctx->appInstanceId, 3, argv, &ctx->gameOverDialogId); + app_start_for_result("tactility.alertdialog", 3, argv, ctx->appInstanceId, &ctx->gameOverDialogId); } else { // Update score display lv_label_set_text_fmt(ctx->scoreLabel, "SCORE: %" PRId32, score); @@ -269,7 +269,7 @@ void twoElevenTeardown(Context* ctx) { void twoElevenShowSelectionDialog(Context* ctx) { const char* argv[] = { "2048", "How to Play", "3x3", "4x4", "5x5", "6x6" }; - app_manager_start_for_result("tactility.selectiondialog", ctx->appInstanceId, 6, argv, &ctx->selectionDialogId); + app_start_for_result("tactility.selectiondialog", 6, argv, ctx->appInstanceId, &ctx->selectionDialogId); } void twoElevenShowHelpDialog(Context* ctx) { @@ -280,7 +280,7 @@ void twoElevenShowHelpDialog(Context* ctx) { "Reach 2048 to win!", "OK", }; - app_manager_start_for_result("tactility.alertdialog", ctx->appInstanceId, 3, argv, &ctx->helpDialogId); + app_start_for_result("tactility.alertdialog", 3, argv, ctx->appInstanceId, &ctx->helpDialogId); } void twoElevenClearGame(Context* ctx) { diff --git a/Apps/TwoEleven/manifest.properties b/Apps/TwoEleven/manifest.properties index 4a7c7a1..5ffc099 100644 --- a/Apps/TwoEleven/manifest.properties +++ b/Apps/TwoEleven/manifest.properties @@ -1,8 +1,11 @@ -manifest.version=0.2 +manifest.version=0.3 target.sdk=0.8.0-dev target.platforms=esp32,esp32s3,esp32c6,esp32p4 -app.id=tactility.twoeleven -app.version.name=0.14.0 -app.version.code=14 -app.name=2048 -app.description=A fun, customizable 2048 sliding tile game for tactility!\nSlide tiles to combine numbers and reach 2048.\nChoose grid sizes: 3x3 (easy), 4x4 (classic), 5x5, or 6x6 (expert). +id=tactility.twoeleven +version.name=0.15.0 +version.code=15 +name=2048 +description=A fun, customizable 2048 sliding tile game for tactility!\nSlide tiles to combine numbers and reach 2048.\nChoose grid sizes: 3x3 (easy), 4x4 (classic), 5x5, or 6x6 (expert). +app.0.id=tactility.twoeleven +app.0.name=2048 +app.0.binary=twoeleven diff --git a/Tools/bump-versions.py b/Tools/bump-versions.py index 52c2905..7b09291 100755 --- a/Tools/bump-versions.py +++ b/Tools/bump-versions.py @@ -14,8 +14,8 @@ APPS_DIR = Path(__file__).resolve().parent.parent / "Apps" MANIFEST = "manifest.properties" -VERSION_NAME_KEY = "app.version.name" -VERSION_CODE_KEY = "app.version.code" +VERSION_NAME_KEY = "version.name" +VERSION_CODE_KEY = "version.code" def parse_version(name: str) -> list[int]: diff --git a/release.py b/release.py index 4456f5f..257ad50 100644 --- a/release.py +++ b/release.py @@ -71,8 +71,8 @@ def get_manifest(appPath): return None def get_versioned_file_name(manifest): - app_id = manifest["app.id"] - version_code = manifest["app.version.code"] + app_id = manifest["id"] + version_code = manifest["version.code"] return f"{app_id}-{version_code}.app" def get_os_version(manifest): @@ -98,22 +98,22 @@ def manifest_config_to_flat_json(manifest): """Convert a flat (V2) manifest dict into a flat JSON-like dict. Expected keys: - app.id -> appId - app.version.name -> appVersionName - app.version.code -> appVersionCode (int) - app.name -> appName - app.description -> appDescription (optional; default "") + id -> appId + version.name -> appVersionName + version.code -> appVersionCode (int) + name -> appName + description -> appDescription (optional; default "") target.sdk -> targetSdk target.platforms -> targetPlatforms (comma-separated list) Unknown/missing values fall back to sensible defaults per requirements. """ # Map values - app_id = manifest.get("app.id", "") - app_version_name = manifest.get("app.version.name", "") - app_version_code_raw = manifest.get("app.version.code", "0") - app_name = manifest.get("app.name", "") - app_description = manifest.get("app.description", "") or "" + app_id = manifest.get("id", "") + app_version_name = manifest.get("version.name", "") + app_version_code_raw = manifest.get("version.code", "0") + app_name = manifest.get("name", "") + app_description = manifest.get("description", "") or "" # Coerce version code to int safely try: diff --git a/tactility.py b/tactility.py index 91d6c2f..ef8c5f5 100644 --- a/tactility.py +++ b/tactility.py @@ -12,7 +12,7 @@ from urllib.parse import urlparse ttbuild_path = ".tactility" -ttbuild_version = "5.0.1" +ttbuild_version = "6.0.0" ttbuild_cdn = "https://cdn.tactilityproject.org" ttbuild_sdk_json_validity = 3600 # seconds ttport = 6666 @@ -21,7 +21,7 @@ local_base_path = None http_timeout_seconds = 10 # App install uploads the whole package over HTTP and the device only responds once it's -# fully received, extracted and registered - large packages (e.g. bundled fonts/assets) can +# fully received, extracted and registered; large packages (e.g. bundled fonts/assets) can # easily take well over http_timeout_seconds on a slow SD card, so give it a lot more room. install_timeout_seconds = 120 @@ -33,30 +33,55 @@ shell_color_reset = "\033[m" def print_help(): - print("Usage: python tactility.py [app_path] [action] [options]") + print("Usage: python tactility.py [action] [options]") print("") print("Actions:") - print(" build [platform] Build the app. Optionally specify a platform.") + print("") + print(" build Build the app. Optionally specify a platform.") print(" Supported platforms are lower case. Example: esp32s3") print(" Supported platforms are read from manifest.properties") + print(" Parameters:") + print(" (optional) --architecture [input], -a [input]") + print("") print(" clean Clean the build folders") + print("") print(" clearcache Clear the SDK cache") + print("") print(" updateself Update this tool") - print(" run [ip] Run the application") - print(" install [ip] Install the application") - print(" uninstall [ip] Uninstall the application") - print(" bir [ip] [platform] Build, install then run. Optionally specify a platform.") - print(" brrr [ip] [platform] Functionally the same as \"bir\", but \"app goes brrr\" meme variant.") + print("") + print(" run Run the application") + print(" Parameters:") + print(" (required) --host, -h [input]") + print("") + print(" install Install the application") + print(" Parameters:") + print(" (required) --host, -h [input]") + print("") + print(" uninstall Uninstall the application") + print(" Parameters:") + print(" (required) --host, -h [input]") + print("") + print(" bir Build, install then run.") + print(" Parameters:") + print(" (required) --host, -h [input]") + print(" (optional) --architecture, -a [input]") + print("") + print(" brrr Functionally the same as \"bir\", but \"app goes brrr\" meme variant.") + print(" Parameters:") + print(" (required) --host, -h [input]") + print(" (optional) --architecture, -a [input]") print("") print("Options:") + print(" -p, --path [input] Path to the app directory (defaults to current directory)") print(" --help Show this commandline info") print(" --local-sdk Use SDK specified by environment variable TACTILITY_SDK_PATH with platform subfolders matching target platforms.") print(" --skip-build Run everything except the idf.py/CMake commands") print(" --verbose Show extra console output") print("") print("Examples:") - print(" python tactility.py Apps/Snake build esp32s3 --verbose") - print(" python tactility.py Apps/Snake bir 192.168.1.50 esp32s3") + print(" python tactility.py build") + print(" python tactility.py build --path Apps/Snake --architecture esp32s3 --verbose") + print(" python tactility.py bir --path Apps/Snake --host 192.168.1.50 --architecture esp32s3") # region Core @@ -105,8 +130,8 @@ def exit_with_error(message): print_error(message) sys.exit(1) -def get_url(ip, path): - return f"http://{ip}:{ttport}{path}" +def get_url(host, path): + return f"http://{host}:{ttport}{path}" def read_properties_file(path): properties = {} @@ -123,6 +148,126 @@ def read_properties_file(path): #endregion Core +#region Versioning + +class SemanticVersion: + def __init__(self, major, minor, patch, tag=None): + self.major = major + self.minor = minor + self.patch = patch + self.tag = tag + + @staticmethod + def parse(version_string): + match = re.match(r"^(\d+)\.(\d+)\.(\d+)(?:-(.+))?$", version_string) + if match is None: + exit_with_error(f"Invalid version format: {version_string}") + major, minor, patch, tag = match.groups() + return SemanticVersion(int(major), int(minor), int(patch), tag) + + def _numeric(self): + return (self.major, self.minor, self.patch) + + def __eq__(self, other): + return self._numeric() == other._numeric() and self.tag == other.tag + + def __lt__(self, other): + if self._numeric() != other._numeric(): + return self._numeric() < other._numeric() + # Same major.minor.patch: an untagged version outranks any tagged (pre-release) one. + if self.tag == other.tag: + return False + if self.tag is None: + return False + if other.tag is None: + return True + return self.tag < other.tag + + def __str__(self): + return f"{self.major}.{self.minor}.{self.patch}" + (f"-{self.tag}" if self.tag else "") + + # Ignores tags: a "-dev"/"-rc1"-tagged pre-release build of a version is still that version, + # not an older one, unlike __lt__'s full ordering (which ranks pre-releases below releases). + def is_older_release_of(self, other): + return self._numeric() < other._numeric() + +# Append a new entry whenever the tool drops support for older SDKs. +# The last entry is the currently effective minimum. +SDK_COMPATIBILITY_LEDGER = [ + SemanticVersion.parse("0.8.0"), +] + +def minimum_supported_sdk_version(): + return SDK_COMPATIBILITY_LEDGER[-1] + +def validate_sdk_compatibility(target_sdk_version): + minimum = minimum_supported_sdk_version() + if SemanticVersion.parse(target_sdk_version).is_older_release_of(minimum): + exit_with_error( + f"This tool requires SDK version {minimum} or newer " + f"(manifest.properties requests {target_sdk_version})" + ) + +#endregion Versioning + +#region Argument parsing + +# canonical name -> (short flag or None, long flag, takes_value) +ARG_SPECS = { + "path": ("-p", "--path", True), + "host": ("-h", "--host", True), + "platform": ("-a", "--architecture", True), + "verbose": (None, "--verbose", False), + "skip-build": (None, "--skip-build", False), + "local-sdk": (None, "--local-sdk", False), + "help": (None, "--help", False), +} + +class Arguments: + def __init__(self, values): + self.values = values + + def get(self, key, default=None): + return self.values.get(key, default) + + def has(self, key): + return key in self.values + +class ParsedCommand: + def __init__(self, action, args): + self.action = action + self.arguments = args + +def parse_command_line(argv): + token_to_spec = {} + for canonical, (short, long, takes_value) in ARG_SPECS.items(): + if short is not None: + token_to_spec[short] = (canonical, takes_value) + token_to_spec[long] = (canonical, takes_value) + + action = None + args = {} + index = 0 + while index < len(argv): + token = argv[index] + if token in token_to_spec: + canonical, takes_value = token_to_spec[token] + if takes_value: + index += 1 + if index >= len(argv): + exit_with_error(f"Missing value for {token}") + args[canonical] = argv[index] + else: + args[canonical] = "true" + elif action is None: + action = token + else: + exit_with_error(f"Unexpected argument: {token}") + index += 1 + return ParsedCommand(action, Arguments(args)) + +#endregion Argument parsing + #region SDK helpers def read_sdk_json(): @@ -232,7 +377,7 @@ def validate_self(sdk_json): exit_with_error("Server returned invalid SDK data format (toolDownloadUrl not found)") tool_version = sdk_json["toolVersion"] tool_compatibility = sdk_json["toolCompatibility"] - if tool_version != ttbuild_version: + if SemanticVersion.parse(ttbuild_version) < SemanticVersion.parse(tool_version): print_warning(f"New version available: {tool_version} (currently using {ttbuild_version})") print_warning(f"Run 'tactility.py updateself' to update.") if re.search(tool_compatibility, ttbuild_version) is None: @@ -247,10 +392,70 @@ def validate_self(sdk_json): def read_manifest(): return read_properties_file("manifest.properties") +def is_v3_manifest(manifest): + return manifest.get("manifest.version") == "0.3" + +# The package identifier: a v2 manifest has no package/app distinction, so its single "app.id" +# doubles as both; a v3 manifest's package id is the bare "id" key, distinct from any of its +# (possibly several) "app.N.id" app ids. +def get_package_id(manifest): + return manifest["id"] if is_v3_manifest(manifest) else manifest["app.id"] + +# The id of the app "run"/"install" operate on. A v2 manifest has exactly one; a v3 manifest may +# declare several, so this picks the first ("app.0.id") as the one a single-app dev workflow means. +def get_primary_app_id(manifest): + return manifest["app.0.id"] if is_v3_manifest(manifest) else manifest["app.id"] + def validate_manifest(manifest): - for key in ("manifest.version", "target.sdk", "target.platforms", "app.id", "app.version.name", "app.version.code", "app.name"): + for key in ("manifest.version", "target.sdk", "target.platforms"): if key not in manifest: exit_with_error(f"Invalid manifest format: {key} not found") + if manifest["manifest.version"] not in ("0.2", "0.3"): + exit_with_error(f"Unsupported manifest.version: {manifest['manifest.version']}") + if is_v3_manifest(manifest): + for key in ("id", "version.name", "version.code"): + if key not in manifest: + exit_with_error(f"Invalid manifest format: {key} not found") + if "app.0.id" not in manifest: + exit_with_error("Invalid manifest format: app.0.id not found") + index = 0 + while f"app.{index}.id" in manifest: + for suffix in ("name", "binary"): + key = f"app.{index}.{suffix}" + if key not in manifest: + exit_with_error(f"Invalid manifest format: {key} not found") + index += 1 + else: + for key in ("app.id", "app.version.name", "app.version.code", "app.name"): + if key not in manifest: + exit_with_error(f"Invalid manifest format: {key} not found") + validate_sdk_compatibility(manifest["target.sdk"]) + +# Maps each binary this app builds to the directory its own CMake project lives in. A v2 +# manifest always describes exactly one app, built at the app's own root (see +# package_intermediate_binaries() for its fixed "app.{elf,so}" package filename, matching +# package_manifest_parse_v2()'s single implicit binary). A v3 manifest declaring a single +# "app.0.*" block is the same case: built at the app's own root. Only when a v3 manifest +# declares more than one "app.N.*" block (0-indexed, contiguous - the first missing "app.N.id" +# ends the list, mirroring package_manifest_parse_v3()) does each get its own subdirectory, +# named after its own "app.N.binary" (the filename it installs as - see +# package_manifest_parse_v3()'s doc). +def get_binary_dirs(manifest): + if is_v3_manifest(manifest): + binaries = [] + index = 0 + while f"app.{index}.id" in manifest: + binary = manifest[f"app.{index}.binary"] + binaries.append((binary, binary)) + index += 1 + if len(binaries) == 1: + return [(binaries[0][0], ".")] + for binary, directory in binaries: + if not os.path.isdir(directory): + exit_with_error(f"Binary directory not found for '{binary}': {directory}") + return binaries + else: + return [(manifest["app.id"], ".")] def is_valid_manifest_platform(manifest, platform): manifest_platforms = manifest["target.platforms"].split(",") @@ -345,7 +550,7 @@ def sdk_download_all(version, platforms): def cmakelists_version_marker(): return f"# tactility-cmakelists-version: {CMAKELISTS_VERSION}" -def ensure_cmakelists_up_to_date(manifest): +def ensure_cmakelists_up_to_date(project_id): marker = cmakelists_version_marker() if os.path.exists("CMakeLists.txt"): with open("CMakeLists.txt", "r") as file: @@ -353,7 +558,7 @@ def ensure_cmakelists_up_to_date(manifest): if first_line == marker: return print(f"Updating CMakeLists.txt to {marker}") - content = CMAKELISTS_TEMPLATE % {"version": CMAKELISTS_VERSION, "app_id": manifest["app.id"]} + content = CMAKELISTS_TEMPLATE % {"version": CMAKELISTS_VERSION, "app_id": project_id} with open("CMakeLists.txt", "w") as file: file.write(content) @@ -532,43 +737,58 @@ def package_intermediate_manifest(target_path): shutil.copy("manifest.properties", os.path.join(target_path, "manifest.properties")) return True -def package_intermediate_binaries(target_path, platforms): - elf_dir = os.path.join(target_path, "elf") - os.makedirs(elf_dir, exist_ok=True) - for platform in platforms: - elf_path = find_elf_file(platform) - if elf_path is None: - print_error(f"ELF file not found for {platform}") - return False - # app-posix-module's loader resolves an installed app to "elf/posix-.so", matching - # its own compile-time architecture, not "*.elf". - extension = ".so" if platform.startswith("posix") else ".elf" - shutil.copy(elf_path, os.path.join(elf_dir, f"{platform}{extension}")) +def get_artifact_extension(platform): + # POSIX apps are dlopen()ed shared objects (app-posix-module), not idf.py/elf_loader + # relocatable images, so they land as a plain ".so" instead of ".elf". + return ".so" if platform.startswith("posix") else ".elf" + +def package_intermediate_binaries(target_path, platforms, manifest): + # Each binary is built in its own directory (get_binary_dirs()), with its own + # build/cmake-build-{platform} tree; chdir into it so find_elf_file()/get_cmake_path() + # (both CWD-relative) resolve against the right one, then restore CWD for the next binary. + original_cwd = os.getcwd() + for binary, directory in get_binary_dirs(manifest): + os.chdir(directory) + try: + for platform in platforms: + elf_path = find_elf_file(platform) + if elf_path is None: + print_error(f"ELF file not found for '{binary}' on {platform}") + return False + # v2's single app always installs at the fixed path bin/{platform}/app.{elf,so} + # (package_manifest_parse_v2()); v3 apps install under their own declared + # app.N.binary name (package_manifest_parse_v3()). + artifact_name = binary if is_v3_manifest(manifest) else "app" + platform_dir = os.path.join(target_path, "bin", platform) + os.makedirs(platform_dir, exist_ok=True) + shutil.copy(elf_path, os.path.join(platform_dir, f"{artifact_name}{get_artifact_extension(platform)}")) + finally: + os.chdir(original_cwd) return True def package_intermediate_assets(target_path): if os.path.isdir("assets"): shutil.copytree("assets", os.path.join(target_path, "assets"), dirs_exist_ok=True) -def package_intermediate(platforms): - target_path = os.path.join("build", "package-intermediate") +def package_intermediate(platforms, manifest): + target_path = os.path.abspath(os.path.join("build", "package-intermediate")) if os.path.isdir(target_path): shutil.rmtree(target_path) os.makedirs(target_path, exist_ok=True) if not package_intermediate_manifest(target_path): return False - if not package_intermediate_binaries(target_path, platforms): + if not package_intermediate_binaries(target_path, platforms, manifest): return False package_intermediate_assets(target_path) return True def package_name(manifest): - return os.path.join("build", f"{manifest['app.id']}.app") + return os.path.join("build", f"{get_package_id(manifest)}.app") def package_all(manifest, platforms): status = f"Building package with {platforms}" print_status_busy(status) - if not package_intermediate(platforms): + if not package_intermediate(platforms, manifest): print_status_error("Building package failed: missing inputs") return False # Create build/something.app @@ -588,8 +808,9 @@ def setup_environment(): global ttbuild_path os.makedirs(ttbuild_path, exist_ok=True) -def build_action(manifest, platform_arg, skip_build): - ensure_cmakelists_up_to_date(manifest) +def build_action(manifest, arguments): + platform_arg = arguments.get("platform") + skip_build = arguments.has("skip-build") platforms_to_build = get_manifest_target_platforms(manifest, platform_arg) # Environment validation validate_environment(platforms_to_build) @@ -598,10 +819,10 @@ def build_action(manifest, platform_arg, skip_build): global local_base_path local_base_path = os.environ.get("TACTILITY_SDK_PATH") validate_local_sdks(platforms_to_build, manifest["target.sdk"]) - + if should_fetch_sdkconfig_files(platforms_to_build): fetch_sdkconfig_files(platforms_to_build) - + if not use_local_sdk: sdk_json = read_sdk_json() validate_self(sdk_json) @@ -610,19 +831,49 @@ def build_action(manifest, platform_arg, skip_build): if not use_local_sdk: if not sdk_download_all(sdk_version, platforms_to_build): exit_with_error("Failed to download one or more SDKs") - if not build_all(sdk_version, platforms_to_build, skip_build): # Environment validation - return False + + # A multi-binary app builds each of its binaries in its own subdirectory as an independent + # CMake project; a single-binary app has exactly one binary, built at the app root + # ("." from get_binary_dirs()), matching the tool's original single-binary behavior exactly. + original_cwd = os.getcwd() + for binary, directory in get_binary_dirs(manifest): + os.chdir(directory) + try: + ensure_cmakelists_up_to_date(binary if directory == "." else f"{get_package_id(manifest)}.{binary}") + if not build_all(sdk_version, platforms_to_build, skip_build): + return False + finally: + os.chdir(original_cwd) + if not skip_build: if not package_all(manifest, platforms_to_build): return False return True -def clean_action(): +def clean_action(manifest): + cleaned_any = False + # The app root always has its own build/ (package-intermediate + the final .app/.elf, + # regardless of binary count). A multi-binary app additionally has one build/ per binary + # subdirectory (get_binary_dirs()), which "." (the root, handled above) doesn't repeat. if os.path.exists("build"): print_status_busy("Removing build/") shutil.rmtree("build") print_status_success("Removed build/") - else: + cleaned_any = True + original_cwd = os.getcwd() + for _, directory in get_binary_dirs(manifest): + if directory == ".": + continue + os.chdir(directory) + try: + if os.path.exists("build"): + print_status_busy(f"Removing {directory}/build/") + shutil.rmtree("build") + print_status_success(f"Removed {directory}/build/") + cleaned_any = True + finally: + os.chdir(original_cwd) + if not cleaned_any: print("Nothing to clean") def clear_cache_action(): @@ -641,9 +892,9 @@ def update_self_action(): else: exit_with_error("Update failed") -def get_device_info(ip): +def get_device_info(host): print_status_busy(f"Requesting device info") - url = get_url(ip, "/info") + url = get_url(host, "/info") try: response = requests.get(url, timeout=http_timeout_seconds) if response.status_code != 200: @@ -654,10 +905,13 @@ def get_device_info(ip): except requests.RequestException as e: print_status_error(f"Device info request failed: {e}") -def run_action(manifest, ip): - app_id = manifest["app.id"] +def run_action(manifest, arguments): + host = arguments.get("host") + if host is None: + exit_with_error("Missing required argument: --host") + app_id = get_primary_app_id(manifest) print_status_busy("Running") - url = get_url(ip, "/app/run") + url = get_url(host, "/app/run") params = {'id': app_id} try: response = requests.post(url, params=params, timeout=http_timeout_seconds) @@ -668,21 +922,21 @@ def run_action(manifest, ip): except requests.RequestException as e: print_status_error(f"Running request failed: {e}") -def install_action(manifest, ip, platforms): - print_status_busy("Installing") - for platform in platforms: - elf_path = find_elf_file(platform) - if elf_path is None: - print_status_error(f"ELF file not built for {platform}") - return False +def install_action(manifest, arguments): + host = arguments.get("host") + if host is None: + exit_with_error("Missing required argument: --host") package_path = package_name(manifest) - # print(f"Installing {package_path} to {ip}") - url = get_url(ip, "/app/install") + if not os.path.isfile(package_path): + print_status_error(f"Package not found: {package_path} (run 'build' first)") + return False + print_status_busy("Installing") + url = get_url(host, "/app/install") try: # Prepare multipart form data with open(package_path, 'rb') as file: files = { - 'elf': file + 'app': file } response = requests.put(url, files=files, timeout=install_timeout_seconds) if response.status_code != 200: @@ -698,10 +952,13 @@ def install_action(manifest, ip, platforms): print_status_error(f"Install file error: {e}") return False -def uninstall_action(manifest, ip): - app_id = manifest["app.id"] +def uninstall_action(manifest, arguments): + host = arguments.get("host") + if host is None: + exit_with_error("Missing required argument: --host") + app_id = get_package_id(manifest) print_status_busy("Uninstalling") - url = get_url(ip, "/app/uninstall") + url = get_url(host, "/app/uninstall") params = {'id': app_id} try: response = requests.put(url, params=params, timeout=http_timeout_seconds) @@ -716,38 +973,34 @@ def uninstall_action(manifest, ip): if __name__ == "__main__": print(f"Tactility Build System v{ttbuild_version}") - if "--help" in sys.argv: - print_help() - sys.exit() - # Argument validation - if len(sys.argv) == 1: + + # Anchor the cache to the invocation directory, before --path (below) can chdir into the app. + ttbuild_path = os.path.abspath(ttbuild_path) + + argv = sys.argv[1:] + if len(argv) == 0: print_help() sys.exit(1) - if "--verbose" in sys.argv: - verbose = True - sys.argv.remove("--verbose") - skip_build = False - if "--skip-build" in sys.argv: - skip_build = True - sys.argv.remove("--skip-build") - if "--local-sdk" in sys.argv: - use_local_sdk = True - sys.argv.remove("--local-sdk") - - # Check if the first argument is a path to an app directory - if len(sys.argv) > 2: - potential_app_dir = sys.argv[1] - if os.path.isdir(potential_app_dir) and os.path.isfile(os.path.join(potential_app_dir, "manifest.properties")): - if verbose: - print_status_success(f"Switching to app directory: {potential_app_dir}") - os.chdir(potential_app_dir) - sys.argv = [sys.argv[0]] + sys.argv[2:] - - if len(sys.argv) < 2: + + parsed_command = parse_command_line(argv) + + if parsed_command.arguments.has("help"): + print_help() + sys.exit() + if parsed_command.action is None: print_help() sys.exit(1) - - action_arg = sys.argv[1] + + verbose = parsed_command.arguments.has("verbose") + use_local_sdk = parsed_command.arguments.has("local-sdk") + + app_path = parsed_command.arguments.get("path") + if app_path is not None: + if not os.path.isdir(app_path) or not os.path.isfile(os.path.join(app_path, "manifest.properties")): + exit_with_error(f"App path not found or missing manifest.properties: {app_path}") + if verbose: + print_status_success(f"Switching to app directory: {app_path}") + os.chdir(app_path) # Environment setup setup_environment() @@ -755,58 +1008,30 @@ def uninstall_action(manifest, ip): exit_with_error("manifest.properties not found") manifest = read_manifest() validate_manifest(manifest) - all_platform_targets = manifest["target.platforms"].split(",") # Update SDK cache (tool.json) if not use_local_sdk and should_update_tool_json() and not update_tool_json(): exit_with_error("Failed to retrieve SDK info") # Actions + action_arg = parsed_command.action if action_arg == "build": - if len(sys.argv) < 2: - print_help() - exit_with_error("Commandline parameter missing") - platform = None - if len(sys.argv) > 2: - platform = sys.argv[2] - if not build_action(manifest, platform, skip_build): + if not build_action(manifest, parsed_command.arguments): sys.exit(1) elif action_arg == "clean": - clean_action() + clean_action(manifest) elif action_arg == "clearcache": clear_cache_action() elif action_arg == "updateself": update_self_action() elif action_arg == "run": - if len(sys.argv) < 3: - print_help() - exit_with_error("Commandline parameter missing") - run_action(manifest, sys.argv[2]) + run_action(manifest, parsed_command.arguments) elif action_arg == "install": - if len(sys.argv) < 3: - print_help() - exit_with_error("Commandline parameter missing") - platform = None - platforms_to_install = all_platform_targets - if len(sys.argv) >= 4: - platform = sys.argv[3] - platforms_to_install = [platform] - install_action(manifest, sys.argv[2], platforms_to_install) + install_action(manifest, parsed_command.arguments) elif action_arg == "uninstall": - if len(sys.argv) < 3: - print_help() - exit_with_error("Commandline parameter missing") - uninstall_action(manifest, sys.argv[2]) + uninstall_action(manifest, parsed_command.arguments) elif action_arg == "bir" or action_arg == "brrr": - if len(sys.argv) < 3: - print_help() - exit_with_error("Commandline parameter missing") - platform = None - platforms_to_install = all_platform_targets - if len(sys.argv) >= 4: - platform = sys.argv[3] - platforms_to_install = [platform] - if build_action(manifest, platform, skip_build): - if install_action(manifest, sys.argv[2], platforms_to_install): - run_action(manifest, sys.argv[2]) + if build_action(manifest, parsed_command.arguments): + if install_action(manifest, parsed_command.arguments): + run_action(manifest, parsed_command.arguments) else: print_help() exit_with_error("Unknown commandline parameter")