From caad78bb5d17616261c12d0b2b809f20f0a7fc0c Mon Sep 17 00:00:00 2001 From: Guillaume Desmottes Date: Thu, 30 Apr 2020 09:54:19 +0200 Subject: [PATCH 01/55] run cargo fmt --- src/lib.rs | 49 ++++++++++++++++++++++++++++++------------------- tests/test.rs | 45 ++++++++++++++++++++++++++++++++++++--------- 2 files changed, 66 insertions(+), 28 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 699afce..eb9446f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -17,12 +17,12 @@ extern crate error_chain; extern crate pkg_config; extern crate toml; +use pkg_config::{Config, Library}; use std::collections::HashMap; use std::env; use std::fs; use std::io::Read; use std::path::PathBuf; -use pkg_config::{Config, Library}; error_chain! { foreign_links { @@ -36,23 +36,24 @@ pub fn probe() -> Result> { let dir = try!(env::var_os("CARGO_MANIFEST_DIR").ok_or("$CARGO_MANIFEST_DIR not set")); let mut path = PathBuf::from(dir); path.push("Cargo.toml"); - let mut manifest = try!(fs::File::open(&path).chain_err(|| - format!("Error opening {}", path.display()) - )); + let mut manifest = + try!(fs::File::open(&path).chain_err(|| format!("Error opening {}", path.display()))); let mut manifest_str = String::new(); - try!(manifest.read_to_string(&mut manifest_str).chain_err(|| - format!("Error reading {}", path.display()) - )); - let toml = try!(manifest_str.parse::().map_err(|e| - format!("Error parsing TOML from {}: {:?}", path.display(), e) - )); + try!(manifest + .read_to_string(&mut manifest_str) + .chain_err(|| format!("Error reading {}", path.display()))); + let toml = try!(manifest_str.parse::().map_err(|e| format!( + "Error parsing TOML from {}: {:?}", + path.display(), + e + ))); let key = "package.metadata.pkg-config"; - let meta = try!(toml.lookup(key).ok_or( - format!("No {} in {}", key, path.display()) - )); - let table = try!(meta.as_table().ok_or( - format!("{} not a table in {}", key, path.display()) - )); + let meta = try!(toml + .lookup(key) + .ok_or(format!("No {} in {}", key, path.display()))); + let table = try!(meta + .as_table() + .ok_or(format!("{} not a table in {}", key, path.display()))); let mut libraries = HashMap::new(); for (name, value) in table { let ref version = match value { @@ -62,9 +63,19 @@ pub fn probe() -> Result> { let mut version = None; for (tname, tvalue) in t { match (tname.as_str(), tvalue) { - ("feature", &toml::Value::String(ref s)) => { feature = Some(s); } - ("version", &toml::Value::String(ref s)) => { version = Some(s); } - _ => bail!("Unexpected key {}.{}.{} type {}", key, name, tname, tvalue.type_str()), + ("feature", &toml::Value::String(ref s)) => { + feature = Some(s); + } + ("version", &toml::Value::String(ref s)) => { + version = Some(s); + } + _ => bail!( + "Unexpected key {}.{}.{} type {}", + key, + name, + tname, + tvalue.type_str() + ), } } if let Some(feature) = feature { diff --git a/tests/test.rs b/tests/test.rs index b31c4f5..bdc402a 100644 --- a/tests/test.rs +++ b/tests/test.rs @@ -12,8 +12,14 @@ lazy_static! { fn toml(path: &str) -> metadeps::Result> { let _l = LOCK.lock(); - env::set_var("PKG_CONFIG_PATH", &env::current_dir().unwrap().join("tests")); - env::set_var("CARGO_MANIFEST_DIR", &env::current_dir().unwrap().join("tests").join(path)); + env::set_var( + "PKG_CONFIG_PATH", + &env::current_dir().unwrap().join("tests"), + ); + env::set_var( + "CARGO_MANIFEST_DIR", + &env::current_dir().unwrap().join("tests").join(path), + ); env::set_var("CARGO_FEATURE_TEST_FEATURE", ""); metadeps::probe() } @@ -31,7 +37,10 @@ fn good() { fn toml_err(path: &str, err_starts_with: &str) { let err = toml(path).unwrap_err(); if !err.description().starts_with(err_starts_with) { - panic!("Expected error to start with: {:?}\nGot error: {:?}", err_starts_with, err); + panic!( + "Expected error to start with: {:?}\nGot error: {:?}", + err_starts_with, err + ); } } @@ -47,30 +56,48 @@ fn missing_key() { #[test] fn not_table() { - toml_err("toml-not-table", "package.metadata.pkg-config not a table in"); + toml_err( + "toml-not-table", + "package.metadata.pkg-config not a table in", + ); } #[test] fn version_missing() { - toml_err("toml-version-missing", "No version in package.metadata.pkg-config.testlib"); + toml_err( + "toml-version-missing", + "No version in package.metadata.pkg-config.testlib", + ); } #[test] fn version_not_string() { - toml_err("toml-version-not-string", "package.metadata.pkg-config.testlib not a string or table"); + toml_err( + "toml-version-not-string", + "package.metadata.pkg-config.testlib not a string or table", + ); } #[test] fn version_in_table_not_string() { - toml_err("toml-version-in-table-not-string", "Unexpected key package.metadata.pkg-config.testlib.version type integer"); + toml_err( + "toml-version-in-table-not-string", + "Unexpected key package.metadata.pkg-config.testlib.version type integer", + ); } #[test] fn feature_not_string() { - toml_err("toml-feature-not-string", "Unexpected key package.metadata.pkg-config.testlib.feature type integer"); + toml_err( + "toml-feature-not-string", + "Unexpected key package.metadata.pkg-config.testlib.feature type integer", + ); } #[test] fn unexpected_key() { - toml_err("toml-unexpected-key", "Unexpected key package.metadata.pkg-config.testlib.color type string"); + toml_err( + "toml-unexpected-key", + "Unexpected key package.metadata.pkg-config.testlib.color type string", + ); } From a78bfacd88d873e4636d7fc21dab1fc2fc641609 Mon Sep 17 00:00:00 2001 From: Guillaume Desmottes Date: Thu, 30 Apr 2020 09:56:05 +0200 Subject: [PATCH 02/55] use ? instead of deprecated try! --- src/lib.rs | 28 +++++++++++++--------------- 1 file changed, 13 insertions(+), 15 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index eb9446f..58ca4c3 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -33,27 +33,25 @@ error_chain! { /// Probe all libraries configured in the Cargo.toml /// `[package.metadata.pkg-config]` section. pub fn probe() -> Result> { - let dir = try!(env::var_os("CARGO_MANIFEST_DIR").ok_or("$CARGO_MANIFEST_DIR not set")); + let dir = env::var_os("CARGO_MANIFEST_DIR").ok_or("$CARGO_MANIFEST_DIR not set")?; let mut path = PathBuf::from(dir); path.push("Cargo.toml"); let mut manifest = - try!(fs::File::open(&path).chain_err(|| format!("Error opening {}", path.display()))); + fs::File::open(&path).chain_err(|| format!("Error opening {}", path.display()))?; let mut manifest_str = String::new(); - try!(manifest + manifest .read_to_string(&mut manifest_str) - .chain_err(|| format!("Error reading {}", path.display()))); - let toml = try!(manifest_str.parse::().map_err(|e| format!( - "Error parsing TOML from {}: {:?}", - path.display(), - e - ))); + .chain_err(|| format!("Error reading {}", path.display()))?; + let toml = manifest_str + .parse::() + .map_err(|e| format!("Error parsing TOML from {}: {:?}", path.display(), e))?; let key = "package.metadata.pkg-config"; - let meta = try!(toml + let meta = toml .lookup(key) - .ok_or(format!("No {} in {}", key, path.display()))); - let table = try!(meta + .ok_or(format!("No {} in {}", key, path.display()))?; + let table = meta .as_table() - .ok_or(format!("{} not a table in {}", key, path.display()))); + .ok_or(format!("{} not a table in {}", key, path.display()))?; let mut libraries = HashMap::new(); for (name, value) in table { let ref version = match value { @@ -84,11 +82,11 @@ pub fn probe() -> Result> { continue; } } - try!(version.ok_or(format!("No version in {}.{}", key, name))) + version.ok_or(format!("No version in {}.{}", key, name))? } _ => bail!("{}.{} not a string or table", key, name), }; - let library = try!(Config::new().atleast_version(&version).probe(name)); + let library = Config::new().atleast_version(&version).probe(name)?; libraries.insert(name.clone(), library); } Ok(libraries) From 2c7f626252afd0ccf2867c194d72a63b420bdebd Mon Sep 17 00:00:00 2001 From: Guillaume Desmottes Date: Thu, 30 Apr 2020 09:57:47 +0200 Subject: [PATCH 03/55] update error-chain dep Fix std::error::Error::description deprecation warning. --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 8e5328e..6531180 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,7 +8,7 @@ description = "Run pkg-config from declarative dependencies in Cargo.toml" keywords = ["pkg-config", "build-dependencies", "build-depends", "manifest", "metadata"] [dependencies] -error-chain = { version = "0.10", default-features = false } +error-chain = { version = "0.12", default-features = false } pkg-config = "0.3.8" toml = { version = "0.2", default-features = false } From 44460adb9a04bf251afaa825f3635aa1f4b8a70b Mon Sep 17 00:00:00 2001 From: Guillaume Desmottes Date: Thu, 30 Apr 2020 10:00:04 +0200 Subject: [PATCH 04/55] update toml dep --- Cargo.toml | 2 +- src/lib.rs | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 6531180..3a6f693 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,7 +10,7 @@ keywords = ["pkg-config", "build-dependencies", "build-depends", "manifest", "me [dependencies] error-chain = { version = "0.12", default-features = false } pkg-config = "0.3.8" -toml = { version = "0.2", default-features = false } +toml = { version = "0.5", default-features = false } [dev-dependencies] lazy_static = "1" diff --git a/src/lib.rs b/src/lib.rs index 58ca4c3..5142fc2 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -47,7 +47,9 @@ pub fn probe() -> Result> { .map_err(|e| format!("Error parsing TOML from {}: {:?}", path.display(), e))?; let key = "package.metadata.pkg-config"; let meta = toml - .lookup(key) + .get("package") + .and_then(|v| v.get("metadata")) + .and_then(|v| v.get("pkg-config")) .ok_or(format!("No {} in {}", key, path.display()))?; let table = meta .as_table() From 321a3080822749a0010eb4853bbcd8da43df3cdf Mon Sep 17 00:00:00 2001 From: Guillaume Desmottes Date: Thu, 30 Apr 2020 10:03:00 +0200 Subject: [PATCH 05/55] fix toplevel_ref_arg clippy warning --- src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index 5142fc2..a654550 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -56,7 +56,7 @@ pub fn probe() -> Result> { .ok_or(format!("{} not a table in {}", key, path.display()))?; let mut libraries = HashMap::new(); for (name, value) in table { - let ref version = match value { + let version = match value { &toml::Value::String(ref s) => s, &toml::Value::Table(ref t) => { let mut feature = None; From e1fc36eb0da2cc785d7d73fefe1053525c2865a2 Mon Sep 17 00:00:00 2001 From: Guillaume Desmottes Date: Thu, 30 Apr 2020 10:08:26 +0200 Subject: [PATCH 06/55] port to Rust 2018 --- Cargo.toml | 1 + src/lib.rs | 6 ++---- tests/test.rs | 2 -- 3 files changed, 3 insertions(+), 6 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 3a6f693..1da7efc 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,6 +6,7 @@ license = "MIT/Apache-2.0" repository = "https://github.com/joshtriplett/metadeps" description = "Run pkg-config from declarative dependencies in Cargo.toml" keywords = ["pkg-config", "build-dependencies", "build-depends", "manifest", "metadata"] +edition = "2018" [dependencies] error-chain = { version = "0.12", default-features = false } diff --git a/src/lib.rs b/src/lib.rs index a654550..4110dc3 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -14,8 +14,6 @@ #[macro_use] extern crate error_chain; -extern crate pkg_config; -extern crate toml; use pkg_config::{Config, Library}; use std::collections::HashMap; @@ -57,8 +55,8 @@ pub fn probe() -> Result> { let mut libraries = HashMap::new(); for (name, value) in table { let version = match value { - &toml::Value::String(ref s) => s, - &toml::Value::Table(ref t) => { + toml::Value::String(ref s) => s, + toml::Value::Table(ref t) => { let mut feature = None; let mut version = None; for (tname, tvalue) in t { diff --git a/tests/test.rs b/tests/test.rs index bdc402a..1b5ac3a 100644 --- a/tests/test.rs +++ b/tests/test.rs @@ -1,7 +1,5 @@ #[macro_use] extern crate lazy_static; -extern crate metadeps; -extern crate pkg_config; use std::env; use std::sync::Mutex; From 50b1eaca590b28cfc9c15987a77ff412f7c51bc7 Mon Sep 17 00:00:00 2001 From: Guillaume Desmottes Date: Thu, 30 Apr 2020 11:43:32 +0200 Subject: [PATCH 07/55] allow to override lib name Some libraries, such as glib-2.0, include a version number in their name in order to support parallel installation of multiple major versions. Such name can't be used as a toml key so we need a way to override it manually. --- README.md | 6 ++++-- src/lib.rs | 16 ++++++++++++---- tests/test.rs | 7 +++++++ tests/testlib-2.0.pc | 10 ++++++++++ tests/toml-override-name/Cargo.toml | 2 ++ 5 files changed, 35 insertions(+), 6 deletions(-) create mode 100644 tests/testlib-2.0.pc create mode 100644 tests/toml-override-name/Cargo.toml diff --git a/README.md b/README.md index 26db2d6..4453ca8 100644 --- a/README.md +++ b/README.md @@ -10,13 +10,15 @@ In your `Cargo.toml`, add the following to your `[build-dependencies]`: metadeps = "1.1" ``` -Then, to declare a dependency on `testlib >= 1.2`, and a conditional dependency -on `testdata >= 4.5`, add the following section: +Then, to declare a dependency on `testlib >= 1.2`, a conditional dependency +on `testdata >= 4.5` and a dependency on `glib-2.0 >= 2.64` +add the following section: ```toml [package.metadata.pkg-config] testlib = "1.2" testdata = { version = "4.5", feature = "use-testdata" } +glib = { name = "glib-2.0", version = "2.64" } ``` In your `build.rs`, add: diff --git a/src/lib.rs b/src/lib.rs index 4110dc3..9df1972 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -8,6 +8,7 @@ //! [package.metadata.pkg-config] //! testlib = "1.2" //! testdata = { version = "4.5", feature = "some-feature" } +//! glib = { name = "glib-2.0", version = "2.64" } //! ``` #![deny(missing_docs, warnings)] @@ -54,11 +55,12 @@ pub fn probe() -> Result> { .ok_or(format!("{} not a table in {}", key, path.display()))?; let mut libraries = HashMap::new(); for (name, value) in table { - let version = match value { - toml::Value::String(ref s) => s, + let (lib_name, version) = match value { + toml::Value::String(ref s) => (name, s), toml::Value::Table(ref t) => { let mut feature = None; let mut version = None; + let mut lib_name = None; for (tname, tvalue) in t { match (tname.as_str(), tvalue) { ("feature", &toml::Value::String(ref s)) => { @@ -67,6 +69,9 @@ pub fn probe() -> Result> { ("version", &toml::Value::String(ref s)) => { version = Some(s); } + ("name", &toml::Value::String(ref s)) => { + lib_name = Some(s); + } _ => bail!( "Unexpected key {}.{}.{} type {}", key, @@ -82,11 +87,14 @@ pub fn probe() -> Result> { continue; } } - version.ok_or(format!("No version in {}.{}", key, name))? + ( + lib_name.unwrap_or(name), + version.ok_or(format!("No version in {}.{}", key, name))?, + ) } _ => bail!("{}.{} not a string or table", key, name), }; - let library = Config::new().atleast_version(&version).probe(name)?; + let library = Config::new().atleast_version(&version).probe(lib_name)?; libraries.insert(name.clone(), library); } Ok(libraries) diff --git a/tests/test.rs b/tests/test.rs index 1b5ac3a..599a493 100644 --- a/tests/test.rs +++ b/tests/test.rs @@ -99,3 +99,10 @@ fn unexpected_key() { "Unexpected key package.metadata.pkg-config.testlib.color type string", ); } + +#[test] +fn override_name() { + let libraries = toml("toml-override-name").unwrap(); + let testlib = libraries.get("testlib").unwrap(); + assert_eq!(testlib.version, "2.0.0"); +} diff --git a/tests/testlib-2.0.pc b/tests/testlib-2.0.pc new file mode 100644 index 0000000..7866541 --- /dev/null +++ b/tests/testlib-2.0.pc @@ -0,0 +1,10 @@ +prefix=/usr +exec_prefix=${prefix} +libdir=${exec_prefix}/lib/x86_64-linux-gnu +includedir=${prefix}/include/testlib + +Name: Test Library +Description: A fake library to test pkg-config. +Version: 2.0.0 +Libs: L${libdir} -ltest +Cflags: -I${includedir} diff --git a/tests/toml-override-name/Cargo.toml b/tests/toml-override-name/Cargo.toml new file mode 100644 index 0000000..ce438e7 --- /dev/null +++ b/tests/toml-override-name/Cargo.toml @@ -0,0 +1,2 @@ +[package.metadata.pkg-config] +testlib = { name = "testlib-2.0", version = "2" } From 34951b370baf1b341259a94ebb152996c4c5cecb Mon Sep 17 00:00:00 2001 From: Guillaume Desmottes Date: Thu, 30 Apr 2020 14:23:13 +0200 Subject: [PATCH 08/55] factor out has_feature() --- src/lib.rs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 9df1972..ea58ec8 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -29,6 +29,11 @@ error_chain! { } } +fn has_feature(feature: &str) -> bool { + let var = format!("CARGO_FEATURE_{}", feature.to_uppercase().replace('-', "_")); + env::var_os(var).is_some() +} + /// Probe all libraries configured in the Cargo.toml /// `[package.metadata.pkg-config]` section. pub fn probe() -> Result> { @@ -82,8 +87,7 @@ pub fn probe() -> Result> { } } if let Some(feature) = feature { - let var = format!("CARGO_FEATURE_{}", feature.to_uppercase().replace('-', "_")); - if env::var_os(var).is_none() { + if !has_feature(feature) { continue; } } From 9557e93803649067fbb876b3148233f9674322cd Mon Sep 17 00:00:00 2001 From: Guillaume Desmottes Date: Thu, 30 Apr 2020 12:35:49 +0200 Subject: [PATCH 09/55] add feature-versions Allow user to specificy different versions depending on enabled features. This is especially useful for -sys crates aiming to not hard depend on the latest version of the C library they are wrapping. --- Cargo.toml | 1 + README.md | 13 ++++++++++ src/lib.rs | 35 ++++++++++++++++++++++++++ tests/test.rs | 34 +++++++++++++++++++++++++ tests/toml-feature-versions/Cargo.toml | 2 ++ 5 files changed, 85 insertions(+) create mode 100644 tests/toml-feature-versions/Cargo.toml diff --git a/Cargo.toml b/Cargo.toml index 1da7efc..513a929 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,6 +12,7 @@ edition = "2018" error-chain = { version = "0.12", default-features = false } pkg-config = "0.3.8" toml = { version = "0.5", default-features = false } +version-compare = "0.0.10" [dev-dependencies] lazy_static = "1" diff --git a/README.md b/README.md index 4453ca8..28bb67a 100644 --- a/README.md +++ b/README.md @@ -30,3 +30,16 @@ fn main() { metadeps::probe().unwrap(); } ``` + +Dependency versions can also be controlled using features: + +```toml +[features] +v1_2 = [] +v1_4 = ["v1_4"] + +[package.metadata.pkg-config] +gstreamer = { name = "gstreamer-1.0", version = "1.0", feature-versions = { v1_2 = "1.2", v1_4 = "1.4" }} +``` + +In this case the highest version among enabled features will be used. diff --git a/src/lib.rs b/src/lib.rs index ea58ec8..f53753e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -9,6 +9,7 @@ //! testlib = "1.2" //! testdata = { version = "4.5", feature = "some-feature" } //! glib = { name = "glib-2.0", version = "2.64" } +//! gstreamer = { name = "gstreamer-1.0", version = "1.0", feature-versions = { v1_2 = "1.2", v1_4 = "1.4" }} //! ``` #![deny(missing_docs, warnings)] @@ -22,6 +23,7 @@ use std::env; use std::fs; use std::io::Read; use std::path::PathBuf; +use version_compare::VersionCompare; error_chain! { foreign_links { @@ -66,6 +68,7 @@ pub fn probe() -> Result> { let mut feature = None; let mut version = None; let mut lib_name = None; + let mut enabled_feature_versions = Vec::new(); for (tname, tvalue) in t { match (tname.as_str(), tvalue) { ("feature", &toml::Value::String(ref s)) => { @@ -77,6 +80,22 @@ pub fn probe() -> Result> { ("name", &toml::Value::String(ref s)) => { lib_name = Some(s); } + ("feature-versions", &toml::Value::Table(ref feature_versions)) => { + for (k, v) in feature_versions { + match (k.as_str(), v) { + (_, &toml::Value::String(ref feat_vers)) => { + if has_feature(&k) { + enabled_feature_versions.push(feat_vers); + } + } + _ => bail!( + "Unexpected feature-version key: {} type {}", + k, + v.type_str() + ), + } + } + } _ => bail!( "Unexpected key {}.{}.{} type {}", key, @@ -91,6 +110,22 @@ pub fn probe() -> Result> { continue; } } + + let version = { + // Pick the highest feature enabled version + if !enabled_feature_versions.is_empty() { + enabled_feature_versions.sort_by(|a, b| { + VersionCompare::compare(b, a) + .expect("failed to compare versions") + .ord() + .expect("invalid version") + }); + Some(enabled_feature_versions[0]) + } else { + version + } + }; + ( lib_name.unwrap_or(name), version.ok_or(format!("No version in {}.{}", key, name))?, diff --git a/tests/test.rs b/tests/test.rs index 599a493..3485cd0 100644 --- a/tests/test.rs +++ b/tests/test.rs @@ -1,6 +1,7 @@ #[macro_use] extern crate lazy_static; +use pkg_config; use std::env; use std::sync::Mutex; @@ -42,6 +43,24 @@ fn toml_err(path: &str, err_starts_with: &str) { } } +// Assert a PkgConfig error because requested lib version cannot be found +fn toml_pkg_config_err_version(path: &str, expected_version: &str) { + let err = toml(path).unwrap_err(); + match err.kind() { + metadeps::ErrorKind::PkgConfig(e) => match e { + pkg_config::Error::Failure { + command: cmd, + output: _, + } => { + let s = format!(">= {}\"", expected_version); + assert!(cmd.ends_with(&s)); + } + _ => panic!("Wrong pkg-config error type"), + }, + _ => panic!("Wrong error type"), + } +} + #[test] fn missing_file() { toml_err("toml-missing-file", "Error opening"); @@ -106,3 +125,18 @@ fn override_name() { let testlib = libraries.get("testlib").unwrap(); assert_eq!(testlib.version, "2.0.0"); } + +#[test] +fn feature_versions() { + let libraries = toml("toml-feature-versions").unwrap(); + let testdata = libraries.get("testdata").unwrap(); + assert_eq!(testdata.version, "4.5.6"); + + // version 5 is not available + env::set_var("CARGO_FEATURE_V5", ""); + toml_pkg_config_err_version("toml-feature-versions", "5"); + + // We check the highest version enabled by features + env::set_var("CARGO_FEATURE_V6", ""); + toml_pkg_config_err_version("toml-feature-versions", "6"); +} diff --git a/tests/toml-feature-versions/Cargo.toml b/tests/toml-feature-versions/Cargo.toml new file mode 100644 index 0000000..92640a1 --- /dev/null +++ b/tests/toml-feature-versions/Cargo.toml @@ -0,0 +1,2 @@ +[package.metadata.pkg-config] +testdata = { version = "4", feature-versions = { v5 = "5", v6 = "6" }} From 07b8b703ff448193b9922a63d423794f6b549b7a Mon Sep 17 00:00:00 2001 From: Guillaume Desmottes Date: Thu, 30 Apr 2020 16:52:22 +0200 Subject: [PATCH 10/55] disable PKG_CONFIG_ALLOW_SYSTEM_LIBS For some reason pkg-config-rs sets PKG_CONFIG_ALLOW_SYSTEM_LIBS by default, breaking uninstalled builds, see https://github.com/rust-lang/pkg-config-rs/pull/35 Users should manually enable this if needed, by defining the env variable, and should not be enforced by the build system. --- src/lib.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index f53753e..0f8ca1f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -133,7 +133,10 @@ pub fn probe() -> Result> { } _ => bail!("{}.{} not a string or table", key, name), }; - let library = Config::new().atleast_version(&version).probe(lib_name)?; + let library = Config::new() + .atleast_version(&version) + .print_system_libs(false) + .probe(lib_name)?; libraries.insert(name.clone(), library); } Ok(libraries) From af9be0ff1b00caa71e93c94f8300f4db0aa32358 Mon Sep 17 00:00:00 2001 From: Guillaume Desmottes Date: Sat, 2 May 2020 12:54:28 +0200 Subject: [PATCH 11/55] move tests to the main crate Will allow us to easily test internal methods. --- src/lib.rs | 7 +++++++ {tests => src}/test.rs | 19 +++++++++++-------- {tests => src/tests}/testdata.pc | 0 {tests => src/tests}/testlib-2.0.pc | 0 {tests => src/tests}/testlib.pc | 0 .../tests}/toml-feature-not-string/Cargo.toml | 0 .../tests}/toml-feature-versions/Cargo.toml | 0 {tests => src/tests}/toml-good/Cargo.toml | 0 .../toml-missing-file/no-cargo-toml-here | 0 .../tests}/toml-missing-key/Cargo.toml | 0 .../tests}/toml-not-table/Cargo.toml | 0 .../tests}/toml-override-name/Cargo.toml | 0 .../tests}/toml-unexpected-key/Cargo.toml | 0 .../Cargo.toml | 0 .../tests}/toml-version-missing/Cargo.toml | 0 .../tests}/toml-version-not-string/Cargo.toml | 0 16 files changed, 18 insertions(+), 8 deletions(-) rename {tests => src}/test.rs (89%) rename {tests => src/tests}/testdata.pc (100%) rename {tests => src/tests}/testlib-2.0.pc (100%) rename {tests => src/tests}/testlib.pc (100%) rename {tests => src/tests}/toml-feature-not-string/Cargo.toml (100%) rename {tests => src/tests}/toml-feature-versions/Cargo.toml (100%) rename {tests => src/tests}/toml-good/Cargo.toml (100%) rename {tests => src/tests}/toml-missing-file/no-cargo-toml-here (100%) rename {tests => src/tests}/toml-missing-key/Cargo.toml (100%) rename {tests => src/tests}/toml-not-table/Cargo.toml (100%) rename {tests => src/tests}/toml-override-name/Cargo.toml (100%) rename {tests => src/tests}/toml-unexpected-key/Cargo.toml (100%) rename {tests => src/tests}/toml-version-in-table-not-string/Cargo.toml (100%) rename {tests => src/tests}/toml-version-missing/Cargo.toml (100%) rename {tests => src/tests}/toml-version-not-string/Cargo.toml (100%) diff --git a/src/lib.rs b/src/lib.rs index 0f8ca1f..97cdbd6 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -17,6 +17,13 @@ #[macro_use] extern crate error_chain; +#[cfg(test)] +#[macro_use] +extern crate lazy_static; + +#[cfg(test)] +mod test; + use pkg_config::{Config, Library}; use std::collections::HashMap; use std::env; diff --git a/tests/test.rs b/src/test.rs similarity index 89% rename from tests/test.rs rename to src/test.rs index 3485cd0..d92b35e 100644 --- a/tests/test.rs +++ b/src/test.rs @@ -1,26 +1,29 @@ -#[macro_use] -extern crate lazy_static; - use pkg_config; use std::env; use std::sync::Mutex; +use super::{probe, ErrorKind, Result}; + lazy_static! { static ref LOCK: Mutex<()> = Mutex::new(()); } -fn toml(path: &str) -> metadeps::Result> { +fn toml(path: &str) -> Result> { let _l = LOCK.lock(); env::set_var( "PKG_CONFIG_PATH", - &env::current_dir().unwrap().join("tests"), + &env::current_dir().unwrap().join("src").join("tests"), ); env::set_var( "CARGO_MANIFEST_DIR", - &env::current_dir().unwrap().join("tests").join(path), + &env::current_dir() + .unwrap() + .join("src") + .join("tests") + .join(path), ); env::set_var("CARGO_FEATURE_TEST_FEATURE", ""); - metadeps::probe() + probe() } #[test] @@ -47,7 +50,7 @@ fn toml_err(path: &str, err_starts_with: &str) { fn toml_pkg_config_err_version(path: &str, expected_version: &str) { let err = toml(path).unwrap_err(); match err.kind() { - metadeps::ErrorKind::PkgConfig(e) => match e { + ErrorKind::PkgConfig(e) => match e { pkg_config::Error::Failure { command: cmd, output: _, diff --git a/tests/testdata.pc b/src/tests/testdata.pc similarity index 100% rename from tests/testdata.pc rename to src/tests/testdata.pc diff --git a/tests/testlib-2.0.pc b/src/tests/testlib-2.0.pc similarity index 100% rename from tests/testlib-2.0.pc rename to src/tests/testlib-2.0.pc diff --git a/tests/testlib.pc b/src/tests/testlib.pc similarity index 100% rename from tests/testlib.pc rename to src/tests/testlib.pc diff --git a/tests/toml-feature-not-string/Cargo.toml b/src/tests/toml-feature-not-string/Cargo.toml similarity index 100% rename from tests/toml-feature-not-string/Cargo.toml rename to src/tests/toml-feature-not-string/Cargo.toml diff --git a/tests/toml-feature-versions/Cargo.toml b/src/tests/toml-feature-versions/Cargo.toml similarity index 100% rename from tests/toml-feature-versions/Cargo.toml rename to src/tests/toml-feature-versions/Cargo.toml diff --git a/tests/toml-good/Cargo.toml b/src/tests/toml-good/Cargo.toml similarity index 100% rename from tests/toml-good/Cargo.toml rename to src/tests/toml-good/Cargo.toml diff --git a/tests/toml-missing-file/no-cargo-toml-here b/src/tests/toml-missing-file/no-cargo-toml-here similarity index 100% rename from tests/toml-missing-file/no-cargo-toml-here rename to src/tests/toml-missing-file/no-cargo-toml-here diff --git a/tests/toml-missing-key/Cargo.toml b/src/tests/toml-missing-key/Cargo.toml similarity index 100% rename from tests/toml-missing-key/Cargo.toml rename to src/tests/toml-missing-key/Cargo.toml diff --git a/tests/toml-not-table/Cargo.toml b/src/tests/toml-not-table/Cargo.toml similarity index 100% rename from tests/toml-not-table/Cargo.toml rename to src/tests/toml-not-table/Cargo.toml diff --git a/tests/toml-override-name/Cargo.toml b/src/tests/toml-override-name/Cargo.toml similarity index 100% rename from tests/toml-override-name/Cargo.toml rename to src/tests/toml-override-name/Cargo.toml diff --git a/tests/toml-unexpected-key/Cargo.toml b/src/tests/toml-unexpected-key/Cargo.toml similarity index 100% rename from tests/toml-unexpected-key/Cargo.toml rename to src/tests/toml-unexpected-key/Cargo.toml diff --git a/tests/toml-version-in-table-not-string/Cargo.toml b/src/tests/toml-version-in-table-not-string/Cargo.toml similarity index 100% rename from tests/toml-version-in-table-not-string/Cargo.toml rename to src/tests/toml-version-in-table-not-string/Cargo.toml diff --git a/tests/toml-version-missing/Cargo.toml b/src/tests/toml-version-missing/Cargo.toml similarity index 100% rename from tests/toml-version-missing/Cargo.toml rename to src/tests/toml-version-missing/Cargo.toml diff --git a/tests/toml-version-not-string/Cargo.toml b/src/tests/toml-version-not-string/Cargo.toml similarity index 100% rename from tests/toml-version-not-string/Cargo.toml rename to src/tests/toml-version-not-string/Cargo.toml From e198c6e286f34d43d0d793a38847a009a72219dd Mon Sep 17 00:00:00 2001 From: Guillaume Desmottes Date: Fri, 1 May 2020 14:29:16 +0200 Subject: [PATCH 12/55] export lib include paths as env variable This is useful for users of -sys crates which may need to access to those headers, see https://kornel.ski/rust-sys-crate#headers We are going to extend this to export flags directly instead of relying on pkg-config to do so. --- src/lib.rs | 77 ++++++++++++++++++++++++++++++++++++++++++++++++++--- src/test.rs | 19 ++++++++----- 2 files changed, 87 insertions(+), 9 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 97cdbd6..3550cfa 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -27,6 +27,7 @@ mod test; use pkg_config::{Config, Library}; use std::collections::HashMap; use std::env; +use std::fmt; use std::fs; use std::io::Read; use std::path::PathBuf; @@ -43,9 +44,7 @@ fn has_feature(feature: &str) -> bool { env::var_os(var).is_some() } -/// Probe all libraries configured in the Cargo.toml -/// `[package.metadata.pkg-config]` section. -pub fn probe() -> Result> { +fn probe_pkg_config() -> Result> { let dir = env::var_os("CARGO_MANIFEST_DIR").ok_or("$CARGO_MANIFEST_DIR not set")?; let mut path = PathBuf::from(dir); path.push("Cargo.toml"); @@ -148,3 +147,75 @@ pub fn probe() -> Result> { } Ok(libraries) } + +#[derive(Debug, PartialEq)] +enum BuildFlag { + Include(String), +} + +impl fmt::Display for BuildFlag { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + BuildFlag::Include(paths) => write!(f, "include={}", paths), + } + } +} + +#[derive(Debug, PartialEq)] +struct BuildFlags(Vec); + +impl BuildFlags { + fn new() -> Self { + Self(Vec::new()) + } + + fn add(&mut self, flag: BuildFlag) { + self.0.push(flag); + } +} + +impl fmt::Display for BuildFlags { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + for flag in self.0.iter() { + writeln!(f, "cargo:{}", flag)?; + } + Ok(()) + } +} + +fn gen_flags(libraries: &HashMap) -> BuildFlags { + let mut flags = BuildFlags::new(); + let mut include_paths = Vec::new(); + + for (_name, lib) in libraries.iter() { + include_paths.extend(lib.include_paths.clone()); + } + + // Export DEP_$CRATE_INCLUDE env variable with the headers paths, + // see https://kornel.ski/rust-sys-crate#headers + if !include_paths.is_empty() { + if let Ok(paths) = std::env::join_paths(include_paths) { + flags.add(BuildFlag::Include(paths.to_string_lossy().to_string())); + } + } + + flags +} + +fn probe_full() -> Result<(HashMap, BuildFlags)> { + let libraries = probe_pkg_config()?; + let flags = gen_flags(&libraries); + + Ok((libraries, flags)) +} + +/// Probe all libraries configured in the Cargo.toml +/// `[package.metadata.pkg-config]` section. +pub fn probe() -> Result> { + let (libraries, flags) = probe_full()?; + + // Output cargo flags + println!("{}", flags); + + Ok(libraries) +} diff --git a/src/test.rs b/src/test.rs index d92b35e..9a432ea 100644 --- a/src/test.rs +++ b/src/test.rs @@ -2,13 +2,18 @@ use pkg_config; use std::env; use std::sync::Mutex; -use super::{probe, ErrorKind, Result}; +use super::{probe_full, BuildFlags, ErrorKind, Result}; lazy_static! { static ref LOCK: Mutex<()> = Mutex::new(()); } -fn toml(path: &str) -> Result> { +fn toml( + path: &str, +) -> Result<( + std::collections::HashMap, + BuildFlags, +)> { let _l = LOCK.lock(); env::set_var( "PKG_CONFIG_PATH", @@ -23,17 +28,19 @@ fn toml(path: &str) -> Result Date: Sat, 2 May 2020 16:15:16 +0200 Subject: [PATCH 13/55] remove deny warnings It's anoying when hacking, we can enforce not having any warning as a policy. --- src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index 3550cfa..2d58e1b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -12,7 +12,7 @@ //! gstreamer = { name = "gstreamer-1.0", version = "1.0", feature-versions = { v1_2 = "1.2", v1_4 = "1.4" }} //! ``` -#![deny(missing_docs, warnings)] +#![deny(missing_docs)] #[macro_use] extern crate error_chain; From 331bab3e90687b3892acf2e8868668448611766b Mon Sep 17 00:00:00 2001 From: Guillaume Desmottes Date: Sat, 2 May 2020 14:15:16 +0200 Subject: [PATCH 14/55] stop relying on pkg-config to print cargo metadata Will be needed as we are about to give more control to what's exported or not. Also allow us to test what's exported to cargo. --- src/lib.rs | 23 +++++++++++++++++++++++ src/test.rs | 10 +++++++++- src/tests/testlib.pc | 2 +- 3 files changed, 33 insertions(+), 2 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 2d58e1b..d6d442d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -142,21 +142,31 @@ fn probe_pkg_config() -> Result> { let library = Config::new() .atleast_version(&version) .print_system_libs(false) + .cargo_metadata(false) .probe(lib_name)?; libraries.insert(name.clone(), library); } Ok(libraries) } +// TODO: add support for "rustc-link-lib=static=" ? #[derive(Debug, PartialEq)] enum BuildFlag { Include(String), + SearchNative(String), + SearchFramework(String), + Lib(String), + LibFramework(String), } impl fmt::Display for BuildFlag { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { BuildFlag::Include(paths) => write!(f, "include={}", paths), + BuildFlag::SearchNative(lib) => write!(f, "rustc-link-search=native={}", lib), + BuildFlag::SearchFramework(lib) => write!(f, "rustc-link-search=framework={}", lib), + BuildFlag::Lib(lib) => write!(f, "rustc-link-lib={}", lib), + BuildFlag::LibFramework(lib) => write!(f, "rustc-link-lib=framework={}", lib), } } } @@ -189,6 +199,19 @@ fn gen_flags(libraries: &HashMap) -> BuildFlags { for (_name, lib) in libraries.iter() { include_paths.extend(lib.include_paths.clone()); + + lib.link_paths + .iter() + .for_each(|l| flags.add(BuildFlag::SearchNative(l.to_string_lossy().to_string()))); + lib.framework_paths + .iter() + .for_each(|f| flags.add(BuildFlag::SearchFramework(f.to_string_lossy().to_string()))); + lib.libs + .iter() + .for_each(|l| flags.add(BuildFlag::Lib(l.clone()))); + lib.frameworks + .iter() + .for_each(|f| flags.add(BuildFlag::LibFramework(f.clone()))); } // Export DEP_$CRATE_INCLUDE env variable with the headers paths, diff --git a/src/test.rs b/src/test.rs index 9a432ea..fed05f1 100644 --- a/src/test.rs +++ b/src/test.rs @@ -40,7 +40,15 @@ fn good() { assert_eq!(testdata.version, "4.5.6"); assert!(libraries.get("testmore").is_none()); - assert_eq!(flags.to_string(), "cargo:include=/usr/include/testlib\n"); + assert_eq!( + flags.to_string(), + r#"cargo:rustc-link-search=native=/usr/lib/x86_64-linux-gnu +cargo:rustc-link-search=framework=/usr/lib/x86_64-linux-gnu +cargo:rustc-link-lib=test +cargo:rustc-link-lib=framework=someframework +cargo:include=/usr/include/testlib +"# + ); } fn toml_err(path: &str, err_starts_with: &str) { diff --git a/src/tests/testlib.pc b/src/tests/testlib.pc index d330eef..4e1986f 100644 --- a/src/tests/testlib.pc +++ b/src/tests/testlib.pc @@ -6,5 +6,5 @@ includedir=${prefix}/include/testlib Name: Test Library Description: A fake library to test pkg-config. Version: 1.2.3 -Libs: L${libdir} -ltest +Libs: -L${libdir} -ltest -F${libdir} -framework someframework Cflags: -I${includedir} From faab013eecd8a1ae675c686c10254a5194fc10d6 Mon Sep 17 00:00:00 2001 From: Guillaume Desmottes Date: Sat, 2 May 2020 16:15:59 +0200 Subject: [PATCH 15/55] add env variables mock for tests Setting env variables isn't convenient in tests are they are shared among all instances when running in parallel. --- src/lib.rs | 42 +++++++++++++++++++++++++++++++--------- src/test.rs | 56 +++++++++++++++++++++++++++++++++++------------------ 2 files changed, 70 insertions(+), 28 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index d6d442d..cd009aa 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -39,13 +39,36 @@ error_chain! { } } -fn has_feature(feature: &str) -> bool { +#[derive(Debug)] +enum EnvVariables { + Environnement, + #[cfg(test)] + Mock(HashMap<&'static str, String>), +} + +impl EnvVariables { + fn contains(&self, var: &str) -> bool { + self.get(var).is_some() + } + + fn get(&self, var: &str) -> Option { + match self { + EnvVariables::Environnement => env::var(var).ok(), + #[cfg(test)] + EnvVariables::Mock(vars) => vars.get(var).cloned(), + } + } +} + +fn has_feature(env_vars: &EnvVariables, feature: &str) -> bool { let var = format!("CARGO_FEATURE_{}", feature.to_uppercase().replace('-', "_")); - env::var_os(var).is_some() + env_vars.contains(&var) } -fn probe_pkg_config() -> Result> { - let dir = env::var_os("CARGO_MANIFEST_DIR").ok_or("$CARGO_MANIFEST_DIR not set")?; +fn probe_pkg_config(env_vars: &EnvVariables) -> Result> { + let dir = env_vars + .get("CARGO_MANIFEST_DIR") + .ok_or("$CARGO_MANIFEST_DIR not set")?; let mut path = PathBuf::from(dir); path.push("Cargo.toml"); let mut manifest = @@ -90,7 +113,7 @@ fn probe_pkg_config() -> Result> { for (k, v) in feature_versions { match (k.as_str(), v) { (_, &toml::Value::String(ref feat_vers)) => { - if has_feature(&k) { + if has_feature(&env_vars, &k) { enabled_feature_versions.push(feat_vers); } } @@ -112,7 +135,7 @@ fn probe_pkg_config() -> Result> { } } if let Some(feature) = feature { - if !has_feature(feature) { + if !has_feature(&env_vars, feature) { continue; } } @@ -225,8 +248,8 @@ fn gen_flags(libraries: &HashMap) -> BuildFlags { flags } -fn probe_full() -> Result<(HashMap, BuildFlags)> { - let libraries = probe_pkg_config()?; +fn probe_full(env: EnvVariables) -> Result<(HashMap, BuildFlags)> { + let libraries = probe_pkg_config(&env)?; let flags = gen_flags(&libraries); Ok((libraries, flags)) @@ -235,7 +258,8 @@ fn probe_full() -> Result<(HashMap, BuildFlags)> { /// Probe all libraries configured in the Cargo.toml /// `[package.metadata.pkg-config]` section. pub fn probe() -> Result> { - let (libraries, flags) = probe_full()?; + let env = EnvVariables::Environnement; + let (libraries, flags) = probe_full(env)?; // Output cargo flags println!("{}", flags); diff --git a/src/test.rs b/src/test.rs index fed05f1..eb0a17e 100644 --- a/src/test.rs +++ b/src/test.rs @@ -1,8 +1,9 @@ use pkg_config; +use std::collections::HashMap; use std::env; use std::sync::Mutex; -use super::{probe_full, BuildFlags, ErrorKind, Result}; +use super::{probe_full, BuildFlags, EnvVariables, ErrorKind, Result}; lazy_static! { static ref LOCK: Mutex<()> = Mutex::new(()); @@ -10,30 +11,43 @@ lazy_static! { fn toml( path: &str, + env: Vec<(&'static str, &'static str)>, ) -> Result<( std::collections::HashMap, BuildFlags, )> { - let _l = LOCK.lock(); - env::set_var( - "PKG_CONFIG_PATH", - &env::current_dir().unwrap().join("src").join("tests"), - ); - env::set_var( + { + // PKG_CONFIG_PATH is read by pkg-config so we need to actually change the env + let _l = LOCK.lock(); + env::set_var( + "PKG_CONFIG_PATH", + &env::current_dir().unwrap().join("src").join("tests"), + ); + } + + let mut hash = HashMap::new(); + hash.insert( "CARGO_MANIFEST_DIR", - &env::current_dir() + env::current_dir() .unwrap() .join("src") .join("tests") - .join(path), + .join(path) + .to_string_lossy() + .to_string(), ); - env::set_var("CARGO_FEATURE_TEST_FEATURE", ""); - probe_full() + + hash.insert("CARGO_FEATURE_TEST_FEATURE", "".to_string()); + env.iter().for_each(|(k, v)| { + hash.insert(k, v.to_string()); + }); + + probe_full(EnvVariables::Mock(hash)) } #[test] fn good() { - let (libraries, flags) = toml("toml-good").unwrap(); + let (libraries, flags) = toml("toml-good", vec![]).unwrap(); let testlib = libraries.get("testlib").unwrap(); assert_eq!(testlib.version, "1.2.3"); let testdata = libraries.get("testdata").unwrap(); @@ -52,7 +66,7 @@ cargo:include=/usr/include/testlib } fn toml_err(path: &str, err_starts_with: &str) { - let err = toml(path).unwrap_err(); + let err = toml(path, vec![]).unwrap_err(); if !err.description().starts_with(err_starts_with) { panic!( "Expected error to start with: {:?}\nGot error: {:?}", @@ -62,8 +76,12 @@ fn toml_err(path: &str, err_starts_with: &str) { } // Assert a PkgConfig error because requested lib version cannot be found -fn toml_pkg_config_err_version(path: &str, expected_version: &str) { - let err = toml(path).unwrap_err(); +fn toml_pkg_config_err_version( + path: &str, + expected_version: &str, + env_vars: Vec<(&'static str, &'static str)>, +) { + let err = toml(path, env_vars).unwrap_err(); match err.kind() { ErrorKind::PkgConfig(e) => match e { pkg_config::Error::Failure { @@ -139,22 +157,22 @@ fn unexpected_key() { #[test] fn override_name() { - let (libraries, _) = toml("toml-override-name").unwrap(); + let (libraries, _) = toml("toml-override-name", vec![]).unwrap(); let testlib = libraries.get("testlib").unwrap(); assert_eq!(testlib.version, "2.0.0"); } #[test] fn feature_versions() { - let (libraries, _) = toml("toml-feature-versions").unwrap(); + let (libraries, _) = toml("toml-feature-versions", vec![]).unwrap(); let testdata = libraries.get("testdata").unwrap(); assert_eq!(testdata.version, "4.5.6"); // version 5 is not available env::set_var("CARGO_FEATURE_V5", ""); - toml_pkg_config_err_version("toml-feature-versions", "5"); + toml_pkg_config_err_version("toml-feature-versions", "5", vec![("CARGO_FEATURE_V5", "")]); // We check the highest version enabled by features env::set_var("CARGO_FEATURE_V6", ""); - toml_pkg_config_err_version("toml-feature-versions", "6"); + toml_pkg_config_err_version("toml-feature-versions", "6", vec![("CARGO_FEATURE_V6", "")]); } From 6133caa7af22ea4d18e8f44377e0c75176d90747 Mon Sep 17 00:00:00 2001 From: Guillaume Desmottes Date: Sun, 3 May 2020 12:13:17 +0200 Subject: [PATCH 16/55] allow user to override build flags using env variable TODO: doc --- Cargo.toml | 1 + src/lib.rs | 47 +++++++++++++++++- src/test.rs | 134 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 180 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 513a929..7591129 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,6 +13,7 @@ error-chain = { version = "0.12", default-features = false } pkg-config = "0.3.8" toml = { version = "0.5", default-features = false } version-compare = "0.0.10" +heck = "0.3" [dev-dependencies] lazy_static = "1" diff --git a/src/lib.rs b/src/lib.rs index cd009aa..2b206df 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -24,13 +24,14 @@ extern crate lazy_static; #[cfg(test)] mod test; +use heck::ShoutySnakeCase; use pkg_config::{Config, Library}; use std::collections::HashMap; use std::env; use std::fmt; use std::fs; use std::io::Read; -use std::path::PathBuf; +use std::path::{Path, PathBuf}; use version_compare::VersionCompare; error_chain! { @@ -248,8 +249,50 @@ fn gen_flags(libraries: &HashMap) -> BuildFlags { flags } +fn flag_override_var(lib: &str, flag: &str) -> String { + format!("METADEPS_{}_{}", lib.to_shouty_snake_case(), flag) +} + +fn split_paths(value: &str) -> Vec { + if !value.is_empty() { + let paths = env::split_paths(&value); + paths.map(|p| Path::new(&p).into()).collect() + } else { + Vec::new() + } +} + +fn split_string(value: &str) -> Vec { + if !value.is_empty() { + value.split(' ').map(|s| s.to_string()).collect() + } else { + Vec::new() + } +} + +fn override_from_flags(env_vars: &EnvVariables, libraries: &mut HashMap) { + for (name, lib) in libraries.iter_mut() { + if let Some(value) = env_vars.get(&flag_override_var(name, "SEARCH_NATIVE")) { + lib.link_paths = split_paths(&value); + } + if let Some(value) = env_vars.get(&flag_override_var(name, "SEARCH_FRAMEWORK")) { + lib.framework_paths = split_paths(&value); + } + if let Some(value) = env_vars.get(&flag_override_var(name, "LIB")) { + lib.libs = split_string(&value); + } + if let Some(value) = env_vars.get(&flag_override_var(name, "LIB_FRAMEWORK")) { + lib.frameworks = split_string(&value); + } + if let Some(value) = env_vars.get(&flag_override_var(name, "INCLUDE")) { + lib.include_paths = split_paths(&value); + } + } +} + fn probe_full(env: EnvVariables) -> Result<(HashMap, BuildFlags)> { - let libraries = probe_pkg_config(&env)?; + let mut libraries = probe_pkg_config(&env)?; + override_from_flags(&env, &mut libraries); let flags = gen_flags(&libraries); Ok((libraries, flags)) diff --git a/src/test.rs b/src/test.rs index eb0a17e..34723cc 100644 --- a/src/test.rs +++ b/src/test.rs @@ -1,6 +1,7 @@ use pkg_config; use std::collections::HashMap; use std::env; +use std::path::{Path, PathBuf}; use std::sync::Mutex; use super::{probe_full, BuildFlags, EnvVariables, ErrorKind, Result}; @@ -176,3 +177,136 @@ fn feature_versions() { env::set_var("CARGO_FEATURE_V6", ""); toml_pkg_config_err_version("toml-feature-versions", "6", vec![("CARGO_FEATURE_V6", "")]); } + +#[test] +fn override_search_native() { + let (libraries, flags) = toml( + "toml-good", + vec![("METADEPS_TESTLIB_SEARCH_NATIVE", "/custom/path:/other/path")], + ) + .unwrap(); + let testlib = libraries.get("testlib").unwrap(); + assert_eq!( + testlib.link_paths, + vec![Path::new("/custom/path"), Path::new("/other/path")] + ); + + assert_eq!( + flags.to_string(), + r#"cargo:rustc-link-search=native=/custom/path +cargo:rustc-link-search=native=/other/path +cargo:rustc-link-search=framework=/usr/lib/x86_64-linux-gnu +cargo:rustc-link-lib=test +cargo:rustc-link-lib=framework=someframework +cargo:include=/usr/include/testlib +"# + ); +} + +#[test] +fn override_search_framework() { + let (libraries, flags) = toml( + "toml-good", + vec![("METADEPS_TESTLIB_SEARCH_FRAMEWORK", "/custom/path")], + ) + .unwrap(); + let testlib = libraries.get("testlib").unwrap(); + assert_eq!(testlib.framework_paths, vec![Path::new("/custom/path")]); + + assert_eq!( + flags.to_string(), + r#"cargo:rustc-link-search=native=/usr/lib/x86_64-linux-gnu +cargo:rustc-link-search=framework=/custom/path +cargo:rustc-link-lib=test +cargo:rustc-link-lib=framework=someframework +cargo:include=/usr/include/testlib +"# + ); +} + +#[test] +fn override_lib() { + let (libraries, flags) = toml( + "toml-good", + vec![("METADEPS_TESTLIB_LIB", "overrided-test other-test")], + ) + .unwrap(); + let testlib = libraries.get("testlib").unwrap(); + assert_eq!(testlib.libs, vec!["overrided-test", "other-test"]); + + assert_eq!( + flags.to_string(), + r#"cargo:rustc-link-search=native=/usr/lib/x86_64-linux-gnu +cargo:rustc-link-search=framework=/usr/lib/x86_64-linux-gnu +cargo:rustc-link-lib=overrided-test +cargo:rustc-link-lib=other-test +cargo:rustc-link-lib=framework=someframework +cargo:include=/usr/include/testlib +"# + ); +} + +#[test] +fn override_framework() { + let (libraries, flags) = toml( + "toml-good", + vec![("METADEPS_TESTLIB_LIB_FRAMEWORK", "overrided-framework")], + ) + .unwrap(); + let testlib = libraries.get("testlib").unwrap(); + assert_eq!(testlib.frameworks, vec!["overrided-framework"]); + + assert_eq!( + flags.to_string(), + r#"cargo:rustc-link-search=native=/usr/lib/x86_64-linux-gnu +cargo:rustc-link-search=framework=/usr/lib/x86_64-linux-gnu +cargo:rustc-link-lib=test +cargo:rustc-link-lib=framework=overrided-framework +cargo:include=/usr/include/testlib +"# + ); +} + +#[test] +fn override_include() { + let (libraries, flags) = toml( + "toml-good", + vec![("METADEPS_TESTLIB_INCLUDE", "/other/include")], + ) + .unwrap(); + let testlib = libraries.get("testlib").unwrap(); + assert_eq!(testlib.include_paths, vec![Path::new("/other/include")]); + + assert_eq!( + flags.to_string(), + r#"cargo:rustc-link-search=native=/usr/lib/x86_64-linux-gnu +cargo:rustc-link-search=framework=/usr/lib/x86_64-linux-gnu +cargo:rustc-link-lib=test +cargo:rustc-link-lib=framework=someframework +cargo:include=/other/include +"# + ); +} + +#[test] +fn override_unset() { + let (libraries, flags) = toml( + "toml-good", + vec![ + ("METADEPS_TESTLIB_SEARCH_NATIVE", ""), + ("METADEPS_TESTLIB_SEARCH_FRAMEWORK", ""), + ("METADEPS_TESTLIB_LIB", ""), + ("METADEPS_TESTLIB_LIB_FRAMEWORK", ""), + ("METADEPS_TESTLIB_INCLUDE", ""), + ], + ) + .unwrap(); + let testlib = libraries.get("testlib").unwrap(); + assert_eq!(testlib.link_paths, Vec::::new()); + assert_eq!(testlib.framework_paths, Vec::::new()); + assert_eq!(testlib.libs, Vec::::new()); + assert_eq!(testlib.frameworks, Vec::::new()); + assert_eq!(testlib.include_paths, Vec::::new()); + + assert_eq!(flags.to_string(), ""); +} From bb6a87a9dd481223ecbbfd0047f1863ab4bfbadc Mon Sep 17 00:00:00 2001 From: Guillaume Desmottes Date: Sun, 3 May 2020 15:01:30 +0200 Subject: [PATCH 17/55] use our own Library struct instead of pkg-config's Have the exact same API for now. This will be needed if/when we'll want to support no pkg-config libs. --- src/lib.rs | 51 +++++++++++++++++++++++++++++++++++++++++++++++++-- src/test.rs | 7 ++----- 2 files changed, 51 insertions(+), 7 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 2b206df..aff6657 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -25,7 +25,7 @@ extern crate lazy_static; mod test; use heck::ShoutySnakeCase; -use pkg_config::{Config, Library}; +use pkg_config::Config; use std::collections::HashMap; use std::env; use std::fmt; @@ -40,6 +40,53 @@ error_chain! { } } +#[derive(Debug)] +/// A system dependency +pub struct Library { + /// libraries the linker should link on + pub libs: Vec, + /// directories where the compiler should look for libraries + pub link_paths: Vec, + /// frameworks the linker should link on + pub frameworks: Vec, + /// directories where the compiler should look for frameworks + pub framework_paths: Vec, + /// directories where the compiler should look for header files + pub include_paths: Vec, + /// macros that should be defined by the compiler + pub defines: HashMap>, + /// library version + pub version: String, +} + +impl Default for Library { + fn default() -> Self { + Self { + libs: Vec::new(), + link_paths: Vec::new(), + include_paths: Vec::new(), + frameworks: Vec::new(), + framework_paths: Vec::new(), + defines: HashMap::new(), + version: String::new(), + } + } +} + +impl Library { + fn from_pkg_config(l: pkg_config::Library) -> Self { + Self { + libs: l.libs, + link_paths: l.link_paths, + include_paths: l.include_paths, + frameworks: l.frameworks, + framework_paths: l.framework_paths, + defines: l.defines, + version: l.version, + } + } +} + #[derive(Debug)] enum EnvVariables { Environnement, @@ -168,7 +215,7 @@ fn probe_pkg_config(env_vars: &EnvVariables) -> Result> .print_system_libs(false) .cargo_metadata(false) .probe(lib_name)?; - libraries.insert(name.clone(), library); + libraries.insert(name.clone(), Library::from_pkg_config(library)); } Ok(libraries) } diff --git a/src/test.rs b/src/test.rs index 34723cc..878d51f 100644 --- a/src/test.rs +++ b/src/test.rs @@ -4,7 +4,7 @@ use std::env; use std::path::{Path, PathBuf}; use std::sync::Mutex; -use super::{probe_full, BuildFlags, EnvVariables, ErrorKind, Result}; +use super::{probe_full, BuildFlags, EnvVariables, ErrorKind, Library, Result}; lazy_static! { static ref LOCK: Mutex<()> = Mutex::new(()); @@ -13,10 +13,7 @@ lazy_static! { fn toml( path: &str, env: Vec<(&'static str, &'static str)>, -) -> Result<( - std::collections::HashMap, - BuildFlags, -)> { +) -> Result<(std::collections::HashMap, BuildFlags)> { { // PKG_CONFIG_PATH is read by pkg-config so we need to actually change the env let _l = LOCK.lock(); From 118982bafd20930d874cbba1389c9e65fcb8a34b Mon Sep 17 00:00:00 2001 From: Guillaume Desmottes Date: Sun, 3 May 2020 14:31:51 +0200 Subject: [PATCH 18/55] support NO_PKG_CONFIG env variables Can be used to fully by-pass pkg-config and rely solely on override variables. --- src/lib.rs | 19 +++++++++++++------ src/test.rs | 20 ++++++++++++++++++++ 2 files changed, 33 insertions(+), 6 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index aff6657..a28efd0 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -210,12 +210,19 @@ fn probe_pkg_config(env_vars: &EnvVariables) -> Result> } _ => bail!("{}.{} not a string or table", key, name), }; - let library = Config::new() - .atleast_version(&version) - .print_system_libs(false) - .cargo_metadata(false) - .probe(lib_name)?; - libraries.insert(name.clone(), Library::from_pkg_config(library)); + let library = if env_vars.contains(&flag_override_var(name, "NO_PKG_CONFIG")) { + Library::default() + } else { + Library::from_pkg_config( + Config::new() + .atleast_version(&version) + .print_system_libs(false) + .cargo_metadata(false) + .probe(lib_name)?, + ) + }; + + libraries.insert(name.clone(), library); } Ok(libraries) } diff --git a/src/test.rs b/src/test.rs index 878d51f..6c9e1ab 100644 --- a/src/test.rs +++ b/src/test.rs @@ -307,3 +307,23 @@ fn override_unset() { assert_eq!(flags.to_string(), ""); } + +#[test] +fn override_no_pkg_config() { + let (libraries, flags) = toml( + "toml-good", + vec![ + ("METADEPS_TESTLIB_NO_PKG_CONFIG", "1"), + ("METADEPS_TESTLIB_LIB", "custom-lib"), + ], + ) + .unwrap(); + let testlib = libraries.get("testlib").unwrap(); + assert_eq!(testlib.link_paths, Vec::::new()); + assert_eq!(testlib.framework_paths, Vec::::new()); + assert_eq!(testlib.libs, vec!["custom-lib"]); + assert_eq!(testlib.frameworks, Vec::::new()); + assert_eq!(testlib.include_paths, Vec::::new()); + + assert_eq!(flags.to_string(), "cargo:rustc-link-lib=custom-lib\n"); +} From e63253515fe079d911dfbe7eff8c95a2961488a8 Mon Sep 17 00:00:00 2001 From: Guillaume Desmottes Date: Wed, 6 May 2020 10:46:21 +0200 Subject: [PATCH 19/55] add source field indicating the source of a library --- src/lib.rs | 41 ++++++++++++++++++++++++++--------------- 1 file changed, 26 insertions(+), 15 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index a28efd0..4d8d4f9 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -40,9 +40,20 @@ error_chain! { } } +#[derive(Debug, PartialEq)] +/// From where the library settings have been retrieved +pub enum Source { + /// Settings have been retrieved from `pkg-config` + PkgConfig, + /// Settings have been defined using user defined environnement variables + EnvVariables, +} + #[derive(Debug)] /// A system dependency pub struct Library { + /// From where the library settings have been retrieved + pub source: Source, /// libraries the linker should link on pub libs: Vec, /// directories where the compiler should look for libraries @@ -59,23 +70,10 @@ pub struct Library { pub version: String, } -impl Default for Library { - fn default() -> Self { - Self { - libs: Vec::new(), - link_paths: Vec::new(), - include_paths: Vec::new(), - frameworks: Vec::new(), - framework_paths: Vec::new(), - defines: HashMap::new(), - version: String::new(), - } - } -} - impl Library { fn from_pkg_config(l: pkg_config::Library) -> Self { Self { + source: Source::PkgConfig, libs: l.libs, link_paths: l.link_paths, include_paths: l.include_paths, @@ -85,6 +83,19 @@ impl Library { version: l.version, } } + + fn from_env_variables() -> Self { + Self { + source: Source::EnvVariables, + libs: Vec::new(), + link_paths: Vec::new(), + include_paths: Vec::new(), + frameworks: Vec::new(), + framework_paths: Vec::new(), + defines: HashMap::new(), + version: String::new(), + } + } } #[derive(Debug)] @@ -211,7 +222,7 @@ fn probe_pkg_config(env_vars: &EnvVariables) -> Result> _ => bail!("{}.{} not a string or table", key, name), }; let library = if env_vars.contains(&flag_override_var(name, "NO_PKG_CONFIG")) { - Library::default() + Library::from_env_variables() } else { Library::from_pkg_config( Config::new() From aeea975d941eeac38dd96b77cd0ba84bf122d384 Mon Sep 17 00:00:00 2001 From: Guillaume Desmottes Date: Wed, 6 May 2020 10:52:15 +0200 Subject: [PATCH 20/55] ensure that manually defined deps have at least one lib --- src/lib.rs | 22 ++++++++++++++++++---- src/test.rs | 9 +++++++++ 2 files changed, 27 insertions(+), 4 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 4d8d4f9..ffbdc0d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -38,6 +38,16 @@ error_chain! { foreign_links { PkgConfig(pkg_config::Error) #[doc="pkg-config error"]; } + errors { + /// Raised when dependency defined manually using `METADEPS_$NAME_NO_PKG_CONFIG` + /// did not define at least one lib using `METADEPS_$NAME_LIB` or + /// `METADEPS_$NAME_LIB_FRAMEWORK` + MissingLib(name: String) { + display("You should define at least one lib using {} or {}", + flag_override_var(name, "LIB"), + flag_override_var(name, "LIB_FRAMEWORK")) + } + } } #[derive(Debug, PartialEq)] @@ -282,13 +292,17 @@ impl fmt::Display for BuildFlags { } } -fn gen_flags(libraries: &HashMap) -> BuildFlags { +fn gen_flags(libraries: &HashMap) -> Result { let mut flags = BuildFlags::new(); let mut include_paths = Vec::new(); - for (_name, lib) in libraries.iter() { + for (name, lib) in libraries.iter() { include_paths.extend(lib.include_paths.clone()); + if lib.source == Source::EnvVariables && lib.libs.is_empty() && lib.frameworks.is_empty() { + bail!(ErrorKind::MissingLib(name.clone())); + } + lib.link_paths .iter() .for_each(|l| flags.add(BuildFlag::SearchNative(l.to_string_lossy().to_string()))); @@ -311,7 +325,7 @@ fn gen_flags(libraries: &HashMap) -> BuildFlags { } } - flags + Ok(flags) } fn flag_override_var(lib: &str, flag: &str) -> String { @@ -358,7 +372,7 @@ fn override_from_flags(env_vars: &EnvVariables, libraries: &mut HashMap Result<(HashMap, BuildFlags)> { let mut libraries = probe_pkg_config(&env)?; override_from_flags(&env, &mut libraries); - let flags = gen_flags(&libraries); + let flags = gen_flags(&libraries)?; Ok((libraries, flags)) } diff --git a/src/test.rs b/src/test.rs index 6c9e1ab..b8d98ce 100644 --- a/src/test.rs +++ b/src/test.rs @@ -327,3 +327,12 @@ fn override_no_pkg_config() { assert_eq!(flags.to_string(), "cargo:rustc-link-lib=custom-lib\n"); } + +#[test] +fn override_no_pkg_config_error() { + let err = toml("toml-good", vec![("METADEPS_TESTLIB_NO_PKG_CONFIG", "1")]).unwrap_err(); + assert_eq!( + err.to_string(), + "You should define at least one lib using METADEPS_TESTLIB_LIB or METADEPS_TESTLIB_LIB_FRAMEWORK" + ); +} From b963e52a64d597217795371cb0106b1a3dd98d43 Mon Sep 17 00:00:00 2001 From: Guillaume Desmottes Date: Wed, 6 May 2020 11:26:57 +0200 Subject: [PATCH 21/55] add ErrorKind::InvalidMetadata Better to return actual errors. --- src/lib.rs | 17 ++++++++++++----- src/test.rs | 2 +- 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index ffbdc0d..cb4d3f5 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -39,6 +39,10 @@ error_chain! { PkgConfig(pkg_config::Error) #[doc="pkg-config error"]; } errors { + /// Raised when an error is detected in the metadata defined in `Cargo.toml` + InvalidMetadata(details: String) { + display("{}", details) + } /// Raised when dependency defined manually using `METADEPS_$NAME_NO_PKG_CONFIG` /// did not define at least one lib using `METADEPS_$NAME_LIB` or /// `METADEPS_$NAME_LIB_FRAMEWORK` @@ -186,21 +190,21 @@ fn probe_pkg_config(env_vars: &EnvVariables) -> Result> enabled_feature_versions.push(feat_vers); } } - _ => bail!( + _ => bail!(ErrorKind::InvalidMetadata(format!( "Unexpected feature-version key: {} type {}", k, v.type_str() - ), + ))), } } } - _ => bail!( + _ => bail!(ErrorKind::InvalidMetadata(format!( "Unexpected key {}.{}.{} type {}", key, name, tname, tvalue.type_str() - ), + ))), } } if let Some(feature) = feature { @@ -229,7 +233,10 @@ fn probe_pkg_config(env_vars: &EnvVariables) -> Result> version.ok_or(format!("No version in {}.{}", key, name))?, ) } - _ => bail!("{}.{} not a string or table", key, name), + _ => bail!(ErrorKind::InvalidMetadata(format!( + "{}.{} not a string or table", + key, name + ))), }; let library = if env_vars.contains(&flag_override_var(name, "NO_PKG_CONFIG")) { Library::from_env_variables() diff --git a/src/test.rs b/src/test.rs index b8d98ce..9b72a8f 100644 --- a/src/test.rs +++ b/src/test.rs @@ -65,7 +65,7 @@ cargo:include=/usr/include/testlib fn toml_err(path: &str, err_starts_with: &str) { let err = toml(path, vec![]).unwrap_err(); - if !err.description().starts_with(err_starts_with) { + if !err.to_string().starts_with(err_starts_with) { panic!( "Expected error to start with: {:?}\nGot error: {:?}", err_starts_with, err From 3c2fcfecb36aeccf3d5469e51927d38d94603b13 Mon Sep 17 00:00:00 2001 From: Guillaume Desmottes Date: Wed, 6 May 2020 13:42:23 +0200 Subject: [PATCH 22/55] add Config object to the API using builder pattern Will be used to define custom build functions. This is an API change so we should bump the major version when releasing. --- src/lib.rs | 423 ++++++++++++++++++++++++++++------------------------ src/test.rs | 4 +- 2 files changed, 226 insertions(+), 201 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index cb4d3f5..117ba40 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -25,7 +25,6 @@ extern crate lazy_static; mod test; use heck::ShoutySnakeCase; -use pkg_config::Config; use std::collections::HashMap; use std::env; use std::fmt; @@ -54,6 +53,230 @@ error_chain! { } } +#[derive(Debug)] +/// Structure used to configure `metadata` before starting to probe for dependencies +pub struct Config { + env: EnvVariables, +} + +impl Default for Config { + fn default() -> Self { + Self::new_with_env(EnvVariables::Environnement) + } +} + +impl Config { + /// Create a new set of configuration + pub fn new() -> Self { + Self::default() + } + + fn new_with_env(env: EnvVariables) -> Self { + Self { env } + } + + /// Probe all libraries configured in the Cargo.toml + /// `[package.metadata.pkg-config]` section. + pub fn probe(self) -> Result> { + let (libraries, flags) = self.probe_full()?; + + // Output cargo flags + println!("{}", flags); + + Ok(libraries) + } + + fn probe_full(self) -> Result<(HashMap, BuildFlags)> { + let mut libraries = self.probe_pkg_config()?; + self.override_from_flags(&mut libraries); + let flags = self.gen_flags(&libraries)?; + + Ok((libraries, flags)) + } + + fn probe_pkg_config(&self) -> Result> { + let dir = self + .env + .get("CARGO_MANIFEST_DIR") + .ok_or("$CARGO_MANIFEST_DIR not set")?; + let mut path = PathBuf::from(dir); + path.push("Cargo.toml"); + let mut manifest = + fs::File::open(&path).chain_err(|| format!("Error opening {}", path.display()))?; + let mut manifest_str = String::new(); + manifest + .read_to_string(&mut manifest_str) + .chain_err(|| format!("Error reading {}", path.display()))?; + let toml = manifest_str + .parse::() + .map_err(|e| format!("Error parsing TOML from {}: {:?}", path.display(), e))?; + let key = "package.metadata.pkg-config"; + let meta = toml + .get("package") + .and_then(|v| v.get("metadata")) + .and_then(|v| v.get("pkg-config")) + .ok_or(format!("No {} in {}", key, path.display()))?; + let table = meta + .as_table() + .ok_or(format!("{} not a table in {}", key, path.display()))?; + let mut libraries = HashMap::new(); + for (name, value) in table { + let (lib_name, version) = match value { + toml::Value::String(ref s) => (name, s), + toml::Value::Table(ref t) => { + let mut feature = None; + let mut version = None; + let mut lib_name = None; + let mut enabled_feature_versions = Vec::new(); + for (tname, tvalue) in t { + match (tname.as_str(), tvalue) { + ("feature", &toml::Value::String(ref s)) => { + feature = Some(s); + } + ("version", &toml::Value::String(ref s)) => { + version = Some(s); + } + ("name", &toml::Value::String(ref s)) => { + lib_name = Some(s); + } + ("feature-versions", &toml::Value::Table(ref feature_versions)) => { + for (k, v) in feature_versions { + match (k.as_str(), v) { + (_, &toml::Value::String(ref feat_vers)) => { + if self.has_feature(&k) { + enabled_feature_versions.push(feat_vers); + } + } + _ => bail!(ErrorKind::InvalidMetadata(format!( + "Unexpected feature-version key: {} type {}", + k, + v.type_str() + ))), + } + } + } + _ => bail!(ErrorKind::InvalidMetadata(format!( + "Unexpected key {}.{}.{} type {}", + key, + name, + tname, + tvalue.type_str() + ))), + } + } + if let Some(feature) = feature { + if !self.has_feature(feature) { + continue; + } + } + + let version = { + // Pick the highest feature enabled version + if !enabled_feature_versions.is_empty() { + enabled_feature_versions.sort_by(|a, b| { + VersionCompare::compare(b, a) + .expect("failed to compare versions") + .ord() + .expect("invalid version") + }); + Some(enabled_feature_versions[0]) + } else { + version + } + }; + + ( + lib_name.unwrap_or(name), + version.ok_or(format!("No version in {}.{}", key, name))?, + ) + } + _ => bail!(ErrorKind::InvalidMetadata(format!( + "{}.{} not a string or table", + key, name + ))), + }; + let library = if self.env.contains(&flag_override_var(name, "NO_PKG_CONFIG")) { + Library::from_env_variables() + } else { + Library::from_pkg_config( + pkg_config::Config::new() + .atleast_version(&version) + .print_system_libs(false) + .cargo_metadata(false) + .probe(lib_name)?, + ) + }; + + libraries.insert(name.clone(), library); + } + Ok(libraries) + } + + fn override_from_flags(&self, libraries: &mut HashMap) { + for (name, lib) in libraries.iter_mut() { + if let Some(value) = self.env.get(&flag_override_var(name, "SEARCH_NATIVE")) { + lib.link_paths = split_paths(&value); + } + if let Some(value) = self.env.get(&flag_override_var(name, "SEARCH_FRAMEWORK")) { + lib.framework_paths = split_paths(&value); + } + if let Some(value) = self.env.get(&flag_override_var(name, "LIB")) { + lib.libs = split_string(&value); + } + if let Some(value) = self.env.get(&flag_override_var(name, "LIB_FRAMEWORK")) { + lib.frameworks = split_string(&value); + } + if let Some(value) = self.env.get(&flag_override_var(name, "INCLUDE")) { + lib.include_paths = split_paths(&value); + } + } + } + + fn gen_flags(&self, libraries: &HashMap) -> Result { + let mut flags = BuildFlags::new(); + let mut include_paths = Vec::new(); + + for (name, lib) in libraries.iter() { + include_paths.extend(lib.include_paths.clone()); + + if lib.source == Source::EnvVariables + && lib.libs.is_empty() + && lib.frameworks.is_empty() + { + bail!(ErrorKind::MissingLib(name.clone())); + } + + lib.link_paths + .iter() + .for_each(|l| flags.add(BuildFlag::SearchNative(l.to_string_lossy().to_string()))); + lib.framework_paths.iter().for_each(|f| { + flags.add(BuildFlag::SearchFramework(f.to_string_lossy().to_string())) + }); + lib.libs + .iter() + .for_each(|l| flags.add(BuildFlag::Lib(l.clone()))); + lib.frameworks + .iter() + .for_each(|f| flags.add(BuildFlag::LibFramework(f.clone()))); + } + + // Export DEP_$CRATE_INCLUDE env variable with the headers paths, + // see https://kornel.ski/rust-sys-crate#headers + if !include_paths.is_empty() { + if let Ok(paths) = std::env::join_paths(include_paths) { + flags.add(BuildFlag::Include(paths.to_string_lossy().to_string())); + } + } + + Ok(flags) + } + + fn has_feature(&self, feature: &str) -> bool { + let var = format!("CARGO_FEATURE_{}", feature.to_uppercase().replace('-', "_")); + self.env.contains(&var) + } +} + #[derive(Debug, PartialEq)] /// From where the library settings have been retrieved pub enum Source { @@ -133,128 +356,6 @@ impl EnvVariables { } } -fn has_feature(env_vars: &EnvVariables, feature: &str) -> bool { - let var = format!("CARGO_FEATURE_{}", feature.to_uppercase().replace('-', "_")); - env_vars.contains(&var) -} - -fn probe_pkg_config(env_vars: &EnvVariables) -> Result> { - let dir = env_vars - .get("CARGO_MANIFEST_DIR") - .ok_or("$CARGO_MANIFEST_DIR not set")?; - let mut path = PathBuf::from(dir); - path.push("Cargo.toml"); - let mut manifest = - fs::File::open(&path).chain_err(|| format!("Error opening {}", path.display()))?; - let mut manifest_str = String::new(); - manifest - .read_to_string(&mut manifest_str) - .chain_err(|| format!("Error reading {}", path.display()))?; - let toml = manifest_str - .parse::() - .map_err(|e| format!("Error parsing TOML from {}: {:?}", path.display(), e))?; - let key = "package.metadata.pkg-config"; - let meta = toml - .get("package") - .and_then(|v| v.get("metadata")) - .and_then(|v| v.get("pkg-config")) - .ok_or(format!("No {} in {}", key, path.display()))?; - let table = meta - .as_table() - .ok_or(format!("{} not a table in {}", key, path.display()))?; - let mut libraries = HashMap::new(); - for (name, value) in table { - let (lib_name, version) = match value { - toml::Value::String(ref s) => (name, s), - toml::Value::Table(ref t) => { - let mut feature = None; - let mut version = None; - let mut lib_name = None; - let mut enabled_feature_versions = Vec::new(); - for (tname, tvalue) in t { - match (tname.as_str(), tvalue) { - ("feature", &toml::Value::String(ref s)) => { - feature = Some(s); - } - ("version", &toml::Value::String(ref s)) => { - version = Some(s); - } - ("name", &toml::Value::String(ref s)) => { - lib_name = Some(s); - } - ("feature-versions", &toml::Value::Table(ref feature_versions)) => { - for (k, v) in feature_versions { - match (k.as_str(), v) { - (_, &toml::Value::String(ref feat_vers)) => { - if has_feature(&env_vars, &k) { - enabled_feature_versions.push(feat_vers); - } - } - _ => bail!(ErrorKind::InvalidMetadata(format!( - "Unexpected feature-version key: {} type {}", - k, - v.type_str() - ))), - } - } - } - _ => bail!(ErrorKind::InvalidMetadata(format!( - "Unexpected key {}.{}.{} type {}", - key, - name, - tname, - tvalue.type_str() - ))), - } - } - if let Some(feature) = feature { - if !has_feature(&env_vars, feature) { - continue; - } - } - - let version = { - // Pick the highest feature enabled version - if !enabled_feature_versions.is_empty() { - enabled_feature_versions.sort_by(|a, b| { - VersionCompare::compare(b, a) - .expect("failed to compare versions") - .ord() - .expect("invalid version") - }); - Some(enabled_feature_versions[0]) - } else { - version - } - }; - - ( - lib_name.unwrap_or(name), - version.ok_or(format!("No version in {}.{}", key, name))?, - ) - } - _ => bail!(ErrorKind::InvalidMetadata(format!( - "{}.{} not a string or table", - key, name - ))), - }; - let library = if env_vars.contains(&flag_override_var(name, "NO_PKG_CONFIG")) { - Library::from_env_variables() - } else { - Library::from_pkg_config( - Config::new() - .atleast_version(&version) - .print_system_libs(false) - .cargo_metadata(false) - .probe(lib_name)?, - ) - }; - - libraries.insert(name.clone(), library); - } - Ok(libraries) -} - // TODO: add support for "rustc-link-lib=static=" ? #[derive(Debug, PartialEq)] enum BuildFlag { @@ -299,42 +400,6 @@ impl fmt::Display for BuildFlags { } } -fn gen_flags(libraries: &HashMap) -> Result { - let mut flags = BuildFlags::new(); - let mut include_paths = Vec::new(); - - for (name, lib) in libraries.iter() { - include_paths.extend(lib.include_paths.clone()); - - if lib.source == Source::EnvVariables && lib.libs.is_empty() && lib.frameworks.is_empty() { - bail!(ErrorKind::MissingLib(name.clone())); - } - - lib.link_paths - .iter() - .for_each(|l| flags.add(BuildFlag::SearchNative(l.to_string_lossy().to_string()))); - lib.framework_paths - .iter() - .for_each(|f| flags.add(BuildFlag::SearchFramework(f.to_string_lossy().to_string()))); - lib.libs - .iter() - .for_each(|l| flags.add(BuildFlag::Lib(l.clone()))); - lib.frameworks - .iter() - .for_each(|f| flags.add(BuildFlag::LibFramework(f.clone()))); - } - - // Export DEP_$CRATE_INCLUDE env variable with the headers paths, - // see https://kornel.ski/rust-sys-crate#headers - if !include_paths.is_empty() { - if let Ok(paths) = std::env::join_paths(include_paths) { - flags.add(BuildFlag::Include(paths.to_string_lossy().to_string())); - } - } - - Ok(flags) -} - fn flag_override_var(lib: &str, flag: &str) -> String { format!("METADEPS_{}_{}", lib.to_shouty_snake_case(), flag) } @@ -355,43 +420,3 @@ fn split_string(value: &str) -> Vec { Vec::new() } } - -fn override_from_flags(env_vars: &EnvVariables, libraries: &mut HashMap) { - for (name, lib) in libraries.iter_mut() { - if let Some(value) = env_vars.get(&flag_override_var(name, "SEARCH_NATIVE")) { - lib.link_paths = split_paths(&value); - } - if let Some(value) = env_vars.get(&flag_override_var(name, "SEARCH_FRAMEWORK")) { - lib.framework_paths = split_paths(&value); - } - if let Some(value) = env_vars.get(&flag_override_var(name, "LIB")) { - lib.libs = split_string(&value); - } - if let Some(value) = env_vars.get(&flag_override_var(name, "LIB_FRAMEWORK")) { - lib.frameworks = split_string(&value); - } - if let Some(value) = env_vars.get(&flag_override_var(name, "INCLUDE")) { - lib.include_paths = split_paths(&value); - } - } -} - -fn probe_full(env: EnvVariables) -> Result<(HashMap, BuildFlags)> { - let mut libraries = probe_pkg_config(&env)?; - override_from_flags(&env, &mut libraries); - let flags = gen_flags(&libraries)?; - - Ok((libraries, flags)) -} - -/// Probe all libraries configured in the Cargo.toml -/// `[package.metadata.pkg-config]` section. -pub fn probe() -> Result> { - let env = EnvVariables::Environnement; - let (libraries, flags) = probe_full(env)?; - - // Output cargo flags - println!("{}", flags); - - Ok(libraries) -} diff --git a/src/test.rs b/src/test.rs index 9b72a8f..8e7824c 100644 --- a/src/test.rs +++ b/src/test.rs @@ -4,7 +4,7 @@ use std::env; use std::path::{Path, PathBuf}; use std::sync::Mutex; -use super::{probe_full, BuildFlags, EnvVariables, ErrorKind, Library, Result}; +use super::{BuildFlags, Config, EnvVariables, ErrorKind, Library, Result}; lazy_static! { static ref LOCK: Mutex<()> = Mutex::new(()); @@ -40,7 +40,7 @@ fn toml( hash.insert(k, v.to_string()); }); - probe_full(EnvVariables::Mock(hash)) + Config::new_with_env(EnvVariables::Mock(hash)).probe_full() } #[test] From f03845b403ffdc821c0aa30d85f3edbeb193a043 Mon Sep 17 00:00:00 2001 From: Guillaume Desmottes Date: Wed, 6 May 2020 14:03:27 +0200 Subject: [PATCH 23/55] tests: factor out create_config() --- src/test.rs | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/src/test.rs b/src/test.rs index 8e7824c..4bd6a9a 100644 --- a/src/test.rs +++ b/src/test.rs @@ -10,10 +10,7 @@ lazy_static! { static ref LOCK: Mutex<()> = Mutex::new(()); } -fn toml( - path: &str, - env: Vec<(&'static str, &'static str)>, -) -> Result<(std::collections::HashMap, BuildFlags)> { +fn create_config(path: &str, env: Vec<(&'static str, &'static str)>) -> Config { { // PKG_CONFIG_PATH is read by pkg-config so we need to actually change the env let _l = LOCK.lock(); @@ -40,7 +37,14 @@ fn toml( hash.insert(k, v.to_string()); }); - Config::new_with_env(EnvVariables::Mock(hash)).probe_full() + Config::new_with_env(EnvVariables::Mock(hash)) +} + +fn toml( + path: &str, + env: Vec<(&'static str, &'static str)>, +) -> Result<(std::collections::HashMap, BuildFlags)> { + create_config(path, env).probe_full() } #[test] From bd4f96983dd04cf6599ec11af05352098482614f Mon Sep 17 00:00:00 2001 From: Guillaume Desmottes Date: Wed, 6 May 2020 14:07:04 +0200 Subject: [PATCH 24/55] add hooks to internally build libs Allow users to trigger an internal build of a system lib using standarized env variables. --- Cargo.toml | 3 + src/lib.rs | 213 +++++++++++++++++++++++++++++++++++++++++++++++++--- src/test.rs | 188 +++++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 392 insertions(+), 12 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 7591129..a3a581c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,6 +14,9 @@ pkg-config = "0.3.8" toml = { version = "0.5", default-features = false } version-compare = "0.0.10" heck = "0.3" +strum = "0.18" +strum_macros = "0.18" +thiserror = "1" [dev-dependencies] lazy_static = "1" diff --git a/src/lib.rs b/src/lib.rs index 117ba40..60ee766 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -31,11 +31,16 @@ use std::fmt; use std::fs; use std::io::Read; use std::path::{Path, PathBuf}; +use std::str::FromStr; +use strum_macros::EnumString; +use thiserror::Error; use version_compare::VersionCompare; +// TODO: port to this-error error_chain! { foreign_links { PkgConfig(pkg_config::Error) #[doc="pkg-config error"]; + BuildInternalClosureError(BuildInternalClosureError) #[doc="build internal closure failure"]; } errors { /// Raised when an error is detected in the metadata defined in `Cargo.toml` @@ -50,13 +55,55 @@ error_chain! { flag_override_var(name, "LIB"), flag_override_var(name, "LIB_FRAMEWORK")) } + /// An environnement variable in the form of `METADEPS_$NAME_BUILD_INTERNAL` + /// contained an invalid value (allowed: `auto`, `always`, `never`) + BuildInternalInvalid(details: String) { + display("{}", details) + } + /// Metadeps has been asked to internally build a lib, through + /// `METADEPS_$NAME_BUILD_INTERNAL=always' or `METADEPS_$NAME_BUILD_INTERNAL=auto', + /// but not closure has been defined using `Config::add_build_internal` to build + /// this lib + BuildInternalNoClosure(lib: String, version: String) { + display("Missing build internal closure for {} (version {})", lib, version) + } + /// The library which has been build internally does not match the + /// required version defined in `Cargo.toml` + BuildInternalWrongVersion(lib: String, version: String, expected: String) { + display("Internally built {} {} but minimum required version is {}", lib, version, expected) + } } } -#[derive(Debug)] +#[derive(Error, Debug)] +/// Error used in return value of `Config::add_build_internal` closures +pub enum BuildInternalClosureError { + /// `pkg-config` error + #[error(transparent)] + PkgConfig(#[from] pkg_config::Error), + /// General failure + #[error("{0}")] + Failed(String), +} + +impl BuildInternalClosureError { + /// Create a new `BuildInternalClosureError::Failed` representing a general + /// failure. + /// + /// # Arguments + /// + /// * `details`: human-readable details about the failure + pub fn failed(details: &str) -> Self { + Self::Failed(details.to_string()) + } +} + +type FnBuildInternal = dyn FnOnce(&str) -> std::result::Result; + /// Structure used to configure `metadata` before starting to probe for dependencies pub struct Config { env: EnvVariables, + build_internals: HashMap>, } impl Default for Config { @@ -72,7 +119,10 @@ impl Config { } fn new_with_env(env: EnvVariables) -> Self { - Self { env } + Self { + env, + build_internals: HashMap::new(), + } } /// Probe all libraries configured in the Cargo.toml @@ -86,7 +136,31 @@ impl Config { Ok(libraries) } - fn probe_full(self) -> Result<(HashMap, BuildFlags)> { + /// Add hook so metadeps can internally build library `name` if requested by user. + /// + /// It will only be triggered if the environnement variable + /// `METADEPS_$NAME_BUILD_INTERNAL` is defined with either `always` or + /// `auto` as value. In the latter case, `func` is called only if the requested + /// version of the library was not found on the system. + /// + /// # Arguments + /// * `name`: the name of the library, as defined in `Cargo.toml` + /// * `func`: closure called when internally building the library. + /// It receives as argument the minimum library version required. + pub fn add_build_internal(self, name: &str, func: F) -> Self + where + F: 'static + FnOnce(&str) -> std::result::Result, + { + let mut build_internals = self.build_internals; + build_internals.insert(name.to_string(), Box::new(func)); + + Self { + env: self.env, + build_internals, + } + } + + fn probe_full(mut self) -> Result<(HashMap, BuildFlags)> { let mut libraries = self.probe_pkg_config()?; self.override_from_flags(&mut libraries); let flags = self.gen_flags(&libraries)?; @@ -94,7 +168,7 @@ impl Config { Ok((libraries, flags)) } - fn probe_pkg_config(&self) -> Result> { + fn probe_pkg_config(&mut self) -> Result> { let dir = self .env .get("CARGO_MANIFEST_DIR") @@ -195,16 +269,30 @@ impl Config { key, name ))), }; + + let build_internal = self.get_build_internal_status(name)?; + let library = if self.env.contains(&flag_override_var(name, "NO_PKG_CONFIG")) { Library::from_env_variables() + } else if build_internal == BuildInternal::Always { + self.call_build_internal(name, version)? } else { - Library::from_pkg_config( - pkg_config::Config::new() - .atleast_version(&version) - .print_system_libs(false) - .cargo_metadata(false) - .probe(lib_name)?, - ) + match pkg_config::Config::new() + .atleast_version(&version) + .print_system_libs(false) + .cargo_metadata(false) + .probe(lib_name) + { + Ok(lib) => Library::from_pkg_config(lib), + Err(e) => { + if build_internal == BuildInternal::Auto { + // Try building the lib internally as a fallback + self.call_build_internal(name, version)? + } else { + return Err(e.into()); + } + } + } }; libraries.insert(name.clone(), library); @@ -212,6 +300,41 @@ impl Config { Ok(libraries) } + fn get_build_internal_status(&self, name: &str) -> Result { + let var = flag_override_var(name, "BUILD_INTERNAL"); + let b = match self.env.get(&var).as_deref() { + Some(s) => BuildInternal::from_str(s).map_err(|_| { + ErrorKind::BuildInternalInvalid(format!( + "Invalid value in {}: {} (allowed: 'auto', 'always', 'never')", + var, s + )) + })?, + None => BuildInternal::default(), + }; + + Ok(b) + } + + fn call_build_internal(&mut self, name: &str, version: &str) -> Result { + let lib = match self.build_internals.remove(name) { + Some(f) => f(version)?, + None => bail!(ErrorKind::BuildInternalNoClosure( + name.into(), + version.into() + )), + }; + + // Check that the lib built internally matches the required version + match VersionCompare::compare(&lib.version, version) { + Ok(version_compare::CompOp::Lt) => bail!(ErrorKind::BuildInternalWrongVersion( + name.into(), + lib.version.clone(), + version.into() + )), + _ => Ok(lib), + } + } + fn override_from_flags(&self, libraries: &mut HashMap) { for (name, lib) in libraries.iter_mut() { if let Some(value) = self.env.get(&flag_override_var(name, "SEARCH_NATIVE")) { @@ -333,6 +456,60 @@ impl Library { version: String::new(), } } + + /// Create a `Library` by probing `pkg-config` on an internal directory. + /// This helper is meant to be used by `Config::add_build_internal` closures + /// after having built the lib to return the library information to metadata. + /// + /// # Arguments + /// + /// * `pkg_config_dir`: the directory where the library `.pc` file is located + /// * `lib`: the name of the library to look for + /// * `version`: the minimum version of `lib` required + /// + /// # Examples + /// + /// ``` + /// let mut config = metadeps::Config::new(); + /// config.add_build_internal("mylib", |version| { + /// // Actually build the library here + /// metadeps::Library::from_internal_pkg_config("build-dir", + /// "mylib", version) + /// }); + /// ``` + pub fn from_internal_pkg_config

( + pkg_config_dir: P, + lib: &str, + version: &str, + ) -> std::result::Result + where + P: AsRef, + { + // save current PKG_CONFIG_PATH so we can restore it + let old = env::var("PKG_CONFIG_PATH"); + + match old { + Ok(ref s) => { + let paths = [s, &pkg_config_dir.as_ref().to_string_lossy().to_string()]; + let paths = env::join_paths(paths.iter()).unwrap(); + env::set_var("PKG_CONFIG_PATH", paths) + } + Err(_) => env::set_var("PKG_CONFIG_PATH", pkg_config_dir.as_ref()), + } + + let lib = pkg_config::Config::new() + .atleast_version(&version) + .print_system_libs(false) + .cargo_metadata(false) + .probe(lib); + + env::set_var("PKG_CONFIG_PATH", &old.unwrap_or_else(|_| "".into())); + + match lib { + Ok(lib) => Ok(Self::from_pkg_config(lib)), + Err(e) => Err(e.into()), + } + } } #[derive(Debug)] @@ -420,3 +597,17 @@ fn split_string(value: &str) -> Vec { Vec::new() } } + +#[derive(Debug, PartialEq, EnumString)] +#[strum(serialize_all = "snake_case")] +enum BuildInternal { + Auto, + Always, + Never, +} + +impl Default for BuildInternal { + fn default() -> Self { + BuildInternal::Never + } +} diff --git a/src/test.rs b/src/test.rs index 4bd6a9a..30e93b0 100644 --- a/src/test.rs +++ b/src/test.rs @@ -1,10 +1,14 @@ use pkg_config; +use std::cell::Cell; use std::collections::HashMap; use std::env; use std::path::{Path, PathBuf}; +use std::rc::Rc; use std::sync::Mutex; -use super::{BuildFlags, Config, EnvVariables, ErrorKind, Library, Result}; +use super::{ + BuildFlags, BuildInternalClosureError, Config, EnvVariables, ErrorKind, Library, Result, +}; lazy_static! { static ref LOCK: Mutex<()> = Mutex::new(()); @@ -340,3 +344,185 @@ fn override_no_pkg_config_error() { "You should define at least one lib using METADEPS_TESTLIB_LIB or METADEPS_TESTLIB_LIB_FRAMEWORK" ); } + +#[test] +fn build_internal_always() { + let called = Rc::new(Cell::new(false)); + let called_clone = called.clone(); + let config = create_config( + "toml-good", + vec![("METADEPS_TESTLIB_BUILD_INTERNAL", "always")], + ) + .add_build_internal("testlib", move |version| { + called_clone.replace(true); + assert_eq!(version, "1"); + let lib = pkg_config::Config::new() + .print_system_libs(false) + .cargo_metadata(false) + .probe("testlib") + .unwrap(); + Ok(Library::from_pkg_config(lib)) + }); + + let (libraries, _flags) = config.probe_full().unwrap(); + + assert_eq!(called.get(), true); + assert!(libraries.get("testlib").is_some()); +} + +#[test] +fn build_internal_auto_not_called() { + // No need to build the lib as the existing version is new enough + let called = Rc::new(Cell::new(false)); + let called_clone = called.clone(); + let config = create_config( + "toml-good", + vec![("METADEPS_TESTLIB_BUILD_INTERNAL", "auto")], + ) + .add_build_internal("testlib", move |_version| { + called_clone.replace(true); + let lib = pkg_config::Config::new() + .print_system_libs(false) + .cargo_metadata(false) + .probe("testlib") + .unwrap(); + Ok(Library::from_pkg_config(lib)) + }); + + let (libraries, _flags) = config.probe_full().unwrap(); + + assert_eq!(called.get(), false); + assert!(libraries.get("testlib").is_some()); +} + +#[test] +fn build_internal_auto_called() { + // Version 5 is not available so we should try building + let called = Rc::new(Cell::new(false)); + let called_clone = called.clone(); + let config = create_config( + "toml-feature-versions", + vec![ + ("METADEPS_TESTDATA_BUILD_INTERNAL", "auto"), + ("CARGO_FEATURE_V5", ""), + ], + ) + .add_build_internal("testdata", move |version| { + called_clone.replace(true); + assert_eq!(version, "5"); + let mut lib = pkg_config::Config::new() + .print_system_libs(false) + .cargo_metadata(false) + .probe("testdata") + .unwrap(); + lib.version = "5.0".to_string(); + Ok(Library::from_pkg_config(lib)) + }); + + let (libraries, _flags) = config.probe_full().unwrap(); + + assert_eq!(called.get(), true); + assert!(libraries.get("testdata").is_some()); +} + +#[test] +fn build_internal_auto_never() { + // Version 5 is not available but we forbid to build the lib + let called = Rc::new(Cell::new(false)); + let called_clone = called.clone(); + let config = create_config( + "toml-feature-versions", + vec![ + ("METADEPS_TESTDATA_BUILD_INTERNAL", "never"), + ("CARGO_FEATURE_V5", ""), + ], + ) + .add_build_internal("testdata", move |version| { + called_clone.replace(true); + assert_eq!(version, "5"); + let lib = pkg_config::Config::new() + .print_system_libs(false) + .cargo_metadata(false) + .probe("testdata") + .unwrap(); + Ok(Library::from_pkg_config(lib)) + }); + + let err = config.probe_full().unwrap_err(); + assert!(matches!(err.into(), ErrorKind::PkgConfig(..))); + + assert_eq!(called.get(), false); +} + +#[test] +fn build_internal_always_no_closure() { + let config = create_config( + "toml-good", + vec![("METADEPS_TESTLIB_BUILD_INTERNAL", "always")], + ); + + let err = config.probe_full().unwrap_err(); + assert!(matches!(err.into(), ErrorKind::BuildInternalNoClosure(..))); +} + +#[test] +fn build_internal_invalid() { + let config = create_config( + "toml-good", + vec![("METADEPS_TESTLIB_BUILD_INTERNAL", "badger")], + ); + + let err = config.probe_full().unwrap_err(); + assert!(matches!(err.into(), ErrorKind::BuildInternalInvalid(..))); +} + +#[test] +fn build_internal_wrong_version() { + // Require version 5 + let called = Rc::new(Cell::new(false)); + let called_clone = called.clone(); + let config = create_config( + "toml-feature-versions", + vec![ + ("METADEPS_TESTDATA_BUILD_INTERNAL", "auto"), + ("CARGO_FEATURE_V5", ""), + ], + ) + .add_build_internal("testdata", move |_version| { + called_clone.replace(true); + let lib = pkg_config::Config::new() + .print_system_libs(false) + .cargo_metadata(false) + .probe("testdata") + .unwrap(); + Ok(Library::from_pkg_config(lib)) + }); + + let err = config.probe_full().unwrap_err(); + assert!(matches!( + err.into(), + ErrorKind::BuildInternalWrongVersion(..) + )); + assert_eq!(called.get(), true); +} + +#[test] +fn build_internal_fail() { + let called = Rc::new(Cell::new(false)); + let called_clone = called.clone(); + let config = create_config( + "toml-good", + vec![("METADEPS_TESTLIB_BUILD_INTERNAL", "always")], + ) + .add_build_internal("testlib", move |_version| { + called_clone.replace(true); + Err(BuildInternalClosureError::failed("Something went wrong")) + }); + + let err = config.probe_full().unwrap_err(); + assert!(matches!( + err.into(), + ErrorKind::BuildInternalClosureError(..) + )); + assert_eq!(called.get(), true); +} From 3499f24bf2c7cf4007ebeaaf2319be8a60fc1abf Mon Sep 17 00:00:00 2001 From: Guillaume Desmottes Date: Fri, 8 May 2020 11:51:15 +0200 Subject: [PATCH 25/55] fully replace error-chain by this-error --- Cargo.toml | 1 - src/lib.rs | 171 ++++++++++++++++++++++++++-------------------------- src/test.rs | 26 +++----- 3 files changed, 96 insertions(+), 102 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index a3a581c..1584922 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,7 +9,6 @@ keywords = ["pkg-config", "build-dependencies", "build-depends", "manifest", "me edition = "2018" [dependencies] -error-chain = { version = "0.12", default-features = false } pkg-config = "0.3.8" toml = { version = "0.5", default-features = false } version-compare = "0.0.10" diff --git a/src/lib.rs b/src/lib.rs index 60ee766..5ea71d8 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -14,9 +14,6 @@ #![deny(missing_docs)] -#[macro_use] -extern crate error_chain; - #[cfg(test)] #[macro_use] extern crate lazy_static; @@ -36,43 +33,40 @@ use strum_macros::EnumString; use thiserror::Error; use version_compare::VersionCompare; -// TODO: port to this-error -error_chain! { - foreign_links { - PkgConfig(pkg_config::Error) #[doc="pkg-config error"]; - BuildInternalClosureError(BuildInternalClosureError) #[doc="build internal closure failure"]; - } - errors { - /// Raised when an error is detected in the metadata defined in `Cargo.toml` - InvalidMetadata(details: String) { - display("{}", details) - } - /// Raised when dependency defined manually using `METADEPS_$NAME_NO_PKG_CONFIG` - /// did not define at least one lib using `METADEPS_$NAME_LIB` or - /// `METADEPS_$NAME_LIB_FRAMEWORK` - MissingLib(name: String) { - display("You should define at least one lib using {} or {}", - flag_override_var(name, "LIB"), - flag_override_var(name, "LIB_FRAMEWORK")) - } - /// An environnement variable in the form of `METADEPS_$NAME_BUILD_INTERNAL` - /// contained an invalid value (allowed: `auto`, `always`, `never`) - BuildInternalInvalid(details: String) { - display("{}", details) - } - /// Metadeps has been asked to internally build a lib, through - /// `METADEPS_$NAME_BUILD_INTERNAL=always' or `METADEPS_$NAME_BUILD_INTERNAL=auto', - /// but not closure has been defined using `Config::add_build_internal` to build - /// this lib - BuildInternalNoClosure(lib: String, version: String) { - display("Missing build internal closure for {} (version {})", lib, version) - } - /// The library which has been build internally does not match the - /// required version defined in `Cargo.toml` - BuildInternalWrongVersion(lib: String, version: String, expected: String) { - display("Internally built {} {} but minimum required version is {}", lib, version, expected) - } - } +/// Metadeps errors +#[derive(Error, Debug)] +pub enum Error { + /// pkg-config error + #[error(transparent)] + PkgConfig(#[from] pkg_config::Error), + /// One of the `Config::add_build_internal` closures failed + #[error("Failed to build {0}: {1}")] + BuildInternalClosureError(String, #[source] BuildInternalClosureError), + /// Failed to read `Cargo.toml` + #[error("{0}")] + FailToRead(String, #[source] std::io::Error), + /// Raised when an error is detected in the metadata defined in `Cargo.toml` + #[error("{0}")] + InvalidMetadata(String), + /// Raised when dependency defined manually using `METADEPS_$NAME_NO_PKG_CONFIG` + /// did not define at least one lib using `METADEPS_$NAME_LIB` or + /// `METADEPS_$NAME_LIB_FRAMEWORK` + #[error("You should define at least one lib using {} or {}", flag_override_var(.0, "LIB"), flag_override_var(.0, "LIB_FRAMEWORK"))] + MissingLib(String), + /// An environnement variable in the form of `METADEPS_$NAME_BUILD_INTERNAL` + /// contained an invalid value (allowed: `auto`, `always`, `never`) + #[error("{0}")] + BuildInternalInvalid(String), + /// Metadeps has been asked to internally build a lib, through + /// `METADEPS_$NAME_BUILD_INTERNAL=always' or `METADEPS_$NAME_BUILD_INTERNAL=auto', + /// but not closure has been defined using `Config::add_build_internal` to build + /// this lib + #[error("Missing build internal closure for {0} (version {1})")] + BuildInternalNoClosure(String, String), + /// The library which has been build internally does not match the + /// required version defined in `Cargo.toml` + #[error("Internally built {0} {1} but minimum required version is {2}")] + BuildInternalWrongVersion(String, String, String), } #[derive(Error, Debug)] @@ -127,7 +121,7 @@ impl Config { /// Probe all libraries configured in the Cargo.toml /// `[package.metadata.pkg-config]` section. - pub fn probe(self) -> Result> { + pub fn probe(self) -> Result, Error> { let (libraries, flags) = self.probe_full()?; // Output cargo flags @@ -160,7 +154,7 @@ impl Config { } } - fn probe_full(mut self) -> Result<(HashMap, BuildFlags)> { + fn probe_full(mut self) -> Result<(HashMap, BuildFlags), Error> { let mut libraries = self.probe_pkg_config()?; self.override_from_flags(&mut libraries); let flags = self.gen_flags(&libraries)?; @@ -168,31 +162,35 @@ impl Config { Ok((libraries, flags)) } - fn probe_pkg_config(&mut self) -> Result> { + fn probe_pkg_config(&mut self) -> Result, Error> { let dir = self .env .get("CARGO_MANIFEST_DIR") - .ok_or("$CARGO_MANIFEST_DIR not set")?; + .ok_or_else(|| Error::InvalidMetadata("$CARGO_MANIFEST_DIR not set".into()))?; let mut path = PathBuf::from(dir); path.push("Cargo.toml"); - let mut manifest = - fs::File::open(&path).chain_err(|| format!("Error opening {}", path.display()))?; + let mut manifest = fs::File::open(&path) + .map_err(|e| Error::FailToRead(format!("Error opening {}", path.display()), e))?; let mut manifest_str = String::new(); manifest .read_to_string(&mut manifest_str) - .chain_err(|| format!("Error reading {}", path.display()))?; - let toml = manifest_str - .parse::() - .map_err(|e| format!("Error parsing TOML from {}: {:?}", path.display(), e))?; + .map_err(|e| Error::FailToRead(format!("Error reading {}", path.display()), e))?; + let toml = manifest_str.parse::().map_err(|e| { + Error::InvalidMetadata(format!( + "Error parsing TOML from {}: {:?}", + path.display(), + e + )) + })?; let key = "package.metadata.pkg-config"; let meta = toml .get("package") .and_then(|v| v.get("metadata")) .and_then(|v| v.get("pkg-config")) - .ok_or(format!("No {} in {}", key, path.display()))?; - let table = meta - .as_table() - .ok_or(format!("{} not a table in {}", key, path.display()))?; + .ok_or_else(|| Error::InvalidMetadata(format!("No {} in {}", key, path.display())))?; + let table = meta.as_table().ok_or_else(|| { + Error::InvalidMetadata(format!("{} not a table in {}", key, path.display())) + })?; let mut libraries = HashMap::new(); for (name, value) in table { let (lib_name, version) = match value { @@ -221,21 +219,25 @@ impl Config { enabled_feature_versions.push(feat_vers); } } - _ => bail!(ErrorKind::InvalidMetadata(format!( - "Unexpected feature-version key: {} type {}", - k, - v.type_str() - ))), + _ => { + return Err(Error::InvalidMetadata(format!( + "Unexpected feature-version key: {} type {}", + k, + v.type_str() + ))) + } } } } - _ => bail!(ErrorKind::InvalidMetadata(format!( - "Unexpected key {}.{}.{} type {}", - key, - name, - tname, - tvalue.type_str() - ))), + _ => { + return Err(Error::InvalidMetadata(format!( + "Unexpected key {}.{}.{} type {}", + key, + name, + tname, + tvalue.type_str() + ))) + } } } if let Some(feature) = feature { @@ -261,13 +263,17 @@ impl Config { ( lib_name.unwrap_or(name), - version.ok_or(format!("No version in {}.{}", key, name))?, + version.ok_or_else(|| { + Error::InvalidMetadata(format!("No version in {}.{}", key, name)) + })?, ) } - _ => bail!(ErrorKind::InvalidMetadata(format!( - "{}.{} not a string or table", - key, name - ))), + _ => { + return Err(Error::InvalidMetadata(format!( + "{}.{} not a string or table", + key, name + ))) + } }; let build_internal = self.get_build_internal_status(name)?; @@ -300,11 +306,11 @@ impl Config { Ok(libraries) } - fn get_build_internal_status(&self, name: &str) -> Result { + fn get_build_internal_status(&self, name: &str) -> Result { let var = flag_override_var(name, "BUILD_INTERNAL"); let b = match self.env.get(&var).as_deref() { Some(s) => BuildInternal::from_str(s).map_err(|_| { - ErrorKind::BuildInternalInvalid(format!( + Error::BuildInternalInvalid(format!( "Invalid value in {}: {} (allowed: 'auto', 'always', 'never')", var, s )) @@ -315,21 +321,18 @@ impl Config { Ok(b) } - fn call_build_internal(&mut self, name: &str, version: &str) -> Result { + fn call_build_internal(&mut self, name: &str, version: &str) -> Result { let lib = match self.build_internals.remove(name) { - Some(f) => f(version)?, - None => bail!(ErrorKind::BuildInternalNoClosure( - name.into(), - version.into() - )), + Some(f) => f(version).map_err(|e| Error::BuildInternalClosureError(name.into(), e))?, + None => return Err(Error::BuildInternalNoClosure(name.into(), version.into())), }; // Check that the lib built internally matches the required version match VersionCompare::compare(&lib.version, version) { - Ok(version_compare::CompOp::Lt) => bail!(ErrorKind::BuildInternalWrongVersion( + Ok(version_compare::CompOp::Lt) => Err(Error::BuildInternalWrongVersion( name.into(), lib.version.clone(), - version.into() + version.into(), )), _ => Ok(lib), } @@ -355,7 +358,7 @@ impl Config { } } - fn gen_flags(&self, libraries: &HashMap) -> Result { + fn gen_flags(&self, libraries: &HashMap) -> Result { let mut flags = BuildFlags::new(); let mut include_paths = Vec::new(); @@ -366,7 +369,7 @@ impl Config { && lib.libs.is_empty() && lib.frameworks.is_empty() { - bail!(ErrorKind::MissingLib(name.clone())); + return Err(Error::MissingLib(name.clone())); } lib.link_paths @@ -481,7 +484,7 @@ impl Library { pkg_config_dir: P, lib: &str, version: &str, - ) -> std::result::Result + ) -> Result where P: AsRef, { diff --git a/src/test.rs b/src/test.rs index 30e93b0..99cf257 100644 --- a/src/test.rs +++ b/src/test.rs @@ -6,9 +6,7 @@ use std::path::{Path, PathBuf}; use std::rc::Rc; use std::sync::Mutex; -use super::{ - BuildFlags, BuildInternalClosureError, Config, EnvVariables, ErrorKind, Library, Result, -}; +use super::{BuildFlags, BuildInternalClosureError, Config, EnvVariables, Error, Library}; lazy_static! { static ref LOCK: Mutex<()> = Mutex::new(()); @@ -47,7 +45,7 @@ fn create_config(path: &str, env: Vec<(&'static str, &'static str)>) -> Config { fn toml( path: &str, env: Vec<(&'static str, &'static str)>, -) -> Result<(std::collections::HashMap, BuildFlags)> { +) -> Result<(std::collections::HashMap, BuildFlags), Error> { create_config(path, env).probe_full() } @@ -88,8 +86,8 @@ fn toml_pkg_config_err_version( env_vars: Vec<(&'static str, &'static str)>, ) { let err = toml(path, env_vars).unwrap_err(); - match err.kind() { - ErrorKind::PkgConfig(e) => match e { + match err { + Error::PkgConfig(e) => match e { pkg_config::Error::Failure { command: cmd, output: _, @@ -449,7 +447,7 @@ fn build_internal_auto_never() { }); let err = config.probe_full().unwrap_err(); - assert!(matches!(err.into(), ErrorKind::PkgConfig(..))); + assert!(matches!(err, Error::PkgConfig(..))); assert_eq!(called.get(), false); } @@ -462,7 +460,7 @@ fn build_internal_always_no_closure() { ); let err = config.probe_full().unwrap_err(); - assert!(matches!(err.into(), ErrorKind::BuildInternalNoClosure(..))); + assert!(matches!(err, Error::BuildInternalNoClosure(..))); } #[test] @@ -473,7 +471,7 @@ fn build_internal_invalid() { ); let err = config.probe_full().unwrap_err(); - assert!(matches!(err.into(), ErrorKind::BuildInternalInvalid(..))); + assert!(matches!(err, Error::BuildInternalInvalid(..))); } #[test] @@ -499,10 +497,7 @@ fn build_internal_wrong_version() { }); let err = config.probe_full().unwrap_err(); - assert!(matches!( - err.into(), - ErrorKind::BuildInternalWrongVersion(..) - )); + assert!(matches!(err, Error::BuildInternalWrongVersion(..))); assert_eq!(called.get(), true); } @@ -520,9 +515,6 @@ fn build_internal_fail() { }); let err = config.probe_full().unwrap_err(); - assert!(matches!( - err.into(), - ErrorKind::BuildInternalClosureError(..) - )); + assert!(matches!(err, Error::BuildInternalClosureError(..))); assert_eq!(called.get(), true); } From dfa28946be7b1d4e6b2533adef8d9dd062ec0523 Mon Sep 17 00:00:00 2001 From: Guillaume Desmottes Date: Fri, 19 Jun 2020 14:05:19 +0200 Subject: [PATCH 26/55] rename to system-deps --- Cargo.toml | 8 ++++---- README.md | 8 ++++---- src/lib.rs | 16 ++++++++-------- 3 files changed, 16 insertions(+), 16 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 1584922..74ce065 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,9 +1,9 @@ [package] -name = "metadeps" -version = "1.1.2" -authors = ["Josh Triplett "] +name = "system-deps" +version = "1.1.0" +authors = ["Guillaume Desmottes ", "Josh Triplett "] license = "MIT/Apache-2.0" -repository = "https://github.com/joshtriplett/metadeps" +repository = "https://github.com/gdesmott/system-deps" description = "Run pkg-config from declarative dependencies in Cargo.toml" keywords = ["pkg-config", "build-dependencies", "build-depends", "manifest", "metadata"] edition = "2018" diff --git a/README.md b/README.md index 28bb67a..899578f 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -metadeps lets you write `pkg-config` dependencies in `Cargo.toml` metadata, +`system-deps` lets you write `pkg-config` dependencies in `Cargo.toml` metadata, rather than programmatically in `build.rs`. This makes those dependencies declarative, so other tools can read them as well. @@ -7,7 +7,7 @@ declarative, so other tools can read them as well. In your `Cargo.toml`, add the following to your `[build-dependencies]`: ```toml -metadeps = "1.1" +system-deps = "1.1" ``` Then, to declare a dependency on `testlib >= 1.2`, a conditional dependency @@ -24,10 +24,10 @@ glib = { name = "glib-2.0", version = "2.64" } In your `build.rs`, add: ```rust -extern crate metadeps; +extern crate system_deps; fn main() { - metadeps::probe().unwrap(); + system_deps::probe().unwrap(); } ``` diff --git a/src/lib.rs b/src/lib.rs index 5ea71d8..418cdb8 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,8 +1,8 @@ -//! metadeps lets you write `pkg-config` dependencies in `Cargo.toml` metadata, +//! system-deps lets you write `pkg-config` dependencies in `Cargo.toml` metadata, //! rather than programmatically in `build.rs`. This makes those dependencies //! declarative, so other tools can read them as well. //! -//! metadeps parses metadata like this in `Cargo.toml`: +//! system-deps parses metadata like this in `Cargo.toml`: //! //! ```toml //! [package.metadata.pkg-config] @@ -33,7 +33,7 @@ use strum_macros::EnumString; use thiserror::Error; use version_compare::VersionCompare; -/// Metadeps errors +/// system-deps errors #[derive(Error, Debug)] pub enum Error { /// pkg-config error @@ -57,7 +57,7 @@ pub enum Error { /// contained an invalid value (allowed: `auto`, `always`, `never`) #[error("{0}")] BuildInternalInvalid(String), - /// Metadeps has been asked to internally build a lib, through + /// system-deps has been asked to internally build a lib, through /// `METADEPS_$NAME_BUILD_INTERNAL=always' or `METADEPS_$NAME_BUILD_INTERNAL=auto', /// but not closure has been defined using `Config::add_build_internal` to build /// this lib @@ -130,7 +130,7 @@ impl Config { Ok(libraries) } - /// Add hook so metadeps can internally build library `name` if requested by user. + /// Add hook so system-deps can internally build library `name` if requested by user. /// /// It will only be triggered if the environnement variable /// `METADEPS_$NAME_BUILD_INTERNAL` is defined with either `always` or @@ -462,7 +462,7 @@ impl Library { /// Create a `Library` by probing `pkg-config` on an internal directory. /// This helper is meant to be used by `Config::add_build_internal` closures - /// after having built the lib to return the library information to metadata. + /// after having built the lib to return the library information to system-deps. /// /// # Arguments /// @@ -473,10 +473,10 @@ impl Library { /// # Examples /// /// ``` - /// let mut config = metadeps::Config::new(); + /// let mut config = system_deps::Config::new(); /// config.add_build_internal("mylib", |version| { /// // Actually build the library here - /// metadeps::Library::from_internal_pkg_config("build-dir", + /// system_deps::Library::from_internal_pkg_config("build-dir", /// "mylib", version) /// }); /// ``` From b03ec8703381ba55c32e16a9666111d7ff76e01c Mon Sep 17 00:00:00 2001 From: Guillaume Desmottes Date: Fri, 19 Jun 2020 14:16:02 +0200 Subject: [PATCH 27/55] rename METADEPS_* env variables --- src/lib.rs | 14 +++++++------- src/test.rs | 51 +++++++++++++++++++++++++++++---------------------- 2 files changed, 36 insertions(+), 29 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 418cdb8..2e415a9 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -48,17 +48,17 @@ pub enum Error { /// Raised when an error is detected in the metadata defined in `Cargo.toml` #[error("{0}")] InvalidMetadata(String), - /// Raised when dependency defined manually using `METADEPS_$NAME_NO_PKG_CONFIG` - /// did not define at least one lib using `METADEPS_$NAME_LIB` or - /// `METADEPS_$NAME_LIB_FRAMEWORK` + /// Raised when dependency defined manually using `SYSTEM_DEPS_$NAME_NO_PKG_CONFIG` + /// did not define at least one lib using `SYSTEM_DEPS_$NAME_LIB` or + /// `SYSTEM_DEPS_$NAME_LIB_FRAMEWORK` #[error("You should define at least one lib using {} or {}", flag_override_var(.0, "LIB"), flag_override_var(.0, "LIB_FRAMEWORK"))] MissingLib(String), - /// An environnement variable in the form of `METADEPS_$NAME_BUILD_INTERNAL` + /// An environnement variable in the form of `SYSTEM_DEPS_$NAME_BUILD_INTERNAL` /// contained an invalid value (allowed: `auto`, `always`, `never`) #[error("{0}")] BuildInternalInvalid(String), /// system-deps has been asked to internally build a lib, through - /// `METADEPS_$NAME_BUILD_INTERNAL=always' or `METADEPS_$NAME_BUILD_INTERNAL=auto', + /// `SYSTEM_DEPS_$NAME_BUILD_INTERNAL=always' or `SYSTEM_DEPS_$NAME_BUILD_INTERNAL=auto', /// but not closure has been defined using `Config::add_build_internal` to build /// this lib #[error("Missing build internal closure for {0} (version {1})")] @@ -133,7 +133,7 @@ impl Config { /// Add hook so system-deps can internally build library `name` if requested by user. /// /// It will only be triggered if the environnement variable - /// `METADEPS_$NAME_BUILD_INTERNAL` is defined with either `always` or + /// `SYSTEM_DEPS_$NAME_BUILD_INTERNAL` is defined with either `always` or /// `auto` as value. In the latter case, `func` is called only if the requested /// version of the library was not found on the system. /// @@ -581,7 +581,7 @@ impl fmt::Display for BuildFlags { } fn flag_override_var(lib: &str, flag: &str) -> String { - format!("METADEPS_{}_{}", lib.to_shouty_snake_case(), flag) + format!("SYSTEM_DEPS_{}_{}", lib.to_shouty_snake_case(), flag) } fn split_paths(value: &str) -> Vec { diff --git a/src/test.rs b/src/test.rs index 99cf257..ea9f35e 100644 --- a/src/test.rs +++ b/src/test.rs @@ -185,7 +185,10 @@ fn feature_versions() { fn override_search_native() { let (libraries, flags) = toml( "toml-good", - vec![("METADEPS_TESTLIB_SEARCH_NATIVE", "/custom/path:/other/path")], + vec![( + "SYSTEM_DEPS_TESTLIB_SEARCH_NATIVE", + "/custom/path:/other/path", + )], ) .unwrap(); let testlib = libraries.get("testlib").unwrap(); @@ -210,7 +213,7 @@ cargo:include=/usr/include/testlib fn override_search_framework() { let (libraries, flags) = toml( "toml-good", - vec![("METADEPS_TESTLIB_SEARCH_FRAMEWORK", "/custom/path")], + vec![("SYSTEM_DEPS_TESTLIB_SEARCH_FRAMEWORK", "/custom/path")], ) .unwrap(); let testlib = libraries.get("testlib").unwrap(); @@ -231,7 +234,7 @@ cargo:include=/usr/include/testlib fn override_lib() { let (libraries, flags) = toml( "toml-good", - vec![("METADEPS_TESTLIB_LIB", "overrided-test other-test")], + vec![("SYSTEM_DEPS_TESTLIB_LIB", "overrided-test other-test")], ) .unwrap(); let testlib = libraries.get("testlib").unwrap(); @@ -253,7 +256,7 @@ cargo:include=/usr/include/testlib fn override_framework() { let (libraries, flags) = toml( "toml-good", - vec![("METADEPS_TESTLIB_LIB_FRAMEWORK", "overrided-framework")], + vec![("SYSTEM_DEPS_TESTLIB_LIB_FRAMEWORK", "overrided-framework")], ) .unwrap(); let testlib = libraries.get("testlib").unwrap(); @@ -274,7 +277,7 @@ cargo:include=/usr/include/testlib fn override_include() { let (libraries, flags) = toml( "toml-good", - vec![("METADEPS_TESTLIB_INCLUDE", "/other/include")], + vec![("SYSTEM_DEPS_TESTLIB_INCLUDE", "/other/include")], ) .unwrap(); let testlib = libraries.get("testlib").unwrap(); @@ -296,11 +299,11 @@ fn override_unset() { let (libraries, flags) = toml( "toml-good", vec![ - ("METADEPS_TESTLIB_SEARCH_NATIVE", ""), - ("METADEPS_TESTLIB_SEARCH_FRAMEWORK", ""), - ("METADEPS_TESTLIB_LIB", ""), - ("METADEPS_TESTLIB_LIB_FRAMEWORK", ""), - ("METADEPS_TESTLIB_INCLUDE", ""), + ("SYSTEM_DEPS_TESTLIB_SEARCH_NATIVE", ""), + ("SYSTEM_DEPS_TESTLIB_SEARCH_FRAMEWORK", ""), + ("SYSTEM_DEPS_TESTLIB_LIB", ""), + ("SYSTEM_DEPS_TESTLIB_LIB_FRAMEWORK", ""), + ("SYSTEM_DEPS_TESTLIB_INCLUDE", ""), ], ) .unwrap(); @@ -319,8 +322,8 @@ fn override_no_pkg_config() { let (libraries, flags) = toml( "toml-good", vec![ - ("METADEPS_TESTLIB_NO_PKG_CONFIG", "1"), - ("METADEPS_TESTLIB_LIB", "custom-lib"), + ("SYSTEM_DEPS_TESTLIB_NO_PKG_CONFIG", "1"), + ("SYSTEM_DEPS_TESTLIB_LIB", "custom-lib"), ], ) .unwrap(); @@ -336,10 +339,14 @@ fn override_no_pkg_config() { #[test] fn override_no_pkg_config_error() { - let err = toml("toml-good", vec![("METADEPS_TESTLIB_NO_PKG_CONFIG", "1")]).unwrap_err(); + let err = toml( + "toml-good", + vec![("SYSTEM_DEPS_TESTLIB_NO_PKG_CONFIG", "1")], + ) + .unwrap_err(); assert_eq!( err.to_string(), - "You should define at least one lib using METADEPS_TESTLIB_LIB or METADEPS_TESTLIB_LIB_FRAMEWORK" + "You should define at least one lib using SYSTEM_DEPS_TESTLIB_LIB or SYSTEM_DEPS_TESTLIB_LIB_FRAMEWORK" ); } @@ -349,7 +356,7 @@ fn build_internal_always() { let called_clone = called.clone(); let config = create_config( "toml-good", - vec![("METADEPS_TESTLIB_BUILD_INTERNAL", "always")], + vec![("SYSTEM_DEPS_TESTLIB_BUILD_INTERNAL", "always")], ) .add_build_internal("testlib", move |version| { called_clone.replace(true); @@ -375,7 +382,7 @@ fn build_internal_auto_not_called() { let called_clone = called.clone(); let config = create_config( "toml-good", - vec![("METADEPS_TESTLIB_BUILD_INTERNAL", "auto")], + vec![("SYSTEM_DEPS_TESTLIB_BUILD_INTERNAL", "auto")], ) .add_build_internal("testlib", move |_version| { called_clone.replace(true); @@ -401,7 +408,7 @@ fn build_internal_auto_called() { let config = create_config( "toml-feature-versions", vec![ - ("METADEPS_TESTDATA_BUILD_INTERNAL", "auto"), + ("SYSTEM_DEPS_TESTDATA_BUILD_INTERNAL", "auto"), ("CARGO_FEATURE_V5", ""), ], ) @@ -431,7 +438,7 @@ fn build_internal_auto_never() { let config = create_config( "toml-feature-versions", vec![ - ("METADEPS_TESTDATA_BUILD_INTERNAL", "never"), + ("SYSTEM_DEPS_TESTDATA_BUILD_INTERNAL", "never"), ("CARGO_FEATURE_V5", ""), ], ) @@ -456,7 +463,7 @@ fn build_internal_auto_never() { fn build_internal_always_no_closure() { let config = create_config( "toml-good", - vec![("METADEPS_TESTLIB_BUILD_INTERNAL", "always")], + vec![("SYSTEM_DEPS_TESTLIB_BUILD_INTERNAL", "always")], ); let err = config.probe_full().unwrap_err(); @@ -467,7 +474,7 @@ fn build_internal_always_no_closure() { fn build_internal_invalid() { let config = create_config( "toml-good", - vec![("METADEPS_TESTLIB_BUILD_INTERNAL", "badger")], + vec![("SYSTEM_DEPS_TESTLIB_BUILD_INTERNAL", "badger")], ); let err = config.probe_full().unwrap_err(); @@ -482,7 +489,7 @@ fn build_internal_wrong_version() { let config = create_config( "toml-feature-versions", vec![ - ("METADEPS_TESTDATA_BUILD_INTERNAL", "auto"), + ("SYSTEM_DEPS_TESTDATA_BUILD_INTERNAL", "auto"), ("CARGO_FEATURE_V5", ""), ], ) @@ -507,7 +514,7 @@ fn build_internal_fail() { let called_clone = called.clone(); let config = create_config( "toml-good", - vec![("METADEPS_TESTLIB_BUILD_INTERNAL", "always")], + vec![("SYSTEM_DEPS_TESTLIB_BUILD_INTERNAL", "always")], ) .add_build_internal("testlib", move |_version| { called_clone.replace(true); From 1fb4e57144bc16f0bd954cf6e521b60bd7187a3b Mon Sep 17 00:00:00 2001 From: Guillaume Desmottes Date: Fri, 19 Jun 2020 14:26:50 +0200 Subject: [PATCH 28/55] rename toml section Prevent clash with metadeps meta formats. Also, hopefully we'll no longer be pkg-config specific. --- README.md | 4 ++-- src/lib.rs | 9 ++++----- src/test.rs | 17 +++++++---------- src/tests/toml-feature-not-string/Cargo.toml | 2 +- src/tests/toml-feature-versions/Cargo.toml | 2 +- src/tests/toml-good/Cargo.toml | 2 +- src/tests/toml-not-table/Cargo.toml | 4 ++-- src/tests/toml-override-name/Cargo.toml | 2 +- src/tests/toml-unexpected-key/Cargo.toml | 2 +- .../toml-version-in-table-not-string/Cargo.toml | 2 +- src/tests/toml-version-missing/Cargo.toml | 2 +- src/tests/toml-version-not-string/Cargo.toml | 2 +- 12 files changed, 23 insertions(+), 27 deletions(-) diff --git a/README.md b/README.md index 899578f..e677a15 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,7 @@ on `testdata >= 4.5` and a dependency on `glib-2.0 >= 2.64` add the following section: ```toml -[package.metadata.pkg-config] +[package.system-deps] testlib = "1.2" testdata = { version = "4.5", feature = "use-testdata" } glib = { name = "glib-2.0", version = "2.64" } @@ -38,7 +38,7 @@ Dependency versions can also be controlled using features: v1_2 = [] v1_4 = ["v1_4"] -[package.metadata.pkg-config] +[package.system-deps] gstreamer = { name = "gstreamer-1.0", version = "1.0", feature-versions = { v1_2 = "1.2", v1_4 = "1.4" }} ``` diff --git a/src/lib.rs b/src/lib.rs index 2e415a9..39ac946 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -5,7 +5,7 @@ //! system-deps parses metadata like this in `Cargo.toml`: //! //! ```toml -//! [package.metadata.pkg-config] +//! [package.system-deps] //! testlib = "1.2" //! testdata = { version = "4.5", feature = "some-feature" } //! glib = { name = "glib-2.0", version = "2.64" } @@ -120,7 +120,7 @@ impl Config { } /// Probe all libraries configured in the Cargo.toml - /// `[package.metadata.pkg-config]` section. + /// `[package.system-deps]` section. pub fn probe(self) -> Result, Error> { let (libraries, flags) = self.probe_full()?; @@ -182,11 +182,10 @@ impl Config { e )) })?; - let key = "package.metadata.pkg-config"; + let key = "package.system-deps"; let meta = toml .get("package") - .and_then(|v| v.get("metadata")) - .and_then(|v| v.get("pkg-config")) + .and_then(|v| v.get("system-deps")) .ok_or_else(|| Error::InvalidMetadata(format!("No {} in {}", key, path.display())))?; let table = meta.as_table().ok_or_else(|| { Error::InvalidMetadata(format!("{} not a table in {}", key, path.display())) diff --git a/src/test.rs b/src/test.rs index ea9f35e..70b17c2 100644 --- a/src/test.rs +++ b/src/test.rs @@ -108,22 +108,19 @@ fn missing_file() { #[test] fn missing_key() { - toml_err("toml-missing-key", "No package.metadata.pkg-config in"); + toml_err("toml-missing-key", "No package.system-deps in"); } #[test] fn not_table() { - toml_err( - "toml-not-table", - "package.metadata.pkg-config not a table in", - ); + toml_err("toml-not-table", "package.system-deps not a table in"); } #[test] fn version_missing() { toml_err( "toml-version-missing", - "No version in package.metadata.pkg-config.testlib", + "No version in package.system-deps.testlib", ); } @@ -131,7 +128,7 @@ fn version_missing() { fn version_not_string() { toml_err( "toml-version-not-string", - "package.metadata.pkg-config.testlib not a string or table", + "package.system-deps.testlib not a string or table", ); } @@ -139,7 +136,7 @@ fn version_not_string() { fn version_in_table_not_string() { toml_err( "toml-version-in-table-not-string", - "Unexpected key package.metadata.pkg-config.testlib.version type integer", + "Unexpected key package.system-deps.testlib.version type integer", ); } @@ -147,7 +144,7 @@ fn version_in_table_not_string() { fn feature_not_string() { toml_err( "toml-feature-not-string", - "Unexpected key package.metadata.pkg-config.testlib.feature type integer", + "Unexpected key package.system-deps.testlib.feature type integer", ); } @@ -155,7 +152,7 @@ fn feature_not_string() { fn unexpected_key() { toml_err( "toml-unexpected-key", - "Unexpected key package.metadata.pkg-config.testlib.color type string", + "Unexpected key package.system-deps.testlib.color type string", ); } diff --git a/src/tests/toml-feature-not-string/Cargo.toml b/src/tests/toml-feature-not-string/Cargo.toml index 5d2d7f3..dcc53e4 100644 --- a/src/tests/toml-feature-not-string/Cargo.toml +++ b/src/tests/toml-feature-not-string/Cargo.toml @@ -1,2 +1,2 @@ -[package.metadata.pkg-config] +[package.system-deps] testlib = { version = "1", feature = 2 } diff --git a/src/tests/toml-feature-versions/Cargo.toml b/src/tests/toml-feature-versions/Cargo.toml index 92640a1..cba417e 100644 --- a/src/tests/toml-feature-versions/Cargo.toml +++ b/src/tests/toml-feature-versions/Cargo.toml @@ -1,2 +1,2 @@ -[package.metadata.pkg-config] +[package.system-deps] testdata = { version = "4", feature-versions = { v5 = "5", v6 = "6" }} diff --git a/src/tests/toml-good/Cargo.toml b/src/tests/toml-good/Cargo.toml index af324de..852ce2e 100644 --- a/src/tests/toml-good/Cargo.toml +++ b/src/tests/toml-good/Cargo.toml @@ -1,4 +1,4 @@ -[package.metadata.pkg-config] +[package.system-deps] testdata = "4" testlib = { version = "1", feature = "test-feature" } testmore = { version = "2", feature = "another-test-feature" } diff --git a/src/tests/toml-not-table/Cargo.toml b/src/tests/toml-not-table/Cargo.toml index 290f07c..ca063cd 100644 --- a/src/tests/toml-not-table/Cargo.toml +++ b/src/tests/toml-not-table/Cargo.toml @@ -1,2 +1,2 @@ -[package.metadata] -pkg-config = "not a table" +[package] +system-deps = "not a table" diff --git a/src/tests/toml-override-name/Cargo.toml b/src/tests/toml-override-name/Cargo.toml index ce438e7..e3654a1 100644 --- a/src/tests/toml-override-name/Cargo.toml +++ b/src/tests/toml-override-name/Cargo.toml @@ -1,2 +1,2 @@ -[package.metadata.pkg-config] +[package.system-deps] testlib = { name = "testlib-2.0", version = "2" } diff --git a/src/tests/toml-unexpected-key/Cargo.toml b/src/tests/toml-unexpected-key/Cargo.toml index c79e7cb..a475353 100644 --- a/src/tests/toml-unexpected-key/Cargo.toml +++ b/src/tests/toml-unexpected-key/Cargo.toml @@ -1,2 +1,2 @@ -[package.metadata.pkg-config] +[package.system-deps] testlib = { version = "1", color = "blue" } diff --git a/src/tests/toml-version-in-table-not-string/Cargo.toml b/src/tests/toml-version-in-table-not-string/Cargo.toml index a0e3293..e3279b2 100644 --- a/src/tests/toml-version-in-table-not-string/Cargo.toml +++ b/src/tests/toml-version-in-table-not-string/Cargo.toml @@ -1,2 +1,2 @@ -[package.metadata.pkg-config] +[package.system-deps] testlib = { version = 1 } diff --git a/src/tests/toml-version-missing/Cargo.toml b/src/tests/toml-version-missing/Cargo.toml index 35a1088..21a1088 100644 --- a/src/tests/toml-version-missing/Cargo.toml +++ b/src/tests/toml-version-missing/Cargo.toml @@ -1,2 +1,2 @@ -[package.metadata.pkg-config] +[package.system-deps] testlib = { feature = "test-feature" } diff --git a/src/tests/toml-version-not-string/Cargo.toml b/src/tests/toml-version-not-string/Cargo.toml index 5ae3f6e..351883f 100644 --- a/src/tests/toml-version-not-string/Cargo.toml +++ b/src/tests/toml-version-not-string/Cargo.toml @@ -1,2 +1,2 @@ -[package.metadata.pkg-config] +[package.system-deps] testlib = 1 From 231e60785de62914204cb1ade3a481593b6b8336 Mon Sep 17 00:00:00 2001 From: Guillaume Desmottes Date: Fri, 19 Jun 2020 14:38:33 +0200 Subject: [PATCH 29/55] README: update --- README.md | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index e677a15..99efb9d 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,13 @@ -`system-deps` lets you write `pkg-config` dependencies in `Cargo.toml` metadata, -rather than programmatically in `build.rs`. This makes those dependencies +`system-deps` lets you write system dependencies in `Cargo.toml` metadata, +rather than programmatically in `build.rs`. This makes those dependencies declarative, so other tools can read them as well. +For now only `pkg-config` dependencies are supported but we are planning to +[expand it](https://github.com/gdesmott/system-deps/issues/3) at some point. + +`system-deps` has been started as a fork of the +[metadeps](https://github.com/joshtriplett/metadeps) project. + # Usage In your `Cargo.toml`, add the following to your `[build-dependencies]`: @@ -24,10 +30,8 @@ glib = { name = "glib-2.0", version = "2.64" } In your `build.rs`, add: ```rust -extern crate system_deps; - fn main() { - system_deps::probe().unwrap(); + system_deps::Config::new().probe().unwrap(); } ``` From bc4b1e5da888b0c95cdf0c870fadc1508d869ffd Mon Sep 17 00:00:00 2001 From: Guillaume Desmottes Date: Fri, 19 Jun 2020 14:53:52 +0200 Subject: [PATCH 30/55] doc: sync with README --- src/lib.rs | 31 ++++++++++++++++++++++++++----- 1 file changed, 26 insertions(+), 5 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 39ac946..47e5044 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,16 +1,37 @@ -//! system-deps lets you write `pkg-config` dependencies in `Cargo.toml` metadata, -//! rather than programmatically in `build.rs`. This makes those dependencies +//!`system-deps` lets you write system dependencies in `Cargo.toml` metadata, +//! rather than programmatically in `build.rs`. This makes those dependencies //! declarative, so other tools can read them as well. -//! -//! system-deps parses metadata like this in `Cargo.toml`: +//! Then, to declare a dependency on `testlib >= 1.2`, a conditional dependency +//! on `testdata >= 4.5` and a dependency on `glib-2.0 >= 2.64` +//! add the following section: //! //! ```toml //! [package.system-deps] //! testlib = "1.2" -//! testdata = { version = "4.5", feature = "some-feature" } +//! testdata = { version = "4.5", feature = "use-testdata" } //! glib = { name = "glib-2.0", version = "2.64" } +//! ``` +//! +//! In your `build.rs`, add: +//! +//! ```should_panic +//! fn main() { +//! system_deps::Config::new().probe().unwrap(); +//! } +//! ``` +//! +//! Dependency versions can also be controlled using features: +//! +//! ```toml +//! [features] +//! v1_2 = [] +//! v1_4 = ["v1_4"] +//! +//! [package.system-deps] //! gstreamer = { name = "gstreamer-1.0", version = "1.0", feature-versions = { v1_2 = "1.2", v1_4 = "1.4" }} //! ``` +//! +//! In this case the highest version among enabled features will be used. #![deny(missing_docs)] From 47a1c5de422bf7cef6cd9b1b6d0f13bbe00d3b1f Mon Sep 17 00:00:00 2001 From: Guillaume Desmottes Date: Fri, 19 Jun 2020 15:12:17 +0200 Subject: [PATCH 31/55] Cargo: add documentation and readme --- Cargo.toml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Cargo.toml b/Cargo.toml index 74ce065..b2391db 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,6 +7,8 @@ repository = "https://github.com/gdesmott/system-deps" description = "Run pkg-config from declarative dependencies in Cargo.toml" keywords = ["pkg-config", "build-dependencies", "build-depends", "manifest", "metadata"] edition = "2018" +documentation = "https://docs.rs/system-deps/" +readme = "README.md" [dependencies] pkg-config = "0.3.8" From 65d4da09746d94b24e0fd4fc1d687ba163bba17a Mon Sep 17 00:00:00 2001 From: Guillaume Desmottes Date: Fri, 19 Jun 2020 15:13:19 +0200 Subject: [PATCH 32/55] toml: move back to metadata section It's actually mandator, see https://doc.rust-lang.org/cargo/reference/manifest.html#the-metadata-table --- README.md | 4 ++-- src/lib.rs | 7 ++++--- src/test.rs | 17 ++++++++++------- src/tests/toml-feature-not-string/Cargo.toml | 2 +- src/tests/toml-feature-versions/Cargo.toml | 2 +- src/tests/toml-good/Cargo.toml | 2 +- src/tests/toml-not-table/Cargo.toml | 2 +- src/tests/toml-override-name/Cargo.toml | 2 +- src/tests/toml-unexpected-key/Cargo.toml | 2 +- .../toml-version-in-table-not-string/Cargo.toml | 2 +- src/tests/toml-version-missing/Cargo.toml | 2 +- src/tests/toml-version-not-string/Cargo.toml | 2 +- 12 files changed, 25 insertions(+), 21 deletions(-) diff --git a/README.md b/README.md index 99efb9d..acf37e8 100644 --- a/README.md +++ b/README.md @@ -21,7 +21,7 @@ on `testdata >= 4.5` and a dependency on `glib-2.0 >= 2.64` add the following section: ```toml -[package.system-deps] +[package.metadata.system-deps] testlib = "1.2" testdata = { version = "4.5", feature = "use-testdata" } glib = { name = "glib-2.0", version = "2.64" } @@ -42,7 +42,7 @@ Dependency versions can also be controlled using features: v1_2 = [] v1_4 = ["v1_4"] -[package.system-deps] +[package.metadata.system-deps] gstreamer = { name = "gstreamer-1.0", version = "1.0", feature-versions = { v1_2 = "1.2", v1_4 = "1.4" }} ``` diff --git a/src/lib.rs b/src/lib.rs index 47e5044..9dff677 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -6,7 +6,7 @@ //! add the following section: //! //! ```toml -//! [package.system-deps] +//! [package.metadata.system-deps] //! testlib = "1.2" //! testdata = { version = "4.5", feature = "use-testdata" } //! glib = { name = "glib-2.0", version = "2.64" } @@ -141,7 +141,7 @@ impl Config { } /// Probe all libraries configured in the Cargo.toml - /// `[package.system-deps]` section. + /// `[package.metadata.system-deps]` section. pub fn probe(self) -> Result, Error> { let (libraries, flags) = self.probe_full()?; @@ -203,9 +203,10 @@ impl Config { e )) })?; - let key = "package.system-deps"; + let key = "package.metadata.system-deps"; let meta = toml .get("package") + .and_then(|v| v.get("metadata")) .and_then(|v| v.get("system-deps")) .ok_or_else(|| Error::InvalidMetadata(format!("No {} in {}", key, path.display())))?; let table = meta.as_table().ok_or_else(|| { diff --git a/src/test.rs b/src/test.rs index 70b17c2..62a28d5 100644 --- a/src/test.rs +++ b/src/test.rs @@ -108,19 +108,22 @@ fn missing_file() { #[test] fn missing_key() { - toml_err("toml-missing-key", "No package.system-deps in"); + toml_err("toml-missing-key", "No package.metadata.system-deps in"); } #[test] fn not_table() { - toml_err("toml-not-table", "package.system-deps not a table in"); + toml_err( + "toml-not-table", + "package.metadata.system-deps not a table in", + ); } #[test] fn version_missing() { toml_err( "toml-version-missing", - "No version in package.system-deps.testlib", + "No version in package.metadata.system-deps.testlib", ); } @@ -128,7 +131,7 @@ fn version_missing() { fn version_not_string() { toml_err( "toml-version-not-string", - "package.system-deps.testlib not a string or table", + "package.metadata.system-deps.testlib not a string or table", ); } @@ -136,7 +139,7 @@ fn version_not_string() { fn version_in_table_not_string() { toml_err( "toml-version-in-table-not-string", - "Unexpected key package.system-deps.testlib.version type integer", + "Unexpected key package.metadata.system-deps.testlib.version type integer", ); } @@ -144,7 +147,7 @@ fn version_in_table_not_string() { fn feature_not_string() { toml_err( "toml-feature-not-string", - "Unexpected key package.system-deps.testlib.feature type integer", + "Unexpected key package.metadata.system-deps.testlib.feature type integer", ); } @@ -152,7 +155,7 @@ fn feature_not_string() { fn unexpected_key() { toml_err( "toml-unexpected-key", - "Unexpected key package.system-deps.testlib.color type string", + "Unexpected key package.metadata.system-deps.testlib.color type string", ); } diff --git a/src/tests/toml-feature-not-string/Cargo.toml b/src/tests/toml-feature-not-string/Cargo.toml index dcc53e4..e794ddd 100644 --- a/src/tests/toml-feature-not-string/Cargo.toml +++ b/src/tests/toml-feature-not-string/Cargo.toml @@ -1,2 +1,2 @@ -[package.system-deps] +[package.metadata.system-deps] testlib = { version = "1", feature = 2 } diff --git a/src/tests/toml-feature-versions/Cargo.toml b/src/tests/toml-feature-versions/Cargo.toml index cba417e..8d10422 100644 --- a/src/tests/toml-feature-versions/Cargo.toml +++ b/src/tests/toml-feature-versions/Cargo.toml @@ -1,2 +1,2 @@ -[package.system-deps] +[package.metadata.system-deps] testdata = { version = "4", feature-versions = { v5 = "5", v6 = "6" }} diff --git a/src/tests/toml-good/Cargo.toml b/src/tests/toml-good/Cargo.toml index 852ce2e..a77ad9f 100644 --- a/src/tests/toml-good/Cargo.toml +++ b/src/tests/toml-good/Cargo.toml @@ -1,4 +1,4 @@ -[package.system-deps] +[package.metadata.system-deps] testdata = "4" testlib = { version = "1", feature = "test-feature" } testmore = { version = "2", feature = "another-test-feature" } diff --git a/src/tests/toml-not-table/Cargo.toml b/src/tests/toml-not-table/Cargo.toml index ca063cd..d309622 100644 --- a/src/tests/toml-not-table/Cargo.toml +++ b/src/tests/toml-not-table/Cargo.toml @@ -1,2 +1,2 @@ -[package] +[package.metadata] system-deps = "not a table" diff --git a/src/tests/toml-override-name/Cargo.toml b/src/tests/toml-override-name/Cargo.toml index e3654a1..a89d8c8 100644 --- a/src/tests/toml-override-name/Cargo.toml +++ b/src/tests/toml-override-name/Cargo.toml @@ -1,2 +1,2 @@ -[package.system-deps] +[package.metadata.system-deps] testlib = { name = "testlib-2.0", version = "2" } diff --git a/src/tests/toml-unexpected-key/Cargo.toml b/src/tests/toml-unexpected-key/Cargo.toml index a475353..6d05bfa 100644 --- a/src/tests/toml-unexpected-key/Cargo.toml +++ b/src/tests/toml-unexpected-key/Cargo.toml @@ -1,2 +1,2 @@ -[package.system-deps] +[package.metadata.system-deps] testlib = { version = "1", color = "blue" } diff --git a/src/tests/toml-version-in-table-not-string/Cargo.toml b/src/tests/toml-version-in-table-not-string/Cargo.toml index e3279b2..79ed920 100644 --- a/src/tests/toml-version-in-table-not-string/Cargo.toml +++ b/src/tests/toml-version-in-table-not-string/Cargo.toml @@ -1,2 +1,2 @@ -[package.system-deps] +[package.metadata.system-deps] testlib = { version = 1 } diff --git a/src/tests/toml-version-missing/Cargo.toml b/src/tests/toml-version-missing/Cargo.toml index 21a1088..0b6c924 100644 --- a/src/tests/toml-version-missing/Cargo.toml +++ b/src/tests/toml-version-missing/Cargo.toml @@ -1,2 +1,2 @@ -[package.system-deps] +[package.metadata.system-deps] testlib = { feature = "test-feature" } diff --git a/src/tests/toml-version-not-string/Cargo.toml b/src/tests/toml-version-not-string/Cargo.toml index 351883f..1dcf8ed 100644 --- a/src/tests/toml-version-not-string/Cargo.toml +++ b/src/tests/toml-version-not-string/Cargo.toml @@ -1,2 +1,2 @@ -[package.system-deps] +[package.metadata.system-deps] testlib = 1 From 5c5fafebaa14fab7d657a1dd408474cdb782476a Mon Sep 17 00:00:00 2001 From: Guillaume Desmottes Date: Fri, 19 Jun 2020 15:21:52 +0200 Subject: [PATCH 33/55] 1.2.0 --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index b2391db..e569430 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "system-deps" -version = "1.1.0" +version = "1.2.0" authors = ["Guillaume Desmottes ", "Josh Triplett "] license = "MIT/Apache-2.0" repository = "https://github.com/gdesmott/system-deps" From 192e0b794ca5988a1a005f42acc5afdd5a7a1fe7 Mon Sep 17 00:00:00 2001 From: Guillaume Desmottes Date: Fri, 19 Jun 2020 15:25:49 +0200 Subject: [PATCH 34/55] Cargo: update description --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index e569430..919f9d7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,7 +4,7 @@ version = "1.2.0" authors = ["Guillaume Desmottes ", "Josh Triplett "] license = "MIT/Apache-2.0" repository = "https://github.com/gdesmott/system-deps" -description = "Run pkg-config from declarative dependencies in Cargo.toml" +description = "Discover and configure system dependencies from declarative dependencies in Cargo.toml" keywords = ["pkg-config", "build-dependencies", "build-depends", "manifest", "metadata"] edition = "2018" documentation = "https://docs.rs/system-deps/" From 3585b22d4a2c14d0493007ae481c7fedb24978c6 Mon Sep 17 00:00:00 2001 From: Guillaume Desmottes Date: Fri, 19 Jun 2020 15:28:30 +0200 Subject: [PATCH 35/55] README: update system-deps version --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index acf37e8..5705f07 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,7 @@ For now only `pkg-config` dependencies are supported but we are planning to In your `Cargo.toml`, add the following to your `[build-dependencies]`: ```toml -system-deps = "1.1" +system-deps = "1.2" ``` Then, to declare a dependency on `testlib >= 1.2`, a conditional dependency From fe2892f112d5c9bee96213ca706ca6da204ef2b8 Mon Sep 17 00:00:00 2001 From: Guillaume Desmottes Date: Fri, 19 Jun 2020 16:04:41 +0200 Subject: [PATCH 36/55] README: simplify doc --- README.md | 35 ++++++++++++++++------------------- 1 file changed, 16 insertions(+), 19 deletions(-) diff --git a/README.md b/README.md index 5705f07..7828931 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,5 @@ +# system-deps [![](https://img.shields.io/crates/v/system-deps.svg)](https://crates.io/crates/system-deps) [![](https://docs.rs/system-deps/badge.svg)](https://docs.rs/system-deps) + `system-deps` lets you write system dependencies in `Cargo.toml` metadata, rather than programmatically in `build.rs`. This makes those dependencies declarative, so other tools can read them as well. @@ -5,29 +7,35 @@ declarative, so other tools can read them as well. For now only `pkg-config` dependencies are supported but we are planning to [expand it](https://github.com/gdesmott/system-deps/issues/3) at some point. +Users can override dependency flags using environment variables if needed. +`system-deps` also allow `-sys` crates to optionnally internally build and +static link the required system library. + `system-deps` has been started as a fork of the [metadeps](https://github.com/joshtriplett/metadeps) project. -# Usage +## Documentation + +See the [crate documentation](https://docs.rs/system-deps/). -In your `Cargo.toml`, add the following to your `[build-dependencies]`: +## Usage + +In your `Cargo.toml`: ```toml +[build-dependencies] system-deps = "1.2" ``` -Then, to declare a dependency on `testlib >= 1.2`, a conditional dependency -on `testdata >= 4.5` and a dependency on `glib-2.0 >= 2.64` +Then, to declare a dependency on `testlib >= 1.2` add the following section: ```toml [package.metadata.system-deps] testlib = "1.2" -testdata = { version = "4.5", feature = "use-testdata" } -glib = { name = "glib-2.0", version = "2.64" } ``` -In your `build.rs`, add: +Finally, in your `build.rs`, add: ```rust fn main() { @@ -35,15 +43,4 @@ fn main() { } ``` -Dependency versions can also be controlled using features: - -```toml -[features] -v1_2 = [] -v1_4 = ["v1_4"] - -[package.metadata.system-deps] -gstreamer = { name = "gstreamer-1.0", version = "1.0", feature-versions = { v1_2 = "1.2", v1_4 = "1.4" }} -``` - -In this case the highest version among enabled features will be used. +See the [crate documentation](https://docs.rs/system-deps/) for more advanced features. \ No newline at end of file From acf3e8ff02f5ce663c6cae08e380434526414347 Mon Sep 17 00:00:00 2001 From: Guillaume Desmottes Date: Mon, 22 Jun 2020 12:03:47 +0200 Subject: [PATCH 37/55] improve doc --- src/lib.rs | 85 +++++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 72 insertions(+), 13 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 9dff677..9223acb 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,18 +1,24 @@ //!`system-deps` lets you write system dependencies in `Cargo.toml` metadata, //! rather than programmatically in `build.rs`. This makes those dependencies //! declarative, so other tools can read them as well. -//! Then, to declare a dependency on `testlib >= 1.2`, a conditional dependency -//! on `testdata >= 4.5` and a dependency on `glib-2.0 >= 2.64` +//! +//! # Usage +//! In your `Cargo.toml`: +//! +//! ```toml +//! [build-dependencies] +//! system-deps = "1.2" +//! ``` +//! +//! Then, to declare a dependency on `testlib >= 1.2` //! add the following section: //! //! ```toml //! [package.metadata.system-deps] //! testlib = "1.2" -//! testdata = { version = "4.5", feature = "use-testdata" } -//! glib = { name = "glib-2.0", version = "2.64" } //! ``` //! -//! In your `build.rs`, add: +//! Finally, in your `build.rs`, add: //! //! ```should_panic //! fn main() { @@ -20,18 +26,71 @@ //! } //! ``` //! -//! Dependency versions can also be controlled using features: +//! # Optional dependency +//! You can easily declare an optional system dependency by associating it with a feature: +//! +//! ```toml +//! [package.metadata.system-deps] +//! testdata = { version = "4.5", feature = "use-testdata" } +//! ``` +//! +//! # Overriding library name +//! `toml` keys cannot contain dot characters so if your library name does you can define it using the `name` field: +//! +//! ```toml +//! [package.metadata.system-deps] +//! glib = { name = "glib-2.0", version = "2.64" } +//! ``` +//! # Feature versions +//! `-sys` crates willing to support various versions of their underlying system libraries +//! can use features to control the version of the dependency required. +//! `system-deps` will pick the highest version among enabled features. //! //! ```toml //! [features] //! v1_2 = [] -//! v1_4 = ["v1_4"] +//! v1_4 = ["v1_2"] +//! v1_6 = ["v1_4"] +//! +//! [package.metadata.system-deps] +//! gstreamer = { name = "gstreamer-1.0", version = "1.0", feature-versions = { v1_2 = "1.2", v1_4 = "1.4", v1_6 = "1.6" }} +//! ``` //! -//! [package.system-deps] -//! gstreamer = { name = "gstreamer-1.0", version = "1.0", feature-versions = { v1_2 = "1.2", v1_4 = "1.4" }} +//! # 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: +//! - `SYSTEM_DEPS_$NAME_SEARCH_NATIVE` to override the [`cargo:rustc-link-search=native`](https://doc.rust-lang.org/cargo/reference/build-scripts.html#cargorustc-link-searchkindpath) flag; +//! - `SYSTEM_DEPS_$NAME_SEARCH_FRAMEWORK` to override the [`cargo:rustc-link-search=framework`](https://doc.rust-lang.org/cargo/reference/build-scripts.html#cargorustc-link-searchkindpath) flag; +//! - `SYSTEM_DEPS_$NAME_LIB` to override the [`cargo:rustc-link-lib`](https://doc.rust-lang.org/cargo/reference/build-scripts.html#rustc-link-lib) flag; +//! - `SYSTEM_DEPS_$NAME_LIB_FRAMEWORK` to override the [`cargo:rustc-link-lib=framework`](https://doc.rust-lang.org/cargo/reference/build-scripts.html#rustc-link-lib) flag; +//! - `SYSTEM_DEPS_$NAME_INCLUDE` to override the [`cargo:include`](https://kornel.ski/rust-sys-crate#headers) flag. +//! +//! With `$NAME` being the upper case name of the key defining the dependency in `Cargo.toml`. +//! For example `SYSTEM_DEPS_TESTLIB_SEARCH_NATIVE=/opt/lib` could be used to override a dependency named `testlib`. +//! +//! One can also define the environment variable `SYSTEM_DEPS_$NAME_NO_PKG_CONFIG` to fully disable `pkg-config` lookup +//! for the given dependency. In this case at least SYSTEM_DEPS_$NAME_LIB or SYSTEM_DEPS_$NAME_LIB_FRAMEWORK should be defined as well. +//! +//! # Statically build system library +//! `-sys` crates can provide support for building and statically link their underlying system libray as part of their build process. +//! Here is how to do this in your `build.rs`: +//! ```should_panic +//! fn main() { +//! system_deps::Config::new() +//! .add_build_internal("testlib", |version| { +//! // Actually build the library here +//! system_deps::Library::from_internal_pkg_config("build/path-to-pc-file", "testlib", version) +//! }) +//! .probe() +//! .unwrap(); +//! } //! ``` //! -//! In this case the highest version among enabled features will be used. +//! This feature can be controlled using the `SYSTEM_DEPS_$NAME_BUILD_INTERNAL` environment variable +//! which can have the following values: +//! - `auto`: (default) build the dependency only if the required version has not been found by `pkg-config`; +//! - `always`: always build the dependency, ignoring any version which may be installed on the system; +//! - `never`: never build the dependency, `system-deps` will fail if the required version is not found on the system. #![deny(missing_docs)] @@ -74,7 +133,7 @@ pub enum Error { /// `SYSTEM_DEPS_$NAME_LIB_FRAMEWORK` #[error("You should define at least one lib using {} or {}", flag_override_var(.0, "LIB"), flag_override_var(.0, "LIB_FRAMEWORK"))] MissingLib(String), - /// An environnement variable in the form of `SYSTEM_DEPS_$NAME_BUILD_INTERNAL` + /// An environment variable in the form of `SYSTEM_DEPS_$NAME_BUILD_INTERNAL` /// contained an invalid value (allowed: `auto`, `always`, `never`) #[error("{0}")] BuildInternalInvalid(String), @@ -153,7 +212,7 @@ impl Config { /// Add hook so system-deps can internally build library `name` if requested by user. /// - /// It will only be triggered if the environnement variable + /// It will only be triggered if the environment variable /// `SYSTEM_DEPS_$NAME_BUILD_INTERNAL` is defined with either `always` or /// `auto` as value. In the latter case, `func` is called only if the requested /// version of the library was not found on the system. @@ -429,7 +488,7 @@ impl Config { pub enum Source { /// Settings have been retrieved from `pkg-config` PkgConfig, - /// Settings have been defined using user defined environnement variables + /// Settings have been defined using user defined environment variables EnvVariables, } From 07900f366374bebbe046e571d2ea1690dea1b414 Mon Sep 17 00:00:00 2001 From: Guillaume Desmottes Date: Mon, 22 Jun 2020 14:15:38 +0200 Subject: [PATCH 38/55] doc: fix default BUILD_INTERNAL --- src/lib.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 9223acb..632b146 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -88,9 +88,9 @@ //! //! This feature can be controlled using the `SYSTEM_DEPS_$NAME_BUILD_INTERNAL` environment variable //! which can have the following values: -//! - `auto`: (default) build the dependency only if the required version has not been found by `pkg-config`; +//! - `auto`: build the dependency only if the required version has not been found by `pkg-config`; //! - `always`: always build the dependency, ignoring any version which may be installed on the system; -//! - `never`: never build the dependency, `system-deps` will fail if the required version is not found on the system. +//! - `never`: (default) never build the dependency, `system-deps` will fail if the required version is not found on the system. #![deny(missing_docs)] From e0ae62a12eb12fbd8ac1615b2a6e038946550e3f Mon Sep 17 00:00:00 2001 From: Guillaume Desmottes Date: Mon, 22 Jun 2020 14:27:36 +0200 Subject: [PATCH 39/55] test: factor out test_build_internal() --- src/test.rs | 96 ++++++++++++++++++++--------------------------------- 1 file changed, 36 insertions(+), 60 deletions(-) diff --git a/src/test.rs b/src/test.rs index 62a28d5..8cbf0f8 100644 --- a/src/test.rs +++ b/src/test.rs @@ -350,113 +350,89 @@ fn override_no_pkg_config_error() { ); } -#[test] -fn build_internal_always() { +fn test_build_internal( + path: &'static str, + env: Vec<(&'static str, &'static str)>, + lib: &'static str, +) -> Result<(HashMap, bool), (Error, bool)> { let called = Rc::new(Cell::new(false)); let called_clone = called.clone(); - let config = create_config( - "toml-good", - vec![("SYSTEM_DEPS_TESTLIB_BUILD_INTERNAL", "always")], - ) - .add_build_internal("testlib", move |version| { + let config = create_config(path, env).add_build_internal(lib, move |version| { called_clone.replace(true); - assert_eq!(version, "1"); - let lib = pkg_config::Config::new() + let mut lib = pkg_config::Config::new() .print_system_libs(false) .cargo_metadata(false) - .probe("testlib") + .probe(lib) .unwrap(); + lib.version = version.to_string(); Ok(Library::from_pkg_config(lib)) }); - let (libraries, _flags) = config.probe_full().unwrap(); + match config.probe_full() { + Ok((libraries, _flags)) => Ok((libraries, called.get())), + Err(e) => Err((e, called.get())), + } +} - assert_eq!(called.get(), true); +#[test] +fn build_internal_always() { + let (libraries, called) = test_build_internal( + "toml-good", + vec![("SYSTEM_DEPS_TESTLIB_BUILD_INTERNAL", "always")], + "testlib", + ) + .unwrap(); + + assert_eq!(called, true); assert!(libraries.get("testlib").is_some()); } #[test] fn build_internal_auto_not_called() { // No need to build the lib as the existing version is new enough - let called = Rc::new(Cell::new(false)); - let called_clone = called.clone(); - let config = create_config( + let (libraries, called) = test_build_internal( "toml-good", vec![("SYSTEM_DEPS_TESTLIB_BUILD_INTERNAL", "auto")], + "testlib", ) - .add_build_internal("testlib", move |_version| { - called_clone.replace(true); - let lib = pkg_config::Config::new() - .print_system_libs(false) - .cargo_metadata(false) - .probe("testlib") - .unwrap(); - Ok(Library::from_pkg_config(lib)) - }); - - let (libraries, _flags) = config.probe_full().unwrap(); + .unwrap(); - assert_eq!(called.get(), false); + assert_eq!(called, false); assert!(libraries.get("testlib").is_some()); } #[test] fn build_internal_auto_called() { // Version 5 is not available so we should try building - let called = Rc::new(Cell::new(false)); - let called_clone = called.clone(); - let config = create_config( + let (libraries, called) = test_build_internal( "toml-feature-versions", vec![ ("SYSTEM_DEPS_TESTDATA_BUILD_INTERNAL", "auto"), ("CARGO_FEATURE_V5", ""), ], + "testdata", ) - .add_build_internal("testdata", move |version| { - called_clone.replace(true); - assert_eq!(version, "5"); - let mut lib = pkg_config::Config::new() - .print_system_libs(false) - .cargo_metadata(false) - .probe("testdata") - .unwrap(); - lib.version = "5.0".to_string(); - Ok(Library::from_pkg_config(lib)) - }); - - let (libraries, _flags) = config.probe_full().unwrap(); + .unwrap(); - assert_eq!(called.get(), true); + assert_eq!(called, true); assert!(libraries.get("testdata").is_some()); } #[test] fn build_internal_auto_never() { // Version 5 is not available but we forbid to build the lib - let called = Rc::new(Cell::new(false)); - let called_clone = called.clone(); - let config = create_config( + let (err, called) = test_build_internal( "toml-feature-versions", vec![ ("SYSTEM_DEPS_TESTDATA_BUILD_INTERNAL", "never"), ("CARGO_FEATURE_V5", ""), ], + "testdata", ) - .add_build_internal("testdata", move |version| { - called_clone.replace(true); - assert_eq!(version, "5"); - let lib = pkg_config::Config::new() - .print_system_libs(false) - .cargo_metadata(false) - .probe("testdata") - .unwrap(); - Ok(Library::from_pkg_config(lib)) - }); + .unwrap_err(); - let err = config.probe_full().unwrap_err(); assert!(matches!(err, Error::PkgConfig(..))); - - assert_eq!(called.get(), false); + assert_eq!(called, false); } #[test] From 1a6dcc7669f742941f62ea8f7a45e4fef73bbbe9 Mon Sep 17 00:00:00 2001 From: Guillaume Desmottes Date: Mon, 22 Jun 2020 15:57:39 +0200 Subject: [PATCH 40/55] pass lib name to build internal closure --- src/lib.rs | 19 +++++++++++-------- src/test.rs | 10 ++++++---- 2 files changed, 17 insertions(+), 12 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 632b146..29e572e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -77,9 +77,9 @@ //! ```should_panic //! fn main() { //! system_deps::Config::new() -//! .add_build_internal("testlib", |version| { +//! .add_build_internal("testlib", |lib, version| { //! // Actually build the library here -//! system_deps::Library::from_internal_pkg_config("build/path-to-pc-file", "testlib", version) +//! system_deps::Library::from_internal_pkg_config("build/path-to-pc-file", lib, version) //! }) //! .probe() //! .unwrap(); @@ -172,7 +172,8 @@ impl BuildInternalClosureError { } } -type FnBuildInternal = dyn FnOnce(&str) -> std::result::Result; +type FnBuildInternal = + dyn FnOnce(&str, &str) -> std::result::Result; /// Structure used to configure `metadata` before starting to probe for dependencies pub struct Config { @@ -220,10 +221,10 @@ impl Config { /// # Arguments /// * `name`: the name of the library, as defined in `Cargo.toml` /// * `func`: closure called when internally building the library. - /// It receives as argument the minimum library version required. + /// It receives as argument the library name and the minimum version required. pub fn add_build_internal(self, name: &str, func: F) -> Self where - F: 'static + FnOnce(&str) -> std::result::Result, + F: 'static + FnOnce(&str, &str) -> std::result::Result, { let mut build_internals = self.build_internals; build_internals.insert(name.to_string(), Box::new(func)); @@ -403,7 +404,9 @@ impl Config { fn call_build_internal(&mut self, name: &str, version: &str) -> Result { let lib = match self.build_internals.remove(name) { - Some(f) => f(version).map_err(|e| Error::BuildInternalClosureError(name.into(), e))?, + Some(f) => { + f(name, version).map_err(|e| Error::BuildInternalClosureError(name.into(), e))? + } None => return Err(Error::BuildInternalNoClosure(name.into(), version.into())), }; @@ -554,10 +557,10 @@ impl Library { /// /// ``` /// let mut config = system_deps::Config::new(); - /// config.add_build_internal("mylib", |version| { + /// config.add_build_internal("mylib", |lib, version| { /// // Actually build the library here /// system_deps::Library::from_internal_pkg_config("build-dir", - /// "mylib", version) + /// lib, version) /// }); /// ``` pub fn from_internal_pkg_config

( diff --git a/src/test.rs b/src/test.rs index 8cbf0f8..3bf19a1 100644 --- a/src/test.rs +++ b/src/test.rs @@ -357,7 +357,7 @@ fn test_build_internal( ) -> Result<(HashMap, bool), (Error, bool)> { let called = Rc::new(Cell::new(false)); let called_clone = called.clone(); - let config = create_config(path, env).add_build_internal(lib, move |version| { + let config = create_config(path, env).add_build_internal(lib, move |lib, version| { called_clone.replace(true); let mut lib = pkg_config::Config::new() .print_system_libs(false) @@ -469,12 +469,13 @@ fn build_internal_wrong_version() { ("CARGO_FEATURE_V5", ""), ], ) - .add_build_internal("testdata", move |_version| { + .add_build_internal("testdata", move |lib, _version| { called_clone.replace(true); + assert_eq!(lib, "testdata"); let lib = pkg_config::Config::new() .print_system_libs(false) .cargo_metadata(false) - .probe("testdata") + .probe(lib) .unwrap(); Ok(Library::from_pkg_config(lib)) }); @@ -492,8 +493,9 @@ fn build_internal_fail() { "toml-good", vec![("SYSTEM_DEPS_TESTLIB_BUILD_INTERNAL", "always")], ) - .add_build_internal("testlib", move |_version| { + .add_build_internal("testlib", move |lib, _version| { called_clone.replace(true); + assert_eq!(lib, "testlib"); Err(BuildInternalClosureError::failed("Something went wrong")) }); From 9487145c17ce89217065012be7d87d0783a03c43 Mon Sep 17 00:00:00 2001 From: Guillaume Desmottes Date: Mon, 22 Jun 2020 16:03:09 +0200 Subject: [PATCH 41/55] add SYSTEM_DEPS_BUILD_INTERNAL env variable Can be used to control the internal build of all deps. ix #2 --- src/lib.rs | 34 ++++++++++++++++------- src/test.rs | 77 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 101 insertions(+), 10 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 29e572e..816a1f9 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -91,6 +91,9 @@ //! - `auto`: build the dependency only if the required version has not been found by `pkg-config`; //! - `always`: always build the dependency, ignoring any version which may be installed on the system; //! - `never`: (default) never build the dependency, `system-deps` will fail if the required version is not found on the system. +//! +//! You can also use the `SYSTEM_DEPS_BUILD_INTERNAL` environment variable with the same values +//! defining the behavior for all the dependencies which don't have `SYSTEM_DEPS_$NAME_BUILD_INTERNAL` defined. #![deny(missing_docs)] @@ -387,19 +390,30 @@ impl Config { Ok(libraries) } + fn get_build_internal_env_var(&self, var: &str) -> Result, Error> { + match self.env.get(&var).as_deref() { + Some(s) => { + let b = BuildInternal::from_str(s).map_err(|_| { + Error::BuildInternalInvalid(format!( + "Invalid value in {}: {} (allowed: 'auto', 'always', 'never')", + var, s + )) + })?; + Ok(Some(b)) + } + None => Ok(None), + } + } + fn get_build_internal_status(&self, name: &str) -> Result { let var = flag_override_var(name, "BUILD_INTERNAL"); - let b = match self.env.get(&var).as_deref() { - Some(s) => BuildInternal::from_str(s).map_err(|_| { - Error::BuildInternalInvalid(format!( - "Invalid value in {}: {} (allowed: 'auto', 'always', 'never')", - var, s - )) - })?, - None => BuildInternal::default(), - }; - Ok(b) + match self.get_build_internal_env_var(&var)? { + Some(b) => Ok(b), + None => Ok(self + .get_build_internal_env_var("SYSTEM_DEPS_BUILD_INTERNAL")? + .unwrap_or_default()), + } } fn call_build_internal(&mut self, name: &str, version: &str) -> Result { diff --git a/src/test.rs b/src/test.rs index 3bf19a1..cf02758 100644 --- a/src/test.rs +++ b/src/test.rs @@ -503,3 +503,80 @@ fn build_internal_fail() { assert!(matches!(err, Error::BuildInternalClosureError(..))); assert_eq!(called.get(), true); } + +#[test] +fn build_internal_always_gobal() { + let called = Rc::new(Cell::new((false, false))); + let called_clone = called.clone(); + let called_clone2 = called.clone(); + let config = create_config("toml-good", vec![("SYSTEM_DEPS_BUILD_INTERNAL", "always")]) + .add_build_internal("testlib", move |lib, version| { + let (_, b) = called_clone.get(); + called_clone.replace((true, b)); + let mut lib = pkg_config::Config::new() + .print_system_libs(false) + .cargo_metadata(false) + .probe(lib) + .unwrap(); + lib.version = version.to_string(); + Ok(Library::from_pkg_config(lib)) + }) + .add_build_internal("testdata", move |lib, version| { + let (a, _) = called_clone2.get(); + called_clone2.replace((a, true)); + let mut lib = pkg_config::Config::new() + .print_system_libs(false) + .cargo_metadata(false) + .probe(lib) + .unwrap(); + lib.version = version.to_string(); + Ok(Library::from_pkg_config(lib)) + }); + + let (libraries, _flags) = config.probe_full().unwrap(); + assert_eq!(called.get(), (true, true)); + assert!(libraries.get("testlib").is_some()); + assert!(libraries.get("testdata").is_some()); +} + +#[test] +fn build_internal_gobal_override() { + // Request to build all libs using global var but disable it for a specific one + let called = Rc::new(Cell::new((false, false))); + let called_clone = called.clone(); + let called_clone2 = called.clone(); + let config = create_config( + "toml-good", + vec![ + ("SYSTEM_DEPS_BUILD_INTERNAL", "always"), + ("SYSTEM_DEPS_TESTLIB_BUILD_INTERNAL", "never"), + ], + ) + .add_build_internal("testlib", move |lib, version| { + let (_, b) = called_clone.get(); + called_clone.replace((true, b)); + let mut lib = pkg_config::Config::new() + .print_system_libs(false) + .cargo_metadata(false) + .probe(lib) + .unwrap(); + lib.version = version.to_string(); + Ok(Library::from_pkg_config(lib)) + }) + .add_build_internal("testdata", move |lib, version| { + let (a, _) = called_clone2.get(); + called_clone2.replace((a, true)); + let mut lib = pkg_config::Config::new() + .print_system_libs(false) + .cargo_metadata(false) + .probe(lib) + .unwrap(); + lib.version = version.to_string(); + Ok(Library::from_pkg_config(lib)) + }); + + let (libraries, _flags) = config.probe_full().unwrap(); + assert_eq!(called.get(), (false, true)); + assert!(libraries.get("testlib").is_some()); + assert!(libraries.get("testdata").is_some()); +} From a2908412f39aae18bea4bbeb17cb8bf6801623c5 Mon Sep 17 00:00:00 2001 From: Guillaume Desmottes Date: Mon, 22 Jun 2020 16:44:32 +0200 Subject: [PATCH 42/55] ignore needless_doctest_main clippy warnings --- src/lib.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/lib.rs b/src/lib.rs index 816a1f9..4728572 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,3 +1,4 @@ +#![allow(clippy::needless_doctest_main)] //!`system-deps` lets you write system dependencies in `Cargo.toml` metadata, //! rather than programmatically in `build.rs`. This makes those dependencies //! declarative, so other tools can read them as well. From 22e67780df907c9150f783e93eb595aef6301a02 Mon Sep 17 00:00:00 2001 From: Guillaume Desmottes Date: Mon, 22 Jun 2020 16:46:17 +0200 Subject: [PATCH 43/55] 1.3.0 --- Cargo.toml | 2 +- README.md | 2 +- src/lib.rs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 919f9d7..26e4675 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "system-deps" -version = "1.2.0" +version = "1.3.0" authors = ["Guillaume Desmottes ", "Josh Triplett "] license = "MIT/Apache-2.0" repository = "https://github.com/gdesmott/system-deps" diff --git a/README.md b/README.md index 7828931..719daba 100644 --- a/README.md +++ b/README.md @@ -24,7 +24,7 @@ In your `Cargo.toml`: ```toml [build-dependencies] -system-deps = "1.2" +system-deps = "1.3" ``` Then, to declare a dependency on `testlib >= 1.2` diff --git a/src/lib.rs b/src/lib.rs index 4728572..e8a0698 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -8,7 +8,7 @@ //! //! ```toml //! [build-dependencies] -//! system-deps = "1.2" +//! system-deps = "1.3" //! ``` //! //! Then, to declare a dependency on `testlib >= 1.2` From 5aab5dd01967d7c3029b905bedc8b5527e3e61eb Mon Sep 17 00:00:00 2001 From: Guillaume Desmottes Date: Mon, 22 Jun 2020 16:58:50 +0200 Subject: [PATCH 44/55] pass real lib name to build internal closure We need it when looking for the generated .pc file. --- src/lib.rs | 2 +- src/test.rs | 18 ++++++++++++++++-- 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index e8a0698..4b73519 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -366,7 +366,7 @@ impl Config { let library = if self.env.contains(&flag_override_var(name, "NO_PKG_CONFIG")) { Library::from_env_variables() } else if build_internal == BuildInternal::Always { - self.call_build_internal(name, version)? + self.call_build_internal(lib_name, version)? } else { match pkg_config::Config::new() .atleast_version(&version) diff --git a/src/test.rs b/src/test.rs index cf02758..4ff4423 100644 --- a/src/test.rs +++ b/src/test.rs @@ -353,12 +353,13 @@ fn override_no_pkg_config_error() { fn test_build_internal( path: &'static str, env: Vec<(&'static str, &'static str)>, - lib: &'static str, + expected_lib: &'static str, ) -> Result<(HashMap, bool), (Error, bool)> { let called = Rc::new(Cell::new(false)); let called_clone = called.clone(); - let config = create_config(path, env).add_build_internal(lib, move |lib, version| { + let config = create_config(path, env).add_build_internal(expected_lib, move |lib, version| { called_clone.replace(true); + assert_eq!(lib, expected_lib); let mut lib = pkg_config::Config::new() .print_system_libs(false) .cargo_metadata(false) @@ -580,3 +581,16 @@ fn build_internal_gobal_override() { assert!(libraries.get("testlib").is_some()); assert!(libraries.get("testdata").is_some()); } + +#[test] +fn build_internal_override_name() { + let (libraries, called) = test_build_internal( + "toml-override-name", + vec![("SYSTEM_DEPS_BUILD_INTERNAL", "always")], + "testlib-2.0", + ) + .unwrap(); + + assert_eq!(called, true); + assert!(libraries.get("testlib").is_some()); +} From 6d193fa9c687782443429914ec18650b66ea9a5e Mon Sep 17 00:00:00 2001 From: Guillaume Desmottes Date: Mon, 22 Jun 2020 17:10:37 +0200 Subject: [PATCH 45/55] 1.3.1 --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 26e4675..5f922d1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "system-deps" -version = "1.3.0" +version = "1.3.1" authors = ["Guillaume Desmottes ", "Josh Triplett "] license = "MIT/Apache-2.0" repository = "https://github.com/gdesmott/system-deps" From 44d0397549e23b4bef5666f4fd625a6709e6045f Mon Sep 17 00:00:00 2001 From: Antonio Ospite Date: Tue, 23 Jun 2020 09:44:46 +0200 Subject: [PATCH 46/55] README: fix a couple of typos --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 719daba..f211815 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ For now only `pkg-config` dependencies are supported but we are planning to [expand it](https://github.com/gdesmott/system-deps/issues/3) at some point. Users can override dependency flags using environment variables if needed. -`system-deps` also allow `-sys` crates to optionnally internally build and +`system-deps` also allows `-sys` crates to optionally internally build and static link the required system library. `system-deps` has been started as a fork of the @@ -43,4 +43,4 @@ fn main() { } ``` -See the [crate documentation](https://docs.rs/system-deps/) for more advanced features. \ No newline at end of file +See the [crate documentation](https://docs.rs/system-deps/) for more advanced features. From a22397fbd4f26e8efe4d42c1efe8c4475cee6259 Mon Sep 17 00:00:00 2001 From: Guillaume Desmottes Date: Tue, 23 Jun 2020 12:25:59 +0200 Subject: [PATCH 47/55] add environment variable enum Make code safer as we stop duplicating env variables strings around. Will also make it easier to exhaustively list all the variables supported. --- src/lib.rs | 116 ++++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 96 insertions(+), 20 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 4b73519..efac203 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -135,7 +135,7 @@ pub enum Error { /// Raised when dependency defined manually using `SYSTEM_DEPS_$NAME_NO_PKG_CONFIG` /// did not define at least one lib using `SYSTEM_DEPS_$NAME_LIB` or /// `SYSTEM_DEPS_$NAME_LIB_FRAMEWORK` - #[error("You should define at least one lib using {} or {}", flag_override_var(.0, "LIB"), flag_override_var(.0, "LIB_FRAMEWORK"))] + #[error("You should define at least one lib using {} or {}", EnvVariable::new_lib(.0).to_string(), EnvVariable::new_lib_framework(.0))] MissingLib(String), /// An environment variable in the form of `SYSTEM_DEPS_$NAME_BUILD_INTERNAL` /// contained an invalid value (allowed: `auto`, `always`, `never`) @@ -176,6 +176,77 @@ impl BuildInternalClosureError { } } +// enums representing the environment variables user can define to tune system-deps +enum EnvVariable { + Lib(String), + LibFramework(String), + SearchNative(String), + SearchFramework(String), + Include(String), + NoPkgConfig(String), + BuildInternal(Option), +} + +impl EnvVariable { + fn new_lib(lib: &str) -> Self { + Self::Lib(lib.to_string()) + } + + fn new_lib_framework(lib: &str) -> Self { + Self::LibFramework(lib.to_string()) + } + + fn new_search_native(lib: &str) -> Self { + Self::SearchNative(lib.to_string()) + } + + fn new_search_framework(lib: &str) -> Self { + Self::SearchFramework(lib.to_string()) + } + + fn new_include(lib: &str) -> Self { + Self::Include(lib.to_string()) + } + + fn new_no_pkg_config(lib: &str) -> Self { + Self::NoPkgConfig(lib.to_string()) + } + + fn new_build_internal(lib: Option<&str>) -> Self { + Self::BuildInternal(lib.map(|l| l.to_string())) + } + + fn suffix(&self) -> &'static str { + match self { + EnvVariable::Lib(_) => "LIB", + EnvVariable::LibFramework(_) => "LIB_FRAMEWORK", + EnvVariable::SearchNative(_) => "SEARCH_NATIVE", + EnvVariable::SearchFramework(_) => "SEARCH_FRAMEWORK", + EnvVariable::Include(_) => "INCLUDE", + EnvVariable::NoPkgConfig(_) => "NO_PKG_CONFIG", + EnvVariable::BuildInternal(_) => "BUILD_INTERNAL", + } + } +} + +impl fmt::Display for EnvVariable { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let suffix = match self { + EnvVariable::Lib(lib) + | EnvVariable::LibFramework(lib) + | EnvVariable::SearchNative(lib) + | EnvVariable::SearchFramework(lib) + | EnvVariable::Include(lib) + | EnvVariable::NoPkgConfig(lib) + | EnvVariable::BuildInternal(Some(lib)) => { + format!("{}_{}", lib.to_shouty_snake_case(), self.suffix()) + } + EnvVariable::BuildInternal(None) => self.suffix().to_string(), + }; + write!(f, "SYSTEM_DEPS_{}", suffix) + } +} + type FnBuildInternal = dyn FnOnce(&str, &str) -> std::result::Result; @@ -363,7 +434,7 @@ impl Config { let build_internal = self.get_build_internal_status(name)?; - let library = if self.env.contains(&flag_override_var(name, "NO_PKG_CONFIG")) { + let library = if self.env.contains(&EnvVariable::new_no_pkg_config(name)) { Library::from_env_variables() } else if build_internal == BuildInternal::Always { self.call_build_internal(lib_name, version)? @@ -391,7 +462,7 @@ impl Config { Ok(libraries) } - fn get_build_internal_env_var(&self, var: &str) -> Result, Error> { + fn get_build_internal_env_var(&self, var: EnvVariable) -> Result, Error> { match self.env.get(&var).as_deref() { Some(s) => { let b = BuildInternal::from_str(s).map_err(|_| { @@ -407,12 +478,10 @@ impl Config { } fn get_build_internal_status(&self, name: &str) -> Result { - let var = flag_override_var(name, "BUILD_INTERNAL"); - - match self.get_build_internal_env_var(&var)? { + match self.get_build_internal_env_var(EnvVariable::new_build_internal(Some(name)))? { Some(b) => Ok(b), None => Ok(self - .get_build_internal_env_var("SYSTEM_DEPS_BUILD_INTERNAL")? + .get_build_internal_env_var(EnvVariable::new_build_internal(None))? .unwrap_or_default()), } } @@ -438,19 +507,19 @@ impl Config { fn override_from_flags(&self, libraries: &mut HashMap) { for (name, lib) in libraries.iter_mut() { - if let Some(value) = self.env.get(&flag_override_var(name, "SEARCH_NATIVE")) { + if let Some(value) = self.env.get(&EnvVariable::new_search_native(name)) { lib.link_paths = split_paths(&value); } - if let Some(value) = self.env.get(&flag_override_var(name, "SEARCH_FRAMEWORK")) { + if let Some(value) = self.env.get(&EnvVariable::new_search_framework(name)) { lib.framework_paths = split_paths(&value); } - if let Some(value) = self.env.get(&flag_override_var(name, "LIB")) { + if let Some(value) = self.env.get(&EnvVariable::new_lib(name)) { lib.libs = split_string(&value); } - if let Some(value) = self.env.get(&flag_override_var(name, "LIB_FRAMEWORK")) { + if let Some(value) = self.env.get(&EnvVariable::new_lib_framework(name)) { lib.frameworks = split_string(&value); } - if let Some(value) = self.env.get(&flag_override_var(name, "INCLUDE")) { + if let Some(value) = self.env.get(&EnvVariable::new_include(name)) { lib.include_paths = split_paths(&value); } } @@ -496,8 +565,8 @@ impl Config { } fn has_feature(&self, feature: &str) -> bool { - let var = format!("CARGO_FEATURE_{}", feature.to_uppercase().replace('-', "_")); - self.env.contains(&var) + let var: &str = &format!("CARGO_FEATURE_{}", feature.to_uppercase().replace('-', "_")); + self.env.contains(var) } } @@ -620,11 +689,14 @@ enum EnvVariables { Mock(HashMap<&'static str, String>), } -impl EnvVariables { - fn contains(&self, var: &str) -> bool { +trait EnvVariablesExt { + fn contains(&self, var: T) -> bool { self.get(var).is_some() } + fn get(&self, var: T) -> Option; +} +impl EnvVariablesExt<&str> for EnvVariables { fn get(&self, var: &str) -> Option { match self { EnvVariables::Environnement => env::var(var).ok(), @@ -634,6 +706,14 @@ impl EnvVariables { } } +impl EnvVariablesExt<&EnvVariable> for EnvVariables { + fn get(&self, var: &EnvVariable) -> Option { + let s = var.to_string(); + let var: &str = s.as_ref(); + self.get(var) + } +} + // TODO: add support for "rustc-link-lib=static=" ? #[derive(Debug, PartialEq)] enum BuildFlag { @@ -678,10 +758,6 @@ impl fmt::Display for BuildFlags { } } -fn flag_override_var(lib: &str, flag: &str) -> String { - format!("SYSTEM_DEPS_{}_{}", lib.to_shouty_snake_case(), flag) -} - fn split_paths(value: &str) -> Vec { if !value.is_empty() { let paths = env::split_paths(&value); From 2a54c937eb77719fc4d435c3b51fab9bde0c3a70 Mon Sep 17 00:00:00 2001 From: Guillaume Desmottes Date: Tue, 23 Jun 2020 11:42:24 +0200 Subject: [PATCH 48/55] tests: sort flags before comparing The flags order isn't guaranteed as it depends of the hash table internal storage. --- Cargo.toml | 1 + src/test.rs | 48 ++++++++++++++++++++++++++++-------------------- 2 files changed, 29 insertions(+), 20 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 5f922d1..3d5ea61 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,3 +21,4 @@ thiserror = "1" [dev-dependencies] lazy_static = "1" +itertools = "0.9" diff --git a/src/test.rs b/src/test.rs index 4ff4423..6a067e4 100644 --- a/src/test.rs +++ b/src/test.rs @@ -1,3 +1,4 @@ +use itertools::Itertools; use pkg_config; use std::cell::Cell; use std::collections::HashMap; @@ -49,6 +50,13 @@ fn toml( create_config(path, env).probe_full() } +fn assert_flags(flags: BuildFlags, expected: &str) { + // flags ordering isn't guaranteed so sort them out before comparing + let flags = flags.to_string().split("\n").sorted().join("\n"); + let expected = expected.to_string().split("\n").sorted().join("\n"); + assert_eq!(flags, expected); +} + #[test] fn good() { let (libraries, flags) = toml("toml-good", vec![]).unwrap(); @@ -58,14 +66,14 @@ fn good() { assert_eq!(testdata.version, "4.5.6"); assert!(libraries.get("testmore").is_none()); - assert_eq!( - flags.to_string(), + assert_flags( + flags, r#"cargo:rustc-link-search=native=/usr/lib/x86_64-linux-gnu cargo:rustc-link-search=framework=/usr/lib/x86_64-linux-gnu cargo:rustc-link-lib=test cargo:rustc-link-lib=framework=someframework cargo:include=/usr/include/testlib -"# +"#, ); } @@ -197,15 +205,15 @@ fn override_search_native() { vec![Path::new("/custom/path"), Path::new("/other/path")] ); - assert_eq!( - flags.to_string(), + assert_flags( + flags, r#"cargo:rustc-link-search=native=/custom/path cargo:rustc-link-search=native=/other/path cargo:rustc-link-search=framework=/usr/lib/x86_64-linux-gnu cargo:rustc-link-lib=test cargo:rustc-link-lib=framework=someframework cargo:include=/usr/include/testlib -"# +"#, ); } @@ -219,14 +227,14 @@ fn override_search_framework() { let testlib = libraries.get("testlib").unwrap(); assert_eq!(testlib.framework_paths, vec![Path::new("/custom/path")]); - assert_eq!( - flags.to_string(), + assert_flags( + flags, r#"cargo:rustc-link-search=native=/usr/lib/x86_64-linux-gnu cargo:rustc-link-search=framework=/custom/path cargo:rustc-link-lib=test cargo:rustc-link-lib=framework=someframework cargo:include=/usr/include/testlib -"# +"#, ); } @@ -240,15 +248,15 @@ fn override_lib() { let testlib = libraries.get("testlib").unwrap(); assert_eq!(testlib.libs, vec!["overrided-test", "other-test"]); - assert_eq!( - flags.to_string(), + assert_flags( + flags, r#"cargo:rustc-link-search=native=/usr/lib/x86_64-linux-gnu cargo:rustc-link-search=framework=/usr/lib/x86_64-linux-gnu cargo:rustc-link-lib=overrided-test cargo:rustc-link-lib=other-test cargo:rustc-link-lib=framework=someframework cargo:include=/usr/include/testlib -"# +"#, ); } @@ -262,14 +270,14 @@ fn override_framework() { let testlib = libraries.get("testlib").unwrap(); assert_eq!(testlib.frameworks, vec!["overrided-framework"]); - assert_eq!( - flags.to_string(), + assert_flags( + flags, r#"cargo:rustc-link-search=native=/usr/lib/x86_64-linux-gnu cargo:rustc-link-search=framework=/usr/lib/x86_64-linux-gnu cargo:rustc-link-lib=test cargo:rustc-link-lib=framework=overrided-framework cargo:include=/usr/include/testlib -"# +"#, ); } @@ -283,14 +291,14 @@ fn override_include() { let testlib = libraries.get("testlib").unwrap(); assert_eq!(testlib.include_paths, vec![Path::new("/other/include")]); - assert_eq!( - flags.to_string(), + assert_flags( + flags, r#"cargo:rustc-link-search=native=/usr/lib/x86_64-linux-gnu cargo:rustc-link-search=framework=/usr/lib/x86_64-linux-gnu cargo:rustc-link-lib=test cargo:rustc-link-lib=framework=someframework cargo:include=/other/include -"# +"#, ); } @@ -314,7 +322,7 @@ fn override_unset() { assert_eq!(testlib.frameworks, Vec::::new()); assert_eq!(testlib.include_paths, Vec::::new()); - assert_eq!(flags.to_string(), ""); + assert_flags(flags, ""); } #[test] @@ -334,7 +342,7 @@ fn override_no_pkg_config() { assert_eq!(testlib.frameworks, Vec::::new()); assert_eq!(testlib.include_paths, Vec::::new()); - assert_eq!(flags.to_string(), "cargo:rustc-link-lib=custom-lib\n"); + assert_flags(flags, "cargo:rustc-link-lib=custom-lib\n"); } #[test] From c15d04a5b9caa635a225efc931a14bf731e4cd8f Mon Sep 17 00:00:00 2001 From: Guillaume Desmottes Date: Tue, 23 Jun 2020 11:50:55 +0200 Subject: [PATCH 49/55] trigger rebuild if system-deps env variables changed --- src/lib.rs | 26 ++++++++++- src/test.rs | 131 +++++++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 154 insertions(+), 3 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index efac203..5b465ab 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -113,7 +113,8 @@ use std::fs; use std::io::Read; use std::path::{Path, PathBuf}; use std::str::FromStr; -use strum_macros::EnumString; +use strum::IntoEnumIterator; +use strum_macros::{EnumIter, EnumString}; use thiserror::Error; use version_compare::VersionCompare; @@ -177,6 +178,7 @@ impl BuildInternalClosureError { } // enums representing the environment variables user can define to tune system-deps +#[derive(Debug, PartialEq, EnumIter)] enum EnvVariable { Lib(String), LibFramework(String), @@ -561,6 +563,26 @@ impl Config { } } + // Export cargo:rerun-if-env-changed instructions for all env variables affecting system-deps behaviour + flags.add(BuildFlag::RerunIfEnvChanged( + EnvVariable::new_build_internal(None), + )); + + for (name, _lib) in libraries.iter() { + for var in EnvVariable::iter() { + let var = match var { + EnvVariable::Lib(_) => EnvVariable::new_lib(name), + EnvVariable::LibFramework(_) => EnvVariable::new_lib_framework(name), + EnvVariable::SearchNative(_) => EnvVariable::new_search_native(name), + EnvVariable::SearchFramework(_) => EnvVariable::new_search_framework(name), + EnvVariable::Include(_) => EnvVariable::new_include(name), + EnvVariable::NoPkgConfig(_) => EnvVariable::new_no_pkg_config(name), + EnvVariable::BuildInternal(_) => EnvVariable::new_build_internal(Some(name)), + }; + flags.add(BuildFlag::RerunIfEnvChanged(var)); + } + } + Ok(flags) } @@ -722,6 +744,7 @@ enum BuildFlag { SearchFramework(String), Lib(String), LibFramework(String), + RerunIfEnvChanged(EnvVariable), } impl fmt::Display for BuildFlag { @@ -732,6 +755,7 @@ impl fmt::Display for BuildFlag { BuildFlag::SearchFramework(lib) => write!(f, "rustc-link-search=framework={}", lib), BuildFlag::Lib(lib) => write!(f, "rustc-link-lib={}", lib), BuildFlag::LibFramework(lib) => write!(f, "rustc-link-lib=framework={}", lib), + BuildFlag::RerunIfEnvChanged(env) => write!(f, "rerun-if-env-changed={}", env), } } } diff --git a/src/test.rs b/src/test.rs index 6a067e4..393b851 100644 --- a/src/test.rs +++ b/src/test.rs @@ -73,6 +73,21 @@ cargo:rustc-link-search=framework=/usr/lib/x86_64-linux-gnu cargo:rustc-link-lib=test cargo:rustc-link-lib=framework=someframework cargo:include=/usr/include/testlib +cargo:rerun-if-env-changed=SYSTEM_DEPS_TESTDATA_INCLUDE +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_NO_PKG_CONFIG +cargo:rerun-if-env-changed=SYSTEM_DEPS_TESTDATA_SEARCH_FRAMEWORK +cargo:rerun-if-env-changed=SYSTEM_DEPS_TESTDATA_SEARCH_NATIVE +cargo:rerun-if-env-changed=SYSTEM_DEPS_TESTLIB_INCLUDE +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_NO_PKG_CONFIG +cargo:rerun-if-env-changed=SYSTEM_DEPS_TESTLIB_SEARCH_FRAMEWORK +cargo:rerun-if-env-changed=SYSTEM_DEPS_TESTLIB_SEARCH_NATIVE +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 "#, ); } @@ -213,6 +228,21 @@ cargo:rustc-link-search=framework=/usr/lib/x86_64-linux-gnu cargo:rustc-link-lib=test cargo:rustc-link-lib=framework=someframework cargo:include=/usr/include/testlib +cargo:rerun-if-env-changed=SYSTEM_DEPS_TESTDATA_INCLUDE +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_NO_PKG_CONFIG +cargo:rerun-if-env-changed=SYSTEM_DEPS_TESTDATA_SEARCH_FRAMEWORK +cargo:rerun-if-env-changed=SYSTEM_DEPS_TESTDATA_SEARCH_NATIVE +cargo:rerun-if-env-changed=SYSTEM_DEPS_TESTLIB_INCLUDE +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_NO_PKG_CONFIG +cargo:rerun-if-env-changed=SYSTEM_DEPS_TESTLIB_SEARCH_FRAMEWORK +cargo:rerun-if-env-changed=SYSTEM_DEPS_TESTLIB_SEARCH_NATIVE +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 "#, ); } @@ -234,6 +264,21 @@ cargo:rustc-link-search=framework=/custom/path cargo:rustc-link-lib=test cargo:rustc-link-lib=framework=someframework cargo:include=/usr/include/testlib +cargo:rerun-if-env-changed=SYSTEM_DEPS_TESTDATA_INCLUDE +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_NO_PKG_CONFIG +cargo:rerun-if-env-changed=SYSTEM_DEPS_TESTDATA_SEARCH_FRAMEWORK +cargo:rerun-if-env-changed=SYSTEM_DEPS_TESTDATA_SEARCH_NATIVE +cargo:rerun-if-env-changed=SYSTEM_DEPS_TESTLIB_INCLUDE +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_NO_PKG_CONFIG +cargo:rerun-if-env-changed=SYSTEM_DEPS_TESTLIB_SEARCH_FRAMEWORK +cargo:rerun-if-env-changed=SYSTEM_DEPS_TESTLIB_SEARCH_NATIVE +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 "#, ); } @@ -256,6 +301,21 @@ cargo:rustc-link-lib=overrided-test cargo:rustc-link-lib=other-test cargo:rustc-link-lib=framework=someframework cargo:include=/usr/include/testlib +cargo:rerun-if-env-changed=SYSTEM_DEPS_TESTDATA_INCLUDE +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_NO_PKG_CONFIG +cargo:rerun-if-env-changed=SYSTEM_DEPS_TESTDATA_SEARCH_FRAMEWORK +cargo:rerun-if-env-changed=SYSTEM_DEPS_TESTDATA_SEARCH_NATIVE +cargo:rerun-if-env-changed=SYSTEM_DEPS_TESTLIB_INCLUDE +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_NO_PKG_CONFIG +cargo:rerun-if-env-changed=SYSTEM_DEPS_TESTLIB_SEARCH_FRAMEWORK +cargo:rerun-if-env-changed=SYSTEM_DEPS_TESTLIB_SEARCH_NATIVE +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 "#, ); } @@ -277,6 +337,21 @@ cargo:rustc-link-search=framework=/usr/lib/x86_64-linux-gnu cargo:rustc-link-lib=test cargo:rustc-link-lib=framework=overrided-framework cargo:include=/usr/include/testlib +cargo:rerun-if-env-changed=SYSTEM_DEPS_TESTDATA_INCLUDE +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_NO_PKG_CONFIG +cargo:rerun-if-env-changed=SYSTEM_DEPS_TESTDATA_SEARCH_FRAMEWORK +cargo:rerun-if-env-changed=SYSTEM_DEPS_TESTDATA_SEARCH_NATIVE +cargo:rerun-if-env-changed=SYSTEM_DEPS_TESTLIB_INCLUDE +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_NO_PKG_CONFIG +cargo:rerun-if-env-changed=SYSTEM_DEPS_TESTLIB_SEARCH_FRAMEWORK +cargo:rerun-if-env-changed=SYSTEM_DEPS_TESTLIB_SEARCH_NATIVE +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 "#, ); } @@ -298,6 +373,21 @@ cargo:rustc-link-search=framework=/usr/lib/x86_64-linux-gnu cargo:rustc-link-lib=test cargo:rustc-link-lib=framework=someframework cargo:include=/other/include +cargo:rerun-if-env-changed=SYSTEM_DEPS_TESTDATA_INCLUDE +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_NO_PKG_CONFIG +cargo:rerun-if-env-changed=SYSTEM_DEPS_TESTDATA_SEARCH_FRAMEWORK +cargo:rerun-if-env-changed=SYSTEM_DEPS_TESTDATA_SEARCH_NATIVE +cargo:rerun-if-env-changed=SYSTEM_DEPS_TESTLIB_INCLUDE +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_NO_PKG_CONFIG +cargo:rerun-if-env-changed=SYSTEM_DEPS_TESTLIB_SEARCH_FRAMEWORK +cargo:rerun-if-env-changed=SYSTEM_DEPS_TESTLIB_SEARCH_NATIVE +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 "#, ); } @@ -322,7 +412,25 @@ fn override_unset() { assert_eq!(testlib.frameworks, Vec::::new()); assert_eq!(testlib.include_paths, Vec::::new()); - assert_flags(flags, ""); + assert_flags( + flags, + r"cargo:rerun-if-env-changed=SYSTEM_DEPS_TESTDATA_INCLUDE +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_NO_PKG_CONFIG +cargo:rerun-if-env-changed=SYSTEM_DEPS_TESTDATA_SEARCH_FRAMEWORK +cargo:rerun-if-env-changed=SYSTEM_DEPS_TESTDATA_SEARCH_NATIVE +cargo:rerun-if-env-changed=SYSTEM_DEPS_TESTLIB_INCLUDE +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_NO_PKG_CONFIG +cargo:rerun-if-env-changed=SYSTEM_DEPS_TESTLIB_SEARCH_FRAMEWORK +cargo:rerun-if-env-changed=SYSTEM_DEPS_TESTLIB_SEARCH_NATIVE +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 +", + ); } #[test] @@ -342,7 +450,26 @@ fn override_no_pkg_config() { assert_eq!(testlib.frameworks, Vec::::new()); assert_eq!(testlib.include_paths, Vec::::new()); - assert_flags(flags, "cargo:rustc-link-lib=custom-lib\n"); + assert_flags( + flags, + r"cargo:rustc-link-lib=custom-lib +cargo:rerun-if-env-changed=SYSTEM_DEPS_TESTDATA_INCLUDE +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_NO_PKG_CONFIG +cargo:rerun-if-env-changed=SYSTEM_DEPS_TESTDATA_SEARCH_FRAMEWORK +cargo:rerun-if-env-changed=SYSTEM_DEPS_TESTDATA_SEARCH_NATIVE +cargo:rerun-if-env-changed=SYSTEM_DEPS_TESTLIB_INCLUDE +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_NO_PKG_CONFIG +cargo:rerun-if-env-changed=SYSTEM_DEPS_TESTLIB_SEARCH_FRAMEWORK +cargo:rerun-if-env-changed=SYSTEM_DEPS_TESTLIB_SEARCH_NATIVE +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 +", + ); } #[test] From 0cb92abd8b7f54956c7fc2e3f1992d45b7cdbf3c Mon Sep 17 00:00:00 2001 From: Guillaume Desmottes Date: Fri, 10 Jul 2020 09:52:02 +0200 Subject: [PATCH 50/55] add LICENSE files Fix #3 --- LICENSE-APACHE | 201 +++++++++++++++++++++++++++++++++++++++++++++++++ LICENSE-MIT | 23 ++++++ 2 files changed, 224 insertions(+) create mode 100644 LICENSE-APACHE create mode 100644 LICENSE-MIT diff --git a/LICENSE-APACHE b/LICENSE-APACHE new file mode 100644 index 0000000..16fe87b --- /dev/null +++ b/LICENSE-APACHE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/LICENSE-MIT b/LICENSE-MIT new file mode 100644 index 0000000..31aa793 --- /dev/null +++ b/LICENSE-MIT @@ -0,0 +1,23 @@ +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. From 4b22eca7afd508a899cb7105ede8d15d7561c5a3 Mon Sep 17 00:00:00 2001 From: Guillaume Desmottes Date: Fri, 10 Jul 2020 09:53:23 +0200 Subject: [PATCH 51/55] 1.3.2 --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 3d5ea61..5f3ab0d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "system-deps" -version = "1.3.1" +version = "1.3.2" authors = ["Guillaume Desmottes ", "Josh Triplett "] license = "MIT/Apache-2.0" repository = "https://github.com/gdesmott/system-deps" From c19b18128f2fe03fa3b67ee50e2244cb14f9b943 Mon Sep 17 00:00:00 2001 From: Guillaume Desmottes Date: Thu, 27 Aug 2020 10:12:53 +0200 Subject: [PATCH 52/55] update deps --- Cargo.toml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 5f3ab0d..eddce60 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,12 +11,12 @@ documentation = "https://docs.rs/system-deps/" readme = "README.md" [dependencies] -pkg-config = "0.3.8" +pkg-config = "0.3" toml = { version = "0.5", default-features = false } -version-compare = "0.0.10" +version-compare = "0.0.11" heck = "0.3" -strum = "0.18" -strum_macros = "0.18" +strum = "0.19" +strum_macros = "0.19" thiserror = "1" [dev-dependencies] From 64bfa041409fd1c5484c6ee3cddea9a1ebd88a5d Mon Sep 17 00:00:00 2001 From: Guillaume Desmottes Date: Thu, 29 Oct 2020 14:57:16 +0100 Subject: [PATCH 53/55] change version features syntax Replace feature-versions dict by a more generic syntax which will allow us to define more version-specific fields. --- src/lib.rs | 34 +++++++++++++++------- src/tests/toml-feature-versions/Cargo.toml | 2 +- 2 files changed, 25 insertions(+), 11 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 5b465ab..bb29666 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -46,6 +46,7 @@ //! `-sys` crates willing to support various versions of their underlying system libraries //! can use features to control the version of the dependency required. //! `system-deps` will pick the highest version among enabled features. +//! Such version features all have to start with "v". //! //! ```toml //! [features] @@ -53,8 +54,16 @@ //! v1_4 = ["v1_2"] //! v1_6 = ["v1_4"] //! -//! [package.metadata.system-deps] -//! gstreamer = { name = "gstreamer-1.0", version = "1.0", feature-versions = { v1_2 = "1.2", v1_4 = "1.4", v1_6 = "1.6" }} +//! [package.metadata.system-deps.gstreamer_1_0] +//! name = "gstreamer-1.0" +//! version = "1.0" +//! +//! [package.metadata.system-deps.gstreamer_1_0.v1_2] +//! version = "1.2" +//! [package.metadata.system-deps.gstreamer_1_0.v1_4] +//! version = "1.4" +//! [package.metadata.system-deps.gstreamer_1_0.v1_6] +//! version = "1.6" //! ``` //! //! # Overriding build flags @@ -369,20 +378,25 @@ impl Config { ("name", &toml::Value::String(ref s)) => { lib_name = Some(s); } - ("feature-versions", &toml::Value::Table(ref feature_versions)) => { - for (k, v) in feature_versions { + (version_feature, &toml::Value::Table(ref version_settings)) + if version_feature.starts_with("v") => + { + for (k, v) in version_settings { match (k.as_str(), v) { - (_, &toml::Value::String(ref feat_vers)) => { - if self.has_feature(&k) { + ("version", &toml::Value::String(ref feat_vers)) => { + if self.has_feature(&version_feature) { enabled_feature_versions.push(feat_vers); } } _ => { return Err(Error::InvalidMetadata(format!( - "Unexpected feature-version key: {} type {}", - k, - v.type_str() - ))) + "Unexpected version settings key: {}.{}.{}.{} type: {}", + key, + name, + tname, + k, + v.type_str() + ))) } } } diff --git a/src/tests/toml-feature-versions/Cargo.toml b/src/tests/toml-feature-versions/Cargo.toml index 8d10422..c4ade2e 100644 --- a/src/tests/toml-feature-versions/Cargo.toml +++ b/src/tests/toml-feature-versions/Cargo.toml @@ -1,2 +1,2 @@ [package.metadata.system-deps] -testdata = { version = "4", feature-versions = { v5 = "5", v6 = "6" }} +testdata = { version = "4", v5 = { version = "5" }, v6 = { version = "6" }} From 15a05907c90256865cad46d89e0b42ca480bcfa9 Mon Sep 17 00:00:00 2001 From: Guillaume Desmottes Date: Thu, 29 Oct 2020 16:09:55 +0100 Subject: [PATCH 54/55] generalize feature overrides Currently we can only override versions but I'm about to add names --- src/lib.rs | 48 ++++++++++++++++++++++++++++++++++++------------ 1 file changed, 36 insertions(+), 12 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index bb29666..4076fa1 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -198,6 +198,18 @@ enum EnvVariable { BuildInternal(Option), } +struct FeatureOverride { + version: String, +} + +impl FeatureOverride { + fn new(version: &str) -> Self { + Self { + version: version.to_string(), + } + } +} + impl EnvVariable { fn new_lib(lib: &str) -> Self { Self::Lib(lib.to_string()) @@ -361,12 +373,12 @@ impl Config { let mut libraries = HashMap::new(); for (name, value) in table { let (lib_name, version) = match value { - toml::Value::String(ref s) => (name, s), + toml::Value::String(ref s) => (name, s.to_string()), toml::Value::Table(ref t) => { let mut feature = None; let mut version = None; let mut lib_name = None; - let mut enabled_feature_versions = Vec::new(); + let mut enabled_feature_overrides = Vec::new(); for (tname, tvalue) in t { match (tname.as_str(), tvalue) { ("feature", &toml::Value::String(ref s)) => { @@ -381,12 +393,12 @@ impl Config { (version_feature, &toml::Value::Table(ref version_settings)) if version_feature.starts_with("v") => { + let mut override_version = None; + for (k, v) in version_settings { match (k.as_str(), v) { ("version", &toml::Value::String(ref feat_vers)) => { - if self.has_feature(&version_feature) { - enabled_feature_versions.push(feat_vers); - } + override_version = Some(feat_vers); } _ => { return Err(Error::InvalidMetadata(format!( @@ -400,6 +412,18 @@ impl Config { } } } + + let override_version = override_version.ok_or_else(|| { + Error::InvalidMetadata(format!( + "Missing version field for {}.{}.{}", + key, name, tvalue + )) + })?; + + if self.has_feature(&version_feature) { + let f_override = FeatureOverride::new(&override_version); + enabled_feature_overrides.push(f_override); + } } _ => { return Err(Error::InvalidMetadata(format!( @@ -420,16 +444,16 @@ impl Config { let version = { // Pick the highest feature enabled version - if !enabled_feature_versions.is_empty() { - enabled_feature_versions.sort_by(|a, b| { - VersionCompare::compare(b, a) + if !enabled_feature_overrides.is_empty() { + enabled_feature_overrides.sort_by(|a, b| { + VersionCompare::compare(&b.version, &a.version) .expect("failed to compare versions") .ord() .expect("invalid version") }); - Some(enabled_feature_versions[0]) + Some(enabled_feature_overrides[0].version.clone()) } else { - version + version.cloned() } }; @@ -453,7 +477,7 @@ impl Config { let library = if self.env.contains(&EnvVariable::new_no_pkg_config(name)) { Library::from_env_variables() } else if build_internal == BuildInternal::Always { - self.call_build_internal(lib_name, version)? + self.call_build_internal(lib_name, &version)? } else { match pkg_config::Config::new() .atleast_version(&version) @@ -465,7 +489,7 @@ impl Config { Err(e) => { if build_internal == BuildInternal::Auto { // Try building the lib internally as a fallback - self.call_build_internal(name, version)? + self.call_build_internal(name, &version)? } else { return Err(e.into()); } From 70c5825d9b31fdc2754d7562bbe03d8f11efb7f4 Mon Sep 17 00:00:00 2001 From: Guillaume Desmottes Date: Thu, 29 Oct 2020 15:16:57 +0100 Subject: [PATCH 55/55] allow to override libname using version features Fix #11 --- src/lib.rs | 38 +++++++++++++++++++------ src/test.rs | 9 ++++++ src/tests/toml-version-names/Cargo.toml | 2 ++ 3 files changed, 40 insertions(+), 9 deletions(-) create mode 100644 src/tests/toml-version-names/Cargo.toml diff --git a/src/lib.rs b/src/lib.rs index 4076fa1..6f7967d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -66,6 +66,18 @@ //! version = "1.6" //! ``` //! +//! The same mechanism can be used to require a different library name depending on the version: +//! +//! ```toml +//! [package.metadata.system-deps.gst_gl] +//! name = "gstreamer-gl-1.0" +//! version = "1.14" +//! +//! [package.metadata.system-deps.gst_gl.v1_18] +//! version = "1.18" +//! name = "gstreamer-gl-egl-1.0" +//! ``` +//! //! # 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: @@ -200,12 +212,14 @@ enum EnvVariable { struct FeatureOverride { version: String, + name: Option, } impl FeatureOverride { - fn new(version: &str) -> Self { + fn new(version: &str, name: Option<&String>) -> Self { Self { version: version.to_string(), + name: name.cloned(), } } } @@ -373,7 +387,7 @@ impl Config { let mut libraries = HashMap::new(); for (name, value) in table { let (lib_name, version) = match value { - toml::Value::String(ref s) => (name, s.to_string()), + toml::Value::String(ref s) => (name.to_string(), s.to_string()), toml::Value::Table(ref t) => { let mut feature = None; let mut version = None; @@ -394,12 +408,16 @@ impl Config { if version_feature.starts_with("v") => { let mut override_version = None; + let mut override_name = None; for (k, v) in version_settings { match (k.as_str(), v) { ("version", &toml::Value::String(ref feat_vers)) => { override_version = Some(feat_vers); } + ("name", &toml::Value::String(ref feat_name)) => { + override_name = Some(feat_name); + } _ => { return Err(Error::InvalidMetadata(format!( "Unexpected version settings key: {}.{}.{}.{} type: {}", @@ -421,7 +439,8 @@ impl Config { })?; if self.has_feature(&version_feature) { - let f_override = FeatureOverride::new(&override_version); + let f_override = + FeatureOverride::new(&override_version, override_name); enabled_feature_overrides.push(f_override); } } @@ -442,7 +461,7 @@ impl Config { } } - let version = { + let (version, lib_name) = { // Pick the highest feature enabled version if !enabled_feature_overrides.is_empty() { enabled_feature_overrides.sort_by(|a, b| { @@ -451,14 +470,15 @@ impl Config { .ord() .expect("invalid version") }); - Some(enabled_feature_overrides[0].version.clone()) + let o = &enabled_feature_overrides[0]; + (Some(o.version.clone()), o.name.clone()) } else { - version.cloned() + (version.cloned(), lib_name.cloned()) } }; ( - lib_name.unwrap_or(name), + lib_name.unwrap_or(name.to_string()), version.ok_or_else(|| { Error::InvalidMetadata(format!("No version in {}.{}", key, name)) })?, @@ -477,13 +497,13 @@ impl Config { let library = if self.env.contains(&EnvVariable::new_no_pkg_config(name)) { Library::from_env_variables() } else if build_internal == BuildInternal::Always { - self.call_build_internal(lib_name, &version)? + self.call_build_internal(&lib_name, &version)? } else { match pkg_config::Config::new() .atleast_version(&version) .print_system_libs(false) .cargo_metadata(false) - .probe(lib_name) + .probe(&lib_name) { Ok(lib) => Library::from_pkg_config(lib), Err(e) => { diff --git a/src/test.rs b/src/test.rs index 393b851..3c14187 100644 --- a/src/test.rs +++ b/src/test.rs @@ -202,6 +202,15 @@ fn feature_versions() { // We check the highest version enabled by features env::set_var("CARGO_FEATURE_V6", ""); toml_pkg_config_err_version("toml-feature-versions", "6", vec![("CARGO_FEATURE_V6", "")]); + + let (libraries, _) = toml("toml-version-names", vec![]).unwrap(); + let testlib = libraries.get("testlib").unwrap(); + assert_eq!(testlib.version, "1.2.3"); + + // Enable feature v2 + let (libraries, _) = toml("toml-version-names", vec![("CARGO_FEATURE_V2", "")]).unwrap(); + let testlib = libraries.get("testlib").unwrap(); + assert_eq!(testlib.version, "2.0.0"); } #[test] diff --git a/src/tests/toml-version-names/Cargo.toml b/src/tests/toml-version-names/Cargo.toml new file mode 100644 index 0000000..ec1b800 --- /dev/null +++ b/src/tests/toml-version-names/Cargo.toml @@ -0,0 +1,2 @@ +[package.metadata.system-deps] +testlib = { version = "1.2", v2 = { version = "2.0", name = "testlib-2.0"}} \ No newline at end of file