From 33db7b5b0146d7cd0bb0e509a90ac84a2518e589 Mon Sep 17 00:00:00 2001 From: Kumar Gala Date: Sat, 17 Oct 2020 11:46:03 -0500 Subject: [PATCH] scripts: edtlib: Add support for 'deprecated' Add the ability to mark a property as 'deprecated' to get a warning that it will be removed in the future. Signed-off-by: Kumar Gala --- dts/binding-template.yaml | 6 ++++ scripts/dts/edtlib.py | 41 +++++++++++++++++------ scripts/dts/test-bindings/deprecated.yaml | 15 +++++++++ scripts/dts/test.dts | 10 ++++++ scripts/dts/testedtlib.py | 3 +- 5 files changed, 63 insertions(+), 12 deletions(-) create mode 100644 scripts/dts/test-bindings/deprecated.yaml diff --git a/dts/binding-template.yaml b/dts/binding-template.yaml index df91287797..7e5e144e0d 100644 --- a/dts/binding-template.yaml +++ b/dts/binding-template.yaml @@ -87,6 +87,7 @@ on-bus: # A typical property entry looks like this: # # : +# deprecated: # required: # type: @@ -213,6 +214,11 @@ on-bus: # Note that it only makes sense to combine 'default:' with 'required: false'. # Combining it with 'required: true' will raise an error. # +# A property with 'deprecated: True' indicates to both the user and the tooling +# that the property is meant to be phased out. The tooling will report a +# warning if the devicetree includes the property that is flagged as deprecated. +# There will be no other impact to having 'deprecated: True' set on the property. +# # See below for examples of 'default:'. Putting 'default:' on any property type # besides those used in the examples will raise an error. properties: diff --git a/scripts/dts/edtlib.py b/scripts/dts/edtlib.py index ac1de9717a..6d532cde09 100644 --- a/scripts/dts/edtlib.py +++ b/scripts/dts/edtlib.py @@ -886,7 +886,7 @@ class Node: for name in node.props: if name in _DEFAULT_PROP_TYPES: prop_type = _DEFAULT_PROP_TYPES[name] - val = self._prop_val(name, prop_type, False, None) + val = self._prop_val(name, prop_type, False, False, None) prop = Property() prop.node = self prop.name = name @@ -906,8 +906,8 @@ class Node: if not prop_type: _err("'{}' in {} lacks 'type'".format(name, self.binding_path)) - val = self._prop_val(name, prop_type, prop_spec.required, - prop_spec.default) + val = self._prop_val(name, prop_type, prop_spec.deprecated, + prop_spec.required, prop_spec.default) if val is None: # 'required: false' property that wasn't there, or a property type @@ -945,7 +945,7 @@ class Node: self.props[name] = prop - def _prop_val(self, name, prop_type, required, default): + def _prop_val(self, name, prop_type, deprecated, required, default): # _init_prop() helper for getting the property's value # # name: @@ -954,6 +954,9 @@ class Node: # prop_type: # Property type from binding (a string like "int") # + # deprecated: + # True if the property is deprecated + # # required: # True if the property is required to exist # @@ -964,6 +967,10 @@ class Node: node = self._node prop = node.props.get(name) + if prop and deprecated: + self.edt._warn("'{}' is marked as deprecated in 'properties:' in {} " + "for node {}.".format(name, self.binding_path, node.path)) + if not prop: if required and self.status == "okay": _err("'{}' is marked as required in 'properties:' in {}, but " @@ -1648,7 +1655,7 @@ class Binding: return ok_prop_keys = {"description", "type", "required", - "enum", "const", "default"} + "enum", "const", "default", "deprecated"} for prop_name, options in raw["properties"].items(): for key in options: @@ -1662,12 +1669,17 @@ class Binding: options.get("default"), self.path) - if "required" in options: - required = options["required"] - if not isinstance(required, bool): - _err(f"malformed 'required:' setting '{required}' " - f"for '{prop_name}' in 'properties' in {self.path}, " - "expected true/false") + for true_false_opt in ["required", "deprecated"]: + if true_false_opt in options: + option = options[true_false_opt] + if not isinstance(option, bool): + _err(f"malformed '{true_false_opt}:' setting '{option}' " + f"for '{prop_name}' in 'properties' in {self.path}, " + "expected true/false") + + if options.get("deprecated") and options.get("required"): + _err(f"'{prop_name}' in 'properties' in {self.path} should not " + "have both 'deprecated' and 'required' set") if "description" in options and \ not isinstance(options["description"], str): @@ -1722,6 +1734,9 @@ class PropertySpec: default: The property's default value as given in the binding, or None. + deprecated: + True if the property is deprecated; False otherwise. + required: True if the property is marked required; False otherwise. """ @@ -1769,6 +1784,10 @@ class PropertySpec: "See the class docstring" return self._raw.get("required", False) + @property + def deprecated(self): + "See the class docstring" + return self._raw.get("deprecated", False) class EDTError(Exception): "Exception raised for devicetree- and binding-related errors" diff --git a/scripts/dts/test-bindings/deprecated.yaml b/scripts/dts/test-bindings/deprecated.yaml new file mode 100644 index 0000000000..d8c72e5c05 --- /dev/null +++ b/scripts/dts/test-bindings/deprecated.yaml @@ -0,0 +1,15 @@ +# SPDX-License-Identifier: BSD-3-Clause + +description: Property deprecated value test + +compatible: "test-deprecated" + +properties: + oldprop: + type: int + deprecated: true + required: false + + curprop: + type: int + required: false diff --git a/scripts/dts/test.dts b/scripts/dts/test.dts index e1d2865d9c..05430c21f2 100644 --- a/scripts/dts/test.dts +++ b/scripts/dts/test.dts @@ -386,6 +386,16 @@ }; }; + // + // For testing deprecated property + // + test-deprecated { + compatible = "test-deprecated"; + oldprop = <4>; /* deprecated property */ + curprop = <5>; + }; + + // // For testing deprecated features // diff --git a/scripts/dts/testedtlib.py b/scripts/dts/testedtlib.py index 48ec03a859..3ceb5fc3cb 100644 --- a/scripts/dts/testedtlib.py +++ b/scripts/dts/testedtlib.py @@ -30,7 +30,8 @@ def test_warnings(): warnings = io.StringIO() edtlib.EDT("test.dts", ["test-bindings"], warnings) - assert warnings.getvalue() == """\ + assert warnings.getvalue() == f"""\ +warning: 'oldprop' is marked as deprecated in 'properties:' in {hpath('test-bindings/deprecated.yaml')} for node /test-deprecated. warning: unit address and first address in 'reg' (0x1) don't match for /reg-zero-size-cells/node warning: unit address and first address in 'reg' (0x5) don't match for /reg-ranges/parent/node warning: unit address and first address in 'reg' (0x30000000200000001) don't match for /reg-nested-ranges/grandparent/parent/node