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