From bb79f1354446b67c40e2360ec293b5faef9fe41a Mon Sep 17 00:00:00 2001 From: Ken Van Hoeylandt Date: Thu, 15 Oct 2026 19:44:27 +0200 Subject: [PATCH] Add ccache support for app building --- .../TactilitySDK/TactilitySDK.esp32.cmake | 74 +++++++++++++++++++ Buildscripts/TactilitySDK/Tools/app.py | 13 +++- Documentation/ideas.md | 3 +- 3 files changed, 86 insertions(+), 4 deletions(-) diff --git a/Buildscripts/TactilitySDK/TactilitySDK.esp32.cmake b/Buildscripts/TactilitySDK/TactilitySDK.esp32.cmake index a1d493035..8065d43c0 100644 --- a/Buildscripts/TactilitySDK/TactilitySDK.esp32.cmake +++ b/Buildscripts/TactilitySDK/TactilitySDK.esp32.cmake @@ -62,8 +62,82 @@ macro(tactility_project_post project_name) service-module ) + _tactility_enable_sdk_ccache() + endmacro() +# Compiles the ESP-IDF and SDK components through ccache, so apps built for the same SDK and +# platform reuse each other's objects: the SDK's fixed sdkconfig makes these objects identical +# across apps. The app's own components get no launcher, so their sources are never cached. +# Each platform uses its own cache directory: /. +# Disable with -D TACTILITY_CCACHE=OFF (the tool's --no-ccache). +function(_tactility_enable_sdk_ccache) + set(TACTILITY_CCACHE ON CACHE BOOL "Compile ESP-IDF and SDK components through ccache") + if (NOT TACTILITY_CCACHE) + return() + endif() + find_program(TACTILITY_CCACHE_PROGRAM ccache) + if (NOT TACTILITY_CCACHE_PROGRAM) + return() + endif() + + if (DEFINED ENV{TACTILITY_CCACHE_DIR}) + set(cache_root "$ENV{TACTILITY_CCACHE_DIR}") + elseif (CMAKE_HOST_WIN32) + set(cache_root "$ENV{USERPROFILE}/.tactility/ccache") + else() + set(cache_root "$ENV{HOME}/.tactility/ccache") + endif() + idf_build_get_property(target IDF_TARGET) + + # The settings are part of the launcher, so they also apply when idf.py runs without the tool. + # They remove the parts of an SDK compile command that differ per app: + # - CCACHE_BASEDIR: makes paths into the build directory (-I/config) relative + # - CCACHE_NOHASHDIR: ignores the working directory that -g would otherwise add to the hash + # - CCACHE_IGNOREOPTIONS: ignores -fmacro-prefix-map==., which can't affect + # sources outside the app directory + set(launcher + ${CMAKE_COMMAND} -E env + "CCACHE_DIR=${cache_root}/${target}" + "CCACHE_BASEDIR=${CMAKE_BINARY_DIR}" + "CCACHE_NOHASHDIR=1" + "CCACHE_IGNOREOPTIONS=-fmacro-prefix-map=*" + ${TACTILITY_CCACHE_PROGRAM} + ) + + # Selected by the directory that defines the target, not by component: some components define + # extra library targets (e.g. mbedtls's mbedcrypto), and the app's own targets (main, the ELF) + # are defined in the app directory. CMake has no launcher for assembly, so .S files aren't cached. + idf_build_get_property(idf_path IDF_PATH) + set(sdk_roots "${idf_path}" "${TACTILITY_SDK_PATH}") + set(directories "${CMAKE_SOURCE_DIR}") + while (directories) + list(POP_FRONT directories directory) + get_property(subdirectories DIRECTORY "${directory}" PROPERTY SUBDIRECTORIES) + list(APPEND directories ${subdirectories}) + set(is_sdk_directory FALSE) + foreach(sdk_root ${sdk_roots}) + cmake_path(IS_PREFIX sdk_root "${directory}" NORMALIZE is_prefix) + if (is_prefix) + set(is_sdk_directory TRUE) + endif() + endforeach() + if (NOT is_sdk_directory) + continue() + endif() + get_property(targets DIRECTORY "${directory}" PROPERTY BUILDSYSTEM_TARGETS) + foreach(target_name ${targets}) + get_target_property(target_type ${target_name} TYPE) + if (target_type MATCHES "^(STATIC_LIBRARY|SHARED_LIBRARY|MODULE_LIBRARY|OBJECT_LIBRARY|EXECUTABLE)$") + set_target_properties(${target_name} PROPERTIES + C_COMPILER_LAUNCHER "${launcher}" + CXX_COMPILER_LAUNCHER "${launcher}" + ) + endif() + endforeach() + endwhile() +endfunction() + macro(tactility_component_register) cmake_parse_arguments(TT_COMPONENT "" "" "SRCS;INCLUDE_DIRS;REQUIRES;PRIV_REQUIRES" ${ARGN}) idf_component_register( diff --git a/Buildscripts/TactilitySDK/Tools/app.py b/Buildscripts/TactilitySDK/Tools/app.py index e77ef9d24..8be87e5c3 100755 --- a/Buildscripts/TactilitySDK/Tools/app.py +++ b/Buildscripts/TactilitySDK/Tools/app.py @@ -12,6 +12,8 @@ import _device_host ttbuild_version = "8.0.0" verbose = False +# Compile the ESP-IDF and SDK components through ccache (see TactilitySDK.esp32.cmake) +use_ccache = True # The development server client (shell.py) is next to this script tool_directory = os.path.dirname(os.path.abspath(__file__)) # The SDKs are in TACTILITY_SDK_PATH/sdk/, which tt sets along with the version it selected. @@ -65,6 +67,7 @@ def print_help(): print("Options:") print(" -p, --path [input] Path to the app directory (defaults to current directory)") print(" --help Show this commandline info") + print(" --no-ccache Don't reuse ESP-IDF/SDK objects from other app builds via ccache") print(" --skip-build Run everything except the idf.py/CMake commands") print(" --verbose Show extra console output") print("") @@ -182,6 +185,7 @@ ARG_SPECS = { "platform": ("-a", "--architecture", True), "verbose": (None, "--verbose", False), "skip-build": (None, "--skip-build", False), + "no-ccache": (None, "--no-ccache", False), "help": (None, "--help", False), } @@ -472,6 +476,10 @@ def build_all(version, platforms, skip_build): return False return True +# Always passed, because a -D value persists in CMakeCache.txt +def get_ccache_define(): + return f"TACTILITY_CCACHE={'ON' if use_ccache else 'OFF'}" + def wait_for_process(process): buffer = [] if sys.platform != "win32": @@ -514,7 +522,7 @@ def build_first(version, platform, skip_build): cmake_path = get_cmake_path(platform) print_status_busy(f"Building {platform} ELF") shell_needed = sys.platform == "win32" - build_command = ["idf.py", "-B", cmake_path, "build"] + build_command = ["idf.py", "-B", cmake_path, "-D", get_ccache_define(), "build"] if verbose: print(f"Running command: {' '.join(build_command)}") with subprocess.Popen(build_command, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, shell=shell_needed) as process: @@ -544,7 +552,7 @@ def build_consecutively(version, platform, skip_build): cmake_path = get_cmake_path(platform) print_status_busy(f"Building {platform} ELF") shell_needed = sys.platform == "win32" - build_command = ["idf.py", "-B", cmake_path, "elf"] + build_command = ["idf.py", "-B", cmake_path, "-D", get_ccache_define(), "elf"] if verbose: print(f"Running command: {' '.join(build_command)}") with subprocess.Popen(build_command, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, shell=shell_needed) as process: @@ -758,6 +766,7 @@ if __name__ == "__main__": sys.exit(1) verbose = parsed_command.arguments.has("verbose") + use_ccache = not parsed_command.arguments.has("no-ccache") app_path = parsed_command.arguments.get("path") if app_path is not None: diff --git a/Documentation/ideas.md b/Documentation/ideas.md index 60c036b42..2431bb108 100644 --- a/Documentation/ideas.md +++ b/Documentation/ideas.md @@ -7,6 +7,7 @@ ## Higher Priority +- Improve Setup: Show progress of some kind (page_indicator?). Improve skip/next buttons UX? e.g. add outline button style and put buttons together? (update docs!) - BlueTooth as radio backend for MeshCore/Meshtastic? - ESP-NOW as radio backend. Use it for MeshCore/Meshtastic? - Find out why the first build of an app with a clean build cache folder always fails @@ -15,11 +16,9 @@ - Improve Wi-Fi mock so it actually works with connect/disconnect/scan list. It connects to literally anything from the scan list. - Fix posix Wi-Fi so it actually connects to something - Support http/https on posix -- Vi apps are both named "Vi" in the App Hub - Use GPS time to set/update the current time: when enabled, at boot, when wifi is not enabled, start the gps device and wait until a lock is acquired, then get the time and sync it. Then stop the GPS device. - lvgl-module's spinner relies on hard-coded spinner asset from Tactility main project, should be sized based on DPI - Cursor is hard-coded, needs size based on DPI -- Improve Setup: Show progress of some kind. Put skip/next buttons closer? And add outline button style (update docs!) - Files app should have a page indicator like Launcher. - Move Bluetooth code from kernel to Modules/bluetooth-module, and have includes in path bluetooth/ then make a bluetooth_command.cpp like wifi_command.cpp - Inspect code for memory improvements: replace `std::*` with `tt::std::*`