From nobody Tue Jun 16 19:24:44 2026 Received: from pidgin.makrotopia.org (pidgin.makrotopia.org [185.142.180.65]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 38EC93368AF; Mon, 20 Apr 2026 15:50:32 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=185.142.180.65 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1776700234; cv=none; b=AFlZsh4lAe+KO58sL/DHuqk+vMh8ur/j3yGfeHbMNACI32vvmAGQ+XikiG2ouA0k2EbSF0EwOu57YahtFt+mu8hRkV3ed+/xCx4uK+jbmjDrZEJsjAy/pDBGxTXguqZ1KqpMyJ0eNzuenAk5vLmxmXanasdpmXj0Pu15ZP5f3Dw= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1776700234; c=relaxed/simple; bh=eGaLR1LuTlFmK9QFcantO7x68tspLjhb53MDRnhtzS4=; h=Date:From:To:Subject:Message-ID:References:MIME-Version: Content-Type:Content-Disposition:In-Reply-To; b=uObQMy0heoTZSEIk+kxkZBMvaTpGqiyHnMekUh0l4WfV3OLgxep9NDDB7SDRWJ76SO8FIIbGKJExgz3ZQVb++5h3aUYaARe+4A8lbSBUg6Gt3sYP0p8PgIAEiVQZmOzz4CCW/7zybScT/oKlgr9vQIj4x5KLzDH0qpwxo4z1apc= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=makrotopia.org; spf=pass smtp.mailfrom=makrotopia.org; arc=none smtp.client-ip=185.142.180.65 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=makrotopia.org Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=makrotopia.org Received: from local by pidgin.makrotopia.org with esmtpsa (TLS1.3:TLS_AES_256_GCM_SHA384:256) (Exim 4.99) (envelope-from ) id 1wEqtJ-000000000qV-2YQF; Mon, 20 Apr 2026 15:50:29 +0000 Date: Mon, 20 Apr 2026 16:50:27 +0100 From: Daniel Golle To: Rob Herring , Krzysztof Kozlowski , Conor Dooley , Saravana Kannan , Daniel Golle , devicetree@vger.kernel.org, linux-kernel@vger.kernel.org Subject: [PATCH 1/2] dt-bindings: add DTS example style checker Message-ID: <941c1ca0cd30c596556a88c36d28d60368867409.1776700167.git.daniel@makrotopia.org> References: Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Disposition: inline In-Reply-To: Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Add a Python script that enforces coding style rules from dts-coding-style.rst on examples embedded in YAML binding files. Checks indentation, property and child node ordering, blank line placement, node naming, tabs, trailing whitespace and unused labels. Produces a canonical form and diffs it against the original. Signed-off-by: Daniel Golle --- scripts/dtc/dt-check-example-style | 712 +++++++++++++++++++++++++++++ 1 file changed, 712 insertions(+) create mode 100755 scripts/dtc/dt-check-example-style diff --git a/scripts/dtc/dt-check-example-style b/scripts/dtc/dt-check-exam= ple-style new file mode 100755 index 0000000000000..3daccee3a6f61 --- /dev/null +++ b/scripts/dtc/dt-check-example-style @@ -0,0 +1,712 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-2.0-only +# +# Check DTS example style in YAML binding files. +# +# Enforces the coding style rules from +# Documentation/devicetree/bindings/dts-coding-style.rst on the examples: +# sections of devicetree binding YAML files. + +import argparse +import difflib +import re +import sys +from enum import Enum, auto + +import ruamel.yaml + + +class LineType(Enum): + BLANK =3D auto() + COMMENT =3D auto() + COMMENT_START =3D auto() + COMMENT_BODY =3D auto() + COMMENT_END =3D auto() + PREPROCESSOR =3D auto() + NODE_OPEN =3D auto() + NODE_CLOSE =3D auto() + PROPERTY =3D auto() + CONTINUATION =3D auto() + + +class DtsLine: + __slots__ =3D ('raw', 'linetype', 'indent', 'stripped', 'prop_name', + 'continuations', 'node_name', 'node_addr', 'label', + 'ref_name') + + def __init__(self, raw, linetype, indent, stripped): + self.raw =3D raw + self.linetype =3D linetype + self.indent =3D indent + self.stripped =3D stripped + self.prop_name =3D None + self.continuations =3D [] + self.node_name =3D None + self.node_addr =3D None + self.label =3D None + self.ref_name =3D None + + +re_cpp_directive =3D re.compile( + r'^#\s*(include|define|undef|ifdef|ifndef|if|else|elif|endif|' + r'pragma|error|warning)\b') + + +def is_preprocessor(stripped): + """Distinguish C preprocessor directives from DTS # properties. + + DTS properties like #address-cells, #size-cells, #interrupt-cells + start with # but are followed by a property name pattern and =3D or ;. + """ + return re_cpp_directive.match(stripped) is not None + + +def classify_lines(text): + """Classify each line of a DTS example into structural types.""" + lines =3D text.split('\n') + result =3D [] + in_block_comment =3D False + prev_complete =3D True + + for raw in lines: + stripped =3D raw.lstrip() + indent =3D len(raw) - len(stripped) if stripped else 0 + + if not stripped: + result.append(DtsLine(raw, LineType.BLANK, 0, '')) + continue + + if in_block_comment: + if '*/' in stripped: + in_block_comment =3D False + result.append(DtsLine(raw, LineType.COMMENT_END, + indent, stripped)) + else: + result.append(DtsLine(raw, LineType.COMMENT_BODY, + indent, stripped)) + continue + + if stripped.startswith('/*'): + if '*/' in stripped: + result.append(DtsLine(raw, LineType.COMMENT, + indent, stripped)) + else: + in_block_comment =3D True + result.append(DtsLine(raw, LineType.COMMENT_START, + indent, stripped)) + continue + + if stripped.startswith('//'): + result.append(DtsLine(raw, LineType.COMMENT, indent, stripped)) + continue + + if stripped.startswith('#') and is_preprocessor(stripped): + result.append(DtsLine(raw, LineType.PREPROCESSOR, + indent, stripped)) + prev_complete =3D True + continue + + if not prev_complete: + dl =3D DtsLine(raw, LineType.CONTINUATION, indent, stripped) + result.append(dl) + prev_complete =3D (stripped.endswith(';') or + stripped.endswith('{') or + stripped.rstrip().endswith('};')) + continue + + if stripped.rstrip(';').rstrip() =3D=3D '}' or stripped =3D=3D '};= ': + result.append(DtsLine(raw, LineType.NODE_CLOSE, + indent, stripped)) + prev_complete =3D True + continue + + if stripped.endswith('{'): + dl =3D DtsLine(raw, LineType.NODE_OPEN, indent, stripped) + parse_node_header(dl) + result.append(dl) + prev_complete =3D True + continue + + # Property or boolean property + dl =3D DtsLine(raw, LineType.PROPERTY, indent, stripped) + parse_property_name(dl) + result.append(dl) + prev_complete =3D stripped.endswith(';') + continue + + return result + + +# Regex for node header: optional "label: " then name optionally @addr +# then optional whitespace and { +re_node_header =3D re.compile( + r'^(?:([a-zA-Z_][a-zA-Z0-9_]*):\s*)?' # optional label + r'([a-zA-Z][a-zA-Z0-9,._+-]*|/)' # node name or / + r'(?:@([0-9a-fA-F,]+))?' # optional @address + r'\s*\{$' +) + +# Regex for &reference node: &label { ... } +re_ref_node =3D re.compile( + r'^&([a-zA-Z_][a-zA-Z0-9_]*)\s*\{$' +) + + +def parse_node_header(dl): + """Extract label, node name, and unit address from a NODE_OPEN line.""" + m =3D re_node_header.match(dl.stripped) + if m: + dl.label =3D m.group(1) + dl.node_name =3D m.group(2) + dl.node_addr =3D m.group(3) + return + + m =3D re_ref_node.match(dl.stripped) + if m: + dl.ref_name =3D m.group(1) + + +def parse_property_name(dl): + """Extract the property name from a PROPERTY line.""" + s =3D dl.stripped + # "prop-name =3D value;" or "prop-name;" + m =3D re.match(r'^([a-zA-Z#][a-zA-Z0-9,._+#-]*)\s*[=3D;]', s) + if m: + dl.prop_name =3D m.group(1) + + +def group_continuations(lines): + """Attach CONTINUATION lines to their preceding PROPERTY.""" + grouped =3D [] + for dl in lines: + if dl.linetype =3D=3D LineType.CONTINUATION and grouped: + # Find the last PROPERTY in grouped + for prev in reversed(grouped): + if prev.linetype =3D=3D LineType.PROPERTY: + prev.continuations.append(dl) + break + else: + grouped.append(dl) + else: + grouped.append(dl) + return grouped + + +def collect_labels_and_refs(text): + """Collect all label definitions and &references in example text.""" + labels_defined =3D set() + labels_referenced =3D set() + + for m in re.finditer(r'([a-zA-Z_][a-zA-Z0-9_]*):', text): + # Exclude things like http: or property names before =3D + ctx_after =3D text[m.end():m.end() + 20].lstrip() + # A label is followed by a node name or another label + # Property names are followed by =3D or ; + if not ctx_after or ctx_after[0] not in ('=3D', ';', '"', '<'): + labels_defined.add(m.group(1)) + + for m in re.finditer(r'&([a-zA-Z_][a-zA-Z0-9_]*)', text): + labels_referenced.add(m.group(1)) + + return labels_defined, labels_referenced + + +def prop_sort_key(dl, orig_index): + """Sort key for property ordering within a node. + + Order: compatible, reg, ranges, standard props (original order), + vendor props (original order), status. + + Within the standard and vendor groups, original order is preserved + (stable sort) because the coding style examples are not fully + consistent on natural sort within groups. + """ + name =3D dl.prop_name or '' + check_name =3D name.lstrip('#') + + if name =3D=3D 'compatible': + return (0, 0) + if name =3D=3D 'reg': + return (1, 0) + if name =3D=3D 'reg-names': + return (1, 1) + if name =3D=3D 'ranges': + return (2, 0) + if name =3D=3D 'status': + return (5, 0) + + # Vendor-specific: name contains a comma + if ',' in check_name: + return (4, orig_index) + + # Standard property -- preserve original order + return (3, orig_index) + + +def natural_sort_key(s): + """Sort key that handles embedded numbers naturally.""" + parts =3D [] + for part in re.split(r'(\d+)', s): + if part.isdigit(): + parts.append((0, int(part))) + else: + parts.append((1, part)) + return parts + + +def node_sort_key(dl): + """Sort key for child node ordering. + + Nodes with unit addresses sort by address (numeric), then nodes + without addresses sort by name (natural). + """ + if dl.node_addr is not None: + # Parse comma-separated hex address parts + parts =3D dl.node_addr.split(',') + try: + nums =3D tuple(int(p, 16) for p in parts) + return (0, nums, '') + except ValueError: + return (0, (0,), dl.node_addr) + name =3D dl.node_name or '' + return (1, (0,), natural_sort_key(name)) + + +def continuation_align_col(first_line_stripped): + """Find the column to align continuation lines to. + + Aligns to the first < or " after =3D in the property's first line. + Returns None if no alignment target found. + """ + eq_pos =3D first_line_stripped.find('=3D') + if eq_pos < 0: + return None + rest =3D first_line_stripped[eq_pos + 1:] + for i, ch in enumerate(rest): + if ch in '<"': + return eq_pos + 1 + i + return None + + +def canonicalize_node_body(lines, depth): + """Re-emit a node body in canonical form. + + lines: list of DtsLine within a single { } scope (excluding the + { and } lines themselves). + depth: nesting depth of contents (1 for top-level node body). + Returns list of canonical output lines (strings). + """ + indent =3D ' ' * depth + output =3D [] + + # Separate into properties, comments attached to properties, + # and child nodes + properties =3D [] + child_nodes =3D [] + i =3D 0 + pending_comments =3D [] + + while i < len(lines): + dl =3D lines[i] + + if dl.linetype =3D=3D LineType.BLANK: + i +=3D 1 + continue + + if dl.linetype in (LineType.COMMENT, LineType.COMMENT_START, + LineType.COMMENT_BODY, LineType.COMMENT_END): + pending_comments.append(dl) + i +=3D 1 + continue + + if dl.linetype =3D=3D LineType.PREPROCESSOR: + # Preprocessor lines within a node are unusual but pass through + properties.append(('preproc', pending_comments[:], dl)) + pending_comments.clear() + i +=3D 1 + continue + + if dl.linetype =3D=3D LineType.PROPERTY: + properties.append(('prop', pending_comments[:], dl)) + pending_comments.clear() + i +=3D 1 + continue + + if dl.linetype =3D=3D LineType.NODE_OPEN: + # Collect the entire child node + child_lines =3D [] + child_open =3D dl + child_depth =3D 1 + i +=3D 1 + while i < len(lines) and child_depth > 0: + if lines[i].linetype =3D=3D LineType.NODE_OPEN: + child_depth +=3D 1 + elif lines[i].linetype =3D=3D LineType.NODE_CLOSE: + child_depth -=3D 1 + if child_depth =3D=3D 0: + i +=3D 1 + break + child_lines.append(lines[i]) + i +=3D 1 + child_nodes.append(('node', pending_comments[:], + child_open, child_lines)) + pending_comments.clear() + continue + + # Unexpected line type -- pass through + properties.append(('other', pending_comments[:], dl)) + pending_comments.clear() + i +=3D 1 + + # Sort properties by canonical order (with original index for stabilit= y) + prop_items =3D [(p, idx) for idx, p in enumerate(properties) + if p[0] =3D=3D 'prop'] + sorted_props =3D sorted( + prop_items, + key=3Dlambda pi: prop_sort_key(pi[0][2], pi[1]) + ) + sorted_props =3D [p for p, _ in sorted_props] + # Keep preprocessor and other lines in their relative position + # (prepend them before the sorted properties) + preprocs =3D [p for p in properties if p[0] !=3D 'prop'] + + # Sort child nodes + sorted_children =3D sorted(child_nodes, key=3Dlambda c: node_sort_key(= c[2])) + + # Emit preprocessor lines first (if any within node) + for kind, comments, dl in preprocs: + for c in comments: + output.append(format_comment_line(c, indent)) + output.append(dl.raw.rstrip()) # preprocessor: preserve original + need_blank =3D True + + # Emit sorted properties + prev_was_status =3D False + for idx, (kind, comments, dl) in enumerate(sorted_props): + # Blank line before status + if dl.prop_name =3D=3D 'status' and output: + output.append('') + + for c in comments: + output.append(format_comment_line(c, indent)) + + output.append(format_property(dl, indent)) + for cont in dl.continuations: + output.append(format_continuation(dl, cont, indent)) + + # Emit sorted child nodes + for kind, comments, child_open, child_body in sorted_children: + # Blank line before each child node + if output: + output.append('') + + for c in comments: + output.append(format_comment_line(c, indent)) + + output.append(indent + format_node_open(child_open)) + body_output =3D canonicalize_node_body(child_body, depth + 1) + output.extend(body_output) + output.append(indent + '};') + + # Trailing comments that weren't attached + if pending_comments: + for c in pending_comments: + output.append(format_comment_line(c, indent)) + + return output + + +def format_comment_line(dl, indent): + """Format a comment line with canonical indent.""" + # Block comment body/end lines get extra space for " * " style + if dl.linetype in (LineType.COMMENT_BODY, LineType.COMMENT_END): + return indent + ' ' + dl.stripped + return indent + dl.stripped + + +def format_property(dl, indent): + """Format a property line with canonical indent.""" + return indent + dl.stripped + + +def format_continuation(prop_dl, cont_dl, indent): + """Format a continuation line aligned to the property's value.""" + col =3D continuation_align_col(prop_dl.stripped) + if col is not None: + # Align to that column relative to indent + total_col =3D len(indent) + col + return ' ' * total_col + cont_dl.stripped + # Fallback: indent + 4 extra spaces + return indent + ' ' + cont_dl.stripped + + +def format_node_open(dl): + """Format a node opening line canonically.""" + if dl.ref_name: + return '&{} {{'.format(dl.ref_name) + parts =3D [] + if dl.label: + parts.append(dl.label + ': ') + if dl.node_name: + parts.append(dl.node_name) + if dl.node_addr is not None: + parts.append('@' + dl.node_addr) + parts.append(' {') + return ''.join(parts) + + +def canonicalize_example(text): + """Produce canonical form of a DTS example.""" + lines =3D classify_lines(text) + lines =3D group_continuations(lines) + + output =3D [] + # Top-level: preprocessor lines first, then nodes + top_preprocs =3D [] + top_nodes =3D [] + i =3D 0 + pending_comments =3D [] + + while i < len(lines): + dl =3D lines[i] + + if dl.linetype =3D=3D LineType.BLANK: + i +=3D 1 + continue + + if dl.linetype in (LineType.COMMENT, LineType.COMMENT_START, + LineType.COMMENT_BODY, LineType.COMMENT_END): + pending_comments.append(dl) + i +=3D 1 + continue + + if dl.linetype =3D=3D LineType.PREPROCESSOR: + top_preprocs.append((pending_comments[:], dl)) + pending_comments.clear() + i +=3D 1 + continue + + if dl.linetype =3D=3D LineType.NODE_OPEN: + node_lines =3D [] + node_open =3D dl + depth =3D 1 + i +=3D 1 + while i < len(lines) and depth > 0: + if lines[i].linetype =3D=3D LineType.NODE_OPEN: + depth +=3D 1 + elif lines[i].linetype =3D=3D LineType.NODE_CLOSE: + depth -=3D 1 + if depth =3D=3D 0: + i +=3D 1 + break + node_lines.append(lines[i]) + i +=3D 1 + top_nodes.append((pending_comments[:], node_open, node_lines)) + pending_comments.clear() + continue + + # Top-level property (unusual but possible, e.g. /delete-node/) + output.append(dl.raw.rstrip()) + pending_comments.clear() + i +=3D 1 + + # Emit preprocessor includes + for comments, dl in top_preprocs: + for c in comments: + output.append(format_comment_line(c, '')) + output.append(dl.stripped) + + # Blank line between preprocessor and first node + if top_preprocs and top_nodes: + output.append('') + + # Emit top-level nodes + for idx, (comments, node_open, node_body) in enumerate(top_nodes): + if idx > 0: + output.append('') + + for c in comments: + output.append(format_comment_line(c, '')) + + output.append(format_node_open(node_open)) + body =3D canonicalize_node_body(node_body, 1) + output.extend(body) + output.append('};') + + # Trailing comments + for c in pending_comments: + output.append(format_comment_line(c, '')) + + return '\n'.join(output) + + +def check_node_names(lines, errors, filepath, base_line): + """Check node name coding style rules.""" + re_valid_name =3D re.compile(r'^[a-z][a-z0-9-]*$') + + for dl in lines: + if dl.linetype !=3D LineType.NODE_OPEN: + continue + if dl.node_name is None: + continue + # Skip root node "/" + if dl.node_name =3D=3D '/': + continue + + name =3D dl.node_name + if not re_valid_name.match(name): + errors.append( + "node name '{}' must use only [a-z0-9-]".format(name)) + + if dl.node_addr is not None: + addr =3D dl.node_addr + # Check no leading zeros in each address part + for part in addr.split(','): + if len(part) > 1 and part.startswith('0'): + errors.append( + "unit address '{}' has leading zeros" + .format(addr)) + break + # Must be valid hex + try: + int(part, 16) + except ValueError: + errors.append( + "unit address '{}' is not valid hex" + .format(addr)) + break + + +def check_unused_labels(text, errors): + """Check for labels that are defined but never referenced.""" + labels_defined, labels_referenced =3D collect_labels_and_refs(text) + for label in sorted(labels_defined - labels_referenced): + # Skip fake_intc labels (injected by dt-extract-example wrapper) + if label.startswith('fake_intc'): + continue + errors.append( + "label '{}' defined but never referenced".format(label)) + + +def check_tabs(lines, errors): + """Check for tabs in non-preprocessor DTS lines.""" + for dl in lines: + if dl.linetype =3D=3D LineType.PREPROCESSOR: + continue + if dl.linetype =3D=3D LineType.BLANK: + continue + if '\t' in dl.raw: + errors.append("tab character in DTS line: {}".format( + dl.raw.rstrip())) + + +def check_trailing_whitespace(lines, errors): + """Check for trailing whitespace.""" + for dl in lines: + if dl.raw !=3D dl.raw.rstrip(): + errors.append("trailing whitespace: {}".format( + repr(dl.raw.rstrip()[-30:]))) + + +def check_example(text, filepath, yaml_line, show_diff=3DFalse): + """Check a single DTS example. Returns list of error strings.""" + errors =3D [] + lines =3D classify_lines(text) + + check_trailing_whitespace(lines, errors) + check_tabs(lines, errors) + check_node_names(lines, errors, filepath, yaml_line) + check_unused_labels(text, errors) + + # Generate canonical form and compare + canonical =3D canonicalize_example(text) + + # Strip trailing newlines for comparison + orig_stripped =3D text.rstrip('\n') + canon_stripped =3D canonical.rstrip('\n') + + if orig_stripped !=3D canon_stripped: + if show_diff: + orig_lines =3D orig_stripped.splitlines() + canon_lines =3D canon_stripped.splitlines() + diff =3D difflib.unified_diff( + orig_lines, canon_lines, + fromfile=3D'{} (original)'.format(filepath), + tofile=3D'{} (canonical)'.format(filepath), + lineterm=3D'') + errors.append('canonical form differs:\n' + '\n'.join(diff)) + else: + # Generate concise per-line errors + orig_l =3D orig_stripped.splitlines() + canon_l =3D canon_stripped.splitlines() + maxlen =3D max(len(orig_l), len(canon_l)) + for i in range(maxlen): + o =3D orig_l[i] if i < len(orig_l) else '' + c =3D canon_l[i] if i < len(canon_l) else '' + if o !=3D c: + errors.append( + 'line {}: expected: {}'.format(i + 1, c.rstrip())) + + return errors + + +def process_file(filepath, show_diff=3DFalse): + """Process a single YAML binding file. Returns total error count.""" + yaml =3D ruamel.yaml.YAML() + try: + with open(filepath, encoding=3D'utf-8') as f: + data =3D yaml.load(f) + except Exception as e: + print('{}: error loading YAML: {}'.format(filepath, e), + file=3Dsys.stderr) + return 0 + + if not isinstance(data, dict) or 'examples' not in data: + return 0 + + examples =3D data['examples'] + total_errors =3D 0 + + for i, ex in enumerate(examples): + if not isinstance(ex, str): + continue + + # Get YAML line number for error reporting + try: + yaml_line =3D examples.lc.item(i)[0] + 2 + except Exception: + yaml_line =3D 0 + + errors =3D check_example(str(ex), filepath, yaml_line, + show_diff=3Dshow_diff) + for err in errors: + total_errors +=3D 1 + if yaml_line: + print('{}:{}: example {}: {}'.format( + filepath, yaml_line, i, err), file=3Dsys.stderr) + else: + print('{}: example {}: {}'.format( + filepath, i, err), file=3Dsys.stderr) + + return total_errors + + +def main(): + ap =3D argparse.ArgumentParser( + description=3D'Check DTS example style in YAML binding files.', + fromfile_prefix_chars=3D'@') + ap.add_argument('yamlfiles', nargs=3D'+', metavar=3D'yamlfile', + help=3D'YAML binding files to check') + ap.add_argument('--diff', action=3D'store_true', + help=3D'Show unified diff of original vs. canonical') + args =3D ap.parse_args() + + total_errors =3D 0 + for filepath in args.yamlfiles: + total_errors +=3D process_file(filepath, show_diff=3Dargs.diff) + + sys.exit(1 if total_errors > 0 else 0) + + +if __name__ =3D=3D '__main__': + main() --=20 2.53.0 From nobody Tue Jun 16 19:24:44 2026 Received: from pidgin.makrotopia.org (pidgin.makrotopia.org [185.142.180.65]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id ABC0233BBCC; Mon, 20 Apr 2026 15:50:38 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=185.142.180.65 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1776700240; cv=none; b=fyzcnflMjTdYhu4y7No+sJ3bBc80YxEoi+NKQ6z80mlSwbQ8AV9kcyE/vchlM19O2bR9rVaHJ3oBV+fcD263DJvpW1K6oECoijTewA/wvMoAjO/wSScQbCjbOu20in7fzkCLzgK+bnCGEqUsVeF2QUkTbRI3B7MwAuCBpiRkMjU= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1776700240; c=relaxed/simple; bh=CLCPgkQgMRPGwrXuCFS2MSR57Ws93kW9/oM7ypStVGU=; h=Date:From:To:Subject:Message-ID:References:MIME-Version: Content-Type:Content-Disposition:In-Reply-To; b=toRMlwMTqt4OEjdhfiCpJClG+XkZPY03Am/SKcBaVjB+Sz2l3N2F6aYE+mjsh4YGTN5YBwwpuKPE1LFsgd8hoZ0rP62xqC+Bn83zmyH/FcSNlh40Ngdc9YUDBt+3ok9U6tRLUXepNR5fVwG6W/atnOoqzkz4Fgj89zwT201V0Zo= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=makrotopia.org; spf=pass smtp.mailfrom=makrotopia.org; arc=none smtp.client-ip=185.142.180.65 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=makrotopia.org Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=makrotopia.org Received: from local by pidgin.makrotopia.org with esmtpsa (TLS1.3:TLS_AES_256_GCM_SHA384:256) (Exim 4.99) (envelope-from ) id 1wEqtQ-000000000qh-2HKA; Mon, 20 Apr 2026 15:50:36 +0000 Date: Mon, 20 Apr 2026 16:50:34 +0100 From: Daniel Golle To: Rob Herring , Krzysztof Kozlowski , Conor Dooley , Saravana Kannan , Daniel Golle , devicetree@vger.kernel.org, linux-kernel@vger.kernel.org Subject: [PATCH 2/2] dt-bindings: wire example style check into dt_binding_check Message-ID: <30996f9ca58afada03ec62100c36e1d0566a4561.1776700167.git.daniel@makrotopia.org> References: Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Disposition: inline In-Reply-To: Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Run dt-check-example-style as part of dt_binding_check_one to catch formatting issues in DTS examples during schema validation. Signed-off-by: Daniel Golle --- Documentation/devicetree/bindings/Makefile | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/Documentation/devicetree/bindings/Makefile b/Documentation/dev= icetree/bindings/Makefile index 7b668f7fd4007..364d167e8dd27 100644 --- a/Documentation/devicetree/bindings/Makefile +++ b/Documentation/devicetree/bindings/Makefile @@ -46,6 +46,14 @@ quiet_cmd_chk_bindings =3D CHKDT $(src) xargs -n200 -P$$(nproc) $(DT_DOC_CHECKER) -u $(src)) \ && touch $@ || true =20 +DT_CHK_EX_STYLE =3D $(srctree)/scripts/dtc/dt-check-example-style + +quiet_cmd_chk_ex_style =3D STYLE $(src) + cmd_chk_ex_style =3D ($(find_cmd) | \ + xargs -n200 -P$$(nproc) \ + $(PYTHON3) $(DT_CHK_EX_STYLE) --diff) \ + && touch $@ || true + quiet_cmd_mk_schema =3D SCHEMA $@ cmd_mk_schema =3D f=3D$$(mktemp) ; \ $(find_all_cmd) > $$f ; \ @@ -62,13 +70,16 @@ override DTC_FLAGS :=3D \ $(obj)/processed-schema.json: $(DT_DOCS) check_dtschema_version FORCE $(call if_changed,mk_schema) =20 -targets +=3D .dt-binding.checked .yamllint.checked +targets +=3D .dt-binding.checked .yamllint.checked .dt-example-style.check= ed $(obj)/.yamllint.checked: $(DT_DOCS) $(src)/.yamllint FORCE $(if $(DT_SCHEMA_LINT),$(call if_changed,yamllint),) =20 $(obj)/.dt-binding.checked: $(DT_DOCS) FORCE $(call if_changed,chk_bindings) =20 +$(obj)/.dt-example-style.checked: $(DT_DOCS) FORCE + $(call if_changed,chk_ex_style) + always-y +=3D processed-schema.json targets +=3D $(patsubst $(obj)/%,%, $(CHK_DT_EXAMPLES)) targets +=3D $(patsubst $(obj)/%.dtb,%.dts, $(CHK_DT_EXAMPLES)) @@ -82,7 +93,7 @@ dt_compatible_check: $(obj)/processed-schema.json $(Q)$(srctree)/scripts/dtc/dt-extract-compatibles $(srctree) | xargs dt-c= heck-compatible -v -s $< =20 PHONY +=3D dt_binding_check_one -dt_binding_check_one: $(obj)/.dt-binding.checked $(obj)/.yamllint.checked +dt_binding_check_one: $(obj)/.dt-binding.checked $(obj)/.yamllint.checked = $(obj)/.dt-example-style.checked =20 PHONY +=3D dt_binding_check dt_binding_check: dt_binding_check_one $(CHK_DT_EXAMPLES) --=20 2.53.0