devicetree: Add filename and line number tracking for nodes & properties
This change enhances the devicetree library by adding support for tracking the source filename and line number for nodes and properties. Signed-off-by: Benjamin Cabé <benjamin@zephyrproject.org>
This commit is contained in:
parent
8081008a08
commit
529656e2be
@ -38,6 +38,12 @@ class Node:
|
||||
name:
|
||||
The name of the node (a string).
|
||||
|
||||
filename:
|
||||
The name of the .dts file where the node is defined
|
||||
|
||||
lineno:
|
||||
The line number in the .dts file where the node starts.
|
||||
|
||||
unit_addr:
|
||||
The portion after the '@' in the node's name, or the empty string if the
|
||||
name has no '@' in it.
|
||||
@ -85,13 +91,15 @@ class Node:
|
||||
# Public interface
|
||||
#
|
||||
|
||||
def __init__(self, name: str, parent: Optional['Node'], dt: 'DT'):
|
||||
def __init__(self, name: str, parent: Optional["Node"], dt: "DT", filename: str, lineno: int):
|
||||
"""
|
||||
Node constructor. Not meant to be called directly by clients.
|
||||
"""
|
||||
# Remember to update DT.__deepcopy__() if you change this.
|
||||
|
||||
self._name = name
|
||||
self._filename = filename
|
||||
self._lineno = lineno
|
||||
self.props: dict[str, Property] = {}
|
||||
self.nodes: dict[str, Node] = {}
|
||||
self.labels: list[str] = []
|
||||
@ -118,6 +126,20 @@ class Node:
|
||||
# via DT.move_node.
|
||||
return self._name
|
||||
|
||||
@property
|
||||
def lineno(self) -> int:
|
||||
"""
|
||||
See the class documentation.
|
||||
"""
|
||||
return self._lineno
|
||||
|
||||
@property
|
||||
def filename(self) -> str:
|
||||
"""
|
||||
See the class documentation.
|
||||
"""
|
||||
return self._filename
|
||||
|
||||
@property
|
||||
def unit_addr(self) -> str:
|
||||
"""
|
||||
@ -240,6 +262,12 @@ class Property:
|
||||
name:
|
||||
The name of the property (a string).
|
||||
|
||||
filename:
|
||||
The name of the .dts file where the property is defined
|
||||
|
||||
lineno:
|
||||
The line number in the .dts file where the property starts.
|
||||
|
||||
value:
|
||||
The value of the property, as a 'bytes' string. Numbers are stored in
|
||||
big-endian format, and strings are null-terminated. Putting multiple
|
||||
@ -308,6 +336,8 @@ class Property:
|
||||
node.dt._parse_error("'@' is only allowed in node names")
|
||||
|
||||
self.name = name
|
||||
self.filename = ""
|
||||
self.lineno = -1
|
||||
self.value = b""
|
||||
self.labels: list[str] = []
|
||||
# We have to wait to set this until later, when we've got
|
||||
@ -613,7 +643,6 @@ class Property:
|
||||
|
||||
return s + ";"
|
||||
|
||||
|
||||
def __repr__(self):
|
||||
return (f"<Property '{self.name}' at '{self.node.path}' in "
|
||||
f"'{self.node.dt.filename}'>")
|
||||
@ -927,7 +956,7 @@ class DT:
|
||||
# them without any properties. We will recursively initialize
|
||||
# copies of parents before copies of children next.
|
||||
path2node_copy = {
|
||||
node.path: Node(node.name, None, ret)
|
||||
node.path: Node(node.name, None, ret, node.filename, node.lineno)
|
||||
for node in self.node_iter()
|
||||
}
|
||||
|
||||
@ -956,6 +985,8 @@ class DT:
|
||||
prop_copy.offset_labels = prop.offset_labels.copy()
|
||||
prop_copy._label_offset_lst = prop._label_offset_lst[:]
|
||||
prop_copy._markers = [marker[:] for marker in prop._markers]
|
||||
prop_copy.filename = prop.filename
|
||||
prop_copy.lineno = prop.lineno
|
||||
node_copy.props = prop_name2prop_copy
|
||||
|
||||
node_copy.nodes = {
|
||||
@ -1083,7 +1114,9 @@ class DT:
|
||||
if tok.val == "/":
|
||||
# '/ { ... };', the root node
|
||||
if not self._root:
|
||||
self._root = Node(name="/", parent=None, dt=self)
|
||||
self._root = Node(
|
||||
name="/", parent=None, dt=self, filename=self.filename, lineno=self._lineno
|
||||
)
|
||||
self._parse_node(self.root)
|
||||
|
||||
elif tok.id in (_T.LABEL, _T.REF):
|
||||
@ -1146,7 +1179,13 @@ class DT:
|
||||
if child.name in current_child_names:
|
||||
self._parse_error(f'{child.path}: duplicate node name')
|
||||
else:
|
||||
child = Node(name=tok.val, parent=node, dt=self)
|
||||
child = Node(
|
||||
name=tok.val,
|
||||
parent=node,
|
||||
dt=self,
|
||||
filename=self.filename,
|
||||
lineno=self._lineno,
|
||||
)
|
||||
current_child_names.add(tok.val)
|
||||
|
||||
for label in labels:
|
||||
@ -1166,6 +1205,8 @@ class DT:
|
||||
"/omit-if-no-ref/ can only be used on nodes")
|
||||
|
||||
prop = node._get_prop(tok.val)
|
||||
prop.filename = self.filename
|
||||
prop.lineno = self._lineno
|
||||
|
||||
if self._check_token("="):
|
||||
self._parse_assignment(prop)
|
||||
|
||||
@ -1054,6 +1054,16 @@ class Node:
|
||||
"See the class docstring"
|
||||
return self._node.name
|
||||
|
||||
@property
|
||||
def filename(self) -> str:
|
||||
"See the class docstring"
|
||||
return self._node.filename
|
||||
|
||||
@property
|
||||
def lineno(self) -> int:
|
||||
"See the class docstring"
|
||||
return self._node.lineno
|
||||
|
||||
@property
|
||||
def unit_addr(self) -> Optional[int]:
|
||||
"See the class docstring"
|
||||
|
||||
@ -2499,3 +2499,59 @@ def test_move_node():
|
||||
with dtlib_raises("can't move '/newpath' to '/foo/bar': "
|
||||
"parent node '/foo' doesn't exist"):
|
||||
dt.move_node(parent, '/foo/bar')
|
||||
|
||||
def test_filename_and_lineno():
|
||||
"""Test that filename and lineno are correctly tracked for nodes and properties."""
|
||||
|
||||
with tempfile.TemporaryDirectory() as tmpdir:
|
||||
included_file = os.path.join(tmpdir, 'included.dtsi')
|
||||
with open(included_file, 'w') as f:
|
||||
f.write('''/* a new node */
|
||||
/ {
|
||||
node1: test-node1 {
|
||||
prop1A = "value1A";
|
||||
};
|
||||
};
|
||||
''')
|
||||
|
||||
main_file = os.path.join(tmpdir, 'test_with_include.dts')
|
||||
with open(main_file, 'w') as f:
|
||||
f.write('''/dts-v1/;
|
||||
|
||||
/include/ "included.dtsi"
|
||||
|
||||
/ {
|
||||
node2: test-node2 {
|
||||
prop2A = "value2A";
|
||||
prop2B = "value2B";
|
||||
};
|
||||
};
|
||||
|
||||
&node1 {
|
||||
prop1B = "value1B";
|
||||
};
|
||||
''')
|
||||
|
||||
dt = dtlib.DT(main_file, include_path=[tmpdir])
|
||||
|
||||
test_node2 = dt.get_node('/test-node2')
|
||||
prop2A = test_node2.props['prop2A']
|
||||
prop2B = test_node2.props['prop2B']
|
||||
|
||||
assert os.path.samefile(test_node2.filename, main_file)
|
||||
assert test_node2.lineno == 6
|
||||
assert os.path.samefile(prop2A.filename, main_file)
|
||||
assert prop2A.lineno == 7
|
||||
assert os.path.samefile(prop2B.filename, main_file)
|
||||
assert prop2B.lineno == 8
|
||||
|
||||
test_node1 = dt.get_node('/test-node1')
|
||||
prop1A = test_node1.props['prop1A']
|
||||
prop1B = test_node1.props['prop1B']
|
||||
|
||||
assert os.path.samefile(test_node1.filename, included_file)
|
||||
assert test_node1.lineno == 3
|
||||
assert os.path.samefile(prop1A.filename, included_file)
|
||||
assert prop1A.lineno == 4
|
||||
assert os.path.samefile(prop1B.filename, main_file)
|
||||
assert prop1B.lineno == 13
|
||||
|
||||
Loading…
Reference in New Issue
Block a user