Implement posix app loading (#643)
- Added POSIX desktop support for SDK builds, application packaging, and integration testing. - Added POSIX filesystem partitions and improved application path handling. - Added simulator support for loading and running applications dynamically. - Improved simulator task stack handling and display startup reliability. - Improved simulator display scaling, resizing, high-DPI support, and pointer accuracy. - Fixed LVGL timers and input polling on POSIX. (fixes simulator with Linux on some Intel graphics platforms) - Standardized data paths across platforms. - Logging now works via separate task: this allows apps to write to log without it affecting their stdout (for apps that output text as relevant date for other apps, like the File Selection app) - Fixes for app stdio
This commit is contained in:
committed by
GitHub
parent
d3656bcd3d
commit
643cbc3806
+16
-1
@@ -4,7 +4,12 @@ endfunction()
|
||||
function(_tactility_project)
|
||||
endfunction()
|
||||
|
||||
macro(tactility_project project_name)
|
||||
macro(tactility_project_pre project_name)
|
||||
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||
set(EXTRA_COMPONENT_DIRS ${TACTILITY_SDK_PATH} ${TACTILITY_SDK_PATH}/Modules)
|
||||
endmacro()
|
||||
|
||||
macro(tactility_project_post project_name)
|
||||
set(TACTILITY_SKIP_SPIFFS 1)
|
||||
|
||||
# Tactility's PanicHandler.cpp needs s0 to stay a frame pointer to capture a callstack for
|
||||
@@ -45,3 +50,13 @@ macro(tactility_project project_name)
|
||||
)
|
||||
|
||||
endmacro()
|
||||
|
||||
macro(tactility_component_register)
|
||||
cmake_parse_arguments(TT_COMPONENT "" "" "SRCS;INCLUDE_DIRS;REQUIRES;PRIV_REQUIRES" ${ARGN})
|
||||
idf_component_register(
|
||||
SRCS ${TT_COMPONENT_SRCS}
|
||||
INCLUDE_DIRS ${TT_COMPONENT_INCLUDE_DIRS}
|
||||
REQUIRES TactilitySDK ${TT_COMPONENT_REQUIRES}
|
||||
PRIV_REQUIRES ${TT_COMPONENT_PRIV_REQUIRES}
|
||||
)
|
||||
endmacro()
|
||||
@@ -0,0 +1,51 @@
|
||||
function(tactility_project)
|
||||
endfunction()
|
||||
|
||||
function(_tactility_project)
|
||||
endfunction()
|
||||
|
||||
macro(tactility_project_pre project_name)
|
||||
endmacro()
|
||||
|
||||
macro(tactility_project_post project_name)
|
||||
# The app's own library target is defined in a subdirectory (e.g. "main"); without this it
|
||||
# would land nested under that subdirectory instead of directly in the build dir.
|
||||
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR})
|
||||
|
||||
# Mirrors the ESP-IDF "TactilitySDK" component (Buildscripts/TactilitySDK/CMakeLists.txt):
|
||||
# apps link against this single target instead of listing SDK include dirs themselves.
|
||||
# Posix apps are dlopen()ed into a running Tactility process (see app-posix-module) and
|
||||
# resolve symbols against Tactility's own copies at load time, so headers are all they need
|
||||
# at compile time - no libraries to link.
|
||||
add_library(TactilitySDK INTERFACE)
|
||||
target_include_directories(TactilitySDK INTERFACE
|
||||
${TACTILITY_SDK_PATH}/Modules/app-module/include
|
||||
${TACTILITY_SDK_PATH}/Modules/crypt-module/include
|
||||
${TACTILITY_SDK_PATH}/Modules/gps-module/include
|
||||
${TACTILITY_SDK_PATH}/Modules/lvgl-module/include
|
||||
${TACTILITY_SDK_PATH}/Modules/lvgl-window-manager-module/include
|
||||
${TACTILITY_SDK_PATH}/Modules/service-module/include
|
||||
${TACTILITY_SDK_PATH}/Libraries/TactilityKernel/include
|
||||
${TACTILITY_SDK_PATH}/Libraries/lvgl/include
|
||||
${TACTILITY_SDK_PATH}/Libraries/FreeRTOS-Kernel/include
|
||||
${TACTILITY_SDK_PATH}/Libraries/FreeRTOS-Kernel/portable/ThirdParty/GCC/Posix
|
||||
${TACTILITY_SDK_PATH}/Libraries/FreeRTOS-Kernel/portable/ThirdParty/GCC/Posix/utils
|
||||
)
|
||||
target_compile_definitions(TactilitySDK INTERFACE LV_LVGL_H_INCLUDE_SIMPLE)
|
||||
|
||||
# ESP-IDF's project() auto-discovers the "main" component; plain CMake doesn't.
|
||||
add_subdirectory(main)
|
||||
endmacro()
|
||||
|
||||
macro(tactility_component_register)
|
||||
cmake_parse_arguments(TT_COMPONENT "" "" "SRCS;INCLUDE_DIRS;REQUIRES;PRIV_REQUIRES" ${ARGN})
|
||||
# Must be a SHARED object, not a -pie executable: glibc's dlopen() unconditionally refuses
|
||||
# any ET_DYN carrying the DF_1_PIE flag ("cannot dynamically load position-independent
|
||||
# executable"), regardless of whether it has a dynamic-linker segment - verified empirically.
|
||||
add_library(${PROJECT_NAME} SHARED ${TT_COMPONENT_SRCS})
|
||||
target_link_libraries(${PROJECT_NAME} PRIVATE TactilitySDK)
|
||||
set_target_properties(${PROJECT_NAME} PROPERTIES POSITION_INDEPENDENT_CODE ON)
|
||||
if (TT_COMPONENT_INCLUDE_DIRS)
|
||||
target_include_directories(${PROJECT_NAME} PRIVATE ${TT_COMPONENT_INCLUDE_DIRS})
|
||||
endif ()
|
||||
endmacro()
|
||||
@@ -2,6 +2,8 @@ if (COMMAND tactility_add_module)
|
||||
return()
|
||||
endif()
|
||||
|
||||
cmake_minimum_required(VERSION 3.24)
|
||||
|
||||
macro(tactility_get_module_name NAME OUT_NAME)
|
||||
if (DEFINED ENV{ESP_IDF_VERSION})
|
||||
set(${OUT_NAME} ${COMPONENT_LIB})
|
||||
@@ -16,8 +18,7 @@ macro(tactility_add_module NAME)
|
||||
# undefined reference to. Needed when this module provides symbols a component it depends on
|
||||
# (e.g. lvgl__lvgl's custom-allocator hooks) calls back into - a reverse reference a normal
|
||||
# single-pass static-archive link can't resolve, since that component is scanned after this
|
||||
# one's archive has already been passed once. POSIX builds link everything as plain OBJECT
|
||||
# libraries (no archive-pruning to begin with), so this is a no-op there.
|
||||
# one's archive has already been passed once.
|
||||
set(options WHOLE_ARCHIVE)
|
||||
set(oneValueArgs)
|
||||
set(multiValueArgs SRCS INCLUDE_DIRS PRIV_INCLUDE_DIRS REQUIRES PRIV_REQUIRES)
|
||||
@@ -40,7 +41,7 @@ macro(tactility_add_module NAME)
|
||||
${whole_archive_arg}
|
||||
)
|
||||
else()
|
||||
add_library(${NAME} OBJECT)
|
||||
add_library(${NAME} STATIC)
|
||||
target_sources(${NAME} PRIVATE ${ARG_SRCS})
|
||||
target_include_directories(${NAME}
|
||||
PRIVATE ${ARG_PRIV_INCLUDE_DIRS}
|
||||
@@ -48,5 +49,12 @@ macro(tactility_add_module NAME)
|
||||
)
|
||||
target_link_libraries(${NAME} PUBLIC ${ARG_REQUIRES})
|
||||
target_link_libraries(${NAME} PRIVATE ${ARG_PRIV_REQUIRES})
|
||||
if (ARG_WHOLE_ARCHIVE)
|
||||
# A static archive only pulls in object files that already have a pending undefined
|
||||
# reference at the point the archive is scanned, so a plain link drops ${NAME}'s
|
||||
# reverse dependencies (see WHOLE_ARCHIVE comment above). Make whoever links ${NAME}
|
||||
# whole-archive it instead of just archive-pruning it.
|
||||
set_property(TARGET ${NAME} APPEND PROPERTY INTERFACE_LINK_LIBRARIES $<LINK_LIBRARY:WHOLE_ARCHIVE,${NAME}>)
|
||||
endif()
|
||||
endif()
|
||||
endmacro()
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import os
|
||||
import platform
|
||||
import shutil
|
||||
import subprocess
|
||||
import sys
|
||||
@@ -17,42 +18,53 @@ def get_idf_target():
|
||||
return None
|
||||
return None
|
||||
|
||||
def main():
|
||||
# 1. Get idf_target
|
||||
idf_target = get_idf_target()
|
||||
if not idf_target:
|
||||
print("Could not determine IDF target from sdkconfig")
|
||||
sys.exit(1)
|
||||
|
||||
# 2. Get version
|
||||
def get_version():
|
||||
try:
|
||||
with open("version.txt", "r") as f:
|
||||
version = f.read().strip()
|
||||
return f.read().strip()
|
||||
except FileNotFoundError:
|
||||
print("version.txt not found")
|
||||
sys.exit(1)
|
||||
|
||||
# 3. Construct sdk_path
|
||||
# release/TactilitySDK/${version}-${idf_target}/TactilitySDK
|
||||
sdk_path = os.path.join("release", "TactilitySDK", f"{version}-{idf_target}", "TactilitySDK")
|
||||
|
||||
# 4. Cleanup sdk_path
|
||||
def run_release_script(script_name, sdk_path):
|
||||
# Cleanup sdk_path
|
||||
if os.path.exists(sdk_path):
|
||||
print(f"Cleaning up {sdk_path}")
|
||||
shutil.rmtree(sdk_path)
|
||||
|
||||
|
||||
os.makedirs(sdk_path, exist_ok=True)
|
||||
|
||||
# 5. Call release-sdk.py
|
||||
# Note: Using sys.executable to ensure we use the same python interpreter
|
||||
script_path = os.path.join("Buildscripts", "release-sdk.py")
|
||||
script_path = os.path.join("Buildscripts", script_name)
|
||||
print(f"Running {script_path} {sdk_path}")
|
||||
|
||||
|
||||
result = subprocess.run([sys.executable, script_path, sdk_path])
|
||||
|
||||
|
||||
if result.returncode != 0:
|
||||
print(f"Error: {script_path} failed with return code {result.returncode}")
|
||||
sys.exit(result.returncode)
|
||||
|
||||
def main():
|
||||
version = get_version()
|
||||
|
||||
# ESP_IDF_VERSION is only set once an ESP-IDF environment has been activated (export.sh /
|
||||
# the Windows PowerShell profile - see building.md); same check release-sdk-esp32.py and
|
||||
# release-sdk-posix.py themselves use to tell the two builds apart.
|
||||
esp_idf_version = os.environ.get("ESP_IDF_VERSION", "")
|
||||
|
||||
if esp_idf_version:
|
||||
idf_target = get_idf_target()
|
||||
if not idf_target:
|
||||
print("Could not determine IDF target from sdkconfig")
|
||||
sys.exit(1)
|
||||
# release/TactilitySDK/${version}-${idf_target}/TactilitySDK
|
||||
sdk_path = os.path.join("release", "TactilitySDK", f"{version}-{idf_target}", "TactilitySDK")
|
||||
run_release_script("release-sdk-esp32.py", sdk_path)
|
||||
else:
|
||||
# release/TactilitySDK/${version}-posix-${arch}/TactilitySDK
|
||||
arch = platform.machine()
|
||||
sdk_path = os.path.join("release", "TactilitySDK", f"{version}-posix-{arch}", "TactilitySDK")
|
||||
run_release_script("release-sdk-posix.py", sdk_path)
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
||||
@@ -1,66 +1,16 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import os
|
||||
import shutil
|
||||
import glob
|
||||
import subprocess
|
||||
import sys
|
||||
import importlib.util
|
||||
from textwrap import dedent
|
||||
|
||||
def map_copy(mappings, target_base):
|
||||
"""
|
||||
Helper function to map input files/directories to output files/directories.
|
||||
mappings: list of dicts with 'src' (glob pattern) and 'dst' (relative to target_base or absolute)
|
||||
'src' can be a single file or a directory (if it ends with /).
|
||||
"""
|
||||
for mapping in mappings:
|
||||
src_pattern = mapping['src']
|
||||
dst_rel = mapping['dst']
|
||||
dst_path = os.path.join(target_base, dst_rel)
|
||||
_shared_spec = importlib.util.spec_from_file_location("release_sdk_shared", os.path.join("Buildscripts", "release-sdk-shared.py"))
|
||||
shared = importlib.util.module_from_spec(_shared_spec)
|
||||
_shared_spec.loader.exec_module(shared)
|
||||
|
||||
# To preserve directory structure, we need to know where the wildcard starts
|
||||
# or have a way to determine the "base" of the search.
|
||||
# We'll split the pattern into a fixed base and a pattern part.
|
||||
|
||||
# Simple heuristic: find the first occurrence of '*' or '?'
|
||||
wildcard_idx = -1
|
||||
for i, char in enumerate(src_pattern):
|
||||
if char in '*?':
|
||||
wildcard_idx = i
|
||||
break
|
||||
|
||||
if wildcard_idx != -1:
|
||||
# Found a wildcard. The base is the directory containing it.
|
||||
pattern_base = os.path.dirname(src_pattern[:wildcard_idx])
|
||||
else:
|
||||
# No wildcard. If it's a directory, we might want to preserve its name?
|
||||
# For now, let's treat no-wildcard as no relative structure needed.
|
||||
pattern_base = None
|
||||
|
||||
src_files = glob.glob(src_pattern, recursive=True)
|
||||
if not src_files:
|
||||
continue
|
||||
|
||||
for src in src_files:
|
||||
if os.path.isdir(src):
|
||||
continue
|
||||
|
||||
if pattern_base and src.startswith(pattern_base):
|
||||
# Calculate relative path from the base of the glob pattern
|
||||
rel_src = os.path.relpath(src, pattern_base)
|
||||
# If dst_rel ends with /, it's a target directory
|
||||
if dst_rel.endswith('/') or os.path.isdir(dst_path):
|
||||
final_dst = os.path.join(dst_path, rel_src)
|
||||
else:
|
||||
# If dst_rel is a file, we can't really preserve structure
|
||||
# unless we join it. But usually it's a dir if structure is preserved.
|
||||
final_dst = dst_path
|
||||
else:
|
||||
final_dst = dst_path if not (dst_rel.endswith('/') or os.path.isdir(dst_path)) else os.path.join(dst_path, os.path.basename(src))
|
||||
|
||||
os.makedirs(os.path.dirname(final_dst), exist_ok=True)
|
||||
shutil.copy2(src, final_dst)
|
||||
|
||||
def get_driver_mappings(driver_name):
|
||||
return [
|
||||
{'src': f'Drivers/{driver_name}/include/**', 'dst': f'Drivers/{driver_name}/include/'},
|
||||
@@ -82,11 +32,7 @@ def create_module_cmakelists(module_name):
|
||||
INCLUDE_DIRS "include"
|
||||
)
|
||||
add_prebuilt_library({module_name} "binary/lib{module_name}.a")
|
||||
'''.format(module_name=module_name))
|
||||
|
||||
def write_module_cmakelists(path, content):
|
||||
with open(path, 'w') as f:
|
||||
f.write(content)
|
||||
''')
|
||||
|
||||
def driver_is_available(driver_name):
|
||||
"""
|
||||
@@ -101,28 +47,20 @@ def driver_is_available(driver_name):
|
||||
|
||||
def add_driver(target_path, driver_name):
|
||||
mappings = get_driver_mappings(driver_name)
|
||||
map_copy(mappings, target_path)
|
||||
shared.map_copy(mappings, target_path)
|
||||
cmakelists_content = create_module_cmakelists(driver_name)
|
||||
write_module_cmakelists(os.path.join(target_path, f"Drivers/{driver_name}/CMakeLists.txt"), cmakelists_content)
|
||||
shared.write_module_cmakelists(os.path.join(target_path, f"Drivers/{driver_name}/CMakeLists.txt"), cmakelists_content)
|
||||
|
||||
def add_module(target_path, module_name):
|
||||
mappings = get_module_mappings(module_name)
|
||||
map_copy(mappings, target_path)
|
||||
shared.map_copy(mappings, target_path)
|
||||
cmakelists_content = create_module_cmakelists(module_name)
|
||||
write_module_cmakelists(os.path.join(target_path, f"Modules/{module_name}/CMakeLists.txt"), cmakelists_content)
|
||||
|
||||
def generate_tactility_sdk_cmake(target_path):
|
||||
src = os.path.join('Buildscripts', 'TactilitySDK', 'TactilitySDK.cmake')
|
||||
shutil.copy2(src, os.path.join(target_path, 'TactilitySDK.cmake'))
|
||||
|
||||
def generate_tactility_sdk_top_cmakelists(target_path):
|
||||
src = os.path.join('Buildscripts', 'TactilitySDK', 'CMakeLists.txt')
|
||||
shutil.copy2(src, os.path.join(target_path, 'CMakeLists.txt'))
|
||||
shared.write_module_cmakelists(os.path.join(target_path, f"Modules/{module_name}/CMakeLists.txt"), cmakelists_content)
|
||||
|
||||
def main():
|
||||
if len(sys.argv) < 2:
|
||||
print("Usage: release-sdk.py [target_path]")
|
||||
print("Example: release-sdk.py release/TactilitySDK")
|
||||
print("Usage: release-sdk-esp32.py [target_path]")
|
||||
print("Example: release-sdk-esp32.py release/TactilitySDK")
|
||||
sys.exit(1)
|
||||
|
||||
esp_idf_version = os.environ.get("ESP_IDF_VERSION", "")
|
||||
@@ -169,20 +107,16 @@ def main():
|
||||
{'src': 'Libraries/minmea/COPYING', 'dst': 'Libraries/minmea/'},
|
||||
]
|
||||
|
||||
map_copy(mappings, target_path)
|
||||
shared.map_copy(mappings, target_path)
|
||||
|
||||
# Modules
|
||||
add_module(target_path, "app-module")
|
||||
add_module(target_path, "crypt-module")
|
||||
add_module(target_path, "gps-module")
|
||||
add_module(target_path, "http-module")
|
||||
add_module(target_path, "lvgl-module")
|
||||
add_module(target_path, "lvgl-window-manager-module")
|
||||
add_module(target_path, "service-module")
|
||||
module_names = shared.read_module_list(os.path.join('Buildscripts', 'release-sdk-modules.txt'))
|
||||
for module_name in module_names:
|
||||
add_module(target_path, module_name)
|
||||
|
||||
# Final scripts - copied verbatim
|
||||
generate_tactility_sdk_cmake(target_path)
|
||||
generate_tactility_sdk_top_cmakelists(target_path)
|
||||
shared.generate_tactility_sdk_cmake(target_path, 'esp32')
|
||||
shared.generate_tactility_sdk_top_cmakelists(target_path)
|
||||
|
||||
# Output ESP-IDF SDK version to file
|
||||
with open(os.path.join(target_path, "idf-version.txt"), "a") as f:
|
||||
@@ -0,0 +1,7 @@
|
||||
app-module
|
||||
crypt-module
|
||||
gps-module
|
||||
http-module
|
||||
lvgl-module
|
||||
lvgl-window-manager-module
|
||||
service-module
|
||||
Executable
+101
@@ -0,0 +1,101 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import os
|
||||
import sys
|
||||
import importlib.util
|
||||
from textwrap import dedent
|
||||
|
||||
_shared_spec = importlib.util.spec_from_file_location("release_sdk_shared", os.path.join("Buildscripts", "release-sdk-shared.py"))
|
||||
shared = importlib.util.module_from_spec(_shared_spec)
|
||||
_shared_spec.loader.exec_module(shared)
|
||||
|
||||
def get_module_mappings(module_name):
|
||||
return [
|
||||
{'src': f'Modules/{module_name}/include/**', 'dst': f'Modules/{module_name}/include/'},
|
||||
{'src': f'Modules/{module_name}/*.md', 'dst': f'Modules/{module_name}/'},
|
||||
{'src': f'buildsim/Modules/{module_name}/lib{module_name}.a', 'dst': f'Modules/{module_name}/binary/lib{module_name}.a'},
|
||||
]
|
||||
|
||||
def create_module_cmakelists(module_name):
|
||||
return dedent(f'''
|
||||
cmake_minimum_required(VERSION 3.20)
|
||||
add_library({module_name} STATIC IMPORTED)
|
||||
set_target_properties({module_name} PROPERTIES
|
||||
IMPORTED_LOCATION "${{CMAKE_CURRENT_LIST_DIR}}/binary/lib{module_name}.a"
|
||||
INTERFACE_INCLUDE_DIRECTORIES "${{CMAKE_CURRENT_LIST_DIR}}/include"
|
||||
)
|
||||
''')
|
||||
|
||||
def add_module(target_path, module_name):
|
||||
mappings = get_module_mappings(module_name)
|
||||
shared.map_copy(mappings, target_path)
|
||||
cmakelists_content = create_module_cmakelists(module_name)
|
||||
shared.write_module_cmakelists(os.path.join(target_path, f"Modules/{module_name}/CMakeLists.txt"), cmakelists_content)
|
||||
|
||||
def main():
|
||||
if len(sys.argv) < 2:
|
||||
print("Usage: release-sdk-posix.py [target_path]")
|
||||
print("Example: release-sdk-posix.py release/TactilitySDK")
|
||||
sys.exit(1)
|
||||
|
||||
esp_idf_version = os.environ.get("ESP_IDF_VERSION", "")
|
||||
if esp_idf_version:
|
||||
print("Error: ESP_IDF_VERSION environment variable is set - this script packages the POSIX/simulator build, run it outside an ESP-IDF environment")
|
||||
sys.exit(1)
|
||||
|
||||
target_path = os.path.abspath(sys.argv[1])
|
||||
os.makedirs(target_path, exist_ok=True)
|
||||
|
||||
# Mapping logic
|
||||
mappings = [
|
||||
{'src': 'version.txt', 'dst': ''},
|
||||
# TactilityFreeRtos
|
||||
{'src': 'TactilityFreeRtos/Include/**', 'dst': 'Libraries/TactilityFreeRtos/Include/'},
|
||||
{'src': 'TactilityFreeRtos/CMakeLists.txt', 'dst': 'Libraries/TactilityFreeRtos/'},
|
||||
{'src': 'TactilityFreeRtos/LICENSE*.*', 'dst': 'Libraries/TactilityFreeRtos/'},
|
||||
# TactilityKernel
|
||||
{'src': 'buildsim/TactilityKernel/libTactilityKernel.a', 'dst': 'Libraries/TactilityKernel/binary/'},
|
||||
{'src': 'TactilityKernel/include/**', 'dst': 'Libraries/TactilityKernel/include/'},
|
||||
{'src': 'TactilityKernel/CMakeLists.txt', 'dst': 'Libraries/TactilityKernel/'},
|
||||
{'src': 'TactilityKernel/*.md', 'dst': 'Libraries/TactilityKernel/'},
|
||||
# FreeRTOS-Kernel - TactilityKernel's public headers (tactility/freertos/*.h) include the
|
||||
# real FreeRTOS.h/task.h/etc directly, unlike ESP32 where ESP-IDF's own "freertos"
|
||||
# component and Kconfig-generated FreeRTOSConfig.h are already part of every project.
|
||||
{'src': 'Libraries/FreeRTOS-Kernel/include/**', 'dst': 'Libraries/FreeRTOS-Kernel/include/'},
|
||||
{'src': 'Libraries/FreeRTOS-Kernel/portable/ThirdParty/GCC/Posix/*.h', 'dst': 'Libraries/FreeRTOS-Kernel/portable/ThirdParty/GCC/Posix/'},
|
||||
{'src': 'Libraries/FreeRTOS-Kernel/portable/ThirdParty/GCC/Posix/utils/*.h', 'dst': 'Libraries/FreeRTOS-Kernel/portable/ThirdParty/GCC/Posix/utils/'},
|
||||
{'src': 'Libraries/FreeRTOS-Kernel/LICENSE*.*', 'dst': 'Libraries/FreeRTOS-Kernel/'},
|
||||
{'src': 'Devices/simulator/Source/FreeRTOSConfig.h', 'dst': 'Libraries/FreeRTOS-Kernel/include/'},
|
||||
# lvgl (basics)
|
||||
{'src': 'buildsim/Libraries/lvgl/lib/liblvgl.a', 'dst': 'Libraries/lvgl/binary/liblvgl.a'},
|
||||
{'src': 'Libraries/lvgl/lvgl.h', 'dst': 'Libraries/lvgl/include/'},
|
||||
{'src': 'Libraries/lvgl/lv_version.h', 'dst': 'Libraries/lvgl/include/'},
|
||||
{'src': 'Libraries/lvgl/LICENCE*.*', 'dst': 'Libraries/lvgl/'},
|
||||
{'src': 'lv_conf.h', 'dst': 'Libraries/lvgl/include/'},
|
||||
{'src': 'Libraries/lvgl/src/**/*.h', 'dst': 'Libraries/lvgl/include/src/'},
|
||||
# minitar
|
||||
{'src': 'buildsim/Libraries/minitar/libminitar.a', 'dst': 'Libraries/minitar/binary/'},
|
||||
{'src': 'Libraries/minitar/minitar/minitar.h', 'dst': 'Libraries/minitar/include/'},
|
||||
{'src': 'Libraries/minitar/minitar/LICENSE*', 'dst': 'Libraries/minitar/'},
|
||||
# minmea
|
||||
{'src': 'buildsim/Libraries/minmea/libminmea.a', 'dst': 'Libraries/minmea/binary/'},
|
||||
{'src': 'Libraries/minmea/Include/**', 'dst': 'Libraries/minmea/include/'},
|
||||
{'src': 'Libraries/minmea/CMakeLists.txt', 'dst': 'Libraries/minmea/'},
|
||||
{'src': 'Libraries/minmea/README.md', 'dst': 'Libraries/minmea/'},
|
||||
{'src': 'Libraries/minmea/LICENSE*.*', 'dst': 'Libraries/minmea/'},
|
||||
{'src': 'Libraries/minmea/COPYING', 'dst': 'Libraries/minmea/'},
|
||||
]
|
||||
|
||||
shared.map_copy(mappings, target_path)
|
||||
|
||||
# Modules
|
||||
module_names = shared.read_module_list(os.path.join('Buildscripts', 'release-sdk-modules.txt'))
|
||||
for module_name in module_names:
|
||||
add_module(target_path, module_name)
|
||||
|
||||
# Final scripts - copied verbatim
|
||||
shared.generate_tactility_sdk_cmake(target_path, 'posix')
|
||||
shared.generate_tactility_sdk_top_cmakelists(target_path)
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,91 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
# Functions shared between release-sdk-esp32.py and release-sdk-posix.py. Not runnable on its
|
||||
# own; loaded by those scripts via importlib (its hyphenated filename isn't a valid Python
|
||||
# module name for a plain "import").
|
||||
|
||||
import os
|
||||
import shutil
|
||||
import glob
|
||||
import sys
|
||||
|
||||
def map_copy(mappings, target_base):
|
||||
"""
|
||||
Helper function to map input files/directories to output files/directories.
|
||||
mappings: list of dicts with 'src' (glob pattern) and 'dst' (relative to target_base or absolute)
|
||||
'src' can be a single file or a directory (if it ends with /).
|
||||
"""
|
||||
for mapping in mappings:
|
||||
src_pattern = mapping['src']
|
||||
dst_rel = mapping['dst']
|
||||
dst_path = os.path.join(target_base, dst_rel)
|
||||
|
||||
# To preserve directory structure, we need to know where the wildcard starts
|
||||
# or have a way to determine the "base" of the search.
|
||||
# We'll split the pattern into a fixed base and a pattern part.
|
||||
|
||||
# Simple heuristic: find the first occurrence of '*' or '?'
|
||||
wildcard_idx = -1
|
||||
for i, char in enumerate(src_pattern):
|
||||
if char in '*?':
|
||||
wildcard_idx = i
|
||||
break
|
||||
|
||||
if wildcard_idx != -1:
|
||||
# Found a wildcard. The base is the directory containing it.
|
||||
pattern_base = os.path.dirname(src_pattern[:wildcard_idx])
|
||||
else:
|
||||
# No wildcard. If it's a directory, we might want to preserve its name?
|
||||
# For now, let's treat no-wildcard as no relative structure needed.
|
||||
pattern_base = None
|
||||
|
||||
src_files = glob.glob(src_pattern, recursive=True)
|
||||
if not src_files:
|
||||
continue
|
||||
|
||||
for src in src_files:
|
||||
if os.path.isdir(src):
|
||||
continue
|
||||
|
||||
if pattern_base and src.startswith(pattern_base):
|
||||
# Calculate relative path from the base of the glob pattern
|
||||
rel_src = os.path.relpath(src, pattern_base)
|
||||
# If dst_rel ends with /, it's a target directory
|
||||
if dst_rel.endswith('/') or os.path.isdir(dst_path):
|
||||
final_dst = os.path.join(dst_path, rel_src)
|
||||
else:
|
||||
# If dst_rel is a file, we can't really preserve structure
|
||||
# unless we join it. But usually it's a dir if structure is preserved.
|
||||
final_dst = dst_path
|
||||
else:
|
||||
final_dst = dst_path if not (dst_rel.endswith('/') or os.path.isdir(dst_path)) else os.path.join(dst_path, os.path.basename(src))
|
||||
|
||||
os.makedirs(os.path.dirname(final_dst), exist_ok=True)
|
||||
shutil.copy2(src, final_dst)
|
||||
|
||||
def write_module_cmakelists(path, content):
|
||||
with open(path, 'w') as f:
|
||||
f.write(content)
|
||||
|
||||
def read_module_list(path):
|
||||
"""Reads a newline-separated module name list, skipping empty lines, and checks that each
|
||||
named module actually exists under Modules/ - exits the process with an error if not."""
|
||||
with open(path, 'r') as f:
|
||||
module_names = [line.strip() for line in f if line.strip()]
|
||||
|
||||
for module_name in module_names:
|
||||
if not os.path.isdir(os.path.join('Modules', module_name)):
|
||||
print(f"Error: Modules/{module_name} does not exist (listed in {path})")
|
||||
sys.exit(1)
|
||||
|
||||
return module_names
|
||||
|
||||
def generate_tactility_sdk_cmake(target_path, variant):
|
||||
"""variant selects Buildscripts/TactilitySDK/TactilitySDK.{variant}.cmake (e.g. "esp32" or
|
||||
"posix") - always copied into the SDK as the platform-neutral name TactilitySDK.cmake."""
|
||||
src = os.path.join('Buildscripts', 'TactilitySDK', f'TactilitySDK.{variant}.cmake')
|
||||
shutil.copy2(src, os.path.join(target_path, 'TactilitySDK.cmake'))
|
||||
|
||||
def generate_tactility_sdk_top_cmakelists(target_path):
|
||||
src = os.path.join('Buildscripts', 'TactilitySDK', 'CMakeLists.txt')
|
||||
shutil.copy2(src, os.path.join(target_path, 'CMakeLists.txt'))
|
||||
Reference in New Issue
Block a user