diff --git a/components/ulp/CMakeLists_v2.txt b/components/ulp/CMakeLists_v2.txt index e728857e357..2b187bcf39d 100644 --- a/components/ulp/CMakeLists_v2.txt +++ b/components/ulp/CMakeLists_v2.txt @@ -181,19 +181,3 @@ elseif(ULP_TYPE STREQUAL "fsm") FLAGS "${ulp_ld_flags}") target_link_options(${COMPONENT_LIB} INTERFACE "SHELL:-u entry") endif() - -function(__ulp_configure_executable ulp_app_name) - # Generate the files consumed by the parent app: .bin payload plus symbol - # header/linker exports produced from the ULP ELF. - set(binary_target_args) - if(DEFINED ULP_VAR_PREFIX) - set(ulp_var_prefix "${ULP_VAR_PREFIX}") - if(ULP_PREFIX_APPEND_BIN_NAME) - string(MAKE_C_IDENTIFIER "${ulp_var_prefix}${ulp_app_name}_" ulp_var_prefix) - endif() - list(APPEND binary_target_args PREFIX ${ulp_var_prefix}) - endif() - ulp_add_build_binary_targets(${ulp_app_name} ${binary_target_args}) -endfunction() - -idf_component_register_build_event_callback(EVENT POST_ELF CALLBACK __ulp_configure_executable) diff --git a/components/ulp/cmake/IDFULPProject.cmake b/components/ulp/cmake/IDFULPProject.cmake index 8ad47778706..5c447ffb925 100644 --- a/components/ulp/cmake/IDFULPProject.cmake +++ b/components/ulp/cmake/IDFULPProject.cmake @@ -1,8 +1,7 @@ -if(IDF_BUILD_V2 AND COMMAND idf_project_init) - include(${CMAKE_CURRENT_LIST_DIR}/IDFULPProjectv2.cmake) - return() -endif() - +# This is the CMake v1 (legacy) ULP child entry point, used by ulp_embed_binary. +# CMake v2 full-subproject builds include components/ulp/cmake/ulp_project.cmake +# instead. +# # Legacy ULP child projects are plain CMake projects, so the parent-provided # sdkconfig.cmake has to be loaded here before the common ULP helpers inspect # CONFIG_* values. CMake v1 callers pass this path explicitly. diff --git a/components/ulp/cmake/IDFULPProjectv2.cmake b/components/ulp/cmake/IDFULPProjectv2.cmake deleted file mode 100644 index 77fded22a23..00000000000 --- a/components/ulp/cmake/IDFULPProjectv2.cmake +++ /dev/null @@ -1,87 +0,0 @@ -include(${CMAKE_CURRENT_LIST_DIR}/IDFULPProjectCommon.cmake) - -if(NOT COMMAND idf_project_init) - message(FATAL_ERROR "CMake v2 ULP projects must include tools/cmakev2/idf.cmake " - "before including IDFULPProject.") -endif() - -# Full IDF-style ULP projects include sdkconfig.cmake again during -# idf_project_init(). Re-including it is harmless because the generated file -# only assigns CONFIG_* variables, and this early include keeps lower-level ULP -# helper APIs working before idf_project_init(). -include(${SDKCONFIG_CMAKE}) - -ulp_detect_build_type() - -get_property(enabled_languages GLOBAL PROPERTY ENABLED_LANGUAGES) -if(NOT ASM IN_LIST enabled_languages) - enable_language(ASM) -endif() -ulp_apply_build_type_options() - -function(ulp_prepare_build) - idf_build_get_property(ulp_build_prepared __ULP_BUILD_PREPARED) - if(ulp_build_prepared) - return() - endif() - - if(BUILD_FSM) - check_expected_tool_version("esp32ulp-elf" ${CMAKE_ASM_COMPILER}) - # FSM links with esp32ulp-elf-ld directly, not through GCC, so avoid - # GCC-driver link grouping flags from the generic IDF executable helper. - idf_build_set_property(LINKER_TYPE ULP_FSM) - endif() - - # ULP projects use their own toolchain files and component-provided options, - # so discard compile/link options added for normal app builds. - foreach(property IN ITEMS - COMPILE_OPTIONS - C_COMPILE_OPTIONS - CXX_COMPILE_OPTIONS - ASM_COMPILE_OPTIONS - COMPILE_DEFINITIONS - LINK_OPTIONS) - idf_build_set_property(${property} "") - endforeach() - - # Do not apply the default app component closure to ULP child projects. - # ULP components should contribute dependencies explicitly. - idf_build_set_property(__COMPONENT_REQUIRES_COMMON "") - idf_build_set_property(__COMMON_COMPONENT_INTERFACES "") - idf_build_set_property(__COMMON_COMPONENTS_INITIALIZED YES) - - # Some helpers can be called in sequence by custom ULP projects; keep this - # setup idempotent within a child configure. - idf_build_set_property(__ULP_BUILD_PREPARED YES) -endfunction() - -function(__ulp_project_default) - set(ulp_app_name "${PROJECT_NAME}") - - set(executable_args COMPONENTS main) - if(NOT BUILD_FSM) - list(APPEND executable_args MAPFILE_TARGET ${ulp_app_name}_mapfile) - endif() - - idf_build_executable(${ulp_app_name} ${executable_args}) - - idf_build_generate_metadata(EXECUTABLE ${ulp_app_name}) - - if(TARGET "${ulp_app_name}_mapfile") - idf_create_size_report("${ulp_app_name}_mapfile" - TARGET size) - endif() - - idf_build_generate_depgraph("${ulp_app_name}") -endfunction() - -macro(ulp_project_default) - idf_project_init() - ulp_prepare_build() - - # Use DEFERRED optional-requires resolution only when this will be the sole - # library being built. - idf_build_set_property(IDF_COMPONENT_OPTIONAL_REQUIRES_MODE DEFERRED) - - __ulp_project_default() -endmacro() diff --git a/components/ulp/cmake/ulp_project.cmake b/components/ulp/cmake/ulp_project.cmake new file mode 100644 index 00000000000..072d46522f7 --- /dev/null +++ b/components/ulp/cmake/ulp_project.cmake @@ -0,0 +1,188 @@ +# ULP subproject entry point for CMake v2 full-subproject builds. +# +# A ULP child project includes this file instead of tools/cmakev2/idf.cmake +# directly. It pulls in the cmakev2 build system and layers the ULP subproject +# API on top: ulp_project_init, ulp_build_executable, ulp_build_binary and +# ulp_project_default, the ULP analogs of idf_project_init, +# idf_build_executable, idf_build_binary and idf_project_default. + +include(${IDF_PATH}/tools/cmakev2/idf.cmake) +include(${CMAKE_CURRENT_LIST_DIR}/IDFULPProjectCommon.cmake) + +# Reset the compile/link state inherited from the parent app build and select +# ULP-specific build behavior. Idempotent within a child configure. +function(__ulp_prepare_build) + idf_build_get_property(ulp_build_prepared __ULP_BUILD_PREPARED) + if(ulp_build_prepared) + return() + endif() + + if(BUILD_FSM) + check_expected_tool_version("esp32ulp-elf" ${CMAKE_ASM_COMPILER}) + # FSM links with esp32ulp-elf-ld directly, not through GCC, so avoid + # GCC-driver link grouping flags from the generic executable helper. + idf_build_set_property(LINKER_TYPE ULP_FSM) + endif() + + # ULP projects use their own toolchain files and component-provided options, + # so discard compile/link options added for normal app builds. + foreach(property IN ITEMS + COMPILE_OPTIONS + C_COMPILE_OPTIONS + CXX_COMPILE_OPTIONS + ASM_COMPILE_OPTIONS + COMPILE_DEFINITIONS + LINK_OPTIONS) + idf_build_set_property(${property} "") + endforeach() + + # Do not apply the default app component closure to ULP child projects. + # ULP components should contribute dependencies explicitly. + idf_build_set_property(__COMPONENT_REQUIRES_COMMON "") + idf_build_set_property(__COMMON_COMPONENT_INTERFACES "") + idf_build_set_property(__COMMON_COMPONENTS_INITIALIZED YES) + + idf_build_set_property(__ULP_BUILD_PREPARED YES) +endfunction() + +#[[api +.. cmakev2:macro:: ulp_project_init + + .. code-block:: cmake + + ulp_project_init() + + Initialize a ULP full-subproject build. Calls :cmakev2:ref:`idf_project_init` + and then applies the ULP-specific setup: it detects the ULP type (RISC-V, LP + core or FSM) from the ``ULP_TYPE`` variable or the project configuration and + resets the compile and link options inherited from the parent application + build, so the ULP executable uses only its own toolchain and + component-provided options. + + This is the ULP analog of :cmakev2:ref:`idf_project_init` and is the entry + point for projects that build more than one ULP executable, followed by + :cmakev2:ref:`ulp_build_executable` and :cmakev2:ref:`ulp_build_binary` + calls. For the common single-executable case use + :cmakev2:ref:`ulp_project_default` instead. +#]] +macro(ulp_project_init) + idf_project_init() + ulp_detect_build_type() + ulp_apply_build_type_options() + __ulp_prepare_build() +endmacro() + +#[[api +.. cmakev2:function:: ulp_build_executable + + .. code-block:: cmake + + ulp_build_executable( + [COMPONENTS ...] + [MAPFILE_TARGET ]) + + *executable[in]* + + Name of the ULP executable target to create. + + Build a ULP executable. This is the ULP analog of + :cmakev2:ref:`idf_build_executable` and forwards its arguments to + :cmakev2:ref:`idf_build_executable`. Call :cmakev2:ref:`ulp_build_binary` + for each executable that should produce embeddable ULP artifacts. +#]] +function(ulp_build_executable executable) + if("PREFIX" IN_LIST ARGN) + message(FATAL_ERROR "Use ulp_build_binary(${executable} PREFIX ...) to set ULP symbol prefixes.") + endif() + + idf_build_executable(${executable} ${ARGN}) +endfunction() + +#[[api +.. cmakev2:function:: ulp_build_binary + + .. code-block:: cmake + + ulp_build_binary( + [PREFIX ]) + + *executable[in]* + + ULP executable target for which to generate embeddable artifacts. + + *PREFIX[in,opt]* + + Symbol name prefix for the generated symbol header and linker export + files. Defaults to ``ulp_``. Use a distinct prefix per executable when a + project builds several ULP binaries. + + Generate the ``.bin`` payload and symbol header/linker export files for a + ULP executable. This is the ULP analog of :cmakev2:ref:`idf_build_binary`. +#]] +function(ulp_build_binary executable) + cmake_parse_arguments(_ULP "" "PREFIX" "" ${ARGN}) + if(_ULP_UNPARSED_ARGUMENTS) + message(FATAL_ERROR "Unexpected arguments to ulp_build_binary(): ${_ULP_UNPARSED_ARGUMENTS}") + endif() + + if(DEFINED _ULP_PREFIX) + ulp_add_build_binary_targets(${executable} PREFIX "${_ULP_PREFIX}") + elseif(ULP_PREFIX_APPEND_BIN_NAME) + set(prefix "ulp_") + if(DEFINED ULP_VAR_PREFIX) + set(prefix "${ULP_VAR_PREFIX}") + endif() + string(MAKE_C_IDENTIFIER "${prefix}${executable}_" prefix) + ulp_add_build_binary_targets(${executable} PREFIX "${prefix}") + else() + ulp_add_build_binary_targets("${executable}") + endif() +endfunction() + +# Build the single default ULP executable from the main component. +function(__ulp_project_default) + set(ulp_app_name "${PROJECT_NAME}") + + set(executable_args COMPONENTS main) + set(binary_args) + if(NOT BUILD_FSM) + list(APPEND executable_args MAPFILE_TARGET ${ulp_app_name}_mapfile) + endif() + if(DEFINED ULP_VAR_PREFIX AND NOT "${ULP_VAR_PREFIX}" STREQUAL "") + list(APPEND binary_args PREFIX ${ULP_VAR_PREFIX}) + endif() + + ulp_build_executable(${ulp_app_name} ${executable_args}) + ulp_build_binary(${ulp_app_name} ${binary_args}) + + idf_build_generate_metadata(EXECUTABLE ${ulp_app_name}) + + if(TARGET "${ulp_app_name}_mapfile") + idf_create_size_report("${ulp_app_name}_mapfile" TARGET size) + endif() + + idf_build_generate_depgraph("${ulp_app_name}") +endfunction() + +#[[api +.. cmakev2:macro:: ulp_project_default + + .. code-block:: cmake + + ulp_project_default() + + Build a single ULP executable from the ``main`` component and its transitive + dependencies, the ULP analog of :cmakev2:ref:`idf_project_default`. Calls + :cmakev2:ref:`ulp_project_init`, then builds the executable (named after + ``PROJECT_NAME``) with :cmakev2:ref:`ulp_build_executable` and generates its + embeddable artifacts with :cmakev2:ref:`ulp_build_binary`. +#]] +macro(ulp_project_default) + ulp_project_init() + + # A single executable is built, so DEFERRED optional-requires resolution is + # safe and keeps the linked component set minimal. + idf_build_set_property(IDF_COMPONENT_OPTIONAL_REQUIRES_MODE DEFERRED) + + __ulp_project_default() +endmacro() diff --git a/components/ulp/project_include.cmake b/components/ulp/project_include.cmake index e349f76f7f0..0d68833185c 100644 --- a/components/ulp/project_include.cmake +++ b/components/ulp/project_include.cmake @@ -125,6 +125,10 @@ function(__setup_ulp_project app_name project_path prefix prefix_append_bin_name set(ulp_project_args) if(IDF_BUILD_V2) set(ulp_project_args + # The parent passes a superset of variables that different ULP + # subproject styles consume (e.g. ULP_VAR_PREFIX only by + # ulp_project_default, COMPONENT_INCLUDES only by hand-written + # subprojects). Do not warn about the ones a given child ignores. --no-warn-unused-cli -DIDF_DEFAULT_PROJECT_NAME=${app_name} -DIDF_BUILD_V2=y @@ -166,7 +170,9 @@ function(__setup_ulp_project app_name project_path prefix prefix_append_bin_name -DCOMPONENT_DIR=${COMPONENT_DIR} -DCOMPONENT_INCLUDES=$ -DSDKCONFIG_HEADER=${SDKCONFIG_HEADER} - -DSDKCONFIG_CMAKE=${SDKCONFIG_CMAKE}) + -DSDKCONFIG_CMAKE=${SDKCONFIG_CMAKE} + # The v1 ULP child resolves include(IDFULPProject) via the module path. + -DCMAKE_MODULE_PATH=${ulp_cmake_dir}) endif() externalproject_add(${app_name} @@ -183,7 +189,6 @@ function(__setup_ulp_project app_name project_path prefix prefix_append_bin_name -DIDF_TARGET=${idf_target} -DIDF_PATH=${idf_path} -DPYTHON=${python} - -DCMAKE_MODULE_PATH=${ulp_cmake_dir} ${extra_cmake_args} BUILD_COMMAND ${CMAKE_COMMAND} --build ${ulp_binary_dir} --target build BUILD_BYPRODUCTS ${ulp_artifacts} ${ulp_artifacts_extras} ${ulp_ps_sources} diff --git a/tools/cmakev2/build.cmake b/tools/cmakev2/build.cmake index 9847d22f3a8..f434adfd018 100644 --- a/tools/cmakev2/build.cmake +++ b/tools/cmakev2/build.cmake @@ -599,12 +599,14 @@ function(idf_build_library library) # seen before any section-placement script references them. foreach(script IN LISTS memory_scripts other_scripts) get_filename_component(script_dir "${script}" DIRECTORY) - # Add linker script directory to the linker search path. + # Add the linker script directory to the linker search path so INCLUDE + # directives inside a script can resolve sibling scripts by name. target_link_directories("${library}" INTERFACE "${script_dir}") - # Add linker script to link. Use the full path because direct ld - # resolves -T against only the -L directories that came before it, while - # CMake may emit link options before link directories. Keep -L above so - # linker scripts can still INCLUDE siblings by name. + # Add the linker script to the link by absolute path. Passing the full + # path (rather than a bare name resolved via -L) keeps this working for + # direct linker drivers such as the ULP FSM esp32ulp-elf-ld link, where + # the -L search directories follow the -T options on the command line + # and GNU ld therefore does not use them to locate the -T script. # Regarding the usage of SHELL, see # https://cmake.org/cmake/help/latest/command/target_link_options.html#option-de-duplication # Quote the path: SHELL: strings are re-split on spaces, so an unquoted diff --git a/tools/test_apps/system/ulp/full_subproject/combined/main/ulp_fsm/CMakeLists.txt b/tools/test_apps/system/ulp/full_subproject/combined/main/ulp_fsm/CMakeLists.txt index cfd6772517c..b48a7286f3e 100644 --- a/tools/test_apps/system/ulp/full_subproject/combined/main/ulp_fsm/CMakeLists.txt +++ b/tools/test_apps/system/ulp/full_subproject/combined/main/ulp_fsm/CMakeLists.txt @@ -1,8 +1,7 @@ cmake_minimum_required(VERSION 3.22) -include(${IDF_PATH}/tools/cmakev2/idf.cmake) +include(${IDF_PATH}/components/ulp/cmake/ulp_project.cmake) project(${IDF_DEFAULT_PROJECT_NAME} ASM) -include(IDFULPProject) ulp_project_default() diff --git a/tools/test_apps/system/ulp/full_subproject/combined/main/ulp_riscv/CMakeLists.txt b/tools/test_apps/system/ulp/full_subproject/combined/main/ulp_riscv/CMakeLists.txt index 2bf72312b31..ec8b058fc29 100644 --- a/tools/test_apps/system/ulp/full_subproject/combined/main/ulp_riscv/CMakeLists.txt +++ b/tools/test_apps/system/ulp/full_subproject/combined/main/ulp_riscv/CMakeLists.txt @@ -1,8 +1,7 @@ cmake_minimum_required(VERSION 3.22) -include(${IDF_PATH}/tools/cmakev2/idf.cmake) +include(${IDF_PATH}/components/ulp/cmake/ulp_project.cmake) project(${IDF_DEFAULT_PROJECT_NAME} C CXX ASM) -include(IDFULPProject) ulp_project_default() diff --git a/tools/test_apps/system/ulp/full_subproject/fsm/main/ulp/CMakeLists.txt b/tools/test_apps/system/ulp/full_subproject/fsm/main/ulp/CMakeLists.txt index cfd6772517c..b48a7286f3e 100644 --- a/tools/test_apps/system/ulp/full_subproject/fsm/main/ulp/CMakeLists.txt +++ b/tools/test_apps/system/ulp/full_subproject/fsm/main/ulp/CMakeLists.txt @@ -1,8 +1,7 @@ cmake_minimum_required(VERSION 3.22) -include(${IDF_PATH}/tools/cmakev2/idf.cmake) +include(${IDF_PATH}/components/ulp/cmake/ulp_project.cmake) project(${IDF_DEFAULT_PROJECT_NAME} ASM) -include(IDFULPProject) ulp_project_default() diff --git a/tools/test_apps/system/ulp/full_subproject/lp_core/main/ulp/CMakeLists.txt b/tools/test_apps/system/ulp/full_subproject/lp_core/main/ulp/CMakeLists.txt index 2bf72312b31..ec8b058fc29 100644 --- a/tools/test_apps/system/ulp/full_subproject/lp_core/main/ulp/CMakeLists.txt +++ b/tools/test_apps/system/ulp/full_subproject/lp_core/main/ulp/CMakeLists.txt @@ -1,8 +1,7 @@ cmake_minimum_required(VERSION 3.22) -include(${IDF_PATH}/tools/cmakev2/idf.cmake) +include(${IDF_PATH}/components/ulp/cmake/ulp_project.cmake) project(${IDF_DEFAULT_PROJECT_NAME} C CXX ASM) -include(IDFULPProject) ulp_project_default() diff --git a/tools/test_apps/system/ulp/full_subproject/multi_binary/README.md b/tools/test_apps/system/ulp/full_subproject/multi_binary/README.md index 0ed65956809..2468f274f13 100644 --- a/tools/test_apps/system/ulp/full_subproject/multi_binary/README.md +++ b/tools/test_apps/system/ulp/full_subproject/multi_binary/README.md @@ -3,11 +3,22 @@ # ULP LP-Core Multi-Binary Build System Example -This example demonstrates a CMake v2 ULP full subproject that builds multiple ULP binaries from one ULP child project. The ULP project uses the lower-level IDF build APIs instead of `ulp_project_default()`: it calls `idf_project_init()`, prepares the child build with `ulp_prepare_build()`, creates a shared ULP component library with `idf_build_library()`, and creates two ULP executables with `idf_build_executable()`. The `ulp` component generates the binary artifacts for each executable. +This example demonstrates a CMake v2 ULP full subproject that builds multiple +ULP binaries from one ULP child project. The ULP project uses the lower-level +ULP build APIs instead of `ulp_project_default()`: it calls +`ulp_project_init()`, creates two ULP executables with +`ulp_build_executable()`, and generates the binary artifacts for each +executable with `ulp_build_binary()`. -The main application embeds both generated ULP binaries, runs the first one to calculate an addition result, then loads and runs the second one to calculate a multiplication result. +The main application embeds both generated ULP binaries, runs the first one to +calculate an addition result, then loads and runs the second one to calculate a +multiplication result. -Both ULP binaries define a global variable named `result`. Because no explicit `PREFIX` is passed to `ulp_add_project()`, the build system gives each binary a binary-scoped default symbol prefix, so the parent app can read `ulp_add_result` and `ulp_multiply_result` without the generated linker exports colliding. +Both ULP binaries define a global variable named `result`. Because no explicit +`PREFIX` is passed to `ulp_add_project()`, the build system gives each binary a +binary-scoped default symbol prefix, so the parent app can read +`ulp_add_result` and `ulp_multiply_result` without the generated linker exports +colliding. ## Example Output diff --git a/tools/test_apps/system/ulp/full_subproject/multi_binary/main/ulp/CMakeLists.txt b/tools/test_apps/system/ulp/full_subproject/multi_binary/main/ulp/CMakeLists.txt index f3dd3db30d1..1df774c07fb 100644 --- a/tools/test_apps/system/ulp/full_subproject/multi_binary/main/ulp/CMakeLists.txt +++ b/tools/test_apps/system/ulp/full_subproject/multi_binary/main/ulp/CMakeLists.txt @@ -1,23 +1,17 @@ cmake_minimum_required(VERSION 3.22) -include(${IDF_PATH}/tools/cmakev2/idf.cmake) +include(${IDF_PATH}/components/ulp/cmake/ulp_project.cmake) project(${IDF_DEFAULT_PROJECT_NAME} C CXX ASM) -include(IDFULPProject) +ulp_project_init() -idf_project_init() -ulp_prepare_build() - -idf_build_library(ulp_shared_library - COMPONENTS ulp_shared) - -idf_build_executable(add - COMPONENTS ulp_add_main +ulp_build_executable(add + COMPONENTS ulp_add_main ulp_shared MAPFILE_TARGET add_mapfile) -target_link_libraries(add PRIVATE ulp_shared_library) +ulp_build_binary(add) -idf_build_executable(multiply - COMPONENTS ulp_multiply_main +ulp_build_executable(multiply + COMPONENTS ulp_multiply_main ulp_shared MAPFILE_TARGET multiply_mapfile) -target_link_libraries(multiply PRIVATE ulp_shared_library) +ulp_build_binary(multiply) diff --git a/tools/test_apps/system/ulp/full_subproject/riscv/main/ulp/CMakeLists.txt b/tools/test_apps/system/ulp/full_subproject/riscv/main/ulp/CMakeLists.txt index 2bf72312b31..ec8b058fc29 100644 --- a/tools/test_apps/system/ulp/full_subproject/riscv/main/ulp/CMakeLists.txt +++ b/tools/test_apps/system/ulp/full_subproject/riscv/main/ulp/CMakeLists.txt @@ -1,8 +1,7 @@ cmake_minimum_required(VERSION 3.22) -include(${IDF_PATH}/tools/cmakev2/idf.cmake) +include(${IDF_PATH}/components/ulp/cmake/ulp_project.cmake) project(${IDF_DEFAULT_PROJECT_NAME} C CXX ASM) -include(IDFULPProject) ulp_project_default()