Skip to content
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

llext-edk: rework, read data from build_info.yml and .config #83705

Draft
wants to merge 12 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 9 additions & 16 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -1990,7 +1990,6 @@ if (CONFIG_LLEXT AND CONFIG_LLEXT_EXPORT_BUILTINS_BY_SLID)
--elf-file ${PROJECT_BINARY_DIR}/${KERNEL_ELF_NAME}
--slid-listing ${PROJECT_BINARY_DIR}/slid_listing.txt
)

endif()

if(NOT CMAKE_C_COMPILER_ID STREQUAL "ARMClang")
Expand Down Expand Up @@ -2236,6 +2235,10 @@ list(APPEND llext_edk_cflags ${llext_filt_flags})
list(APPEND llext_edk_cflags ${LLEXT_APPEND_FLAGS})
list(APPEND llext_edk_cflags ${LLEXT_EDK_APPEND_FLAGS})

build_info(llext-edk file PATH ${llext_edk_file})
build_info(llext-edk cflags VALUE ${llext_edk_cflags} GENEX)
build_info(llext-edk include-dirs VALUE "$<TARGET_PROPERTY:zephyr_interface,INTERFACE_INCLUDE_DIRECTORIES>" GENEX)

add_custom_command(
OUTPUT ${llext_edk_file}
# Regenerate syscalls in case CONFIG_LLEXT_EDK_USERSPACE_ONLY
Expand All @@ -2252,15 +2255,6 @@ add_custom_command(
${SYSCALL_LONG_REGISTERS_ARG}
${SYSCALL_SPLIT_TIMEOUT_ARG}
COMMAND ${CMAKE_COMMAND}
-DPROJECT_BINARY_DIR=${PROJECT_BINARY_DIR}
-DAPPLICATION_SOURCE_DIR=${APPLICATION_SOURCE_DIR}
-DINTERFACE_INCLUDE_DIRECTORIES="$<TARGET_PROPERTY:zephyr_interface,INTERFACE_INCLUDE_DIRECTORIES>"
-Dllext_edk_file=${llext_edk_file}
-Dllext_edk_cflags="${llext_edk_cflags}"
-Dllext_edk_name=${CONFIG_LLEXT_EDK_NAME}
-DWEST_TOPDIR=${WEST_TOPDIR}
-DZEPHYR_BASE=${ZEPHYR_BASE}
-DCONFIG_LLEXT_EDK_USERSPACE_ONLY=${CONFIG_LLEXT_EDK_USERSPACE_ONLY}
-P ${ZEPHYR_BASE}/cmake/llext-edk.cmake
DEPENDS ${logical_target_for_zephyr_elf}
COMMAND_EXPAND_LISTS
Expand All @@ -2286,9 +2280,8 @@ add_subdirectory_ifdef(

toolchain_linker_finalize()

yaml_context(EXISTS NAME build_info result)
if(result)
build_info(zephyr version VALUE ${PROJECT_VERSION_STR})
build_info(zephyr zephyr-base VALUE ${ZEPHYR_BASE})
yaml_save(NAME build_info)
endif()
# export build information
build_info(zephyr version VALUE ${PROJECT_VERSION_STR})
build_info(zephyr zephyr-base VALUE ${ZEPHYR_BASE})

yaml_save(NAME build_info TARGET ${logical_target_for_zephyr_elf})
156 changes: 96 additions & 60 deletions cmake/llext-edk.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -11,33 +11,31 @@
# directories (build/zephyr, zephyr base, west top dir and application source
# dir), to avoid leaking any information about the host system.
#
# The following arguments are expected:
# - llext_edk_name: Name of the extension, used to name the tarball and the
# install directory variable for Makefile.
# - INTERFACE_INCLUDE_DIRECTORIES: List of include directories to copy headers
# from. It should simply be the INTERFACE_INCLUDE_DIRECTORIES property of the
# zephyr_interface target.
# - llext_edk_file: Output file name for the tarball.
# - llext_edk_cflags: Flags to be used for source compile commands.
# - ZEPHYR_BASE: Path to the zephyr base directory.
# - WEST_TOPDIR: Path to the west top directory.
# - APPLICATION_SOURCE_DIR: Path to the application source directory.
# - PROJECT_BINARY_DIR: Path to the project binary build directory.
# - CONFIG_LLEXT_EDK_USERSPACE_ONLY: Whether to copy syscall headers from the
# edk directory. This is necessary when building an extension that only
# supports userspace, as the syscall headers are regenerated in the edk
# directory.
# The script expects a build_info.yml file in the project binary directory.
# This file should contain the following entries:
# - cmake application source-dir
# - cmake llext-edk cflags
# - cmake llext-edk file
# - cmake llext-edk include-dirs
# - west topdir

cmake_minimum_required(VERSION 3.20.0)

# initialize the same paths as the main CMakeLists.txt for consistency
set(PROJECT_BINARY_DIR ${CMAKE_BINARY_DIR})
set(ZEPHYR_BASE ${CMAKE_CURRENT_LIST_DIR}/../)
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/modules")
include(extensions)
include(yaml)

# read in computed build configuration
import_kconfig(CONFIG ${PROJECT_BINARY_DIR}/.config)

if (CONFIG_LLEXT_EXPORT_BUILTINS_BY_SLID)
message(FATAL_ERROR
"The LLEXT EDK is not compatible with CONFIG_LLEXT_EXPORT_BUILTINS_BY_SLID.")
endif()

set(llext_edk ${PROJECT_BINARY_DIR}/${llext_edk_name})
set(llext_edk_inc ${llext_edk}/include)

# Usage:
# relative_dir(<dir> <relative_out> <bindir_out>)
#
Expand Down Expand Up @@ -89,12 +87,23 @@ function(relative_dir dir relative_out bindir_out)
endif()
endfunction()

set(build_info_file ${PROJECT_BINARY_DIR}/../build_info.yml)
yaml_load(FILE ${build_info_file} NAME build_info)

yaml_get(llext_edk_cflags NAME build_info KEY cmake llext-edk cflags)
yaml_get(llext_edk_file NAME build_info KEY cmake llext-edk file)
yaml_get(INTERFACE_INCLUDE_DIRECTORIES NAME build_info KEY cmake llext-edk include-dirs)
yaml_get(APPLICATION_SOURCE_DIR NAME build_info KEY cmake application source-dir)
yaml_get(WEST_TOPDIR NAME build_info KEY west topdir)

set(llext_edk_name ${CONFIG_LLEXT_EDK_NAME})
set(llext_edk ${PROJECT_BINARY_DIR}/${llext_edk_name})
set(llext_edk_inc ${llext_edk}/include)

string(REGEX REPLACE "[^a-zA-Z0-9]" "_" llext_edk_name_sane ${llext_edk_name})
string(TOUPPER ${llext_edk_name_sane} llext_edk_name_sane)
set(install_dir_var "${llext_edk_name_sane}_INSTALL_DIR")

separate_arguments(llext_edk_cflags NATIVE_COMMAND ${llext_edk_cflags})

set(make_relative FALSE)
foreach(flag ${llext_edk_cflags})
if (flag STREQUAL "-imacros")
Expand All @@ -106,24 +115,20 @@ foreach(flag ${llext_edk_cflags})
relative_dir(${parent} dest bindir)
cmake_path(RELATIVE_PATH dest BASE_DIRECTORY ${llext_edk} OUTPUT_VARIABLE dest_rel)
if(bindir)
list(APPEND imacros_gen_make "-imacros\$(${install_dir_var})/${dest_rel}/${name}")
list(APPEND imacros_gen_cmake "-imacros\${CMAKE_CURRENT_LIST_DIR}/${dest_rel}/${name}")
list(APPEND imacros_gen "@DASHIMACROS@${dest_rel}/${name}")
else()
list(APPEND imacros_make "-imacros\$(${install_dir_var})/${dest_rel}/${name}")
list(APPEND imacros_cmake "-imacros\${CMAKE_CURRENT_LIST_DIR}/${dest_rel}/${name}")
list(APPEND imacros "@DASHIMACROS@${dest_rel}/${name}")
endif()
else()
list(APPEND new_cflags ${flag})
endif()
endforeach()
set(llext_edk_cflags ${new_cflags})

list(APPEND base_flags_make ${llext_edk_cflags} ${imacros_make})
list(APPEND base_flags_cmake ${llext_edk_cflags} ${imacros_cmake})
list(APPEND base_flags ${llext_edk_cflags} ${imacros})

separate_arguments(include_dirs NATIVE_COMMAND ${INTERFACE_INCLUDE_DIRECTORIES})
file(MAKE_DIRECTORY ${llext_edk_inc})
foreach(dir ${include_dirs})
foreach(dir ${INTERFACE_INCLUDE_DIRECTORIES})
if (NOT EXISTS ${dir})
continue()
endif()
Expand All @@ -137,54 +142,85 @@ foreach(dir ${include_dirs})

cmake_path(RELATIVE_PATH dest BASE_DIRECTORY ${llext_edk} OUTPUT_VARIABLE dest_rel)
if(bindir)
list(APPEND inc_gen_flags_make "-I\$(${install_dir_var})/${dest_rel}")
list(APPEND inc_gen_flags_cmake "-I\${CMAKE_CURRENT_LIST_DIR}/${dest_rel}")
list(APPEND gen_inc_flags "@DASHI@${dest_rel}")
else()
list(APPEND inc_flags_make "-I\$(${install_dir_var})/${dest_rel}")
list(APPEND inc_flags_cmake "-I\${CMAKE_CURRENT_LIST_DIR}/${dest_rel}")
list(APPEND inc_flags "@DASHI@${dest_rel}")
endif()
list(APPEND all_inc_flags_make "-I\$(${install_dir_var})/${dest_rel}")
list(APPEND all_inc_flags_cmake "-I\${CMAKE_CURRENT_LIST_DIR}/${dest_rel}")
list(APPEND all_inc_flags "@DASHI@${dest_rel}")
endforeach()

list(APPEND all_flags ${base_flags} ${imacros_gen} ${all_inc_flags})

if(CONFIG_LLEXT_EDK_USERSPACE_ONLY)
# Copy syscall headers from edk directory, as they were regenerated there.
file(COPY ${PROJECT_BINARY_DIR}/edk/include/generated/ DESTINATION ${llext_edk_inc}/zephyr/include/generated)
endif()

# Generate flags for Makefile
list(APPEND all_flags_make ${base_flags_make} ${imacros_gen_make} ${all_inc_flags_make})
list(JOIN all_flags_make " " all_flags_str)
file(WRITE ${llext_edk}/Makefile.cflags "LLEXT_CFLAGS = ${all_flags_str}")

list(JOIN all_inc_flags_make " " all_inc_flags_str)
file(APPEND ${llext_edk}/Makefile.cflags "\n\nLLEXT_ALL_INCLUDE_CFLAGS = ${all_inc_flags_str}")

list(JOIN inc_flags_make " " inc_flags_str)
file(APPEND ${llext_edk}/Makefile.cflags "\n\nLLEXT_INCLUDE_CFLAGS = ${inc_flags_str}")

list(JOIN inc_gen_flags_make " " inc_gen_flags_str)
file(APPEND ${llext_edk}/Makefile.cflags "\n\nLLEXT_GENERATED_INCLUDE_CFLAGS = ${inc_gen_flags_str}")

list(JOIN base_flags_make " " base_flags_str)
file(APPEND ${llext_edk}/Makefile.cflags "\n\nLLEXT_BASE_CFLAGS = ${base_flags_str}")
#
# Generate the EDK flags files
#

list(JOIN imacros_gen_make " " imacros_gen_str)
file(APPEND ${llext_edk}/Makefile.cflags "\n\nLLEXT_GENERATED_IMACROS_CFLAGS = ${imacros_gen_str}")
set(edk_targets MAKEFILE CMAKE)
set(edk_file_MAKEFILE ${llext_edk}/Makefile.cflags)
set(edk_file_CMAKE ${llext_edk}/cmake.cflags)

# Generate flags for CMake
list(APPEND all_flags_cmake ${base_flags_cmake} ${imacros_gen_cmake} ${all_inc_flags_cmake})
file(WRITE ${llext_edk}/cmake.cflags "set(LLEXT_CFLAGS ${all_flags_cmake})")
# Escape problematic characters in a string
function(edk_escape target str_in str_out)
string(REPLACE "\\" "\\\\" str_escaped "${str_in}")
string(REPLACE "\"" "\\\"" str_escaped "${str_escaped}")
set(${str_out} "${str_escaped}" PARENT_SCOPE)
endfunction()

file(APPEND ${llext_edk}/cmake.cflags "\n\nset(LLEXT_ALL_INCLUDE_CFLAGS ${all_inc_flags_cmake})")
# Clear the contents of the requested file
function(edk_write_header target)
file(WRITE ${edk_file_${target}} "")
endfunction()

file(APPEND ${llext_edk}/cmake.cflags "\n\nset(LLEXT_INCLUDE_CFLAGS ${inc_flags_cmake})")
# Mark a section in the file with a single line comment
function(edk_write_comment target comment)
file(APPEND ${edk_file_${target}} "\n# ${comment}\n")
endfunction()

file(APPEND ${llext_edk}/cmake.cflags "\n\nset(LLEXT_GENERATED_INCLUDE_CFLAGS ${inc_gen_flags_cmake})")
# Define a variable in the file
function(edk_write_var target var_name var_value)
if(target STREQUAL "CMAKE")
# CMake: export assignments of the form:
#
# set(var "value1;value2;...")
#
set(DASHIMACROS "-imacros\${CMAKE_CURRENT_LIST_DIR}/")
set(DASHI "-I\${CMAKE_CURRENT_LIST_DIR}/")
edk_escape(${target} "${var_value}" var_value)
string(CONFIGURE "${var_value}" exp_var_value @ONLY)
# The list is otherwise exported verbatim, surrounded by quotes.
file(APPEND ${edk_file_${target}} "set(${var_name} \"${exp_var_value}\")\n")
elseif(target STREQUAL "MAKEFILE")
# Makefile: export assignments of the form:
#
# var = "value1" "value2" ...
#
set(DASHIMACROS "-imacros\$(${install_dir_var})/")
set(DASHI "-I\$(${install_dir_var})/")
edk_escape(${target} "${var_value}" var_value)
string(CONFIGURE "${var_value}" exp_var_value @ONLY)
# Each element of the list is wrapped in quotes and is separated by a space.
list(JOIN exp_var_value "\" \"" exp_var_value_str)
file(APPEND ${edk_file_${target}} "${var_name} = \"${exp_var_value_str}\"\n")
endif()
endfunction()

file(APPEND ${llext_edk}/cmake.cflags "\n\nset(LLEXT_BASE_CFLAGS ${base_flags_cmake})")
foreach(target ${edk_targets})
edk_write_header(${target})

file(APPEND ${llext_edk}/cmake.cflags "\n\nset(LLEXT_GENERATED_IMACROS_CFLAGS ${imacros_gen_cmake})")
edk_write_comment(${target} "Compile flags")
edk_write_var(${target} "LLEXT_CFLAGS" "${all_flags}")
edk_write_var(${target} "LLEXT_ALL_INCLUDE_CFLAGS" "${all_inc_flags}")
edk_write_var(${target} "LLEXT_INCLUDE_CFLAGS" "${inc_flags}")
edk_write_var(${target} "LLEXT_GENERATED_INCLUDE_CFLAGS" "${gen_inc_flags}")
edk_write_var(${target} "LLEXT_BASE_CFLAGS" "${base_flags}")
edk_write_var(${target} "LLEXT_GENERATED_IMACROS_CFLAGS" "${imacros_gen}")
endforeach()

# Generate the tarball
file(ARCHIVE_CREATE
Expand Down
38 changes: 22 additions & 16 deletions cmake/modules/extensions.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -3703,10 +3703,10 @@ function(topological_sort)
endfunction()

# Usage:
# build_info(<tag>... VALUE <value>... )
# build_info(<tag>... VALUE <value>... [GENEX])
# build_info(<tag>... PATH <path>... )
#
# This function populates updates the build_info.yml info file with exchangable build information
# This function populates the build_info.yml info file with exchangable build information
# related to the current build.
#
# Example:
Expand All @@ -3724,17 +3724,27 @@ endfunction()
# PATH <path>... : path(s) to place in the build_info.yml file. All paths are converted to CMake
# style. If no conversion is required, for example when paths are already
# guaranteed to be CMake style, then VALUE can also be used.
# GENEX : the value(s) contain a generator expression. Cannot be used with PATH.
# Resulting context must be saved providing a TARGET to yaml_save().
function(build_info)
set(convert_path FALSE)
set(arg_list ${ARGV})
list(FIND arg_list VALUE index)
if(index EQUAL -1)
list(FIND arg_list PATH index)
cmake_parse_arguments(ARG_BUILD_INFO "GENEX" "" "VALUE;PATH" ${ARGN})

zephyr_check_arguments_required_allow_empty(${CMAKE_CURRENT_FUNCTION} ARG_BUILD_INFO VALUE PATH)
zephyr_check_arguments_exclusive(${CMAKE_CURRENT_FUNCTION} ARG_BUILD_INFO VALUE PATH)

set(keys ${ARG_BUILD_INFO_UNPARSED_ARGUMENTS})
if (DEFINED ARG_BUILD_INFO_PATH)
set(convert_path TRUE)
set(values ${ARG_BUILD_INFO_PATH})
else()
set(convert_path FALSE)
set(values ${ARG_BUILD_INFO_VALUE})
endif()

if(index EQUAL -1)
message(FATAL_ERROR "${CMAKE_CURRENT_FUNCTION}(...) missing a required argument: VALUE or PATH")
if (ARG_BUILD_INFO_GENEX)
if (convert_path)
message(FATAL_ERROR "build_info: GENEX is unsupported on PATH entries")
endif()
set(genex_flag "GENEX")
endif()

yaml_context(EXISTS NAME build_info result)
Expand All @@ -3748,10 +3758,6 @@ function(build_info)
yaml_set(NAME build_info KEY version VALUE "0.1.0")
endif()

list(SUBLIST arg_list 0 ${index} keys)
list(SUBLIST arg_list ${index} -1 values)
list(POP_FRONT values)

if(convert_path)
set(converted_values)
foreach(val ${values})
Expand Down Expand Up @@ -3779,7 +3785,7 @@ function(build_info)
endif()
endif()

yaml_set(NAME build_info KEY cmake ${keys} ${type} "${values}")
yaml_set(NAME build_info KEY cmake ${keys} ${type} "${values}" ${genex_flag})
endfunction()

########################################################
Expand Down Expand Up @@ -5916,7 +5922,7 @@ endfunction()
# depending on the exact use of the function in script mode.
#
# Current Zephyr CMake scripts which includes `extensions.cmake` in script mode
# are: package_helper.cmake, verify-toolchain.cmake
# are: package_helper.cmake, verify-toolchain.cmake, llext-edk.cmake
#

if(CMAKE_SCRIPT_MODE_FILE)
Expand Down
Loading
Loading