1# SPDX-License-Identifier: Apache-2.0
2#
3# Copyright (c) 2021, Nordic Semiconductor ASA
4
5# Validate shields and setup shields target.
6#
7# This module will validate the SHIELD argument.
8#
9# If a shield implementation is not found for one of the specified shields, an
10# error will be raised and a list of valid shields will be printed.
11#
12# Outcome:
13# The following variables will be defined when this module completes:
14# - shield_conf_files: List of shield-specific Kconfig fragments
15# - shield_dts_files : List of shield-specific devicetree files
16# - SHIELD_AS_LIST   : A CMake list of shields created from the SHIELD variable.
17# - SHIELD_DIRS      : A CMake list of directories which contain shield definitions
18#
19# The following targets will be defined when this CMake module completes:
20# - shields: when invoked, a list of valid shields will be printed
21#
22# If the SHIELD variable is changed after this module completes,
23# a warning will be printed.
24#
25# Optional variables:
26# - BOARD_ROOT: CMake list of board roots containing board implementations
27#
28# Variables set by this module and not mentioned above are for internal
29# use only, and may be removed, renamed, or re-purposed without prior notice.
30
31include_guard(GLOBAL)
32
33include(extensions)
34include(python)
35
36# Check that SHIELD has not changed.
37zephyr_check_cache(SHIELD WATCH)
38
39if(SHIELD)
40  message(STATUS "Shield(s): ${SHIELD}")
41endif()
42
43if(DEFINED SHIELD)
44  string(REPLACE " " ";" SHIELD_AS_LIST "${SHIELD}")
45endif()
46# SHIELD-NOTFOUND is a real CMake list, from which valid shields can be popped.
47# After processing all shields, only invalid shields will be left in this list.
48set(SHIELD-NOTFOUND ${SHIELD_AS_LIST})
49
50# Prepare list shields command.
51# This command is used for locating the shield dir as well as printing all shields
52# in the system in the following cases:
53# - User specifies an invalid SHIELD
54# - User invokes '<build-command> shields' target
55list(TRANSFORM BOARD_ROOT PREPEND "--board-root=" OUTPUT_VARIABLE board_root_args)
56
57set(list_shields_commands
58  COMMAND ${PYTHON_EXECUTABLE} ${ZEPHYR_BASE}/scripts/list_shields.py
59  ${board_root_args} --json
60)
61
62# Get list of shields in JSON format
63execute_process(${list_shields_commands}
64  OUTPUT_VARIABLE shields_json
65  ERROR_VARIABLE err_shields
66  RESULT_VARIABLE ret_val
67)
68
69if(ret_val)
70  message(FATAL_ERROR "Error finding shields\nError message: ${err_shields}")
71endif()
72
73string(JSON shields_length LENGTH ${shields_json})
74
75if(shields_length GREATER 0)
76  math(EXPR shields_length "${shields_length} - 1")
77
78  foreach(i RANGE ${shields_length})
79    string(JSON shield GET "${shields_json}" "${i}")
80    string(JSON shield_name GET ${shield} name)
81    string(JSON shield_dir GET ${shield} dir)
82    list(APPEND SHIELD_LIST ${shield_name})
83    set(SHIELD_DIR_${shield_name} ${shield_dir})
84  endforeach()
85endif()
86
87# Process shields in-order
88if(DEFINED SHIELD)
89  foreach(s ${SHIELD_AS_LIST})
90    if(NOT ${s} IN_LIST SHIELD_LIST)
91      continue()
92    endif()
93
94    list(REMOVE_ITEM SHIELD-NOTFOUND ${s})
95
96    # Add <shield>.overlay to the shield_dts_files output variable.
97    list(APPEND
98      shield_dts_files
99      ${SHIELD_DIR_${s}}/${s}.overlay
100      )
101
102    # Add the shield's directory to the SHIELD_DIRS output variable.
103    list(APPEND
104      SHIELD_DIRS
105      ${SHIELD_DIR_${s}}
106      )
107
108    include(${SHIELD_DIR_${s}}/pre_dt_shield.cmake OPTIONAL)
109
110    # Search for shield/shield.conf file
111    if(EXISTS ${SHIELD_DIR_${s}}/${s}.conf)
112      list(APPEND
113        shield_conf_files
114        ${SHIELD_DIR_${s}}/${s}.conf
115        )
116    endif()
117
118    # Add board-specific .conf and .overlay files to their
119    # respective output variables.
120    zephyr_file(CONF_FILES ${SHIELD_DIR_${s}}/boards
121                DTS   shield_dts_files
122                KCONF shield_conf_files
123    )
124    zephyr_file(CONF_FILES ${SHIELD_DIR_${s}}/boards/${s}
125                DTS   shield_dts_files
126                KCONF shield_conf_files
127    )
128  endforeach()
129endif()
130
131# Prepare shield usage command printing.
132# This command prints all shields in the system in the following cases:
133# - User specifies an invalid SHIELD
134# - User invokes '<build-command> shields' target
135list(SORT SHIELD_LIST)
136
137if(DEFINED SHIELD AND NOT (SHIELD-NOTFOUND STREQUAL ""))
138  # Convert the list to pure string with newlines for printing.
139  string(REPLACE ";" "\n" shield_string "${SHIELD_LIST}")
140
141  foreach (s ${SHIELD-NOTFOUND})
142    message("No shield named '${s}' found")
143  endforeach()
144  message("Please choose from among the following shields:\n"
145          "${shield_string}"
146  )
147  unset(CACHED_SHIELD CACHE)
148  message(FATAL_ERROR "Invalid SHIELD; see above.")
149endif()
150
151# Prepend each shield with COMMAND <cmake> -E echo <shield>" for printing.
152# Each shield is printed as new command because build files are not fond of newlines.
153list(TRANSFORM SHIELD_LIST PREPEND "COMMAND;${CMAKE_COMMAND};-E;echo;"
154     OUTPUT_VARIABLE shields_target_cmd
155)
156
157add_custom_target(shields ${shields_target_cmd} USES_TERMINAL)
158