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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
82 changes: 82 additions & 0 deletions scripts/schemas/snippet-schema.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
# SPDX-License-Identifier: Apache-2.0
#
# Copyright (c) 2022-2025, Nordic Semiconductor ASA

# JSON Schema for snippet YAML files
# When possible, constraints and validation rules should be modeled directly in this file.

$schema: "https://json-schema.org/draft/2020-12/schema"
$id: "https://zephyrproject.org/schemas/zephyr/snippet"
title: Zephyr snippet Schema
description: Schema for validating Zephyr snippet files
type: object
$defs:
append-schema:
# Sub-schema for appending onto CMake list variables.
# See uses under 'include: append-schema' keys below.
type: object
properties:
EXTRA_DTC_OVERLAY_FILE:
type: string
EXTRA_CONF_FILE:
type: string
SB_EXTRA_CONF_FILE:
type: string
DTS_EXTRA_CPPFLAGS:
type: string
additionalProperties: false

properties:
name:
type: string
append:
example: |
Snippet-wide appending can be done here:

name: foo
append:
EXTRA_DTC_OVERLAY_FILE: m3.overlay
include: append-schema
boards:
example: |
Board-specific appending can be done here:

name: foo
boards:
qemu_cortex_m3:
append:
EXTRA_DTC_OVERLAY_FILE: m3.overlay

Board revisions can also be used which includes additional files:
name: foo
boards:
nrf9160dk/nrf9160:
append:
# Base file will be applied first
EXTRA_DTC_OVERLAY_FILE: first.overlay
revisions:
"0.7.0":
append:
# Will be applied on top of common board file
EXTRA_DTC_OVERLAY_FILE: extra_0_7_0.overlay
type: object
patternProperties:
"(.*)":
type: object
properties:
append:
include: append-schema
revisions:
type: object
patternProperties:
"(.*)":
type: object
properties:
append:
include: append-schema
additionalProperties: false
additionalProperties: false
additionalProperties: false
required:
- name
additionalProperties: false
49 changes: 0 additions & 49 deletions scripts/schemas/snippet-schema.yml

This file was deleted.

59 changes: 35 additions & 24 deletions scripts/snippets.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,26 +18,26 @@
from dataclasses import dataclass, field
from pathlib import Path, PurePosixPath
from typing import Dict, Iterable, List, Set
from jsonschema.exceptions import best_match
import argparse
import logging
import os
import pykwalify.core
import pykwalify.errors
import re
import sys
import textwrap
import yaml
import platform
import jsonschema

# Marker type for an 'append:' configuration. Maps variables
# to the list of values to append to them.
Appends = Dict[str, List[str]]
BoardRevisionAppends = Dict[str, Dict[str, List[str]]]

def _new_append():
return defaultdict(list)

def _new_board2appends():
return defaultdict(_new_append)
return defaultdict(lambda: defaultdict(_new_append))

@dataclass
class Snippet:
Expand All @@ -46,11 +46,11 @@ class Snippet:

name: str
appends: Appends = field(default_factory=_new_append)
board2appends: Dict[str, Appends] = field(default_factory=_new_board2appends)
board2appends: Dict[str, BoardRevisionAppends] = field(default_factory=_new_board2appends)

def process_data(self, pathobj: Path, snippet_data: dict, sysbuild: bool):
'''Process the data in a snippet.yml file, after it is loaded into a
python object and validated by pykwalify.'''
python object and validated by jsonschema.'''
def append_value(variable, value):
if variable in ('SB_EXTRA_CONF_FILE', 'EXTRA_DTC_OVERLAY_FILE', 'EXTRA_CONF_FILE'):
path = pathobj.parent / value
Expand All @@ -69,10 +69,16 @@ def append_value(variable, value):
if board.startswith('/') and not board.endswith('/'):
_err(f"snippet file {pathobj}: board {board} starts with '/', so "
"it must end with '/' to use a regular expression")
for revision, appenddata in settings.get('revisions', {}).items():
for variable, value in appenddata.get('append', {}).items():
if (sysbuild is True and variable[0:3] == 'SB_') or \
(sysbuild is False and variable[0:3] != 'SB_'):
self.board2appends[board][revision][variable].append(
append_value(variable, value))
for variable, value in settings.get('append', {}).items():
if (sysbuild is True and variable[0:3] == 'SB_') or \
(sysbuild is False and variable[0:3] != 'SB_'):
self.board2appends[board][variable].append(
self.board2appends[board][""][variable].append(
append_value(variable, value))

class Snippets(UserDict):
Expand Down Expand Up @@ -168,7 +174,18 @@ def print_appends_for_board(self, board: str, appends: Appends):
self.print(f'''\
# Appends for board '{board}'
if("${{BOARD}}${{BOARD_QUALIFIERS}}" STREQUAL "{board}")''')
self.print_appends(appends, 1)

# Output board variables first then board revision variables
self.print_appends(appends[""], 1)

for revision in appends:
if revision != "":
self.print(f'''\
# Appends for revision '{revision}'
if("${{BOARD_REVISION}}" STREQUAL "{revision}")''')
self.print_appends(appends[revision], 2)
self.print(' endif()')

self.print('endif()')

def print_appends(self, appends: Appends, indent: int):
Expand All @@ -181,9 +198,9 @@ def print(self, *args, **kwargs):
kwargs['file'] = self.out_file
print(*args, **kwargs)

# Name of the file containing the pykwalify schema for snippet.yml
# Name of the file containing the jsonschema schema for snippet.yml
# files.
SCHEMA_PATH = str(Path(__file__).parent / 'schemas' / 'snippet-schema.yml')
SCHEMA_PATH = str(Path(__file__).parent / 'schemas' / 'snippet-schema.yaml')
with open(SCHEMA_PATH, 'r') as f:
SNIPPET_SCHEMA = yaml.safe_load(f.read())

Expand Down Expand Up @@ -221,10 +238,6 @@ def parse_args():
return parser.parse_args()

def setup_logging():
# Silence validation errors from pykwalify, which are logged at
# logging.ERROR level. We want to handle those ourselves as
# needed.
logging.getLogger('pykwalify').setLevel(logging.CRITICAL)
logging.basicConfig(level=logging.INFO,
format=' %(name)s: %(message)s')

Expand Down Expand Up @@ -296,17 +309,15 @@ def load_snippet_yml(snippet_yml: Path) -> dict:
except yaml.scanner.ScannerError:
_err(f'snippets file {snippet_yml} is invalid YAML')

def pykwalify_err(e):
return f'''\
invalid {SNIPPET_YML} file: {snippet_yml}
{textwrap.indent(e.msg, ' ')}
'''
validator_class = jsonschema.validators.validator_for(SNIPPET_SCHEMA)
validator_class.check_schema(SNIPPET_SCHEMA)
snippet_validator = validator_class(SNIPPET_SCHEMA)
errors = list(snippet_validator.iter_errors(snippet_data))

try:
pykwalify.core.Core(source_data=snippet_data,
schema_data=SNIPPET_SCHEMA).validate()
except pykwalify.errors.PyKwalifyException as e:
_err(pykwalify_err(e))
if errors:
sys.exit('ERROR: Malformed snippet YAML file: '
f'{snippet_yml.as_posix()}\n'
f'{best_match(errors).message} in {best_match(errors).json_path}')

name = snippet_data['name']
if not SNIPPET_NAME_RE.fullmatch(name):
Expand Down
21 changes: 21 additions & 0 deletions tests/cmake/snippets/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,16 @@ config TEST_TYPE_BAR_FOO
help
Test the snippet processing order (1. bar, 2. foo)

config TEST_TYPE_VER_CHECK
bool "Test Type: Version check"
help
Test board version snippet application

config TEST_TYPE_VER_CHECK_SPECIFIC
bool "Test Type: Version check specific"
help
Test board version snippet application with specific board version

endchoice

# Test values set by the snippet config overlays and tested by the test logic
Expand All @@ -57,3 +67,14 @@ config TEST_COMMON_VAL
help
This option's value should be overridden by the snippet config
overlays.

# Used for testing board version snippets
config TEST_VER_CHECK_APPLIED
bool "Test version check snippet applied"
help
This option's value should be set by the ver_check snippet.

config TEST_VER_CHECK_SPECIFIC_VERSION_APPLIED
bool "Test version check with board version snippet applied"
help
This option's value should be set by the ver_check snippet.
9 changes: 9 additions & 0 deletions tests/cmake/snippets/snippets/ver_check/snippet.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
name: ver_check
boards:
nrf9160dk/nrf9160:
revisions:
"0.7.0":
append:
EXTRA_CONF_FILE: ver_check_0_7_0.conf
append:
EXTRA_CONF_FILE: ver_check.conf
2 changes: 2 additions & 0 deletions tests/cmake/snippets/snippets/ver_check/ver_check.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
CONFIG_TEST_VER_CHECK_APPLIED=y
CONFIG_TEST_VER_CHECK_SPECIFIC_VERSION_APPLIED=n
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
CONFIG_TEST_VER_CHECK_SPECIFIC_VERSION_APPLIED=y
17 changes: 17 additions & 0 deletions tests/cmake/snippets/src/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,23 @@
#define TEST_BAR_VAL_BAR (964183)
#define TEST_COMMON_VAL_BAR (109234)

/* Check board-specific snippet files */
#if defined(CONFIG_TEST_TYPE_VER_CHECK)
#if !defined(CONFIG_TEST_VER_CHECK_APPLIED)
#error "Base ver_check snippet has not been applied"
#endif
#if defined(CONFIG_TEST_VER_CHECK_SPECIFIC_VERSION_APPLIED)
#error "Board specific ver_check snippet has wrongly been applied"
#endif
#elif defined(CONFIG_TEST_TYPE_VER_CHECK_SPECIFIC)
#if !defined(CONFIG_TEST_VER_CHECK_APPLIED)
#error "Base ver_check snippet has not been applied"
#endif
#if !defined(CONFIG_TEST_VER_CHECK_SPECIFIC_VERSION_APPLIED)
#error "Board specific ver_check snippet has not been applied"
#endif
#endif

ZTEST_SUITE(snippet_tests, NULL, NULL, NULL, NULL, NULL);

ZTEST(snippet_tests, test_overlay_config)
Expand Down
Loading
Loading