1#------------------------------------------------------------------------------- 2# Copyright (c) 2022-2023, Arm Limited. All rights reserved. 3# 4# SPDX-License-Identifier: BSD-3-Clause 5# 6#------------------------------------------------------------------------------- 7 8# Load multiple config files and merge into one, generates CMake config file and config header file. 9# The first loaded configs would be overridden by later ones. That's how the "merge" works. 10# Check CONFIG_FILE_LIST for the loading order. 11# Configurations not set by any of the config files would be set to the default values in Kconfig 12# files with dependencies respected. 13# If a ".config" already exists in the output folder, then the CONFIG_FILE_LIST is ignored. 14# For more details, check the kconfig_system.rst. 15 16set(KCONFIG_OUTPUT_DIR ${CMAKE_BINARY_DIR}/kconfig) 17 18set(DOTCONFIG_FILE ${KCONFIG_OUTPUT_DIR}/.config) 19set(ROOT_KCONFIG ${CMAKE_SOURCE_DIR}/Kconfig) 20set(PLATFORM_KCONFIG ${TARGET_PLATFORM_PATH}/Kconfig) 21 22# This function parses the input ${cmake_file} to get normal CMake variables and their values in the 23# format of "set(_VAR_ _VALUE_)". The format could be split into multiple lines. 24# Note that CMake does not allow the "(" to be in a different line as "set" and no white spaces are 25# recommanded between "set" and "(". So the function only covers format of "set(". 26function(convert_normal_cmake_config_to_kconfig cmake_file out_var) 27 # Operate on a local var and write back to the "out_var" later. 28 set(local_var "") 29 30 # Read out the strings of the file. Binary data in the file are ignored 31 file(STRINGS ${cmake_file} CONTENTS) 32 33 # Exclude lines of comments (started with "#") 34 set(CONTENTS_WITHOUT_COMMENTS "") 35 36 foreach(LINE ${CONTENTS}) 37 string(REGEX MATCH "^#.*" OUT_STRING ${LINE}) 38 if(NOT OUT_STRING) 39 string(APPEND CONTENTS_WITHOUT_COMMENTS "${LINE}\n") 40 endif() 41 42 string(REGEX MATCH "^include\\((.*)\\)$" OUT_STRING ${LINE}) 43 if(OUT_STRING AND CMAKE_MATCH_COUNT EQUAL 1) 44 message(FATAL_ERROR "Including another file in config file is not supported yet: ${LINE}") 45 endif() 46 endforeach() 47 48 # Search for strings match set(_VAR_ _VALUE_) with support of multi-line format 49 string(REGEX MATCHALL 50 "set\\([ \t\r\n]*([A-Za-z0-9_]*)[ \t\r\n]*([^ \t\r\n]*)[ \t\r\n]*\\)" 51 OUT_STRINGS ${CONTENTS_WITHOUT_COMMENTS}) 52 53 foreach(MATCHED_ITEM ${OUT_STRINGS}) 54 # Try to convert CMake format to Kconfig one 55 # If the format does not match, the content will not be changed and fall down to the next 56 57 # Bool types 58 string(REGEX REPLACE 59 "set\\([ \t\r\n]*([A-Za-z0-9_]*)[ \t\r\n]*(TRUE|ON)[ \t\r\n]*\\)" 60 "config \\1\n default y\n" 61 MATCHED_ITEM ${MATCHED_ITEM}) 62 string(REGEX REPLACE 63 "set\\([ \t\r\n]*([A-Za-z0-9_]*)[ \t\r\n]*(FALSE|OFF)[ \t\r\n]*\\)" 64 "config \\1\n default n\n" 65 MATCHED_ITEM ${MATCHED_ITEM}) 66 # Hex int 67 string(REGEX REPLACE 68 "set\\([ \t\r\n]*([A-Za-z0-9_]*)[ \t\r\n]*(0x[0-9a-fA-F]+[ \t\r\n]*)\\)" 69 "config \\1\n default \\2\n" 70 MATCHED_ITEM ${MATCHED_ITEM}) 71 # Decimal int 72 string(REGEX REPLACE 73 "set\\([ \t\r\n]*([A-Za-z0-9_]*)[ \t\r\n]*([0-9]+)[ \t\r\n]*\\)" 74 "config \\1\n default \\2\n" 75 MATCHED_ITEM ${MATCHED_ITEM}) 76 # Quoted string 77 string(REGEX REPLACE 78 "set\\([ \t\r\n]*([A-Za-z0-9_]*)[ \t\r\n]*(\".*\")[ \t\r\n]*\\)" 79 "config \\1\n default \\2\n" 80 MATCHED_ITEM ${MATCHED_ITEM}) 81 # If none of the above matches, must be a non-quoted string 82 string(REGEX REPLACE 83 "set\\([ \t\r\n]*([A-Za-z0-9_]*)[ \t\r\n]*(.*)[ \t\r\n]*\\)" 84 "config \\1\n default \"\\2\"\n" 85 MATCHED_ITEM ${MATCHED_ITEM}) 86 87 string(APPEND local_var ${MATCHED_ITEM}) 88 endforeach() 89 90 set(${out_var} ${local_var} PARENT_SCOPE) 91endfunction() 92 93# This function goes through the CMake cache variables and convert them to .config format. 94# The function distinguishes command-line variables and other ones and it can only handle one of 95# them in the same time. 96function(convert_cache_config_to_dotconfig convert_cl_var out_var) 97 # Operate on a local var and write back to the out_var later 98 set(local_var "") 99 100 get_cmake_property(CACHE_VARS CACHE_VARIABLES) 101 foreach(CACHE_VAR ${CACHE_VARS}) 102 get_property(HELP_STRING CACHE ${CACHE_VAR} PROPERTY HELPSTRING) 103 104 if("${HELP_STRING}" MATCHES "variable specified on the command line") 105 # Command-line variables have the help string above by default 106 set(IS_CL_VAR TRUE) 107 else() 108 set(IS_CL_VAR FALSE) 109 endif() 110 111 if((IS_CL_VAR AND NOT ${convert_cl_var}) OR (NOT IS_CL_VAR AND ${convert_cl_var})) 112 continue() 113 endif() 114 115 set(CACHE_VAR_VAL ${${CACHE_VAR}}) 116 STRING(TOUPPER "${CACHE_VAR_VAL}" CACHE_VAR_VAL_UPPER) 117 118 set(CACHE_VAR "CONFIG_${CACHE_VAR}") 119 120 set(KCONFIG_OPTION_ITEM "") 121 122 # False CMAKE values 123 if(CACHE_VAR_VAL_UPPER STREQUAL "OFF" OR CACHE_VAR_VAL_UPPER STREQUAL "FALSE") 124 set(KCONFIG_OPTION_ITEM "${CACHE_VAR}=n\r\n") 125 # True CMAKE Values 126 elseif(CACHE_VAR_VAL_UPPER STREQUAL "ON" OR CACHE_VAR_VAL_UPPER STREQUAL "TRUE") 127 set(KCONFIG_OPTION_ITEM "${CACHE_VAR}=y\r\n") 128 # Non-quoted values (hex and decimal numbers) 129 elseif(CACHE_VAR_VAL MATCHES "^0x[a-fA-F0-9]+$" OR CACHE_VAR_VAL MATCHES "^[0-9]+$" ) 130 set(KCONFIG_OPTION_ITEM "${CACHE_VAR}=${CACHE_VAR_VAL}\r\n") 131 # Everything else is a quoted string 132 else() 133 if(${CACHE_VAR} STREQUAL "CONFIG_TEST_PSA_API" AND CACHE_VAR_VAL_UPPER) 134 # Turn on the corresponding "choice" option for psa-arch-test 135 list(APPEND _LEGAL_PSA_API_TEST_LIST "IPC" "CRYPTO" "INITIAL_ATTESTATION" "INTERNAL_TRUSTED_STORAGE" "PROTECTED_STORAGE" "STORAGE") 136 list(FIND _LEGAL_PSA_API_TEST_LIST ${CACHE_VAR_VAL_UPPER} _RET_VAL) 137 if(NOT ${_RET_VAL} EQUAL -1) 138 set(KCONFIG_OPTION_ITEM "CONFIG_PSA_API_TEST_${CACHE_VAR_VAL_UPPER}=y\r\n") 139 140 if(${CACHE_VAR_VAL_UPPER} STREQUAL "IPC") 141 # PSA API IPC test requires IPC model to be enabled while 142 # the CONFIG_TFM_SPM_BACKEND_IPC cannot be selected or implied because it is a choice. 143 # It can be only enabled in a Kconfig config file. So append it here. 144 string(APPEND KCONFIG_OPTION_ITEM "CONFIG_CONFIG_TFM_SPM_BACKEND_IPC=y\r\n") 145 endif() 146 endif() 147 elseif(${CACHE_VAR} STREQUAL "CONFIG_CONFIG_TFM_SPM_BACKEND") 148 # Turn on the corresponding "choice" option for SPM backend 149 set(KCONFIG_OPTION_ITEM "CONFIG_CONFIG_TFM_SPM_BACKEND_${CACHE_VAR_VAL_UPPER}=y\r\n") 150 else() 151 set(KCONFIG_OPTION_ITEM "${CACHE_VAR}=\"${CACHE_VAR_VAL}\"\r\n") 152 endif() 153 endif() 154 155 string(APPEND local_var ${KCONFIG_OPTION_ITEM}) 156 endforeach() 157 158 set(${out_var} ${local_var} PARENT_SCOPE) 159endfunction() 160 161# Initialize the .cl_config 162set(COMMAND_LINE_CONFIG_TO_FILE ${KCONFIG_OUTPUT_DIR}/.cl_config) 163file(REMOVE ${COMMAND_LINE_CONFIG_TO_FILE}) 164 165# Initialize the .cache_var_config 166set(CACHE_VAR_CONFIG_FILE ${KCONFIG_OUTPUT_DIR}/.cache_var_config) 167file(REMOVE ${CACHE_VAR_CONFIG_FILE}) 168 169if(NOT EXISTS ${PLATFORM_KCONFIG} AND NOT EXISTS ${DOTCONFIG_FILE}) 170 # Parse platform's preload.cmake and config.cmake to get config options. 171 set(PLATFORM_KCONFIG_OPTIONS "") 172 set(PLATFORM_KCONFIG ${KCONFIG_OUTPUT_DIR}/platform/Kconfig) 173 174 convert_normal_cmake_config_to_kconfig(${TARGET_PLATFORM_PATH}/preload.cmake PLATFORM_KCONFIG_OPTIONS) 175 file(WRITE ${PLATFORM_KCONFIG} ${PLATFORM_KCONFIG_OPTIONS}) 176 177 if(EXISTS ${TARGET_PLATFORM_PATH}/config.cmake) 178 include(${TARGET_PLATFORM_PATH}/config.cmake) 179 convert_normal_cmake_config_to_kconfig(${TARGET_PLATFORM_PATH}/config.cmake PLATFORM_KCONFIG_OPTIONS) 180 file(APPEND ${PLATFORM_KCONFIG} ${PLATFORM_KCONFIG_OPTIONS}) 181 182 set(PLATFORM_CMAKE_CONFIGS "") 183 set(CONVERT_CL_VAR FALSE) 184 convert_cache_config_to_dotconfig(CONVERT_CL_VAR PLATFORM_CMAKE_CONFIGS) 185 file(APPEND ${CACHE_VAR_CONFIG_FILE} ${PLATFORM_CMAKE_CONFIGS}) 186 endif() 187endif() 188get_filename_component(PLATFORM_KCONFIG_PATH ${PLATFORM_KCONFIG} DIRECTORY) 189 190# Build type Kconfig file, for example 'Kconfig.minsizerel', the suffix passed 191# by Kconfig environment variables and it shall be lowercase. 192string(TOLOWER "${CMAKE_BUILD_TYPE}" CMAKE_BUILD_TYPE_LOWERCASE) 193 194# TF-M profile config file 195if(TFM_PROFILE) 196 set(TFM_PROFILE_KCONFIG_FILE ${CMAKE_SOURCE_DIR}/config/profile/${TFM_PROFILE}.conf) 197 if(NOT EXISTS ${TFM_PROFILE_KCONFIG_FILE}) 198 message(FATAL_ERROR "No such file: ${TFM_PROFILE_KCONFIG_FILE}, please check ${TFM_PROFILE} is right.") 199 endif() 200 201 set(TFM_PROFILE_TEST_KCONFIG_FILE ${CMAKE_SOURCE_DIR}/lib/ext/tf-m-tests/${TFM_PROFILE}_test.conf) 202 if(NOT EXISTS ${TFM_PROFILE_TEST_KCONFIG_FILE}) 203 message(FATAL_ERROR "No such file: ${TFM_PROFILE_TEST_KCONFIG_FILE}, please check ${TFM_PROFILE} is right.") 204 endif() 205endif() 206 207# Parse command-line variables 208set(CL_CONFIGS "") 209set(CONVERT_CL_VAR TRUE) 210convert_cache_config_to_dotconfig(CONVERT_CL_VAR CL_CONFIGS) 211file(APPEND ${COMMAND_LINE_CONFIG_TO_FILE} ${CL_CONFIGS}) 212 213if(NOT EXISTS ${CACHE_VAR_CONFIG_FILE}) 214 set(CACHE_VAR_CONFIG_FILE "") 215endif() 216 217# User customized config file 218if(DEFINED KCONFIG_CONFIG_FILE AND NOT EXISTS ${KCONFIG_CONFIG_FILE}) 219 message(FATAL_ERROR "No such file: ${KCONFIG_CONFIG_FILE}") 220endif() 221 222# Note the order of CONFIG_FILE_LIST, as the first loaded configs would be 223# overridden by later ones. 224list(APPEND CONFIG_FILE_LIST 225 ${TFM_PROFILE_KCONFIG_FILE} 226 ${TFM_PROFILE_TEST_KCONFIG_FILE} 227 ${CACHE_VAR_CONFIG_FILE} 228 ${KCONFIG_CONFIG_FILE} 229 ${COMMAND_LINE_CONFIG_TO_FILE}) 230 231# Set up ENV variables for the tfm_kconfig.py which are then consumed by Kconfig files. 232set(KCONFIG_ENV_VARS "TFM_SOURCE_DIR=${CMAKE_SOURCE_DIR} \ 233 PLATFORM_PATH=${PLATFORM_KCONFIG_PATH} \ 234 CMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE_LOWERCASE}") 235 236if(MENUCONFIG) 237 # Note: Currently, only GUI menuconfig can be supported with CMake integration 238 set(MENUCONFIG_ARG "-u=gui") 239else() 240 set(MENUCONFIG_ARG "") 241endif() 242 243if(DEFINED PROJECT_CONFIG_HEADER_FILE) 244 get_property(HELP_STRING CACHE PROJECT_CONFIG_HEADER_FILE PROPERTY HELPSTRING) 245 246 # It is not supported to set PROJECT_CONFIG_HEADER_FILE while using Kconfig, either from 247 # command line or CMake files. It should be set to the file generated the Kconfig system. 248 # As this file set it itself, if the user re-run the CMake config command, the 249 # PROJECT_CONFIG_HEADER_FILE will be already defined set. 250 # So the existence of the ${DOTCONFIG_FILE} is used to indicate if it is a re-configuration. 251 if("${HELP_STRING}" MATCHES "variable specified on the command line" OR NOT EXISTS ${DOTCONFIG_FILE}) 252 message(FATAL_ERROR "It is NOT supported to manually set PROJECT_CONFIG_HEADER_FILE while using Kconfig.") 253 endif() 254endif() 255 256find_package(Python3) 257 258execute_process( 259 COMMAND 260 ${Python3_EXECUTABLE} ${CMAKE_SOURCE_DIR}/tools/kconfig/tfm_kconfig.py 261 -k ${ROOT_KCONFIG} -o ${KCONFIG_OUTPUT_DIR} 262 --envs ${KCONFIG_ENV_VARS} 263 --config-files ${CONFIG_FILE_LIST} 264 ${MENUCONFIG_ARG} 265 WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} 266 RESULT_VARIABLE ret 267) 268 269if(NOT ret EQUAL 0) 270 message(FATAL_ERROR "Kconfig tool failed!") 271endif() 272 273# Component configs generated by tfm_kconfig.py 274set(PROJECT_CONFIG_HEADER_FILE ${KCONFIG_OUTPUT_DIR}/project_config.h CACHE FILEPATH "User defined header file for TF-M config") 275 276# Load project cmake configs generated by tfm_kconfig.py 277include(${KCONFIG_OUTPUT_DIR}/project_config.cmake) 278