Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
120 changes: 58 additions & 62 deletions cmake/modules/shields.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
# - shield_dts_files : List of shield-specific devicetree files
# - SHIELD_AS_LIST : A CMake list of shields created from the SHIELD variable.
# - SHIELD_DIRS : A CMake list of directories which contain shield definitions
# Note: The variables above are only populated when SHIELD is defined.
#
# The following targets will be defined when this CMake module completes:
# - shields: when invoked, a list of valid shields will be printed
Expand All @@ -33,34 +34,39 @@ include_guard(GLOBAL)
include(extensions)
include(python)

# 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})

# Prepare list shields command.
# This command is used for locating the shield dir as well as printing all shields
# in the system in the following cases:
# - User specifies an invalid SHIELD
# - User invokes '<build-command> shields' target
list(TRANSFORM BOARD_ROOT PREPEND "--board-root=" OUTPUT_VARIABLE board_root_args)

set(list_shields_commands
COMMAND ${PYTHON_EXECUTABLE} ${ZEPHYR_BASE}/scripts/list_shields.py
${board_root_args} --json
${board_root_args}
)

add_custom_target(shields
${list_shields_commands}
USES_TERMINAL
)

# Check that SHIELD has not changed.
zephyr_check_cache(SHIELD WATCH)

if(NOT DEFINED SHIELD)
# No shields required for the build, skip unnecessary work
return()
endif()

message(STATUS "Shield(s): ${SHIELD}")

string(REPLACE " " ";" SHIELD_AS_LIST "${SHIELD}")
# 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})

# Get list of shields in JSON format
execute_process(${list_shields_commands}
execute_process(${list_shields_commands} --json
OUTPUT_VARIABLE shields_json
ERROR_VARIABLE err_shields
RESULT_VARIABLE ret_val
Expand All @@ -85,56 +91,54 @@ if(shields_length GREATER 0)
endif()

# Process shields in-order
if(DEFINED SHIELD)
foreach(s ${SHIELD_AS_LIST})
if(NOT ${s} IN_LIST SHIELD_LIST)
continue()
endif()
foreach(s ${SHIELD_AS_LIST})
if(NOT ${s} IN_LIST SHIELD_LIST)
continue()
endif()

list(REMOVE_ITEM SHIELD-NOTFOUND ${s})
list(REMOVE_ITEM SHIELD-NOTFOUND ${s})

# Add <shield>.overlay to the shield_dts_files output variable.
list(APPEND
shield_dts_files
${SHIELD_DIR_${s}}/${s}.overlay
)
# Add <shield>.overlay to the shield_dts_files output variable.
list(APPEND
shield_dts_files
${SHIELD_DIR_${s}}/${s}.overlay
)

# Add the shield's directory to the SHIELD_DIRS output variable.
list(APPEND
SHIELD_DIRS
${SHIELD_DIR_${s}}
)

include(${SHIELD_DIR_${s}}/pre_dt_shield.cmake OPTIONAL)

# Add the shield's directory to the SHIELD_DIRS output variable.
# Search for shield/shield.conf file
if(EXISTS ${SHIELD_DIR_${s}}/${s}.conf)
list(APPEND
SHIELD_DIRS
${SHIELD_DIR_${s}}
shield_conf_files
${SHIELD_DIR_${s}}/${s}.conf
)
endif()

include(${SHIELD_DIR_${s}}/pre_dt_shield.cmake OPTIONAL)

# Search for shield/shield.conf file
if(EXISTS ${SHIELD_DIR_${s}}/${s}.conf)
list(APPEND
shield_conf_files
${SHIELD_DIR_${s}}/${s}.conf
)
endif()

# Add board-specific .conf and .overlay files to their
# respective output variables.
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()
# Add board-specific .conf and .overlay files to their
# respective output variables.
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()

# Prepare shield usage command printing.
# This command prints all shields 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 ""))
if(NOT (SHIELD-NOTFOUND STREQUAL ""))
# Convert the list to pure string with newlines for printing.
string(REPLACE ";" "\n" shield_string "${SHIELD_LIST}")

Expand All @@ -147,11 +151,3 @@ if(DEFINED SHIELD AND NOT (SHIELD-NOTFOUND STREQUAL ""))
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)
45 changes: 45 additions & 0 deletions scripts/ci/check_compliance.py
Original file line number Diff line number Diff line change
Expand Up @@ -394,6 +394,51 @@ def run(self):
self.check_board_file(file, vendor_prefixes)


class ShieldYmlCheck(ComplianceTest):
"""
Validate shield.yml files when shield-related files change.
"""

name = "ShieldYml"
doc = "Check the shield.yml file format"

@staticmethod
def _is_shield_related(path: str) -> bool:
return (
"/boards/shields/" in f"/{path}"
or path.endswith("/shield.yml")
or path == "scripts/list_shields.py"
or path == "scripts/schemas/shield-schema.yml"
)

def run(self):
changed = get_files(filter="d")
shield_related = [p for p in changed if self._is_shield_related(p)]
if not shield_related:
self.skip("No shield-related files changed")

# Validate all shields for impacted board roots, not only changed files.
# This catches malformed shield.yml and schema/parser regressions.
board_roots = {str(ZEPHYR_BASE)}
for rel_path in shield_related:
abs_path = (GIT_TOP / rel_path).resolve()
parts = list(abs_path.parts)
if "boards" in parts and "shields" in parts:
idx = parts.index("boards")
if idx > 0:
board_roots.add(str(Path(*parts[:idx])))

cmd = [sys.executable, str(ZEPHYR_BASE / "scripts" / "list_shields.py")]
for root in sorted(board_roots):
cmd.append(f"--board-root={root}")
cmd.append("--json")

try:
subprocess.run(cmd, check=True, stdout=subprocess.DEVNULL, stderr=subprocess.PIPE)
except subprocess.CalledProcessError as ex:
self.failure(ex.stderr.decode("utf-8") or "Shield YAML validation failed")


class ClangFormatCheck(ComplianceTest):
"""
Check if clang-format reports any issues
Expand Down
Loading