Skip to content

Commit c29684a

Browse files
committed
zephyr-build: Include DT node presence configs
Generate a full list of nodes that are present in the given device tree, and provide a tool that build.rs in the app can use to make these active. This will allow conditionals like: #[cfg(dt = "aliases::led")] to be used, which will make it possible to handle nodes being present or not in the DTS. See a subsequent patch to the blinky sample for an example of usage. Signed-off-by: David Brown <[email protected]>
1 parent 0915349 commit c29684a

File tree

2 files changed

+74
-1
lines changed

2 files changed

+74
-1
lines changed

zephyr-build/src/devicetree/output.rs

+51
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,9 @@
88
// Support for particular devices should also be added to the device tree here, so that the nodes
99
// make sense for that device, and that there are general accessors that return wrapped node types.
1010

11+
use std::io::Write;
12+
13+
use anyhow::Result;
1114
use proc_macro2::{Ident, TokenStream};
1215
use quote::{format_ident, quote};
1316

@@ -23,6 +26,20 @@ impl DeviceTree {
2326
self.node_walk(self.root.as_ref(), None, &augments)
2427
}
2528

29+
/// Write to the given file a list of the path names of all of the nodes present in this
30+
/// devicetree.
31+
pub fn output_node_paths<W: Write>(&self, write: &mut W) -> Result<()> {
32+
self.root.as_ref().output_path_walk(write, None)?;
33+
34+
// Also, output all of the labels. Technically, this depends on the labels augment being
35+
// present.
36+
writeln!(write, "labels")?;
37+
for label in self.labels.keys() {
38+
writeln!(write, "labels::{}", fix_id(label))?;
39+
}
40+
Ok(())
41+
}
42+
2643
fn node_walk(&self, node: &Node, name: Option<&str>, augments: &[Box<dyn Augment>]) -> TokenStream {
2744
let children = node.children.iter().map(|child| {
2845
self.node_walk(child.as_ref(), Some(&child.name), augments)
@@ -118,6 +135,29 @@ impl Node {
118135
crate :: devicetree #(:: #route)*
119136
}
120137
}
138+
139+
/// Walk this tree of nodes, writing out the path names of the nodes that are present. The name
140+
/// of None, indicates the root node.
141+
fn output_path_walk<W: Write>(&self, write: &mut W, name: Option<&str>) -> Result<()> {
142+
for child in &self.children {
143+
let fixed_name = fix_id(&child.name);
144+
let child_name = if let Some(name) = name {
145+
format!("{}::{}", name, fixed_name)
146+
} else {
147+
fixed_name
148+
};
149+
150+
writeln!(write, "{}", child_name)?;
151+
152+
for prop in &child.properties {
153+
prop.output_path(write, &child_name)?;
154+
}
155+
156+
child.output_path_walk(write, Some(&child_name))?;
157+
}
158+
159+
Ok(())
160+
}
121161
}
122162

123163
impl Property {
@@ -129,6 +169,17 @@ impl Property {
129169
None
130170
}
131171
}
172+
173+
// If this property is a single top-level phandle, output that a that path is valid. It isn't a
174+
// real node, but acts like one.
175+
fn output_path<W: Write>(&self, write: &mut W, name: &str) -> Result<()> {
176+
if let Some(value) = self.get_single_value() {
177+
if let Value::Phandle(_) = value {
178+
writeln!(write, "{}::{}", name, self.name)?;
179+
}
180+
}
181+
Ok(())
182+
}
132183
}
133184

134185
fn general_property(prop: &Property) -> TokenStream {

zephyr-build/src/lib.rs

+23-1
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,7 @@ pub fn build_dts() {
8686
let outdir = env::var("OUT_DIR").expect("OUT_DIR must be set");
8787
let gen_include = env::var("BINARY_DIR_INCLUDE_GENERATED")
8888
.expect("BINARY_DIR_INCLUDE_GENERATED");
89+
let builddir = env::var("BUILD_DIR").expect("BUILD_DIR");
8990

9091
let augments = env::var("DT_AUGMENTS").expect("DT_AUGMENTS must be set");
9192
let augments: Vec<String> = augments.split_whitespace().map(String::from).collect();
@@ -111,7 +112,6 @@ pub fn build_dts() {
111112

112113
let generated = format!("{}/devicetree_generated.h", gen_include);
113114
let dt = DeviceTree::new(&zephyr_dts, generated);
114-
let _ = dt;
115115

116116
let out_path = Path::new(&outdir).join("devicetree.rs");
117117
let mut out = File::create(&out_path).expect("Unable to create devicetree.rs");
@@ -122,6 +122,28 @@ pub fn build_dts() {
122122
} else {
123123
writeln!(out, "{}", tokens).unwrap();
124124
};
125+
126+
// Output all of the node names in the discovered tree.
127+
let all_nodes_path = Path::new(&builddir)
128+
.join("rust")
129+
.join("all-dt-nodes.txt");
130+
let mut out = File::create(&all_nodes_path).expect("Unable to create all-dt-nodex.txt");
131+
dt.output_node_paths(&mut out).expect("Unable to write to all-dt-nodes.txt");
132+
}
133+
134+
/// Generate cfg directives for each of the nodes in the generated device tree.
135+
///
136+
/// This assumes that build_dts was already run by the `zephyr` crate, which should happen if this
137+
/// is called from a user application.
138+
pub fn dt_cfgs() {
139+
let builddir = env::var("BUILD_DIR").expect("BUILD_DIR");
140+
let path = Path::new(&builddir)
141+
.join("rust")
142+
.join("all-dt-nodes.txt");
143+
for line in BufReader::new(File::open(&path).expect("Unable to open all-dt-nodes")).lines() {
144+
let line = line.expect("Error reading line from all-dt-nodes");
145+
println!("cargo:rustc-cfg=dt=\"{}\"", line);
146+
}
125147
}
126148

127149
/// Determine if `rustfmt` is in the path, and can be excecuted. Returns false on any kind of error.

0 commit comments

Comments
 (0)