From nobody Sun May 24 20:33:09 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 6D3AF36729D; Fri, 22 May 2026 18:04:59 +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=1779473105; cv=none; b=QyCK1Jgf4mkFaz7CrVX09u674LiBhLVaB+i19KOcJHJAnxYlXEDcegyVChb3CU5/w0nqFWeHdmhlR4Cx2lCyF5D8zAxjB0ajlEGYCP9gVHQDB+BYCueBwuJOdkJAHPQWOkVjoABnNga6sU/aoMJ07XDrXJu/1vmYCzvKO54ROZs= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1779473105; c=relaxed/simple; bh=0DTNxYnlnfjX4WC1LZRetU3zSET+7o0dWRfnZu62THk=; h=Date:From:To:Subject:Message-ID:References:MIME-Version: Content-Type:Content-Disposition:In-Reply-To; b=pGFZPaIJwTu7Kf0jopuy/IXf9AILaKLPln3kcZbrlb2pKEuCr+MRJO+NsvguNwWTiKrxMmcEEMq7HWEAZacylwWgI7Yz9ZE2T7Kl7tK86saczUrI7OgrM/L6ioOitNiVfUweYyu919M1aNj7pcjFKIglhajZaehvZPIE5cQ6W3w= 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 1wQUEn-000000006xS-2Pmb; Fri, 22 May 2026 18:04:45 +0000 Date: Fri, 22 May 2026 19:04:42 +0100 From: Daniel Golle To: Rob Herring , Krzysztof Kozlowski , Conor Dooley , Nathan Chancellor , Nicolas Schier , Saravana Kannan , Daniel Golle , Miguel Ojeda , Gary Guo , Tamir Duberstein , Thomas =?iso-8859-1?Q?Wei=DFschuh?= , Steven Rostedt , Masahiro Yamada , Aleksander Jan Bajkowski , Guenter Roeck , Test User , devicetree@vger.kernel.org, linux-kernel@vger.kernel.org, linux-kbuild@vger.kernel.org Subject: [PATCH v4 1/3] dt-bindings: add DTS style checker Message-ID: <457f328d4c5d639482fddf40e79be61b97db5814.1779472837.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 tool that checks DTS coding style on examples in YAML binding files and on .dts/.dtsi/.dtso source files. Rules are kept in a small declarative registry, each tagged 'relaxed' (default; must be zero-violation on the current tree) or 'strict' (opt-in for new submissions). Promoting a rule from strict to relaxed is a one-line edit once the tree is clean. Relaxed mode covers trailing whitespace, tab characters in YAML examples, mixed tab+space indents, and missing tabs in .dts files. Strict adds indent unit and consistency checks, blank-line placement, sibling address ordering, "compatible" and "reg" ordering, and unused labels. The tool reads file paths from @argfile and parallelises across CPUs via -j N. With no -j given it picks up $PARALLELISM (set by scripts/jobserver-exec from the GNU make jobserver) and falls back to os.cpu_count() otherwise. Running as one Python invocation amortises the ruamel.yaml import across the whole tree -- ~2s on a 32-CPU host vs ~28s sequential. Signed-off-by: Daniel Golle --- Changes since v3: - node and property name regexes now accept a leading digit; the DT spec permits node names like 1wire@10 or 3d-engine@20 - classify_lines() strips trailing // and /* */ comments before the structural endswith() checks (new _split_code() helper), so a line such as "node { /* c */" or "prop =3D <1>; // c" is no longer misclassified as a property or continuation - continuation-alignment now compares display columns (tabs expanded to 8) instead of raw string length, so tab-and-space aligned .dts continuation lines are not falsely flagged; the column helper is shared with line-length - value-whitespace flags only whitespace directly inside the brackets (after '<', before '>') and checks single- and multi-line cell lists; it no longer flags inter-value spacing Changes since v2: - route findings output from stdout to stderr so a quiet dt_binding_check produces no output (Rob) Changes since v1: - renamed dt-check-example-style -> dt-check-style; tool now also accepts .dts/.dtsi/.dtso files directly (tab-indent variant) and distinguishes .dts/.dtsi/.dtso so unused-labels skips .dtsi/.dtso where labels are exported to includers/applies-to - rules declared in a registry tagged relaxed/strict; default relaxed mode is zero-violation on the current tree - added -j N with $PARALLELISM (jobserver) awareness - dropped node-name [a-z0-9-] check (Rob: better as a meta-schema) - property-order rebuilt around buckets + declarative pairing rules (-names after , pinctrl-names after last pinctrl-N) plus natural-sort fallback - added child-name-order, required-blank-lines, hex-case, unit-address-format, value-whitespace, node-close-alone, line-length and continuation-alignment to strict mode --- scripts/dtc/dt-check-style | 1120 ++++++++++++++++++++++++++++++++++++ 1 file changed, 1120 insertions(+) create mode 100755 scripts/dtc/dt-check-style diff --git a/scripts/dtc/dt-check-style b/scripts/dtc/dt-check-style new file mode 100755 index 000000000000..93cb1d7d2985 --- /dev/null +++ b/scripts/dtc/dt-check-style @@ -0,0 +1,1120 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-2.0-only +# +# Check DTS coding style on YAML binding examples and on +# .dts/.dtsi/.dtso source files. Enforces rules from +# Documentation/devicetree/bindings/dts-coding-style.rst. +# +# Two modes: +# --mode=3Drelaxed (default) +# Only rules that produce zero warnings on the current tree. +# Suitable for dt_binding_check. +# --mode=3Dstrict +# All rules. Required for new submissions. +# +# Two input types (auto-detected by file extension): +# *.yaml -- DT binding; check each example block +# *.dts/*.dtsi/*.dtso -- DTS source; whole file is one block +# +# Rules are declared in a registry (see RULES below); each rule is +# tagged with the lowest mode that runs it. Promoting a rule from +# 'strict' to 'relaxed' is a one-line change. + +import argparse +import re +import sys +from enum import Enum, auto + +import ruamel.yaml + + +# ------------------------------------------------------------------------= --- +# Line classification +# ------------------------------------------------------------------------= --- + +class LineType(Enum): + BLANK =3D auto() + COMMENT =3D auto() # // ... or /* ... */ on one line + COMMENT_START =3D auto() # /* without closing */ + COMMENT_BODY =3D auto() # inside a multi-line comment + COMMENT_END =3D auto() # closing */ + PREPROCESSOR =3D auto() # #include / #define / #ifdef / ... + NODE_OPEN =3D auto() # something { (with optional label/name/add= r) + NODE_CLOSE =3D auto() # }; + PROPERTY =3D auto() # name =3D value; or name; + CONTINUATION =3D auto() # continuation of a multi-line property + + +re_cpp_directive =3D re.compile( + r'^#\s*(include|define|undef|ifdef|ifndef|if|else|elif|endif|' + r'pragma|error|warning)\b') + +# label: name@addr { -- label and addr optional; name can be "/" +# Per the DT spec a node name may start with a digit (e.g. 1wire@...). +re_node_header =3D re.compile( + r'^(?:([a-zA-Z_][a-zA-Z0-9_]*):\s*)?' + r'([a-zA-Z0-9][a-zA-Z0-9,._+-]*|/)' + r'(?:@([0-9a-fA-F,]+))?' + r'\s*\{$') + +re_ref_node =3D re.compile( + r'^&([a-zA-Z_][a-zA-Z0-9_]*)\s*\{$') + + +def is_preprocessor(stripped): + """Tell C preprocessor directives apart from DTS '#'-prefixed props.""" + return re_cpp_directive.match(stripped) is not None + + +class DtsLine: + __slots__ =3D ('lineno', 'raw', 'linetype', 'indent_str', 'stripped', + 'prop_name', 'continuations', + 'node_name', 'node_addr', 'label', 'ref_name', 'depth') + + def __init__(self, lineno, raw, linetype, indent_str, stripped): + self.lineno =3D lineno # 1-based within the block + self.raw =3D raw + self.linetype =3D linetype + self.indent_str =3D indent_str # leading whitespace as-is + 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 + self.depth =3D 0 # filled in by classify_lines + + +def _split_code(text): + """Return (code, opens_block) for a leading-stripped line: the + code portion with // and /* */ comments removed (string literals + kept verbatim), and whether a /* */ block comment is left open. + The code portion is right-stripped so the endswith() checks in + classify_lines see code only, not a trailing comment or blanks.""" + out =3D [] + i =3D 0 + n =3D len(text) + while i < n: + c =3D text[i] + if c =3D=3D '"': + j =3D i + 1 + while j < n: + if text[j] =3D=3D '\\': + j +=3D 2 + continue + if text[j] =3D=3D '"': + j +=3D 1 + break + j +=3D 1 + out.append(text[i:j]) + i =3D j + continue + if c =3D=3D '/' and i + 1 < n and text[i + 1] =3D=3D '/': + break + if c =3D=3D '/' and i + 1 < n and text[i + 1] =3D=3D '*': + end =3D text.find('*/', i + 2) + if end < 0: + return (''.join(out).rstrip(), True) + i =3D end + 2 + continue + out.append(c) + i +=3D 1 + return (''.join(out).rstrip(), False) + + +def classify_lines(text): + """Return a list of DtsLine. Tracks { } depth and groups + continuation lines onto their leading PROPERTY line.""" + out =3D [] + in_block_comment =3D False + prev_complete =3D True + depth =3D 0 + + # Split preserving the indent string verbatim + re_lead =3D re.compile(r'^([ \t]*)(.*)$') + + for i, raw in enumerate(text.split('\n'), start=3D1): + m =3D re_lead.match(raw) + indent_str =3D m.group(1) + stripped =3D m.group(2) + + if not stripped: + dl =3D DtsLine(i, raw, LineType.BLANK, '', '') + dl.depth =3D depth + out.append(dl) + continue + + if in_block_comment: + ltype =3D (LineType.COMMENT_END if '*/' in stripped + else LineType.COMMENT_BODY) + if ltype =3D=3D LineType.COMMENT_END: + in_block_comment =3D False + dl =3D DtsLine(i, raw, ltype, indent_str, stripped) + dl.depth =3D depth + out.append(dl) + continue + + if stripped.startswith('/*'): + if '*/' in stripped: + ltype =3D LineType.COMMENT + else: + in_block_comment =3D True + ltype =3D LineType.COMMENT_START + dl =3D DtsLine(i, raw, ltype, indent_str, stripped) + dl.depth =3D depth + out.append(dl) + continue + + if stripped.startswith('//'): + dl =3D DtsLine(i, raw, LineType.COMMENT, indent_str, stripped) + dl.depth =3D depth + out.append(dl) + continue + + if stripped.startswith('#') and is_preprocessor(stripped): + dl =3D DtsLine(i, raw, LineType.PREPROCESSOR, + indent_str, stripped) + dl.depth =3D depth + out.append(dl) + prev_complete =3D True + continue + + # Drop a trailing comment so the structural suffix checks + # below see code only; a '/*' left open carries over. + code, opens_block =3D _split_code(stripped) + if opens_block: + in_block_comment =3D True + + if not prev_complete: + dl =3D DtsLine(i, raw, LineType.CONTINUATION, indent_str, code) + dl.depth =3D depth + out.append(dl) + prev_complete =3D (code.endswith(';') or + code.endswith('{') or + code.endswith('};')) + continue + + if code =3D=3D '};' or code =3D=3D '}': + depth =3D max(depth - 1, 0) + dl =3D DtsLine(i, raw, LineType.NODE_CLOSE, indent_str, code) + dl.depth =3D depth + out.append(dl) + prev_complete =3D True + continue + + if code.endswith('{'): + dl =3D DtsLine(i, raw, LineType.NODE_OPEN, indent_str, code) + parse_node_header(dl) + dl.depth =3D depth + out.append(dl) + depth +=3D 1 + prev_complete =3D True + continue + + # Property (or first line of a multi-line property). + dl =3D DtsLine(i, raw, LineType.PROPERTY, indent_str, code) + parse_property_name(dl) + dl.depth =3D depth + out.append(dl) + prev_complete =3D code.endswith(';') + + # Group continuation lines onto their leading PROPERTY. + last_prop =3D None + grouped =3D [] + for dl in out: + if dl.linetype =3D=3D LineType.CONTINUATION and last_prop is not N= one: + last_prop.continuations.append(dl) + continue + if dl.linetype =3D=3D LineType.PROPERTY: + last_prop =3D dl + elif dl.linetype !=3D LineType.BLANK and \ + dl.linetype not in (LineType.COMMENT, LineType.COMMENT_BOD= Y, + LineType.COMMENT_END, + LineType.COMMENT_START): + last_prop =3D None + grouped.append(dl) + return grouped + + +def parse_node_header(dl): + 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): + m =3D re.match(r'^([a-zA-Z0-9#][a-zA-Z0-9,._+#-]*)\s*[=3D;]', dl.strip= ped) + if m: + dl.prop_name =3D m.group(1) + + +def collect_labels_and_refs(text): + """Return (defined_labels, referenced_labels) found anywhere outside + /* */ comments. Labels named fake_intc* (injected by + dt-extract-example) are skipped.""" + # Strip block comments first so labels inside them don't count + stripped =3D re.sub(r'/\*.*?\*/', '', text, flags=3Dre.DOTALL) + # Strip line comments + stripped =3D re.sub(r'//[^\n]*', '', stripped) + defined =3D set() + referenced =3D set() + for m in re.finditer(r'(?:^|[\s{])([a-zA-Z_][a-zA-Z0-9_]*):\s*[a-zA-Z/= &]', + stripped): + name =3D m.group(1) + if not name.startswith('fake_intc'): + defined.add(name) + for m in re.finditer(r'&([a-zA-Z_][a-zA-Z0-9_]*)', stripped): + referenced.add(m.group(1)) + return defined, referenced + + +# ------------------------------------------------------------------------= --- +# Rule registry +# ------------------------------------------------------------------------= --- + +class Ctx: + """Context passed to each rule check. Carries the parsed lines, + raw text, mode, and indent kind.""" + + def __init__(self, lines, text, mode, indent_kind): + self.lines =3D lines + self.text =3D text + self.mode =3D mode # 'relaxed' or 'strict' + self.indent_kind =3D indent_kind # 'spaces' or 'tab' + + +class Rule: + __slots__ =3D ('name', 'mode', 'description', 'check', 'applies_to') + + def __init__(self, name, mode, description, check, + applies_to=3D('yaml', 'dts', 'dtsi', 'dtso')): + self.name =3D name + self.mode =3D mode # 'relaxed' or 'strict' + self.description =3D description + self.check =3D check + self.applies_to =3D applies_to # input types this rule covers + + +# --- individual rule check functions ------------------------------------= -- + +def check_trailing_whitespace(ctx): + for dl in ctx.lines: + if dl.raw !=3D dl.raw.rstrip(): + yield (dl.lineno, 'trailing whitespace') + + +def check_tab_in_dts(ctx): + """Reject literal tabs in DTS lines when input is YAML. + + For YAML examples, indent and content must use spaces. Tabs inside + a #define value are tolerated (those are CPP macros, not DTS). + For .dts files, this rule does not apply -- tabs are required. + """ + if ctx.indent_kind !=3D 'spaces': + return + for dl in ctx.lines: + if dl.linetype =3D=3D LineType.PREPROCESSOR: + continue + if dl.linetype =3D=3D LineType.BLANK: + continue + if '\t' in dl.raw: + yield (dl.lineno, 'tab character not allowed in DTS example') + + +def check_mixed_indent_chars(ctx): + """Indent must be all-spaces or all-tabs, never mixed on one line.""" + for dl in ctx.lines: + if not dl.indent_str: + continue + if dl.linetype =3D=3D LineType.PREPROCESSOR: + continue + if ' ' in dl.indent_str and '\t' in dl.indent_str: + yield (dl.lineno, 'mixed tabs and spaces in indent') + + +def detect_indent_unit(ctx): + """Find the indent unit used at depth 1 in this block. + + Returns one of: ' ' (2 spaces), ' ' (4 spaces), '\\t' (tab), + or None if depth-1 is empty or ambiguous.""" + for dl in ctx.lines: + if dl.depth !=3D 1: + continue + if dl.linetype in (LineType.BLANK, LineType.PREPROCESSOR): + continue + if dl.linetype in (LineType.COMMENT_BODY, LineType.COMMENT_END): + continue + if not dl.indent_str: + continue + if dl.indent_str =3D=3D '\t': + return '\t' + if dl.indent_str =3D=3D ' ': + return ' ' + if dl.indent_str =3D=3D ' ': + return ' ' + # Anything else at depth 1 is non-canonical; flag elsewhere. + return dl.indent_str + return None + + +def check_indent_unit_relaxed(ctx): + """YAML examples: 2 or 4 spaces. Never tabs or other widths.""" + unit =3D detect_indent_unit(ctx) + if unit is None: + return + if unit not in (' ', ' '): + yield (1, 'indent unit must be 2 or 4 spaces, got %r' % unit) + + +def check_indent_unit_dts(ctx): + """DTS files: 1 tab per level. Always required.""" + unit =3D detect_indent_unit(ctx) + if unit is None: + return + if unit !=3D '\t': + yield (1, 'indent unit must be 1 tab in DTS, got %r' % unit) + + +def check_indent_unit_strict(ctx): + """YAML: must be exactly 4 spaces. DTS: 1 tab (same as relaxed).""" + unit =3D detect_indent_unit(ctx) + if unit is None: + return + if ctx.indent_kind =3D=3D 'spaces': + if unit !=3D ' ': + yield (1, 'indent unit must be 4 spaces in strict mode, ' + 'got %r' % unit) + + +def check_indent_consistent(ctx): + """All indented lines must be a multiple of the detected unit.""" + unit =3D detect_indent_unit(ctx) + if unit is None: + return + if ctx.indent_kind =3D=3D 'spaces': + if unit not in (' ', ' '): + return # let check_indent_unit_* report this + else: + if unit !=3D '\t': + return + + for dl in ctx.lines: + if dl.linetype in (LineType.BLANK, LineType.PREPROCESSOR): + continue + if dl.linetype =3D=3D LineType.CONTINUATION: + continue # continuations align to <, not to indent unit + if dl.linetype in (LineType.COMMENT_BODY, LineType.COMMENT_END): + continue + if not dl.indent_str: + continue + # The indent must be 'unit' repeated dl.depth times, exactly. + # NODE_CLOSE lines have depth equal to the post-decrement value, + # which matches the indent expected. + expected =3D unit * dl.depth + if dl.indent_str !=3D expected: + yield (dl.lineno, + 'indent mismatch (expected depth %d * %r)' % + (dl.depth, unit)) + + +def check_blank_lines(ctx): + """No two consecutive blank lines, no leading/trailing blank lines + in any node body.""" + lines =3D ctx.lines + # Consecutive blanks + for i in range(1, len(lines)): + if lines[i].linetype =3D=3D LineType.BLANK and \ + lines[i - 1].linetype =3D=3D LineType.BLANK: + yield (lines[i].lineno, 'consecutive blank lines') + # Blank right after { or right before } + for i, dl in enumerate(lines): + if dl.linetype !=3D LineType.BLANK: + continue + prev =3D lines[i - 1] if i > 0 else None + nxt =3D lines[i + 1] if i + 1 < len(lines) else None + if prev is not None and prev.linetype =3D=3D LineType.NODE_OPEN: + yield (dl.lineno, 'blank line at start of node body') + if nxt is not None and nxt.linetype =3D=3D LineType.NODE_CLOSE: + yield (dl.lineno, 'blank line at end of node body') + + +def _walk_bodies(lines): + """Yield lists of immediate-child NODE_OPEN lines for each node body + in the input. Skips ref-nodes (&label) since those don't have an + intrinsic ordering.""" + body_stack =3D [[]] + for dl in lines: + if dl.linetype =3D=3D LineType.NODE_OPEN: + body_stack[-1].append(dl) + body_stack.append([]) + continue + if dl.linetype =3D=3D LineType.NODE_CLOSE: + if len(body_stack) <=3D 1: + # Unbalanced; ignore to avoid crashing on malformed input + continue + yield body_stack.pop() + continue + while body_stack: + yield body_stack.pop() + + +def _natural_sort_key(s): + """Split a string into a tuple of (kind, value) pairs that compares + numeric runs as ints, so 'foo10' sorts after 'foo2'.""" + parts =3D [] + for part in re.split(r'(\d+)', s): + if part.isdigit(): + parts.append((0, int(part))) + else: + parts.append((1, part)) + return tuple(parts) + + +def check_child_address_order(ctx): + """Addressed siblings (foo@N) must appear in ascending address + order within their parent node body.""" + for children in _walk_bodies(ctx.lines): + addressed =3D [] + for c in children: + if c.node_addr is None: + continue + try: + parts =3D tuple(int(p, 16) for p in c.node_addr.split(',')) + except ValueError: + continue + addressed.append((parts, c)) + for i in range(1, len(addressed)): + if addressed[i][0] < addressed[i - 1][0]: + dl =3D addressed[i][1] + yield (dl.lineno, + 'child node @%s out of address order' % + dl.node_addr) + + +def check_child_name_order(ctx): + """Unaddressed siblings must appear in natural-sort order by node + name within their parent node body. Addressed children are scoped + by check_child_address_order; reference nodes (&label { ... }) and + the root node are skipped.""" + for children in _walk_bodies(ctx.lines): + unaddressed =3D [] + for c in children: + if c.node_addr is not None: + continue + if c.node_name in (None, '/'): + continue + if c.ref_name is not None: + continue + unaddressed.append((_natural_sort_key(c.node_name), c)) + for i in range(1, len(unaddressed)): + if unaddressed[i][0] < unaddressed[i - 1][0]: + dl =3D unaddressed[i][1] + yield (dl.lineno, + 'child node %r out of name order' % dl.node_name) + + +def _property_bucket(name): + """Return the canonical bucket index for a property: + 0 compatible + 1 reg / reg-names + 2 ranges + 3 standard properties (no vendor comma in #-stripped name) + 4 vendor-specific properties + 5 status + Plus a sub-key inside the bucket for fixed slots (compatible, reg, + reg-names, ranges, status). 'standard' and 'vendor' return None for + the sub-key, signalling that the within-bucket key is computed by + the pairing rules.""" + stripped =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) + return (4 if ',' in stripped else 3, None) + + +# Declarative pairing rules: each is a callable +# (name, all_names) -> anchor_name_or_None +# If a rule returns an anchor, the property sorts immediately after the +# anchor. Rules are tried in order; the first match wins. If none +# matches, the within-bucket key falls back to natural sort by the +# #-stripped name. + +def _pair_pinctrl_names(name, all_names): + """pinctrl-names follows the highest pinctrl-N in the same node.""" + if name !=3D 'pinctrl-names': + return None + cands =3D [n for n in all_names if re.match(r'^pinctrl-\d+$', n)] + if not cands: + return None + return max(cands, key=3D_natural_sort_key) + + +def _pair_x_names(name, all_names): + """Generic -names follows its owning property. The owner is + usually plural (clocks/clock-names, dmas/dma-names, + resets/reset-names) but occasionally singular (reg/reg-names is + handled by the fixed slot above; this rule catches anything else).""" + if not name.endswith('-names'): + return None + base =3D name[:-len('-names')] + # Try plural and singular forms. + if (base + 's') in all_names: + return base + 's' + if base in all_names: + return base + return None + + +PAIRING_RULES =3D (_pair_pinctrl_names, _pair_x_names) + + +def _property_sort_key(name, all_names): + """Sort key for a property among its node-body siblings. + + Format: (bucket, within_key, tiebreak). 'within_key' for + standard/vendor buckets follows pairing rules: a property paired + with anchor X sorts as if it were X with a higher tiebreak.""" + bucket, fixed_sub =3D _property_bucket(name) + if fixed_sub is not None: + return (bucket, (), fixed_sub) + + for rule in PAIRING_RULES: + anchor =3D rule(name, all_names) + if anchor is not None: + return (bucket, _natural_sort_key(anchor.lstrip('#')), 1) + + return (bucket, _natural_sort_key(name.lstrip('#')), 0) + + +def check_property_order(ctx): + """Properties within a node body must appear in canonical order: + compatible, reg(/reg-names), ranges, then the standard group, then + the vendor-specific group, then status. Inside the standard and + vendor groups, pairing rules apply (e.g. -names follows ); + everything else falls back to natural sort by the #-stripped name.""" + lines =3D ctx.lines + for i, dl in enumerate(lines): + if dl.linetype !=3D LineType.NODE_OPEN: + continue + body_depth =3D dl.depth + 1 + props =3D [] + for j in range(i + 1, len(lines)): + d =3D lines[j] + if d.linetype =3D=3D LineType.NODE_CLOSE and \ + d.depth =3D=3D body_depth - 1: + break + if d.linetype =3D=3D LineType.PROPERTY and d.depth =3D=3D body= _depth \ + and d.prop_name is not None: + props.append(d) + if len(props) < 2: + continue + all_names =3D [p.prop_name for p in props] + keyed =3D [(p, _property_sort_key(p.prop_name, all_names)) + for p in props] + for k in range(1, len(keyed)): + if keyed[k][1] < keyed[k - 1][1]: + p =3D keyed[k][0] + prev =3D keyed[k - 1][0] + yield (p.lineno, + 'property %r out of canonical order ' + '(should sort before %r)' % + (p.prop_name, prev.prop_name)) + + +def _strip_strings_and_comments(text): + """Remove string literals and /* */ + // comments from a single + line, replacing them with empty strings. Used so syntactic checks + (whitespace, hex case, etc.) don't false-positive on contents of + quoted strings or comments.""" + text =3D re.sub(r'"(?:[^"\\]|\\.)*"', '""', text) + text =3D re.sub(r'/\*.*?\*/', '', text) + text =3D re.sub(r'//.*$', '', text) + return text + + +def check_required_blank_lines(ctx): + """A blank line must precede each child node and the 'status' + property within a node body, except when these are the first + substantive item in the body.""" + lines =3D ctx.lines + for i, open_dl in enumerate(lines): + if open_dl.linetype !=3D LineType.NODE_OPEN: + continue + body_depth =3D open_dl.depth + 1 + prev_substantive =3D None + between_blanks =3D 0 + depth_inside =3D 0 + for j in range(i + 1, len(lines)): + d =3D lines[j] + if d.linetype =3D=3D LineType.NODE_CLOSE and \ + d.depth =3D=3D body_depth - 1 and depth_inside =3D=3D = 0: + break + # Track depth inside nested children so we only look at + # immediate-body items. + if d.linetype =3D=3D LineType.NODE_OPEN and \ + d.depth >=3D body_depth and depth_inside > 0: + depth_inside +=3D 1 + continue + if d.linetype =3D=3D LineType.NODE_CLOSE and depth_inside > 0: + depth_inside -=3D 1 + continue + if depth_inside > 0: + continue + if d.linetype =3D=3D LineType.BLANK: + if prev_substantive is not None: + between_blanks +=3D 1 + continue + if d.linetype in (LineType.COMMENT, LineType.COMMENT_START, + LineType.COMMENT_BODY, LineType.COMMENT_END, + LineType.PREPROCESSOR): + continue + if d.linetype =3D=3D LineType.CONTINUATION: + continue + + needs_blank =3D False + if d.linetype =3D=3D LineType.NODE_OPEN: + needs_blank =3D True + depth_inside =3D 1 # entered the child body + elif d.linetype =3D=3D LineType.PROPERTY and d.prop_name =3D= =3D 'status': + needs_blank =3D True + + if needs_blank and prev_substantive is not None and \ + between_blanks =3D=3D 0: + if d.linetype =3D=3D LineType.NODE_OPEN: + yield (d.lineno, + 'child node must be preceded by a blank line') + else: + yield (d.lineno, + '"status" must be preceded by a blank line') + + prev_substantive =3D d + between_blanks =3D 0 + + +def check_hex_case(ctx): + """Hex literals (0xN) must use lowercase digits and prefix.""" + for dl in ctx.lines: + if dl.linetype in (LineType.BLANK, LineType.COMMENT, + LineType.COMMENT_START, LineType.COMMENT_BODY, + LineType.COMMENT_END, LineType.PREPROCESSOR): + continue + text =3D _strip_strings_and_comments(dl.raw) + for m in re.finditer(r'\b0[xX][0-9a-fA-F]+\b', text): + lit =3D m.group(0) + if any(c.isupper() for c in lit[2:]) or lit[1] =3D=3D 'X': + yield (dl.lineno, + 'hex literal %r must be lowercase' % lit) + + +def check_unit_address_format(ctx): + """Unit addresses must be lowercase hex without leading zeros. + For multi-cell addresses (comma-separated), each part is checked + independently. A single '0' is permitted (canonical zero).""" + for dl in ctx.lines: + if dl.linetype !=3D LineType.NODE_OPEN: + continue + if dl.node_addr is None: + continue + addr =3D dl.node_addr + for part in addr.split(','): + if any(c in 'ABCDEF' for c in part): + yield (dl.lineno, + 'unit address %r must be lowercase hex' % addr) + break + if len(part) > 1 and part.startswith('0'): + yield (dl.lineno, + 'unit address %r has leading zeros' % addr) + break + + +def check_value_whitespace(ctx): + """A <...> cell list must have no whitespace directly after '<' + or directly before '>'. Continuation lines are joined onto the + property so a <...> split across lines is checked too; a '<' or + '>' at a line break is glued straight to the neighbouring value, + so the break itself is not counted as padding. Outside strings + and comments only.""" + for dl in ctx.lines: + if dl.linetype !=3D LineType.PROPERTY: + continue + segs =3D [_strip_strings_and_comments(dl.raw).strip()] + for cont in dl.continuations: + segs.append(_strip_strings_and_comments(cont.stripped).strip()) + text =3D '' + for s in segs: + if not s: + continue + if not text or text.endswith('<') or s.startswith('>'): + text +=3D s + else: + text +=3D ' ' + s + for m in re.finditer(r'<([^<>]*)>', text): + content =3D m.group(1) + if content and content !=3D content.strip(): + yield (dl.lineno, 'extra whitespace inside <...>') + break + + +def check_node_close_alone(ctx): + """The closing '};' of a node must be on its own line. The + classifier already accepts only `};` or `}` as NODE_CLOSE; any + other line that still contains `};` (in code, not in strings or + comments) is mixing a node close with something else.""" + for dl in ctx.lines: + if dl.linetype in (LineType.BLANK, LineType.COMMENT, + LineType.COMMENT_START, LineType.COMMENT_BODY, + LineType.COMMENT_END, LineType.PREPROCESSOR, + LineType.NODE_CLOSE): + continue + text =3D _strip_strings_and_comments(dl.raw) + if '};' in text: + yield (dl.lineno, + 'closing brace must be on its own line') + + +def _display_col(text): + """Visual column width of text, with tabs expanded to the next + 8-column stop, matching how printf and most editors render a + line and the kernel-wide line length convention.""" + col =3D 0 + for ch in text: + if ch =3D=3D '\t': + col =3D (col // 8 + 1) * 8 + else: + col +=3D 1 + return col + + +def check_line_length(ctx): + """Lines must not exceed 80 columns; tabs count as 8 (see + _display_col).""" + for dl in ctx.lines: + if dl.linetype =3D=3D LineType.BLANK: + continue + cols =3D _display_col(dl.raw) + if cols > 80: + yield (dl.lineno, + 'line exceeds 80 columns (%d)' % cols) + + +def check_continuation_alignment(ctx): + """A multi-line property's continuation lines must align their + first non-whitespace character to the display column of the first + '<' or '"' after the '=3D' in the leading line. Display columns are + used so tab-indented .dts files (where a continuation aligns with + tabs plus spaces) are compared correctly.""" + for dl in ctx.lines: + if dl.linetype !=3D LineType.PROPERTY: + continue + if not dl.continuations: + continue + eq =3D dl.raw.find('=3D') + if eq < 0: + continue + # First '<' or '"' after '=3D' + rest =3D dl.raw[eq + 1:] + m =3D re.search(r'[<"]', rest) + if not m: + continue + target_col =3D _display_col(dl.raw[:eq + 1 + m.start()]) + for cont in dl.continuations: + if _display_col(cont.indent_str) !=3D target_col: + yield (cont.lineno, + 'continuation should align to column %d ' + '(under "<" or \\")' % (target_col + 1)) + + +def check_unused_labels(ctx): + """Labels defined but never referenced are clutter.""" + defined, referenced =3D collect_labels_and_refs(ctx.text) + for label in sorted(defined - referenced): + # Find the line where this label is defined for line-number + # reporting. + m =3D re.search(r'(?m)^.*\b' + re.escape(label) + r'\s*:', ctx.tex= t) + lineno =3D ctx.text[:m.start()].count('\n') + 1 if m else 1 + yield (lineno, 'label %r defined but never &-referenced' % label) + + +# --- registry -----------------------------------------------------------= --- + +RULES =3D [ + # 'relaxed' is the default; rules in this group must produce zero + # output on a clean kernel tree (post the small prep-cleanup + # commit at the head of this series). + Rule('trailing-whitespace', 'relaxed', + 'no trailing whitespace on any line', + check_trailing_whitespace), + Rule('tab-in-dts', 'relaxed', + 'YAML examples may not contain tab characters', + check_tab_in_dts, applies_to=3D('yaml',)), + Rule('mixed-indent-chars', 'relaxed', + 'indent must not mix tabs and spaces', + check_mixed_indent_chars), + + # DTS files always use tabs; this is not negotiable per kernel + # coding style (.dts files are real source). Relaxed mode. + Rule('indent-unit-dts', 'relaxed', + 'DTS files: 1 tab per nesting level', + check_indent_unit_dts, + applies_to=3D('dts', 'dtsi', 'dtso')), + + # 'strict' rules are opt-in (e.g. for new submissions via + # checkpatch.pl in a follow-up series). They flag many existing + # files and can be promoted to relaxed once those are cleaned up. + Rule('indent-unit', 'strict', + 'YAML: 2 or 4 spaces per level', + check_indent_unit_relaxed, applies_to=3D('yaml',)), + Rule('indent-unit-strict', 'strict', + 'YAML: must be 4 spaces per level', + check_indent_unit_strict, applies_to=3D('yaml',)), + Rule('indent-consistent', 'strict', + 'every line indented at depth * unit', + check_indent_consistent), + Rule('blank-lines', 'strict', + 'no consecutive blanks; no blanks at node body edges', + check_blank_lines), + Rule('child-address-order', 'strict', + 'addressed siblings must be in ascending address order', + check_child_address_order), + Rule('child-name-order', 'strict', + 'unaddressed siblings must be in natural-sort name order', + check_child_name_order), + Rule('property-order', 'strict', + 'canonical bucket + pairing + natural-sort order of properties', + check_property_order), + Rule('required-blank-lines', 'strict', + 'blank line before child nodes and before "status"', + check_required_blank_lines), + Rule('hex-case', 'strict', + 'hex literals must be lowercase', + check_hex_case), + Rule('unit-address-format', 'strict', + 'unit addresses must be lowercase hex without leading zeros', + check_unit_address_format), + Rule('value-whitespace', 'strict', + 'no whitespace directly inside <...> brackets', + check_value_whitespace), + Rule('node-close-alone', 'strict', + 'closing brace must be on its own line', + check_node_close_alone), + Rule('line-length', 'strict', + 'lines must not exceed 80 columns', + check_line_length), + Rule('continuation-alignment', 'strict', + 'multi-line property continuations align under "<" or "\\""', + check_continuation_alignment), + Rule('unused-labels', 'strict', + 'every label must be &-referenced in the same example/file ' + '(skipped for .dtsi/.dtso since labels there are exported)', + check_unused_labels, applies_to=3D('yaml', 'dts')), +] + + +def select_rules(mode, input_kind): + """Return rules that apply to the given mode and input type.""" + rank =3D {'relaxed': 0, 'strict': 1} + out =3D [] + for r in RULES: + if rank[r.mode] > rank[mode]: + continue + if input_kind not in r.applies_to: + continue + out.append(r) + return out + + +# ------------------------------------------------------------------------= --- +# Block runner +# ------------------------------------------------------------------------= --- + +def check_block(text, mode, indent_kind, input_type): + """Run all selected rules on a single block of DTS text. Returns a + list of (lineno, rule_name, message) tuples.""" + lines =3D classify_lines(text) + ctx =3D Ctx(lines, text, mode, indent_kind) + rules =3D select_rules(mode, input_type) + findings =3D [] + for r in rules: + for lineno, msg in r.check(ctx): + findings.append((lineno, r.name, msg)) + findings.sort(key=3Dlambda t: (t[0], t[1])) + return findings + + +# ------------------------------------------------------------------------= --- +# Input drivers (YAML examples vs raw DTS) +# ------------------------------------------------------------------------= --- + +def _yaml_loader(): + return ruamel.yaml.YAML() + + +def iter_yaml_examples(filepath): + """Yield (example_text, base_lineno_in_file, example_index) tuples.""" + yaml =3D _yaml_loader() + try: + with open(filepath, encoding=3D'utf-8') as f: + data =3D yaml.load(f) + except Exception as e: + print('%s: error loading YAML: %s' % (filepath, e), + file=3Dsys.stderr) + return + if not isinstance(data, dict) or 'examples' not in data: + return + examples =3D data['examples'] + if not hasattr(examples, '__iter__'): + return + for i, ex in enumerate(examples): + if not isinstance(ex, str): + continue + try: + base =3D examples.lc.item(i)[0] + 2 + except Exception: + base =3D 1 + yield (str(ex), base, i) + + +def iter_dts_file(filepath): + """Treat the whole file as a single block.""" + try: + with open(filepath, encoding=3D'utf-8') as f: + text =3D f.read() + except Exception as e: + print('%s: error reading: %s' % (filepath, e), file=3Dsys.stderr) + return + yield (text, 1, None) + + +# ------------------------------------------------------------------------= --- +# Top-level processing +# ------------------------------------------------------------------------= --- + +def input_kind(filepath): + p =3D filepath.lower() + if p.endswith('.yaml') or p.endswith('.yml'): + return 'yaml' + if p.endswith('.dts'): + return 'dts' + if p.endswith('.dtsi'): + return 'dtsi' + if p.endswith('.dtso'): + return 'dtso' + return None + + +# All input types that use tab indentation and follow DTS coding style. +DTS_FAMILY =3D ('dts', 'dtsi', 'dtso') + + +def collect_findings(filepath, mode): + """Return a (lines, count) pair for filepath. lines is a list of + formatted output strings; count is the number of findings.""" + kind =3D input_kind(filepath) + if kind =3D=3D 'yaml': + indent_kind =3D 'spaces' + iterator =3D iter_yaml_examples(filepath) + elif kind in DTS_FAMILY: + indent_kind =3D 'tab' + iterator =3D iter_dts_file(filepath) + else: + return (['%s: unknown file type, skipping' % filepath], 0) + + out =3D [] + for text, base, idx in iterator: + for lineno, rule, msg in check_block(text, mode, indent_kind, kind= ): + abs_line =3D base + lineno - 1 + ex_tag =3D '' if idx is None else ' example %d' % idx + out.append('%s:%d:%s [%s] %s' % + (filepath, abs_line, ex_tag, rule, msg)) + return (out, len(out)) + + +# Worker entry point for ProcessPoolExecutor.map(). Top-level so it is +# picklable on every platform. +def _worker(args): + filepath, mode =3D args + return collect_findings(filepath, mode) + + +def main(): + import os + ap =3D argparse.ArgumentParser( + description=3D'Check DTS coding style on YAML examples and ' + '.dts/.dtsi/.dtso files.', + fromfile_prefix_chars=3D'@') + ap.add_argument('--mode', choices=3D('relaxed', 'strict'), + default=3D'relaxed', + help=3D'which rule set to apply (default: relaxed)') + ap.add_argument('-j', '--jobs', type=3Dint, default=3D0, + metavar=3D'N', + help=3D'run N workers in parallel (default: respect ' + 'the make jobserver via $PARALLELISM, otherwise ' + 'os.cpu_count(); use 1 to disable multiprocessing)') + ap.add_argument('--list-rules', action=3D'store_true', + help=3D'print all rules with their mode and exit') + ap.add_argument('files', nargs=3D'*', metavar=3D'file', + help=3D'YAML binding files or .dts/.dtsi/.dtso files; ' + 'use @argfile to read paths from a file') + args =3D ap.parse_args() + + if args.list_rules: + for r in RULES: + applies =3D ','.join(r.applies_to) + print('%-22s %-7s [%s] %s' % + (r.name, r.mode, applies, r.description)) + return 0 + + if not args.files: + ap.error('no input files') + + if args.jobs > 0: + jobs =3D args.jobs + else: + # When invoked under scripts/jobserver-exec, $PARALLELISM + # holds the slot count make has reserved for us; this lets + # `make -j N dt_binding_check` constrain our worker pool to N. + try: + jobs =3D int(os.environ['PARALLELISM']) + except (KeyError, ValueError): + jobs =3D os.cpu_count() or 1 + # Single-process path: keep import surface small for tests and + # easy debugging. + if jobs =3D=3D 1 or len(args.files) =3D=3D 1: + total =3D 0 + for f in args.files: + lines, n =3D collect_findings(f, args.mode) + for line in lines: + print(line, file=3Dsys.stderr) + total +=3D n + return 1 if total else 0 + + # Multi-process path. ex.map preserves input order so output is + # deterministic across runs. + from concurrent.futures import ProcessPoolExecutor + total =3D 0 + work =3D [(f, args.mode) for f in args.files] + chunk =3D max(1, len(work) // (jobs * 8)) if work else 1 + with ProcessPoolExecutor(max_workers=3Djobs) as ex: + for lines, n in ex.map(_worker, work, chunksize=3Dchunk): + for line in lines: + print(line, file=3Dsys.stderr) + total +=3D n + return 1 if total else 0 + + +if __name__ =3D=3D '__main__': + sys.exit(main()) --=20 2.54.0 From nobody Sun May 24 20:33:09 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 1D355367F26; Fri, 22 May 2026 18:04:58 +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=1779473102; cv=none; b=M0xjsCVUybjEO/x+CqFOfdg0pOrEsgZ+Anii7B2HXQ8NgrAUJzK4NtzoJ4eU43/0cf5OXHfBLzGviMN9oIjf4L1GEhIAVQaYwQvQ8LGXgwo8aXFzyU7u2ExlyFYY940vzPmzO28rSgX7xC+ajaTMHN8BR6YoahmogBGrb8h/bE4= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1779473102; c=relaxed/simple; bh=VdzibMjS69RcKL42m6TG47UmRNAwDIGZjTq8j4b6w38=; h=Date:From:To:Subject:Message-ID:References:MIME-Version: Content-Type:Content-Disposition:In-Reply-To; b=K9FZwBJW3ZpvJSzI+Njmwuc2WwjmOCAg431lY6eCJY5PejrPnvNT5qD/iPKptjWHoTAvIozVIUz6V3PIoiwnaoD4qKgh+0Ejwkx6/guFPUYfMhGEHVvBbbn/021IrdzhQvyTHvMnq80J59xNsFKDO8MirLTN++nVku+BUTXNECg= 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 1wQUEu-000000006y0-1kS3; Fri, 22 May 2026 18:04:52 +0000 Date: Fri, 22 May 2026 19:04:49 +0100 From: Daniel Golle To: Rob Herring , Krzysztof Kozlowski , Conor Dooley , Nathan Chancellor , Nicolas Schier , Saravana Kannan , Daniel Golle , Miguel Ojeda , Gary Guo , Tamir Duberstein , Thomas =?iso-8859-1?Q?Wei=DFschuh?= , Steven Rostedt , Masahiro Yamada , Aleksander Jan Bajkowski , Guenter Roeck , Test User , devicetree@vger.kernel.org, linux-kernel@vger.kernel.org, linux-kbuild@vger.kernel.org Subject: [PATCH v4 2/3] dt-bindings: wire style checker into dt_binding_check Message-ID: <93923232b32423d33dc3b6632165ccc9571dea8f.1779472837.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-style as part of dt_binding_check_one. The recipe wraps the tool with scripts/jobserver-exec so worker count follows the GNU make jobserver -- `make -j N dt_binding_check` constrains the checker to N workers rather than spawning one per CPU. Default mode (relaxed) is zero-violation on the current tree, so this does not introduce new warnings into make dt_binding_check. Stricter rules are available via --mode=3Dstrict (eg. for use by checkpatch.pl in a future series). Signed-off-by: Daniel Golle --- Changes since v3: - build the @argfile with f=3D$(mktemp) and remove it with rm -f (matching cmd_mk_schema), instead of Kbuild's $(tmp-target) which leaves a stale .tmp_.dt-style.checked in the build tree Changes since v2: - use Kbuild's $(tmp-target) instead of mktemp so build output stays inside the build folder (Nathan) - collapse the conditional cleanup tail into the familiar "&& touch $@ || true" pattern, matching cmd_chk_bindings; keeps future warnings non-fatal (Rob, Nathan) - retained the explicit $(PYTHON3) prefix (Rob asked why it differs from the rest of this Makefile): per Documentation/kbuild/makefiles.rst "Script invocation", in-tree scripts should be called through their interpreter so the executable bit and shebang are not relied on and the user's $(PYTHON3) override is respected. The neighbouring recipes invoke their Python helpers directly because those come from external packages (dtschema's dt-extract-*, dt-check-compatible, dt-doc-validate), which is the case Rob asked about and which sits outside that rule. Changes since v1: - dropped xargs -n200 -P$(nproc) sharding; single Python invocation with file list via @argfile - dropped `|| true`: relaxed mode is zero-output today - wrapped under scripts/jobserver-exec so worker count follows the make jobserver --- Documentation/devicetree/bindings/Makefile | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/Documentation/devicetree/bindings/Makefile b/Documentation/dev= icetree/bindings/Makefile index 7b668f7fd400..00149e824261 100644 --- a/Documentation/devicetree/bindings/Makefile +++ b/Documentation/devicetree/bindings/Makefile @@ -46,6 +46,18 @@ quiet_cmd_chk_bindings =3D CHKDT $(src) xargs -n200 -P$$(nproc) $(DT_DOC_CHECKER) -u $(src)) \ && touch $@ || true =20 +DT_CHK_STYLE =3D $(srctree)/scripts/dtc/dt-check-style + +# Feed the file list to the checker via @argfile in a single Python +# process so the ruamel.yaml import is paid once. scripts/jobserver-exec +# claims slots from the GNU make jobserver and exposes the count via +# $PARALLELISM, which dt-check-style picks up to size its worker pool. +quiet_cmd_chk_style =3D STYLE $(src) + cmd_chk_style =3D f=3D$$(mktemp) && $(find_cmd) > $$f && \ + $(PYTHON3) $(srctree)/scripts/jobserver-exec \ + $(PYTHON3) $(DT_CHK_STYLE) @$$f \ + && touch $@ || true; rm -f $$f + quiet_cmd_mk_schema =3D SCHEMA $@ cmd_mk_schema =3D f=3D$$(mktemp) ; \ $(find_all_cmd) > $$f ; \ @@ -62,13 +74,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-style.checked $(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-style.checked: $(DT_DOCS) FORCE + $(call if_changed,chk_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 +97,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-style.checked =20 PHONY +=3D dt_binding_check dt_binding_check: dt_binding_check_one $(CHK_DT_EXAMPLES) --=20 2.54.0 From nobody Sun May 24 20:33:09 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 5C4292F28E3; Fri, 22 May 2026 18:05:04 +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=1779473109; cv=none; b=tev9QPi/skr1GrQWQVqProgzyb+UJbEXT7d686d6xxj8UZTYjXl3aaNIDaEi1kqftKXlK7BokbzEnal4FMD3ZpfxaD0LTIvnI1KiE3JeowSc4V9emgDurpfWdiNUn460PmCPBsseDIPvPBku7jdq5Vvn0Ku5S7qOnASdYCSJFxk= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1779473109; c=relaxed/simple; bh=roLsyvh7Au9NzD7SCyrcMjGyzOECIDRc99xmI4q7UZA=; h=Date:From:To:Subject:Message-ID:References:MIME-Version: Content-Type:Content-Disposition:In-Reply-To; b=kbf4Au4M8bjkukD7mndp3jIcmWDCPSxou2rb4SM3Ariayjuylx0u802BzASG85HQu5tnIx/Mou79h9w+YvVjYHG0J/hFRE2bhrHTFzQjsDcgZlQmsGRqPqCgfvOyMxpFGIIQ07Bz1YOT01QQKpl2+Svz/Zr2bsWPpnGVEgbayf0= 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 1wQUF1-000000006yX-09A1; Fri, 22 May 2026 18:04:59 +0000 Date: Fri, 22 May 2026 19:04:56 +0100 From: Daniel Golle To: Rob Herring , Krzysztof Kozlowski , Conor Dooley , Nathan Chancellor , Nicolas Schier , Saravana Kannan , Daniel Golle , Miguel Ojeda , Gary Guo , Tamir Duberstein , Thomas =?iso-8859-1?Q?Wei=DFschuh?= , Steven Rostedt , Masahiro Yamada , Aleksander Jan Bajkowski , Guenter Roeck , Test User , devicetree@vger.kernel.org, linux-kernel@vger.kernel.org, linux-kbuild@vger.kernel.org Subject: [PATCH v4 3/3] dt-bindings: add self-test fixtures for style checker Message-ID: <732bbcc6ec056cb1cf83f4f6cdc45c0c84cc1630.1779472837.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" Provide good/ and bad/ DTS and YAML fixtures plus a small runner that feeds them to dt-check-style and diffs the output against expected text files. Wired into a new top-level dt_style_selftest make target so the suite can be exercised independently of the full tree. Signed-off-by: Daniel Golle --- Changes since v3: - run.sh: replace the bash-only "<()" process substitution with a POSIX temp-file plus stdin diff, so the runner works when /bin/sh is dash - run.sh: check dt-check-style's exit status -- good/ fixtures must exit 0, bad/ fixtures must exit 1 -- instead of discarding it with "|| true" - add fixtures for the checker fixes in this revision: good/dts-cont-align.dts (tab-and-space aligned multi-line property), bad/yaml-digit-node-order.yaml (digit-leading node names), bad/yaml-trailing-comment.yaml (trailing // and /* */ comments) and bad/yaml-value-ws-multiline.yaml (multi-line cell array) - update bad/yaml-value-ws.yaml (and its expected output) to exercise the reworked value-whitespace rule Changes since v2: - append a trailing newline to every expected/*.txt fixture (Rob) - restore the trailing whitespace inside yaml-trailing-ws.yaml that had been silently stripped during re-application, so the selftest actually exercises the trailing-whitespace rule Changes since v1: - new patch (Krzysztof: "would be happy to see at least a few test cases for it") --- Makefile | 6 ++ .../dtc/dt-style-selftest/bad/dts-spaces.dts | 13 ++++ .../bad/yaml-child-addr-order.yaml | 41 +++++++++++ .../bad/yaml-child-name-order.yaml | 37 ++++++++++ .../bad/yaml-cont-align.yaml | 30 ++++++++ .../bad/yaml-digit-node-order.yaml | 37 ++++++++++ .../dt-style-selftest/bad/yaml-hex-case.yaml | 29 ++++++++ .../bad/yaml-indent-strict.yaml | 29 ++++++++ .../bad/yaml-line-length.yaml | 29 ++++++++ .../bad/yaml-mixed-indent.yaml | 29 ++++++++ .../bad/yaml-node-close.yaml | 31 ++++++++ .../bad/yaml-prop-order.yaml | 29 ++++++++ .../bad/yaml-prop-pairing.yaml | 33 +++++++++ .../bad/yaml-required-blank.yaml | 33 +++++++++ .../dtc/dt-style-selftest/bad/yaml-tab.yaml | 29 ++++++++ .../bad/yaml-trailing-comment.yaml | 26 +++++++ .../bad/yaml-trailing-ws.yaml | 29 ++++++++ .../dt-style-selftest/bad/yaml-unit-addr.yaml | 29 ++++++++ .../bad/yaml-unused-label.yaml | 29 ++++++++ .../bad/yaml-value-ws-multiline.yaml | 27 +++++++ .../dt-style-selftest/bad/yaml-value-ws.yaml | 29 ++++++++ .../expected/dts-spaces.dts.txt | 2 + .../expected/yaml-child-addr-order.yaml.txt | 2 + .../expected/yaml-child-name-order.yaml.txt | 2 + .../expected/yaml-cont-align.yaml.txt | 2 + .../expected/yaml-digit-node-order.yaml.txt | 2 + .../expected/yaml-hex-case.yaml.txt | 2 + .../expected/yaml-indent-strict.yaml.txt | 2 + .../expected/yaml-line-length.yaml.txt | 2 + .../expected/yaml-mixed-indent.yaml.txt | 3 + .../expected/yaml-node-close.yaml.txt | 2 + .../expected/yaml-prop-order.yaml.txt | 2 + .../expected/yaml-prop-pairing.yaml.txt | 3 + .../expected/yaml-required-blank.yaml.txt | 3 + .../expected/yaml-tab.yaml.txt | 2 + .../expected/yaml-trailing-comment.yaml.txt | 2 + .../expected/yaml-trailing-ws.yaml.txt | 2 + .../expected/yaml-unit-addr.yaml.txt | 2 + .../expected/yaml-unused-label.yaml.txt | 2 + .../expected/yaml-value-ws-multiline.yaml.txt | 2 + .../expected/yaml-value-ws.yaml.txt | 2 + .../dt-style-selftest/good/dts-cont-align.dts | 27 +++++++ .../dtc/dt-style-selftest/good/dts-tab.dts | 30 ++++++++ .../dt-style-selftest/good/yaml-4space.yaml | 41 +++++++++++ scripts/dtc/dt-style-selftest/run.sh | 71 +++++++++++++++++++ 45 files changed, 816 insertions(+) create mode 100644 scripts/dtc/dt-style-selftest/bad/dts-spaces.dts create mode 100644 scripts/dtc/dt-style-selftest/bad/yaml-child-addr-order= .yaml create mode 100644 scripts/dtc/dt-style-selftest/bad/yaml-child-name-order= .yaml create mode 100644 scripts/dtc/dt-style-selftest/bad/yaml-cont-align.yaml create mode 100644 scripts/dtc/dt-style-selftest/bad/yaml-digit-node-order= .yaml create mode 100644 scripts/dtc/dt-style-selftest/bad/yaml-hex-case.yaml create mode 100644 scripts/dtc/dt-style-selftest/bad/yaml-indent-strict.ya= ml create mode 100644 scripts/dtc/dt-style-selftest/bad/yaml-line-length.yaml create mode 100644 scripts/dtc/dt-style-selftest/bad/yaml-mixed-indent.yaml create mode 100644 scripts/dtc/dt-style-selftest/bad/yaml-node-close.yaml create mode 100644 scripts/dtc/dt-style-selftest/bad/yaml-prop-order.yaml create mode 100644 scripts/dtc/dt-style-selftest/bad/yaml-prop-pairing.yaml create mode 100644 scripts/dtc/dt-style-selftest/bad/yaml-required-blank.y= aml create mode 100644 scripts/dtc/dt-style-selftest/bad/yaml-tab.yaml create mode 100644 scripts/dtc/dt-style-selftest/bad/yaml-trailing-comment= .yaml create mode 100644 scripts/dtc/dt-style-selftest/bad/yaml-trailing-ws.yaml create mode 100644 scripts/dtc/dt-style-selftest/bad/yaml-unit-addr.yaml create mode 100644 scripts/dtc/dt-style-selftest/bad/yaml-unused-label.yaml create mode 100644 scripts/dtc/dt-style-selftest/bad/yaml-value-ws-multili= ne.yaml create mode 100644 scripts/dtc/dt-style-selftest/bad/yaml-value-ws.yaml create mode 100644 scripts/dtc/dt-style-selftest/expected/dts-spaces.dts.t= xt create mode 100644 scripts/dtc/dt-style-selftest/expected/yaml-child-addr-= order.yaml.txt create mode 100644 scripts/dtc/dt-style-selftest/expected/yaml-child-name-= order.yaml.txt create mode 100644 scripts/dtc/dt-style-selftest/expected/yaml-cont-align.= yaml.txt create mode 100644 scripts/dtc/dt-style-selftest/expected/yaml-digit-node-= order.yaml.txt create mode 100644 scripts/dtc/dt-style-selftest/expected/yaml-hex-case.ya= ml.txt create mode 100644 scripts/dtc/dt-style-selftest/expected/yaml-indent-stri= ct.yaml.txt create mode 100644 scripts/dtc/dt-style-selftest/expected/yaml-line-length= .yaml.txt create mode 100644 scripts/dtc/dt-style-selftest/expected/yaml-mixed-inden= t.yaml.txt create mode 100644 scripts/dtc/dt-style-selftest/expected/yaml-node-close.= yaml.txt create mode 100644 scripts/dtc/dt-style-selftest/expected/yaml-prop-order.= yaml.txt create mode 100644 scripts/dtc/dt-style-selftest/expected/yaml-prop-pairin= g.yaml.txt create mode 100644 scripts/dtc/dt-style-selftest/expected/yaml-required-bl= ank.yaml.txt create mode 100644 scripts/dtc/dt-style-selftest/expected/yaml-tab.yaml.txt create mode 100644 scripts/dtc/dt-style-selftest/expected/yaml-trailing-co= mment.yaml.txt create mode 100644 scripts/dtc/dt-style-selftest/expected/yaml-trailing-ws= .yaml.txt create mode 100644 scripts/dtc/dt-style-selftest/expected/yaml-unit-addr.y= aml.txt create mode 100644 scripts/dtc/dt-style-selftest/expected/yaml-unused-labe= l.yaml.txt create mode 100644 scripts/dtc/dt-style-selftest/expected/yaml-value-ws-mu= ltiline.yaml.txt create mode 100644 scripts/dtc/dt-style-selftest/expected/yaml-value-ws.ya= ml.txt create mode 100644 scripts/dtc/dt-style-selftest/good/dts-cont-align.dts create mode 100644 scripts/dtc/dt-style-selftest/good/dts-tab.dts create mode 100644 scripts/dtc/dt-style-selftest/good/yaml-4space.yaml create mode 100755 scripts/dtc/dt-style-selftest/run.sh diff --git a/Makefile b/Makefile index b7b80e84e1eb..2436ed72ca83 100644 --- a/Makefile +++ b/Makefile @@ -295,6 +295,7 @@ no-dot-config-targets :=3D $(clean-targets) \ cscope gtags TAGS tags help% %docs check% coccicheck \ $(version_h) headers headers_% archheaders archscripts \ %asm-generic kernelversion %src-pkg dt_binding_check \ + dt_style_selftest \ outputmakefile rustavailable rustfmt rustfmtcheck \ run-command no-sync-config-targets :=3D $(no-dot-config-targets) %install modules_sign= kernelrelease \ @@ -1645,6 +1646,10 @@ PHONY +=3D dt_compatible_check dt_compatible_check: dt_binding_schemas $(Q)$(MAKE) $(build)=3D$(dtbindingtree) $@ =20 +PHONY +=3D dt_style_selftest +dt_style_selftest: + $(Q)$(srctree)/scripts/dtc/dt-style-selftest/run.sh + # ------------------------------------------------------------------------= --- # Modules =20 @@ -1847,6 +1852,7 @@ help: echo ' dtbs_install - Install dtbs to $(INSTALL_DTBS_PATH)'; \ echo ' dt_binding_check - Validate device tree binding documents and = examples'; \ echo ' dt_binding_schemas - Build processed device tree binding schemas= '; \ + echo ' dt_style_selftest - Run dt-check-style fixture tests'; \ echo ' dtbs_check - Validate device tree source files';\ echo '') =20 diff --git a/scripts/dtc/dt-style-selftest/bad/dts-spaces.dts b/scripts/dtc= /dt-style-selftest/bad/dts-spaces.dts new file mode 100644 index 000000000000..905a91824a50 --- /dev/null +++ b/scripts/dtc/dt-style-selftest/bad/dts-spaces.dts @@ -0,0 +1,13 @@ +/* + * SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause + * + * Test fixture: a .dts using space indent (must use tabs). + */ + +/dts-v1/; + +/ { + compatible =3D "example,test-board"; + #address-cells =3D <1>; + #size-cells =3D <1>; +}; diff --git a/scripts/dtc/dt-style-selftest/bad/yaml-child-addr-order.yaml b= /scripts/dtc/dt-style-selftest/bad/yaml-child-addr-order.yaml new file mode 100644 index 000000000000..3df56e69a1ff --- /dev/null +++ b/scripts/dtc/dt-style-selftest/bad/yaml-child-addr-order.yaml @@ -0,0 +1,41 @@ +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/test-bad-child-order.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Test fixture with addressed children out of order + +maintainers: + - Test User + +properties: + compatible: + const: example,test-child-order + reg: + maxItems: 1 + +required: + - compatible + - reg + +additionalProperties: false + +examples: + - | + bus@10000 { + compatible =3D "simple-bus"; + reg =3D <0x10000 0x1000>; + #address-cells =3D <1>; + #size-cells =3D <1>; + + device@200 { + compatible =3D "example,test-child-order"; + reg =3D <0x200 0x10>; + }; + + device@100 { + compatible =3D "example,test-child-order"; + reg =3D <0x100 0x10>; + }; + }; diff --git a/scripts/dtc/dt-style-selftest/bad/yaml-child-name-order.yaml b= /scripts/dtc/dt-style-selftest/bad/yaml-child-name-order.yaml new file mode 100644 index 000000000000..35d85e5573c2 --- /dev/null +++ b/scripts/dtc/dt-style-selftest/bad/yaml-child-name-order.yaml @@ -0,0 +1,37 @@ +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/test-bad-child-name-order.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Test fixture with unaddressed children out of name order + +maintainers: + - Test User + +properties: + compatible: + const: example,test-child-name-order + reg: + maxItems: 1 + +required: + - compatible + - reg + +additionalProperties: false + +examples: + - | + bus@10000 { + compatible =3D "simple-bus"; + reg =3D <0x10000 0x1000>; + + foo { + label =3D "foo"; + }; + + bar { + label =3D "bar"; + }; + }; diff --git a/scripts/dtc/dt-style-selftest/bad/yaml-cont-align.yaml b/scrip= ts/dtc/dt-style-selftest/bad/yaml-cont-align.yaml new file mode 100644 index 000000000000..92778540b056 --- /dev/null +++ b/scripts/dtc/dt-style-selftest/bad/yaml-cont-align.yaml @@ -0,0 +1,30 @@ +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/test-bad-cont-align.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Test fixture with mis-aligned multi-line property + +maintainers: + - Test User + +properties: + compatible: + const: example,test-cont-align + reg: + maxItems: 2 + +required: + - compatible + - reg + +additionalProperties: false + +examples: + - | + foo@1000 { + compatible =3D "example,test-cont-align"; + reg =3D <0x1000 0x100>, + <0x2000 0x100>; + }; diff --git a/scripts/dtc/dt-style-selftest/bad/yaml-digit-node-order.yaml b= /scripts/dtc/dt-style-selftest/bad/yaml-digit-node-order.yaml new file mode 100644 index 000000000000..44a9d25e5ba0 --- /dev/null +++ b/scripts/dtc/dt-style-selftest/bad/yaml-digit-node-order.yaml @@ -0,0 +1,37 @@ +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/test-bad-digit-node-order.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Test fixture with digit-leading nodes out of address order + +maintainers: + - Test User + +properties: + compatible: + const: example,test-digit-node-order + +required: + - compatible + +additionalProperties: false + +examples: + - | + bus@0 { + compatible =3D "simple-bus"; + #address-cells =3D <1>; + #size-cells =3D <1>; + + 3d-engine@20 { + compatible =3D "example,3d-engine"; + reg =3D <0x20 0x4>; + }; + + 1wire@10 { + compatible =3D "example,1wire"; + reg =3D <0x10 0x4>; + }; + }; diff --git a/scripts/dtc/dt-style-selftest/bad/yaml-hex-case.yaml b/scripts= /dtc/dt-style-selftest/bad/yaml-hex-case.yaml new file mode 100644 index 000000000000..b26d1bf58de9 --- /dev/null +++ b/scripts/dtc/dt-style-selftest/bad/yaml-hex-case.yaml @@ -0,0 +1,29 @@ +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/test-bad-hex-case.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Test fixture with uppercase hex literals + +maintainers: + - Test User + +properties: + compatible: + const: example,test-hex-case + reg: + maxItems: 1 + +required: + - compatible + - reg + +additionalProperties: false + +examples: + - | + foo@1000 { + compatible =3D "example,test-hex-case"; + reg =3D <0xABCD 0x100>; + }; diff --git a/scripts/dtc/dt-style-selftest/bad/yaml-indent-strict.yaml b/sc= ripts/dtc/dt-style-selftest/bad/yaml-indent-strict.yaml new file mode 100644 index 000000000000..bee4cf118d73 --- /dev/null +++ b/scripts/dtc/dt-style-selftest/bad/yaml-indent-strict.yaml @@ -0,0 +1,29 @@ +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/test-bad-indent-strict.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Test fixture using 2-space indent (rejected by strict mode) + +maintainers: + - Test User + +properties: + compatible: + const: example,test-indent-strict + reg: + maxItems: 1 + +required: + - compatible + - reg + +additionalProperties: false + +examples: + - | + device@1000 { + compatible =3D "example,test-indent-strict"; + reg =3D <0x1000 0x100>; + }; diff --git a/scripts/dtc/dt-style-selftest/bad/yaml-line-length.yaml b/scri= pts/dtc/dt-style-selftest/bad/yaml-line-length.yaml new file mode 100644 index 000000000000..64427bf1c385 --- /dev/null +++ b/scripts/dtc/dt-style-selftest/bad/yaml-line-length.yaml @@ -0,0 +1,29 @@ +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/test-bad-line-length.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Test fixture exceeding 80 columns + +maintainers: + - Test User + +properties: + compatible: + const: example,test-line-length + reg: + maxItems: 1 + +required: + - compatible + - reg + +additionalProperties: false + +examples: + - | + foo@1000 { + compatible =3D "example,test-line-length-this-is-a-very-long-name-= indeed-yeah"; + reg =3D <0x1000 0x100>; + }; diff --git a/scripts/dtc/dt-style-selftest/bad/yaml-mixed-indent.yaml b/scr= ipts/dtc/dt-style-selftest/bad/yaml-mixed-indent.yaml new file mode 100644 index 000000000000..5401d1a423a1 --- /dev/null +++ b/scripts/dtc/dt-style-selftest/bad/yaml-mixed-indent.yaml @@ -0,0 +1,29 @@ +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/test-bad-mixed-indent.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Test fixture mixing tabs and spaces in indent + +maintainers: + - Test User + +properties: + compatible: + const: example,test-mixed + reg: + maxItems: 1 + +required: + - compatible + - reg + +additionalProperties: false + +examples: + - | + device@1000 { + compatible =3D "example,test-mixed"; + reg =3D <0x1000 0x100>; + }; diff --git a/scripts/dtc/dt-style-selftest/bad/yaml-node-close.yaml b/scrip= ts/dtc/dt-style-selftest/bad/yaml-node-close.yaml new file mode 100644 index 000000000000..e107659fd9e8 --- /dev/null +++ b/scripts/dtc/dt-style-selftest/bad/yaml-node-close.yaml @@ -0,0 +1,31 @@ +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/test-bad-node-close.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Test fixture with closing brace not on its own line + +maintainers: + - Test User + +properties: + compatible: + const: example,test-node-close + reg: + maxItems: 1 + +required: + - compatible + - reg + +additionalProperties: false + +examples: + - | + bus@10000 { + compatible =3D "simple-bus"; + reg =3D <0x10000 0x1000>; + + empty {}; + }; diff --git a/scripts/dtc/dt-style-selftest/bad/yaml-prop-order.yaml b/scrip= ts/dtc/dt-style-selftest/bad/yaml-prop-order.yaml new file mode 100644 index 000000000000..75582a3d2f6e --- /dev/null +++ b/scripts/dtc/dt-style-selftest/bad/yaml-prop-order.yaml @@ -0,0 +1,29 @@ +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/test-bad-prop-order.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Test fixture with reg before compatible + +maintainers: + - Test User + +properties: + compatible: + const: example,test-prop-order + reg: + maxItems: 1 + +required: + - compatible + - reg + +additionalProperties: false + +examples: + - | + device@1000 { + reg =3D <0x1000 0x100>; + compatible =3D "example,test-prop-order"; + }; diff --git a/scripts/dtc/dt-style-selftest/bad/yaml-prop-pairing.yaml b/scr= ipts/dtc/dt-style-selftest/bad/yaml-prop-pairing.yaml new file mode 100644 index 000000000000..767ab21c39f3 --- /dev/null +++ b/scripts/dtc/dt-style-selftest/bad/yaml-prop-pairing.yaml @@ -0,0 +1,33 @@ +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/test-bad-prop-pairing.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Test fixture exercising -names and pinctrl-names pairing + +maintainers: + - Test User + +properties: + compatible: + const: example,test-prop-pairing + reg: + maxItems: 1 + +required: + - compatible + - reg + +additionalProperties: false + +examples: + - | + foo@1000 { + compatible =3D "example,test-prop-pairing"; + reg =3D <0x1000 0x100>; + clock-names =3D "bus"; + clocks =3D <&clk 0>; + pinctrl-names =3D "default"; + pinctrl-0 =3D <&p0>; + }; diff --git a/scripts/dtc/dt-style-selftest/bad/yaml-required-blank.yaml b/s= cripts/dtc/dt-style-selftest/bad/yaml-required-blank.yaml new file mode 100644 index 000000000000..8bb53240cffa --- /dev/null +++ b/scripts/dtc/dt-style-selftest/bad/yaml-required-blank.yaml @@ -0,0 +1,33 @@ +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/test-bad-required-blank.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Test fixture missing required blank lines + +maintainers: + - Test User + +properties: + compatible: + const: example,test-required-blank + reg: + maxItems: 1 + +required: + - compatible + - reg + +additionalProperties: false + +examples: + - | + bus@10000 { + compatible =3D "simple-bus"; + reg =3D <0x10000 0x1000>; + status =3D "okay"; + child@100 { + reg =3D <0x100>; + }; + }; diff --git a/scripts/dtc/dt-style-selftest/bad/yaml-tab.yaml b/scripts/dtc/= dt-style-selftest/bad/yaml-tab.yaml new file mode 100644 index 000000000000..487d07ff8cb6 --- /dev/null +++ b/scripts/dtc/dt-style-selftest/bad/yaml-tab.yaml @@ -0,0 +1,29 @@ +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/test-bad-tab.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Test fixture with a tab in a DTS line + +maintainers: + - Test User + +properties: + compatible: + const: example,test-tab + reg: + maxItems: 1 + +required: + - compatible + - reg + +additionalProperties: false + +examples: + - | + device@1000 { + compatible =3D "example,test-tab"; + reg =3D <0x1000 0x100>; /* registers */ + }; diff --git a/scripts/dtc/dt-style-selftest/bad/yaml-trailing-comment.yaml b= /scripts/dtc/dt-style-selftest/bad/yaml-trailing-comment.yaml new file mode 100644 index 000000000000..2368ada8106f --- /dev/null +++ b/scripts/dtc/dt-style-selftest/bad/yaml-trailing-comment.yaml @@ -0,0 +1,26 @@ +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/test-bad-trailing-comment.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Test fixture with properties out of order behind trailing comments + +maintainers: + - Test User + +properties: + compatible: + const: example,test-trailing-comment + +required: + - compatible + +additionalProperties: false + +examples: + - | + foo@0 { /* the device node */ + reg =3D <0x0 0x4>; /* registers */ + compatible =3D "example,test-trailing-comment"; // misplaced + }; diff --git a/scripts/dtc/dt-style-selftest/bad/yaml-trailing-ws.yaml b/scri= pts/dtc/dt-style-selftest/bad/yaml-trailing-ws.yaml new file mode 100644 index 000000000000..5c4b4bd833c5 --- /dev/null +++ b/scripts/dtc/dt-style-selftest/bad/yaml-trailing-ws.yaml @@ -0,0 +1,29 @@ +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/test-bad-trailing.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Test fixture with trailing whitespace + +maintainers: + - Test User + +properties: + compatible: + const: example,test-trailing + reg: + maxItems: 1 + +required: + - compatible + - reg + +additionalProperties: false + +examples: + - | + device@1000 { + compatible =3D "example,test-trailing"; =20 + reg =3D <0x1000 0x100>; + }; diff --git a/scripts/dtc/dt-style-selftest/bad/yaml-unit-addr.yaml b/script= s/dtc/dt-style-selftest/bad/yaml-unit-addr.yaml new file mode 100644 index 000000000000..93705cd45410 --- /dev/null +++ b/scripts/dtc/dt-style-selftest/bad/yaml-unit-addr.yaml @@ -0,0 +1,29 @@ +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/test-bad-unit-addr.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Test fixture with malformed unit address + +maintainers: + - Test User + +properties: + compatible: + const: example,test-unit-addr + reg: + maxItems: 1 + +required: + - compatible + - reg + +additionalProperties: false + +examples: + - | + foo@01000 { + compatible =3D "example,test-unit-addr"; + reg =3D <0x1000 0x100>; + }; diff --git a/scripts/dtc/dt-style-selftest/bad/yaml-unused-label.yaml b/scr= ipts/dtc/dt-style-selftest/bad/yaml-unused-label.yaml new file mode 100644 index 000000000000..28d7176cbf08 --- /dev/null +++ b/scripts/dtc/dt-style-selftest/bad/yaml-unused-label.yaml @@ -0,0 +1,29 @@ +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/test-bad-unused-label.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Test fixture with an unused label + +maintainers: + - Test User + +properties: + compatible: + const: example,test-unused-label + reg: + maxItems: 1 + +required: + - compatible + - reg + +additionalProperties: false + +examples: + - | + dev: device@1000 { + compatible =3D "example,test-unused-label"; + reg =3D <0x1000 0x100>; + }; diff --git a/scripts/dtc/dt-style-selftest/bad/yaml-value-ws-multiline.yaml= b/scripts/dtc/dt-style-selftest/bad/yaml-value-ws-multiline.yaml new file mode 100644 index 000000000000..504bf0931c27 --- /dev/null +++ b/scripts/dtc/dt-style-selftest/bad/yaml-value-ws-multiline.yaml @@ -0,0 +1,27 @@ +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/test-bad-value-ws-multiline.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Test fixture with extra whitespace in a multi-line cell array + +maintainers: + - Test User + +properties: + compatible: + const: example,test-value-ws-multiline + +required: + - compatible + +additionalProperties: false + +examples: + - | + foo@0 { + compatible =3D "example,test-value-ws-multiline"; + reg =3D < 0x0 0x4 + 0x8 0xc>; + }; diff --git a/scripts/dtc/dt-style-selftest/bad/yaml-value-ws.yaml b/scripts= /dtc/dt-style-selftest/bad/yaml-value-ws.yaml new file mode 100644 index 000000000000..342ab9f399f1 --- /dev/null +++ b/scripts/dtc/dt-style-selftest/bad/yaml-value-ws.yaml @@ -0,0 +1,29 @@ +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/test-bad-value-ws.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Test fixture with extra whitespace inside <...> + +maintainers: + - Test User + +properties: + compatible: + const: example,test-value-ws + reg: + maxItems: 1 + +required: + - compatible + - reg + +additionalProperties: false + +examples: + - | + foo@1000 { + compatible =3D "example,test-value-ws"; + reg =3D < 0x1000 0x100 >; + }; diff --git a/scripts/dtc/dt-style-selftest/expected/dts-spaces.dts.txt b/sc= ripts/dtc/dt-style-selftest/expected/dts-spaces.dts.txt new file mode 100644 index 000000000000..070025c4568c --- /dev/null +++ b/scripts/dtc/dt-style-selftest/expected/dts-spaces.dts.txt @@ -0,0 +1,2 @@ +# mode=3Drelaxed +bad/dts-spaces.dts:1: [indent-unit-dts] indent unit must be 1 tab in DTS, = got ' ' diff --git a/scripts/dtc/dt-style-selftest/expected/yaml-child-addr-order.y= aml.txt b/scripts/dtc/dt-style-selftest/expected/yaml-child-addr-order.yaml= .txt new file mode 100644 index 000000000000..f0db79a0018b --- /dev/null +++ b/scripts/dtc/dt-style-selftest/expected/yaml-child-addr-order.yaml.txt @@ -0,0 +1,2 @@ +# mode=3Dstrict +bad/yaml-child-addr-order.yaml:37: example 0 [child-address-order] child n= ode @100 out of address order diff --git a/scripts/dtc/dt-style-selftest/expected/yaml-child-name-order.y= aml.txt b/scripts/dtc/dt-style-selftest/expected/yaml-child-name-order.yaml= .txt new file mode 100644 index 000000000000..bb434b126191 --- /dev/null +++ b/scripts/dtc/dt-style-selftest/expected/yaml-child-name-order.yaml.txt @@ -0,0 +1,2 @@ +# mode=3Dstrict +bad/yaml-child-name-order.yaml:34: example 0 [child-name-order] child node= 'bar' out of name order diff --git a/scripts/dtc/dt-style-selftest/expected/yaml-cont-align.yaml.tx= t b/scripts/dtc/dt-style-selftest/expected/yaml-cont-align.yaml.txt new file mode 100644 index 000000000000..b5576dd0f6b1 --- /dev/null +++ b/scripts/dtc/dt-style-selftest/expected/yaml-cont-align.yaml.txt @@ -0,0 +1,2 @@ +# mode=3Dstrict +bad/yaml-cont-align.yaml:29: example 0 [continuation-alignment] continuati= on should align to column 11 (under "<" or \") diff --git a/scripts/dtc/dt-style-selftest/expected/yaml-digit-node-order.y= aml.txt b/scripts/dtc/dt-style-selftest/expected/yaml-digit-node-order.yaml= .txt new file mode 100644 index 000000000000..6de275e2dcb5 --- /dev/null +++ b/scripts/dtc/dt-style-selftest/expected/yaml-digit-node-order.yaml.txt @@ -0,0 +1,2 @@ +# mode=3Dstrict +bad/yaml-digit-node-order.yaml:33: example 0 [child-address-order] child n= ode @10 out of address order diff --git a/scripts/dtc/dt-style-selftest/expected/yaml-hex-case.yaml.txt = b/scripts/dtc/dt-style-selftest/expected/yaml-hex-case.yaml.txt new file mode 100644 index 000000000000..6600f7cd1ba5 --- /dev/null +++ b/scripts/dtc/dt-style-selftest/expected/yaml-hex-case.yaml.txt @@ -0,0 +1,2 @@ +# mode=3Dstrict +bad/yaml-hex-case.yaml:28: example 0 [hex-case] hex literal '0xABCD' must = be lowercase diff --git a/scripts/dtc/dt-style-selftest/expected/yaml-indent-strict.yaml= .txt b/scripts/dtc/dt-style-selftest/expected/yaml-indent-strict.yaml.txt new file mode 100644 index 000000000000..5ef290d3a847 --- /dev/null +++ b/scripts/dtc/dt-style-selftest/expected/yaml-indent-strict.yaml.txt @@ -0,0 +1,2 @@ +# mode=3Dstrict +bad/yaml-indent-strict.yaml:26: example 0 [indent-unit-strict] indent unit= must be 4 spaces in strict mode, got ' ' diff --git a/scripts/dtc/dt-style-selftest/expected/yaml-line-length.yaml.t= xt b/scripts/dtc/dt-style-selftest/expected/yaml-line-length.yaml.txt new file mode 100644 index 000000000000..89b36360caa4 --- /dev/null +++ b/scripts/dtc/dt-style-selftest/expected/yaml-line-length.yaml.txt @@ -0,0 +1,2 @@ +# mode=3Dstrict +bad/yaml-line-length.yaml:27: example 0 [line-length] line exceeds 80 colu= mns (81) diff --git a/scripts/dtc/dt-style-selftest/expected/yaml-mixed-indent.yaml.= txt b/scripts/dtc/dt-style-selftest/expected/yaml-mixed-indent.yaml.txt new file mode 100644 index 000000000000..c989f8f19853 --- /dev/null +++ b/scripts/dtc/dt-style-selftest/expected/yaml-mixed-indent.yaml.txt @@ -0,0 +1,3 @@ +# mode=3Drelaxed +bad/yaml-mixed-indent.yaml:27: example 0 [mixed-indent-chars] mixed tabs a= nd spaces in indent +bad/yaml-mixed-indent.yaml:27: example 0 [tab-in-dts] tab character not al= lowed in DTS example diff --git a/scripts/dtc/dt-style-selftest/expected/yaml-node-close.yaml.tx= t b/scripts/dtc/dt-style-selftest/expected/yaml-node-close.yaml.txt new file mode 100644 index 000000000000..ee894747b5b9 --- /dev/null +++ b/scripts/dtc/dt-style-selftest/expected/yaml-node-close.yaml.txt @@ -0,0 +1,2 @@ +# mode=3Dstrict +bad/yaml-node-close.yaml:30: example 0 [node-close-alone] closing brace mu= st be on its own line diff --git a/scripts/dtc/dt-style-selftest/expected/yaml-prop-order.yaml.tx= t b/scripts/dtc/dt-style-selftest/expected/yaml-prop-order.yaml.txt new file mode 100644 index 000000000000..578df7209170 --- /dev/null +++ b/scripts/dtc/dt-style-selftest/expected/yaml-prop-order.yaml.txt @@ -0,0 +1,2 @@ +# mode=3Dstrict +bad/yaml-prop-order.yaml:28: example 0 [property-order] property 'compatib= le' out of canonical order (should sort before 'reg') diff --git a/scripts/dtc/dt-style-selftest/expected/yaml-prop-pairing.yaml.= txt b/scripts/dtc/dt-style-selftest/expected/yaml-prop-pairing.yaml.txt new file mode 100644 index 000000000000..e6e21349a939 --- /dev/null +++ b/scripts/dtc/dt-style-selftest/expected/yaml-prop-pairing.yaml.txt @@ -0,0 +1,3 @@ +# mode=3Dstrict +bad/yaml-prop-pairing.yaml:30: example 0 [property-order] property 'clocks= ' out of canonical order (should sort before 'clock-names') +bad/yaml-prop-pairing.yaml:32: example 0 [property-order] property 'pinctr= l-0' out of canonical order (should sort before 'pinctrl-names') diff --git a/scripts/dtc/dt-style-selftest/expected/yaml-required-blank.yam= l.txt b/scripts/dtc/dt-style-selftest/expected/yaml-required-blank.yaml.txt new file mode 100644 index 000000000000..04ea0bacdcb9 --- /dev/null +++ b/scripts/dtc/dt-style-selftest/expected/yaml-required-blank.yaml.txt @@ -0,0 +1,3 @@ +# mode=3Dstrict +bad/yaml-required-blank.yaml:29: example 0 [required-blank-lines] "status"= must be preceded by a blank line +bad/yaml-required-blank.yaml:30: example 0 [required-blank-lines] child no= de must be preceded by a blank line diff --git a/scripts/dtc/dt-style-selftest/expected/yaml-tab.yaml.txt b/scr= ipts/dtc/dt-style-selftest/expected/yaml-tab.yaml.txt new file mode 100644 index 000000000000..9e83246fbaa1 --- /dev/null +++ b/scripts/dtc/dt-style-selftest/expected/yaml-tab.yaml.txt @@ -0,0 +1,2 @@ +# mode=3Drelaxed +bad/yaml-tab.yaml:28: example 0 [tab-in-dts] tab character not allowed in = DTS example diff --git a/scripts/dtc/dt-style-selftest/expected/yaml-trailing-comment.y= aml.txt b/scripts/dtc/dt-style-selftest/expected/yaml-trailing-comment.yaml= .txt new file mode 100644 index 000000000000..69dbb1d03239 --- /dev/null +++ b/scripts/dtc/dt-style-selftest/expected/yaml-trailing-comment.yaml.txt @@ -0,0 +1,2 @@ +# mode=3Dstrict +bad/yaml-trailing-comment.yaml:25: example 0 [property-order] property 'co= mpatible' out of canonical order (should sort before 'reg') diff --git a/scripts/dtc/dt-style-selftest/expected/yaml-trailing-ws.yaml.t= xt b/scripts/dtc/dt-style-selftest/expected/yaml-trailing-ws.yaml.txt new file mode 100644 index 000000000000..cfdbc8476c73 --- /dev/null +++ b/scripts/dtc/dt-style-selftest/expected/yaml-trailing-ws.yaml.txt @@ -0,0 +1,2 @@ +# mode=3Drelaxed +bad/yaml-trailing-ws.yaml:27: example 0 [trailing-whitespace] trailing whi= tespace diff --git a/scripts/dtc/dt-style-selftest/expected/yaml-unit-addr.yaml.txt= b/scripts/dtc/dt-style-selftest/expected/yaml-unit-addr.yaml.txt new file mode 100644 index 000000000000..b52f0ef20bee --- /dev/null +++ b/scripts/dtc/dt-style-selftest/expected/yaml-unit-addr.yaml.txt @@ -0,0 +1,2 @@ +# mode=3Dstrict +bad/yaml-unit-addr.yaml:26: example 0 [unit-address-format] unit address '= 01000' has leading zeros diff --git a/scripts/dtc/dt-style-selftest/expected/yaml-unused-label.yaml.= txt b/scripts/dtc/dt-style-selftest/expected/yaml-unused-label.yaml.txt new file mode 100644 index 000000000000..4f00202f0902 --- /dev/null +++ b/scripts/dtc/dt-style-selftest/expected/yaml-unused-label.yaml.txt @@ -0,0 +1,2 @@ +# mode=3Dstrict +bad/yaml-unused-label.yaml:26: example 0 [unused-labels] label 'dev' defin= ed but never &-referenced diff --git a/scripts/dtc/dt-style-selftest/expected/yaml-value-ws-multiline= .yaml.txt b/scripts/dtc/dt-style-selftest/expected/yaml-value-ws-multiline.= yaml.txt new file mode 100644 index 000000000000..3df55b1762d0 --- /dev/null +++ b/scripts/dtc/dt-style-selftest/expected/yaml-value-ws-multiline.yaml.t= xt @@ -0,0 +1,2 @@ +# mode=3Dstrict +bad/yaml-value-ws-multiline.yaml:25: example 0 [value-whitespace] extra wh= itespace inside <...> diff --git a/scripts/dtc/dt-style-selftest/expected/yaml-value-ws.yaml.txt = b/scripts/dtc/dt-style-selftest/expected/yaml-value-ws.yaml.txt new file mode 100644 index 000000000000..cbb5f88fe85f --- /dev/null +++ b/scripts/dtc/dt-style-selftest/expected/yaml-value-ws.yaml.txt @@ -0,0 +1,2 @@ +# mode=3Dstrict +bad/yaml-value-ws.yaml:28: example 0 [value-whitespace] extra whitespace i= nside <...> diff --git a/scripts/dtc/dt-style-selftest/good/dts-cont-align.dts b/script= s/dtc/dt-style-selftest/good/dts-cont-align.dts new file mode 100644 index 000000000000..5c5ffdd1a7df --- /dev/null +++ b/scripts/dtc/dt-style-selftest/good/dts-cont-align.dts @@ -0,0 +1,27 @@ +/* + * SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause + * + * Test fixture: tab-indented .dts with a tab-and-space aligned + * multi-line property. Continuation lines mix tabs for indent and + * spaces for alignment by design; that must not be flagged. + */ + +/dts-v1/; + +/ { + compatible =3D "example,test-board"; + #address-cells =3D <1>; + #size-cells =3D <1>; + + interrupt-controller@10000 { + compatible =3D "example,intc"; + reg =3D <0x10000 0x1000>; + interrupts =3D <1 2 3>, + <4 5 6>, + <7 8 9>; + pinmux =3D < + 0x01 + 0x02 + >; + }; +}; diff --git a/scripts/dtc/dt-style-selftest/good/dts-tab.dts b/scripts/dtc/d= t-style-selftest/good/dts-tab.dts new file mode 100644 index 000000000000..14295811c2bc --- /dev/null +++ b/scripts/dtc/dt-style-selftest/good/dts-tab.dts @@ -0,0 +1,30 @@ +/* + * SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause + * + * Test fixture: a properly formatted .dts using one-tab indent. + */ + +/dts-v1/; + +/ { + compatible =3D "example,test-board"; + #address-cells =3D <1>; + #size-cells =3D <1>; + + bus@10000 { + compatible =3D "simple-bus"; + reg =3D <0x10000 0x1000>; + #address-cells =3D <1>; + #size-cells =3D <1>; + + device@100 { + compatible =3D "example,test"; + reg =3D <0x100 0x10>; + }; + + device@200 { + compatible =3D "example,test"; + reg =3D <0x200 0x10>; + }; + }; +}; diff --git a/scripts/dtc/dt-style-selftest/good/yaml-4space.yaml b/scripts/= dtc/dt-style-selftest/good/yaml-4space.yaml new file mode 100644 index 000000000000..1502f803c24c --- /dev/null +++ b/scripts/dtc/dt-style-selftest/good/yaml-4space.yaml @@ -0,0 +1,41 @@ +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/test-good-4space.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Test fixture for dt-check-style + +maintainers: + - Test User + +properties: + compatible: + const: example,test-4space + reg: + maxItems: 1 + +required: + - compatible + - reg + +additionalProperties: false + +examples: + - | + bus@10000 { + compatible =3D "simple-bus"; + reg =3D <0x10000 0x1000>; + #address-cells =3D <1>; + #size-cells =3D <1>; + + device@100 { + compatible =3D "example,test-4space"; + reg =3D <0x100 0x10>; + }; + + device@200 { + compatible =3D "example,test-4space"; + reg =3D <0x200 0x10>; + }; + }; diff --git a/scripts/dtc/dt-style-selftest/run.sh b/scripts/dtc/dt-style-se= lftest/run.sh new file mode 100755 index 000000000000..8117dd9be90a --- /dev/null +++ b/scripts/dtc/dt-style-selftest/run.sh @@ -0,0 +1,71 @@ +#!/bin/sh +# SPDX-License-Identifier: GPL-2.0-only +# +# Run dt-check-style against fixtures under good/ and bad/. +# good/ files must produce no output and exit 0 in both modes. +# bad/ files must produce the expected output (in expected/.txt) +# and exit 1. +# +# The mode used for a bad fixture is whichever produces a violation: +# trailing-whitespace and tab fixtures use the default (relaxed), +# the rest use --mode=3Dstrict. The expected output files name the +# mode in their first line. + +set -u + +here=3D$(cd "$(dirname "$0")" && pwd) +tool=3D"$here/../dt-check-style" +fail=3D0 + +run() { + file=3D$1 + mode=3D$2 + "$tool" --mode=3D"$mode" "$file" 2>&1 +} + +# good/ -- must exit 0 and produce no output in both modes +for f in "$here"/good/*; do + [ -e "$f" ] || continue + for mode in relaxed strict; do + out=3D$(run "$f" "$mode") + rc=3D$? + if [ -n "$out" ] || [ "$rc" -ne 0 ]; then + echo "FAIL good/$mode: $(basename "$f") (exit $rc, want 0):" + echo "$out" | sed 's/^/ /' + fail=3D$((fail + 1)) + fi + done +done + +# bad/ -- must match expected/.txt +for f in "$here"/bad/*; do + [ -e "$f" ] || continue + name=3D$(basename "$f") + expected=3D"$here/expected/$name.txt" + if [ ! -f "$expected" ]; then + echo "FAIL bad: missing $expected" + fail=3D$((fail + 1)) + continue + fi + mode=3D$(head -1 "$expected" | sed 's/^# mode=3D//') + body=3D$(tail -n +2 "$expected") + out=3D$(run "$f" "$mode") + rc=3D$? + # Strip the directory prefix so expected files are portable. + out=3D$(printf '%s\n' "$out" | sed "s|$here/bad/|bad/|g") + if [ "$out" !=3D "$body" ] || [ "$rc" -ne 1 ]; then + echo "FAIL bad/$mode: $name (exit $rc, want 1):" + bf=3D$(mktemp) + printf '%s\n' "$body" > "$bf" + printf '%s\n' "$out" | diff -u "$bf" - | sed 's/^/ /' + rm -f "$bf" + fail=3D$((fail + 1)) + fi +done + +if [ "$fail" -eq 0 ]; then + echo "PASS" + exit 0 +fi +echo "FAILED ($fail)" +exit 1 --=20 2.54.0