-
Notifications
You must be signed in to change notification settings - Fork 6.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
cmake: add llext compilation module #67431
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
# SPDX-License-Identifier: Apache-2.0 | ||
|
||
# Flags not supported by llext linker | ||
# (regexps are supported and match whole word) | ||
set(LLEXT_REMOVE_FLAGS | ||
-fno-pic | ||
-fno-pie | ||
-ffunction-sections | ||
-fdata-sections | ||
-g.* | ||
-Os | ||
-mcpu=.* | ||
) | ||
|
||
# Flags to be added to llext code compilation | ||
set(LLEXT_APPEND_FLAGS | ||
-fPIC | ||
-nostdlib | ||
-nodefaultlibs | ||
-shared | ||
) |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -34,6 +34,7 @@ include(CheckCXXCompilerFlag) | |
# 5. Zephyr linker functions | ||
# 5.1. zephyr_linker* | ||
# 6 Function helper macros | ||
# 7 Linkable loadable extensions (llext) | ||
|
||
######################################################## | ||
# 1. Zephyr-aware extensions | ||
|
@@ -4786,3 +4787,138 @@ macro(zephyr_check_flags_exclusive function prefix) | |
) | ||
endif() | ||
endmacro() | ||
|
||
######################################################## | ||
# 7. Linkable loadable extensions (llext) | ||
######################################################## | ||
# | ||
# These functions simplify the creation and management of linkable | ||
# loadable extensions (llexts). | ||
# | ||
|
||
# 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 | ||
# arguments. Only one source file is currently supported. | ||
# | ||
# The llext code will be compiled with mostly the same C compiler flags used | ||
# in the Zephyr build, but with some important modifications. The list of | ||
# flags to remove and flags to append is controlled respectively by the | ||
# LLEXT_REMOVE_FLAGS and LLEXT_APPEND_FLAGS global variables. | ||
|
||
# The C_FLAGS argument can be used to pass additional compiler flags to the | ||
# compilation of this particular llext. | ||
# | ||
# Example usage: | ||
# add_llext_target(hello_world | ||
# OUTPUT ${PROJECT_BINARY_DIR}/hello_world.llext | ||
# SOURCES ${PROJECT_SOURCE_DIR}/src/llext/hello_world.c | ||
# C_FLAGS -Werror | ||
# ) | ||
# will compile the source file src/llext/hello_world.c to a file | ||
# ${PROJECT_BINARY_DIR}/hello_world.llext, adding -Werror to the compilation. | ||
# | ||
function(add_llext_target target_name) | ||
set(single_args OUTPUT) | ||
set(multi_args SOURCES;C_FLAGS) | ||
cmake_parse_arguments(PARSE_ARGV 1 LLEXT "${options}" "${single_args}" "${multi_args}") | ||
|
||
# Check that the llext subsystem is enabled for this build | ||
if (NOT CONFIG_LLEXT) | ||
message(FATAL_ERROR "add_llext_target: CONFIG_LLEXT must be enabled") | ||
endif() | ||
Comment on lines
+4827
to
+4829
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Just curious, what would be wrong with trying/wanting to generate an llext for a project that doesn't have LLEXT enabled. One needn't enable LLEXT in a project if they only care about having "snippets" of it built as llext, no? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Well, indeed you could compile only the llext, since there's no real technical barrier. However, the generated file would not work with the same Zephyr core with which it was built (no exported symbols or machinery to load it), so it looked like a config error in my eyes. Now that I look at it again, the converse is useful though - if you know what you are doing and purposefully disable Taking into account your other comment on dependencies ("why isn't a llext built if it's defined as part of a project?"), I think the most appropriate way to deal with all this should be:
This would result in an actual build error only when LLEXT is disabled and the output file is a required dependency (for example by @teburd, what do you think about this? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Sorry for the belated reply and - much worse - sorry for not being @teburd.
I probably (!) disagree with that? The main purpose of Kconfig is to turn software features on and off. But "llext" isn't really a software feature, it's more about HOW you want some of those features to be delivered: as a separate module or built-in? So CONFIG_LLEXT is more like a "dependency" of real features. Consider the Linux kernel, which many Of course there's a massive difference: the Linux kernel can very quickly and easily flip drivers that support between module and built-in, whereas So if you:
... then you have a configuration error and the build should 1) fail, 2) fail as soon as possible, not wait until compilation whether some symbol is used maybe, maybe not. In other words, if you turn off CONFIG_LLEXT then you MUST also turn off all features that depend on it - and maybe Kconfig can/should help with that but either way CMake should fail ASAP if you feed it an inconsistent configuration. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
In fact, llext is only the implementation enabling the "dynamic loading" feature. The HOW you want [features] to be delivered part is Given that discussion and the existence of both However, and this is when
OT: We need an RFC / implementation plan on llext pronto! 😅 . I'll try to draft one shortly and share it so we can all agree on the long term path. 👍 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Thank you so much for leading here again. Whatever are the use cases and intended Kconfig interactions, I think we all agree they cannot just be inferred from scattered comments in source and code reviews. Some high-level design documentation is required indeed. |
||
|
||
# Output file must be provided | ||
if(NOT LLEXT_OUTPUT) | ||
message(FATAL_ERROR "add_llext_target: OUTPUT argument must be provided") | ||
endif() | ||
|
||
# Source list length must currently be 1 | ||
list(LENGTH LLEXT_SOURCES source_count) | ||
if(NOT source_count EQUAL 1) | ||
message(FATAL_ERROR "add_llext_target: only one source file is supported") | ||
endif() | ||
|
||
set(output_file ${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 | ||
list(TRANSFORM LLEXT_REMOVE_FLAGS | ||
REPLACE "(.+)" "^\\1$" | ||
OUTPUT_VARIABLE llext_remove_flags_regexp | ||
) | ||
string(REPLACE ";" "|" llext_remove_flags_regexp "${llext_remove_flags_regexp}") | ||
set(zephyr_flags | ||
"$<TARGET_PROPERTY:zephyr_interface,INTERFACE_COMPILE_OPTIONS>" | ||
) | ||
set(zephyr_filtered_flags | ||
"$<FILTER:${zephyr_flags},EXCLUDE,${llext_remove_flags_regexp}>" | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is scary stuff :-) How about adding a couple There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I used plenty of them for debugging indeed 😇 but I did not push them, as they are quite cumbersome: to see the end result to that gibberish you need to add a There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ah yes of course, it's build-time. Got it. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. cmake is next level painful, thanks for taking this on |
||
) | ||
|
||
# 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}) | ||
teburd marked this conversation as resolved.
Show resolved
Hide resolved
|
||
target_compile_definitions(${target_name}_lib PRIVATE | ||
$<TARGET_PROPERTY:zephyr_interface,INTERFACE_COMPILE_DEFINITIONS> | ||
) | ||
target_compile_options(${target_name}_lib PRIVATE | ||
${zephyr_filtered_flags} | ||
${LLEXT_APPEND_FLAGS} | ||
${LLEXT_C_FLAGS} | ||
) | ||
target_include_directories(${target_name}_lib PRIVATE | ||
$<TARGET_PROPERTY:zephyr_interface,INTERFACE_INCLUDE_DIRECTORIES> | ||
) | ||
target_include_directories(${target_name}_lib SYSTEM PUBLIC | ||
$<TARGET_PROPERTY:zephyr_interface,INTERFACE_SYSTEM_INCLUDE_DIRECTORIES> | ||
) | ||
add_dependencies(${target_name}_lib | ||
zephyr_interface | ||
zephyr_generated_headers | ||
) | ||
|
||
# Arch-specific conversion of the object file to an llext | ||
if(CONFIG_ARM) | ||
|
||
# No conversion 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> | ||
) | ||
|
||
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 | ||
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> | ||
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_final> | ||
DEPENDS ${target_name}_lib $<TARGET_OBJECTS:${target_name}_lib> | ||
) | ||
|
||
else() | ||
message(FATAL_ERROR "add_llext_target: unsupported architecture") | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Removed and added back, looks like a small git glitch. |
||
endif() | ||
endfunction() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
in CI we use command-line parameters like
-DEXTRA_CXXFLAGS=-Werror
a lot. Will they work withllext
too?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am no CMake guru so I just tested - it does (well, with EXTRA_CFLAGS as it's a C file, but it does indeed use command line flags in the llext as well).