#[cfg(feature = "debian")]
use crate::dependencies::debian::DebianDependency;
#[cfg(feature = "debian")]
use crate::dependencies::python::PythonPackageDependency;
use crate::dependencies::BinaryDependency;
use crate::dependencies::Dependency;
use crate::dependencies::PkgConfigDependency;
use crate::session::Session;
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct VagueDependency {
pub name: String,
pub minimum_version: Option<String>,
}
impl VagueDependency {
pub fn new(name: &str, minimum_version: Option<&str>) -> Self {
Self {
name: name.to_string(),
minimum_version: minimum_version.map(|s| s.trim().to_string()),
}
}
pub fn simple(name: &str) -> Self {
Self {
name: name.to_string(),
minimum_version: None,
}
}
pub fn expand(&self) -> Vec<Box<dyn Dependency>> {
let mut ret: Vec<Box<dyn Dependency>> = vec![];
let lcname = self.name.to_lowercase();
if !self.name.contains(' ') {
ret.push(Box::new(BinaryDependency::new(&self.name)) as Box<dyn Dependency>);
ret.push(Box::new(BinaryDependency::new(&self.name)) as Box<dyn Dependency>);
ret.push(Box::new(PkgConfigDependency::new(
&self.name.clone(),
self.minimum_version.clone().as_deref(),
)) as Box<dyn Dependency>);
if lcname != self.name {
ret.push(Box::new(BinaryDependency::new(&lcname)) as Box<dyn Dependency>);
ret.push(Box::new(BinaryDependency::new(&lcname)) as Box<dyn Dependency>);
ret.push(Box::new(PkgConfigDependency::new(
&lcname,
self.minimum_version.clone().as_deref(),
)) as Box<dyn Dependency>);
}
#[cfg(feature = "debian")]
{
ret.push(Box::new(
if let Some(minimum_version) = &self.minimum_version {
DebianDependency::new_with_min_version(
&self.name,
&minimum_version.parse().unwrap(),
)
} else {
DebianDependency::new(&self.name)
},
));
let devname = if lcname.starts_with("lib") {
format!("{}-dev", lcname)
} else {
format!("lib{}-dev", lcname)
};
ret.push(if let Some(minimum_version) = &self.minimum_version {
Box::new(DebianDependency::new_with_min_version(
&devname,
&minimum_version.parse().unwrap(),
))
} else {
Box::new(DebianDependency::new(&devname))
});
}
}
ret
}
}
impl Dependency for VagueDependency {
fn family(&self) -> &'static str {
"vague"
}
fn present(&self, session: &dyn Session) -> bool {
self.expand().iter().any(|d| d.present(session))
}
fn project_present(&self, session: &dyn Session) -> bool {
self.expand().iter().any(|d| d.project_present(session))
}
fn as_any(&self) -> &dyn std::any::Any {
self
}
}
#[cfg(feature = "debian")]
fn known_vague_dep_to_debian(name: &str) -> Option<&str> {
match name {
"the Gnu Scientific Library" => Some("libgsl-dev"),
"the required FreeType library" => Some("libfreetype-dev"),
"the Boost C++ libraries" => Some("libboost-dev"),
"the sndfile library" => Some("libsndfile-dev"),
"PythonLibs" => Some("libpython3-dev"),
"PythonInterp" => Some("python3"),
"ZLIB" => Some("libz3-dev"),
"Osmium" => Some("libosmium2-dev"),
"glib" => Some("libglib2.0-dev"),
"OpenGL" => Some("libgl-dev"),
"Python" => Some("libpython3-dev"),
"Lua" => Some("liblua5.4-dev"),
_ => None,
}
}
#[cfg(feature = "debian")]
fn resolve_vague_dep_req(
apt_mgr: &crate::debian::apt::AptManager,
req: VagueDependency,
) -> Vec<DebianDependency> {
let name = req.name.as_str();
let mut options = vec![];
if name.contains(" or ") {
for entry in name.split(" or ") {
options.extend(resolve_vague_dep_req(
apt_mgr,
VagueDependency {
name: entry.to_string(),
minimum_version: req.minimum_version.clone(),
},
));
}
}
if let Some(dep) = known_vague_dep_to_debian(name) {
options.push(
if let Some(minimum_version) = req.minimum_version.as_ref() {
DebianDependency::new_with_min_version(dep, &minimum_version.parse().unwrap())
} else {
DebianDependency::new(dep)
},
);
}
for x in req.expand() {
options.extend(crate::debian::apt::dependency_to_possible_deb_dependencies(
apt_mgr,
x.as_ref(),
));
}
if let Some(rest) = name.strip_prefix("GNU ") {
options.extend(resolve_vague_dep_req(
apt_mgr,
VagueDependency::simple(rest),
));
}
if name.starts_with("py") || name.ends_with("py") {
let dep = if let Some(min_version) = req.minimum_version.as_ref() {
PythonPackageDependency::new_with_min_version(name, min_version)
} else {
PythonPackageDependency::simple(name)
};
options.extend(crate::debian::apt::dependency_to_possible_deb_dependencies(
apt_mgr, &dep,
));
}
if options.is_empty() {
use std::path::Path;
let paths = [
Path::new("/usr/lib")
.join(".*")
.join("pkgconfig")
.join(format!("{}-.*\\.pc", regex::escape(&req.name))),
Path::new("/usr/lib/pkgconfig").join(format!("{}-.*\\.pc", regex::escape(&req.name))),
];
options.extend(
apt_mgr
.get_packages_for_paths(
paths.iter().map(|x| x.to_str().unwrap()).collect(),
true,
true,
)
.unwrap()
.iter()
.map(|x| DebianDependency::new(x)),
)
}
options
}
#[cfg(feature = "debian")]
impl crate::dependencies::debian::IntoDebianDependency for VagueDependency {
fn try_into_debian_dependency(
&self,
apt_mgr: &crate::debian::apt::AptManager,
) -> std::option::Option<std::vec::Vec<crate::dependencies::debian::DebianDependency>> {
Some(resolve_vague_dep_req(apt_mgr, self.clone()))
}
}
impl crate::buildlog::ToDependency
for buildlog_consultant::problems::common::MissingVagueDependency
{
fn to_dependency(&self) -> Option<Box<dyn Dependency>> {
Some(Box::new(VagueDependency::new(
&self.name,
self.minimum_version.as_deref(),
)))
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::any::Any;
#[test]
fn test_vague_dependency_new() {
let dep = VagueDependency::new("zlib", Some("1.2.11"));
assert_eq!(dep.name, "zlib");
assert_eq!(dep.minimum_version, Some("1.2.11".to_string()));
}
#[test]
fn test_vague_dependency_new_trims_version() {
let dep = VagueDependency::new("zlib", Some(" 1.2.11 "));
assert_eq!(dep.minimum_version, Some("1.2.11".to_string()));
}
#[test]
fn test_vague_dependency_simple() {
let dep = VagueDependency::simple("zlib");
assert_eq!(dep.name, "zlib");
assert_eq!(dep.minimum_version, None);
}
#[test]
fn test_vague_dependency_family() {
let dep = VagueDependency::simple("zlib");
assert_eq!(dep.family(), "vague");
}
#[test]
fn test_vague_dependency_as_any() {
let dep = VagueDependency::simple("zlib");
let any_dep: &dyn Any = dep.as_any();
assert!(any_dep.downcast_ref::<VagueDependency>().is_some());
}
#[test]
fn test_vague_dependency_expand() {
let dep = VagueDependency::simple("zlib");
let expanded = dep.expand();
assert!(expanded.iter().any(|d| d.family() == "binary"
&& d.as_any()
.downcast_ref::<BinaryDependency>()
.map(|bd| bd.binary_name == "zlib")
.unwrap_or(false)));
assert!(expanded.iter().any(|d| d.family() == "pkg-config"
&& d.as_any()
.downcast_ref::<PkgConfigDependency>()
.map(|pd| pd.module == "zlib")
.unwrap_or(false)));
assert!(expanded.iter().any(|d| d.family() == "binary"
&& d.as_any()
.downcast_ref::<BinaryDependency>()
.map(|bd| bd.binary_name == "zlib")
.unwrap_or(false)));
}
#[test]
fn test_vague_dependency_expand_with_spaces() {
let dep = VagueDependency::simple("zlib library");
let expanded = dep.expand();
assert!(expanded.is_empty());
}
#[cfg(feature = "debian")]
#[test]
fn test_known_vague_dep_to_debian() {
assert_eq!(
known_vague_dep_to_debian("the Gnu Scientific Library"),
Some("libgsl-dev")
);
assert_eq!(
known_vague_dep_to_debian("the required FreeType library"),
Some("libfreetype-dev")
);
assert_eq!(
known_vague_dep_to_debian("the Boost C++ libraries"),
Some("libboost-dev")
);
assert_eq!(
known_vague_dep_to_debian("PythonLibs"),
Some("libpython3-dev")
);
assert_eq!(known_vague_dep_to_debian("Python"), Some("libpython3-dev"));
assert_eq!(known_vague_dep_to_debian("ZLIB"), Some("libz3-dev"));
assert_eq!(known_vague_dep_to_debian("OpenGL"), Some("libgl-dev"));
assert_eq!(known_vague_dep_to_debian("unknown_library"), None);
assert_eq!(known_vague_dep_to_debian(""), None);
}
}