1#![allow(clippy::needless_doctest_main)]
2#![allow(clippy::result_large_err)]
3#![deny(missing_docs)]
207
208#[cfg(test)]
209#[macro_use]
210extern crate lazy_static;
211
212#[cfg(test)]
213mod test;
214
215use heck::{ToShoutySnakeCase, ToSnakeCase};
216use std::collections::HashMap;
217use std::env;
218use std::fmt;
219use std::ops::RangeBounds;
220use std::path::{Path, PathBuf};
221use std::str::FromStr;
222
223mod metadata;
224use metadata::MetaData;
225
226#[derive(Debug)]
228pub enum Error {
229 PkgConfig(pkg_config::Error),
231 BuildInternalClosureError(String, BuildInternalClosureError),
233 FailToRead(String, std::io::Error),
235 InvalidMetadata(String),
237 MissingLib(String),
241 BuildInternalInvalid(String),
244 BuildInternalNoClosure(String, String),
249 BuildInternalWrongVersion(String, String, String),
252 UnsupportedCfg(String),
254}
255
256impl From<pkg_config::Error> for Error {
257 fn from(err: pkg_config::Error) -> Self {
258 Self::PkgConfig(err)
259 }
260}
261
262impl std::error::Error for Error {
263 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
264 match self {
265 Self::PkgConfig(e) => Some(e),
266 Self::BuildInternalClosureError(_, e) => Some(e),
267 Self::FailToRead(_, e) => Some(e),
268 _ => None,
269 }
270 }
271}
272
273impl fmt::Display for Error {
274 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
275 match self {
276 Self::PkgConfig(e) => write!(f, "{}", e),
277 Self::BuildInternalClosureError(s, e) => write!(f, "Failed to build {}: {}", s, e),
278 Self::FailToRead(s, _) => write!(f, "{}", s),
279 Self::InvalidMetadata(s) => write!(f, "{}", s),
280 Self::MissingLib(s) => write!(
281 f,
282 "You should define at least one lib using {} or {}",
283 EnvVariable::new_lib(s),
284 EnvVariable::new_lib_framework(s),
285 ),
286 Self::BuildInternalInvalid(s) => write!(f, "{}", s),
287 Self::BuildInternalNoClosure(s1, s2) => write!(
288 f,
289 "Missing build internal closure for {} (version {})",
290 s1, s2
291 ),
292 Self::BuildInternalWrongVersion(s1, s2, s3) => write!(
293 f,
294 "Internally built {} {} but minimum required version is {}",
295 s1, s2, s3
296 ),
297 Self::UnsupportedCfg(s) => write!(f, "Unsupported cfg() expression: {}", s),
298 }
299 }
300}
301
302#[derive(Debug, Default)]
303pub struct Dependencies {
305 libs: HashMap<String, Library>,
306}
307
308impl Dependencies {
309 pub fn get_by_name(&self, name: &str) -> Option<&Library> {
315 self.libs.get(name)
316 }
317
318 pub fn iter(&self) -> Vec<(&str, &Library)> {
322 let mut v = self
323 .libs
324 .iter()
325 .map(|(k, v)| (k.as_str(), v))
326 .collect::<Vec<_>>();
327 v.sort_by_key(|x| x.0);
328 v
329 }
330
331 fn aggregate_str<F: Fn(&Library) -> &Vec<String>>(&self, getter: F) -> Vec<&str> {
332 let mut v = self
333 .libs
334 .values()
335 .flat_map(getter)
336 .map(|s| s.as_str())
337 .collect::<Vec<_>>();
338 v.sort_unstable();
339 v.dedup();
340 v
341 }
342
343 fn aggregate_path_buf<F: Fn(&Library) -> &Vec<PathBuf>>(&self, getter: F) -> Vec<&PathBuf> {
344 let mut v = self.libs.values().flat_map(getter).collect::<Vec<_>>();
345 v.sort();
346 v.dedup();
347 v
348 }
349
350 pub fn all_libs(&self) -> Vec<&str> {
352 let mut v = self
353 .libs
354 .values()
355 .flat_map(|l| l.libs.iter().map(|lib| lib.name.as_str()))
356 .collect::<Vec<_>>();
357 v.sort_unstable();
358 v.dedup();
359 v
360 }
361
362 pub fn all_link_paths(&self) -> Vec<&PathBuf> {
364 self.aggregate_path_buf(|l| &l.link_paths)
365 }
366
367 pub fn all_frameworks(&self) -> Vec<&str> {
369 self.aggregate_str(|l| &l.frameworks)
370 }
371
372 pub fn all_framework_paths(&self) -> Vec<&PathBuf> {
374 self.aggregate_path_buf(|l| &l.framework_paths)
375 }
376
377 pub fn all_include_paths(&self) -> Vec<&PathBuf> {
379 self.aggregate_path_buf(|l| &l.include_paths)
380 }
381
382 pub fn all_linker_args(&self) -> Vec<&Vec<String>> {
384 let mut v = self
385 .libs
386 .values()
387 .flat_map(|l| &l.ld_args)
388 .collect::<Vec<_>>();
389 v.sort_unstable();
390 v.dedup();
391 v
392 }
393
394 pub fn all_defines(&self) -> Vec<(&str, &Option<String>)> {
396 let mut v = self
397 .libs
398 .values()
399 .flat_map(|l| l.defines.iter())
400 .map(|(k, v)| (k.as_str(), v))
401 .collect::<Vec<_>>();
402 v.sort();
403 v.dedup();
404 v
405 }
406
407 fn add(&mut self, name: &str, lib: Library) {
408 self.libs.insert(name.to_string(), lib);
409 }
410
411 fn override_from_flags(&mut self, env: &EnvVariables) {
412 for (name, lib) in self.libs.iter_mut() {
413 if let Some(value) = env.get(&EnvVariable::new_search_native(name)) {
414 lib.link_paths = split_paths(&value);
415 }
416 if let Some(value) = env.get(&EnvVariable::new_search_framework(name)) {
417 lib.framework_paths = split_paths(&value);
418 }
419 if let Some(value) = env.get(&EnvVariable::new_lib(name)) {
420 let should_be_linked_statically = env
421 .has_value(&EnvVariable::new_link(Some(name)), "static")
422 || env.has_value(&EnvVariable::new_link(None), "static");
423
424 let is_static_lib_available = should_be_linked_statically;
428
429 lib.libs = split_string(&value)
430 .into_iter()
431 .map(|l| InternalLib::new(l, is_static_lib_available))
432 .collect();
433 }
434 if let Some(value) = env.get(&EnvVariable::new_lib_framework(name)) {
435 lib.frameworks = split_string(&value);
436 }
437 if let Some(value) = env.get(&EnvVariable::new_include(name)) {
438 lib.include_paths = split_paths(&value);
439 }
440 if let Some(value) = env.get(&EnvVariable::new_linker_args(name)) {
441 lib.ld_args = split_string(&value)
442 .into_iter()
443 .map(|l| l.split(',').map(|l| l.to_string()).collect())
444 .collect();
445 }
446 }
447 }
448
449 fn gen_flags(&self) -> Result<BuildFlags, Error> {
450 let mut flags = BuildFlags::new();
451 let mut include_paths = Vec::new();
452
453 for (name, lib) in self.iter() {
454 include_paths.extend(lib.include_paths.clone());
455
456 if lib.source == Source::EnvVariables
457 && lib.libs.is_empty()
458 && lib.frameworks.is_empty()
459 {
460 return Err(Error::MissingLib(name.to_string()));
461 }
462
463 lib.link_paths
464 .iter()
465 .for_each(|l| flags.add(BuildFlag::SearchNative(l.to_string_lossy().to_string())));
466 lib.framework_paths.iter().for_each(|f| {
467 flags.add(BuildFlag::SearchFramework(f.to_string_lossy().to_string()))
468 });
469 lib.libs.iter().for_each(|l| {
470 flags.add(BuildFlag::Lib(
471 l.name.clone(),
472 lib.statik && l.is_static_available,
473 ))
474 });
475 lib.frameworks
476 .iter()
477 .for_each(|f| flags.add(BuildFlag::LibFramework(f.clone())));
478 lib.ld_args
479 .iter()
480 .for_each(|f| flags.add(BuildFlag::LinkArg(f.clone())))
481 }
482
483 if !include_paths.is_empty() {
486 if let Ok(paths) = std::env::join_paths(include_paths) {
487 flags.add(BuildFlag::Include(paths.to_string_lossy().to_string()));
488 }
489 }
490
491 flags.add(BuildFlag::RerunIfEnvChanged(
493 EnvVariable::new_build_internal(None),
494 ));
495 flags.add(BuildFlag::RerunIfEnvChanged(EnvVariable::new_link(None)));
496
497 for (name, _lib) in self.libs.iter() {
498 EnvVariable::set_rerun_if_changed_for_all_variants(&mut flags, name);
499 }
500
501 Ok(flags)
502 }
503}
504
505#[derive(Debug)]
506pub enum BuildInternalClosureError {
508 PkgConfig(pkg_config::Error),
510 Failed(String),
512}
513
514impl From<pkg_config::Error> for BuildInternalClosureError {
515 fn from(err: pkg_config::Error) -> Self {
516 Self::PkgConfig(err)
517 }
518}
519
520impl BuildInternalClosureError {
521 pub fn failed(details: &str) -> Self {
528 Self::Failed(details.to_string())
529 }
530}
531
532impl std::error::Error for BuildInternalClosureError {
533 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
534 match self {
535 Self::PkgConfig(e) => Some(e),
536 _ => None,
537 }
538 }
539}
540
541impl fmt::Display for BuildInternalClosureError {
542 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
543 match self {
544 Self::PkgConfig(e) => write!(f, "{}", e),
545 Self::Failed(s) => write!(f, "{}", s),
546 }
547 }
548}
549
550#[derive(Debug, PartialEq)]
552enum EnvVariable {
553 Lib(String),
554 LibFramework(String),
555 SearchNative(String),
556 SearchFramework(String),
557 Include(String),
558 NoPkgConfig(String),
559 BuildInternal(Option<String>),
560 Link(Option<String>),
561 LinkerArgs(String),
562}
563
564impl EnvVariable {
565 fn new_lib(lib: &str) -> Self {
566 Self::Lib(lib.to_string())
567 }
568
569 fn new_lib_framework(lib: &str) -> Self {
570 Self::LibFramework(lib.to_string())
571 }
572
573 fn new_search_native(lib: &str) -> Self {
574 Self::SearchNative(lib.to_string())
575 }
576
577 fn new_search_framework(lib: &str) -> Self {
578 Self::SearchFramework(lib.to_string())
579 }
580
581 fn new_include(lib: &str) -> Self {
582 Self::Include(lib.to_string())
583 }
584
585 fn new_linker_args(lib: &str) -> Self {
586 Self::LinkerArgs(lib.to_string())
587 }
588
589 fn new_no_pkg_config(lib: &str) -> Self {
590 Self::NoPkgConfig(lib.to_string())
591 }
592
593 fn new_build_internal(lib: Option<&str>) -> Self {
594 Self::BuildInternal(lib.map(|l| l.to_string()))
595 }
596
597 fn new_link(lib: Option<&str>) -> Self {
598 Self::Link(lib.map(|l| l.to_string()))
599 }
600
601 fn suffix(&self) -> &'static str {
602 match self {
603 EnvVariable::Lib(_) => "LIB",
604 EnvVariable::LibFramework(_) => "LIB_FRAMEWORK",
605 EnvVariable::SearchNative(_) => "SEARCH_NATIVE",
606 EnvVariable::SearchFramework(_) => "SEARCH_FRAMEWORK",
607 EnvVariable::Include(_) => "INCLUDE",
608 EnvVariable::NoPkgConfig(_) => "NO_PKG_CONFIG",
609 EnvVariable::BuildInternal(_) => "BUILD_INTERNAL",
610 EnvVariable::Link(_) => "LINK",
611 EnvVariable::LinkerArgs(_) => "LDFLAGS",
612 }
613 }
614
615 fn set_rerun_if_changed_for_all_variants(flags: &mut BuildFlags, name: &str) {
616 #[inline]
617 fn add_to_flags(flags: &mut BuildFlags, var: EnvVariable) {
618 flags.add(BuildFlag::RerunIfEnvChanged(var));
619 }
620 add_to_flags(flags, EnvVariable::new_lib(name));
621 add_to_flags(flags, EnvVariable::new_lib_framework(name));
622 add_to_flags(flags, EnvVariable::new_search_native(name));
623 add_to_flags(flags, EnvVariable::new_search_framework(name));
624 add_to_flags(flags, EnvVariable::new_include(name));
625 add_to_flags(flags, EnvVariable::new_linker_args(name));
626 add_to_flags(flags, EnvVariable::new_no_pkg_config(name));
627 add_to_flags(flags, EnvVariable::new_build_internal(Some(name)));
628 add_to_flags(flags, EnvVariable::new_link(Some(name)));
629 }
630}
631
632impl fmt::Display for EnvVariable {
633 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
634 let suffix = match self {
635 EnvVariable::Lib(lib)
636 | EnvVariable::LibFramework(lib)
637 | EnvVariable::SearchNative(lib)
638 | EnvVariable::SearchFramework(lib)
639 | EnvVariable::Include(lib)
640 | EnvVariable::LinkerArgs(lib)
641 | EnvVariable::NoPkgConfig(lib)
642 | EnvVariable::BuildInternal(Some(lib))
643 | EnvVariable::Link(Some(lib)) => {
644 format!("{}_{}", lib.to_shouty_snake_case(), self.suffix())
645 }
646 EnvVariable::BuildInternal(None) | EnvVariable::Link(None) => self.suffix().to_string(),
647 };
648 write!(f, "SYSTEM_DEPS_{}", suffix)
649 }
650}
651
652type FnBuildInternal =
653 dyn FnOnce(&str, &str) -> std::result::Result<Library, BuildInternalClosureError>;
654
655pub struct Config {
657 env: EnvVariables,
658 build_internals: HashMap<String, Box<FnBuildInternal>>,
659}
660
661impl Default for Config {
662 fn default() -> Self {
663 Self::new_with_env(EnvVariables::Environment)
664 }
665}
666
667impl Config {
668 pub fn new() -> Self {
670 Self::default()
671 }
672
673 fn new_with_env(env: EnvVariables) -> Self {
674 Self {
675 env,
676 build_internals: HashMap::new(),
677 }
678 }
679
680 pub fn probe(self) -> Result<Dependencies, Error> {
685 let libraries = self.probe_full()?;
686 let flags = libraries.gen_flags()?;
687
688 println!("{}", flags);
690
691 for (name, _) in libraries.iter() {
692 println!("cargo:rustc-cfg=system_deps_have_{}", name.to_snake_case());
693 }
694
695 Ok(libraries)
696 }
697
698 pub fn add_build_internal<F>(self, name: &str, func: F) -> Self
711 where
712 F: 'static + FnOnce(&str, &str) -> std::result::Result<Library, BuildInternalClosureError>,
713 {
714 let mut build_internals = self.build_internals;
715 build_internals.insert(name.to_string(), Box::new(func));
716
717 Self {
718 env: self.env,
719 build_internals,
720 }
721 }
722
723 fn probe_full(mut self) -> Result<Dependencies, Error> {
724 let mut libraries = self.probe_pkg_config()?;
725 libraries.override_from_flags(&self.env);
726
727 Ok(libraries)
728 }
729
730 fn probe_pkg_config(&mut self) -> Result<Dependencies, Error> {
731 let dir = self
732 .env
733 .get("CARGO_MANIFEST_DIR")
734 .ok_or_else(|| Error::InvalidMetadata("$CARGO_MANIFEST_DIR not set".into()))?;
735 let mut path = PathBuf::from(dir);
736 path.push("Cargo.toml");
737
738 println!("cargo:rerun-if-changed={}", &path.to_string_lossy());
739
740 let metadata = MetaData::from_file(&path)?;
741
742 let mut libraries = Dependencies::default();
743
744 for dep in metadata.deps.iter() {
745 if let Some(cfg) = &dep.cfg {
746 if !self.check_cfg(cfg)? {
748 continue;
749 }
750 }
751
752 let mut enabled_feature_overrides = Vec::new();
753
754 for o in dep.version_overrides.iter() {
755 if self.has_feature(&o.key) {
756 enabled_feature_overrides.push(o);
757 }
758 }
759
760 if let Some(feature) = dep.feature.as_ref() {
761 if !self.has_feature(feature) {
762 continue;
763 }
764 }
765
766 let version;
768 let lib_name;
769 let fallback_lib_names;
770 let optional;
771 if enabled_feature_overrides.is_empty() {
772 version = dep.version.as_deref();
773 lib_name = dep.lib_name();
774 fallback_lib_names = dep.fallback_names.as_deref().unwrap_or(&[]);
775 optional = dep.optional;
776 } else {
777 enabled_feature_overrides.sort_by(|a, b| {
778 fn min_version(r: metadata::VersionRange) -> &str {
779 match r.start_bound() {
780 std::ops::Bound::Unbounded => unreachable!(),
781 std::ops::Bound::Excluded(_) => unreachable!(),
782 std::ops::Bound::Included(b) => b,
783 }
784 }
785
786 let a = min_version(metadata::parse_version(&a.version));
787 let b = min_version(metadata::parse_version(&b.version));
788
789 version_compare::compare(a, b)
790 .expect("failed to compare versions")
791 .ord()
792 .expect("invalid version")
793 });
794 let highest = enabled_feature_overrides.into_iter().last().unwrap();
795
796 version = Some(highest.version.as_str());
797 lib_name = highest.name.as_deref().unwrap_or(dep.lib_name());
798 fallback_lib_names = highest
799 .fallback_names
800 .as_deref()
801 .or(dep.fallback_names.as_deref())
802 .unwrap_or(&[]);
803 optional = highest.optional.unwrap_or(dep.optional);
804 };
805
806 let version = version.ok_or_else(|| {
807 Error::InvalidMetadata(format!("No version defined for {}", dep.key))
808 })?;
809
810 let name = &dep.key;
811 let build_internal = self.get_build_internal_status(name)?;
812
813 let statik = self
815 .env
816 .has_value(&EnvVariable::new_link(Some(name)), "static")
817 || self.env.has_value(&EnvVariable::new_link(None), "static");
818
819 let mut library = if self.env.contains(&EnvVariable::new_no_pkg_config(name)) {
820 Library::from_env_variables(name)
821 } else if build_internal == BuildInternal::Always {
822 self.call_build_internal(lib_name, version)?
823 } else {
824 let mut config = pkg_config::Config::new();
825 config
826 .print_system_libs(false)
827 .cargo_metadata(false)
828 .range_version(metadata::parse_version(version))
829 .statik(statik);
830
831 match Self::probe_with_fallback(config, lib_name, fallback_lib_names) {
832 Ok((lib_name, lib)) => Library::from_pkg_config(lib_name, lib),
833 Err(e) => {
834 if build_internal == BuildInternal::Auto {
835 self.call_build_internal(name, version)?
837 } else if optional {
838 continue;
840 } else {
841 return Err(e.into());
842 }
843 }
844 }
845 };
846
847 library.statik = statik;
848
849 libraries.add(name, library);
850 }
851 Ok(libraries)
852 }
853
854 fn probe_with_fallback<'a>(
855 config: pkg_config::Config,
856 name: &'a str,
857 fallback_names: &'a [String],
858 ) -> Result<(&'a str, pkg_config::Library), pkg_config::Error> {
859 let error = match config.probe(name) {
860 Ok(x) => return Ok((name, x)),
861 Err(e) => e,
862 };
863 for name in fallback_names {
864 if let Ok(library) = config.probe(name) {
865 return Ok((name, library));
866 }
867 }
868 Err(error)
869 }
870
871 fn get_build_internal_env_var(&self, var: EnvVariable) -> Result<Option<BuildInternal>, Error> {
872 match self.env.get(&var).as_deref() {
873 Some(s) => {
874 let b = BuildInternal::from_str(s).map_err(|_| {
875 Error::BuildInternalInvalid(format!(
876 "Invalid value in {}: {} (allowed: 'auto', 'always', 'never')",
877 var, s
878 ))
879 })?;
880 Ok(Some(b))
881 }
882 None => Ok(None),
883 }
884 }
885
886 fn get_build_internal_status(&self, name: &str) -> Result<BuildInternal, Error> {
887 match self.get_build_internal_env_var(EnvVariable::new_build_internal(Some(name)))? {
888 Some(b) => Ok(b),
889 None => Ok(self
890 .get_build_internal_env_var(EnvVariable::new_build_internal(None))?
891 .unwrap_or_default()),
892 }
893 }
894
895 fn call_build_internal(&mut self, name: &str, version_str: &str) -> Result<Library, Error> {
896 let lib = match self.build_internals.remove(name) {
897 Some(f) => f(name, version_str)
898 .map_err(|e| Error::BuildInternalClosureError(name.into(), e))?,
899 None => {
900 return Err(Error::BuildInternalNoClosure(
901 name.into(),
902 version_str.into(),
903 ))
904 }
905 };
906
907 let version = metadata::parse_version(version_str);
909 fn min_version(r: metadata::VersionRange) -> &str {
910 match r.start_bound() {
911 std::ops::Bound::Unbounded => unreachable!(),
912 std::ops::Bound::Excluded(_) => unreachable!(),
913 std::ops::Bound::Included(b) => b,
914 }
915 }
916 fn max_version(r: metadata::VersionRange) -> Option<&str> {
917 match r.end_bound() {
918 std::ops::Bound::Included(_) => unreachable!(),
919 std::ops::Bound::Unbounded => None,
920 std::ops::Bound::Excluded(b) => Some(*b),
921 }
922 }
923
924 let min = min_version(version.clone());
925 if version_compare::compare(&lib.version, min) == Ok(version_compare::Cmp::Lt) {
926 return Err(Error::BuildInternalWrongVersion(
927 name.into(),
928 lib.version,
929 version_str.into(),
930 ));
931 }
932
933 if let Some(max) = max_version(version) {
934 if version_compare::compare(&lib.version, max) == Ok(version_compare::Cmp::Ge) {
935 return Err(Error::BuildInternalWrongVersion(
936 name.into(),
937 lib.version,
938 version_str.into(),
939 ));
940 }
941 }
942
943 Ok(lib)
944 }
945
946 fn has_feature(&self, feature: &str) -> bool {
947 let var: &str = &format!("CARGO_FEATURE_{}", feature.to_uppercase().replace('-', "_"));
948 self.env.contains(var)
949 }
950
951 fn check_cfg(&self, cfg: &cfg_expr::Expression) -> Result<bool, Error> {
952 use cfg_expr::{targets::get_builtin_target_by_triple, Predicate};
953
954 let target = self
955 .env
956 .get("TARGET")
957 .expect("no TARGET env variable defined");
958
959 let res = if let Some(target) = get_builtin_target_by_triple(&target) {
960 cfg.eval(|pred| match pred {
961 Predicate::Target(tp) => Some(tp.matches(target)),
962 _ => None,
963 })
964 } else {
965 let triple: cfg_expr::target_lexicon::Triple = target.parse().unwrap_or_else(|e| panic!("TARGET {} is not a builtin target, and it could not be parsed as a valid triplet: {}", target, e));
967
968 cfg.eval(|pred| match pred {
969 Predicate::Target(tp) => Some(tp.matches(&triple)),
970 _ => None,
971 })
972 };
973
974 res.ok_or_else(|| Error::UnsupportedCfg(cfg.original().to_string()))
975 }
976}
977
978#[derive(Debug, PartialEq, Eq)]
979pub enum Source {
981 PkgConfig,
983 EnvVariables,
985}
986
987#[derive(Debug, PartialEq, Eq)]
988pub struct InternalLib {
990 pub name: String,
992 pub is_static_available: bool,
994}
995
996impl InternalLib {
997 fn new(name: String, is_static_available: bool) -> Self {
998 InternalLib {
999 name,
1000 is_static_available,
1001 }
1002 }
1003}
1004
1005#[derive(Debug)]
1006pub struct Library {
1008 pub name: String,
1010 pub source: Source,
1012 pub libs: Vec<InternalLib>,
1014 pub link_paths: Vec<PathBuf>,
1016 pub frameworks: Vec<String>,
1018 pub framework_paths: Vec<PathBuf>,
1020 pub include_paths: Vec<PathBuf>,
1022 pub ld_args: Vec<Vec<String>>,
1024 pub defines: HashMap<String, Option<String>>,
1026 pub version: String,
1028 pub statik: bool,
1030}
1031
1032impl Library {
1033 fn from_pkg_config(name: &str, l: pkg_config::Library) -> Self {
1034 let system_roots = if cfg!(target_os = "macos") {
1036 vec![PathBuf::from("/Library"), PathBuf::from("/System")]
1037 } else {
1038 let sysroot = env::var_os("PKG_CONFIG_SYSROOT_DIR")
1039 .or_else(|| env::var_os("SYSROOT"))
1040 .map(PathBuf::from);
1041
1042 if cfg!(target_os = "windows") {
1043 if let Some(sysroot) = sysroot {
1044 vec![sysroot]
1045 } else {
1046 vec![]
1047 }
1048 } else {
1049 vec![sysroot.unwrap_or_else(|| PathBuf::from("/usr"))]
1050 }
1051 };
1052
1053 let is_static_available = |name: &String| -> bool {
1054 let libnames = {
1055 let mut names = vec![format!("lib{}.a", name)];
1056
1057 if cfg!(target_os = "windows") {
1058 names.push(format!("{}.lib", name));
1059 }
1060
1061 names
1062 };
1063
1064 l.link_paths.iter().any(|dir| {
1065 let library_exists = libnames.iter().any(|libname| dir.join(libname).exists());
1066 library_exists && !system_roots.iter().any(|sys| dir.starts_with(sys))
1067 })
1068 };
1069
1070 Self {
1071 name: name.to_string(),
1072 source: Source::PkgConfig,
1073 libs: l
1074 .libs
1075 .iter()
1076 .map(|lib| InternalLib::new(lib.to_owned(), is_static_available(lib)))
1077 .collect(),
1078 link_paths: l.link_paths,
1079 include_paths: l.include_paths,
1080 ld_args: l.ld_args,
1081 frameworks: l.frameworks,
1082 framework_paths: l.framework_paths,
1083 defines: l.defines,
1084 version: l.version,
1085 statik: false,
1086 }
1087 }
1088
1089 fn from_env_variables(name: &str) -> Self {
1090 Self {
1091 name: name.to_string(),
1092 source: Source::EnvVariables,
1093 libs: Vec::new(),
1094 link_paths: Vec::new(),
1095 include_paths: Vec::new(),
1096 ld_args: Vec::new(),
1097 frameworks: Vec::new(),
1098 framework_paths: Vec::new(),
1099 defines: HashMap::new(),
1100 version: String::new(),
1101 statik: false,
1102 }
1103 }
1104
1105 pub fn from_internal_pkg_config<P>(
1128 pkg_config_dir: P,
1129 lib: &str,
1130 version: &str,
1131 ) -> Result<Self, BuildInternalClosureError>
1132 where
1133 P: AsRef<Path>,
1134 {
1135 let old = env::var("PKG_CONFIG_PATH");
1137
1138 match old {
1139 Ok(ref s) => {
1140 let mut paths = env::split_paths(s).collect::<Vec<_>>();
1141 paths.push(PathBuf::from(pkg_config_dir.as_ref()));
1142 let paths = env::join_paths(paths).unwrap();
1143 env::set_var("PKG_CONFIG_PATH", paths)
1144 }
1145 Err(_) => env::set_var("PKG_CONFIG_PATH", pkg_config_dir.as_ref()),
1146 }
1147
1148 let pkg_lib = pkg_config::Config::new()
1149 .atleast_version(version)
1150 .print_system_libs(false)
1151 .cargo_metadata(false)
1152 .statik(true)
1153 .probe(lib);
1154
1155 env::set_var("PKG_CONFIG_PATH", old.unwrap_or_else(|_| "".into()));
1156
1157 match pkg_lib {
1158 Ok(pkg_lib) => {
1159 let mut lib = Self::from_pkg_config(lib, pkg_lib);
1160 lib.statik = true;
1161 Ok(lib)
1162 }
1163 Err(e) => Err(e.into()),
1164 }
1165 }
1166}
1167
1168#[derive(Debug)]
1169enum EnvVariables {
1170 Environment,
1171 #[cfg(test)]
1172 Mock(HashMap<&'static str, String>),
1173}
1174
1175trait EnvVariablesExt<T> {
1176 fn contains(&self, var: T) -> bool {
1177 self.get(var).is_some()
1178 }
1179
1180 fn get(&self, var: T) -> Option<String>;
1181
1182 fn has_value(&self, var: T, val: &str) -> bool {
1183 match self.get(var) {
1184 Some(v) => v == val,
1185 None => false,
1186 }
1187 }
1188}
1189
1190impl EnvVariablesExt<&str> for EnvVariables {
1191 fn get(&self, var: &str) -> Option<String> {
1192 match self {
1193 EnvVariables::Environment => env::var(var).ok(),
1194 #[cfg(test)]
1195 EnvVariables::Mock(vars) => vars.get(var).cloned(),
1196 }
1197 }
1198}
1199
1200impl EnvVariablesExt<&EnvVariable> for EnvVariables {
1201 fn get(&self, var: &EnvVariable) -> Option<String> {
1202 let s = var.to_string();
1203 let var: &str = s.as_ref();
1204 self.get(var)
1205 }
1206}
1207
1208#[derive(Debug, PartialEq)]
1209enum BuildFlag {
1210 Include(String),
1211 SearchNative(String),
1212 SearchFramework(String),
1213 Lib(String, bool), LibFramework(String),
1215 RerunIfEnvChanged(EnvVariable),
1216 LinkArg(Vec<String>),
1217}
1218
1219impl fmt::Display for BuildFlag {
1220 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1221 match self {
1222 BuildFlag::Include(paths) => write!(f, "include={}", paths),
1223 BuildFlag::SearchNative(lib) => write!(f, "rustc-link-search=native={}", lib),
1224 BuildFlag::SearchFramework(lib) => write!(f, "rustc-link-search=framework={}", lib),
1225 BuildFlag::Lib(lib, statik) => {
1226 if *statik {
1227 write!(f, "rustc-link-lib=static={}", lib)
1228 } else {
1229 write!(f, "rustc-link-lib={}", lib)
1230 }
1231 }
1232 BuildFlag::LibFramework(lib) => write!(f, "rustc-link-lib=framework={}", lib),
1233 BuildFlag::RerunIfEnvChanged(env) => write!(f, "rerun-if-env-changed={}", env),
1234 BuildFlag::LinkArg(ld_option) => {
1235 write!(f, "rustc-link-arg=-Wl,{}", ld_option.join(","))
1236 }
1237 }
1238 }
1239}
1240
1241#[derive(Debug, PartialEq)]
1242struct BuildFlags(Vec<BuildFlag>);
1243
1244impl BuildFlags {
1245 fn new() -> Self {
1246 Self(Vec::new())
1247 }
1248
1249 fn add(&mut self, flag: BuildFlag) {
1250 self.0.push(flag);
1251 }
1252}
1253
1254impl fmt::Display for BuildFlags {
1255 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1256 for flag in self.0.iter() {
1257 writeln!(f, "cargo:{}", flag)?;
1258 }
1259 Ok(())
1260 }
1261}
1262
1263fn split_paths(value: &str) -> Vec<PathBuf> {
1264 if !value.is_empty() {
1265 let paths = env::split_paths(&value);
1266 paths.map(|p| Path::new(&p).into()).collect()
1267 } else {
1268 Vec::new()
1269 }
1270}
1271
1272fn split_string(value: &str) -> Vec<String> {
1273 if !value.is_empty() {
1274 value.split(' ').map(|s| s.to_string()).collect()
1275 } else {
1276 Vec::new()
1277 }
1278}
1279
1280#[derive(Debug, PartialEq)]
1281enum BuildInternal {
1282 Auto,
1283 Always,
1284 Never,
1285}
1286
1287impl Default for BuildInternal {
1288 fn default() -> Self {
1289 Self::Never
1290 }
1291}
1292
1293impl FromStr for BuildInternal {
1294 type Err = ParseError;
1295
1296 fn from_str(s: &str) -> Result<Self, Self::Err> {
1297 match s {
1298 "auto" => Ok(Self::Auto),
1299 "always" => Ok(Self::Always),
1300 "never" => Ok(Self::Never),
1301 v => Err(ParseError::VariantNotFound(v.to_owned())),
1302 }
1303 }
1304}
1305
1306#[derive(Debug, PartialEq)]
1307enum ParseError {
1308 VariantNotFound(String),
1309}
1310
1311impl std::error::Error for ParseError {}
1312
1313impl fmt::Display for ParseError {
1314 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1315 match self {
1316 Self::VariantNotFound(v) => write!(f, "Unknown variant: `{}`", v),
1317 }
1318 }
1319}