1use std::collections::HashSet;
15use std::env;
16use std::fs::File;
17use std::io::{BufRead, BufReader, Write};
18use std::path::Path;
19use std::process::{Command, Stdio};
20
21use proc_macro2::TokenStream;
22use regex::Regex;
23
24use devicetree::{Augment, DeviceTree};
25
26mod devicetree;
27
28pub fn extract_kconfig_bool_options(path: &str) -> anyhow::Result<HashSet<String>> {
31 let config_y = Regex::new(r"^(CONFIG_.*)=y$").expect("hardcoded regex is always valid");
32
33 let input = File::open(path)?;
34 let flags: HashSet<String> = BufReader::new(input)
35 .lines()
36 .map(|x| x.unwrap_or_default())
38 .filter_map(|line| config_y.captures(&line).map(|caps| caps[1].to_string()))
39 .collect();
40
41 Ok(flags)
42}
43
44pub fn export_kconfig_bool_options() {
47 let dotconfig = env::var("DOTCONFIG").expect("DOTCONFIG must be set by wrapper");
48
49 println!("cargo:rerun-if-env-changed=DOTCONFIG");
51 println!("cargo-rerun-if-changed={}", dotconfig);
52
53 extract_kconfig_bool_options(&dotconfig)
54 .expect("failed to extract flags from .config")
55 .iter()
56 .for_each(|flag| println!("cargo:rustc-cfg={flag}"));
57}
58
59pub fn build_kconfig_mod() {
64 let dotconfig = env::var("DOTCONFIG").expect("DOTCONFIG must be set by wrapper");
65 let outdir = env::var("OUT_DIR").expect("OUT_DIR must be set");
66
67 let config_hex = Regex::new(r"^(CONFIG_.*)=(0x[0-9a-fA-F]+)$").unwrap();
69 let config_int = Regex::new(r"^(CONFIG_.*)=(-?[1-9][0-9]*|0)$").unwrap();
70 let config_str = Regex::new(r#"^(CONFIG_.*)=(".*")$"#).unwrap();
72 let gen_path = Path::new(&outdir).join("kconfig.rs");
73
74 let mut f = File::create(&gen_path).unwrap();
75
76 let file = File::open(&dotconfig).expect("Unable to open dotconfig");
77 for line in BufReader::new(file).lines() {
78 let line = line.expect("reading line from dotconfig");
79 if let Some(caps) = config_hex.captures(&line) {
80 writeln!(&mut f, "#[allow(dead_code)]").unwrap();
81 writeln!(&mut f, "pub const {}: usize = {};", &caps[1], &caps[2]).unwrap();
82 } else if let Some(caps) = config_int.captures(&line) {
83 writeln!(&mut f, "#[allow(dead_code)]").unwrap();
84 writeln!(&mut f, "pub const {}: isize = {};", &caps[1], &caps[2]).unwrap();
85 } else if let Some(caps) = config_str.captures(&line) {
86 writeln!(&mut f, "#[allow(dead_code)]").unwrap();
87 writeln!(&mut f, "pub const {}: &str = {};", &caps[1], &caps[2]).unwrap();
88 }
89 }
90}
91
92fn import_dt() -> DeviceTree {
94 let zephyr_dts = env::var("ZEPHYR_DTS").expect("ZEPHYR_DTS must be set");
95 let gen_include =
96 env::var("BINARY_DIR_INCLUDE_GENERATED").expect("BINARY_DIR_INCLUDE_GENERATED must be set");
97
98 let generated = format!("{}/devicetree_generated.h", gen_include);
99 DeviceTree::new(&zephyr_dts, generated)
100}
101
102pub fn build_dts() {
103 let dt = import_dt();
104
105 let outdir = env::var("OUT_DIR").expect("OUT_DIR must be set");
106 let out_path = Path::new(&outdir).join("devicetree.rs");
107 let mut out = File::create(&out_path).expect("Unable to create devicetree.rs");
108
109 let augments = env::var("DT_AUGMENTS").expect("DT_AUGMENTS must be set");
110 let augments: Vec<String> = augments.split_whitespace().map(String::from).collect();
111
112 println!("cargo:rerun-if-env-changed=DT_AUGMENTS");
114 for name in &augments {
115 println!("cargo:rerun-if-changed={}", name);
116 }
117
118 let mut augs = Vec::new();
119 for aug in &augments {
120 let mut aug = devicetree::load_augments(aug).expect("Loading augment file");
122 augs.append(&mut aug);
123 }
124 let augs: Vec<_> = augs
127 .into_iter()
128 .map(|aug| Box::new(aug) as Box<dyn Augment>)
129 .collect();
130
131 let tokens = dt.to_tokens(&augs);
132 if has_rustfmt() {
133 write_formatted(out, tokens);
134 } else {
135 writeln!(out, "{}", tokens).unwrap();
136 };
137}
138
139pub fn dt_cfgs() {
144 let dt = import_dt();
145 dt.output_node_paths(&mut std::io::stdout()).unwrap();
146}
147
148pub fn has_rustfmt() -> bool {
150 matches!(Command::new("rustfmt").arg("--version").status(), Ok(st) if st.success())
151}
152
153fn write_formatted(file: File, tokens: TokenStream) {
156 let mut rustfmt = Command::new("rustfmt")
157 .args(["--emit", "stdout"])
158 .stdin(Stdio::piped())
159 .stdout(file)
160 .stderr(Stdio::inherit())
161 .spawn()
162 .expect("Failed to run rustfmt");
163 let mut stdin = rustfmt
166 .stdin
167 .as_ref()
168 .expect("Stdin should have been opened by spawn");
169 writeln!(stdin, "{}", tokens).expect("Writing to rustfmt");
170
171 match rustfmt.wait() {
172 Ok(st) if st.success() => (),
173 _ => panic!("Failure running rustfmt"),
174 }
175}