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 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}