Skip to content

Commit

Permalink
polishing touches
Browse files Browse the repository at this point in the history
  • Loading branch information
eerii committed Dec 13, 2024
1 parent 4472359 commit c6e2884
Show file tree
Hide file tree
Showing 18 changed files with 951 additions and 755 deletions.
6 changes: 5 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -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" ]
12 changes: 6 additions & 6 deletions build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ 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},
Expand All @@ -23,10 +23,10 @@ mod binary {
let paths: Paths = metadata.build(merge_binary)?.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(())
}
}
5 changes: 1 addition & 4 deletions meta/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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 = [ ]
111 changes: 70 additions & 41 deletions meta/src/binary.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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::{
Expand Down Expand Up @@ -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("<error>".into()));
};

match ext {
#[cfg(feature = "gz")]
e if e == "gz" || e == "tgz" => Ok(Extension::TarGz),
Expand Down Expand Up @@ -123,8 +121,14 @@ pub struct UrlBinary {
paths: Option<Vec<String>>,
}

#[derive(Debug)]
pub struct Paths(HashMap<String, Vec<PathBuf>>);
// TODO: Get paths from a toml file in the binary itself

#[derive(Debug, Default, Deserialize, Serialize)]
pub struct Paths {
paths: HashMap<String, Vec<PathBuf>>,
follows: HashMap<String, String>,
wildcards: HashMap<String, String>,
}

impl<T> FromIterator<(String, T)> for Paths
where
Expand All @@ -137,7 +141,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<I: IntoIterator<Item = (String, T)>>(binaries: I) -> Self {
let mut paths: HashMap<String, Vec<PathBuf>> = HashMap::new();
let mut res = Self::default();

let (url_binaries, follow_binaries): (Vec<_>, Vec<_>) = binaries
.into_iter()
Expand All @@ -152,7 +156,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(),
);
Expand All @@ -163,51 +167,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::<Table>(&info) else {
continue;

Check warning on line 182 in meta/src/binary.rs

View check run for this annotation

Codecov / codecov/patch

meta/src/binary.rs#L182

Added line #L182 was not covered by tests
};
if let Some(Value::Array(paths)) = table.get("paths") {
for p in paths.into_iter().filter_map(|p| p.as_str()) {
let p = dst.join(p);
if !list.contains(&p) {
list.push(p);
}
}
}

Check warning on line 191 in meta/src/binary.rs

View check run for this annotation

Codecov / codecov/patch

meta/src/binary.rs#L191

Added line #L191 was not covered by tests
}

// Binaries that follow others
for (name, bin) in follow_binaries {
let Binary::Follow(bin) = bin else {
unreachable!();

Check warning on line 197 in meta/src/binary.rs

View check run for this annotation

Codecov / codecov/patch

meta/src/binary.rs#L197

Added line #L197 was not covered by tests
};
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));

Check warning on line 200 in meta/src/binary.rs

View check run for this annotation

Codecov / codecov/patch

meta/src/binary.rs#L200

Added line #L200 was not covered by tests
};
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<PathBuf>, 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<PathBuf>> {
if let Some(paths) = self.paths.get(key) {
return Some(paths);
};

pub fn build(self, path: impl AsRef<Path>) -> 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<String, Error> {
Ok(toml::to_string(self)?)
}
}

Expand Down Expand Up @@ -237,19 +260,23 @@ fn check_valid_dir(dst: &Path, checksum: Option<&str>) -> Result<bool, BinaryErr

/// Retrieve a binary archive from the specified `url` and decompress it in the target directory.
/// "Download" is used as an umbrella term, since this can also be a local file.
fn make_available(url: String, checksum: Option<String>, 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<Mutex<()>> = 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("<folder>".into()));

Check warning on line 278 in meta/src/binary.rs

View check run for this annotation

Codecov / codecov/patch

meta/src/binary.rs#L278

Added line #L278 was not covered by tests
}
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() {
Expand All @@ -267,16 +294,17 @@ fn make_available(url: String, checksum: Option<String>, 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("<empty>".into()),
bin.checksum.unwrap_or("<empty>".into()),
calculated,
)),
}?;
Expand All @@ -285,6 +313,7 @@ fn make_available(url: String, checksum: Option<String>, dst: &Path) -> Result<(

// Decompress the binary archive
decompress(&file, dst, ext)?;

Ok(())
}

Expand Down
1 change: 0 additions & 1 deletion meta/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
6 changes: 4 additions & 2 deletions meta/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
//#![warn(missing_docs)]

pub mod error;
pub mod parse;
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");
Expand Down
Loading

0 comments on commit c6e2884

Please sign in to comment.