# SPDX-License-Identifier: Apache-2.0 # # Copyright (c) 2021, Nordic Semiconductor ASA # Validate board and setup boards target. # # This CMake module will validate the BOARD argument as well as splitting the # BOARD argument into and . When BOARD_EXTENSIONS option # is enabled (default) this module will also take care of finding board # extension directories. # # If a board implementation is not found for the specified board an error will # be raised and list of valid boards will be printed. # # If user provided board is a board alias, the board will be adjusted to real # board name. # # If board name is deprecated, then board will be adjusted to new board name and # a deprecation warning will be printed to the user. # # Outcome: # The following variables will be defined when this CMake module completes: # # - BOARD: Board, without revision field. # - BOARD_REVISION: Board revision # - BOARD_QUALIFIERS: Board qualifiers # - NORMALIZED_BOARD_QUALIFIERS: Board qualifiers in lower-case format where slashes have been # replaced with underscores # - NORMALIZED_BOARD_TARGET: Board target in lower-case format where slashes have been # replaced with underscores # - BOARD_DIR: Board directory with the implementation for selected board # - ARCH_DIR: Arch dir for extracted from selected board # - BOARD_ROOT: BOARD_ROOT with ZEPHYR_BASE appended # - BOARD_EXTENSION_DIRS: List of board extension directories (If # BOARD_EXTENSIONS is not explicitly disabled) # # The following targets will be defined when this CMake module completes: # - board: when invoked, a list of valid boards will be printed # # Required variables: # - BOARD: Board name, including any optional revision field, for example: `foo` or `foo@1.0.0` # # Optional variables: # - BOARD_ROOT: CMake list of board roots containing board implementations # - ARCH_ROOT: CMake list of arch roots containing arch implementations # # Optional environment variables: # - ZEPHYR_BOARD_ALIASES: Environment setting pointing to a CMake file # containing board aliases. # # Variables set by this module and not mentioned above are for internal # use only, and may be removed, renamed, or re-purposed without prior notice. include_guard(GLOBAL) include(python) include(extensions) # Check that BOARD has been provided, and that it has not changed. # If user tries to change the BOARD, the BOARD value is reset to the BOARD_CACHED value. zephyr_check_cache(BOARD REQUIRED) # 'BOARD_ROOT' is a prioritized list of directories where boards may # be found. It always includes ${ZEPHYR_BASE} at the lowest priority (except for unittesting). if(NOT unittest IN_LIST Zephyr_FIND_COMPONENTS) list(APPEND BOARD_ROOT ${ZEPHYR_BASE}) endif() # Helper function for parsing a board's name, revision, and qualifiers, # from one input variable to three separate output variables. function(parse_board_components board_in name_out revision_out qualifiers_out) if(NOT "${${board_in}}" MATCHES "^([^@/]+)(@[^@/]+)?(/[^@]+)?$") message(FATAL_ERROR "Invalid revision / qualifiers format for ${board_in} (${${board_in}}). " "Valid format is: @/" ) endif() string(REPLACE "@" "" board_revision "${CMAKE_MATCH_2}") set(${name_out} ${CMAKE_MATCH_1} PARENT_SCOPE) set(${revision_out} ${board_revision} PARENT_SCOPE) set(${qualifiers_out} ${CMAKE_MATCH_3} PARENT_SCOPE) endfunction() parse_board_components( BOARD BOARD BOARD_REVISION BOARD_QUALIFIERS ) zephyr_get(ZEPHYR_BOARD_ALIASES) if(DEFINED ZEPHYR_BOARD_ALIASES) include(${ZEPHYR_BOARD_ALIASES}) if(${BOARD}_BOARD_ALIAS) set(BOARD_ALIAS ${BOARD} CACHE STRING "Board alias, provided by user") parse_board_components( ${BOARD}_BOARD_ALIAS BOARD BOARD_ALIAS_REVISION BOARD_ALIAS_QUALIFIERS ) message(STATUS "Aliased BOARD=${BOARD_ALIAS} changed to ${BOARD}") if(NOT DEFINED BOARD_REVISION) set(BOARD_REVISION ${BOARD_ALIAS_REVISION}) endif() set(BOARD_QUALIFIERS ${BOARD_ALIAS_QUALIFIERS}${BOARD_QUALIFIERS}) endif() endif() include(${ZEPHYR_BASE}/boards/deprecated.cmake) if(${BOARD}${BOARD_QUALIFIERS}_DEPRECATED) set(BOARD_DEPRECATED ${BOARD}${BOARD_QUALIFIERS} CACHE STRING "Deprecated BOARD, provided by user") message(WARNING "Deprecated BOARD=${BOARD_DEPRECATED} specified, " "board automatically changed to: ${${BOARD}${BOARD_QUALIFIERS}_DEPRECATED}." ) parse_board_components( ${BOARD}${BOARD_QUALIFIERS}_DEPRECATED BOARD BOARD_DEPRECATED_REVISION BOARD_QUALIFIERS ) if(DEFINED BOARD_DEPRECATED_REVISION) if(DEFINED BOARD_REVISION) message(FATAL_ERROR "Invalid board revision: ${BOARD_REVISION}\n" "Deprecated board '${BOARD_DEPRECATED}' is now implemented as a revision of another board " "(${BOARD}@${BOARD_DEPRECATED_REVISION}), so the specified revision does not apply. " "Please consult the documentation for '${BOARD}' to see how to build for the new board." ) endif() set(BOARD_REVISION ${BOARD_DEPRECATED_REVISION}) endif() endif() zephyr_boilerplate_watch(BOARD) foreach(root ${BOARD_ROOT}) # Check that the board root looks reasonable. if(NOT IS_DIRECTORY "${root}/boards") message(WARNING "BOARD_ROOT element without a 'boards' subdirectory: ${root} Hints: - if your board directory is '/foo/bar/boards/my_board' then add '/foo/bar' to BOARD_ROOT, not the entire board directory - if in doubt, use absolute paths") endif() endforeach() if((HWMv1 AND NOT EXISTS ${BOARD_DIR}/${BOARD}_defconfig) OR (HWMv2 AND NOT EXISTS ${BOARD_DIR}/board.yml)) message(WARNING "BOARD_DIR: ${BOARD_DIR} has been moved or deleted. " "Trying to find new location." ) set(BOARD_DIR BOARD_DIR-NOTFOUND CACHE PATH "Path to a file." FORCE) endif() # Prepare list boards command. # This command is used for locating the board dir as well as printing all boards # in the system in the following cases: # - User specifies an invalid BOARD # - User invokes ' boards' target list(TRANSFORM ARCH_ROOT PREPEND "--arch-root=" OUTPUT_VARIABLE arch_root_args) list(TRANSFORM BOARD_ROOT PREPEND "--board-root=" OUTPUT_VARIABLE board_root_args) list(TRANSFORM SOC_ROOT PREPEND "--soc-root=" OUTPUT_VARIABLE soc_root_args) set(list_boards_commands COMMAND ${PYTHON_EXECUTABLE} ${ZEPHYR_BASE}/scripts/list_boards.py ${arch_root_args} ${board_root_args} --arch-root=${ZEPHYR_BASE} ${soc_root_args} --soc-root=${ZEPHYR_BASE} ) if(NOT BOARD_DIR) if(BOARD_ALIAS) execute_process(${list_boards_commands} --board=${BOARD_ALIAS} --cmakeformat={DIR} OUTPUT_VARIABLE ret_board ERROR_VARIABLE err_board RESULT_VARIABLE ret_val ) string(STRIP "${ret_board}" ret_board) cmake_parse_arguments(BOARD_HIDDEN "" "DIR" "" ${ret_board}) set(BOARD_HIDDEN_DIR ${BOARD_HIDDEN_DIR} CACHE PATH "Path to a folder." FORCE) if(BOARD_HIDDEN_DIR) message("Board alias ${BOARD_ALIAS} is hiding the real board of same name") endif() endif() endif() set(format_str "{NAME}\;{DIR}\;{HWM}\;") set(format_str "${format_str}{REVISION_FORMAT}\;{REVISION_DEFAULT}\;{REVISION_EXACT}\;") set(format_str "${format_str}{REVISIONS}\;{SOCS}\;{QUALIFIERS}") list(TRANSFORM BOARD_DIRECTORIES PREPEND "--board-dir=" OUTPUT_VARIABLE board_dir_arg) execute_process(${list_boards_commands} --board=${BOARD} ${board_dir_arg} --cmakeformat=${format_str} OUTPUT_VARIABLE ret_board ERROR_VARIABLE err_board RESULT_VARIABLE ret_val ) if(ret_val) message(FATAL_ERROR "Error finding board: ${BOARD}\nError message: ${err_board}") endif() if(NOT "${ret_board}" STREQUAL "") string(STRIP "${ret_board}" ret_board) set(single_val "NAME;HWM;REVISION_FORMAT;REVISION_DEFAULT;REVISION_EXACT") set(multi_val "DIR;REVISIONS;SOCS;QUALIFIERS") cmake_parse_arguments(LIST_BOARD "" "${single_val}" "${multi_val}" ${ret_board}) list(GET LIST_BOARD_DIR 0 BOARD_DIR) set(BOARD_DIR ${BOARD_DIR} CACHE PATH "Main board directory for board (${BOARD})" FORCE) set(BOARD_DIRECTORIES ${LIST_BOARD_DIR} CACHE INTERNAL "List of board directories for board (${BOARD})" FORCE) foreach(dir ${BOARD_DIRECTORIES}) set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS ${dir}/board.yml) endforeach() # Create two CMake variables identifying the hw model. # CMake variable: HWM=[v1,v2] # CMake variable: HWMv1=True, when HWMv1 is in use. # CMake variable: HWMv2=True, when HWMv2 is in use. set(HWM ${LIST_BOARD_HWM} CACHE INTERNAL "Zephyr hardware model version") set(HWM${HWM} True CACHE INTERNAL "Zephyr hardware model") elseif(BOARD_DIR) message(FATAL_ERROR "Error finding board: ${BOARD} in ${BOARD_DIR}.\n" "This indicates the board has been removed, renamed, or placed at a new location.\n" "Please run a pristine build." ) else() message("No board named '${BOARD}' found.\n\n" "Please choose one of the following boards:\n" ) execute_process(${list_boards_commands}) unset(CACHED_BOARD CACHE) message(FATAL_ERROR "Invalid BOARD; see above.") endif() if(HWMv1 AND DEFINED BOARD_QUALIFIERS) message(FATAL_ERROR "Board '${BOARD}' does not support board qualifiers, ${BOARD}${BOARD_QUALIFIERS}.\n" "Please specify board without qualifiers.\n" ) endif() cmake_path(IS_PREFIX ZEPHYR_BASE "${BOARD_DIR}" NORMALIZE in_zephyr_tree) if(NOT in_zephyr_tree) set(USING_OUT_OF_TREE_BOARD 1) endif() if(HWMv1) if(EXISTS ${BOARD_DIR}/revision.cmake) # Board provides revision handling. include(${BOARD_DIR}/revision.cmake) elseif(BOARD_REVISION) message(WARNING "Board revision ${BOARD_REVISION} specified for ${BOARD}, \ but board has no revision so revision will be ignored.") endif() elseif(HWMv2) if(LIST_BOARD_REVISION_FORMAT) if(LIST_BOARD_REVISION_FORMAT STREQUAL "custom") include(${BOARD_DIR}/revision.cmake) else() if(EXISTS ${BOARD_DIR}/revision.cmake) message(WARNING "revision.cmake ignored, revision.cmake is only used for revision format: 'custom'" ) endif() string(TOUPPER "${LIST_BOARD_REVISION_FORMAT}" rev_format) if(LIST_BOARD_REVISION_EXACT) set(rev_exact EXACT) endif() board_check_revision( FORMAT ${rev_format} DEFAULT_REVISION ${LIST_BOARD_REVISION_DEFAULT} VALID_REVISIONS ${LIST_BOARD_REVISIONS} ${rev_exact} ) endif() elseif(DEFINED BOARD_REVISION) if(EXISTS ${BOARD_DIR}/revision.cmake) message(WARNING "revision.cmake is not used, revisions must be defined in '${BOARD_DIR}/board.yml'" ) endif() message(FATAL_ERROR "Invalid board revision: ${BOARD_REVISION}\n" "Board '${BOARD}' does not define any revisions." ) endif() if(LIST_BOARD_QUALIFIERS) # Allow users to omit the SoC when building for a board with a single SoC. list(LENGTH LIST_BOARD_SOCS socs_length) if(socs_length EQUAL 1) set(BOARD_SINGLE_SOC TRUE) set(BOARD_${BOARD}_SINGLE_SOC TRUE) if(NOT DEFINED BOARD_QUALIFIERS) set(BOARD_QUALIFIERS "/${LIST_BOARD_SOCS}") elseif("${BOARD_QUALIFIERS}" MATCHES "^//.*") string(REGEX REPLACE "^//" "/${LIST_BOARD_SOCS}/" BOARD_QUALIFIERS "${BOARD_QUALIFIERS}") endif() endif() set(board_targets ${LIST_BOARD_QUALIFIERS}) list(TRANSFORM board_targets PREPEND "${BOARD}/") if(NOT ("${BOARD}${BOARD_QUALIFIERS}" IN_LIST board_targets)) string(REPLACE ";" "\n" board_targets "${board_targets}") unset(CACHED_BOARD CACHE) message(FATAL_ERROR "Board qualifiers `${BOARD_QUALIFIERS}` for board \ `${BOARD}` not found. Please specify a valid board target.\n" "Valid board targets for ${LIST_BOARD_NAME} are:\n${board_targets}\n") endif() endif() else() message(FATAL_ERROR "Unknown hw model (${HWM}) for board: ${BOARD}.") endif() set(board_message "Board: ${BOARD}") if(DEFINED BOARD_REVISION) set(board_message "${board_message}, Revision: ${BOARD_REVISION}") if(DEFINED ACTIVE_BOARD_REVISION) set(board_message "${board_message} (Active: ${ACTIVE_BOARD_REVISION})") set(BOARD_REVISION ${ACTIVE_BOARD_REVISION}) endif() string(REPLACE "." "_" BOARD_REVISION_STRING ${BOARD_REVISION}) endif() if(DEFINED BOARD_QUALIFIERS) string(REGEX REPLACE "^/" "qualifiers: " board_message_qualifiers "${BOARD_QUALIFIERS}") set(board_message "${board_message}, ${board_message_qualifiers}") string(REPLACE "/" "_" NORMALIZED_BOARD_QUALIFIERS "${BOARD_QUALIFIERS}") endif() set(NORMALIZED_BOARD_TARGET "${BOARD}${BOARD_QUALIFIERS}") string(REPLACE "/" "_" NORMALIZED_BOARD_TARGET "${NORMALIZED_BOARD_TARGET}") message(STATUS "${board_message}") add_custom_target(boards ${list_boards_commands} USES_TERMINAL) # Board extensions are enabled by default set(BOARD_EXTENSIONS ON CACHE BOOL "Support board extensions") zephyr_get(BOARD_EXTENSIONS) # Process board extensions if(BOARD_EXTENSIONS) get_filename_component(board_dir_name ${BOARD_DIR} NAME) foreach(root ${BOARD_ROOT}) set(board_extension_dir ${root}/boards/extensions/${board_dir_name}) if(NOT EXISTS ${board_extension_dir}) continue() endif() list(APPEND BOARD_EXTENSION_DIRS ${board_extension_dir}) endforeach() endif() build_info(board name VALUE ${BOARD}) string(REGEX REPLACE "^/" "" qualifiers "${BOARD_QUALIFIERS}") build_info(board qualifiers VALUE ${qualifiers}) build_info(board revision VALUE ${BOARD_REVISION})