Jianliang Shen | 7e48bef | 2022-10-31 16:15:36 +0800 | [diff] [blame] | 1 | #------------------------------------------------------------------------------- |
Kevin Peng | a2b6802 | 2023-01-13 13:54:05 +0800 | [diff] [blame] | 2 | # Copyright (c) 2022-2023, Arm Limited. All rights reserved. |
Jianliang Shen | 7e48bef | 2022-10-31 16:15:36 +0800 | [diff] [blame] | 3 | # |
| 4 | # SPDX-License-Identifier: BSD-3-Clause |
| 5 | # |
| 6 | #------------------------------------------------------------------------------- |
| 7 | |
Kevin Peng | 97ac698 | 2022-11-24 17:10:00 +0800 | [diff] [blame] | 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 | |
| 16 | set(KCONFIG_OUTPUT_DIR ${CMAKE_BINARY_DIR}/kconfig) |
| 17 | |
| 18 | set(DOTCONFIG_FILE ${KCONFIG_OUTPUT_DIR}/.config) |
| 19 | set(ROOT_KCONFIG ${CMAKE_SOURCE_DIR}/Kconfig) |
| 20 | set(PLATFORM_KCONFIG ${TARGET_PLATFORM_PATH}/Kconfig) |
Jianliang Shen | 7e48bef | 2022-10-31 16:15:36 +0800 | [diff] [blame] | 21 | |
Kevin Peng | 026b811 | 2023-01-13 11:24:38 +0800 | [diff] [blame] | 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(". |
| 26 | function(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() |
Kevin Peng | 13f0c25 | 2023-03-23 17:26:38 +0800 | [diff] [blame] | 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() |
Kevin Peng | 026b811 | 2023-01-13 11:24:38 +0800 | [diff] [blame] | 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) |
| 91 | endfunction() |
| 92 | |
Kevin Peng | e72f29e | 2023-01-29 17:05:35 +0800 | [diff] [blame] | 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. |
| 96 | function(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 "") |
Kevin Peng | b9c99f0 | 2022-12-07 11:23:17 +0800 | [diff] [blame] | 99 | |
Kevin Peng | e72f29e | 2023-01-29 17:05:35 +0800 | [diff] [blame] | 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() |
Kevin Peng | c830526 | 2023-04-11 14:43:54 +0800 | [diff] [blame] | 133 | if(${CACHE_VAR} STREQUAL "CONFIG_TEST_PSA_API" AND CACHE_VAR_VAL_UPPER) |
Kevin Peng | e72f29e | 2023-01-29 17:05:35 +0800 | [diff] [blame] | 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) |
| 159 | endfunction() |
| 160 | |
| 161 | # Initialize the .cl_config |
| 162 | set(COMMAND_LINE_CONFIG_TO_FILE ${KCONFIG_OUTPUT_DIR}/.cl_config) |
| 163 | file(REMOVE ${COMMAND_LINE_CONFIG_TO_FILE}) |
| 164 | |
| 165 | # Initialize the .cache_var_config |
| 166 | set(CACHE_VAR_CONFIG_FILE ${KCONFIG_OUTPUT_DIR}/.cache_var_config) |
| 167 | file(REMOVE ${CACHE_VAR_CONFIG_FILE}) |
| 168 | |
Kevin Peng | 1b4e04f | 2023-04-11 14:33:33 +0800 | [diff] [blame] | 169 | if(NOT EXISTS ${PLATFORM_KCONFIG} AND NOT EXISTS ${DOTCONFIG_FILE}) |
Kevin Peng | 026b811 | 2023-01-13 11:24:38 +0800 | [diff] [blame] | 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 | |
Kevin Peng | b9c99f0 | 2022-12-07 11:23:17 +0800 | [diff] [blame] | 177 | if(EXISTS ${TARGET_PLATFORM_PATH}/config.cmake) |
| 178 | include(${TARGET_PLATFORM_PATH}/config.cmake) |
Kevin Peng | 026b811 | 2023-01-13 11:24:38 +0800 | [diff] [blame] | 179 | convert_normal_cmake_config_to_kconfig(${TARGET_PLATFORM_PATH}/config.cmake PLATFORM_KCONFIG_OPTIONS) |
| 180 | file(APPEND ${PLATFORM_KCONFIG} ${PLATFORM_KCONFIG_OPTIONS}) |
Kevin Peng | e72f29e | 2023-01-29 17:05:35 +0800 | [diff] [blame] | 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}) |
Kevin Peng | b9c99f0 | 2022-12-07 11:23:17 +0800 | [diff] [blame] | 186 | endif() |
| 187 | endif() |
Kevin Peng | 026b811 | 2023-01-13 11:24:38 +0800 | [diff] [blame] | 188 | get_filename_component(PLATFORM_KCONFIG_PATH ${PLATFORM_KCONFIG} DIRECTORY) |
Jianliang Shen | 7e48bef | 2022-10-31 16:15:36 +0800 | [diff] [blame] | 189 | |
Jianliang Shen | 5ec1717 | 2023-02-20 15:10:44 +0800 | [diff] [blame] | 190 | # Build type Kconfig file, for example 'Kconfig.minsizerel', the suffix passed |
| 191 | # by Kconfig environment variables and it shall be lowercase. |
| 192 | string(TOLOWER "${CMAKE_BUILD_TYPE}" CMAKE_BUILD_TYPE_LOWERCASE) |
| 193 | |
Jianliang Shen | 9e90a56 | 2023-02-20 15:04:12 +0800 | [diff] [blame] | 194 | # TF-M profile config file |
| 195 | if(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() |
| 205 | endif() |
| 206 | |
Kevin Peng | e72f29e | 2023-01-29 17:05:35 +0800 | [diff] [blame] | 207 | # Parse command-line variables |
| 208 | set(CL_CONFIGS "") |
| 209 | set(CONVERT_CL_VAR TRUE) |
| 210 | convert_cache_config_to_dotconfig(CONVERT_CL_VAR CL_CONFIGS) |
| 211 | file(APPEND ${COMMAND_LINE_CONFIG_TO_FILE} ${CL_CONFIGS}) |
| 212 | |
| 213 | if(NOT EXISTS ${CACHE_VAR_CONFIG_FILE}) |
| 214 | set(CACHE_VAR_CONFIG_FILE "") |
| 215 | endif() |
| 216 | |
Kevin Peng | 97ac698 | 2022-11-24 17:10:00 +0800 | [diff] [blame] | 217 | # User customized config file |
| 218 | if(DEFINED KCONFIG_CONFIG_FILE AND NOT EXISTS ${KCONFIG_CONFIG_FILE}) |
| 219 | message(FATAL_ERROR "No such file: ${KCONFIG_CONFIG_FILE}") |
| 220 | endif() |
| 221 | |
| 222 | # Note the order of CONFIG_FILE_LIST, as the first loaded configs would be |
| 223 | # overridden by later ones. |
| 224 | list(APPEND CONFIG_FILE_LIST |
Jianliang Shen | 9e90a56 | 2023-02-20 15:04:12 +0800 | [diff] [blame] | 225 | ${TFM_PROFILE_KCONFIG_FILE} |
| 226 | ${TFM_PROFILE_TEST_KCONFIG_FILE} |
Kevin Peng | e72f29e | 2023-01-29 17:05:35 +0800 | [diff] [blame] | 227 | ${CACHE_VAR_CONFIG_FILE} |
| 228 | ${KCONFIG_CONFIG_FILE} |
| 229 | ${COMMAND_LINE_CONFIG_TO_FILE}) |
Kevin Peng | 97ac698 | 2022-11-24 17:10:00 +0800 | [diff] [blame] | 230 | |
| 231 | # Set up ENV variables for the tfm_kconfig.py which are then consumed by Kconfig files. |
Jianliang Shen | c4e719b | 2023-03-02 14:37:54 +0800 | [diff] [blame] | 232 | set(KCONFIG_ENV_VARS "TFM_SOURCE_DIR=${CMAKE_SOURCE_DIR} \ |
Kevin Peng | 026b811 | 2023-01-13 11:24:38 +0800 | [diff] [blame] | 233 | PLATFORM_PATH=${PLATFORM_KCONFIG_PATH} \ |
Jianliang Shen | 5ec1717 | 2023-02-20 15:10:44 +0800 | [diff] [blame] | 234 | CMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE_LOWERCASE}") |
Kevin Peng | 97ac698 | 2022-11-24 17:10:00 +0800 | [diff] [blame] | 235 | |
| 236 | if(MENUCONFIG) |
| 237 | # Note: Currently, only GUI menuconfig can be supported with CMake integration |
| 238 | set(MENUCONFIG_ARG "-u=gui") |
| 239 | else() |
| 240 | set(MENUCONFIG_ARG "") |
| 241 | endif() |
| 242 | |
Jianliang Shen | 7c67ab1 | 2023-03-14 14:41:34 +0800 | [diff] [blame] | 243 | if(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() |
| 254 | endif() |
| 255 | |
Jianliang Shen | 7e48bef | 2022-10-31 16:15:36 +0800 | [diff] [blame] | 256 | find_package(Python3) |
| 257 | |
Jianliang Shen | 7e48bef | 2022-10-31 16:15:36 +0800 | [diff] [blame] | 258 | execute_process( |
| 259 | COMMAND |
| 260 | ${Python3_EXECUTABLE} ${CMAKE_SOURCE_DIR}/tools/kconfig/tfm_kconfig.py |
Kevin Peng | 97ac698 | 2022-11-24 17:10:00 +0800 | [diff] [blame] | 261 | -k ${ROOT_KCONFIG} -o ${KCONFIG_OUTPUT_DIR} |
| 262 | --envs ${KCONFIG_ENV_VARS} |
| 263 | --config-files ${CONFIG_FILE_LIST} |
| 264 | ${MENUCONFIG_ARG} |
Jianliang Shen | 7e48bef | 2022-10-31 16:15:36 +0800 | [diff] [blame] | 265 | WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} |
| 266 | RESULT_VARIABLE ret |
| 267 | ) |
| 268 | |
| 269 | if(NOT ret EQUAL 0) |
| 270 | message(FATAL_ERROR "Kconfig tool failed!") |
| 271 | endif() |
| 272 | |
Kevin Peng | 97ac698 | 2022-11-24 17:10:00 +0800 | [diff] [blame] | 273 | # Component configs generated by tfm_kconfig.py |
| 274 | set(PROJECT_CONFIG_HEADER_FILE ${KCONFIG_OUTPUT_DIR}/project_config.h CACHE FILEPATH "User defined header file for TF-M config") |
| 275 | |
Jianliang Shen | 7e48bef | 2022-10-31 16:15:36 +0800 | [diff] [blame] | 276 | # Load project cmake configs generated by tfm_kconfig.py |
Kevin Peng | 97ac698 | 2022-11-24 17:10:00 +0800 | [diff] [blame] | 277 | include(${KCONFIG_OUTPUT_DIR}/project_config.cmake) |