blob: 30fc2b42a63c6c0ee4110840e1918d6e4a1a3acf [file] [log] [blame]
#-------------------------------------------------------------------------------
# Copyright (c) 2022-2023, Arm Limited. All rights reserved.
#
# SPDX-License-Identifier: BSD-3-Clause
#
#-------------------------------------------------------------------------------
# Load multiple config files and merge into one, generates CMake config file and config header file.
# The first loaded configs would be overridden by later ones. That's how the "merge" works.
# Check CONFIG_FILE_LIST for the loading order.
# Configurations not set by any of the config files would be set to the default values in Kconfig
# files with dependencies respected.
# If a ".config" already exists in the output folder, then the CONFIG_FILE_LIST is ignored.
# For more details, check the kconfig_system.rst.
set(KCONFIG_OUTPUT_DIR ${CMAKE_BINARY_DIR}/kconfig)
set(DOTCONFIG_FILE ${KCONFIG_OUTPUT_DIR}/.config)
set(ROOT_KCONFIG ${CMAKE_SOURCE_DIR}/Kconfig)
set(PLATFORM_KCONFIG ${TARGET_PLATFORM_PATH}/Kconfig)
# This function parses the input ${cmake_file} to get normal CMake variables and their values in the
# format of "set(_VAR_ _VALUE_)". The format could be split into multiple lines.
# Note that CMake does not allow the "(" to be in a different line as "set" and no white spaces are
# recommanded between "set" and "(". So the function only covers format of "set(".
function(convert_normal_cmake_config_to_kconfig cmake_file out_var)
# Operate on a local var and write back to the "out_var" later.
set(local_var "")
# Read out the strings of the file. Binary data in the file are ignored
file(STRINGS ${cmake_file} CONTENTS)
# Exclude lines of comments (started with "#")
set(CONTENTS_WITHOUT_COMMENTS "")
foreach(LINE ${CONTENTS})
string(REGEX MATCH "^#.*" OUT_STRING ${LINE})
if(NOT OUT_STRING)
string(APPEND CONTENTS_WITHOUT_COMMENTS "${LINE}\n")
endif()
string(REGEX MATCH "^include\\((.*)\\)$" OUT_STRING ${LINE})
if(OUT_STRING AND CMAKE_MATCH_COUNT EQUAL 1)
message(FATAL_ERROR "Including another file in config file is not supported yet: ${LINE}")
endif()
endforeach()
# Search for strings match set(_VAR_ _VALUE_) with support of multi-line format
string(REGEX MATCHALL
"set\\([ \t\r\n]*([A-Za-z0-9_]*)[ \t\r\n]*([^ \t\r\n]*)[ \t\r\n]*\\)"
OUT_STRINGS ${CONTENTS_WITHOUT_COMMENTS})
foreach(MATCHED_ITEM ${OUT_STRINGS})
# Try to convert CMake format to Kconfig one
# If the format does not match, the content will not be changed and fall down to the next
# Bool types
string(REGEX REPLACE
"set\\([ \t\r\n]*([A-Za-z0-9_]*)[ \t\r\n]*(TRUE|ON)[ \t\r\n]*\\)"
"config \\1\n default y\n"
MATCHED_ITEM ${MATCHED_ITEM})
string(REGEX REPLACE
"set\\([ \t\r\n]*([A-Za-z0-9_]*)[ \t\r\n]*(FALSE|OFF)[ \t\r\n]*\\)"
"config \\1\n default n\n"
MATCHED_ITEM ${MATCHED_ITEM})
# Hex int
string(REGEX REPLACE
"set\\([ \t\r\n]*([A-Za-z0-9_]*)[ \t\r\n]*(0x[0-9a-fA-F]+[ \t\r\n]*)\\)"
"config \\1\n default \\2\n"
MATCHED_ITEM ${MATCHED_ITEM})
# Decimal int
string(REGEX REPLACE
"set\\([ \t\r\n]*([A-Za-z0-9_]*)[ \t\r\n]*([0-9]+)[ \t\r\n]*\\)"
"config \\1\n default \\2\n"
MATCHED_ITEM ${MATCHED_ITEM})
# Quoted string
string(REGEX REPLACE
"set\\([ \t\r\n]*([A-Za-z0-9_]*)[ \t\r\n]*(\".*\")[ \t\r\n]*\\)"
"config \\1\n default \\2\n"
MATCHED_ITEM ${MATCHED_ITEM})
# If none of the above matches, must be a non-quoted string
string(REGEX REPLACE
"set\\([ \t\r\n]*([A-Za-z0-9_]*)[ \t\r\n]*(.*)[ \t\r\n]*\\)"
"config \\1\n default \"\\2\"\n"
MATCHED_ITEM ${MATCHED_ITEM})
string(APPEND local_var ${MATCHED_ITEM})
endforeach()
set(${out_var} ${local_var} PARENT_SCOPE)
endfunction()
# This function goes through the CMake cache variables and convert them to .config format.
# The function distinguishes command-line variables and other ones and it can only handle one of
# them in the same time.
function(convert_cache_config_to_dotconfig convert_cl_var out_var)
# Operate on a local var and write back to the out_var later
set(local_var "")
get_cmake_property(CACHE_VARS CACHE_VARIABLES)
foreach(CACHE_VAR ${CACHE_VARS})
get_property(HELP_STRING CACHE ${CACHE_VAR} PROPERTY HELPSTRING)
if("${HELP_STRING}" MATCHES "variable specified on the command line")
# Command-line variables have the help string above by default
set(IS_CL_VAR TRUE)
else()
set(IS_CL_VAR FALSE)
endif()
if((IS_CL_VAR AND NOT ${convert_cl_var}) OR (NOT IS_CL_VAR AND ${convert_cl_var}))
continue()
endif()
set(CACHE_VAR_VAL ${${CACHE_VAR}})
STRING(TOUPPER "${CACHE_VAR_VAL}" CACHE_VAR_VAL_UPPER)
set(CACHE_VAR "CONFIG_${CACHE_VAR}")
set(KCONFIG_OPTION_ITEM "")
# False CMAKE values
if(CACHE_VAR_VAL_UPPER STREQUAL "OFF" OR CACHE_VAR_VAL_UPPER STREQUAL "FALSE")
set(KCONFIG_OPTION_ITEM "${CACHE_VAR}=n\r\n")
# True CMAKE Values
elseif(CACHE_VAR_VAL_UPPER STREQUAL "ON" OR CACHE_VAR_VAL_UPPER STREQUAL "TRUE")
set(KCONFIG_OPTION_ITEM "${CACHE_VAR}=y\r\n")
# Non-quoted values (hex and decimal numbers)
elseif(CACHE_VAR_VAL MATCHES "^0x[a-fA-F0-9]+$" OR CACHE_VAR_VAL MATCHES "^[0-9]+$" )
set(KCONFIG_OPTION_ITEM "${CACHE_VAR}=${CACHE_VAR_VAL}\r\n")
# Everything else is a quoted string
else()
if(${CACHE_VAR} STREQUAL "CONFIG_TEST_PSA_API" AND CACHE_VAR_VAL_UPPER)
# Turn on the corresponding "choice" option for psa-arch-test
list(APPEND _LEGAL_PSA_API_TEST_LIST "IPC" "CRYPTO" "INITIAL_ATTESTATION" "INTERNAL_TRUSTED_STORAGE" "PROTECTED_STORAGE" "STORAGE")
list(FIND _LEGAL_PSA_API_TEST_LIST ${CACHE_VAR_VAL_UPPER} _RET_VAL)
if(NOT ${_RET_VAL} EQUAL -1)
set(KCONFIG_OPTION_ITEM "CONFIG_PSA_API_TEST_${CACHE_VAR_VAL_UPPER}=y\r\n")
if(${CACHE_VAR_VAL_UPPER} STREQUAL "IPC")
# PSA API IPC test requires IPC model to be enabled while
# the CONFIG_TFM_SPM_BACKEND_IPC cannot be selected or implied because it is a choice.
# It can be only enabled in a Kconfig config file. So append it here.
string(APPEND KCONFIG_OPTION_ITEM "CONFIG_CONFIG_TFM_SPM_BACKEND_IPC=y\r\n")
endif()
endif()
elseif(${CACHE_VAR} STREQUAL "CONFIG_CONFIG_TFM_SPM_BACKEND")
# Turn on the corresponding "choice" option for SPM backend
set(KCONFIG_OPTION_ITEM "CONFIG_CONFIG_TFM_SPM_BACKEND_${CACHE_VAR_VAL_UPPER}=y\r\n")
else()
set(KCONFIG_OPTION_ITEM "${CACHE_VAR}=\"${CACHE_VAR_VAL}\"\r\n")
endif()
endif()
string(APPEND local_var ${KCONFIG_OPTION_ITEM})
endforeach()
set(${out_var} ${local_var} PARENT_SCOPE)
endfunction()
# Initialize the .cl_config
set(COMMAND_LINE_CONFIG_TO_FILE ${KCONFIG_OUTPUT_DIR}/.cl_config)
file(REMOVE ${COMMAND_LINE_CONFIG_TO_FILE})
# Initialize the .cache_var_config
set(CACHE_VAR_CONFIG_FILE ${KCONFIG_OUTPUT_DIR}/.cache_var_config)
file(REMOVE ${CACHE_VAR_CONFIG_FILE})
if(NOT EXISTS ${PLATFORM_KCONFIG} AND NOT EXISTS ${DOTCONFIG_FILE})
# Parse platform's preload.cmake and config.cmake to get config options.
set(PLATFORM_KCONFIG_OPTIONS "")
set(PLATFORM_KCONFIG ${KCONFIG_OUTPUT_DIR}/platform/Kconfig)
convert_normal_cmake_config_to_kconfig(${TARGET_PLATFORM_PATH}/preload.cmake PLATFORM_KCONFIG_OPTIONS)
file(WRITE ${PLATFORM_KCONFIG} ${PLATFORM_KCONFIG_OPTIONS})
if(EXISTS ${TARGET_PLATFORM_PATH}/config.cmake)
include(${TARGET_PLATFORM_PATH}/config.cmake)
convert_normal_cmake_config_to_kconfig(${TARGET_PLATFORM_PATH}/config.cmake PLATFORM_KCONFIG_OPTIONS)
file(APPEND ${PLATFORM_KCONFIG} ${PLATFORM_KCONFIG_OPTIONS})
set(PLATFORM_CMAKE_CONFIGS "")
set(CONVERT_CL_VAR FALSE)
convert_cache_config_to_dotconfig(CONVERT_CL_VAR PLATFORM_CMAKE_CONFIGS)
file(APPEND ${CACHE_VAR_CONFIG_FILE} ${PLATFORM_CMAKE_CONFIGS})
endif()
endif()
get_filename_component(PLATFORM_KCONFIG_PATH ${PLATFORM_KCONFIG} DIRECTORY)
# Build type Kconfig file, for example 'Kconfig.minsizerel', the suffix passed
# by Kconfig environment variables and it shall be lowercase.
string(TOLOWER "${CMAKE_BUILD_TYPE}" CMAKE_BUILD_TYPE_LOWERCASE)
# TF-M profile config file
if(TFM_PROFILE)
set(TFM_PROFILE_KCONFIG_FILE ${CMAKE_SOURCE_DIR}/config/profile/${TFM_PROFILE}.conf)
if(NOT EXISTS ${TFM_PROFILE_KCONFIG_FILE})
message(FATAL_ERROR "No such file: ${TFM_PROFILE_KCONFIG_FILE}, please check ${TFM_PROFILE} is right.")
endif()
set(TFM_PROFILE_TEST_KCONFIG_FILE ${CMAKE_SOURCE_DIR}/lib/ext/tf-m-tests/${TFM_PROFILE}_test.conf)
if(NOT EXISTS ${TFM_PROFILE_TEST_KCONFIG_FILE})
message(FATAL_ERROR "No such file: ${TFM_PROFILE_TEST_KCONFIG_FILE}, please check ${TFM_PROFILE} is right.")
endif()
endif()
# Parse command-line variables
set(CL_CONFIGS "")
set(CONVERT_CL_VAR TRUE)
convert_cache_config_to_dotconfig(CONVERT_CL_VAR CL_CONFIGS)
file(APPEND ${COMMAND_LINE_CONFIG_TO_FILE} ${CL_CONFIGS})
if(NOT EXISTS ${CACHE_VAR_CONFIG_FILE})
set(CACHE_VAR_CONFIG_FILE "")
endif()
# User customized config file
if(DEFINED KCONFIG_CONFIG_FILE AND NOT EXISTS ${KCONFIG_CONFIG_FILE})
message(FATAL_ERROR "No such file: ${KCONFIG_CONFIG_FILE}")
endif()
# Note the order of CONFIG_FILE_LIST, as the first loaded configs would be
# overridden by later ones.
list(APPEND CONFIG_FILE_LIST
${TFM_PROFILE_KCONFIG_FILE}
${TFM_PROFILE_TEST_KCONFIG_FILE}
${CACHE_VAR_CONFIG_FILE}
${KCONFIG_CONFIG_FILE}
${COMMAND_LINE_CONFIG_TO_FILE})
# Set up ENV variables for the tfm_kconfig.py which are then consumed by Kconfig files.
set(KCONFIG_ENV_VARS "TFM_SOURCE_DIR=${CMAKE_SOURCE_DIR} \
PLATFORM_PATH=${PLATFORM_KCONFIG_PATH} \
CMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE_LOWERCASE}")
if(MENUCONFIG)
# Note: Currently, only GUI menuconfig can be supported with CMake integration
set(MENUCONFIG_ARG "-u=gui")
else()
set(MENUCONFIG_ARG "")
endif()
if(DEFINED PROJECT_CONFIG_HEADER_FILE)
get_property(HELP_STRING CACHE PROJECT_CONFIG_HEADER_FILE PROPERTY HELPSTRING)
# It is not supported to set PROJECT_CONFIG_HEADER_FILE while using Kconfig, either from
# command line or CMake files. It should be set to the file generated the Kconfig system.
# As this file set it itself, if the user re-run the CMake config command, the
# PROJECT_CONFIG_HEADER_FILE will be already defined set.
# So the existence of the ${DOTCONFIG_FILE} is used to indicate if it is a re-configuration.
if("${HELP_STRING}" MATCHES "variable specified on the command line" OR NOT EXISTS ${DOTCONFIG_FILE})
message(FATAL_ERROR "It is NOT supported to manually set PROJECT_CONFIG_HEADER_FILE while using Kconfig.")
endif()
endif()
find_package(Python3)
execute_process(
COMMAND
${Python3_EXECUTABLE} ${CMAKE_SOURCE_DIR}/tools/kconfig/tfm_kconfig.py
-k ${ROOT_KCONFIG} -o ${KCONFIG_OUTPUT_DIR}
--envs ${KCONFIG_ENV_VARS}
--config-files ${CONFIG_FILE_LIST}
${MENUCONFIG_ARG}
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
RESULT_VARIABLE ret
)
if(NOT ret EQUAL 0)
message(FATAL_ERROR "Kconfig tool failed!")
endif()
# Component configs generated by tfm_kconfig.py
set(PROJECT_CONFIG_HEADER_FILE ${KCONFIG_OUTPUT_DIR}/project_config.h CACHE FILEPATH "User defined header file for TF-M config")
# Load project cmake configs generated by tfm_kconfig.py
include(${KCONFIG_OUTPUT_DIR}/project_config.cmake)