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. Did you mean:\n") 222 execute_process(${list_boards_commands} --fuzzy-match ${BOARD}) 223 message("\nRun 'west boards' for the full list.") 224 unset(CACHED_BOARD CACHE) 225 message(FATAL_ERROR "Invalid BOARD; see above.") 226endif() 227 228cmake_path(IS_PREFIX ZEPHYR_BASE "${BOARD_DIR}" NORMALIZE in_zephyr_tree) 229if(NOT in_zephyr_tree) 230 set(USING_OUT_OF_TREE_BOARD 1) 231endif() 232 233if(LIST_BOARD_REVISION_FORMAT) 234 if(LIST_BOARD_REVISION_FORMAT STREQUAL "custom") 235 include(${BOARD_DIR}/revision.cmake) 236 else() 237 if(EXISTS ${BOARD_DIR}/revision.cmake) 238 message(WARNING 239 "revision.cmake ignored, revision.cmake is only used for revision format: 'custom'" 240 ) 241 endif() 242 243 string(TOUPPER "${LIST_BOARD_REVISION_FORMAT}" rev_format) 244 if(LIST_BOARD_REVISION_EXACT) 245 set(rev_exact EXACT) 246 endif() 247 248 board_check_revision( 249 FORMAT ${rev_format} 250 DEFAULT_REVISION ${LIST_BOARD_REVISION_DEFAULT} 251 VALID_REVISIONS ${LIST_BOARD_REVISIONS} 252 ${rev_exact} 253 ) 254 endif() 255elseif(DEFINED BOARD_REVISION) 256 if(EXISTS ${BOARD_DIR}/revision.cmake) 257 message(WARNING 258 "revision.cmake is not used, revisions must be defined in '${BOARD_DIR}/board.yml'" 259 ) 260 endif() 261 262 message(FATAL_ERROR "Invalid board revision: ${BOARD_REVISION}\n" 263 "Board '${BOARD}' does not define any revisions." 264 ) 265endif() 266 267if(LIST_BOARD_QUALIFIERS) 268 # Allow users to omit the SoC when building for a board with a single SoC. 269 list(LENGTH LIST_BOARD_SOCS socs_length) 270 if(socs_length EQUAL 1) 271 set(BOARD_SINGLE_SOC TRUE) 272 set(BOARD_${BOARD}_SINGLE_SOC TRUE) 273 if(NOT DEFINED BOARD_QUALIFIERS) 274 set(BOARD_QUALIFIERS "/${LIST_BOARD_SOCS}") 275 elseif("${BOARD_QUALIFIERS}" MATCHES "^//.*") 276 string(REGEX REPLACE "^//" "/${LIST_BOARD_SOCS}/" BOARD_QUALIFIERS "${BOARD_QUALIFIERS}") 277 endif() 278 endif() 279 280 set(board_targets ${LIST_BOARD_QUALIFIERS}) 281 list(TRANSFORM board_targets PREPEND "${BOARD}/") 282 if(NOT ("${BOARD}${BOARD_QUALIFIERS}" IN_LIST board_targets)) 283 string(REPLACE ";" "\n" board_targets "${board_targets}") 284 unset(CACHED_BOARD CACHE) 285 message(FATAL_ERROR "Board qualifiers `${BOARD_QUALIFIERS}` for board \ 286 `${BOARD}` not found. Please specify a valid board target.\n" 287 "Valid board targets for ${LIST_BOARD_NAME} are:\n${board_targets}\n") 288 endif() 289endif() 290 291set(board_message "Board: ${BOARD}") 292 293if(DEFINED BOARD_REVISION) 294 set(board_message "${board_message}, Revision: ${BOARD_REVISION}") 295 if(DEFINED ACTIVE_BOARD_REVISION) 296 set(board_message "${board_message} (Active: ${ACTIVE_BOARD_REVISION})") 297 set(BOARD_REVISION ${ACTIVE_BOARD_REVISION}) 298 endif() 299 300 string(REPLACE "." "_" BOARD_REVISION_STRING ${BOARD_REVISION}) 301endif() 302 303if(DEFINED BOARD_QUALIFIERS) 304 string(REGEX REPLACE "^/" "qualifiers: " board_message_qualifiers "${BOARD_QUALIFIERS}") 305 set(board_message "${board_message}, ${board_message_qualifiers}") 306 307 string(REPLACE "/" "_" NORMALIZED_BOARD_QUALIFIERS "${BOARD_QUALIFIERS}") 308endif() 309 310set(NORMALIZED_BOARD_TARGET "${BOARD}${BOARD_QUALIFIERS}") 311string(REPLACE "/" "_" NORMALIZED_BOARD_TARGET "${NORMALIZED_BOARD_TARGET}") 312 313message(STATUS "${board_message}") 314 315add_custom_target(boards ${list_boards_commands} USES_TERMINAL) 316 317# Board extensions are enabled by default 318set(BOARD_EXTENSIONS ON CACHE BOOL "Support board extensions") 319zephyr_get(BOARD_EXTENSIONS) 320 321# Process board extensions 322if(BOARD_EXTENSIONS) 323 get_filename_component(board_dir_name ${BOARD_DIR} NAME) 324 325 foreach(root ${BOARD_ROOT}) 326 set(board_extension_dir ${root}/boards/extensions/${board_dir_name}) 327 if(NOT EXISTS ${board_extension_dir}) 328 continue() 329 endif() 330 331 list(APPEND BOARD_EXTENSION_DIRS ${board_extension_dir}) 332 endforeach() 333endif() 334build_info(board name VALUE ${BOARD}) 335string(REGEX REPLACE "^/" "" qualifiers "${BOARD_QUALIFIERS}") 336build_info(board qualifiers VALUE ${qualifiers}) 337build_info(board revision VALUE ${BOARD_REVISION}) 338build_info(board path PATH ${BOARD_DIRECTORIES}) 339