llext: use CMake shared library support on Xtensa
This change reworks the Xtensa support in llext to use CMake's native
shared library support, instead of manually running "gcc -shared".
This change minimizes the differences in llext handling by defining
appropriate CMake targets for the different architectures.
Signed-off-by: Luca Burelli <l.burelli@arduino.cc>
diff --git a/cmake/compiler/gcc/target_xtensa.cmake b/cmake/compiler/gcc/target_xtensa.cmake
index 1778304..5321204 100644
--- a/cmake/compiler/gcc/target_xtensa.cmake
+++ b/cmake/compiler/gcc/target_xtensa.cmake
@@ -17,5 +17,4 @@
-fPIC
-nostdlib
-nodefaultlibs
- -shared
)
diff --git a/cmake/modules/extensions.cmake b/cmake/modules/extensions.cmake
index 1444229..8a3b065 100644
--- a/cmake/modules/extensions.cmake
+++ b/cmake/modules/extensions.cmake
@@ -5099,6 +5099,12 @@
# loadable extensions (llexts).
#
+# Usage:
+# add_llext_target(<target_name>
+# OUTPUT <output_file>
+# SOURCES <source_file>
+# )
+#
# Add a custom target that compiles a single source file to a .llext file.
#
# Output and source files must be specified using the OUTPUT and SOURCES
@@ -5112,6 +5118,14 @@
# The C_FLAGS argument can be used to pass additional compiler flags to the
# compilation of this particular llext.
#
+# The following custom properties of <target_name> are defined and can be
+# retrieved using the get_target_property() function:
+#
+# - lib_target Target name for the source compilation and/or link step.
+# - lib_output The binary file resulting from compilation and/or
+# linking steps.
+# - pkg_output The final .llext file.
+#
# Example usage:
# add_llext_target(hello_world
# OUTPUT ${PROJECT_BINARY_DIR}/hello_world.llext
@@ -5131,10 +5145,8 @@
message(FATAL_ERROR "add_llext_target: CONFIG_LLEXT must be enabled")
endif()
- # Output file must be provided
- if(NOT LLEXT_OUTPUT)
- message(FATAL_ERROR "add_llext_target: OUTPUT argument must be provided")
- endif()
+ # Source and output files must be provided
+ zephyr_check_arguments_required_all("add_llext_target" LLEXT OUTPUT SOURCES)
# Source list length must currently be 1
list(LENGTH LLEXT_SOURCES source_count)
@@ -5142,15 +5154,8 @@
message(FATAL_ERROR "add_llext_target: only one source file is supported")
endif()
- set(output_file ${LLEXT_OUTPUT})
+ set(llext_pkg_output ${LLEXT_OUTPUT})
set(source_file ${LLEXT_SOURCES})
- get_filename_component(output_name ${output_file} NAME)
-
- # Add user-visible target and dependency
- add_custom_target(${target_name}
- COMMENT "Compiling ${output_name}"
- DEPENDS ${output_file}
- )
# Convert the LLEXT_REMOVE_FLAGS list to a regular expression, and use it to
# filter out these flags from the Zephyr target settings
@@ -5166,62 +5171,86 @@
"$<FILTER:${zephyr_flags},EXCLUDE,${llext_remove_flags_regexp}>"
)
- # Compile the source file to an object file using current Zephyr settings
- # but a different set of flags
- add_library(${target_name}_lib OBJECT ${source_file})
- target_compile_definitions(${target_name}_lib PRIVATE
+ # Compile the source file using current Zephyr settings but a different
+ # set of flags.
+ # This is currently arch-specific since the ARM loader for .llext files
+ # expects object file format, while the Xtensa one uses shared libraries.
+ set(llext_lib_target ${target_name}_llext_lib)
+ if(CONFIG_ARM)
+
+ # Create an object library to compile the source file
+ add_library(${llext_lib_target} OBJECT ${source_file})
+ set(llext_lib_output $<TARGET_OBJECTS:${llext_lib_target}>)
+
+ elseif(CONFIG_XTENSA)
+
+ # Create a shared library
+ add_library(${llext_lib_target} SHARED ${source_file})
+ set(llext_lib_output $<TARGET_FILE:${llext_lib_target}>)
+
+ # Add the llext flags to the linking step as well
+ target_link_options(${llext_lib_target} PRIVATE
+ ${LLEXT_APPEND_FLAGS}
+ )
+
+ endif()
+
+ target_compile_definitions(${llext_lib_target} PRIVATE
$<TARGET_PROPERTY:zephyr_interface,INTERFACE_COMPILE_DEFINITIONS>
)
- target_compile_options(${target_name}_lib PRIVATE
+ target_compile_options(${llext_lib_target} PRIVATE
${zephyr_filtered_flags}
${LLEXT_APPEND_FLAGS}
${LLEXT_C_FLAGS}
)
- target_include_directories(${target_name}_lib PRIVATE
+ target_include_directories(${llext_lib_target} PRIVATE
$<TARGET_PROPERTY:zephyr_interface,INTERFACE_INCLUDE_DIRECTORIES>
)
- target_include_directories(${target_name}_lib SYSTEM PUBLIC
+ target_include_directories(${llext_lib_target} SYSTEM PUBLIC
$<TARGET_PROPERTY:zephyr_interface,INTERFACE_SYSTEM_INCLUDE_DIRECTORIES>
)
- add_dependencies(${target_name}_lib
+ add_dependencies(${llext_lib_target}
zephyr_interface
zephyr_generated_headers
)
- # Arch-specific conversion of the object file to an llext
+ # Arch-specific packaging of the built binary file into an .llext file
if(CONFIG_ARM)
- # No conversion required, simply copy the object file
+ # No packaging required, simply copy the object file
add_custom_command(
- OUTPUT ${output_file}
- COMMAND ${CMAKE_COMMAND} -E copy $<TARGET_OBJECTS:${target_name}_lib> ${output_file}
- DEPENDS ${target_name}_lib $<TARGET_OBJECTS:${target_name}_lib>
+ OUTPUT ${llext_pkg_output}
+ COMMAND ${CMAKE_COMMAND} -E copy ${llext_lib_output} ${llext_pkg_output}
+ DEPENDS ${llext_lib_target} ${llext_lib_output}
)
elseif(CONFIG_XTENSA)
- # Generate an intermediate file name
- get_filename_component(output_dir ${output_file} DIRECTORY)
- get_filename_component(output_name_we ${output_file} NAME_WE)
- set(pre_output_file ${output_dir}/${output_name_we}.pre.llext)
-
- # Need to convert the object file to a shared library, then strip some sections
+ # Need to strip the shared library of some sections
add_custom_command(
- OUTPUT ${output_file}
- BYPRODUCTS ${pre_output_file}
- COMMAND ${CMAKE_C_COMPILER} ${LLEXT_APPEND_FLAGS}
- -o ${pre_output_file}
- $<TARGET_OBJECTS:${target_name}_lib>
+ OUTPUT ${llext_pkg_output}
COMMAND $<TARGET_PROPERTY:bintools,strip_command>
$<TARGET_PROPERTY:bintools,strip_flag>
$<TARGET_PROPERTY:bintools,strip_flag_remove_section>.xt.*
- $<TARGET_PROPERTY:bintools,strip_flag_infile>${pre_output_file}
- $<TARGET_PROPERTY:bintools,strip_flag_outfile>${output_file}
+ $<TARGET_PROPERTY:bintools,strip_flag_infile>${llext_lib_output}
+ $<TARGET_PROPERTY:bintools,strip_flag_outfile>${llext_pkg_output}
$<TARGET_PROPERTY:bintools,strip_flag_final>
- DEPENDS ${target_name}_lib $<TARGET_OBJECTS:${target_name}_lib>
+ DEPENDS ${llext_lib_target} ${llext_lib_output}
)
else()
message(FATAL_ERROR "add_llext_target: unsupported architecture")
endif()
+
+ # Add user-visible target and dependency, and fill in properties
+ get_filename_component(output_name ${llext_pkg_output} NAME)
+ add_custom_target(${target_name}
+ COMMENT "Generating ${output_name}"
+ DEPENDS ${llext_pkg_output}
+ )
+ set_target_properties(${target_name} PROPERTIES
+ lib_target ${llext_lib_target}
+ lib_output ${llext_lib_output}
+ pkg_output ${llext_pkg_output}
+ )
endfunction()