ydevlib/
layer.rs

1#[allow(unused)]
2use {
3    crate::recipe::Recipe,
4    crate::utils::{scan_dir, scan_dir_for_dir, scan_dir_for_file},
5    crate::yocto::{DataStore, YoctoVar, YoctoVarName, YoctoVarType},
6    anyhow::{Context, Error, Result},
7    jlogger::{jdebug, jerror, jinfo, jwarn},
8    log::{debug, error, info, warn},
9    regex::Regex,
10    std::collections::HashMap,
11    std::fmt::Display,
12    std::fs,
13    std::path::{Path, PathBuf},
14};
15
16pub struct Layer {
17    collection: String,
18    priority: u32,
19    machine: Vec<String>,
20    bbclass: Vec<String>,
21    bb: HashMap<String, Vec<Recipe>>,
22}
23
24impl Layer {
25    pub fn new(layer_dir: &str, gstore: Option<&DataStore>) -> Result<Self> {
26        let mut data_store = DataStore::new();
27
28        let mut overrides = String::new();
29
30        if let Some(d) = gstore {
31            if let Some(yv) = d.value("OVERRIDES") {
32                overrides.push_str(yv.value(None))
33            }
34        }
35
36        /* LAYERDIR is a special variable for current directory */
37        let mut yv = YoctoVar::new(Some("LAYERDIR"))?;
38        yv.set_value(YoctoVarType::Value, layer_dir);
39        data_store.add(yv);
40
41        if let Some(d) = gstore {
42            if d.value("HOSTTOOLS_DIR").is_none() {
43                data_store.add(YoctoVar::new(Some("HOSTTOOLS_DIR"))?);
44            }
45        }
46
47        data_store.from_conf_file(format!("{}/conf/layer.conf", layer_dir).as_str(), gstore)?;
48
49        let collection = data_store
50            .value("BBFILE_COLLECTIONS")
51            .ok_or(Error::msg(format!(
52                "No BBFILE_COLLECTIONS in {}",
53                layer_dir
54            )))?
55            .value(Some(&overrides));
56
57        overrides.push_str(format!(":{}", collection).as_str());
58
59        let priority: u32 = data_store
60            .value("BBFILE_PRIORITY")
61            .ok_or(Error::msg(format!("No BBFILE_PRIORITY in {}", layer_dir)))?
62            .value(Some(&overrides))
63            .parse()?;
64
65        let mut machine = Vec::<String>::new();
66        let machine_dir_str = format!("{}/conf/machine", layer_dir);
67        let machine_dir = Path::new(machine_dir_str.as_str());
68        if machine_dir.is_dir() {
69            let _ = scan_dir_for_file(machine_dir, &mut |p: PathBuf| -> Result<()> {
70                if let Some(l) = p.file_name() {
71                    if let Some(f) = l.to_str() {
72                        if f.ends_with("conf") {
73                            machine.push(f.trim_end_matches(".conf").to_string());
74                        } else {
75                            jwarn!("neglect {} for machine", p.to_str().unwrap());
76                        }
77                    }
78                }
79                Ok(())
80            });
81        }
82
83        let mut bbclass = Vec::<String>::new();
84        let mut bb = HashMap::<String, Vec<Recipe>>::new();
85        let mut bbfiles = Vec::new();
86        let yv = data_store
87            .value("BBFILES")
88            .ok_or(Error::msg(format!("No BBFILES found in {}", layer_dir)))?;
89
90        data_store
91            .expand_value(yv.value(Some(&overrides)), gstore)?
92            .split(" ")
93            .filter(|a| (*a).trim() != "\\" && !(*a).trim().is_empty())
94            .for_each(|a| {
95                let t = a
96                    .trim()
97                    .trim_end_matches("\\")
98                    .trim()
99                    .trim_start_matches("${LAYERDIR}/");
100                bbfiles.push(t.to_string());
101            });
102
103        let mut process = Vec::new();
104
105        let mut res = Vec::new();
106        let mut depth = 0_usize;
107
108        for patterns in &bbfiles {
109            let re_pattern = patterns.replace("*", ".+");
110            let re = Regex::new(&re_pattern).unwrap();
111            res.push(re);
112
113            let v = re_pattern.split("/").collect::<Vec<&str>>();
114            if v.len() > depth {
115                depth = v.len();
116            }
117        }
118
119        process.push(layer_dir.to_string());
120        while !process.is_empty() {
121            let mut next_process = Vec::new();
122            for entry in &process {
123                scan_dir(Path::new(entry), &mut |p| {
124                    if p.is_dir() {
125                        let pname = p.to_str().unwrap();
126                        let depth_now = pname.split("/").collect::<Vec<&str>>().len();
127                        if depth_now < depth {
128                            next_process.push(pname.to_string());
129                        }
130                    }
131
132                    if p.is_file() {
133                        let f = p.to_str().unwrap();
134                        if f.ends_with("bb") || f.ends_with("bbappend") {
135                            for re in &res {
136                                if re.is_match(f) {
137                                    let r = Recipe::new(f).unwrap();
138                                    let key = r.name().to_string();
139                                    if let Some(v) = bb.get_mut(key.as_str()) {
140                                        let mut existed = false;
141
142                                        v.iter().for_each(|a| {
143                                            if a.path() == r.path() {
144                                                existed = true;
145                                            }
146                                        });
147
148                                        if !existed {
149                                            v.push(r);
150                                        }
151                                    } else {
152                                        let mut v = Vec::new();
153                                        v.push(r);
154                                        bb.insert(key, v);
155                                    }
156
157                                    break;
158                                }
159                            }
160                        }
161                    }
162                    true
163                });
164            }
165            process = next_process;
166        }
167
168        let bbclass_dir_str = format!("{}/classes", layer_dir);
169        let bbclass_dir = Path::new(bbclass_dir_str.as_str());
170        if bbclass_dir.is_dir() {
171            let _ = scan_dir_for_file(bbclass_dir, &mut |p| {
172                let filename = p.file_name().unwrap().to_str().unwrap();
173                if filename.ends_with(".bbclass") {
174                    bbclass.push(p.to_str().unwrap().to_string());
175                }
176                Ok(())
177            });
178        }
179
180        Ok(Layer {
181            collection: collection.to_string(),
182            priority,
183            machine,
184            bbclass,
185            bb,
186        })
187    }
188
189    pub fn collection(&self) -> &str {
190        self.collection.as_str()
191    }
192
193    pub fn recipes(&self) -> &HashMap<String, Vec<Recipe>> {
194        &self.bb
195    }
196}
197
198impl Display for Layer {
199    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
200        let mut output = String::new();
201        output.push_str(format!("Collection: {}\n", self.collection).as_str());
202        output.push_str(format!("Priority: {}\n", self.priority).as_str());
203        if !self.machine.is_empty() {
204            output.push_str(format!("Machine ({}):\n", self.machine.len()).as_str());
205            for m in &self.machine {
206                output.push_str(format!("  {}\n", m).as_str());
207            }
208        }
209
210        if !self.bb.is_empty() {
211            output.push_str(format!("BB ({}):\n", self.bb.len()).as_str());
212            for k in self.bb.keys() {
213                output.push_str(format!("  {}\n", k).as_str());
214            }
215        }
216
217        if !self.bbclass.is_empty() {
218            output.push_str(format!("BBCLASS ({}):\n", self.bbclass.len()).as_str());
219            for b in &self.bbclass {
220                let p = Path::new(b).file_name().unwrap().to_str().unwrap();
221                output.push_str(format!("  {}\n", p).as_str());
222            }
223        }
224
225        write!(f, "{}", output)
226    }
227}