Skip to content

Commit d38c05c

Browse files
committed
Merge branch 'refactor/ldgen_library_deps' into 'master'
refactor(cmake): collect libraries from ELF, don't depend on idf_build_target Closes IDF-10772 See merge request espressif/esp-idf!34520
2 parents 762cf05 + 431ee5c commit d38c05c

File tree

3 files changed

+129
-15
lines changed

3 files changed

+129
-15
lines changed

tools/cmake/build.cmake

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -712,6 +712,9 @@ endmacro()
712712
# files used for linking, targets which should execute before creating the specified executable,
713713
# generating additional binary files, generating files related to flashing, etc.)
714714
function(idf_build_executable elf)
715+
# Create a target for linker script generation for this executable
716+
__ldgen_create_target(${elf})
717+
715718
# Set additional link flags for the executable
716719
idf_build_get_property(link_options LINK_OPTIONS)
717720
set_property(TARGET ${elf} APPEND PROPERTY LINK_OPTIONS "${link_options}")

tools/cmake/component.cmake

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -494,7 +494,6 @@ function(idf_component_register)
494494
__component_add_include_dirs(${component_lib} "${__PRIV_INCLUDE_DIRS}" PRIVATE)
495495
__component_add_include_dirs(${component_lib} "${config_dir}" PUBLIC)
496496
set_target_properties(${component_lib} PROPERTIES OUTPUT_NAME ${COMPONENT_NAME} LINKER_LANGUAGE C)
497-
__ldgen_add_component(${component_lib})
498497
else()
499498
add_library(${component_lib} INTERFACE)
500499
__component_set_property(${component_target} COMPONENT_TYPE CONFIG_ONLY)

tools/cmake/ldgen.cmake

Lines changed: 126 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -15,25 +15,134 @@ function(__ldgen_add_fragment_files fragment_files)
1515
idf_build_set_property(__LDGEN_FRAGMENT_FILES "${_fragment_files}" APPEND)
1616
endfunction()
1717

18-
# __ldgen_add_component
19-
#
20-
# Generate sections info for specified target to be used in linker script generation
21-
function(__ldgen_add_component component_lib)
22-
idf_build_set_property(__LDGEN_LIBRARIES "$<TARGET_FILE:${component_lib}>" APPEND)
23-
idf_build_set_property(__LDGEN_DEPENDS ${component_lib} APPEND)
24-
endfunction()
25-
2618
# __ldgen_process_template
2719
#
2820
# Passes a linker script template to the linker script generation tool for
29-
# processing
21+
# processing. This internal function is called from the public "target_linker_script" function.
22+
# It simply records the template and output file names. The actual linker script generation
23+
# target is created later by the __ldgen_create_target function.
24+
#
3025
function(__ldgen_process_template template output)
26+
idf_build_set_property(__LDGEN_TEMPLATES "${template}" APPEND)
27+
idf_build_set_property(__LDGEN_OUTPUTS "${output}" APPEND)
28+
endfunction()
29+
30+
# __ldgen_get_lib_deps_of_target
31+
#
32+
# Helper function to get the list of libraries that a target depends on,
33+
# recursively, and append it to the out_list. The 'target' argument is
34+
# typically an executable.
35+
#
36+
function(__ldgen_get_lib_deps_of_target target out_list_var)
37+
set(out_list ${${out_list_var}})
38+
if(target IN_LIST out_list)
39+
# This target is already in the list, meaning that it has been
40+
# processed already. Bail out.
41+
return()
42+
endif()
43+
44+
# The target is not in the list yet. Add it, and then add all its dependencies.
45+
list(APPEND out_list ${target})
46+
set(${out_list_var} ${out_list} PARENT_SCOPE)
47+
48+
get_target_property(target_deps ${target} LINK_LIBRARIES)
49+
if(NOT target_deps)
50+
# This target has no dependencies, nothing to do.
51+
return()
52+
endif()
53+
54+
foreach(dep ${target_deps})
55+
if(dep IN_LIST out_list)
56+
# This dependency has already been processed, skip it.
57+
continue()
58+
endif()
59+
60+
if(NOT TARGET ${dep})
61+
# LINK_LIBRARIES may contain various non-library-related linker flags
62+
# (-u, -L, -l, etc.), skip them.
63+
if(dep MATCHES "^-" OR dep MATCHES "^\:\:")
64+
continue()
65+
endif()
66+
67+
# If the dependency is not a target, it may be a library. Add it to the list.
68+
list(APPEND out_list ${dep})
69+
else()
70+
# Recursively add the dependencies of this target.
71+
__ldgen_get_lib_deps_of_target(${dep} out_list)
72+
endif()
73+
endforeach()
74+
75+
set(${out_list_var} ${out_list} PARENT_SCOPE)
76+
endfunction()
77+
78+
79+
# __ldgen_create_target
80+
#
81+
# Internal function which creates a custom target for the linker script generation tool.
82+
# This function is called from the public idf_build_executable function.
83+
#
84+
function(__ldgen_create_target exe_target)
3185
idf_build_get_property(idf_target IDF_TARGET)
3286
idf_build_get_property(idf_path IDF_PATH)
87+
idf_build_get_property(templates __LDGEN_TEMPLATES)
88+
idf_build_get_property(outputs __LDGEN_OUTPUTS)
89+
90+
if(NOT templates)
91+
# No templates were passed to ldgen, nothing to do.
92+
return()
93+
endif()
94+
95+
list(LENGTH templates num_templates)
96+
if(NOT num_templates EQUAL 1)
97+
# This limitation can be removed, if necessary, by looping over the list of templates
98+
# and creating a target for each one of them.
99+
# However, this is not needed in IDF for now, hence the simpler implementation.
100+
message(FATAL_ERROR "Only one template file can be passed to __ldgen_process, "
101+
"got ${num_templates}: ${templates}")
102+
endif()
103+
104+
list(GET templates 0 template)
105+
list(GET outputs 0 output)
106+
107+
# Collect all the libraries that the executable depends on, recursively.
108+
# This is needed to pass the list of libraries to the linker script generator tool.
109+
set(ldgen_libraries)
110+
__ldgen_get_lib_deps_of_target(${exe_target} ldgen_libraries)
111+
list(REMOVE_ITEM ldgen_libraries ${exe_target})
112+
set(ldgen_deps)
113+
foreach(lib ${ldgen_libraries})
114+
if(TARGET ${lib})
115+
get_target_property(lib_type ${lib} TYPE)
116+
if(lib_type STREQUAL "INTERFACE_LIBRARY")
117+
continue()
118+
endif()
119+
list(APPEND ldgen_libraries_expr "$<TARGET_FILE:${lib}>")
120+
list(APPEND ldgen_deps ${lib})
121+
else()
122+
# Here we have two cases:
123+
#
124+
# 1. ${lib} is actually a target, but the target is outside the current scope.
125+
# This is the case for imported library targets (such as those added by
126+
# add_prebuilt_library), since by default imported targets are not
127+
# visible outside the directory where they are defined, unless they are
128+
# marked as GLOBAL.
129+
# This case covers many (but not all) of IDF's prebuilt libraries.
130+
#
131+
# 2. ${lib} is the name of a library, which the linker can find in its built-in
132+
# or specified search paths.
133+
# This is the case for toolchain libraries (m, c, gcc, stdc++) as well
134+
# as for some prebuilt libraries which have been added using `-lname -Lpath`
135+
# style flags.
136+
#
137+
# If we can successfully find the absolute path of each such library, this
138+
# will allow us to pass them to ldgen, enabling us to place functions from
139+
# prebuilt libraries to specific sections in the linker script (IDF-12049)
140+
endif()
141+
endforeach()
142+
list(JOIN ldgen_libraries_expr "\n" ldgen_libraries_str)
33143

34144
idf_build_get_property(build_dir BUILD_DIR)
35-
idf_build_get_property(ldgen_libraries __LDGEN_LIBRARIES GENERATOR_EXPRESSION)
36-
file(GENERATE OUTPUT ${build_dir}/ldgen_libraries.in CONTENT $<JOIN:${ldgen_libraries},\n>)
145+
file(WRITE ${build_dir}/ldgen_libraries.in "${ldgen_libraries_str}")
37146
file(GENERATE OUTPUT ${build_dir}/ldgen_libraries INPUT ${build_dir}/ldgen_libraries.in)
38147

39148
set_property(DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}"
@@ -42,7 +151,7 @@ function(__ldgen_process_template template output)
42151
"${build_dir}/ldgen_libraries")
43152

44153
idf_build_get_property(ldgen_fragment_files __LDGEN_FRAGMENT_FILES GENERATOR_EXPRESSION)
45-
idf_build_get_property(ldgen_depends __LDGEN_DEPENDS GENERATOR_EXPRESSION)
154+
46155
# Create command to invoke the linker script generator tool.
47156
idf_build_get_property(sdkconfig SDKCONFIG)
48157
idf_build_get_property(root_kconfig __ROOT_KCONFIG)
@@ -69,12 +178,15 @@ function(__ldgen_process_template template output)
69178
--libraries-file "${build_dir}/ldgen_libraries"
70179
--objdump "${CMAKE_OBJDUMP}"
71180
${ldgen_check}
72-
DEPENDS ${template} ${ldgen_fragment_files} ${ldgen_depends} ${SDKCONFIG}
181+
DEPENDS ${template} ${ldgen_fragment_files} ${ldgen_deps} ${SDKCONFIG}
73182
VERBATIM
74183
)
75184

185+
# The executable depends on the output of the ldgen command.
76186
get_filename_component(_name ${output} NAME)
77187
add_custom_target(__ldgen_output_${_name} DEPENDS ${output})
78-
add_dependencies(__idf_build_target __ldgen_output_${_name})
188+
add_dependencies(${exe_target} __ldgen_output_${_name})
189+
190+
# Add the output of the ldgen command to the list of link dependencies.
79191
idf_build_set_property(__LINK_DEPENDS ${output} APPEND)
80192
endfunction()

0 commit comments

Comments
 (0)