cmake: boards and shields cmake files repurposed

The boards.cmake and shields.cmake has been repurposed to do board and
shield validation so that such validation code can be re-factored out
of boilerplate itself.

The boards.cmake and shields.cmake will both perform validation and
printing errors when validation fails.

Also it will specify the boards and shields build targets.

This ensures that validation code, message printing and target
specification for boards and shields are located logically together.

This is part of a general CMake overhaul to allow better modularization
and reuse of the Zephyr build system.

Signed-off-by: Torsten Rasmussen <Torsten.Rasmussen@nordicsemi.no>
This commit is contained in:
Torsten Rasmussen 2021-12-16 13:55:09 +01:00 committed by Marti Bolivar
parent de34b67c45
commit 5504096560
4 changed files with 278 additions and 259 deletions

View file

@ -175,215 +175,14 @@ add_custom_target(
# Dummy add to generate files.
zephyr_linker_sources(SECTIONS)
# 'BOARD_ROOT' is a prioritized list of directories where boards may
# be found. It always includes ${ZEPHYR_BASE} at the lowest priority.
zephyr_file(APPLICATION_ROOT BOARD_ROOT)
list(APPEND BOARD_ROOT ${ZEPHYR_BASE})
zephyr_file(APPLICATION_ROOT SOC_ROOT)
zephyr_file(APPLICATION_ROOT ARCH_ROOT)
# Check that BOARD has been provided, and that it has not changed.
zephyr_check_cache(BOARD REQUIRED)
string(FIND "${BOARD}" "@" REVISION_SEPARATOR_INDEX)
if(NOT (REVISION_SEPARATOR_INDEX EQUAL -1))
math(EXPR BOARD_REVISION_INDEX "${REVISION_SEPARATOR_INDEX} + 1")
string(SUBSTRING ${BOARD} ${BOARD_REVISION_INDEX} -1 BOARD_REVISION)
string(SUBSTRING ${BOARD} 0 ${REVISION_SEPARATOR_INDEX} BOARD)
endif()
set(BOARD_MESSAGE "Board: ${BOARD}")
if(DEFINED ENV{ZEPHYR_BOARD_ALIASES})
include($ENV{ZEPHYR_BOARD_ALIASES})
if(${BOARD}_BOARD_ALIAS)
set(BOARD_ALIAS ${BOARD} CACHE STRING "Board alias, provided by user")
set(BOARD ${${BOARD}_BOARD_ALIAS})
message(STATUS "Aliased BOARD=${BOARD_ALIAS} changed to ${BOARD}")
endif()
endif()
include(${ZEPHYR_BASE}/boards/deprecated.cmake)
if(${BOARD}_DEPRECATED)
set(BOARD_DEPRECATED ${BOARD} CACHE STRING "Deprecated board name, provided by user")
set(BOARD ${${BOARD}_DEPRECATED})
message(WARNING "Deprecated BOARD=${BOARD_DEPRECATED} name specified, board automatically changed to: ${BOARD}.")
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/<ARCH>/my_board' then add '/foo/bar' to BOARD_ROOT, not the entire board directory
- if in doubt, use absolute paths")
endif()
# NB: find_path will return immediately if the output variable is
# already set
if (BOARD_ALIAS)
find_path(BOARD_HIDDEN_DIR
NAMES ${BOARD_ALIAS}_defconfig
PATHS ${root}/boards/*/*
NO_DEFAULT_PATH
)
if(BOARD_HIDDEN_DIR)
message("Board alias ${BOARD_ALIAS} is hiding the real board of same name")
endif()
endif()
find_path(BOARD_DIR
NAMES ${BOARD}_defconfig
PATHS ${root}/boards/*/*
NO_DEFAULT_PATH
)
if(BOARD_DIR AND NOT (${root} STREQUAL ${ZEPHYR_BASE}))
set(USING_OUT_OF_TREE_BOARD 1)
endif()
endforeach()
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()
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()
# Check that SHIELD has not changed.
zephyr_check_cache(SHIELD WATCH)
if(SHIELD)
set(BOARD_MESSAGE "${BOARD_MESSAGE}, Shield(s): ${SHIELD}")
endif()
message(STATUS "${BOARD_MESSAGE}")
if(DEFINED SHIELD)
string(REPLACE " " ";" SHIELD_AS_LIST "${SHIELD}")
endif()
# SHIELD-NOTFOUND is a real CMake list, from which valid shields can be popped.
# After processing all shields, only invalid shields will be left in this list.
set(SHIELD-NOTFOUND ${SHIELD_AS_LIST})
# Use BOARD to search for a '_defconfig' file.
# e.g. zephyr/boards/arm/96b_carbon_nrf51/96b_carbon_nrf51_defconfig.
# When found, use that path to infer the ARCH we are building for.
foreach(root ${BOARD_ROOT})
set(shield_dir ${root}/boards/shields)
# Match the Kconfig.shield files in the shield directories to make sure we are
# finding shields, e.g. x_nucleo_iks01a1/Kconfig.shield
file(GLOB_RECURSE shields_refs_list ${shield_dir}/*/Kconfig.shield)
# The above gives a list like
# x_nucleo_iks01a1/Kconfig.shield;x_nucleo_iks01a2/Kconfig.shield
# we construct a list of shield names by extracting the folder and find
# and overlay files in there. Each overlay corresponds to a shield.
# We obtain the shield name by removing the overlay extension.
unset(SHIELD_LIST)
foreach(shields_refs ${shields_refs_list})
get_filename_component(shield_path ${shields_refs} DIRECTORY)
file(GLOB shield_overlays RELATIVE ${shield_path} ${shield_path}/*.overlay)
foreach(overlay ${shield_overlays})
get_filename_component(shield ${overlay} NAME_WE)
list(APPEND SHIELD_LIST ${shield})
set(SHIELD_DIR_${shield} ${shield_path})
endforeach()
endforeach()
if(DEFINED SHIELD)
foreach(s ${SHIELD_AS_LIST})
if(NOT ${s} IN_LIST SHIELD_LIST)
continue()
endif()
list(REMOVE_ITEM SHIELD-NOTFOUND ${s})
# if shield config flag is on, add shield overlay to the shield overlays
# list and dts_fixup file to the shield fixup file
list(APPEND
shield_dts_files
${SHIELD_DIR_${s}}/${s}.overlay
)
list(APPEND
shield_dts_fixups
${SHIELD_DIR_${s}}/dts_fixup.h
)
list(APPEND
SHIELD_DIRS
${SHIELD_DIR_${s}}
)
# search for shield/shield.conf file
if(EXISTS ${SHIELD_DIR_${s}}/${s}.conf)
# add shield.conf to the shield config list
list(APPEND
shield_conf_files
${SHIELD_DIR_${s}}/${s}.conf
)
endif()
zephyr_file(CONF_FILES ${SHIELD_DIR_${s}}/boards
DTS shield_dts_files
KCONF shield_conf_files
)
zephyr_file(CONF_FILES ${SHIELD_DIR_${s}}/boards/${s}
DTS shield_dts_files
KCONF shield_conf_files
)
endforeach()
endif()
endforeach()
if(NOT BOARD_DIR)
message("No board named '${BOARD}' found.
Please choose one of the following boards:
")
execute_process(
COMMAND
${CMAKE_COMMAND}
-DZEPHYR_BASE=${ZEPHYR_BASE}
-DBOARD_ROOT=${BOARD_ROOT}
-DCMAKE_MAKE_PROGRAM=${CMAKE_MAKE_PROGRAM}
-P ${ZEPHYR_BASE}/cmake/boards.cmake
)
unset(CACHED_BOARD CACHE)
message(FATAL_ERROR "Invalid BOARD; see above.")
endif()
if(DEFINED SHIELD AND NOT (SHIELD-NOTFOUND STREQUAL ""))
foreach (s ${SHIELD-NOTFOUND})
message("No shield named '${s}' found")
endforeach()
message("Please choose from among the following shields:")
string(REPLACE ";" "\\;" SHIELD_LIST_ESCAPED "${SHIELD_LIST}")
execute_process(
COMMAND
${CMAKE_COMMAND}
-DZEPHYR_BASE=${ZEPHYR_BASE}
-DSHIELD_LIST=${SHIELD_LIST_ESCAPED}
-P ${ZEPHYR_BASE}/cmake/shields.cmake
)
unset(CACHED_SHIELD CACHE)
message(FATAL_ERROR "Invalid SHIELD; see above.")
endif()
include(${ZEPHYR_BASE}/cmake/boards.cmake)
include(${ZEPHYR_BASE}/cmake/shields.cmake)
include(${ZEPHYR_BASE}/cmake/arch.cmake)
if(DEFINED APPLICATION_CONFIG_DIR)

View file

@ -1,40 +1,152 @@
# SPDX-License-Identifier: Apache-2.0
#
# Copyright (c) 2021, Nordic Semiconductor ASA
set(arch_root_args)
foreach(root ${ARCH_ROOT})
list(APPEND arch_root_args "--arch-root=${root}")
endforeach()
# Validate board and setup boards target.
#
# This CMake module will validate the BOARD argument as well as splitting the
# BOARD argument into <BOARD> and <BOARD_REVISION>.
#
# 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_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
#
# 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 considered internal
# use only and may be removed, renamed, or re-purposed without prior notice.
# 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.
list(APPEND BOARD_ROOT ${ZEPHYR_BASE})
string(FIND "${BOARD}" "@" REVISION_SEPARATOR_INDEX)
if(NOT (REVISION_SEPARATOR_INDEX EQUAL -1))
math(EXPR BOARD_REVISION_INDEX "${REVISION_SEPARATOR_INDEX} + 1")
string(SUBSTRING ${BOARD} ${BOARD_REVISION_INDEX} -1 BOARD_REVISION)
string(SUBSTRING ${BOARD} 0 ${REVISION_SEPARATOR_INDEX} BOARD)
endif()
if(DEFINED ENV{ZEPHYR_BOARD_ALIASES})
include($ENV{ZEPHYR_BOARD_ALIASES})
if(${BOARD}_BOARD_ALIAS)
set(BOARD_ALIAS ${BOARD} CACHE STRING "Board alias, provided by user")
set(BOARD ${${BOARD}_BOARD_ALIAS})
message(STATUS "Aliased BOARD=${BOARD_ALIAS} changed to ${BOARD}")
endif()
endif()
include(${ZEPHYR_BASE}/boards/deprecated.cmake)
if(${BOARD}_DEPRECATED)
set(BOARD_DEPRECATED ${BOARD} CACHE STRING "Deprecated board name, provided by user")
set(BOARD ${${BOARD}_DEPRECATED})
message(WARNING "Deprecated BOARD=${BOARD_DEPRECATED} name specified, board automatically changed to: ${BOARD}.")
endif()
zephyr_boilerplate_watch(BOARD)
set(board_root_args)
foreach(root ${BOARD_ROOT})
list(APPEND board_root_args "--board-root=${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/<ARCH>/my_board' then add '/foo/bar' to BOARD_ROOT, not the entire board directory
- if in doubt, use absolute paths")
endif()
# NB: find_path will return immediately if the output variable is
# already set
if (BOARD_ALIAS)
find_path(BOARD_HIDDEN_DIR
NAMES ${BOARD_ALIAS}_defconfig
PATHS ${root}/boards/*/*
NO_DEFAULT_PATH
)
if(BOARD_HIDDEN_DIR)
message("Board alias ${BOARD_ALIAS} is hiding the real board of same name")
endif()
endif()
find_path(BOARD_DIR
NAMES ${BOARD}_defconfig
PATHS ${root}/boards/*/*
NO_DEFAULT_PATH
)
if(BOARD_DIR AND NOT (${root} STREQUAL ${ZEPHYR_BASE}))
set(USING_OUT_OF_TREE_BOARD 1)
endif()
endforeach()
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()
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()
message(STATUS "${board_message}")
# Prepare boards usage command printing.
# This command prints all boards in the system in the following cases:
# - User specifies an invalid BOARD
# - User invokes '<build-command> 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)
set(list_boards_commands
COMMAND ${PYTHON_EXECUTABLE} ${ZEPHYR_BASE}/scripts/list_boards.py
${arch_root_args} ${board_root_args}
COMMAND ${PYTHON_EXECUTABLE} ${ZEPHYR_BASE}/scripts/list_boards.py
${arch_root_args} ${board_root_args}
)
if(CMAKE_SCRIPT_MODE_FILE AND NOT CMAKE_PARENT_LIST_FILE)
# If this file is invoked as a script directly with -P:
# cmake [options] -P board.cmake
# Note that CMAKE_PARENT_LIST_FILE not being set ensures that this present
# file is being invoked directly with -P, and not via an include directive from
# some other script
# The options available are:
# ARCH_ROOT: Semi-colon separated arch roots
# BOARD_ROOT: Semi-colon separated board roots
# FILE_OUT: Set to a file path to save the boards to a file. If not defined the
# the contents will be printed to stdout
cmake_minimum_required(VERSION 3.20.0)
set(NO_BOILERPLATE TRUE)
find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
if (FILE_OUT)
list(APPEND list_boards_commands OUTPUT_FILE "${FILE_OUT}")
if(NOT BOARD_DIR)
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()
execute_process(${list_boards_commands})
endif()
add_custom_target(boards ${list_boards_commands} USES_TERMINAL)

View file

@ -1,18 +1,136 @@
if(CMAKE_SCRIPT_MODE_FILE AND NOT CMAKE_PARENT_LIST_FILE)
# This file was invoked as a script directly with -P:
# cmake -P shields.cmake
#
# Unlike boards.cmake, this takes no OUTPUT_FILE option, but
# SHIELD_LIST_SPACE_SEPARATED is required.
list(SORT SHIELD_LIST)
foreach(shield ${SHIELD_LIST})
message("${shield}")
endforeach()
else()
# This file was included into usage.cmake.
set(sorted_shield_list ${SHIELD_LIST})
list(SORT sorted_shield_list)
foreach(shield ${sorted_shield_list})
list(APPEND sorted_shield_cmds COMMAND ${CMAKE_COMMAND} -E echo "${shield}")
endforeach()
# SPDX-License-Identifier: Apache-2.0
#
# Copyright (c) 2021, Nordic Semiconductor ASA
# Validate shields and setup shields target.
#
# This module will validate the SHIELD argument.
#
# If a shield implementation is not found for one of the specified shields an
# error will be raised and list of valid shields will be printed.
#
# Outcome:
# The following variables will be defined when this module completes:
# - shield_conf_files: List of shield specific Kconfig fragments
# - shield_dts_files : List of shield specific devicetree files
# - shield_dts_fixups: List of shield specific devicetree fixups
# - SHIELD_AS_LIST : A CMake list of shields created from SHIELD variable.
#
# Optional variables:
# - BOARD_ROOT: CMake list of board roots containing board implementations
#
# Variables set by this module and not mentioned above are considered internal
# use only and may be removed, renamed, or re-purposed without prior notice.
# Check that SHIELD has not changed.
zephyr_check_cache(SHIELD WATCH)
if(SHIELD)
message(STATUS "Shield(s): ${SHIELD}")
endif()
if(DEFINED SHIELD)
string(REPLACE " " ";" SHIELD_AS_LIST "${SHIELD}")
endif()
# SHIELD-NOTFOUND is a real CMake list, from which valid shields can be popped.
# After processing all shields, only invalid shields will be left in this list.
set(SHIELD-NOTFOUND ${SHIELD_AS_LIST})
# Use BOARD to search for a '_defconfig' file.
# e.g. zephyr/boards/arm/96b_carbon_nrf51/96b_carbon_nrf51_defconfig.
# When found, use that path to infer the ARCH we are building for.
foreach(root ${BOARD_ROOT})
set(shield_dir ${root}/boards/shields)
# Match the Kconfig.shield files in the shield directories to make sure we are
# finding shields, e.g. x_nucleo_iks01a1/Kconfig.shield
file(GLOB_RECURSE shields_refs_list ${shield_dir}/*/Kconfig.shield)
# The above gives a list like
# x_nucleo_iks01a1/Kconfig.shield;x_nucleo_iks01a2/Kconfig.shield
# we construct a list of shield names by extracting the folder and find
# and overlay files in there. Each overlay corresponds to a shield.
# We obtain the shield name by removing the overlay extension.
unset(SHIELD_LIST)
foreach(shields_refs ${shields_refs_list})
get_filename_component(shield_path ${shields_refs} DIRECTORY)
file(GLOB shield_overlays RELATIVE ${shield_path} ${shield_path}/*.overlay)
foreach(overlay ${shield_overlays})
get_filename_component(shield ${overlay} NAME_WE)
list(APPEND SHIELD_LIST ${shield})
set(SHIELD_DIR_${shield} ${shield_path})
endforeach()
endforeach()
if(DEFINED SHIELD)
foreach(s ${SHIELD_AS_LIST})
if(NOT ${s} IN_LIST SHIELD_LIST)
continue()
endif()
list(REMOVE_ITEM SHIELD-NOTFOUND ${s})
# if shield config flag is on, add shield overlay to the shield overlays
# list and dts_fixup file to the shield fixup file
list(APPEND
shield_dts_files
${SHIELD_DIR_${s}}/${s}.overlay
)
list(APPEND
shield_dts_fixups
${SHIELD_DIR_${s}}/dts_fixup.h
)
list(APPEND
SHIELD_DIRS
${SHIELD_DIR_${s}}
)
# search for shield/shield.conf file
if(EXISTS ${SHIELD_DIR_${s}}/${s}.conf)
# add shield.conf to the shield config list
list(APPEND
shield_conf_files
${SHIELD_DIR_${s}}/${s}.conf
)
endif()
zephyr_file(CONF_FILES ${SHIELD_DIR_${s}}/boards
DTS shield_dts_files
KCONF shield_conf_files
)
zephyr_file(CONF_FILES ${SHIELD_DIR_${s}}/boards/${s}
DTS shield_dts_files
KCONF shield_conf_files
)
endforeach()
endif()
endforeach()
# Prepare shield usage command printing.
# This command prints all ishield in the system in the following cases:
# - User specifies an invalid SHIELD
# - User invokes '<build-command> shields' target
list(SORT SHIELD_LIST)
if(DEFINED SHIELD AND NOT (SHIELD-NOTFOUND STREQUAL ""))
# Convert the list to pure string with newlines for printing.
string(REPLACE ";" "\n" shield_string "${SHIELD_LIST}")
foreach (s ${SHIELD-NOTFOUND})
message("No shield named '${s}' found")
endforeach()
message("Please choose from among the following shields:\n"
"${shield_string}"
)
unset(CACHED_SHIELD CACHE)
message(FATAL_ERROR "Invalid SHIELD; see above.")
endif()
# Prepend each shield with COMMAND <cmake> -E echo <shield>" for printing.
# Each shield is printed as new command because build files are not fond of newlines.
list(TRANSFORM SHIELD_LIST PREPEND "COMMAND;${CMAKE_COMMAND};-E;echo;"
OUTPUT_VARIABLE shields_target_cmd
)
add_custom_target(shields ${shields_target_cmd} USES_TERMINAL)

View file

@ -1,15 +1,5 @@
# SPDX-License-Identifier: Apache-2.0
include (${ZEPHYR_BASE}/cmake/shields.cmake)
include (${ZEPHYR_BASE}/cmake/boards.cmake)
# shields.cmake and boards.cmake can be run with cmake -P for printing
# help output on user error when settings BOARD or SHIELD, and
# add_custom_target() is not available in script mode, so we place
# them in here.
add_custom_target(shields ${sorted_shield_cmds} USES_TERMINAL)
add_custom_target(boards ${list_boards_commands} USES_TERMINAL)
add_custom_target(
usage
${CMAKE_COMMAND}