1# SPDX-License-Identifier: Apache-2.0 2# 3# Copyright (c) 2021, Nordic Semiconductor ASA 4 5# Validate board and setup boards target. 6# 7# This CMake module will validate the BOARD argument as well as splitting the 8# BOARD argument into <BOARD> and <BOARD_REVISION>. When BOARD_EXTENSIONS option 9# is enabled (default) this module will also take care of finding board 10# extension directories. 11# 12# If a board implementation is not found for the specified board an error will 13# be raised and list of valid boards will be printed. 14# 15# If user provided board is a board alias, the board will be adjusted to real 16# board name. 17# 18# If board name is deprecated, then board will be adjusted to new board name and 19# a deprecation warning will be printed to the user. 20# 21# Outcome: 22# The following variables will be defined when this CMake module completes: 23# 24# - BOARD: Board, without revision field. 25# - BOARD_REVISION: Board revision 26# - BOARD_QUALIFIERS: Board qualifiers 27# - NORMALIZED_BOARD_QUALIFIERS: Board qualifiers in lower-case format where slashes have been 28# replaced with underscores 29# - NORMALIZED_BOARD_TARGET: Board target in lower-case format where slashes have been 30# replaced with underscores 31# - BOARD_DIR: Board directory with the implementation for selected board 32# - ARCH_DIR: Arch dir for extracted from selected board 33# - BOARD_ROOT: BOARD_ROOT with ZEPHYR_BASE appended 34# - BOARD_EXTENSION_DIRS: List of board extension directories (If 35# BOARD_EXTENSIONS is not explicitly disabled) 36# 37# The following targets will be defined when this CMake module completes: 38# - board: when invoked, a list of valid boards will be printed 39# 40# Required variables: 41# - BOARD: Board name, including any optional revision field, for example: `foo` or `foo@1.0.0` 42# 43# Optional variables: 44# - BOARD_ROOT: CMake list of board roots containing board implementations 45# - ARCH_ROOT: CMake list of arch roots containing arch implementations 46# 47# Optional environment variables: 48# - ZEPHYR_BOARD_ALIASES: Environment setting pointing to a CMake file 49# containing board aliases. 50# 51# Variables set by this module and not mentioned above are for internal 52# use only, and may be removed, renamed, or re-purposed without prior notice. 53 54include_guard(GLOBAL) 55 56include(python) 57include(extensions) 58 59# Check that BOARD has been provided, and that it has not changed. 60# If user tries to change the BOARD, the BOARD value is reset to the BOARD_CACHED value. 61zephyr_check_cache(BOARD REQUIRED) 62 63# 'BOARD_ROOT' is a prioritized list of directories where boards may 64# be found. It always includes ${ZEPHYR_BASE} at the lowest priority (except for unittesting). 65if(NOT unittest IN_LIST Zephyr_FIND_COMPONENTS) 66 list(APPEND BOARD_ROOT ${ZEPHYR_BASE}) 67endif() 68 69# Helper function for parsing a board's name, revision, and qualifiers, 70# from one input variable to three separate output variables. 71function(parse_board_components board_in name_out revision_out qualifiers_out) 72 if(NOT "${${board_in}}" MATCHES "^([^@/]+)(@[^@/]+)?(/[^@]+)?$") 73 message(FATAL_ERROR 74 "Invalid revision / qualifiers format for ${board_in} (${${board_in}}). " 75 "Valid format is: <board>@<revision>/<qualifiers>" 76 ) 77 endif() 78 string(REPLACE "@" "" board_revision "${CMAKE_MATCH_2}") 79 80 set(${name_out} ${CMAKE_MATCH_1} PARENT_SCOPE) 81 set(${revision_out} ${board_revision} PARENT_SCOPE) 82 set(${qualifiers_out} ${CMAKE_MATCH_3} PARENT_SCOPE) 83endfunction() 84 85parse_board_components( 86 BOARD 87 BOARD BOARD_REVISION BOARD_QUALIFIERS 88) 89 90zephyr_get(ZEPHYR_BOARD_ALIASES) 91if(DEFINED ZEPHYR_BOARD_ALIASES) 92 include(${ZEPHYR_BOARD_ALIASES}) 93 if(${BOARD}_BOARD_ALIAS) 94 set(BOARD_ALIAS ${BOARD} CACHE STRING "Board alias, provided by user") 95 parse_board_components( 96 ${BOARD}_BOARD_ALIAS 97 BOARD BOARD_ALIAS_REVISION BOARD_ALIAS_QUALIFIERS 98 ) 99 message(STATUS "Aliased BOARD=${BOARD_ALIAS} changed to ${BOARD}") 100 if(NOT DEFINED BOARD_REVISION) 101 set(BOARD_REVISION ${BOARD_ALIAS_REVISION}) 102 endif() 103 set(BOARD_QUALIFIERS ${BOARD_ALIAS_QUALIFIERS}${BOARD_QUALIFIERS}) 104 endif() 105endif() 106 107include(${ZEPHYR_BASE}/boards/deprecated.cmake) 108if(${BOARD}${BOARD_QUALIFIERS}_DEPRECATED) 109 set(BOARD_DEPRECATED ${BOARD}${BOARD_QUALIFIERS} CACHE STRING "Deprecated BOARD, provided by user") 110 message(WARNING 111 "Deprecated BOARD=${BOARD_DEPRECATED} specified, " 112 "board automatically changed to: ${${BOARD}${BOARD_QUALIFIERS}_DEPRECATED}." 113 ) 114 parse_board_components( 115 ${BOARD}${BOARD_QUALIFIERS}_DEPRECATED 116 BOARD BOARD_DEPRECATED_REVISION BOARD_QUALIFIERS 117 ) 118 if(DEFINED BOARD_DEPRECATED_REVISION) 119 if(DEFINED BOARD_REVISION) 120 message(FATAL_ERROR 121 "Invalid board revision: ${BOARD_REVISION}\n" 122 "Deprecated board '${BOARD_DEPRECATED}' is now implemented as a revision of another board " 123 "(${BOARD}@${BOARD_DEPRECATED_REVISION}), so the specified revision does not apply. " 124 "Please consult the documentation for '${BOARD}' to see how to build for the new board." 125 ) 126 endif() 127 set(BOARD_REVISION ${BOARD_DEPRECATED_REVISION}) 128 endif() 129endif() 130 131zephyr_boilerplate_watch(BOARD) 132 133foreach(root ${BOARD_ROOT}) 134 # Check that the board root looks reasonable. 135 if(NOT IS_DIRECTORY "${root}/boards") 136 message(WARNING "BOARD_ROOT element without a 'boards' subdirectory: 137${root} 138Hints: 139 - if your board directory is '/foo/bar/boards/my_board' then add '/foo/bar' to BOARD_ROOT, not the entire board directory 140 - if in doubt, use absolute paths") 141 endif() 142endforeach() 143 144if(HWMv2 AND NOT EXISTS ${BOARD_DIR}/board.yml) 145 message(WARNING "BOARD_DIR: ${BOARD_DIR} has been moved or deleted. " 146 "Trying to find new location." 147 ) 148 set(BOARD_DIR BOARD_DIR-NOTFOUND CACHE PATH "Path to a file." FORCE) 149endif() 150 151# Prepare list boards command. 152# This command is used for locating the board dir as well as printing all boards 153# in the system in the following cases: 154# - User specifies an invalid BOARD 155# - User invokes '<build-command> boards' target 156list(TRANSFORM ARCH_ROOT PREPEND "--arch-root=" OUTPUT_VARIABLE arch_root_args) 157list(TRANSFORM BOARD_ROOT PREPEND "--board-root=" OUTPUT_VARIABLE board_root_args) 158list(TRANSFORM SOC_ROOT PREPEND "--soc-root=" OUTPUT_VARIABLE soc_root_args) 159 160set(list_boards_commands 161 COMMAND ${PYTHON_EXECUTABLE} ${ZEPHYR_BASE}/scripts/list_boards.py 162 ${arch_root_args} ${board_root_args} --arch-root=${ZEPHYR_BASE} 163 ${soc_root_args} --soc-root=${ZEPHYR_BASE} 164) 165 166if(NOT BOARD_DIR) 167 if(BOARD_ALIAS) 168 execute_process(${list_boards_commands} --board=${BOARD_ALIAS} --cmakeformat={DIR} 169 OUTPUT_VARIABLE ret_board 170 ERROR_VARIABLE err_board 171 RESULT_VARIABLE ret_val 172 ) 173 string(STRIP "${ret_board}" ret_board) 174 cmake_parse_arguments(BOARD_HIDDEN "" "DIR" "" ${ret_board}) 175 set(BOARD_HIDDEN_DIR ${BOARD_HIDDEN_DIR} CACHE PATH "Path to a folder." FORCE) 176 177 if(BOARD_HIDDEN_DIR) 178 message("Board alias ${BOARD_ALIAS} is hiding the real board of same name") 179 endif() 180 endif() 181endif() 182 183set(format_str "{NAME}\;{DIR}\;{HWM}\;") 184set(format_str "${format_str}{REVISION_FORMAT}\;{REVISION_DEFAULT}\;{REVISION_EXACT}\;") 185set(format_str "${format_str}{REVISIONS}\;{SOCS}\;{QUALIFIERS}") 186 187list(TRANSFORM BOARD_DIRECTORIES PREPEND "--board-dir=" OUTPUT_VARIABLE board_dir_arg) 188execute_process(${list_boards_commands} --board=${BOARD} ${board_dir_arg} 189 --cmakeformat=${format_str} 190 OUTPUT_VARIABLE ret_board 191 ERROR_VARIABLE err_board 192 RESULT_VARIABLE ret_val 193) 194if(ret_val) 195 message(FATAL_ERROR "Error finding board: ${BOARD}\nError message: ${err_board}") 196endif() 197 198if(NOT "${ret_board}" STREQUAL "") 199 string(STRIP "${ret_board}" ret_board) 200 set(single_val "NAME;HWM;REVISION_FORMAT;REVISION_DEFAULT;REVISION_EXACT") 201 set(multi_val "DIR;REVISIONS;SOCS;QUALIFIERS") 202 cmake_parse_arguments(LIST_BOARD "" "${single_val}" "${multi_val}" ${ret_board}) 203 list(GET LIST_BOARD_DIR 0 BOARD_DIR) 204 set(BOARD_DIR ${BOARD_DIR} CACHE PATH "Main board directory for board (${BOARD})" FORCE) 205 set(BOARD_DIRECTORIES ${LIST_BOARD_DIR} CACHE INTERNAL "List of board directories for board (${BOARD})" FORCE) 206 foreach(dir ${BOARD_DIRECTORIES}) 207 set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS ${dir}/board.yml) 208 endforeach() 209 210 # Create two CMake variables identifying the hw model. 211 # CMake variable: HWM=v2 212 # CMake variable: HWMv2=True 213 set(HWM ${LIST_BOARD_HWM} CACHE INTERNAL "Zephyr hardware model version") 214 set(HWM${HWM} True CACHE INTERNAL "Zephyr hardware model") 215elseif(BOARD_DIR) 216 message(FATAL_ERROR "Error finding board: ${BOARD} in ${BOARD_DIR}.\n" 217 "This indicates the board has been removed, renamed, or placed at a new location.\n" 218 "Please run a pristine build." 219 ) 220else() 221 message("No board named '${BOARD}' found.\n\n" 222 "Please choose one of the following boards:\n" 223 ) 224 execute_process(${list_boards_commands}) 225 unset(CACHED_BOARD CACHE) 226 message(FATAL_ERROR "Invalid BOARD; see above.") 227endif() 228 229cmake_path(IS_PREFIX ZEPHYR_BASE "${BOARD_DIR}" NORMALIZE in_zephyr_tree) 230if(NOT in_zephyr_tree) 231 set(USING_OUT_OF_TREE_BOARD 1) 232endif() 233 234if(LIST_BOARD_REVISION_FORMAT) 235 if(LIST_BOARD_REVISION_FORMAT STREQUAL "custom") 236 include(${BOARD_DIR}/revision.cmake) 237 else() 238 if(EXISTS ${BOARD_DIR}/revision.cmake) 239 message(WARNING 240 "revision.cmake ignored, revision.cmake is only used for revision format: 'custom'" 241 ) 242 endif() 243 244 string(TOUPPER "${LIST_BOARD_REVISION_FORMAT}" rev_format) 245 if(LIST_BOARD_REVISION_EXACT) 246 set(rev_exact EXACT) 247 endif() 248 249 board_check_revision( 250 FORMAT ${rev_format} 251 DEFAULT_REVISION ${LIST_BOARD_REVISION_DEFAULT} 252 VALID_REVISIONS ${LIST_BOARD_REVISIONS} 253 ${rev_exact} 254 ) 255 endif() 256elseif(DEFINED BOARD_REVISION) 257 if(EXISTS ${BOARD_DIR}/revision.cmake) 258 message(WARNING 259 "revision.cmake is not used, revisions must be defined in '${BOARD_DIR}/board.yml'" 260 ) 261 endif() 262 263 message(FATAL_ERROR "Invalid board revision: ${BOARD_REVISION}\n" 264 "Board '${BOARD}' does not define any revisions." 265 ) 266endif() 267 268if(LIST_BOARD_QUALIFIERS) 269 # Allow users to omit the SoC when building for a board with a single SoC. 270 list(LENGTH LIST_BOARD_SOCS socs_length) 271 if(socs_length EQUAL 1) 272 set(BOARD_SINGLE_SOC TRUE) 273 set(BOARD_${BOARD}_SINGLE_SOC TRUE) 274 if(NOT DEFINED BOARD_QUALIFIERS) 275 set(BOARD_QUALIFIERS "/${LIST_BOARD_SOCS}") 276 elseif("${BOARD_QUALIFIERS}" MATCHES "^//.*") 277 string(REGEX REPLACE "^//" "/${LIST_BOARD_SOCS}/" BOARD_QUALIFIERS "${BOARD_QUALIFIERS}") 278 endif() 279 endif() 280 281 set(board_targets ${LIST_BOARD_QUALIFIERS}) 282 list(TRANSFORM board_targets PREPEND "${BOARD}/") 283 if(NOT ("${BOARD}${BOARD_QUALIFIERS}" IN_LIST board_targets)) 284 string(REPLACE ";" "\n" board_targets "${board_targets}") 285 unset(CACHED_BOARD CACHE) 286 message(FATAL_ERROR "Board qualifiers `${BOARD_QUALIFIERS}` for board \ 287 `${BOARD}` not found. Please specify a valid board target.\n" 288 "Valid board targets for ${LIST_BOARD_NAME} are:\n${board_targets}\n") 289 endif() 290endif() 291 292set(board_message "Board: ${BOARD}") 293 294if(DEFINED BOARD_REVISION) 295 set(board_message "${board_message}, Revision: ${BOARD_REVISION}") 296 if(DEFINED ACTIVE_BOARD_REVISION) 297 set(board_message "${board_message} (Active: ${ACTIVE_BOARD_REVISION})") 298 set(BOARD_REVISION ${ACTIVE_BOARD_REVISION}) 299 endif() 300 301 string(REPLACE "." "_" BOARD_REVISION_STRING ${BOARD_REVISION}) 302endif() 303 304if(DEFINED BOARD_QUALIFIERS) 305 string(REGEX REPLACE "^/" "qualifiers: " board_message_qualifiers "${BOARD_QUALIFIERS}") 306 set(board_message "${board_message}, ${board_message_qualifiers}") 307 308 string(REPLACE "/" "_" NORMALIZED_BOARD_QUALIFIERS "${BOARD_QUALIFIERS}") 309endif() 310 311set(NORMALIZED_BOARD_TARGET "${BOARD}${BOARD_QUALIFIERS}") 312string(REPLACE "/" "_" NORMALIZED_BOARD_TARGET "${NORMALIZED_BOARD_TARGET}") 313 314message(STATUS "${board_message}") 315 316add_custom_target(boards ${list_boards_commands} USES_TERMINAL) 317 318# Board extensions are enabled by default 319set(BOARD_EXTENSIONS ON CACHE BOOL "Support board extensions") 320zephyr_get(BOARD_EXTENSIONS) 321 322# Process board extensions 323if(BOARD_EXTENSIONS) 324 get_filename_component(board_dir_name ${BOARD_DIR} NAME) 325 326 foreach(root ${BOARD_ROOT}) 327 set(board_extension_dir ${root}/boards/extensions/${board_dir_name}) 328 if(NOT EXISTS ${board_extension_dir}) 329 continue() 330 endif() 331 332 list(APPEND BOARD_EXTENSION_DIRS ${board_extension_dir}) 333 endforeach() 334endif() 335build_info(board name VALUE ${BOARD}) 336string(REGEX REPLACE "^/" "" qualifiers "${BOARD_QUALIFIERS}") 337build_info(board qualifiers VALUE ${qualifiers}) 338build_info(board revision VALUE ${BOARD_REVISION}) 339build_info(board path PATH ${BOARD_DIRECTORIES}) 340