Merge branch 'feat/cmake_add_partition_flash_binary_function_v6.0' into 'release/v6.0'

feat(esp_partition): Add esp_partition_register_target Cmake function (v6.0)

See merge request espressif/esp-idf!51045
This commit is contained in:
Jiang Jiang Jian
2026-09-03 19:50:11 +08:00
15 changed files with 405 additions and 39 deletions
+16
View File
@@ -54,6 +54,22 @@ else()
if(${target} STREQUAL "linux")
# set BUILD_DIR because partition_linux.c uses a file created in the build directory
target_compile_definitions(${COMPONENT_LIB} PRIVATE "BUILD_DIR=\"${build_dir}\"")
target_link_libraries(${COMPONENT_LIB} PRIVATE dl)
# Create the linux_flash_data target that accumulates partition data entries
# registered by esp_partition_register_target(). The target may already exist if
# a project_include.cmake ran before this CMakeLists.txt.
if(NOT TARGET linux_flash_data)
add_custom_target(linux_flash_data)
endif()
# Generate a manifest file listing all partition data binaries to pre-load
# into the emulated flash. Each line has format: "<hex_offset> <absolute_path>".
# Generator expressions are evaluated after all CMakeLists.txt processing completes,
# so all entries accumulated by esp_partition_register_target() will be included.
file(GENERATE
OUTPUT "${build_dir}/linux_flash_data.txt"
CONTENT "$<JOIN:$<TARGET_PROPERTY:linux_flash_data,FLASH_DATA_ENTRIES>,\n>\n")
endif()
if(CMAKE_C_COMPILER_ID MATCHES "GNU")
+98 -1
View File
@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2021-2025 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2021-2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@@ -169,6 +169,99 @@ static size_t esp_partition_calc_required_flash_size_from_file(const char *parti
return required;
}
// Load pre-built partition data binaries into the emulated flash.
// Reads a manifest file (BUILD_DIR "/linux_flash_data.txt") where each line
// has format: "<hex_offset> <absolute_path_to_binary>".
// Each binary is copied into s_spiflash_mem_file_buf at the given offset.
static void esp_partition_load_flash_data(void)
{
const char *manifest_path = BUILD_DIR "/linux_flash_data.txt";
FILE *manifest = fopen(manifest_path, "r");
if (manifest == NULL) {
// No manifest file — nothing to pre-load (this is normal for projects
// that don't use esp_partition_register_target())
return;
}
char line[PATH_MAX + 32];
while (fgets(line, sizeof(line), manifest) != NULL) {
// Skip empty lines
size_t len = strlen(line);
if (len == 0) {
continue;
}
// Trim trailing newline
if (line[len - 1] == '\n') {
line[len - 1] = '\0';
len--;
}
if (len == 0) {
continue;
}
// Parse "<hex_offset> <path>"
char *space = strchr(line, ' ');
if (space == NULL) {
ESP_LOGW(TAG, "Malformed line in flash data manifest: %s", line);
continue;
}
*space = '\0';
const char *offset_str = line;
const char *file_path = space + 1;
unsigned long offset = strtoul(offset_str, NULL, 0);
if (offset == 0 && offset_str[0] != '0') {
ESP_LOGW(TAG, "Invalid offset in flash data manifest: %s", offset_str);
continue;
}
FILE *data_file = fopen(file_path, "rb");
if (data_file == NULL) {
ESP_LOGW(TAG, "Failed to open flash data file %s: %s", file_path, strerror(errno));
continue;
}
// Get file size
if (fseek(data_file, 0L, SEEK_END) != 0) {
ESP_LOGW(TAG, "Failed to seek in flash data file %s: %s", file_path, strerror(errno));
fclose(data_file);
continue;
}
long data_size = ftell(data_file);
if (data_size < 0) {
ESP_LOGW(TAG, "Failed to get size of flash data file %s: %s", file_path, strerror(errno));
fclose(data_file);
continue;
}
if (fseek(data_file, 0L, SEEK_SET) != 0) {
ESP_LOGW(TAG, "Failed to seek in flash data file %s: %s", file_path, strerror(errno));
fclose(data_file);
continue;
}
// Verify the data fits within the emulated flash
if (offset + (size_t)data_size > s_esp_partition_file_mmap_ctrl_act.flash_file_size) {
ESP_LOGW(TAG, "Flash data file %s (offset: 0x%lx, size: %ld) exceeds emulated flash size (%" PRIu32 " B). Skipping.",
file_path, offset, data_size, (uint32_t) s_esp_partition_file_mmap_ctrl_act.flash_file_size);
fclose(data_file);
continue;
}
// Copy the data into the emulated flash at the specified offset
uint8_t *dst = (uint8_t *)s_spiflash_mem_file_buf + offset;
size_t bytes_read = fread(dst, 1, (size_t)data_size, data_file);
fclose(data_file);
if (bytes_read != (size_t)data_size) {
ESP_LOGW(TAG, "Partial read of flash data file %s: expected %ld bytes, got %zu", file_path, data_size, bytes_read);
} else {
ESP_LOGV(TAG, "Loaded flash data: %s at offset 0x%lx (%ld bytes)", file_path, offset, data_size);
}
}
fclose(manifest);
}
esp_err_t esp_partition_file_mmap(const uint8_t **part_desc_addr_start)
{
// temporary file is used only if control structure doesn't specify file name.
@@ -332,6 +425,10 @@ esp_err_t esp_partition_file_mmap(const uint8_t **part_desc_addr_start)
ret = ESP_ERR_INVALID_STATE;
break;
}
// Load any pre-built partition data binaries into the emulated flash.
// The manifest file is generated at build time by esp_partition_register_target().
esp_partition_load_flash_data();
} while (false);
}
@@ -0,0 +1,130 @@
# esp_partition_register_target
#
# @brief Register a binary file to be flashed to a named partition.
#
# Creates a per-partition flash target (e.g., ``idf.py <partition>-flash``) and optionally
# includes the binary in ``idf.py flash``. Automatically resolves the partition offset from
# the partition table and determines encryption requirements.
#
# @note On the 'linux' target, this function registers the binary for pre-loading into
# the emulated flash image. The ALWAYS_PLAINTEXT, FLASH_IN_PROJECT, and
# FLASH_IN_PROJECT_DEPENDENCY_TARGETS options are ignored for 'linux'.
#
# @param[in] partition_name Partition name as defined in the partition table
# @param[in] binary_path Path to the binary image file
# @param[in, optional] ALWAYS_PLAINTEXT (option) If specified, the binary is flashed in plain text even
# when flash encryption is enabled. Must not be used with
# partitions marked as 'encrypted' in the partition table.
# @param[in, optional] DEPENDS (multi value) Additional dependencies of the custom target.
# @param[in, optional] FLASH_IN_PROJECT (option) If specified, the binary is flashed in the project
# when running `idf.py flash`.
# @param[in, optional] FLASH_IN_PROJECT_DEPENDENCY_TARGETS (multi value) Additional targets that will be
# dependent on dependencies set in DEPENDS.
# FLASH_IN_PROJECT has to be set.
#
# Example usage (from spiffs_create_partition_image):
#
# # Create a custom target that generates the SPIFFS image file
# add_custom_target(spiffs_${partition}_bin ALL
# COMMAND ${spiffsgen_py} ${size} ${base_dir} ${image_file}
# ...)
#
# # Flash the generated image to the partition.
# # ALWAYS_PLAINTEXT is used because SPIFFS does not support flash encryption.
# # DEPENDS ensures the image is generated before flashing.
# # FLASH_IN_PROJECT includes it in `idf.py flash`.
# esp_partition_register_target(${partition} "${image_file}"
# ALWAYS_PLAINTEXT
# DEPENDS spiffs_${partition}_bin
# FLASH_IN_PROJECT
# )
#
function(esp_partition_register_target partition_name binary_path)
set(options ALWAYS_PLAINTEXT FLASH_IN_PROJECT)
set(multi DEPENDS FLASH_IN_PROJECT_DEPENDENCY_TARGETS)
cmake_parse_arguments(arg "${options}" "" "${multi}" "${ARGN}")
# Look up partition offset from the partition table
partition_table_get_partition_info(offset "--partition-name ${partition_name}" "offset")
if(NOT offset)
message(FATAL_ERROR "esp_partition_register_target: "
"Could not find partition '${partition_name}' in the partition table.")
endif()
idf_build_get_property(idf_target IDF_TARGET)
if(idf_target STREQUAL "linux")
# On linux, register the binary for loading into the emulated flash at startup.
# The partition_linux.c emulation layer reads a manifest file listing each
# (offset, binary_path) pair and copies the data into the memory-mapped flash image.
get_filename_component(binary_abs_path "${binary_path}" ABSOLUTE)
# Use a global CMake target to accumulate entries via generator expressions.
if(NOT TARGET linux_flash_data)
add_custom_target(linux_flash_data)
endif()
set_property(TARGET linux_flash_data APPEND PROPERTY FLASH_DATA_ENTRIES
"${offset} ${binary_abs_path}")
# Register build dependency so the binary is generated before the app runs
if(arg_DEPENDS)
add_dependencies(linux_flash_data ${arg_DEPENDS})
endif()
return()
endif()
# Validate: ALWAYS_PLAINTEXT must not be used with a partition marked as encrypted
if(arg_ALWAYS_PLAINTEXT)
partition_table_get_partition_info(pt_encrypted "--partition-name ${partition_name}" "encrypted")
if(pt_encrypted STREQUAL "True")
message(FATAL_ERROR
"esp_partition_register_target: ALWAYS_PLAINTEXT was specified for partition '${partition_name}', "
"but this partition is marked as 'encrypted' in the partition table. "
"These settings are contradictory. Either remove ALWAYS_PLAINTEXT or "
"remove the 'encrypted' flag from the partition table entry.")
endif()
endif()
# Determine encryption requirements.
# If caller passed ALWAYS_PLAINTEXT, use it. Otherwise, auto-detect from partition table.
if(arg_ALWAYS_PLAINTEXT)
set(flash_plaintext TRUE)
else()
esptool_py_partition_needs_encryption(needs_encryption ${partition_name})
if(NOT needs_encryption)
set(flash_plaintext TRUE)
else()
set(flash_plaintext FALSE)
endif()
endif()
# Build the esptool_py_flash_target arg list
set(esptool_py_flash_target_arg_list)
if(flash_plaintext)
list(APPEND esptool_py_flash_target_arg_list ALWAYS_PLAINTEXT)
endif()
# Create the per-partition flash target (e.g., "mypart-flash")
idf_component_get_property(main_args esptool_py FLASH_ARGS)
idf_component_get_property(sub_args esptool_py FLASH_SUB_ARGS)
esptool_py_flash_target(${partition_name}-flash "${main_args}" "${sub_args}" ${esptool_py_flash_target_arg_list})
esptool_py_flash_target_image(${partition_name}-flash ${partition_name} ${offset}
"${binary_path}" ${esptool_py_flash_target_arg_list})
if(arg_DEPENDS)
add_dependencies(${partition_name}-flash ${arg_DEPENDS})
endif()
# Add to the main "flash" target if FLASH_IN_PROJECT is set
if(arg_FLASH_IN_PROJECT)
esptool_py_flash_target_image(flash ${partition_name} ${offset}
"${binary_path}" ${esptool_py_flash_target_arg_list})
if(arg_DEPENDS)
add_dependencies(flash ${arg_DEPENDS})
foreach(target ${arg_FLASH_IN_PROJECT_DEPENDENCY_TARGETS})
add_dependencies(${target} ${arg_DEPENDS})
endforeach()
endif()
endif()
endfunction()
+4 -9
View File
@@ -67,18 +67,13 @@ function(fatfs_create_partition_image partition base_dir)
ADDITIONAL_CLEAN_FILES
${image_file})
idf_component_get_property(main_args esptool_py FLASH_ARGS)
idf_component_get_property(sub_args esptool_py FLASH_SUB_ARGS)
# Last (optional) parameter is the encryption for the target. In our
# case, fatfs is not encrypt so pass FALSE to the function.
esptool_py_flash_target(${partition}-flash "${main_args}" "${sub_args}" ALWAYS_PLAINTEXT)
esptool_py_flash_to_partition(${partition}-flash "${partition}" "${image_file}")
set(esp_partition_register_target_optional_args DEPENDS fatfs_${partition}_bin)
add_dependencies(${partition}-flash fatfs_${partition}_bin)
if(arg_FLASH_IN_PROJECT)
esptool_py_flash_to_partition(flash "${partition}" "${image_file}")
add_dependencies(flash fatfs_${partition}_bin)
list(APPEND esp_partition_register_target_optional_args FLASH_IN_PROJECT)
endif()
esp_partition_register_target(${partition} "${image_file}" ${esp_partition_register_target_optional_args})
else()
set(message "Failed to create FATFS image for partition '${partition}'. "
"Check project configuration if using the correct partition table file.")
+5 -9
View File
@@ -43,20 +43,16 @@ function(nvs_create_partition_image partition csv)
PROPERTY ADDITIONAL_CLEAN_FILES ${image_file}
)
idf_component_get_property(main_args esptool_py FLASH_ARGS)
idf_component_get_property(sub_args esptool_py FLASH_SUB_ARGS)
esptool_py_flash_target(${partition}-flash "${main_args}" "${sub_args}" ALWAYS_PLAINTEXT)
esptool_py_flash_to_partition(${partition}-flash "${partition}" "${image_file}")
add_dependencies(${partition}-flash nvs_${partition}_bin)
set(register_target_optional_args ALWAYS_PLAINTEXT DEPENDS nvs_${partition}_bin)
if(arg_FLASH_IN_PROJECT)
esptool_py_flash_to_partition(flash "${partition}" "${image_file}")
add_dependencies(flash nvs_${partition}_bin)
list(APPEND register_target_optional_args FLASH_IN_PROJECT)
if(CONFIG_SECURE_FLASH_ENCRYPTION_MODE_DEVELOPMENT)
add_dependencies(encrypted-flash nvs_${partition}_bin)
list(APPEND register_target_optional_args FLASH_IN_PROJECT_DEPENDENCY_TARGETS encrypted-flash)
endif()
endif()
esp_partition_register_target(${partition} "${image_file}" ${register_target_optional_args})
else()
set(message
"Failed to create NVS image for partition '${partition}'. "
+6 -10
View File
@@ -47,19 +47,15 @@ function(spiffs_create_partition_image partition base_dir)
ADDITIONAL_CLEAN_FILES
${image_file})
idf_component_get_property(main_args esptool_py FLASH_ARGS)
idf_component_get_property(sub_args esptool_py FLASH_SUB_ARGS)
# Last (optional) parameter is the encryption for the target. In our
# case, spiffs is not encrypt so pass FALSE to the function.
esptool_py_flash_target(${partition}-flash "${main_args}" "${sub_args}" ALWAYS_PLAINTEXT)
esptool_py_flash_to_partition(${partition}-flash "${partition}" "${image_file}")
add_dependencies(${partition}-flash spiffs_${partition}_bin)
# Encryption for SPIFFS is not supported, so optional parameter ALWAYS_PLAINTEXT is passed to the function.
set(esp_partition_register_target_optional_args)
list(APPEND esp_partition_register_target_optional_args ALWAYS_PLAINTEXT DEPENDS spiffs_${partition}_bin)
if(arg_FLASH_IN_PROJECT)
esptool_py_flash_to_partition(flash "${partition}" "${image_file}")
add_dependencies(flash spiffs_${partition}_bin)
list(APPEND esp_partition_register_target_optional_args FLASH_IN_PROJECT)
endif()
esp_partition_register_target(${partition} "${image_file}" ${esp_partition_register_target_optional_args})
else()
set(message "Failed to create SPIFFS image for partition '${partition}'. "
"Check project configuration if using the correct partition table file.")