From a3a40b7eb07a6cbf8a627bddaf181bac909b3854 Mon Sep 17 00:00:00 2001 From: eri Date: Thu, 12 Dec 2024 21:05:46 +0100 Subject: [PATCH] polishing touches --- Cargo.toml | 6 +- build.rs | 18 +- meta/Cargo.toml | 5 +- meta/src/binary.rs | 109 ++-- meta/src/error.rs | 1 - meta/src/lib.rs | 6 +- meta/src/parse.rs | 270 ++++----- meta/src/test.rs | 573 +++++++++++++++++- meta/src/test/conditional.rs | 110 ---- meta/src/test/metadata.rs | 428 ------------- meta/src/utils.rs | 2 +- src/lib.rs | 101 +-- src/test.rs | 60 +- meta/src/test/binary.rs => src/test_binary.rs | 304 +++++++--- .../src/test/files => src/tests}/test.tar.gz | Bin .../src/test/files => src/tests}/test.tar.xz | Bin {meta/src/test/files => src/tests}/test.zip | Bin src/tests/uninstalled/info.toml | 1 + .../tests/uninstalled}/lib/libtest.a | 0 .../tests/uninstalled}/lib/pkgconfig/test.pc | 0 20 files changed, 1082 insertions(+), 912 deletions(-) delete mode 100644 meta/src/test/conditional.rs delete mode 100644 meta/src/test/metadata.rs rename meta/src/test/binary.rs => src/test_binary.rs (53%) rename {meta/src/test/files => src/tests}/test.tar.gz (100%) rename {meta/src/test/files => src/tests}/test.tar.xz (100%) rename {meta/src/test/files => src/tests}/test.zip (100%) create mode 100644 src/tests/uninstalled/info.toml rename {meta/src/test/files => src/tests/uninstalled}/lib/libtest.a (100%) rename {meta/src/test/files => src/tests/uninstalled}/lib/pkgconfig/test.pc (100%) diff --git a/Cargo.toml b/Cargo.toml index b9cbb45..a8447bc 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -36,6 +36,7 @@ documentation.workspace = true readme = "README.md" [dependencies] +system-deps-meta = { workspace = true, optional = true } pkg-config = "0.3.25" toml = { version = "0.8", default-features = false, features = ["parse"] } version-compare = "0.2" @@ -46,12 +47,15 @@ cfg-expr = { version = "0.17", features = ["targets"] } system-deps-meta = { workspace = true, optional = true } [dev-dependencies] +system-deps-meta = { workspace = true, features = ["test"] } itertools = "0.13" assert_matches = "1.5" +tiny_http = "0.12" [features] default = [ ] +# How to do this using resolver v2? Since features are separated binary = [ "system-deps-meta/binary" ] -gx = [ "system-deps-meta/gz" ] +gz = [ "system-deps-meta/gz" ] xz = [ "system-deps-meta/xz" ] zip = [ "system-deps-meta/zip" ] diff --git a/build.rs b/build.rs index 3756bc9..d7c1679 100644 --- a/build.rs +++ b/build.rs @@ -5,28 +5,28 @@ pub fn main() { #[cfg(feature = "binary")] mod binary { - use std::path::Path; + use std::{fs, path::Path}; use system_deps_meta::{ binary::{merge_binary, Paths}, error::{BinaryError, Error}, - parse::MetadataList, + parse::read_metadata, BUILD_MANIFEST, BUILD_TARGET_DIR, }; // Add pkg-config paths to the overrides pub fn build() -> Result<(), Error> { // Read metadata from the crate graph - let metadata = MetadataList::new(BUILD_MANIFEST, "system-deps")?; + let metadata = read_metadata(BUILD_MANIFEST, "system-deps", merge_binary)?; // Download the binaries and get their pkg_config paths - let paths: Paths = metadata.build(merge_binary)?.into_iter().collect(); + let paths: Paths = metadata.into_iter().collect(); // Write the binary paths to a file for later use - let dest_path = Path::new(BUILD_TARGET_DIR).join("binary_config.rs"); - println!("cargo:rustc-env=BINARY_CONFIG={}", dest_path.display()); - paths - .build(dest_path) - .map_err(|e| BinaryError::InvalidDirectory(e).into()) + let dest = Path::new(BUILD_TARGET_DIR).join("paths.toml"); + fs::write(&dest, paths.to_string()?).map_err(BinaryError::InvalidDirectory)?; + println!("cargo:rustc-env=BINARY_PATHS={}", dest.display()); + + Ok(()) } } diff --git a/meta/Cargo.toml b/meta/Cargo.toml index d573c03..015d03a 100644 --- a/meta/Cargo.toml +++ b/meta/Cargo.toml @@ -20,12 +20,9 @@ xz = { version = "0.1", optional = true } tar = { version = "0.4", optional = true } zip = { version = "2.2", optional = true } -[dev-dependencies] -tiny_http = "0.12" - [features] -default = [ ] binary = [ "dep:sha256", "dep:attohttpc" ] gz = [ "dep:flate2", "dep:tar" ] xz = [ "dep:xz", "dep:tar" ] zip = [ "dep:zip" ] +test = [ ] diff --git a/meta/src/binary.rs b/meta/src/binary.rs index 01fe701..4fd3681 100644 --- a/meta/src/binary.rs +++ b/meta/src/binary.rs @@ -2,14 +2,13 @@ use std::{ collections::HashMap, convert::{TryFrom, TryInto}, fs, - io::{self, Write}, iter::FromIterator, path::{Path, PathBuf}, sync::{Mutex, OnceLock}, thread, }; -use serde::Deserialize; +use serde::{Deserialize, Serialize}; use toml::{Table, Value}; use crate::{ @@ -47,9 +46,8 @@ impl TryFrom<&Path> for Extension { return Ok(Self::Folder); }; let Some(ext) = path.extension() else { - return Err(BinaryError::UnsupportedExtension("".into())); + return Err(BinaryError::UnsupportedExtension("".into())); }; - match ext { #[cfg(feature = "gz")] e if e == "gz" || e == "tgz" => Ok(Extension::TarGz), @@ -123,8 +121,12 @@ pub struct UrlBinary { paths: Option>, } -#[derive(Debug)] -pub struct Paths(HashMap>); +#[derive(Debug, Default, Deserialize, Serialize)] +pub struct Paths { + paths: HashMap>, + follows: HashMap, + wildcards: HashMap, +} impl FromIterator<(String, T)> for Paths where @@ -137,7 +139,7 @@ where /// decompressing errors. While it would possible to pass these values to the caller, in this /// particular instance it would be hard to use this trait and it complicates error management. fn from_iter>(binaries: I) -> Self { - let mut paths: HashMap> = HashMap::new(); + let mut res = Self::default(); let (url_binaries, follow_binaries): (Vec<_>, Vec<_>) = binaries .into_iter() @@ -152,7 +154,7 @@ where }; let dst = Path::new(&crate::BUILD_TARGET_DIR).join(&name); - paths.insert( + res.paths.insert( name, bin.paths.iter().flatten().map(|p| dst.join(p)).collect(), ); @@ -163,51 +165,70 @@ where // Allow multiple downloads at the same time if !valid { - s.spawn(move || { - make_available(bin.url, bin.checksum, &dst).map_err(|e| panic!("{}", e)) - }); + s.spawn(move || make_available(bin, &dst).map_err(|e| panic!("{}", e))); } } }); + // Check if the package provided extra configuration + for (name, list) in res.paths.iter_mut() { + let dst = Path::new(&crate::BUILD_TARGET_DIR).join(name); + let Ok(info) = fs::read_to_string(dst.join("info.toml")) else { + continue; + }; + let Ok(table) = toml::from_str::(&info) else { + continue; + }; + if let Some(Value::Array(paths)) = table.get("paths") { + for p in paths.iter().filter_map(|p| p.as_str()) { + let p = dst.join(p); + if !list.contains(&p) { + list.push(p); + } + } + } + } + // Binaries that follow others for (name, bin) in follow_binaries { let Binary::Follow(bin) = bin else { unreachable!(); }; - let other = paths - .get(&bin.follows) - .ok_or_else(|| BinaryError::InvalidFollows(name.clone(), bin.follows)) - .unwrap_or_else(|e| panic!("{}", e)); - paths.insert(name, other.clone()); + if !res.paths.contains_key(&bin.follows) { + panic!("{}", BinaryError::InvalidFollows(name, bin.follows)); + }; + match name.strip_suffix("*") { + Some(wildcard) => res.wildcards.insert(wildcard.into(), bin.follows), + None => res.follows.insert(name, bin.follows), + }; } - Self(paths) + res } } impl Paths { - pub fn get(&self, key: &str) -> Result<&Vec, Error> { - self.0.get(key).ok_or(Error::PackageNotFound(key.into())) - } + /// Returns the list of paths for a certain package. Matches wildcards but they never have + /// priority over explicit urls or follows, even if they are defined higher in the hierarchy. + pub fn get(&self, key: &str) -> Option<&Vec> { + if let Some(paths) = self.paths.get(key) { + return Some(paths); + }; - pub fn build(self, path: impl AsRef) -> Result<(), io::Error> { - let mut f = fs::File::create(path.as_ref())?; - writeln!(f, "/// List of pkg_config paths provided by packages")?; - writeln!(f, "pub fn get_path(_name: &str) -> &[&'static str] {{")?; - - if self.0.is_empty() { - writeln!(f, "&[]")?; - } else { - writeln!(f, "match _name {{")?; - for (name, list) in self.0 { - writeln!(f, r#""{}" => &{:?},"#, name, list)?; - } - writeln!(f, "_ => &[]\n}}")?; + if let Some(follows) = self.follows.get(key) { + return self.paths.get(follows); }; - write!(f, "}}")?; - Ok(()) + self.wildcards.iter().find_map(|(k, v)| { + key.starts_with(k) + .then_some(v) + .and_then(|v| self.paths.get(v)) + }) + } + + /// Serializes the path list. + pub fn to_string(&self) -> Result { + Ok(toml::to_string(self)?) } } @@ -237,19 +258,23 @@ fn check_valid_dir(dst: &Path, checksum: Option<&str>) -> Result, dst: &Path) -> Result<(), BinaryError> { +fn make_available(bin: UrlBinary, dst: &Path) -> Result<(), BinaryError> { + // TODO: Find a way of printing download/decompress progress static LOCK: OnceLock> = OnceLock::new(); // Check whether the file is local or not - let (url, local) = match url.strip_prefix("file://") { + let (url, local) = match bin.url.strip_prefix("file://") { Some(file) => (file, true), - None => (url.as_str(), false), + None => (bin.url.as_str(), false), }; let ext = url.try_into()?; // Check if it is a folder and it can be symlinked if matches!(ext, Extension::Folder) { + if !local { + return Err(BinaryError::UnsupportedExtension("".into())); + } let _l = LOCK.get_or_init(|| Mutex::new(())).lock(); if !dst.read_link().is_ok_and(|l| l == Path::new(url)) { if dst.is_symlink() { @@ -267,16 +292,17 @@ fn make_available(url: String, checksum: Option, dst: &Path) -> Result<( let file = if local { fs::read(url).map_err(BinaryError::LocalFileError)? } else { - attohttpc::get(url).send()?.bytes()? + let res = attohttpc::get(url).send()?; + res.error_for_status()?.bytes()? }; // Verify the checksum let calculated = sha256::digest(&*file); - let checksum = match checksum { + let checksum = match bin.checksum { Some(ch) if *ch == calculated => Ok(ch), _ => Err(BinaryError::InvalidChecksum( url.into(), - checksum.unwrap_or("".into()), + bin.checksum.unwrap_or("".into()), calculated, )), }?; @@ -285,6 +311,7 @@ fn make_available(url: String, checksum: Option, dst: &Path) -> Result<( // Decompress the binary archive decompress(&file, dst, ext)?; + Ok(()) } diff --git a/meta/src/error.rs b/meta/src/error.rs index 3f5b248..f3f450a 100644 --- a/meta/src/error.rs +++ b/meta/src/error.rs @@ -10,7 +10,6 @@ pub enum Error { CfgNotObject(String), /// Error while deserializing metadata. DeserializeError(toml::de::Error), - // TODO: Add details /// Merging two incompatible branches. IncompatibleMerge, /// Error while parsing the cfg() expression. diff --git a/meta/src/lib.rs b/meta/src/lib.rs index bd00dff..241b324 100644 --- a/meta/src/lib.rs +++ b/meta/src/lib.rs @@ -1,3 +1,5 @@ +//#![warn(missing_docs)] + pub mod error; pub mod parse; pub mod utils; @@ -5,8 +7,8 @@ pub mod utils; #[cfg(feature = "binary")] pub mod binary; -#[cfg(test)] -mod test; +#[cfg(any(test, feature = "test"))] +pub mod test; /// Path to the top level Cargo.toml. pub const BUILD_MANIFEST: &str = env!("BUILD_MANIFEST"); diff --git a/meta/src/parse.rs b/meta/src/parse.rs index a868fbd..fd7ae4b 100644 --- a/meta/src/parse.rs +++ b/meta/src/parse.rs @@ -11,173 +11,159 @@ use toml::Table; use crate::{error::Error, utils::reduce}; /// Stores a section of metadata found in one package. -#[derive(Debug, Default, Serialize)] +#[derive(Clone, Debug, Default, Serialize)] pub struct MetadataNode { /// Deserialized metadata. table: Table, /// The parents of this package. parents: BTreeSet, + /// The number of children. + children: usize, } -/// Graph like structure that stores the package nodes that have a metadata entry. -#[derive(Debug, Serialize)] -pub struct MetadataList { - /// Stores the metadata of one package. - pub nodes: HashMap, - /// Packages without dependencies. - pub leaves: HashSet, -} +/// Recursively read dependency manifests to find metadata matching a key using cargo_metadata. +/// +/// ```toml +/// [package.metadata.section] +/// some_value = ... +/// other_value = ... +/// ``` +pub fn read_metadata( + manifest: impl AsRef, + section: &str, + merge: impl Fn(&mut Table, Table, bool) -> Result<(), Error>, +) -> Result { + let data = MetadataCommand::new() + .manifest_path(manifest.as_ref()) + .exec() + .unwrap(); + + // Create the root node from the workspace metadata + let value = data.workspace_metadata.get(section).cloned(); + let root_node = MetadataNode { + table: reduce( + value + .and_then(|v| Table::try_from(v).ok()) + .unwrap_or_default(), + )?, + ..Default::default() + }; + + // Use the root package or all the workspace packages as a starting point + let mut packages: VecDeque<_> = if let Some(root) = data.root_package() { + [(root, "")].into() + } else { + data.workspace_packages() + .into_iter() + .zip(iter::repeat("")) + .collect() + }; + + let mut nodes = HashMap::from([("", root_node)]); + + // Iterate through the dependency tree to visit all packages + let mut visited = HashSet::new(); + while let Some((pkg, parent)) = packages.pop_front() { + let name = pkg.name.as_str(); + + // If we already handled this node, update parents and keep going + if !visited.insert(name) { + if let Some(node) = nodes.get_mut(name) { + if node.parents.insert(parent.into()) { + if let Some(p) = nodes.get_mut(parent) { + p.children += 1 + } + } + } + continue; + } -impl MetadataList { - /// Recursively read dependency manifests to find metadata matching a key using cargo_metadata. - /// - /// ```toml - /// [package.metadata.section] - /// some_value = ... - /// other_value = ... - /// ``` - pub fn new(manifest: impl AsRef, section: &str) -> Result { - let data = MetadataCommand::new() - .manifest_path(manifest.as_ref()) - .exec() - .unwrap(); - - // Create the root node from the workspace metadata - let value = data.workspace_metadata.get(section).cloned(); - let root_node = MetadataNode { - table: reduce( - value - .and_then(|v| Table::try_from(v).ok()) - .unwrap_or_default(), - )?, - ..Default::default() + // Keep track of the local manifests to see if they change + if pkg + .manifest_path + .starts_with(manifest.as_ref().parent().unwrap()) + { + println!("cargo:rerun-if-changed={}", pkg.manifest_path); }; - // Use the root package or all the workspace packages as a starting point - let mut packages: VecDeque<_> = if let Some(root) = data.root_package() { - [(root, "")].into() - } else { - data.workspace_packages() - .into_iter() - .zip(iter::repeat("")) - .collect() + // Get `package.metadata.section` and add it to the metadata graph + let node = match (nodes.get_mut(name), pkg.metadata.get(section).cloned()) { + (None, Some(s)) => { + let node = MetadataNode { + table: reduce(Table::try_from(s)?)?, + ..Default::default() + }; + nodes.insert(name, node); + nodes.get_mut(name) + } + (n, _) => n, }; - let mut res = Self { - nodes: HashMap::from([("".into(), root_node)]), - leaves: HashSet::from(["".into()]), + // Update parents + let next_parent = if let Some(node) = node { + if node.parents.insert(parent.into()) { + if let Some(p) = nodes.get_mut(parent) { + p.children += 1 + } + } + name + } else { + parent }; - // Iterate through the dependency tree to visit all packages - let mut visited = HashSet::new(); - while let Some((pkg, parent)) = packages.pop_front() { - // If we already handled this node, update parents and keep going - if !visited.insert(&pkg.name) { - if let Some(node) = res.nodes.get_mut(&pkg.name) { - node.parents.insert(parent.into()); - res.leaves.remove(parent); - } + // Add dependencies to the queue + for dep in &pkg.dependencies { + if !matches!(dep.kind, DependencyKind::Normal) { continue; } - - // Keep track of the local manifests to see if they change - if pkg - .manifest_path - .starts_with(manifest.as_ref().parent().unwrap()) - { - println!("cargo:rerun-if-changed={}", pkg.manifest_path); + if let Some(dep_pkg) = data.packages.iter().find(|p| p.name == dep.name) { + packages.push_back((dep_pkg, next_parent)); }; + } + } - // Get `package.metadata.section` and add it to the metadata graph - let node = match ( - res.nodes.get_mut(&pkg.name), - pkg.metadata.get(section).cloned(), - ) { - (None, Some(s)) => { - let node = MetadataNode { - table: reduce(Table::try_from(s)?)?, - ..Default::default() - }; - res.leaves.insert(pkg.name.clone()); - res.nodes.insert(pkg.name.clone(), node); - res.nodes.get_mut(&pkg.name) - } - (n, _) => n, + // Now that the tree is built, apply the reducing rules + let mut res = Table::new(); + let mut curr = Table::new(); + + // Initialize the queue from the leaves + // NOTE: Use `extract_if` when it is available https://github.com/rust-lang/rust/issues/43244 + let mut queue = VecDeque::new(); + let mut nodes: HashMap<&str, MetadataNode> = nodes + .into_iter() + .filter_map(|(k, v)| { + if v.children == 0 { + queue.push_back(v); + None + } else { + Some((k, v)) + } + }) + .collect(); + + while let Some(node) = queue.pop_front() { + // Push the parents to the queue, avoid unnecessary clones + for p in node.parents.iter().rev() { + let Some(parent) = nodes.get_mut(p.as_str()) else { + return Err(Error::PackageNotFound(p.into())); }; - - // Update parents - let next_parent = if let Some(node) = node { - node.parents.insert(parent.into()); - res.leaves.remove(parent); - pkg.name.as_str() + let next = if parent.children.checked_sub(1).is_some() { + println!("cargo:warning=clone"); + parent.clone() } else { - parent + nodes.remove(p.as_str()).expect("Already checked") }; - - // Add dependencies to the queue - for dep in &pkg.dependencies { - if !matches!(dep.kind, DependencyKind::Normal) { - continue; - } - if let Some(dep_pkg) = data.packages.iter().find(|p| p.name == dep.name) { - packages.push_back((dep_pkg, next_parent)); - }; - } + queue.push_front(next); } - Ok(res) - } + let reduced = reduce(node.table)?; + merge(&mut curr, reduced, true)?; - /// Applies reducing rules to the tree and returns the final value. It first searchs one branch from the - /// package to the project root. Then, it backtracks through the rest of the paths. If the final values - /// from each path are incompatible, a merge error is returned. - /// - /// `merge` is a function that takes the current value, the new value that should be applied to it, and - /// whether it should allow the second value to overwrite the first. When traveling up the tree this is - /// true since we want dependent crates to have priority, but when comparing horizontally it is false to - /// avoid conflicts. - pub fn build( - &self, - merge: impl Fn(&mut Table, Table, bool) -> Result<(), Error>, - ) -> Result { - let mut res = Table::new(); - - for node in &self.leaves { - let value = self.get(&merge, node)?; - merge(&mut res, value, false)?; + if node.parents.is_empty() { + merge(&mut res, curr, false)?; + curr = Table::new(); } - - Ok(res) } - /// Helper for `build` that gets a single package branch. - fn get( - &self, - merge: impl Fn(&mut Table, Table, bool) -> Result<(), Error>, - package: &str, - ) -> Result { - let base = self - .nodes - .get(package) - .ok_or(Error::PackageNotFound(package.into()))?; - - let mut nodes = VecDeque::from([base]); - let mut res = Table::new(); - let mut curr = Table::new(); - - while let Some(node) = nodes.pop_front() { - for p in node.parents.iter().rev() { - let next = self.nodes.get(p).ok_or(Error::PackageNotFound(p.into()))?; - nodes.push_front(next); - } - let value = reduce(node.table.clone())?; - merge(&mut curr, value, true)?; - if node.parents.is_empty() { - merge(&mut res, curr, false)?; - curr = Table::new(); - } - } - - Ok(res) - } + Ok(res) } diff --git a/meta/src/test.rs b/meta/src/test.rs index ef7a044..ba06dc3 100644 --- a/meta/src/test.rs +++ b/meta/src/test.rs @@ -6,12 +6,7 @@ use std::{ use toml::{Table, Value}; -use crate::{error::Error, parse::MetadataList, utils::merge_default}; - -#[cfg(feature = "binary")] -mod binary; -mod conditional; -mod metadata; +use crate::{error::Error, parse::read_metadata, utils::merge_default}; macro_rules! entry { ($table:expr, $key:expr) => { @@ -24,14 +19,14 @@ macro_rules! entry { } #[derive(Clone, Debug, Default)] -struct Package { - name: &'static str, - deps: Vec<&'static str>, - config: Table, +pub struct Package { + pub name: &'static str, + pub deps: Vec<&'static str>, + pub config: Table, } impl Package { - fn write_toml(self, test_name: &str) -> io::Result { + pub fn write_toml(self, test_name: &str) -> io::Result { let mut table = self.config; let package = entry!(table, "package"); @@ -60,15 +55,13 @@ impl Package { } #[derive(Debug)] -struct Test { - metadata: MetadataList, - table: Table, - #[allow(dead_code)] - manifest: PathBuf, +pub struct Test { + pub manifest: PathBuf, + pub metadata: Table, } impl Test { - fn write_manifest(name: impl AsRef, packages: Vec) -> PathBuf { + pub fn write_manifest(name: impl AsRef, packages: Vec) -> PathBuf { assert!(!packages.is_empty()); println!("\n# Dependencies\n"); @@ -84,30 +77,21 @@ impl Test { manifest.expect("There is no main test case") } - fn new(name: impl AsRef, packages: Vec) -> Result { + pub fn new(name: impl AsRef, packages: Vec) -> Result { let manifest = Self::write_manifest(name, packages); - let metadata = MetadataList::new(&manifest, "system-deps")?; - let table = metadata.build(merge_default)?; - - //println!("\n# Metadata\n"); - //println!("{:#?}", metadata); - - Ok(Self { - metadata, - table, - manifest, - }) + let metadata = read_metadata(&manifest, "system-deps", merge_default)?; + Ok(Self { manifest, metadata }) } - fn check(&self, key: &str) -> Result<&Table, Error> { - self.table + pub fn check(&self, key: &str) -> Result<&Table, Error> { + self.metadata .get(key) .and_then(|v| v.as_table()) .ok_or(Error::PackageNotFound(key.into())) } } -fn assert_set( +pub fn assert_set( rhs: impl IntoIterator, lhs: impl IntoIterator, ) { @@ -115,3 +99,528 @@ fn assert_set( let l = lhs.into_iter().collect::>(); assert_eq!(r, l); } + +#[test] +fn simple() -> Result<(), Error> { + let pkgs = vec![Package { + name: "dep", + deps: vec![], + config: toml::toml![ + [package.metadata.system-deps.dep] + value = "simple" + ], + }]; + + let test = Test::new("simple", pkgs)?; + assert_eq!(test.check("dep")?, &toml::toml![value = "simple"]); + + Ok(()) +} + +#[test] +fn inherit() -> Result<(), Error> { + let mut pkgs = vec![ + Package { + name: "main", + deps: vec!["dep"], + config: Default::default(), + }, + Package { + name: "dep", + deps: vec![], + config: toml::toml![ + [package.metadata.system-deps.dep] + value = "original" + ], + }, + ]; + + let test = Test::new("inherit", pkgs.clone())?; + assert_eq!(test.check("dep")?, &toml::toml![value = "original"]); + + pkgs[0].config = toml::toml![ + [package.metadata.system-deps.dep] + value = "final" + ]; + + let test = Test::new("overwrite", pkgs)?; + assert_eq!(test.check("dep")?, &toml::toml![value = "final"]); + + Ok(()) +} + +#[test] +fn chain() -> Result<(), Error> { + let names = ["final", "a", "b", "c", "d", "e", "original", ""]; + let mut pkgs = names + .windows(2) + .map(|p| { + let manifest = format!( + r#" + [package.metadata.system-deps.original] + value = "{}""#, + p[0] + ); + let mut deps = Vec::new(); + if !p[1].is_empty() { + deps.push(p[1]); + } + Package { + name: p[0], + deps, + config: toml::from_str(&manifest).unwrap(), + } + }) + .collect::>(); + + let test = Test::new("chain", pkgs.clone())?; + assert_eq!(test.check("original")?, &toml::toml![value = "final"]); + + for p in pkgs.iter_mut() { + if !["final", "original"].contains(&p.name) { + p.config.retain(|_, _| false); + } + } + + let test = Test::new("gap", pkgs)?; + assert_eq!(test.check("original")?, &toml::toml![value = "final"]); + + Ok(()) +} + +#[test] +fn merge_some() -> Result<(), Error> { + let pkgs = vec![ + Package { + name: "main", + deps: vec!["dep"], + config: toml::toml![ + [package.metadata.system-deps.dep] + text = "final" + added = "top" + value = false + list = [ "c", "d" ] + other = { different = 3, new = 4 } + ], + }, + Package { + name: "dep", + deps: vec![], + config: toml::toml![ + [package.metadata.system-deps.dep] + text = "original" + value = true + number = 256 + list = [ "a", "b" ] + other = { same = 1, different = 2 } + ], + }, + ]; + + let test = Test::new("merge_some", pkgs)?; + + assert_eq!( + test.check("dep")?, + &toml::toml! [ + text = "final" + number = 256 + value = false + added = "top" + list = [ "a", "b", "c", "d" ] + other = { same = 1, different = 3, new = 4 } + ] + ); + + Ok(()) +} + +#[test] +fn incompatible_type() -> Result<(), Error> { + let pkgs = vec![ + Package { + name: "main", + deps: vec!["dep"], + config: toml::toml![ + [package.metadata.system-deps.dep] + value = 256 + ], + }, + Package { + name: "dep", + deps: vec![], + config: toml::toml![ + [package.metadata.system-deps.dep] + value = "simple" + ], + }, + ]; + + let test = Test::new("incompatible", pkgs); + println!("left: {:?}", test); + assert!(matches!(test, Err(Error::IncompatibleMerge))); + + Ok(()) +} + +#[test] +fn root_workspace() -> Result<(), Error> { + let pkgs = vec![Package { + name: "dep", + deps: vec![], + config: toml::toml![ + [workspace.metadata.system-deps.dep] + value = "final" + + [package.metadata.system-deps.dep] + value = "original" + ], + }]; + + let test = Test::new("root_workspace", pkgs)?; + assert_eq!(test.check("dep")?, &toml::toml![value = "final"]); + + Ok(()) +} + +#[test] +fn virtual_workspace() -> Result<(), Error> { + let pkgs = vec![Package { + name: "dep", + deps: vec![], + config: toml::toml![ + [package.metadata.system-deps.dep] + value = "original" + ], + }]; + + let mut path = Test::write_manifest("virtual_workspace", pkgs); + + path.pop(); + path.pop(); + path.push("Cargo.toml"); + + let manifest = toml::toml![ + [workspace] + members = ["dep"] + resolver = "2" + + [workspace.metadata.system-deps.dep] + value = "final" + ]; + std::fs::write(&path, manifest.to_string()).expect("Failed to write manifest"); + + let metadata = read_metadata(&path, "system-deps", merge_default)?; + let test = Test { + metadata, + manifest: path, + }; + assert_eq!(test.check("dep")?, &toml::toml![value = "final"]); + + Ok(()) +} + +#[test] +fn branch() -> Result<(), Error> { + let mut pkgs = vec![ + Package { + name: "main", + deps: vec!["a", "b"], + config: Default::default(), + }, + Package { + name: "a", + deps: vec!["dep"], + config: toml::toml![ + [package.metadata.system-deps.dep] + value = "final" + ], + }, + Package { + name: "b", + deps: vec!["dep"], + config: toml::toml![ + [package.metadata.system-deps.dep] + value = "final" + ], + }, + Package { + name: "dep", + deps: vec![], + config: toml::toml![ + [package.metadata.system-deps.dep] + value = "original" + ], + }, + ]; + + let test = Test::new("branch", pkgs.clone())?; + assert_eq!(test.check("dep")?, &toml::toml![value = "final"]); + + pkgs[2].config = toml::toml![ + [package.metadata.system-deps.dep] + value = "different" + ]; + + let test = Test::new("branch_conflict", pkgs); + println!("left: {:?}", test); + assert!(matches!(test, Err(Error::IncompatibleMerge))); + + Ok(()) +} + +#[test] +fn two_dependencies() -> Result<(), Error> { + let mut pkgs = vec![ + Package { + name: "main", + deps: vec!["a", "b"], + config: Default::default(), + }, + Package { + name: "a", + deps: vec![], + config: toml::toml![ + [package.metadata.system-deps.a] + value = "a" + ], + }, + Package { + name: "b", + deps: vec![], + config: toml::toml![ + [package.metadata.system-deps.b] + value = "b" + ], + }, + ]; + + let test = Test::new("two_dependencies", pkgs.clone())?; + assert_eq!(test.check("a")?, &toml::toml![value = "a"]); + assert_eq!(test.check("b")?, &toml::toml![value = "b"]); + + pkgs[1].config = toml::toml![ + [package.metadata.system-deps.a] + value = "a" + [package.metadata.system-deps.b] + value = "a" + ]; + + let test = Test::new("two_dependencies_incompatible", pkgs.clone()); + println!("left: {:?}", test); + assert!(matches!(test, Err(Error::IncompatibleMerge))); + + pkgs[0].deps.pop(); + pkgs[1].deps.push("b"); + + let test = Test::new("two_dependencies_nested", pkgs)?; + assert_eq!(test.check("a")?, &toml::toml![value = "a"]); + assert_eq!(test.check("b")?, &toml::toml![value = "a"]); + + Ok(()) +} + +#[test] +fn dependency_types() -> Result<(), Error> { + let pkgs = vec![ + Package { + name: "main", + deps: vec![], + config: toml::toml![ + [dependencies] + regular = { path = "../regular" } + [dev-dependencies] + dev = { path = "../dev" } + [build-dependencies] + build = { path = "../build" } + ], + }, + Package { + name: "regular", + deps: vec![], + config: toml::toml![ + [package.metadata.system-deps.regular] + value = "regular" + ], + }, + Package { + name: "dev", + deps: vec![], + config: toml::toml![ + [package.metadata.system-deps.dev] + value = "dev" + ], + }, + Package { + name: "build", + deps: vec![], + config: toml::toml![ + [package.metadata.system-deps.build] + value = "build" + ], + }, + ]; + + let test = Test::new("dependency_types", pkgs)?; + assert_eq!(test.check("regular")?, &toml::toml![value = "regular"]); + + let dev = test.check("dev"); + println!("left: {:?}", dev); + assert!(matches!(dev, Err(Error::PackageNotFound(_)))); + + let build = test.check("build"); + println!("left: {:?}", build); + assert!(matches!(build, Err(Error::PackageNotFound(_)))); + + Ok(()) +} + +#[test] +fn optional_package() -> Result<(), Error> { + let mut pkgs = vec![ + Package { + name: "main", + deps: vec!["dep"], + config: toml::toml![ + [dependencies.dep] + optional = true + [features] + default = [ "dep:dep" ] + ], + }, + Package { + name: "dep", + deps: vec![], + config: toml::toml![ + [package.metadata.system-deps.dep] + value = "simple" + ], + }, + ]; + + let test = Test::new("optional_package", pkgs.clone())?; + assert_eq!(test.check("dep")?, &toml::toml![value = "simple"]); + + pkgs[0].config.remove("features"); + let test = Test::new("optional_package_disabled", pkgs)?; + + let res = test.check("dep"); + println!("left: {:?}", res); + assert!(matches!(res, Err(Error::PackageNotFound(_)))); + + Ok(()) +} + +#[test] +fn conditional() -> Result<(), Error> { + let manifest = r#" + [package.metadata.system-deps.dep] + value = "default" + other = 32 + + [package.metadata.system-deps.'cfg(all())'.dep] + value = "final" + "#; + + let pkgs = vec![Package { + name: "dep", + deps: vec![], + config: toml::from_str(manifest)?, + }]; + let test = Test::new("conditional_true", pkgs)?; + assert_eq!( + test.check("dep")?, + &toml::toml![ + value = "final" + other = 32 + ] + ); + + let pkgs = vec![Package { + name: "dep", + deps: vec![], + config: toml::from_str(&manifest.replace("all", "any"))?, + }]; + let test = Test::new("conditional_false", pkgs)?; + assert_eq!( + test.check("dep")?, + &toml::toml![ + value = "default" + other = 32 + ] + ); + + Ok(()) +} + +#[test] +#[cfg(target_os = "linux")] +fn conditional_conflict() -> Result<(), Error> { + let pkgs = vec![Package { + name: "dep", + deps: vec![], + config: toml::from_str( + r#" + [package.metadata.system-deps.'cfg(target_os = "linux")'.dep] + value = "linux" + + [package.metadata.system-deps.'cfg(unix)'.dep] + value = "unix" + "#, + )?, + }]; + + let test = Test::new("conditional_conflict", pkgs); + println!("left: {:?}", test); + assert!(matches!(test, Err(Error::IncompatibleMerge))); + + Ok(()) +} + +#[test] +fn conditional_not_map() -> Result<(), Error> { + let mut pkgs = vec![Package { + name: "dep", + deps: vec![], + config: toml::from_str( + r#" + [package.metadata.system-deps.'cfg(all())'] + dep = 1234"#, + )?, + }]; + + let test = Test::new("conditional_not_map", pkgs.clone()); + println!("left: {:?}", test); + assert!(matches!(test, Err(Error::CfgNotObject(_)))); + + pkgs[0].config = toml::from_str( + r#" + [package.metadata.system-deps] + 'cfg(all())' = 1234"#, + )?; + + let test = Test::new("conditional_not_map_ext", pkgs); + println!("left: {:?}", test); + assert!(matches!(test, Err(Error::CfgNotObject(_)))); + + Ok(()) +} + +#[test] +fn conditional_unsupported() -> Result<(), Error> { + let pkgs = vec![Package { + name: "dep", + deps: vec![], + config: toml::from_str( + r#" + [package.metadata.system-deps.'cfg(feature = "a")'.dep] + value = "a" + "#, + )?, + }]; + + let test = Test::new("conditional_unsupported", pkgs); + println!("left: {:?}", test); + assert!(matches!(test, Err(Error::UnsupportedCfg(_)))); + + Ok(()) +} diff --git a/meta/src/test/conditional.rs b/meta/src/test/conditional.rs deleted file mode 100644 index 0fb40da..0000000 --- a/meta/src/test/conditional.rs +++ /dev/null @@ -1,110 +0,0 @@ -use crate::{ - error::Error, - test::{Package, Test}, -}; - -#[test] -fn conditional() -> Result<(), Error> { - let manifest = r#" - [package.metadata.system-deps.dep] - value = "default" - other = 32 - - [package.metadata.system-deps.'cfg(all())'.dep] - value = "final" - "#; - - let pkgs = vec![Package { - name: "dep", - deps: vec![], - config: toml::from_str(manifest)?, - }]; - let test = Test::new("conditional_true", pkgs)?; - assert_eq!( - test.check("dep")?, - &toml::toml![ - value = "final" - other = 32 - ] - ); - - let pkgs = vec![Package { - name: "dep", - deps: vec![], - config: toml::from_str(&manifest.replace("all", "any"))?, - }]; - let test = Test::new("conditional_false", pkgs)?; - assert_eq!( - test.check("dep")?, - &toml::toml![ - value = "default" - other = 32 - ] - ); - - Ok(()) -} - -#[test] -#[cfg(target_os = "linux")] -fn conditional_conflict() -> Result<(), Error> { - let pkgs = vec![Package { - name: "dep", - deps: vec![], - config: toml::from_str( - r#" - [package.metadata.system-deps.'cfg(target_os = "linux")'.dep] - value = "linux" - - [package.metadata.system-deps.'cfg(unix)'.dep] - value = "unix" - "#, - )?, - }]; - - let test = Test::new("conditional_conflict", pkgs); - println!("left: {:?}", test); - assert!(matches!(test, Err(Error::IncompatibleMerge))); - - Ok(()) -} - -#[test] -fn conditional_not_map() -> Result<(), Error> { - let pkgs = vec![Package { - name: "dep", - deps: vec![], - config: toml::from_str( - r#" - [package.metadata.system-deps.'cfg(all())'] - dep = 1234 - "#, - )?, - }]; - - let test = Test::new("conditional_not_map", pkgs); - println!("left: {:?}", test); - assert!(matches!(test, Err(Error::CfgNotObject(_)))); - - Ok(()) -} - -#[test] -fn conditional_unsupported() -> Result<(), Error> { - let pkgs = vec![Package { - name: "dep", - deps: vec![], - config: toml::from_str( - r#" - [package.metadata.system-deps.'cfg(feature = "a")'.dep] - value = "a" - "#, - )?, - }]; - - let test = Test::new("conditional_unsupported", pkgs); - println!("left: {:?}", test); - assert!(matches!(test, Err(Error::UnsupportedCfg(_)))); - - Ok(()) -} diff --git a/meta/src/test/metadata.rs b/meta/src/test/metadata.rs deleted file mode 100644 index 1531580..0000000 --- a/meta/src/test/metadata.rs +++ /dev/null @@ -1,428 +0,0 @@ -use std::vec; - -use crate::{ - error::Error, - parse::MetadataList, - test::{assert_set, Package, Test}, - utils::merge_default, -}; - -#[test] -fn simple() -> Result<(), Error> { - let pkgs = vec![Package { - name: "dep", - deps: vec![], - config: toml::toml![ - [package.metadata.system-deps.dep] - value = "simple" - ], - }]; - - let test = Test::new("simple", pkgs)?; - assert_eq!(test.check("dep")?, &toml::toml![value = "simple"]); - - Ok(()) -} - -#[test] -fn inherit() -> Result<(), Error> { - let mut pkgs = vec![ - Package { - name: "main", - deps: vec!["dep"], - config: Default::default(), - }, - Package { - name: "dep", - deps: vec![], - config: toml::toml![ - [package.metadata.system-deps.dep] - value = "original" - ], - }, - ]; - - let test = Test::new("inherit", pkgs.clone())?; - assert_eq!(test.check("dep")?, &toml::toml![value = "original"]); - - pkgs[0].config = toml::toml![ - [package.metadata.system-deps.dep] - value = "final" - ]; - - let test = Test::new("overwrite", pkgs)?; - assert_eq!(test.check("dep")?, &toml::toml![value = "final"]); - - Ok(()) -} - -#[test] -fn chain() -> Result<(), Error> { - let names = ["final", "a", "b", "c", "d", "e", "original", ""]; - let mut pkgs = names - .windows(2) - .map(|p| { - let manifest = format!( - r#" - [package.metadata.system-deps.original] - value = "{}""#, - p[0] - ); - let mut deps = Vec::new(); - if !p[1].is_empty() { - deps.push(p[1]); - } - Package { - name: p[0], - deps, - config: toml::from_str(&manifest).unwrap(), - } - }) - .collect::>(); - - let test = Test::new("chain", pkgs.clone())?; - assert_eq!(test.check("original")?, &toml::toml![value = "final"]); - assert_set(test.metadata.nodes.keys().map(|k| k.as_str()), names); - - for p in pkgs.iter_mut() { - if !["final", "original"].contains(&p.name) { - p.config.retain(|_, _| false); - } - } - - let test = Test::new("gap", pkgs)?; - assert_eq!(test.check("original")?, &toml::toml![value = "final"]); - assert_set( - test.metadata.nodes.keys().map(|k| k.as_str()), - ["final", "original", ""], - ); - - Ok(()) -} - -#[test] -fn merge_some() -> Result<(), Error> { - let pkgs = vec![ - Package { - name: "main", - deps: vec!["dep"], - config: toml::toml![ - [package.metadata.system-deps.dep] - text = "final" - added = "top" - value = false - list = [ "c", "d" ] - other = { different = 3, new = 4 } - ], - }, - Package { - name: "dep", - deps: vec![], - config: toml::toml![ - [package.metadata.system-deps.dep] - text = "original" - value = true - number = 256 - list = [ "a", "b" ] - other = { same = 1, different = 2 } - ], - }, - ]; - - let test = Test::new("merge_some", pkgs)?; - - assert_eq!( - test.check("dep")?, - &toml::toml! [ - text = "final" - number = 256 - value = false - added = "top" - list = [ "a", "b", "c", "d" ] - other = { same = 1, different = 3, new = 4 } - ] - ); - - Ok(()) -} - -#[test] -fn incompatible_type() -> Result<(), Error> { - let pkgs = vec![ - Package { - name: "main", - deps: vec!["dep"], - config: toml::toml![ - [package.metadata.system-deps.dep] - value = 256 - ], - }, - Package { - name: "dep", - deps: vec![], - config: toml::toml![ - [package.metadata.system-deps.dep] - value = "simple" - ], - }, - ]; - - let test = Test::new("incompatible", pkgs); - println!("left: {:?}", test); - assert!(matches!(test, Err(Error::IncompatibleMerge))); - - Ok(()) -} - -#[test] -fn root_workspace() -> Result<(), Error> { - let pkgs = vec![Package { - name: "dep", - deps: vec![], - config: toml::toml![ - [workspace.metadata.system-deps.dep] - value = "final" - - [package.metadata.system-deps.dep] - value = "original" - ], - }]; - - let test = Test::new("root_workspace", pkgs)?; - assert_eq!(test.check("dep")?, &toml::toml![value = "final"]); - - Ok(()) -} - -#[test] -fn virtual_workspace() -> Result<(), Error> { - let pkgs = vec![Package { - name: "dep", - deps: vec![], - config: toml::toml![ - [package.metadata.system-deps.dep] - value = "original" - ], - }]; - - let mut path = Test::write_manifest("virtual_workspace", pkgs); - - path.pop(); - path.pop(); - path.push("Cargo.toml"); - - let manifest = toml::toml![ - [workspace] - members = ["dep"] - resolver = "2" - - [workspace.metadata.system-deps.dep] - value = "final" - ]; - std::fs::write(&path, manifest.to_string()).expect("Failed to write manifest"); - - let metadata = MetadataList::new(&path, "system-deps")?; - let table = metadata.build(merge_default)?; - let test = Test { - metadata, - table, - manifest: path, - }; - assert_eq!(test.check("dep")?, &toml::toml![value = "final"]); - - Ok(()) -} - -#[test] -fn branch() -> Result<(), Error> { - let mut pkgs = vec![ - Package { - name: "main", - deps: vec!["a", "b"], - config: Default::default(), - }, - Package { - name: "a", - deps: vec!["dep"], - config: toml::toml![ - [package.metadata.system-deps.dep] - value = "final" - ], - }, - Package { - name: "b", - deps: vec!["dep"], - config: toml::toml![ - [package.metadata.system-deps.dep] - value = "final" - ], - }, - Package { - name: "dep", - deps: vec![], - config: toml::toml![ - [package.metadata.system-deps.dep] - value = "original" - ], - }, - ]; - - let test = Test::new("branch", pkgs.clone())?; - assert_eq!(test.check("dep")?, &toml::toml![value = "final"]); - - pkgs[2].config = toml::toml![ - [package.metadata.system-deps.dep] - value = "different" - ]; - - let test = Test::new("branch_conflict", pkgs); - println!("left: {:?}", test); - assert!(matches!(test, Err(Error::IncompatibleMerge))); - - Ok(()) -} - -#[test] -fn two_dependencies() -> Result<(), Error> { - let mut pkgs = vec![ - Package { - name: "main", - deps: vec!["a", "b"], - config: Default::default(), - }, - Package { - name: "a", - deps: vec![], - config: toml::toml![ - [package.metadata.system-deps.a] - value = "a" - ], - }, - Package { - name: "b", - deps: vec![], - config: toml::toml![ - [package.metadata.system-deps.b] - value = "b" - ], - }, - ]; - - let test = Test::new("two_dependencies", pkgs.clone())?; - assert_eq!(test.check("a")?, &toml::toml![value = "a"]); - assert_eq!(test.check("b")?, &toml::toml![value = "b"]); - - pkgs[1].config = toml::toml![ - [package.metadata.system-deps.a] - value = "a" - [package.metadata.system-deps.b] - value = "a" - ]; - - let test = Test::new("two_dependencies_incompatible", pkgs.clone()); - println!("left: {:?}", test); - assert!(matches!(test, Err(Error::IncompatibleMerge))); - - pkgs[0].deps.pop(); - pkgs[1].deps.push("b"); - - let test = Test::new("two_dependencies_nested", pkgs)?; - assert_eq!(test.check("a")?, &toml::toml![value = "a"]); - assert_eq!(test.check("b")?, &toml::toml![value = "a"]); - - Ok(()) -} - -#[test] -fn dependency_types() -> Result<(), Error> { - let pkgs = vec![ - Package { - name: "main", - deps: vec![], - config: toml::toml![ - [dependencies] - regular = { path = "../regular" } - [dev-dependencies] - dev = { path = "../dev" } - [build-dependencies] - build = { path = "../build" } - ], - }, - Package { - name: "regular", - deps: vec![], - config: toml::toml![ - [package.metadata.system-deps.regular] - value = "regular" - ], - }, - Package { - name: "dev", - deps: vec![], - config: toml::toml![ - [package.metadata.system-deps.dev] - value = "dev" - ], - }, - Package { - name: "build", - deps: vec![], - config: toml::toml![ - [package.metadata.system-deps.build] - value = "build" - ], - }, - ]; - - let test = Test::new("dependency_types", pkgs)?; - assert_eq!(test.check("regular")?, &toml::toml![value = "regular"]); - - let dev = test.check("dev"); - println!("left: {:?}", dev); - assert!(matches!(dev, Err(Error::PackageNotFound(_)))); - - let build = test.check("build"); - println!("left: {:?}", build); - assert!(matches!(build, Err(Error::PackageNotFound(_)))); - - let nodes = test.metadata.nodes.keys().map(|k| k.as_str()); - assert_set(nodes, ["", "regular"]); - - Ok(()) -} - -#[test] -fn optional_package() -> Result<(), Error> { - let mut pkgs = vec![ - Package { - name: "main", - deps: vec!["dep"], - config: toml::toml![ - [dependencies.dep] - optional = true - [features] - default = [ "dep:dep" ] - ], - }, - Package { - name: "dep", - deps: vec![], - config: toml::toml![ - [package.metadata.system-deps.dep] - value = "simple" - ], - }, - ]; - - let test = Test::new("optional_package", pkgs.clone())?; - assert_eq!(test.check("dep")?, &toml::toml![value = "simple"]); - - pkgs[0].config.remove("features"); - let test = Test::new("optional_package_disabled", pkgs)?; - - let res = test.check("dep"); - println!("left: {:?}", res); - assert!(matches!(res, Err(Error::PackageNotFound(_)))); - - Ok(()) -} diff --git a/meta/src/utils.rs b/meta/src/utils.rs index 5409ea9..6c4c254 100644 --- a/meta/src/utils.rs +++ b/meta/src/utils.rs @@ -3,7 +3,7 @@ use toml::{Table, Value}; use crate::error::Error; -/// Base merge function to use with `MetadataList::get`. +/// Base merge function to use with `read_metadata`. /// It will join `serde_json` values based on some assignment rules. pub fn merge_default(rhs: &mut Table, lhs: Table, overwrite: bool) -> Result<(), Error> { for (key, lhs) in lhs { diff --git a/src/lib.rs b/src/lib.rs index ce117c0..300b379 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -282,7 +282,7 @@ use heck::{ToShoutySnakeCase, ToSnakeCase}; use std::borrow::Borrow; use std::collections::HashMap; use std::env; -use std::ffi::{OsStr, OsString}; +use std::ffi::OsString; use std::fmt; use std::iter; use std::ops::RangeBounds; @@ -292,8 +292,8 @@ use std::str::FromStr; mod metadata; use metadata::MetaData; -#[cfg(feature = "binary")] -include!(env!("BINARY_CONFIG")); +#[cfg(all(test, feature = "binary"))] +mod test_binary; /// system-deps errors #[derive(Debug)] @@ -631,7 +631,7 @@ enum EnvVariable { BuildInternal(Option), Link(Option), LinkerArgs(String), - NoBinary(Option), + NoPrebuilt(Option), } impl EnvVariable { @@ -671,8 +671,8 @@ impl EnvVariable { Self::Link(lib.map(|l| l.to_string())) } - fn new_no_binary(lib: Option<&str>) -> Self { - Self::NoBinary(lib.map(|l| l.to_string())) + fn new_no_prebuilt(lib: Option<&str>) -> Self { + Self::NoPrebuilt(lib.map(|l| l.to_string())) } fn suffix(&self) -> &'static str { @@ -686,7 +686,7 @@ impl EnvVariable { EnvVariable::BuildInternal(_) => "BUILD_INTERNAL", EnvVariable::Link(_) => "LINK", EnvVariable::LinkerArgs(_) => "LDFLAGS", - EnvVariable::NoBinary(_) => "NO_BINARY", + EnvVariable::NoPrebuilt(_) => "NO_PREBUILT", } } @@ -704,7 +704,7 @@ impl EnvVariable { add_to_flags(flags, EnvVariable::new_no_pkg_config(name)); add_to_flags(flags, EnvVariable::new_build_internal(Some(name))); add_to_flags(flags, EnvVariable::new_link(Some(name))); - add_to_flags(flags, EnvVariable::new_no_binary(Some(name))); + add_to_flags(flags, EnvVariable::new_no_prebuilt(Some(name))); } } @@ -720,12 +720,12 @@ impl fmt::Display for EnvVariable { | EnvVariable::NoPkgConfig(lib) | EnvVariable::BuildInternal(Some(lib)) | EnvVariable::Link(Some(lib)) - | EnvVariable::NoBinary(Some(lib)) => { + | EnvVariable::NoPrebuilt(Some(lib)) => { format!("{}_{}", lib.to_shouty_snake_case(), self.suffix()) } EnvVariable::BuildInternal(None) | EnvVariable::Link(None) - | EnvVariable::NoBinary(None) => self.suffix().to_string(), + | EnvVariable::NoPrebuilt(None) => self.suffix().to_string(), }; write!(f, "SYSTEM_DEPS_{}", suffix) } @@ -738,6 +738,8 @@ type FnBuildInternal = pub struct Config { env: EnvVariables, build_internals: HashMap>, + #[cfg(feature = "binary")] + paths: &'static system_deps_meta::binary::Paths, } impl Default for Config { @@ -756,6 +758,17 @@ impl Config { Self { env, build_internals: HashMap::new(), + #[cfg(feature = "binary")] + paths: { + // Constructed by reading the serialized value saved by the build script + const CONTENT: &str = include_str!(env!("BINARY_PATHS")); + static PATHS: std::sync::OnceLock = + std::sync::OnceLock::new(); + PATHS.get_or_init(|| { + toml::from_str(CONTENT) + .expect("The build script should output valid serialization") + }) + }, } } @@ -800,6 +813,18 @@ impl Config { self } + /// Checks the map from packages to their provided prebuilt binaries locations, if available. + pub fn query_path(&self, _pkg: &str) -> Option<&'static Vec> { + #[cfg(not(feature = "binary"))] + return None; + + #[cfg(feature = "binary")] + self.env + .get(&EnvVariable::new_no_prebuilt(Some(_pkg))) + .or(self.env.get(&EnvVariable::new_no_prebuilt(None))) + .map_or_else(|| self.paths.get(_pkg), |_| None) + } + fn probe_full(mut self) -> Result { let mut libraries = self.probe_pkg_config()?; libraries.override_from_flags(&self.env); @@ -889,17 +914,7 @@ impl Config { let build_internal = self.get_build_internal_status(name)?; // Is there an overrided pkg-config path for the library? - #[cfg(not(feature = "binary"))] - let pkg_config_paths: Option<[&str; 0]> = None; - #[cfg(feature = "binary")] - let pkg_config_paths = self - .env - .get(&EnvVariable::new_no_binary(Some(name))) - .or(self.env.get(&EnvVariable::new_no_binary(None))) - .map_or_else( - || Some([get_path(name.as_str()), get_path("")].concat()), - |_| None, - ); + let pkg_config_paths = self.query_path(name); // should the lib be statically linked? let statik = cfg!(feature = "binary") @@ -920,12 +935,9 @@ impl Config { .range_version(metadata::parse_version(version)) .statik(statik); - let probe = match pkg_config_paths { - Some(p) => Library::wrap_pkg_config(&p, || { - Self::probe_with_fallback(&config, lib_name, fallback_lib_names) - }), - None => Self::probe_with_fallback(&config, lib_name, fallback_lib_names), - }; + let probe = Library::wrap_pkg_config(pkg_config_paths, || { + Self::probe_with_fallback(&config, lib_name, fallback_lib_names) + }); match probe { Ok((lib_name, lib)) => Library::from_pkg_config(lib_name, lib), @@ -1272,27 +1284,30 @@ impl Library { pub trait PathOrList { /// Creates an string of paths appropiately joined for an environment variable. /// The paths in `self` will go before the paths in `other`. - fn join_paths(self, other: impl AsRef<[PathBuf]>) -> OsString; + fn join_paths(&self, other: impl AsRef<[PathBuf]>) -> OsString; } -impl> PathOrList for T { - fn join_paths(self, other: impl AsRef<[PathBuf]>) -> OsString { - env::join_paths( - iter::once(self.as_ref()).chain(other.as_ref().iter().map(|p| p.as_os_str())), - ) - .unwrap() +impl> PathOrList for T { + fn join_paths(&self, other: impl AsRef<[PathBuf]>) -> OsString { + let other = other.as_ref().iter().map(|p| p.as_path()); + env::join_paths(iter::once(self.as_ref()).chain(other)).unwrap() } } -impl, S: Borrow<[T]>> PathOrList<(T, S)> for &S { - fn join_paths(self, other: impl AsRef<[PathBuf]>) -> OsString { - env::join_paths( - self.borrow() - .iter() - .map(|p| p.as_ref()) - .chain(other.as_ref().iter().map(|p| p.as_os_str())), - ) - .unwrap() +impl, S: Borrow<[T]>> PathOrList<(T, S)> for &S { + fn join_paths(&self, other: impl AsRef<[PathBuf]>) -> OsString { + let slice: &[T] = (*self).borrow(); + let other = other.as_ref().iter().map(|p| p.as_path()); + env::join_paths(slice.iter().map(|p| p.as_ref()).chain(other)).unwrap() + } +} + +impl, S> PathOrList<(T, S)> for Option { + fn join_paths(&self, other: impl AsRef<[PathBuf]>) -> OsString { + match self { + Some(s) => s.join_paths(other), + None => env::join_paths(other.as_ref().iter().map(|p| p.as_os_str())).unwrap(), + } } } diff --git a/src/test.rs b/src/test.rs index 94bde28..476d79f 100644 --- a/src/test.rs +++ b/src/test.rs @@ -44,7 +44,7 @@ fn create_config(path: &str, env: Vec<(&'static str, &'static str)>) -> Config { }); #[cfg(feature = "binary")] - hash.insert("SYSTEM_DEPS_NO_BINARY", "".to_string()); + hash.insert("SYSTEM_DEPS_NO_PREBUILT", "".to_string()); Config::new_with_env(EnvVariables::Mock(hash)) } @@ -107,9 +107,9 @@ cargo:rerun-if-env-changed=SYSTEM_DEPS_BUILD_INTERNAL cargo:rerun-if-env-changed=SYSTEM_DEPS_TESTLIB_BUILD_INTERNAL cargo:rerun-if-env-changed=SYSTEM_DEPS_TESTDATA_BUILD_INTERNAL cargo:rerun-if-env-changed=SYSTEM_DEPS_TESTDATA_LINK -cargo:rerun-if-env-changed=SYSTEM_DEPS_TESTDATA_NO_BINARY +cargo:rerun-if-env-changed=SYSTEM_DEPS_TESTDATA_NO_PREBUILT cargo:rerun-if-env-changed=SYSTEM_DEPS_TESTLIB_LINK -cargo:rerun-if-env-changed=SYSTEM_DEPS_TESTLIB_NO_BINARY +cargo:rerun-if-env-changed=SYSTEM_DEPS_TESTLIB_NO_PREBUILT cargo:rerun-if-env-changed=SYSTEM_DEPS_LINK "#, ); @@ -288,7 +288,7 @@ cargo:rerun-if-env-changed=SYSTEM_DEPS_TEST_LIB_LDFLAGS cargo:rerun-if-env-changed=SYSTEM_DEPS_TEST_LIB_NO_PKG_CONFIG cargo:rerun-if-env-changed=SYSTEM_DEPS_TEST_LIB_BUILD_INTERNAL cargo:rerun-if-env-changed=SYSTEM_DEPS_TEST_LIB_LINK -cargo:rerun-if-env-changed=SYSTEM_DEPS_TEST_LIB_NO_BINARY +cargo:rerun-if-env-changed=SYSTEM_DEPS_TEST_LIB_NO_PREBUILT "#, ); } @@ -317,7 +317,7 @@ cargo:rerun-if-env-changed=SYSTEM_DEPS_TEST_LIB_LDFLAGS cargo:rerun-if-env-changed=SYSTEM_DEPS_TEST_LIB_NO_PKG_CONFIG cargo:rerun-if-env-changed=SYSTEM_DEPS_TEST_LIB_BUILD_INTERNAL cargo:rerun-if-env-changed=SYSTEM_DEPS_TEST_LIB_LINK -cargo:rerun-if-env-changed=SYSTEM_DEPS_TEST_LIB_NO_BINARY +cargo:rerun-if-env-changed=SYSTEM_DEPS_TEST_LIB_NO_PREBUILT "#, ); @@ -347,7 +347,7 @@ cargo:rerun-if-env-changed=SYSTEM_DEPS_TEST_LIB_LDFLAGS cargo:rerun-if-env-changed=SYSTEM_DEPS_TEST_LIB_NO_PKG_CONFIG cargo:rerun-if-env-changed=SYSTEM_DEPS_TEST_LIB_BUILD_INTERNAL cargo:rerun-if-env-changed=SYSTEM_DEPS_TEST_LIB_LINK -cargo:rerun-if-env-changed=SYSTEM_DEPS_TEST_LIB_NO_BINARY +cargo:rerun-if-env-changed=SYSTEM_DEPS_TEST_LIB_NO_PREBUILT "#, ); } @@ -430,9 +430,9 @@ cargo:rerun-if-env-changed=SYSTEM_DEPS_BUILD_INTERNAL cargo:rerun-if-env-changed=SYSTEM_DEPS_TESTLIB_BUILD_INTERNAL cargo:rerun-if-env-changed=SYSTEM_DEPS_TESTDATA_BUILD_INTERNAL cargo:rerun-if-env-changed=SYSTEM_DEPS_TESTDATA_LINK -cargo:rerun-if-env-changed=SYSTEM_DEPS_TESTDATA_NO_BINARY +cargo:rerun-if-env-changed=SYSTEM_DEPS_TESTDATA_NO_PREBUILT cargo:rerun-if-env-changed=SYSTEM_DEPS_TESTLIB_LINK -cargo:rerun-if-env-changed=SYSTEM_DEPS_TESTLIB_NO_BINARY +cargo:rerun-if-env-changed=SYSTEM_DEPS_TESTLIB_NO_PREBUILT cargo:rerun-if-env-changed=SYSTEM_DEPS_LINK "#, ); @@ -473,9 +473,9 @@ cargo:rerun-if-env-changed=SYSTEM_DEPS_BUILD_INTERNAL cargo:rerun-if-env-changed=SYSTEM_DEPS_TESTLIB_BUILD_INTERNAL cargo:rerun-if-env-changed=SYSTEM_DEPS_TESTDATA_BUILD_INTERNAL cargo:rerun-if-env-changed=SYSTEM_DEPS_TESTDATA_LINK -cargo:rerun-if-env-changed=SYSTEM_DEPS_TESTDATA_NO_BINARY +cargo:rerun-if-env-changed=SYSTEM_DEPS_TESTDATA_NO_PREBUILT cargo:rerun-if-env-changed=SYSTEM_DEPS_TESTLIB_LINK -cargo:rerun-if-env-changed=SYSTEM_DEPS_TESTLIB_NO_BINARY +cargo:rerun-if-env-changed=SYSTEM_DEPS_TESTLIB_NO_PREBUILT cargo:rerun-if-env-changed=SYSTEM_DEPS_LINK "#, ); @@ -523,9 +523,9 @@ cargo:rerun-if-env-changed=SYSTEM_DEPS_BUILD_INTERNAL cargo:rerun-if-env-changed=SYSTEM_DEPS_TESTLIB_BUILD_INTERNAL cargo:rerun-if-env-changed=SYSTEM_DEPS_TESTDATA_BUILD_INTERNAL cargo:rerun-if-env-changed=SYSTEM_DEPS_TESTDATA_LINK -cargo:rerun-if-env-changed=SYSTEM_DEPS_TESTDATA_NO_BINARY +cargo:rerun-if-env-changed=SYSTEM_DEPS_TESTDATA_NO_PREBUILT cargo:rerun-if-env-changed=SYSTEM_DEPS_TESTLIB_LINK -cargo:rerun-if-env-changed=SYSTEM_DEPS_TESTLIB_NO_BINARY +cargo:rerun-if-env-changed=SYSTEM_DEPS_TESTLIB_NO_PREBUILT cargo:rerun-if-env-changed=SYSTEM_DEPS_LINK "#, ); @@ -566,9 +566,9 @@ cargo:rerun-if-env-changed=SYSTEM_DEPS_BUILD_INTERNAL cargo:rerun-if-env-changed=SYSTEM_DEPS_TESTLIB_BUILD_INTERNAL cargo:rerun-if-env-changed=SYSTEM_DEPS_TESTDATA_BUILD_INTERNAL cargo:rerun-if-env-changed=SYSTEM_DEPS_TESTDATA_LINK -cargo:rerun-if-env-changed=SYSTEM_DEPS_TESTDATA_NO_BINARY +cargo:rerun-if-env-changed=SYSTEM_DEPS_TESTDATA_NO_PREBUILT cargo:rerun-if-env-changed=SYSTEM_DEPS_TESTLIB_LINK -cargo:rerun-if-env-changed=SYSTEM_DEPS_TESTLIB_NO_BINARY +cargo:rerun-if-env-changed=SYSTEM_DEPS_TESTLIB_NO_PREBUILT cargo:rerun-if-env-changed=SYSTEM_DEPS_LINK "#, ); @@ -609,9 +609,9 @@ cargo:rerun-if-env-changed=SYSTEM_DEPS_BUILD_INTERNAL cargo:rerun-if-env-changed=SYSTEM_DEPS_TESTLIB_BUILD_INTERNAL cargo:rerun-if-env-changed=SYSTEM_DEPS_TESTDATA_BUILD_INTERNAL cargo:rerun-if-env-changed=SYSTEM_DEPS_TESTDATA_LINK -cargo:rerun-if-env-changed=SYSTEM_DEPS_TESTDATA_NO_BINARY +cargo:rerun-if-env-changed=SYSTEM_DEPS_TESTDATA_NO_PREBUILT cargo:rerun-if-env-changed=SYSTEM_DEPS_TESTLIB_LINK -cargo:rerun-if-env-changed=SYSTEM_DEPS_TESTLIB_NO_BINARY +cargo:rerun-if-env-changed=SYSTEM_DEPS_TESTLIB_NO_PREBUILT cargo:rerun-if-env-changed=SYSTEM_DEPS_LINK "#, ); @@ -657,9 +657,9 @@ cargo:rerun-if-env-changed=SYSTEM_DEPS_BUILD_INTERNAL cargo:rerun-if-env-changed=SYSTEM_DEPS_TESTLIB_BUILD_INTERNAL cargo:rerun-if-env-changed=SYSTEM_DEPS_TESTDATA_BUILD_INTERNAL cargo:rerun-if-env-changed=SYSTEM_DEPS_TESTDATA_LINK -cargo:rerun-if-env-changed=SYSTEM_DEPS_TESTDATA_NO_BINARY +cargo:rerun-if-env-changed=SYSTEM_DEPS_TESTDATA_NO_PREBUILT cargo:rerun-if-env-changed=SYSTEM_DEPS_TESTLIB_LINK -cargo:rerun-if-env-changed=SYSTEM_DEPS_TESTLIB_NO_BINARY +cargo:rerun-if-env-changed=SYSTEM_DEPS_TESTLIB_NO_PREBUILT cargo:rerun-if-env-changed=SYSTEM_DEPS_LINK ", ); @@ -706,9 +706,9 @@ cargo:rerun-if-env-changed=SYSTEM_DEPS_BUILD_INTERNAL cargo:rerun-if-env-changed=SYSTEM_DEPS_TESTLIB_BUILD_INTERNAL cargo:rerun-if-env-changed=SYSTEM_DEPS_TESTDATA_BUILD_INTERNAL cargo:rerun-if-env-changed=SYSTEM_DEPS_TESTDATA_LINK -cargo:rerun-if-env-changed=SYSTEM_DEPS_TESTDATA_NO_BINARY +cargo:rerun-if-env-changed=SYSTEM_DEPS_TESTDATA_NO_PREBUILT cargo:rerun-if-env-changed=SYSTEM_DEPS_TESTLIB_LINK -cargo:rerun-if-env-changed=SYSTEM_DEPS_TESTLIB_NO_BINARY +cargo:rerun-if-env-changed=SYSTEM_DEPS_TESTLIB_NO_PREBUILT cargo:rerun-if-env-changed=SYSTEM_DEPS_LINK ", ); @@ -1091,7 +1091,7 @@ cargo:rerun-if-env-changed=SYSTEM_DEPS_TESTSTATICLIB_LDFLAGS cargo:rerun-if-env-changed=SYSTEM_DEPS_TESTSTATICLIB_NO_PKG_CONFIG cargo:rerun-if-env-changed=SYSTEM_DEPS_TESTSTATICLIB_BUILD_INTERNAL cargo:rerun-if-env-changed=SYSTEM_DEPS_TESTSTATICLIB_LINK -cargo:rerun-if-env-changed=SYSTEM_DEPS_TESTSTATICLIB_NO_BINARY +cargo:rerun-if-env-changed=SYSTEM_DEPS_TESTSTATICLIB_NO_PREBUILT cargo:rerun-if-env-changed=SYSTEM_DEPS_TESTDATA_LIB cargo:rerun-if-env-changed=SYSTEM_DEPS_TESTDATA_LIB_FRAMEWORK cargo:rerun-if-env-changed=SYSTEM_DEPS_TESTDATA_SEARCH_NATIVE @@ -1101,7 +1101,7 @@ cargo:rerun-if-env-changed=SYSTEM_DEPS_TESTDATA_LDFLAGS cargo:rerun-if-env-changed=SYSTEM_DEPS_TESTDATA_NO_PKG_CONFIG cargo:rerun-if-env-changed=SYSTEM_DEPS_TESTDATA_BUILD_INTERNAL cargo:rerun-if-env-changed=SYSTEM_DEPS_TESTDATA_LINK -cargo:rerun-if-env-changed=SYSTEM_DEPS_TESTDATA_NO_BINARY +cargo:rerun-if-env-changed=SYSTEM_DEPS_TESTDATA_NO_PREBUILT "# .to_string() .as_str(), @@ -1151,9 +1151,9 @@ cargo:rerun-if-env-changed=SYSTEM_DEPS_BUILD_INTERNAL cargo:rerun-if-env-changed=SYSTEM_DEPS_TESTSTATICLIB_BUILD_INTERNAL cargo:rerun-if-env-changed=SYSTEM_DEPS_TESTDATA_BUILD_INTERNAL cargo:rerun-if-env-changed=SYSTEM_DEPS_TESTDATA_LINK -cargo:rerun-if-env-changed=SYSTEM_DEPS_TESTDATA_NO_BINARY +cargo:rerun-if-env-changed=SYSTEM_DEPS_TESTDATA_NO_PREBUILT cargo:rerun-if-env-changed=SYSTEM_DEPS_TESTSTATICLIB_LINK -cargo:rerun-if-env-changed=SYSTEM_DEPS_TESTSTATICLIB_NO_BINARY +cargo:rerun-if-env-changed=SYSTEM_DEPS_TESTSTATICLIB_NO_PREBUILT cargo:rerun-if-env-changed=SYSTEM_DEPS_LINK ", ); @@ -1187,7 +1187,7 @@ cargo:rerun-if-env-changed=SYSTEM_DEPS_TESTSTATICLIB_LDFLAGS cargo:rerun-if-env-changed=SYSTEM_DEPS_TESTSTATICLIB_NO_PKG_CONFIG cargo:rerun-if-env-changed=SYSTEM_DEPS_TESTSTATICLIB_BUILD_INTERNAL cargo:rerun-if-env-changed=SYSTEM_DEPS_TESTSTATICLIB_LINK -cargo:rerun-if-env-changed=SYSTEM_DEPS_TESTSTATICLIB_NO_BINARY +cargo:rerun-if-env-changed=SYSTEM_DEPS_TESTSTATICLIB_NO_PREBUILT cargo:rerun-if-env-changed=SYSTEM_DEPS_TESTDATA_LIB cargo:rerun-if-env-changed=SYSTEM_DEPS_TESTDATA_LIB_FRAMEWORK cargo:rerun-if-env-changed=SYSTEM_DEPS_TESTDATA_SEARCH_NATIVE @@ -1197,7 +1197,7 @@ cargo:rerun-if-env-changed=SYSTEM_DEPS_TESTDATA_LDFLAGS cargo:rerun-if-env-changed=SYSTEM_DEPS_TESTDATA_NO_PKG_CONFIG cargo:rerun-if-env-changed=SYSTEM_DEPS_TESTDATA_BUILD_INTERNAL cargo:rerun-if-env-changed=SYSTEM_DEPS_TESTDATA_LINK -cargo:rerun-if-env-changed=SYSTEM_DEPS_TESTDATA_NO_BINARY +cargo:rerun-if-env-changed=SYSTEM_DEPS_TESTDATA_NO_PREBUILT "#, ); } @@ -1232,7 +1232,7 @@ cargo:rerun-if-env-changed=SYSTEM_DEPS_TESTDATA_LDFLAGS cargo:rerun-if-env-changed=SYSTEM_DEPS_TESTDATA_NO_PKG_CONFIG cargo:rerun-if-env-changed=SYSTEM_DEPS_TESTDATA_BUILD_INTERNAL cargo:rerun-if-env-changed=SYSTEM_DEPS_TESTDATA_LINK -cargo:rerun-if-env-changed=SYSTEM_DEPS_TESTDATA_NO_BINARY +cargo:rerun-if-env-changed=SYSTEM_DEPS_TESTDATA_NO_PREBUILT cargo:rerun-if-env-changed=SYSTEM_DEPS_TESTLIB_LIB cargo:rerun-if-env-changed=SYSTEM_DEPS_TESTLIB_LIB_FRAMEWORK cargo:rerun-if-env-changed=SYSTEM_DEPS_TESTLIB_SEARCH_NATIVE @@ -1242,7 +1242,7 @@ cargo:rerun-if-env-changed=SYSTEM_DEPS_TESTLIB_LDFLAGS cargo:rerun-if-env-changed=SYSTEM_DEPS_TESTLIB_NO_PKG_CONFIG cargo:rerun-if-env-changed=SYSTEM_DEPS_TESTLIB_BUILD_INTERNAL cargo:rerun-if-env-changed=SYSTEM_DEPS_TESTLIB_LINK -cargo:rerun-if-env-changed=SYSTEM_DEPS_TESTLIB_NO_BINARY +cargo:rerun-if-env-changed=SYSTEM_DEPS_TESTLIB_NO_PREBUILT "#, ); } @@ -1290,9 +1290,9 @@ cargo:rerun-if-env-changed=SYSTEM_DEPS_BUILD_INTERNAL cargo:rerun-if-env-changed=SYSTEM_DEPS_TESTLIBWITHRPATH_BUILD_INTERNAL cargo:rerun-if-env-changed=SYSTEM_DEPS_TESTDATA_BUILD_INTERNAL cargo:rerun-if-env-changed=SYSTEM_DEPS_TESTDATA_LINK -cargo:rerun-if-env-changed=SYSTEM_DEPS_TESTDATA_NO_BINARY +cargo:rerun-if-env-changed=SYSTEM_DEPS_TESTDATA_NO_PREBUILT cargo:rerun-if-env-changed=SYSTEM_DEPS_TESTLIBWITHRPATH_LINK -cargo:rerun-if-env-changed=SYSTEM_DEPS_TESTLIBWITHRPATH_NO_BINARY +cargo:rerun-if-env-changed=SYSTEM_DEPS_TESTLIBWITHRPATH_NO_PREBUILT cargo:rerun-if-env-changed=SYSTEM_DEPS_LINK "#, ); diff --git a/meta/src/test/binary.rs b/src/test_binary.rs similarity index 53% rename from meta/src/test/binary.rs rename to src/test_binary.rs index e09e0c2..ff082f9 100644 --- a/meta/src/test/binary.rs +++ b/src/test_binary.rs @@ -1,45 +1,43 @@ use std::{ - convert::TryInto, + collections::HashMap, fs, path::{Path, PathBuf}, - str::FromStr, sync::{ atomic::{AtomicUsize, Ordering}, - Arc, + OnceLock, }, - thread, - time::Duration, }; -use tiny_http::{Header, Response, Server, StatusCode}; use toml::{Table, Value}; -use crate::{ - binary::{merge_binary, Extension, Paths}, +use system_deps_meta::{ + binary::{merge_binary, Paths}, error::Error, - test::{Package, Test}, - BUILD_MANIFEST, + parse::read_metadata, + test::{self, assert_set, Package}, + BUILD_MANIFEST, BUILD_TARGET_DIR, }; -use super::assert_set; +use crate::{BuildInternalClosureError, Config, EnvVariables, Library}; -trait BinaryTestExt { - fn new_bin(name: &str, packages: Vec) -> Result; - fn get_paths(&self, key: &str) -> Result, Error>; +#[derive(Debug)] +struct Test { + manifest: PathBuf, + paths: Paths, } -impl BinaryTestExt for Test { - fn new_bin(name: &str, mut packages: Vec) -> Result { +impl Test { + fn new(name: &str, mut packages: Vec) -> Result { let name = format!("bin_{}", name); for p in packages.iter_mut() { replace_paths(&name, &mut p.config); } - Self::new(name, packages) - } - fn get_paths(&self, key: &str) -> Result, Error> { - let paths: Paths = self.metadata.build(merge_binary)?.into_iter().collect(); - paths.get(key).cloned() + let manifest = test::Test::write_manifest(name, packages); + let metadata = read_metadata(&manifest, "system-deps", merge_binary)?; + let paths = metadata.into_iter().collect(); + + Ok(Self { manifest, paths }) } } @@ -60,21 +58,21 @@ fn replace_paths(name: &str, table: &mut Table) { } } -fn assert_paths(paths: &[PathBuf], expected: &[&str]) { +fn assert_paths(paths: Option<&Vec>, expected: &[&str]) { assert_set( - paths, + paths.into_iter().flatten(), &expected .iter() - .map(|p| Path::new(env!("OUT_DIR")).join(p)) + .map(|s| Path::new(BUILD_TARGET_DIR).join(s)) .collect::>(), - ); + ) } fn get_archives(web: Option<&str>) -> (PathBuf, Vec<(Table, &str, String, &str)>) { let base_path = Path::new(BUILD_MANIFEST) .parent() .unwrap() - .join("meta/src/test/files"); + .join("src/tests"); let mut archives = vec![ #[cfg(feature = "gz")] @@ -98,7 +96,7 @@ fn get_archives(web: Option<&str>) -> (PathBuf, Vec<(Table, &str, String, &str)> ]; if web.is_none() { - archives.push(("folder", "", "")); + archives.push(("test", "uninstalled", "")); } let archives = archives @@ -114,10 +112,20 @@ fn get_archives(web: Option<&str>) -> (PathBuf, Vec<(Table, &str, String, &str)> r#" [package.metadata.system-deps.{}] name = "{}" + version = "1.2.3" url = "{}" checksum = "{}" - paths = [ "lib/pkgconfig" ]"#, - name, name, url, checksum, + {}"#, + name, + name, + url, + checksum, + if name == "test" { + // To test the info.toml + "" + } else { + r#"paths = ["lib/pkgconfig"]"# + } ); ( @@ -132,10 +140,7 @@ fn get_archives(web: Option<&str>) -> (PathBuf, Vec<(Table, &str, String, &str)> (base_path, archives) } -// TODO: Version test -// TODO: These tests should be moved to system_deps base, check pkgconfig -// TODO: Change unwraps for specific errors -// TODO: Find a way of printing progress +// TODO: Library versions test #[test] fn simple() -> Result<(), Error> { @@ -149,9 +154,8 @@ fn simple() -> Result<(), Error> { ], }]; - let test = Test::new_bin("simple", pkgs)?; - let paths = test.get_paths("dep")?; - assert_paths(&paths, &["dep/lib/pkgconfig"]); + let test = Test::new("simple", pkgs)?; + assert_paths(test.paths.get("dep"), &["dep/lib/pkgconfig"]); Ok(()) } @@ -179,9 +183,8 @@ fn overrides() -> Result<(), Error> { }, ]; - let test = Test::new_bin("overrides", pkgs)?; - let paths = test.get_paths("dep")?; - assert_paths(&paths, &["dep/new", "dep/old"]); + let test = Test::new("overrides", pkgs)?; + assert_paths(test.paths.get("dep"), &["dep/new", "dep/old"]); Ok(()) } @@ -211,11 +214,9 @@ fn provides() -> Result<(), Error> { }, ]; - let test = Test::new_bin("provides", pkgs)?; - let pkg = test.get_paths("pkg")?; - assert_paths(&pkg, &["pkg/lib/pkgconfig"]); - let dep = test.get_paths("dep")?; - assert_paths(&dep, &["pkg/lib/pkgconfig"]); + let test = Test::new("provides", pkgs)?; + assert_paths(test.paths.get("pkg"), &["pkg/lib/pkgconfig"]); + assert_paths(test.paths.get("dep"), &["pkg/lib/pkgconfig"]); Ok(()) } @@ -253,11 +254,9 @@ fn provides_override() -> Result<(), Error> { }, ]; - let test = Test::new_bin("provides_override", pkgs)?; - let pkg = test.get_paths("pkg")?; - assert_paths(&pkg, &["pkg/lib/pkgconfig"]); - let dep = test.get_paths("dep")?; - assert_paths(&dep, &["dep/lib/pkgconfig"]); + let test = Test::new("provides_override", pkgs)?; + assert_paths(test.paths.get("pkg"), &["pkg/lib/pkgconfig"]); + assert_paths(test.paths.get("dep"), &["dep/lib/pkgconfig"]); Ok(()) } @@ -300,14 +299,71 @@ fn provides_conflict() -> Result<(), Error> { }, ]; - let test = Test::new_bin("provides_conflict", pkgs)?; - let res = test.get_paths("pkg"); - println!("left: {:#?}", res); + let res = Test::new("provides_conflict", pkgs); + println!("left: {:?}", res); assert!(matches!(res, Err(Error::IncompatibleMerge))); Ok(()) } +#[test] +fn provides_wildcard() -> Result<(), Error> { + let mut pkgs = vec![ + Package { + name: "pkg", + deps: vec!["dep1", "dep2"], + config: toml::toml![ + [package.metadata.system-deps.pkg] + name = "pkg" + url = "$TEST" + paths = [ "lib/pkgconfig" ] + provides = [ "dep*" ] + ], + }, + Package { + name: "dep1", + deps: vec![], + config: toml::toml![ + [package.metadata.system-deps.dep1] + name = "dep1" + ], + }, + Package { + name: "dep2", + deps: vec![], + config: toml::toml![ + [package.metadata.system-deps.dep2] + name = "dep2" + ], + }, + ]; + + let test = Test::new("provides_wildcard", pkgs.clone())?; + assert_paths(test.paths.get("pkg"), &["pkg/lib/pkgconfig"]); + assert_paths(test.paths.get("dep1"), &["pkg/lib/pkgconfig"]); + assert_paths(test.paths.get("dep2"), &["pkg/lib/pkgconfig"]); + + pkgs.insert( + 0, + Package { + name: "main", + deps: vec!["pkg"], + config: toml::toml![ + [package.metadata.system-deps.dep2] + url = "$TEST" + paths = [ "lib/pkgconfig" ] + ], + }, + ); + + let test = Test::new("provides_wildcard_overwrite", pkgs)?; + assert_paths(test.paths.get("pkg"), &["pkg/lib/pkgconfig"]); + assert_paths(test.paths.get("dep1"), &["pkg/lib/pkgconfig"]); + assert_paths(test.paths.get("dep2"), &["dep2/lib/pkgconfig"]); + + Ok(()) +} + #[test] fn file_types() -> Result<(), Error> { let (_, archives) = get_archives(None); @@ -319,20 +375,18 @@ fn file_types() -> Result<(), Error> { config, }]; - let test = Test::new_bin(name, pkgs)?; - let mut paths = test.get_paths(name)?; - let mut p = paths.pop().unwrap(); - assert!(paths.is_empty()); + let test = Test::new(name, pkgs)?; + let paths = test.paths.get(name).expect("There should be a path"); + assert!(paths.len() == 1); + let mut p = paths[0].clone(); - println!("{:?}", p); assert!(p.join("test.pc").is_file()); p.pop(); assert!(p.join("libtest.a").is_file()); p.pop(); - // TODO: Folder - if name == "folder" { - println!("URL {:?}", url); + if name == "test" { + // Local folder assert_eq!(p.read_link().unwrap(), Path::new(&url)); } else { p.push("checksum"); @@ -345,8 +399,59 @@ fn file_types() -> Result<(), Error> { } #[test] -//#[cfg(feature = "gz")] +fn unsupported_extensions() -> Result<(), Error> { + let mut pkgs = vec![Package { + name: "dep", + deps: vec![], + config: toml::toml![ + [package.metadata.system-deps.dep] + url = "http://unsuported.ext" + ], + }]; + + let res = std::panic::catch_unwind(|| Test::new("unsupported_extension", pkgs.clone())); + assert!(res.is_err()); + + pkgs[0].config = toml::toml![ + [package.metadata.system-deps.dep] + url = "http://no_ext" + ]; + + let res = std::panic::catch_unwind(|| Test::new("no_extension", pkgs)); + assert!(res.is_err()); + + Ok(()) +} + +#[test] +fn invalid_checksum() -> Result<(), Error> { + let base_path = get_archives(None).0; + let pkgs = vec![Package { + name: "checksum", + deps: vec![], + config: toml::from_str(&format!( + r#" + [package.metadata.system-deps.not_found] + url = "file://{}/test.zip" + checksum = "1234" + "#, + base_path.display() + ))?, + }]; + + let res = std::panic::catch_unwind(|| Test::new("invalid_checksum", pkgs)); + assert!(res.is_err()); + + Ok(()) +} + +#[test] +#[cfg(any(feature = "gz", feature = "xz", feature = "zip"))] fn download() -> Result<(), Error> { + use std::{convert::TryInto, sync::Arc, thread, time::Duration}; + use system_deps_meta::binary::Extension; + use tiny_http::{Header, Response, Server, StatusCode}; + let server_url = "127.0.0.1:8000"; let (base_path, archives) = get_archives(Some(server_url)); @@ -369,11 +474,11 @@ fn download() -> Result<(), Error> { Extension::TarXz => "application/zlib", #[cfg(feature = "zip")] Extension::Zip => "application/zip", - Extension::Folder => "text/plain", + _ => unreachable!(), }; let header = Header { field: "Content-Type".parse().unwrap(), - value: FromStr::from_str(content_type).unwrap(), + value: std::str::FromStr::from_str(content_type).unwrap(), }; match fs::File::open(url) { @@ -396,13 +501,26 @@ fn download() -> Result<(), Error> { config, }]; - let test = Test::new_bin(name, pkgs)?; - let mut paths = test.get_paths(name)?; - assert_eq!(paths.len(), 1); - - path_list.push((paths.pop().unwrap(), checksum.to_string())); + let test = Test::new(name, pkgs)?; + let paths = test.paths.get(name).expect("There should be a path"); + assert!(paths.len() == 1); + path_list.push((paths[0].clone(), checksum.to_string())); } + let pkgs = vec![Package { + name: "not_found", + deps: vec![], + config: toml::from_str(&format!( + r#" + [package.metadata.system-deps.not_found] + url = "http://{}/not_found.zip" + "#, + server_url + ))?, + }]; + let res = std::panic::catch_unwind(|| Test::new("not_found", pkgs)); + assert!(res.is_err()); + handle.unblock(); Ok(path_list) })?; @@ -419,3 +537,53 @@ fn download() -> Result<(), Error> { } Ok(()) } + +#[test] +fn probe() -> Result<(), Error> { + static PATHS: OnceLock = OnceLock::new(); + + let pkgs = vec![Package { + name: "test", + deps: vec![], + config: get_archives(None).1.into_iter().last().unwrap().0, + }]; + + let test = Test::new("probe", pkgs)?; + + let mut config = Config::new_with_env(EnvVariables::Mock(HashMap::from([( + "CARGO_MANIFEST_DIR", + test.manifest.parent().unwrap().to_string_lossy().into(), + )]))); + + let test_path = test.paths.get("test").unwrap().clone(); + config.paths = PATHS.get_or_init(|| test.paths); + assert_eq!(config.query_path("test").unwrap(), &test_path); + + let libs = config.probe_full().unwrap(); + let testlib = libs.get_by_name("test").unwrap(); + assert_eq!(testlib.version, "1.2.3"); + assert!(testlib.statik); + + Ok(()) +} + +#[test] +fn internal_pkg_config() -> Result<(), Error> { + let pkgs = vec![Package { + name: "test", + deps: vec![], + config: get_archives(None).1.into_iter().last().unwrap().0, + }]; + + let test = Test::new("internal_pkg_config", pkgs)?; + let paths = test.paths.get("test").unwrap(); + + let lib = Library::from_internal_pkg_config(paths, "test", "1.0").unwrap(); + assert_eq!(lib.version, "1.2.3"); + + let lib = Library::from_internal_pkg_config(paths, "test", "2.0"); + println!("left: {:?}", lib); + assert!(matches!(lib, Err(BuildInternalClosureError::PkgConfig(_)))); + + Ok(()) +} diff --git a/meta/src/test/files/test.tar.gz b/src/tests/test.tar.gz similarity index 100% rename from meta/src/test/files/test.tar.gz rename to src/tests/test.tar.gz diff --git a/meta/src/test/files/test.tar.xz b/src/tests/test.tar.xz similarity index 100% rename from meta/src/test/files/test.tar.xz rename to src/tests/test.tar.xz diff --git a/meta/src/test/files/test.zip b/src/tests/test.zip similarity index 100% rename from meta/src/test/files/test.zip rename to src/tests/test.zip diff --git a/src/tests/uninstalled/info.toml b/src/tests/uninstalled/info.toml new file mode 100644 index 0000000..d9de98f --- /dev/null +++ b/src/tests/uninstalled/info.toml @@ -0,0 +1 @@ +paths = [ "lib/pkgconfig" ] diff --git a/meta/src/test/files/lib/libtest.a b/src/tests/uninstalled/lib/libtest.a similarity index 100% rename from meta/src/test/files/lib/libtest.a rename to src/tests/uninstalled/lib/libtest.a diff --git a/meta/src/test/files/lib/pkgconfig/test.pc b/src/tests/uninstalled/lib/pkgconfig/test.pc similarity index 100% rename from meta/src/test/files/lib/pkgconfig/test.pc rename to src/tests/uninstalled/lib/pkgconfig/test.pc