boards/shields: re-work handling in cmake and west

Remove the boards and shields lists from the 'usage' target output.
That might have been readable at some point long ago in Zephyr's
history, when only a few boards were available, but right now it's
obscuring the high level targets we really want 'usage' to print.

Instead, add 'boards' and 'shields' targets which the user can run to
get those lists, and reference them from the 'usage' output. This
makes 'usage' squintable again. We use the new list_boards.py script
from the 'boards' target.

Reference the 'help' target from 'usage' as well, and drop the
recommendation that people run '--target help' from the 'west build
--help' output for the 'west build --target' option. The canonical
place to look is 'usage' now.

Use the new list_boards.py code from 'west boards' as well, which
allows us to add the board's directory as a format string key, in
addition to its name and architecture.

Keep west-completion.bash up to date. While doing that, I noticed that
a bunch of references to this file refer to a stale location, so fix
those too.

Finally, the 'usage' output is what we print for a failed board or
shield lookup, so that needs to be updated also. Handle that by
invoking boards.cmake and a new shields.cmake in CMake script mode to
print the relevant output.

Signed-off-by: Martí Bolívar <marti.bolivar@nordicsemi.no>
This commit is contained in:
Martí Bolívar 2020-12-09 15:53:00 -08:00 committed by Anas Nashif
parent 8bd6d08b0b
commit 0d5e6c13e9
11 changed files with 116 additions and 203 deletions

View file

@ -357,19 +357,37 @@ foreach(root ${BOARD_ROOT})
endforeach()
if(NOT BOARD_DIR)
message("No board named '${BOARD}' found")
print_usage()
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 usage")
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()
print_usage()
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 usage")
message(FATAL_ERROR "Invalid SHIELD; see above.")
endif()
get_filename_component(BOARD_ARCH_DIR ${BOARD_DIR} DIRECTORY)

View file

@ -1,91 +1,19 @@
# SPDX-License-Identifier: Apache-2.0
# List all architectures, export the list in list_var
function(list_archs list_var)
set(arch_root_args)
foreach(root ${ARCH_ROOT})
list(APPEND arch_root_args "--arch-root=${root}")
endforeach()
FILE(GLOB arch_contents RELATIVE ${ZEPHYR_BASE}/arch ${ZEPHYR_BASE}/arch/*)
set(_arch_list)
foreach(f ${arch_contents})
if ("${f}" STREQUAL "common")
continue()
endif()
if (IS_DIRECTORY "${ZEPHYR_BASE}/arch/${f}")
list(APPEND _arch_list "${f}")
endif()
endforeach()
set(board_root_args)
foreach(root ${BOARD_ROOT})
list(APPEND board_root_args "--board-root=${root}")
endforeach()
# Export the arch list to the parent scope
set(${list_var} ${_arch_list} PARENT_SCOPE)
endfunction()
# List all boards for a particular arch, export the list in list_var
function(list_boards arch list_var)
# Make sure we start with an empty list for each arch
set(_boards_for_${arch} "")
foreach(root ${BOARD_ROOT})
set(board_arch_dir ${root}/boards/${arch})
# Match the _defconfig files in the board directories to make sure we are
# finding boards, e.g. qemu_xtensa/qemu_xtensa_defconfig
file(GLOB_RECURSE defconfigs_for_${arch}
RELATIVE ${board_arch_dir}
${board_arch_dir}/*_defconfig
)
# The above gives a list like
# nrf51_blenano/nrf51_blenano_defconfig;nrf51dk_nrf51422/nrf51dk_nrf51422_defconfig
# we construct a list of board names by removing both the _defconfig
# suffix and the path.
foreach(defconfig_path ${defconfigs_for_${arch}})
get_filename_component(board ${defconfig_path} NAME)
string(REPLACE "_defconfig" "" board "${board}")
list(APPEND _boards_for_${arch} ${board})
endforeach()
endforeach()
# Export the board list for this arch to the parent scope
set(${list_var} ${_boards_for_${arch}} PARENT_SCOPE)
endfunction()
# Function to dump and optionally save to a file all architectures and boards
# Takes the board root as a semicolon-separated list in:
# BOARD_ROOT
# Additional arguments:
# file_out Filename to save the boards list. If not defined, stdout will be used
# indent Prepend an additional indendation string to each line
function(dump_all_boards file_out indent)
set(arch_list)
list_archs(arch_list)
foreach(arch ${arch_list})
list_boards(${arch} boards_for_${arch})
endforeach()
if(file_out)
file(WRITE ${file_out} "")
endif()
foreach(arch ${arch_list})
set(_arch_str "${indent}${arch}:")
if(file_out)
file(APPEND ${file_out} "${_arch_str}\n")
else()
message(${_arch_str})
endif()
foreach(board ${boards_for_${arch}})
set(_board_str "${indent} ${board}")
if(file_out)
file(APPEND ${file_out} "${_board_str}\n")
else()
message(${_board_str})
endif()
endforeach()
endforeach()
endfunction()
set(list_boards_commands
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:
@ -95,6 +23,7 @@ if(CMAKE_SCRIPT_MODE_FILE AND NOT CMAKE_PARENT_LIST_FILE)
# 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
@ -103,15 +32,9 @@ cmake_minimum_required(VERSION 3.13.1)
set(NO_BOILERPLATE TRUE)
find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
# Appending Zephyr base to list of board roots, as this is also done in boilerplate.cmake.
# But as this file was executed in script mode, it must also be done here, to give same output.
list(APPEND BOARD_ROOT ${ZEPHYR_BASE})
if (NOT FILE_OUT)
set(FILE_OUT FALSE)
if (FILE_OUT)
list(APPEND list_boards_commands OUTPUT_FILE "${FILE_OUT}")
endif()
dump_all_boards(${FILE_OUT} "")
execute_process(${list_boards_commands})
endif()

View file

@ -1758,25 +1758,6 @@ macro(assert_exists var)
endif()
endmacro()
function(print_usage)
if(NOT CMAKE_MAKE_PROGRAM)
# Create dummy project, in order to obtain make program for correct usage printing.
project(dummy_print_usage)
endif()
message("see usage:")
string(REPLACE ";" " " BOARD_ROOT_SPACE_SEPARATED "${BOARD_ROOT}")
string(REPLACE ";" " " SHIELD_LIST_SPACE_SEPARATED "${SHIELD_LIST}")
execute_process(
COMMAND
${CMAKE_COMMAND}
-DZEPHYR_BASE=${ZEPHYR_BASE}
-DBOARD_ROOT_SPACE_SEPARATED=${BOARD_ROOT_SPACE_SEPARATED}
-DSHIELD_LIST_SPACE_SEPARATED=${SHIELD_LIST_SPACE_SEPARATED}
-DCMAKE_MAKE_PROGRAM=${CMAKE_MAKE_PROGRAM}
-P ${ZEPHYR_BASE}/cmake/usage/usage.cmake
)
endfunction()
# 3.5. File system management
function(check_if_directory_is_writeable dir ok)
execute_process(

18
cmake/shields.cmake Normal file
View file

@ -0,0 +1,18 @@
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()
endif()

View file

@ -1,14 +1,19 @@
# SPDX-License-Identifier: Apache-2.0
string(REPLACE ";" " " BOARD_ROOT_SPACE_SEPARATED "${BOARD_ROOT}")
string(REPLACE ";" " " SHIELD_LIST_SPACE_SEPARATED "${SHIELD_LIST}")
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}
-DZEPHYR_BASE=${ZEPHYR_BASE}
-DBOARD_ROOT_SPACE_SEPARATED=${BOARD_ROOT_SPACE_SEPARATED}
-DSHIELD_LIST_SPACE_SEPARATED=${SHIELD_LIST_SPACE_SEPARATED}
-DCMAKE_MAKE_PROGRAM=${CMAKE_MAKE_PROGRAM}
-P ${CMAKE_CURRENT_SOURCE_DIR}/usage.cmake
)

View file

@ -3,8 +3,6 @@ if(NOT DEFINED ZEPHYR_BASE)
message(FATAL_ERROR "ZEPHYR_BASE not set")
endif()
include (${ZEPHYR_BASE}/cmake/boards.cmake)
get_filename_component(generator ${CMAKE_MAKE_PROGRAM} NAME)
if(${generator} STREQUAL ninja)
set(verbose "-v")
@ -12,48 +10,27 @@ else()
set(verbose "VERBOSE=1")
endif()
string(REPLACE " " ";" BOARD_ROOT "${BOARD_ROOT_SPACE_SEPARATED}")
string(REPLACE " " ";" SHIELD_LIST "${SHIELD_LIST_SPACE_SEPARATED}")
message("Cleaning targets:")
message(" clean - Remove most generated files but keep configuration and backup files")
message(" pristine - Remove all files in the build directory")
message("")
message("Configuration targets:")
message("")
message(" menuconfig - Update configuration using an interactive configuration interface")
message("Kconfig targets:")
message(" menuconfig - Update .config using a console-based interface")
message(" guiconfig - Update .config using a graphical interface")
message("")
message("Other generic targets:")
message(" all - Build a zephyr application")
message(" run - Build a zephyr application and run it if the board supports emulation")
message(" flash - Build and flash an application")
message(" debug - Build and debug an application using GDB")
message(" debugserver - Build and start a GDB server (port 1234 for Qemu targets)")
message(" flash - Run \"west flash\"")
message(" debug - Run \"west debug\"")
message(" debugserver - Run \"west debugserver\" (or start GDB server on port 1234 for QEMU targets)")
message(" attach - Run \"west attach\"")
message(" ram_report - Build and create RAM usage report")
message(" rom_report - Build and create ROM usage report")
message(" boards - Display supported boards")
message(" shields - Display supported shields")
message(" usage - Display this text")
message("")
message("Supported Boards:")
message("")
message(" To generate project files for one of the supported boards below, run:")
message("")
message(" $ cmake -DBOARD=<BOARD NAME> [-DSHIELD=<SHIELD NAME>] -Bpath/to/build_dir -Hpath/to/source_dir")
message("")
message(" or")
message("")
message(" $ export BOARD=<BOARD NAME>")
message(" $ export SHIELD=<SHIELD NAME> #optional")
message(" $ cmake -Bpath/to/build_dir -Hpath/to/source_dir")
message("")
dump_all_boards("" " ")
message("")
message("Supported Shields:")
message("")
set(sorted_shield_list ${SHIELD_LIST})
list(SORT sorted_shield_list)
foreach(shield ${sorted_shield_list})
message(" ${shield}")
endforeach()
message(" help - Display all build system targets")
message("")
message("Build flags:")
message("")

View file

@ -3,15 +3,17 @@
# SPDX-License-Identifier: Apache-2.0
import argparse
import collections
import os
from pathlib import Path
import re
import sys
import textwrap
from west import log
from west.commands import WestCommand
from zcmake import run_cmake
from zephyr_ext_common import ZEPHYR_CMAKE
sys.path.append(os.fspath(Path(__file__).parent.parent))
import list_boards
class Boards(WestCommand):
@ -30,23 +32,25 @@ class Boards(WestCommand):
help=self.help,
formatter_class=argparse.RawDescriptionHelpFormatter,
description=self.description,
epilog=textwrap.dedent('''\
epilog=textwrap.dedent(f'''\
FORMAT STRINGS
--------------
Boards are listed using a Python 3 format string. Arguments
to the format string are accessed by name.
The default format string is:
"{}"
"{default_fmt}"
The following arguments are available:
- name: board name
- arch: board architecture
'''.format(default_fmt)))
- dir: directory that contains the board definition
'''))
# Remember to update scripts/west-completion.bash if you add or remove
# Remember to update west-completion.bash if you add or remove
# flags
parser.add_argument('-f', '--format', default=default_fmt,
help='''Format string to use to list each board;
@ -54,47 +58,18 @@ class Boards(WestCommand):
parser.add_argument('-n', '--name', dest='name_re',
help='''a regular expression; only boards whose
names match NAME_RE will be listed''')
list_boards.add_args(parser)
return parser
def do_run(self, args, unknown_args):
cmake_args = ['-P', f'{ZEPHYR_CMAKE}/boards.cmake']
lines = run_cmake(cmake_args, capture_output=True)
arch_re = re.compile(r'\s*([\w-]+)\:')
board_re = re.compile(r'\s*([\w-]+)\s*')
arch = None
boards = collections.OrderedDict()
for line in lines:
match = arch_re.match(line)
if match:
arch = match.group(1)
boards[arch] = []
continue
match = board_re.match(line)
if match:
if not arch:
log.die('Invalid board output from CMake: {}'.
format(lines))
board = match.group(1)
boards[arch].append(board)
def do_run(self, args, _):
if args.name_re is not None:
name_re = re.compile(args.name_re)
else:
name_re = None
for arch in boards:
for board in boards[arch]:
if name_re is not None and not name_re.search(board):
continue
try:
result = args.format.format(
name=board,
arch=arch)
print(result)
except KeyError as e:
# The raised KeyError seems to just put the first
# invalid argument in the args tuple, regardless of
# how many unrecognizable keys there were.
log.die('unknown key "{}" in format string "{}"'.
format(e.args[0], args.format))
for board in list_boards.find_boards(args):
if name_re is not None and not name_re.search(board.name):
continue
log.inf(args.format.format(name=board.name, arch=board.arch,
dir=board.dir))

View file

@ -94,7 +94,7 @@ class Build(Forceable):
description=self.description,
usage=BUILD_USAGE)
# Remember to update scripts/west-completion.bash if you add or remove
# Remember to update west-completion.bash if you add or remove
# flags
parser.add_argument('-b', '--board', help='board to build for')
@ -110,8 +110,8 @@ class Build(Forceable):
group.add_argument('--cmake-only', action='store_true',
help="just run cmake; don't build (implies -c)")
group.add_argument('-t', '--target',
help='''run this build system target (try "-t usage"
or "-t help")''')
help='''run build system target TARGET
(try "-t usage")''')
group.add_argument('-o', '--build-opt', default=[], action='append',
help='''options to pass to the build tool
(make or ninja); may be given more than once''')

View file

@ -28,8 +28,8 @@ class Completion(WestCommand):
formatter_class=argparse.RawDescriptionHelpFormatter,
description=self.description)
# Remember to update completion/west-completion.bash if you add or
# remove flags
# Remember to update west-completion.bash if you add or remove
# flags
parser.add_argument('shell', nargs=1, choices=['bash'],
help='''Select the shell that which the completion
script is intended for.

View file

@ -591,8 +591,24 @@ __comp_west_boards()
{
local boards_args_opts="
--format -f --name -n
--arch-root --board-root
"
case "$prev" in
--format|-f|--name|-n)
# We don't know how to autocomplete these.
return
;;
--arch-root)
__set_comp_dirs
return
;;
--board-root)
__set_comp_dirs
return
;;
esac
case "$cur" in
-*)
__set_comp $boards_args_opts

View file

@ -82,7 +82,7 @@ def add_parser_common(command, parser_adder=None, parser=None):
help=command.help,
description=command.description)
# Remember to update scripts/west-completion.bash if you add or remove
# Remember to update west-completion.bash if you add or remove
# flags
group = parser.add_argument_group('general options',