#[allow(unused)]
use {
crate::recipe::Recipe,
crate::utils::{scan_dir, scan_dir_for_dir, scan_dir_for_file},
crate::yocto::{DataStore, YoctoVar, YoctoVarName, YoctoVarType},
anyhow::{Context, Error, Result},
jlogger::{jdebug, jerror, jinfo, jwarn},
log::{debug, error, info, warn},
regex::Regex,
std::collections::HashMap,
std::fmt::Display,
std::fs,
std::path::{Path, PathBuf},
};
pub struct Layer {
collection: String,
priority: u32,
machine: Vec<String>,
bbclass: Vec<String>,
bb: HashMap<String, Vec<Recipe>>,
}
impl Layer {
pub fn new(layer_dir: &str, gstore: Option<&DataStore>) -> Result<Self> {
let mut data_store = DataStore::new();
let mut overrides = String::new();
if let Some(d) = gstore {
if let Some(yv) = d.value("OVERRIDES") {
overrides.push_str(yv.value(None))
}
}
let mut yv = YoctoVar::new(Some("LAYERDIR"))?;
yv.set_value(YoctoVarType::Value, layer_dir);
data_store.add(yv);
if let Some(d) = gstore {
if d.value("HOSTTOOLS_DIR").is_none() {
data_store.add(YoctoVar::new(Some("HOSTTOOLS_DIR"))?);
}
}
data_store.from_conf_file(format!("{}/conf/layer.conf", layer_dir).as_str(), gstore)?;
let collection = data_store
.value("BBFILE_COLLECTIONS")
.ok_or(Error::msg(format!(
"No BBFILE_COLLECTIONS in {}",
layer_dir
)))?
.value(Some(&overrides));
overrides.push_str(format!(":{}", collection).as_str());
let priority: u32 = data_store
.value("BBFILE_PRIORITY")
.ok_or(Error::msg(format!("No BBFILE_PRIORITY in {}", layer_dir)))?
.value(Some(&overrides))
.parse()?;
let mut machine = Vec::<String>::new();
let machine_dir_str = format!("{}/conf/machine", layer_dir);
let machine_dir = Path::new(machine_dir_str.as_str());
if machine_dir.is_dir() {
let _ = scan_dir_for_file(machine_dir, &mut |p: PathBuf| -> Result<()> {
if let Some(l) = p.file_name() {
if let Some(f) = l.to_str() {
if f.ends_with("conf") {
machine.push(f.trim_end_matches(".conf").to_string());
} else {
jwarn!("neglect {} for machine", p.to_str().unwrap());
}
}
}
Ok(())
});
}
let mut bbclass = Vec::<String>::new();
let mut bb = HashMap::<String, Vec<Recipe>>::new();
let mut bbfiles = Vec::new();
let yv = data_store
.value("BBFILES")
.ok_or(Error::msg(format!("No BBFILES found in {}", layer_dir)))?;
data_store
.expand_value(yv.value(Some(&overrides)), gstore)?
.split(" ")
.filter(|a| (*a).trim() != "\\" && !(*a).trim().is_empty())
.for_each(|a| {
let t = a
.trim()
.trim_end_matches("\\")
.trim()
.trim_start_matches("${LAYERDIR}/");
bbfiles.push(t.to_string());
});
let mut process = Vec::new();
let mut res = Vec::new();
let mut depth = 0_usize;
for patterns in &bbfiles {
let re_pattern = patterns.replace("*", ".+");
let re = Regex::new(&re_pattern).unwrap();
res.push(re);
let v = re_pattern.split("/").collect::<Vec<&str>>();
if v.len() > depth {
depth = v.len();
}
}
process.push(layer_dir.to_string());
while !process.is_empty() {
let mut next_process = Vec::new();
for entry in &process {
scan_dir(Path::new(entry), &mut |p| {
if p.is_dir() {
let pname = p.to_str().unwrap();
let depth_now = pname.split("/").collect::<Vec<&str>>().len();
if depth_now < depth {
next_process.push(pname.to_string());
}
}
if p.is_file() {
let f = p.to_str().unwrap();
if f.ends_with("bb") || f.ends_with("bbappend") {
for re in &res {
if re.is_match(f) {
let r = Recipe::new(f).unwrap();
let key = r.name().to_string();
if let Some(v) = bb.get_mut(key.as_str()) {
let mut existed = false;
v.iter().for_each(|a| {
if a.path() == r.path() {
existed = true;
}
});
if !existed {
v.push(r);
}
} else {
let mut v = Vec::new();
v.push(r);
bb.insert(key, v);
}
break;
}
}
}
}
true
});
}
process = next_process;
}
let bbclass_dir_str = format!("{}/classes", layer_dir);
let bbclass_dir = Path::new(bbclass_dir_str.as_str());
if bbclass_dir.is_dir() {
let _ = scan_dir_for_file(bbclass_dir, &mut |p| {
let filename = p.file_name().unwrap().to_str().unwrap();
if filename.ends_with(".bbclass") {
bbclass.push(p.to_str().unwrap().to_string());
}
Ok(())
});
}
Ok(Layer {
collection: collection.to_string(),
priority,
machine,
bbclass,
bb,
})
}
pub fn collection(&self) -> &str {
self.collection.as_str()
}
pub fn recipes(&self) -> &HashMap<String, Vec<Recipe>> {
&self.bb
}
}
impl Display for Layer {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let mut output = String::new();
output.push_str(format!("Collection: {}\n", self.collection).as_str());
output.push_str(format!("Priority: {}\n", self.priority).as_str());
if !self.machine.is_empty() {
output.push_str(format!("Machine ({}):\n", self.machine.len()).as_str());
for m in &self.machine {
output.push_str(format!(" {}\n", m).as_str());
}
}
if !self.bb.is_empty() {
output.push_str(format!("BB ({}):\n", self.bb.len()).as_str());
for k in self.bb.keys() {
output.push_str(format!(" {}\n", k).as_str());
}
}
if !self.bbclass.is_empty() {
output.push_str(format!("BBCLASS ({}):\n", self.bbclass.len()).as_str());
for b in &self.bbclass {
let p = Path::new(b).file_name().unwrap().to_str().unwrap();
output.push_str(format!(" {}\n", p).as_str());
}
}
write!(f, "{}", output)
}
}