Add ccache support for app building

This commit is contained in:
Ken Van Hoeylandt 2026-10-15 19:44:27 +02:00
parent dae0970cee
commit bb79f13544
3 changed files with 86 additions and 4 deletions

View File

@ -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: <TACTILITY_CCACHE_DIR or ~/.tactility/ccache>/<target>.
# 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<build>/config) relative
# - CCACHE_NOHASHDIR: ignores the working directory that -g would otherwise add to the hash
# - CCACHE_IGNOREOPTIONS: ignores -fmacro-prefix-map=<app directory>=., 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(

View File

@ -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/<version>, 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:

View File

@ -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::*`