diff --git a/scripts/dts/python-devicetree/src/devicetree/edtlib.py b/scripts/dts/python-devicetree/src/devicetree/edtlib.py index f3065b76f85..e9bfa56e774 100644 --- a/scripts/dts/python-devicetree/src/devicetree/edtlib.py +++ b/scripts/dts/python-devicetree/src/devicetree/edtlib.py @@ -475,6 +475,30 @@ class EDT: 'in lowercase: ' + ', '.join(repr(x) for x in spec.enum)) + # Validate the contents of compatible properties. + # The regular expression comes from dt-schema. + compat_re = r'^[a-zA-Z][a-zA-Z0-9,+\-._]+$' + for node in self.nodes: + if 'compatible' not in node.props: + continue + + compatibles = node.props['compatible'].val + + # _check() runs after _init_compat2binding() has called + # _dt_compats(), which already converted every compatible + # property to a list of strings. So we know 'compatibles' + # is a list, but add an assert for future-proofing. + assert isinstance(compatibles, list) + + for compat in compatibles: + # This is also just for future-proofing. + assert isinstance(compat, str) + + if not re.match(compat_re, compat): + _err(f"node '{node.path}' compatible '{compat}' " + 'must match this regular expression: ' + f"'{compat_re}'") + class Node: """ Represents a devicetree node, augmented with information from bindings, and diff --git a/scripts/dts/python-devicetree/tests/test_edtlib.py b/scripts/dts/python-devicetree/tests/test_edtlib.py index 4f318fdcddb..f570d4b9ef2 100644 --- a/scripts/dts/python-devicetree/tests/test_edtlib.py +++ b/scripts/dts/python-devicetree/tests/test_edtlib.py @@ -498,6 +498,24 @@ def test_slice_errs(tmp_path): dts_file, f"'ranges' property in has length 8, which is not evenly divisible by 24 (= 4*(<#address-cells> (= 2) + <#address-cells for parent> (= 1) + <#size-cells> (= 3))). Note that #*-cells properties come either from the parent node or from the controller (in the case of 'interrupts').") +def test_bad_compatible(tmp_path): + # An invalid compatible should cause an error, even on a node with + # no binding. + + dts_file = tmp_path / "error.dts" + + verify_error(""" +/dts-v1/; + +/ { + foo { + compatible = "no, whitespace"; + }; +}; +""", + dts_file, + r"node '/foo' compatible 'no, whitespace' must match this regular expression: '^[a-zA-Z][a-zA-Z0-9,+\-._]+$'") + def verify_error(dts, dts_file, expected_err): # Verifies that parsing a file 'dts_file' with the contents 'dts' # (a string) raises an EDTError with the message 'expected_err'.