Skip to content

Commit

Permalink
allow to define target specific dependencies
Browse files Browse the repository at this point in the history
Fix #33
  • Loading branch information
Guillaume Desmottes authored and gdesmott committed Mar 17, 2021
1 parent 9ac6f2f commit e68b15d
Show file tree
Hide file tree
Showing 6 changed files with 158 additions and 5 deletions.
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ strum_macros = "0.20"
thiserror = "1"
anyhow = "1.0"
itertools = "0.10"
cfg-expr = "0.7.4"

[dev-dependencies]
lazy_static = "1"
Expand Down
53 changes: 53 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,31 @@
//! v1_18 = { version = "1.18", name = "gstreamer-gl-egl-1.0" }
//! ```
//!
//! # Target specific dependencies
//!
//! You can define target specific dependencies:
//!
//! ```toml
//! [package.metadata.system-deps.'cfg(target_os = "linux")']
//! testdata = "1"
//! [package.metadata.system-deps.'cfg(not(target_os = "macos"))']
//! testlib = "1"
//! [package.metadata.system-deps.'cfg(unix)']
//! testanotherlib = { version = "1", optional = true }
//! ```
//!
//! See [the Rust documentation](https://doc.rust-lang.org/reference/conditional-compilation.html)
//! for the exact syntax.
//! Currently those keys are supported:
//! - `target_arch`
//! - `target_endian`
//! - `target_env`
//! - `target_family`
//! - `target_os`
//! - `target_pointer_width`
//! - `target_vendor`
//! - `unix` and `windows`
//!
//! # Overriding build flags
//! By default `system-deps` automatically defines the required build flags for each dependency using the information fetched from `pkg-config`.
//! These flags can be overriden using environment variables if needed:
Expand Down Expand Up @@ -189,6 +214,9 @@ pub enum Error {
/// required version defined in `Cargo.toml`
#[error("Internally built {0} {1} but minimum required version is {2}")]
BuildInternalWrongVersion(String, String, String),
/// The `cfg()` expression used in `Cargo.toml` is currently not supported
#[error("Unsupported cfg() expression: {0}")]
UnsupportedCfg(String),
}

#[derive(Debug, Default)]
Expand Down Expand Up @@ -544,6 +572,13 @@ impl Config {
let mut libraries = Dependencies::default();

for dep in metadata.deps.iter() {
if let Some(cfg) = &dep.cfg {
// Check if `cfg()` expression matches the target settings
if !self.check_cfg(cfg)? {
continue;
}
}

let mut enabled_feature_overrides = Vec::new();

for o in dep.version_overrides.iter() {
Expand Down Expand Up @@ -663,6 +698,24 @@ impl Config {
let var: &str = &format!("CARGO_FEATURE_{}", feature.to_uppercase().replace('-', "_"));
self.env.contains(var)
}

fn check_cfg(&self, cfg: &cfg_expr::Expression) -> Result<bool, Error> {
use cfg_expr::{targets::get_builtin_target_by_triple, Predicate};

let target = self
.env
.get("TARGET")
.expect("no TARGET env variable defined");
let target = get_builtin_target_by_triple(&target)
.unwrap_or_else(|| panic!("Invalid TARGET: {}", target));

let res = cfg.eval(|pred| match pred {
Predicate::Target(tp) => Some(tp.matches(target)),
_ => None,
});

res.ok_or_else(|| Error::UnsupportedCfg(cfg.original().to_string()))
}
}

#[derive(Debug, PartialEq)]
Expand Down
64 changes: 59 additions & 5 deletions src/metadata.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ pub(crate) struct Dependency {
pub(crate) name: Option<String>,
pub(crate) feature: Option<String>,
pub(crate) optional: bool,
pub(crate) cfg: Option<cfg_expr::Expression>,
pub(crate) version_overrides: Vec<VersionOverride>,
}

Expand All @@ -41,6 +42,7 @@ impl Default for Dependency {
name: None,
feature: None,
optional: false,
cfg: None,
version_overrides: Vec::new(),
}
}
Expand Down Expand Up @@ -112,22 +114,41 @@ impl MetaData {
.and_then(|v| v.get("system-deps"))
.ok_or_else(|| anyhow!("no {}", key))?;

let deps = Self::parse_deps_table(meta, key)?;
let deps = Self::parse_deps_table(meta, key, true)?;

Ok(MetaData { deps })
}

fn parse_deps_table(table: &Value, key: &str) -> Result<Vec<Dependency>, Error> {
fn parse_deps_table(
table: &Value,
key: &str,
allow_cfg: bool,
) -> Result<Vec<Dependency>, Error> {
let table = table
.as_table()
.ok_or_else(|| anyhow!("{} not a table", key))?;

let mut deps = Vec::new();

for (name, value) in table {
let dep =
Self::parse_dep(name, value).map_err(|e| anyhow!("{}.{}: {}", key, name, e))?;
deps.push(dep);
if name.starts_with("cfg(") {
if allow_cfg {
let cfg_exp = cfg_expr::Expression::parse(name)?;

for mut dep in
Self::parse_deps_table(value, &format!("{}.{}", key, name), false)?
{
dep.cfg = Some(cfg_exp.clone());
deps.push(dep);
}
} else {
bail!("{}.{}: cfg() cannot be nested", key, name);
}
} else {
let dep =
Self::parse_dep(name, value).map_err(|e| anyhow!("{}.{}: {}", key, name, e))?;
deps.push(dep);
}
}

Ok(deps)
Expand Down Expand Up @@ -208,6 +229,7 @@ impl MetaData {
mod tests {
use super::*;
use assert_matches::assert_matches;
use cfg_expr::Expression;
use std::path::PathBuf;

fn parse_file(dir: &str) -> Result<MetaData, crate::Error> {
Expand Down Expand Up @@ -353,4 +375,36 @@ mod tests {
}
)
}

#[test]
fn parse_os_specific() {
let m = parse_file("toml-os-specific").unwrap();

assert_eq!(
m,
MetaData {
deps: vec![
Dependency {
key: "testlib".into(),
version: Some("1".into()),
cfg: Some(Expression::parse("not(target_os = \"macos\")").unwrap()),
..Default::default()
},
Dependency {
key: "testdata".into(),
version: Some("1".into()),
cfg: Some(Expression::parse("target_os = \"linux\"").unwrap()),
..Default::default()
},
Dependency {
key: "testanotherlib".into(),
version: Some("1".into()),
cfg: Some(Expression::parse("unix").unwrap()),
optional: true,
..Default::default()
},
]
}
)
}
}
37 changes: 37 additions & 0 deletions src/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -823,3 +823,40 @@ fn aggregate() {
]
);
}

#[test]
fn os_specific() {
let (libraries, _) = toml(
"toml-os-specific",
vec![("TARGET", "x86_64-unknown-linux-gnu")],
)
.unwrap();
assert!(libraries.get_by_name("testdata").is_some());
assert!(libraries.get_by_name("testlib").is_some());
assert!(libraries.get_by_name("testanotherlib").is_some());

let (libraries, _) = toml("toml-os-specific", vec![("TARGET", "x86_64-apple-darwin")]).unwrap();
assert!(libraries.get_by_name("testdata").is_none());
assert!(libraries.get_by_name("testlib").is_none());
assert!(libraries.get_by_name("testanotherlib").is_some());

let (libraries, _) = toml(
"toml-os-specific",
vec![("TARGET", "x86_64-pc-windows-gnu")],
)
.unwrap();
assert!(libraries.get_by_name("testdata").is_none());
assert!(libraries.get_by_name("testlib").is_some());
assert!(libraries.get_by_name("testanotherlib").is_none());
}

#[test]
fn invalid_cfg() {
let err = toml(
"toml-invalid-cfg",
vec![("TARGET", "x86_64-unknown-linux-gnu")],
)
.unwrap_err();

assert_matches!(err, Error::UnsupportedCfg(_));
}
2 changes: 2 additions & 0 deletions src/tests/toml-invalid-cfg/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
[package.metadata.system-deps.'cfg(badger)']
testanotherlib = { version = "1", optional = true }
6 changes: 6 additions & 0 deletions src/tests/toml-os-specific/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
[package.metadata.system-deps.'cfg(target_os = "linux")']
testdata = "1"
[package.metadata.system-deps.'cfg(not(target_os = "macos"))']
testlib = "1"
[package.metadata.system-deps.'cfg(unix)']
testanotherlib = { version = "1", optional = true }

0 comments on commit e68b15d

Please sign in to comment.