diff --git a/tools/cmakev2/build.cmake b/tools/cmakev2/build.cmake index 4245aab22a..f6fb3073d7 100644 --- a/tools/cmakev2/build.cmake +++ b/tools/cmakev2/build.cmake @@ -280,22 +280,105 @@ function(idf_build_library) target_link_libraries("${ARG_INTERFACE}" INTERFACE "${component_interface}") endforeach() - # Identify the components linked to the library by obtaining all targets - # that are transitively linked to the library and mapping these targets to - # components. + # Get all targets transitively linked to the library interface target. __get_target_dependencies(TARGET "${ARG_INTERFACE}" OUTPUT dependencies) - set(dep_component_interfaces) + + # Identify the components linked to the library by looking at all targets + # that are transitively linked to the library and mapping these targets to + # components. Store the linked component interfaces in + # LIBRARY_COMPONENTS_LINKED property. + set(component_interfaces_linked) foreach(dep IN LISTS dependencies) __get_component_interface(COMPONENT "${dep}" OUTPUT component_interface) if(NOT component_interface) continue() endif() - if("${component_interface}" IN_LIST dep_component_interfaces) + if("${component_interface}" IN_LIST component_interfaces_linked) continue() endif() - list(APPEND dep_component_interfaces "${component_interface}") + list(APPEND component_interfaces_linked "${component_interface}") idf_component_get_property(component_name "${component_interface}" COMPONENT_NAME) idf_library_set_property("${ARG_INTERFACE}" LIBRARY_COMPONENTS_LINKED "${component_name}" APPEND) endforeach() + + # Collect linker fragment files from all components linked to the library + # interface and store them in the __LDGEN_FRAGMENT_FILES files. This + # property is used by ldgen to generate template-based linker scripts. + foreach(component_interface IN LISTS component_interfaces_linked) + idf_component_get_property(component_ldfragments "${component_interface}" LDFRAGMENTS) + idf_component_get_property(component_directory "${component_interface}" COMPONENT_DIR) + __get_absolute_paths(PATHS "${component_ldfragments}" + BASE_DIR "${component_directory}" + OUTPUT ldfragments) + idf_library_set_property("${ARG_INTERFACE}" __LDGEN_FRAGMENT_FILES "${ldfragments}" APPEND) + endforeach() + + # Collect archive files from all targets linked to the library interface + # and store them in the __LDGEN_DEPENDS and __LDGEN_LIBRARIES library + # properties. These properties are used by ldgen to generate linker scripts + # from templates. The __LDGEN_LIBRARIES property contains a list of + # TARGET_FILE generator expressions for archive files. + foreach(dep IN LISTS dependencies) + if(NOT TARGET "${dep}") + continue() + endif() + + get_target_property(type "${dep}" TYPE) + if("${type}" STREQUAL "INTERFACE_LIBRARY") + continue() + endif() + + idf_library_get_property(ldgen_depends "${ARG_INTERFACE}" __LDGEN_DEPENDS) + if(NOT "${dep}" IN_LIST ldgen_depends) + idf_library_set_property("${ARG_INTERFACE}" __LDGEN_LIBRARIES "$" APPEND) + idf_library_set_property("${ARG_INTERFACE}" __LDGEN_DEPENDS ${dep} APPEND) + endif() + endforeach() + + # Create a sanitized library interface name that can be used as a suffix + # for files and targets specific to the library. + string(REGEX REPLACE "[^A-Za-z0-9_]" "_" suffix "_${ARG_INTERFACE}") + + foreach(component_interface IN LISTS component_interfaces_linked) + # Generate linker scripts from templates. + # LINKER_SCRIPTS_TEMPLATE and LINKER_SCRIPTS_GENERATED are parallel + # lists. The first holds the template linker script path, and the + # second holds the generated linker script path. + idf_component_get_property(template_scripts "${component_interface}" LINKER_SCRIPTS_TEMPLATE) + idf_component_get_property(generated_scripts "${component_interface}" LINKER_SCRIPTS_GENERATED) + + set(scripts) + foreach(template script IN ZIP_LISTS template_scripts generated_scripts) + set(script "${script}${suffix}") + __ldgen_process_template(LIBRARY "${ARG_INTERFACE}" + TEMPLATE "${template}" + SUFFIX "${suffix}" + OUTPUT "${script}") + list(APPEND scripts "${script}") + # Add a custom target for the generated script and include it as a + # dependency for the library interface to ensure the script is + # generated before linking. + get_filename_component(basename "${script}" NAME) + string(REGEX REPLACE "[^A-Za-z0-9_]" "_" basename "${basename}") + add_custom_target(__ldgen_output_${basename} DEPENDS "${script}") + add_dependencies("${ARG_INTERFACE}" __ldgen_output_${basename}) + endforeach() + + # Add linker scripts. + idf_component_get_property(scripts_static "${component_interface}" LINKER_SCRIPTS_STATIC) + list(PREPEND scripts "${scripts_static}") + foreach(script IN LISTS scripts) + get_filename_component(script_dir "${script}" DIRECTORY) + get_filename_component(script_name "${script}" NAME) + # Add linker script directory to the linker search path. + target_link_directories("${ARG_INTERFACE}" INTERFACE "${script_dir}") + # Add linker script to link. Regarding the usage of SHELL, see + # https://cmake.org/cmake/help/latest/command/target_link_options.html#option-de-duplication + target_link_options("${ARG_INTERFACE}" INTERFACE "SHELL:-T ${script_name}") + # Add the linker script as a dependency to ensure the executable is + # re-linked if the script changes. + set_property(TARGET "${ARG_INTERFACE}" APPEND PROPERTY INTERFACE_LINK_DEPENDS "${script}") + endforeach() + endforeach() endfunction() diff --git a/tools/cmakev2/compat.cmake b/tools/cmakev2/compat.cmake index 55cd384f44..f11ec3c366 100644 --- a/tools/cmakev2/compat.cmake +++ b/tools/cmakev2/compat.cmake @@ -131,11 +131,47 @@ endfunction() .. code-block:: cmake - target_linker_script( ...) + target_linker_script( ... + [PROCESS ]) + :target[in]: The component target to which linker script files should be + added. + :deptype[in]: This option is obsolete and maintained solely for backward + compatibility. + :scriptfile[in]: Specifies the linker script file ``scriptfile`` to be added + to the link. Multiple files can be specified. + :PROCESS[in,opt]: Specifies the ``output`` linker script, which is generated + from the ``linkerscript`` template. The ``linkerscript`` + is processed with ldgen to produce the ``output``. + + This function adds one or more linker scripts to the specified component + target, incorporating the linker script into the linking process. + + If the ``PROCESS`` option is specified, the last ``scriptfile`` listed is + processed using the ldgen command, and the generated ``output`` file is used + as the linker script during the linking process. This implies that with the + ``PROCESS`` option, it is logical to provide only a single ``scriptfile`` as + a template. #]] function(target_linker_script target deptype scriptfiles) - # FIXME: This is just a placeholder without implementation. + # The linker script files, templates, and their output filenames are stored + # only as component properties. The script files are generated and added to + # the library link interface in the idf_build_library function. + set(options) + set(one_value PROCESS) + set(multi_value) + cmake_parse_arguments(ARG "${options}" "${one_value}" "${multi_value}" ${ARGN}) + foreach(scriptfile ${scriptfiles}) + get_filename_component(scriptfile "${scriptfile}" ABSOLUTE) + idf_msg("Adding linker script ${scriptfile}") + if(ARG_PROCESS) + get_filename_component(output "${ARG_PROCESS}" ABSOLUTE) + idf_component_set_property("${target}" LINKER_SCRIPTS_TEMPLATE "${scriptfile}" APPEND) + idf_component_set_property("${target}" LINKER_SCRIPTS_GENERATED "${output}" APPEND) + else() + idf_component_set_property("${target}" LINKER_SCRIPTS_STATIC ${scriptfile} APPEND) + endif() + endforeach() endfunction() #[[api diff --git a/tools/cmakev2/component.cmake b/tools/cmakev2/component.cmake index 27e9481149..fcf46680f9 100644 --- a/tools/cmakev2/component.cmake +++ b/tools/cmakev2/component.cmake @@ -629,12 +629,6 @@ function(idf_component_include name) target_add_binary_data(${COMPONENT_TARGET} "${file}" "TEXT") endforeach() - idf_component_get_property(ldfragments "${component_name}" LDFRAGMENTS) - if(ldfragments) - # FIXME: Enable this once the ldgen integration is implemented. - # ldgen_add_fragment_files("${ldfragments}") - endif() - # Components for cmakev1 use the idf_component_register call and are # managed in cmakev2 through a shim. This shim sets the COMPONENT_FORMAT # property to CMAKEV1 and applies global compilation options and diff --git a/tools/cmakev2/idf.cmake b/tools/cmakev2/idf.cmake index 1e1f954992..88630d65f6 100644 --- a/tools/cmakev2/idf.cmake +++ b/tools/cmakev2/idf.cmake @@ -22,6 +22,7 @@ include(build) include(kconfig) include(project) include(compat) +include(ldgen) include(GetGitRevisionDescription) # For backward compatibility, since externalproject_add is used by # project_include.cmake in the bootloader component. The ExternalProject diff --git a/tools/cmakev2/ldgen.cmake b/tools/cmakev2/ldgen.cmake new file mode 100644 index 0000000000..07468a5144 --- /dev/null +++ b/tools/cmakev2/ldgen.cmake @@ -0,0 +1,93 @@ +include_guard(GLOBAL) + + set(one_value LIBRARY TEMPLATE SUFFIX OUTPUT) +#[[ + __ldgen_process_template(LIBRARY + TEMPLATE