Skip to content

Commit

Permalink
Fix statically linked libraries that have dynamically linked dependen…
Browse files Browse the repository at this point in the history
…cies, by falling back to the dynamic library
  • Loading branch information
Philipp-M authored and gdesmott committed Sep 21, 2021
1 parent 4baf072 commit 5f43637
Show file tree
Hide file tree
Showing 5 changed files with 178 additions and 43 deletions.
77 changes: 70 additions & 7 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -309,7 +309,15 @@ impl Dependencies {

/// Returns a vector of [Library::libs] of each library, removing duplicates.
pub fn all_libs(&self) -> Vec<&str> {
self.aggregate_str(|l| &l.libs)
let mut v = self
.libs
.values()
.map(|l| l.libs.iter().map(|lib| lib.name.as_str()))
.flatten()
.collect::<Vec<_>>();
v.sort_unstable();
v.dedup();
v
}

/// Returns a vector of [Library::link_paths] of each library, removing duplicates.
Expand Down Expand Up @@ -359,7 +367,10 @@ impl Dependencies {
lib.framework_paths = split_paths(&value);
}
if let Some(value) = env.get(&EnvVariable::new_lib(name)) {
lib.libs = split_string(&value);
lib.libs = split_string(&value)
.into_iter()
.map(|l| InternalLib::new(l, false))
.collect();
}
if let Some(value) = env.get(&EnvVariable::new_lib_framework(name)) {
lib.frameworks = split_string(&value);
Expand Down Expand Up @@ -390,9 +401,12 @@ impl Dependencies {
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.statik)));
lib.libs.iter().for_each(|l| {
flags.add(BuildFlag::Lib(
l.name.clone(),
lib.statik && l.is_static_available,
))
});
lib.frameworks
.iter()
.for_each(|f| flags.add(BuildFlag::LibFramework(f.clone())));
Expand Down Expand Up @@ -813,6 +827,24 @@ pub enum Source {
EnvVariables,
}

#[derive(Debug, PartialEq)]
/// Internal library name and if a static library is available on the system
pub struct InternalLib {
/// Name of the library
pub name: String,
/// Indicates if a static library is available on the system
pub is_static_available: bool,
}

impl InternalLib {
fn new(name: String, is_static_available: bool) -> Self {
InternalLib {
name,
is_static_available,
}
}
}

#[derive(Debug)]
/// A system dependency
pub struct Library {
Expand All @@ -821,7 +853,7 @@ pub struct Library {
/// From where the library settings have been retrieved
pub source: Source,
/// libraries the linker should link on
pub libs: Vec<String>,
pub libs: Vec<InternalLib>,
/// directories where the compiler should look for libraries
pub link_paths: Vec<PathBuf>,
/// frameworks the linker should link on
Expand All @@ -840,10 +872,41 @@ pub struct Library {

impl Library {
fn from_pkg_config(name: &str, l: pkg_config::Library) -> Self {
// taken from: https://github.com/rust-lang/pkg-config-rs/blob/54325785816695df031cef3b26b6a9a203bbc01b/src/lib.rs#L502
let system_roots = if cfg!(target_os = "macos") {
vec![PathBuf::from("/Library"), PathBuf::from("/System")]
} else {
let sysroot = env::var_os("PKG_CONFIG_SYSROOT_DIR")
.or_else(|| env::var_os("SYSROOT"))
.map(PathBuf::from);

if cfg!(target_os = "windows") {
if let Some(sysroot) = sysroot {
vec![sysroot]
} else {
vec![]
}
} else {
vec![sysroot.unwrap_or_else(|| PathBuf::from("/usr"))]
}
};

let is_static_available = |name: &String| -> bool {
let libname = format!("lib{}.a", name);

l.link_paths.iter().any(|dir| {
!system_roots.iter().any(|sys| dir.starts_with(sys)) && dir.join(&libname).exists()
})
};

Self {
name: name.to_string(),
source: Source::PkgConfig,
libs: l.libs,
libs: l
.libs
.iter()
.map(|lib| InternalLib::new(lib.to_owned(), is_static_available(lib)))
.collect(),
link_paths: l.link_paths,
include_paths: l.include_paths,
frameworks: l.frameworks,
Expand Down
130 changes: 94 additions & 36 deletions src/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,9 @@ use assert_matches::assert_matches;

use crate::Dependencies;

use super::{BuildFlags, BuildInternalClosureError, Config, EnvVariables, Error, Library};
use super::{
BuildFlags, BuildInternalClosureError, Config, EnvVariables, Error, InternalLib, Library,
};

lazy_static! {
static ref LOCK: Mutex<()> = Mutex::new(());
Expand Down Expand Up @@ -347,7 +349,13 @@ fn override_lib() {
)
.unwrap();
let testlib = libraries.get_by_name("testlib").unwrap();
assert_eq!(testlib.libs, vec!["overrided-test", "other-test"]);
assert_eq!(
testlib.libs,
vec!["overrided-test", "other-test"]
.into_iter()
.map(|name| InternalLib::new(name.to_string(), false))
.collect::<Vec<InternalLib>>()
);

assert_flags(
flags,
Expand Down Expand Up @@ -473,7 +481,7 @@ fn override_unset() {
let testlib = libraries.get_by_name("testlib").unwrap();
assert_eq!(testlib.link_paths, Vec::<PathBuf>::new());
assert_eq!(testlib.framework_paths, Vec::<PathBuf>::new());
assert_eq!(testlib.libs, Vec::<String>::new());
assert_eq!(testlib.libs, Vec::<InternalLib>::new());
assert_eq!(testlib.frameworks, Vec::<String>::new());
assert_eq!(testlib.include_paths, Vec::<PathBuf>::new());

Expand Down Expand Up @@ -514,7 +522,10 @@ fn override_no_pkg_config() {
let testlib = libraries.get_by_name("testlib").unwrap();
assert_eq!(testlib.link_paths, Vec::<PathBuf>::new());
assert_eq!(testlib.framework_paths, Vec::<PathBuf>::new());
assert_eq!(testlib.libs, vec!["custom-lib"]);
assert_eq!(
testlib.libs,
vec![InternalLib::new("custom-lib".to_string(), false)]
);
assert_eq!(testlib.frameworks, Vec::<String>::new());
assert_eq!(testlib.include_paths, Vec::<PathBuf>::new());

Expand Down Expand Up @@ -890,79 +901,126 @@ fn invalid_cfg() {

#[test]
fn static_one_lib() {
let (libraries, flags) =
toml("toml-good", vec![("SYSTEM_DEPS_TESTLIB_LINK", "static")]).unwrap();
let (libraries, flags) = toml(
"toml-static",
vec![("SYSTEM_DEPS_TESTSTATICLIB_LINK", "static")],
)
.unwrap();

let testdata = libraries.get_by_name("testdata").unwrap();
assert!(!testdata.statik);

let testlib = libraries.get_by_name("testlib").unwrap();
let testlib = libraries.get_by_name("teststaticlib").unwrap();
assert!(testlib.statik);

assert_flags(
flags,
r#"cargo:rustc-link-search=native=/usr/lib/
cargo:rustc-link-search=framework=/usr/lib/
cargo:rustc-link-lib=static=test
format!(
r#"cargo:rustc-link-search=native=./src/tests/lib/
cargo:rustc-link-search=framework=./src/tests/lib/
cargo:rustc-link-lib=static=teststatic
cargo:rustc-link-lib=framework=someframework
cargo:include=/usr/include/testlib
cargo:rerun-if-env-changed=SYSTEM_DEPS_TESTDATA_INCLUDE
cargo:include=./src/tests/include/testlib
cargo:rerun-if-env-changed=SYSTEM_DEPS_BUILD_INTERNAL
cargo:rerun-if-env-changed=SYSTEM_DEPS_LINK
cargo:rerun-if-env-changed=SYSTEM_DEPS_TESTSTATICLIB_LIB
cargo:rerun-if-env-changed=SYSTEM_DEPS_TESTSTATICLIB_LIB_FRAMEWORK
cargo:rerun-if-env-changed=SYSTEM_DEPS_TESTSTATICLIB_SEARCH_NATIVE
cargo:rerun-if-env-changed=SYSTEM_DEPS_TESTSTATICLIB_SEARCH_FRAMEWORK
cargo:rerun-if-env-changed=SYSTEM_DEPS_TESTSTATICLIB_INCLUDE
cargo:rerun-if-env-changed=SYSTEM_DEPS_TESTSTATICLIB_NO_PKG_CONFIG
cargo:rerun-if-env-changed=SYSTEM_DEPS_TESTSTATICLIB_BUILD_INTERNAL
cargo:rerun-if-env-changed=SYSTEM_DEPS_TESTSTATICLIB_LINK
cargo:rerun-if-env-changed=SYSTEM_DEPS_TESTDATA_LIB
cargo:rerun-if-env-changed=SYSTEM_DEPS_TESTDATA_LIB_FRAMEWORK
cargo:rerun-if-env-changed=SYSTEM_DEPS_TESTDATA_LINK
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_LINK
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_TESTDATA_SEARCH_FRAMEWORK
cargo:rerun-if-env-changed=SYSTEM_DEPS_TESTDATA_INCLUDE
cargo:rerun-if-env-changed=SYSTEM_DEPS_TESTDATA_NO_PKG_CONFIG
cargo:rerun-if-env-changed=SYSTEM_DEPS_TESTDATA_BUILD_INTERNAL
cargo:rerun-if-env-changed=SYSTEM_DEPS_TESTDATA_LINK
"#,
)
.as_str(),
);
}

#[test]
fn static_all_libs() {
let (libraries, flags) = toml("toml-static", vec![("SYSTEM_DEPS_LINK", "static")]).unwrap();

let testdata = libraries.get_by_name("testdata").unwrap();
assert!(testdata.statik);

let testlib = libraries.get_by_name("teststaticlib").unwrap();
assert!(testlib.statik);

assert_flags(
flags,
r#"cargo:rustc-link-search=native=./src/tests/lib/
cargo:rustc-link-search=framework=./src/tests/lib/
cargo:rustc-link-lib=static=teststatic
cargo:rustc-link-lib=framework=someframework
cargo:include=./src/tests/include/testlib
cargo:rerun-if-env-changed=SYSTEM_DEPS_BUILD_INTERNAL
cargo:rerun-if-env-changed=SYSTEM_DEPS_LINK
cargo:rerun-if-env-changed=SYSTEM_DEPS_TESTLIB_BUILD_INTERNAL
cargo:rerun-if-env-changed=SYSTEM_DEPS_TESTSTATICLIB_LIB
cargo:rerun-if-env-changed=SYSTEM_DEPS_TESTSTATICLIB_LIB_FRAMEWORK
cargo:rerun-if-env-changed=SYSTEM_DEPS_TESTSTATICLIB_SEARCH_NATIVE
cargo:rerun-if-env-changed=SYSTEM_DEPS_TESTSTATICLIB_SEARCH_FRAMEWORK
cargo:rerun-if-env-changed=SYSTEM_DEPS_TESTSTATICLIB_INCLUDE
cargo:rerun-if-env-changed=SYSTEM_DEPS_TESTSTATICLIB_NO_PKG_CONFIG
cargo:rerun-if-env-changed=SYSTEM_DEPS_TESTSTATICLIB_BUILD_INTERNAL
cargo:rerun-if-env-changed=SYSTEM_DEPS_TESTSTATICLIB_LINK
cargo:rerun-if-env-changed=SYSTEM_DEPS_TESTDATA_LIB
cargo:rerun-if-env-changed=SYSTEM_DEPS_TESTDATA_LIB_FRAMEWORK
cargo:rerun-if-env-changed=SYSTEM_DEPS_TESTDATA_SEARCH_NATIVE
cargo:rerun-if-env-changed=SYSTEM_DEPS_TESTDATA_SEARCH_FRAMEWORK
cargo:rerun-if-env-changed=SYSTEM_DEPS_TESTDATA_INCLUDE
cargo:rerun-if-env-changed=SYSTEM_DEPS_TESTDATA_NO_PKG_CONFIG
cargo:rerun-if-env-changed=SYSTEM_DEPS_TESTDATA_BUILD_INTERNAL
cargo:rerun-if-env-changed=SYSTEM_DEPS_TESTDATA_LINK
"#,
);
}

#[test]
fn static_all_libs() {
fn static_lib_not_available() {
let (libraries, flags) = toml("toml-good", vec![("SYSTEM_DEPS_LINK", "static")]).unwrap();

let testdata = libraries.get_by_name("testdata").unwrap();
assert!(testdata.statik);

// testlib is not available as static library, which is why it is linked dynamically,
// as seen below
let testlib = libraries.get_by_name("testlib").unwrap();
assert!(testlib.statik);

assert_flags(
flags,
r#"cargo:rustc-link-search=native=/usr/lib/
cargo:rustc-link-search=framework=/usr/lib/
cargo:rustc-link-lib=static=test
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_BUILD_INTERNAL
cargo:rerun-if-env-changed=SYSTEM_DEPS_LINK
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_LINK
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_TESTDATA_SEARCH_FRAMEWORK
cargo:rerun-if-env-changed=SYSTEM_DEPS_TESTDATA_INCLUDE
cargo:rerun-if-env-changed=SYSTEM_DEPS_TESTDATA_NO_PKG_CONFIG
cargo:rerun-if-env-changed=SYSTEM_DEPS_TESTDATA_BUILD_INTERNAL
cargo:rerun-if-env-changed=SYSTEM_DEPS_TESTDATA_LINK
cargo:rerun-if-env-changed=SYSTEM_DEPS_TESTLIB_LIB
cargo:rerun-if-env-changed=SYSTEM_DEPS_TESTLIB_LIB_FRAMEWORK
cargo:rerun-if-env-changed=SYSTEM_DEPS_TESTLIB_LINK
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_LINK
cargo:rerun-if-env-changed=SYSTEM_DEPS_TESTLIB_SEARCH_FRAMEWORK
cargo:rerun-if-env-changed=SYSTEM_DEPS_TESTLIB_INCLUDE
cargo:rerun-if-env-changed=SYSTEM_DEPS_TESTLIB_NO_PKG_CONFIG
cargo:rerun-if-env-changed=SYSTEM_DEPS_TESTLIB_BUILD_INTERNAL
cargo:rerun-if-env-changed=SYSTEM_DEPS_TESTDATA_BUILD_INTERNAL
cargo:rerun-if-env-changed=SYSTEM_DEPS_TESTLIB_LINK
"#,
);
}
Empty file added src/tests/lib/libteststatic.a
Empty file.
10 changes: 10 additions & 0 deletions src/tests/teststaticlib.pc
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
prefix=./src/tests
exec_prefix=${prefix}
libdir=${exec_prefix}/lib/
includedir=${prefix}/include/testlib

Name: Test Library
Description: A fake library to test pkg-config.
Version: 1.2.3
Libs: -L${libdir} -lteststatic -F${libdir} -framework someframework
Cflags: -I${includedir} -DBADGER=yes -DAWESOME
4 changes: 4 additions & 0 deletions src/tests/toml-static/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
[package.metadata.system-deps]
testdata = "4"
teststaticlib = { version = "1", feature = "test-feature" }
testmore = { version = "2", feature = "another-test-feature" }

0 comments on commit 5f43637

Please sign in to comment.