cmake_minimum_required(VERSION 3.5)
set(CMAKE_TOOLCHAIN_FILE $ENV{IDF_PATH}/toolchain.cmake)
-project(idf_bootloader ASM C CXX)
-set(COMPONENTS bootloader esptool_py esp32 soc bootloader_support log spi_flash micro-ecc soc)
+if(NOT SDKCONFIG)
+ message(FATAL_ERROR "Bootloader subproject expects the SDKCONFIG variable to be passed in by the parent build process.")
+endif()
+project(bootloader ASM C CXX)
+
+set(COMPONENTS bootloader esptool_py esp32 soc bootloader_support log spi_flash micro-ecc soc)
set(BOOTLOADER_BUILD 1)
set(IS_BOOTLOADER_BUILD 1) # deprecated, use BOOTLOADER_BUILD
add_definitions(-DBOOTLOADER_BUILD=1)
-if(NOT SDKCONFIG)
- message(FATAL_ERROR "Bootloader subproject expects the SDKCONFIG variable to be passed in by the app build process.")
-endif()
-
-include($ENV{IDF_PATH}/tools/cmake/project.cmake)
-
-add_executable(bootloader.elf
- main/bootloader_start.c
+set(MAIN_SRCS main/bootloader_start.c
main/flash_qio_mode.c)
-target_link_libraries(bootloader.elf ${COMPONENT_LIBRARIES})
+include($ENV{IDF_PATH}/tools/cmake/project.cmake)
target_link_libraries(bootloader.elf "-L ${CMAKE_CURRENT_SOURCE_DIR}/main")
target_link_libraries(bootloader.elf "-T esp32.bootloader.ld")
--- /dev/null
+# Set some global esptool.py variables
+set(ESPTOOLPY "${PYTHON}" "${CMAKE_CURRENT_LIST_DIR}/esptool/esptool.py" --chip esp32)
+set(ESPSECUREPY "${PYTHON}" "${CMAKE_CURRENT_LIST_DIR}/esptool/espsecure.py")
+
+set(ESPPORT $ENV{ESPPORT})
+if(NOT ESPPORT)
+ set(ESPPORT ${CONFIG_ESPTOOLPY_PORT})
+endif()
+
+set(ESPBAUD $ENV{ESPPORT})
+if(NOT ESPBAUD)
+ set(ESPPORT ${CONFIG_ESPTOOLPY_PORT})
+endif()
+
+set(ESPFLASHMODE ${CONFIG_ESPTOOLPY_FLASHMODE})
+set(ESPFLASHFREQ ${CONFIG_ESPTOOLPY_FLASHFREQ})
+set(ESPFLASHSIZE ${CONFIG_ESPTOOLPY_FLASHSIZE})
+
+set(ESPTOOLPY_SERIAL "${ESPTOOLPY}" --port "${ESPPORT}" --baud ${ESPBAUD})
+
+if(CONFIG_ESPTOOLPY_FLASHSIZE_DETECT)
+ set(flashsize_arg detect)
+else()
+ set(flashsize_arg ${ESPFLASHSIZE})
+endif()
+
+set(ESPTOOLPY_ELF2IMAGE_FLASH_OPTIONS --flash_mode ${ESPFLASHMODE} --flash_freq ${ESPFLASHFREQ} --flash_size ${ESPFLASHSIZE})
+
+set(ESPTOOLPY_WRITE_FLASH_OPTIONS --flash_mode ${ESPFLASHMODE} --flash_freq ${ESPFLASHFREQ} --flash_size ${flashsize_Arg})
+
+set(ESPTOOLPY_WRITE_FLASH ${ESPTOOLPY_SERIAL} write_flash ${ESPTOOLPY_WRITE_FLASH_OPTIONS})
+
+#
+# Add 'binary' target - generates with elf2image
+#
+add_custom_command(OUTPUT "${PROJECT_NAME}.bin"
+ COMMAND ${ESPTOOLPY} elf2image ${ESPTOOLPY_ELF2IMAGE_FLASH_OPTIONS} -o "${PROJECT_NAME}.bin" "${PROJECT_NAME}.elf"
+ DEPENDS ${PROJECT_NAME}.elf
+ VERBATIM
+ )
+add_custom_target(binary ALL DEPENDS "${PROJECT_NAME}.bin")
+
+#
+# Add 'flash' target - not all build systems can run this directly
+#
+add_custom_target(flash DEPENDS binary partition_table bootloader_subproject)
+# TODO: this target should call "idftool" not esptool directly, so it can
+# override things (port, baud, etc) at runtime not configure time
set(final_partition_target "build_partition_table")
endif()
-add_custom_target(build_partition_table ALL
- "${PYTHON}" "${CMAKE_CURRENT_SOURCE_DIR}/gen_esp32part.py" -q
- ${partition_csv} ${unsigned_partition_bin}
- BYPRODUCTS ${unsigned_partition_bin}
- DEPENDS ${partition_csv} "${CMAKE_CURRENT_SOURCE_DIR}/gen_esp32part.py"
- VERBATIM)
+if(CONFIG_PARTITION_TABLE_MD5)
+ set(md5_opt --disable-md5sum)
+endif()
+
+if(CONFIG_ESPTOOLPY_FLASHSIZE)
+ set(flashsize_opt --flash-size ${CONFIG_ESPTOOLPY_FLASHSIZE})
+endif()
+
+add_custom_command(OUTPUT "${unsigned_partition_bin}"
+ COMMAND "${PYTHON}" "${CMAKE_CURRENT_SOURCE_DIR}/gen_esp32part.py" -q ${md5_opt} ${flashsize_opt}
+ ${partition_csv} ${unsigned_partition_bin}
+ DEPENDS ${partition_csv} "${CMAKE_CURRENT_SOURCE_DIR}/gen_esp32part.py"
+ VERBATIM)
# Add signing steps
if(CONFIG_SECURE_BOOT_BUILD_SIGNED_BINARIES)
get_filename_component(secure_boot_signing_key "${CONFIG_SECURE_BOOT_SIGNING_KEY}" ABSOLUTE BASE_DIR "${PROJECT_PATH}")
- add_custom_target(signpartition_table ALL
- "${PYTHON}" "${ESPSECUREPY}" sign_data --keyfile "${secure_boot_signing_key}" -o "${final_partition_bin}" "${unsigned_partition_bin}"
- BYPRODUCTS "${final_partition_bin}"
+ add_custom_command(OUTPUT "${final_partition_bin}"
+ COMMAND "${PYTHON}" "${ESPSECUREPY}" sign_data --keyfile "${secure_boot_signing_key}" -o "${final_partition_bin}" "${unsigned_partition_bin}"
DEPENDS "${unsigned_partition_bin}"
VERBATIM)
endif()
-# Use global properties ESPTOOL_WRITE_FLASH_ARGS & FLASH_DEPENDS to pass this info to the esptool component for flashing
-set_property(GLOBAL APPEND_STRING PROPERTY ESPTOOL_WRITE_FLASH_ARGS "${final_partition_bin} ${partition_table_offset} ")
-set_property(GLOBAL APPEND PROPERTY FLASH_DEPENDS "${final_partition_target}")
+add_custom_target(partition_table ALL DEPENDS "${final_partition_bin}")
+
+# Use global properties ESPTOOL_WRITE_FLASH_ARGS to pass this info to build hte list of esptool write arguments for flashing
+set_property(GLOBAL APPEND_STRING PROPERTY ESPTOOL_WRITE_FLASH_ARGS "${partition_table_offset} ${final_partition_bin} ")
register_config_only_component()
-# The following four lines of boilerplate have to be in your project's CMakeLists
+# The following five lines of boilerplate have to be in your project's CMakeLists
# in this exact order for cmake to work correctly
cmake_minimum_required(VERSION 3.5)
set(CMAKE_TOOLCHAIN_FILE $ENV{IDF_PATH}/toolchain.cmake)
-project(idf_project ASM C CXX)
-include($ENV{IDF_PATH}/tools/cmake/project.cmake)
+project(hello-world ASM C CXX)
-add_executable(hello_world.elf
- main/hello_world_main.c)
+set(MAIN_SRCS main/hello_world_main.c)
-target_link_libraries(hello_world.elf ${COMPONENT_LIBRARIES})
+include($ENV{IDF_PATH}/tools/cmake/project.cmake)
endforeach()
endif()
+ # add public includes from other components when building this component
+ if(COMPONENT_SRCS OR embed_binaries)
+ add_library(${component} STATIC ${COMPONENT_SRCS})
+ set(include_type PUBLIC)
+ else()
+ add_library(${component} INTERFACE) # header-only component
+ set(include_type INTERFACE)
+ endif()
+
# binaries to embed directly in library
spaces2list(COMPONENT_EMBED_FILES)
spaces2list(COMPONENT_EMBED_TXTFILES)
target_add_binary_data("${component}" "${embed_data}" "${embed_type}")
endforeach()
- # add public includes from other components when building this component
- if(COMPONENT_SRCS OR embed_binaries)
- add_library(${component} STATIC ${COMPONENT_SRCS} ${embed_binaries})
- set(include_type PUBLIC)
- else()
- add_library(${component} INTERFACE) # header-only component
- set(include_type INTERFACE)
- endif(COMPONENT_SRCS OR embed_binaries)
-
+ # add public includes
foreach(include_dir ${COMPONENT_ADD_INCLUDEDIRS})
get_filename_component(include_dir ${include_dir} ABSOLUTE BASE_DIR ${component_dir})
target_include_directories(${component} ${include_type} ${include_dir})
endforeach()
+ # add private includes
foreach(include_dir ${COMPONENT_PRIV_INCLUDEDIRS})
if (${include_type} STREQUAL INTERFACE)
message(FATAL_ERROR "Component ${component} can't have no source files and COMPONENT_PRIV_INCLUDEDIRS set.")
# each component should see the include directories of each other
#
# (we can't do this until all components are registered, because if(TARGET ...) won't work
- foreach(a ${COMPONENTS})
+ foreach(a ${COMPONENTS} ${CMAKE_PROJECT_NAME}.elf)
if (TARGET ${a})
get_target_property(a_type ${a} TYPE)
- if (${a_type} STREQUAL STATIC_LIBRARY)
+ if (${a_type} STREQUAL STATIC_LIBRARY OR ${a_type} STREQUAL EXECUTABLE)
foreach(b ${COMPONENTS})
if (TARGET ${b} AND NOT ${a} STREQUAL ${b})
# Add all public compile options from b in a
$<TARGET_PROPERTY:${b},INTERFACE_COMPILE_OPTIONS>)
endif()
endforeach(b)
- endif(${a_type} STREQUAL STATIC_LIBRARY)
+ endif(${a_type} STREQUAL STATIC_LIBRARY OR ${a_type} STREQUAL EXECUTABLE)
- set(COMPONENT_LIBRARIES "${COMPONENT_LIBRARIES};${a}")
+ if (${a_type} MATCHES .+_LIBRARY)
+ set(COMPONENT_LIBRARIES "${COMPONENT_LIBRARIES};${a}")
+ endif()
endif()
endforeach()
- # set COMPONENT_LIBRARIES in top-level scope
- set(COMPONENT_LIBRARIES "${COMPONENT_LIBRARIES}" PARENT_SCOPE)
-
# Embedded binary & text files
spaces2list(COMPONENT_EMBED_FILES)
foreach(embed_src ${COMPONENT_EMBED_FILES})
target_add_binary_data(${component} "${embed_src}" TEXT)
endforeach()
+ target_link_libraries(${CMAKE_PROJECT_NAME}.elf ${COMPONENT_LIBRARIES})
+
endfunction(components_finish_registration)
include(crosstool_version_check)
macro(idf_set_global_variables)
+ # Note that CONFIG_xxx is not available when this function is called
set_default(EXTRA_COMPONENT_DIRS "")
spaces2list(COMPONENTS)
+ # Tell cmake to drop executables in the top-level build dir
+ set(EXECUTABLE_OUTPUT_PATH "${CMAKE_BINARY_DIR}")
+
endmacro()
# Add all the IDF global compiler & preprocessor options
endfunction(idf_set_global_compiler_options)
-# Override add_executable to add IDF-specific
-# linker flags & map file to all built executables
-function(add_executable target)
- get_filename_component(basename ${target} NAME_WE)
- set(mapfile "${basename}.map")
-
- _add_executable(${ARGV})
-
- target_link_libraries(${target} "-Wl,--gc-sections -Wl,--cref -Wl,--Map=${mapfile} -Wl,--start-group")
-
- set_property(DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" APPEND PROPERTY ADDITIONAL_MAKE_CLEAN_FILES "${CMAKE_CURRENT_BINARY_DIR}/${mapfile}")
-endfunction(add_executable)
-
# Verify the IDF environment is configured correctly (environment, toolchain, etc)
function(idf_verify_environment)
+ if(NOT CMAKE_PROJECT_NAME)
+ message(FATAL_ERROR "Project top-level CMakeLists.txt file must call project() before including project.cmake")
+ endif()
+
# Check toolchain is configured properly in cmake
if(NOT ( ${CMAKE_SYSTEM_NAME} STREQUAL "Generic" AND ${CMAKE_C_COMPILER} MATCHES xtensa))
- message(FATAL_ERROR "The parent project CMakeLists.txt file needs to set CMAKE_TOOLCHAIN_FILE "
- "before including this file. "
+ message(FATAL_ERROR "Project top-level CMakeLists.txt file needs to set CMAKE_TOOLCHAIN_FILE "
+ "before including project.cmake.\n"
"Update CMakeLists.txt to match the template project and delete CMakeCache.txt before "
"re-running cmake.")
endif()
crosstool_version_check("1.22.0-80-g6c4433a")
endfunction()
+
+# idf_add_executable
+#
+# Calls add_executable to add the final project executable
+# Adds .map & .bin file targets
+# Sets up flash-related targets
+function(idf_add_executable)
+ set(exe_target ${PROJECT_NAME}.elf)
+
+ spaces2list(${MAIN_SRCS})
+ add_executable(${exe_target} "${MAIN_SRCS}")
+
+ add_map_file(${exe_target})
+endfunction(idf_add_executable)
+
+
+# add_map_file
+#
+# Set linker args for 'exe_target' to generate a linker Map file
+function(add_map_file exe_target)
+ get_filename_component(basename ${exe_target} NAME_WE)
+ set(mapfile "${basename}.map")
+ target_link_libraries(${exe_target} "-Wl,--gc-sections -Wl,--cref -Wl,--Map=${mapfile} -Wl,--start-group")
+ set_property(DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" APPEND PROPERTY ADDITIONAL_MAKE_CLEAN_FILES "${CMAKE_CURRENT_BINARY_DIR}/${mapfile}")
+endfunction(add_map_file)
git_submodule_check("${IDF_PATH}")
+# Include any top-level project_include.cmake files from components
+foreach(component ${COMPONENT_PATHS})
+ include_if_exists("${component}/project_include.cmake")
+endforeach()
+
#
-# Add components to the build
+# Add each component to the build as a library
#
foreach(component ${COMPONENT_PATHS})
get_filename_component(component_name ${component} NAME)
add_subdirectory(${component} ${component_name})
endforeach()
+#
+# Add the app executable to the build (has name of PROJECT.elf)
+#
+idf_add_executable()
+
+#
+# Finish component registration (add cross-dependencies, make
+# executable dependent on all components.)
+#
components_finish_registration()
-# Load the targets for the bootloader subproject
+# Define the external target to build the bootloader subproject
if(NOT BOOTLOADER_BUILD)
include(bootloader_subproject)
endif()
# to a binary object as part of the build
function(target_add_binary_data target embed_file embed_type)
- get_filename_component(embed_file "${embed_file}" ABSOLUTE BASE_DIR "${CMAKE_CURRENT_SOURCE_DIR}")
+ get_filename_component(embed_file "${embed_file}" ABSOLUTE)
get_filename_component(name "${embed_file}" NAME)
set(embed_srcfile "${CMAKE_BINARY_DIR}/${name}.c")
target_sources("${target}" PRIVATE "${embed_srcfile}")
endfunction()
+macro(include_if_exists path)
+ if(EXISTS "${path}")
+ include("${path}")
+ endif()
+endmacro(include_if_exists)