1#![allow(clippy::needless_doctest_main)]
2#![allow(clippy::result_large_err)]
3#![deny(missing_docs)]
270
271#[cfg(test)]
272mod test;
273
274use heck::{ToShoutySnakeCase, ToSnakeCase};
275use std::{
276 borrow::Borrow,
277 collections::{BTreeMap, HashMap},
278 env,
279 ffi::OsString,
280 fmt, iter,
281 ops::RangeBounds,
282 path::{Path, PathBuf},
283 str::FromStr,
284};
285
286mod metadata;
287use metadata::MetaData;
288
289#[cfg(all(test, feature = "binary"))]
290mod test_binary;
291
292#[derive(Debug)]
294pub enum Error {
295 PkgConfig(pkg_config::Error),
297 BuildInternalClosureError(String, BuildInternalClosureError),
299 FailToRead(String, std::io::Error),
301 InvalidMetadata(String),
303 MissingLib(String),
307 BuildInternalInvalid(String),
310 BuildInternalNoClosure(String, String),
315 BuildInternalWrongVersion(String, String, String),
318 UnsupportedCfg(String),
320}
321
322impl From<pkg_config::Error> for Error {
323 fn from(err: pkg_config::Error) -> Self {
324 Self::PkgConfig(err)
325 }
326}
327
328impl std::error::Error for Error {
329 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
330 match self {
331 Self::PkgConfig(e) => Some(e),
332 Self::BuildInternalClosureError(_, e) => Some(e),
333 Self::FailToRead(_, e) => Some(e),
334 _ => None,
335 }
336 }
337}
338
339impl fmt::Display for Error {
340 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
341 match self {
342 Self::PkgConfig(e) => write!(f, "{e}"),
343 Self::BuildInternalClosureError(s, e) => write!(f, "Failed to build {s}: {e}"),
344 Self::FailToRead(s, _) => write!(f, "{s}"),
345 Self::InvalidMetadata(s) => write!(f, "{s}"),
346 Self::MissingLib(s) => write!(
347 f,
348 "You should define at least one lib using {} or {}",
349 EnvVariable::new_lib(s),
350 EnvVariable::new_lib_framework(s),
351 ),
352 Self::BuildInternalInvalid(s) => write!(f, "{s}"),
353 Self::BuildInternalNoClosure(s1, s2) => {
354 write!(f, "Missing build internal closure for {s1} (version {s2})")
355 }
356 Self::BuildInternalWrongVersion(s1, s2, s3) => write!(
357 f,
358 "Internally built {s1} {s2} but minimum required version is {s3}"
359 ),
360 Self::UnsupportedCfg(s) => write!(f, "Unsupported cfg() expression: {s}"),
361 }
362 }
363}
364
365#[derive(Debug, Default)]
366pub struct Dependencies {
368 libs: BTreeMap<String, Library>,
369}
370
371impl Dependencies {
372 pub fn get_by_name(&self, name: &str) -> Option<&Library> {
378 self.libs.get(name)
379 }
380
381 pub fn iter(&self) -> Vec<(&str, &Library)> {
385 let mut v = self
386 .libs
387 .iter()
388 .map(|(k, v)| (k.as_str(), v))
389 .collect::<Vec<_>>();
390 v.sort_by_key(|x| x.0);
391 v
392 }
393
394 fn aggregate_str<F: Fn(&Library) -> &Vec<String>>(&self, getter: F) -> Vec<&str> {
395 let mut v = self
396 .libs
397 .values()
398 .flat_map(getter)
399 .map(|s| s.as_str())
400 .collect::<Vec<_>>();
401 v.sort_unstable();
402 v.dedup();
403 v
404 }
405
406 fn aggregate_path_buf<F: Fn(&Library) -> &Vec<PathBuf>>(&self, getter: F) -> Vec<&PathBuf> {
407 let mut v = self.libs.values().flat_map(getter).collect::<Vec<_>>();
408 v.sort();
409 v.dedup();
410 v
411 }
412
413 pub fn all_libs(&self) -> Vec<&str> {
415 let mut v = self
416 .libs
417 .values()
418 .flat_map(|l| l.libs.iter().map(|lib| lib.name.as_str()))
419 .collect::<Vec<_>>();
420 v.sort_unstable();
421 v.dedup();
422 v
423 }
424
425 pub fn all_link_paths(&self) -> Vec<&PathBuf> {
427 self.aggregate_path_buf(|l| &l.link_paths)
428 }
429
430 pub fn all_frameworks(&self) -> Vec<&str> {
432 self.aggregate_str(|l| &l.frameworks)
433 }
434
435 pub fn all_framework_paths(&self) -> Vec<&PathBuf> {
437 self.aggregate_path_buf(|l| &l.framework_paths)
438 }
439
440 pub fn all_include_paths(&self) -> Vec<&PathBuf> {
442 self.aggregate_path_buf(|l| &l.include_paths)
443 }
444
445 pub fn all_linker_args(&self) -> Vec<&Vec<String>> {
447 let mut v = self
448 .libs
449 .values()
450 .flat_map(|l| &l.ld_args)
451 .collect::<Vec<_>>();
452 v.sort_unstable();
453 v.dedup();
454 v
455 }
456
457 pub fn all_defines(&self) -> Vec<(&str, &Option<String>)> {
459 let mut v = self
460 .libs
461 .values()
462 .flat_map(|l| l.defines.iter())
463 .map(|(k, v)| (k.as_str(), v))
464 .collect::<Vec<_>>();
465 v.sort();
466 v.dedup();
467 v
468 }
469
470 fn add(&mut self, name: &str, lib: Library) {
471 self.libs.insert(name.to_string(), lib);
472 }
473
474 fn override_from_flags(&mut self, env: &EnvVariables) {
475 for (name, lib) in self.libs.iter_mut() {
476 if let Some(value) = env.get(&EnvVariable::new_search_native(name)) {
477 lib.link_paths = split_paths(&value);
478 }
479 if let Some(value) = env.get(&EnvVariable::new_search_framework(name)) {
480 lib.framework_paths = split_paths(&value);
481 }
482 if let Some(value) = env.get(&EnvVariable::new_lib(name)) {
483 let should_be_linked_statically = env
484 .has_value(&EnvVariable::new_link(Some(name)), "static")
485 || env.has_value(&EnvVariable::new_link(None), "static");
486
487 let is_static_lib_available = should_be_linked_statically;
491
492 lib.libs = split_string(&value)
493 .into_iter()
494 .map(|l| InternalLib::new(l, is_static_lib_available))
495 .collect();
496 }
497 if let Some(value) = env.get(&EnvVariable::new_lib_framework(name)) {
498 lib.frameworks = split_string(&value);
499 }
500 if let Some(value) = env.get(&EnvVariable::new_include(name)) {
501 lib.include_paths = split_paths(&value);
502 }
503 if let Some(value) = env.get(&EnvVariable::new_linker_args(name)) {
504 lib.ld_args = split_string(&value)
505 .into_iter()
506 .map(|l| l.split(',').map(|l| l.to_string()).collect())
507 .collect();
508 }
509 }
510 }
511
512 fn gen_flags(&self, target: Option<&str>) -> Result<BuildFlags, Error> {
520 let mut flags = BuildFlags::new();
521 let mut include_paths = Vec::new();
522
523 for (name, lib) in self.iter() {
524 include_paths.extend(lib.include_paths.clone());
525
526 if lib.source == Source::EnvVariables
527 && lib.libs.is_empty()
528 && lib.frameworks.is_empty()
529 {
530 return Err(Error::MissingLib(name.to_string()));
531 }
532
533 lib.link_paths
534 .iter()
535 .for_each(|l| flags.add(BuildFlag::SearchNative(l.to_string_lossy().to_string())));
536 lib.framework_paths.iter().for_each(|f| {
537 flags.add(BuildFlag::SearchFramework(f.to_string_lossy().to_string()))
538 });
539 lib.libs.iter().for_each(|l| {
540 flags.add(BuildFlag::Lib(
541 l.name.clone(),
542 lib.statik && l.is_static_available,
543 ))
544 });
545
546 if let Some(target) = target {
550 let mut seen_dirs = Vec::new();
551
552 for path in &lib.link_files {
553 if let (Some(dir), Some(file_name)) = (path.parent(), path.file_name()) {
554 let filename = file_name.to_string_lossy();
555 let mut new = false;
556
557 if !seen_dirs.contains(&dir) {
558 new = true;
559 seen_dirs.push(dir);
560 }
561
562 if let Some(lib_name) =
565 pkg_config::Library::extract_lib_from_filename(target, &filename)
566 {
567 if new {
568 flags.add(BuildFlag::SearchNative(
569 dir.to_string_lossy().to_string(),
570 ));
571 }
572
573 flags.add(BuildFlag::Lib(lib_name.to_string(), lib.statik));
574 }
575 }
576 }
577 }
578
579 lib.frameworks
580 .iter()
581 .for_each(|f| flags.add(BuildFlag::LibFramework(f.clone())));
582 lib.ld_args
583 .iter()
584 .for_each(|f| flags.add(BuildFlag::LinkArg(f.clone())))
585 }
586
587 if !include_paths.is_empty() {
590 if let Ok(paths) = std::env::join_paths(include_paths) {
591 flags.add(BuildFlag::Include(paths.to_string_lossy().to_string()));
592 }
593 }
594
595 flags.add(BuildFlag::RerunIfEnvChanged(
597 EnvVariable::new_build_internal(None),
598 ));
599 flags.add(BuildFlag::RerunIfEnvChanged(EnvVariable::new_link(None)));
600
601 for name in self.libs.keys() {
602 EnvVariable::set_rerun_if_changed_for_all_variants(&mut flags, name);
603 }
604
605 Ok(flags)
606 }
607}
608
609#[derive(Debug)]
610pub enum BuildInternalClosureError {
612 PkgConfig(pkg_config::Error),
614 Failed(String),
616}
617
618impl From<pkg_config::Error> for BuildInternalClosureError {
619 fn from(err: pkg_config::Error) -> Self {
620 Self::PkgConfig(err)
621 }
622}
623
624impl BuildInternalClosureError {
625 pub fn failed(details: &str) -> Self {
632 Self::Failed(details.to_string())
633 }
634}
635
636impl std::error::Error for BuildInternalClosureError {
637 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
638 match self {
639 Self::PkgConfig(e) => Some(e),
640 _ => None,
641 }
642 }
643}
644
645impl fmt::Display for BuildInternalClosureError {
646 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
647 match self {
648 Self::PkgConfig(e) => write!(f, "{e}"),
649 Self::Failed(s) => write!(f, "{s}"),
650 }
651 }
652}
653
654#[derive(Debug, PartialEq)]
656enum EnvVariable {
657 Lib(String),
658 LibFramework(String),
659 SearchNative(String),
660 SearchFramework(String),
661 Include(String),
662 NoPkgConfig(String),
663 BuildInternal(Option<String>),
664 Link(Option<String>),
665 LinkerArgs(String),
666 NoPrebuilt(Option<String>),
667}
668
669impl EnvVariable {
670 fn new_lib(lib: &str) -> Self {
671 Self::Lib(lib.to_string())
672 }
673
674 fn new_lib_framework(lib: &str) -> Self {
675 Self::LibFramework(lib.to_string())
676 }
677
678 fn new_search_native(lib: &str) -> Self {
679 Self::SearchNative(lib.to_string())
680 }
681
682 fn new_search_framework(lib: &str) -> Self {
683 Self::SearchFramework(lib.to_string())
684 }
685
686 fn new_include(lib: &str) -> Self {
687 Self::Include(lib.to_string())
688 }
689
690 fn new_linker_args(lib: &str) -> Self {
691 Self::LinkerArgs(lib.to_string())
692 }
693
694 fn new_no_pkg_config(lib: &str) -> Self {
695 Self::NoPkgConfig(lib.to_string())
696 }
697
698 fn new_build_internal(lib: Option<&str>) -> Self {
699 Self::BuildInternal(lib.map(|l| l.to_string()))
700 }
701
702 fn new_link(lib: Option<&str>) -> Self {
703 Self::Link(lib.map(|l| l.to_string()))
704 }
705
706 fn new_no_prebuilt(lib: Option<&str>) -> Self {
707 Self::NoPrebuilt(lib.map(|l| l.to_string()))
708 }
709
710 const fn suffix(&self) -> &'static str {
711 match self {
712 EnvVariable::Lib(_) => "LIB",
713 EnvVariable::LibFramework(_) => "LIB_FRAMEWORK",
714 EnvVariable::SearchNative(_) => "SEARCH_NATIVE",
715 EnvVariable::SearchFramework(_) => "SEARCH_FRAMEWORK",
716 EnvVariable::Include(_) => "INCLUDE",
717 EnvVariable::NoPkgConfig(_) => "NO_PKG_CONFIG",
718 EnvVariable::BuildInternal(_) => "BUILD_INTERNAL",
719 EnvVariable::Link(_) => "LINK",
720 EnvVariable::LinkerArgs(_) => "LDFLAGS",
721 EnvVariable::NoPrebuilt(_) => "NO_PREBUILT",
722 }
723 }
724
725 fn set_rerun_if_changed_for_all_variants(flags: &mut BuildFlags, name: &str) {
726 #[inline]
727 fn add_to_flags(flags: &mut BuildFlags, var: EnvVariable) {
728 flags.add(BuildFlag::RerunIfEnvChanged(var));
729 }
730 add_to_flags(flags, EnvVariable::new_lib(name));
731 add_to_flags(flags, EnvVariable::new_lib_framework(name));
732 add_to_flags(flags, EnvVariable::new_search_native(name));
733 add_to_flags(flags, EnvVariable::new_search_framework(name));
734 add_to_flags(flags, EnvVariable::new_include(name));
735 add_to_flags(flags, EnvVariable::new_linker_args(name));
736 add_to_flags(flags, EnvVariable::new_no_pkg_config(name));
737 add_to_flags(flags, EnvVariable::new_build_internal(Some(name)));
738 add_to_flags(flags, EnvVariable::new_link(Some(name)));
739 add_to_flags(flags, EnvVariable::new_no_prebuilt(Some(name)));
740 }
741}
742
743impl fmt::Display for EnvVariable {
744 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
745 let suffix = match self {
746 EnvVariable::Lib(lib)
747 | EnvVariable::LibFramework(lib)
748 | EnvVariable::SearchNative(lib)
749 | EnvVariable::SearchFramework(lib)
750 | EnvVariable::Include(lib)
751 | EnvVariable::LinkerArgs(lib)
752 | EnvVariable::NoPkgConfig(lib)
753 | EnvVariable::BuildInternal(Some(lib))
754 | EnvVariable::Link(Some(lib))
755 | EnvVariable::NoPrebuilt(Some(lib)) => {
756 format!("{}_{}", lib.to_shouty_snake_case(), self.suffix())
757 }
758 EnvVariable::BuildInternal(None)
759 | EnvVariable::Link(None)
760 | EnvVariable::NoPrebuilt(None) => self.suffix().to_string(),
761 };
762 write!(f, "SYSTEM_DEPS_{suffix}")
763 }
764}
765
766type FnBuildInternal =
767 dyn FnOnce(&str, &str) -> std::result::Result<Library, BuildInternalClosureError>;
768
769pub struct Config {
771 env: EnvVariables,
772 build_internals: HashMap<String, Box<FnBuildInternal>>,
773 #[cfg(feature = "binary")]
774 paths: &'static system_deps_meta::binary::Paths,
775}
776
777impl Default for Config {
778 fn default() -> Self {
779 Self::new_with_env(EnvVariables::Environment)
780 }
781}
782
783impl Config {
784 pub fn new() -> Self {
786 Self::default()
787 }
788
789 fn new_with_env(env: EnvVariables) -> Self {
790 let me = Self {
791 env,
792 build_internals: HashMap::new(),
793 #[cfg(feature = "binary")]
794 paths: {
795 const CONTENT: &str = include_str!(env!("SYSTEM_DEPS_BINARY_PATHS"));
797 static PATHS: std::sync::OnceLock<system_deps_meta::binary::Paths> =
798 std::sync::OnceLock::new();
799 PATHS.get_or_init(|| {
800 toml::from_str(CONTENT)
801 .expect("The build script should output valid serialization")
802 })
803 },
804 };
805
806 #[cfg(feature = "binary")]
814 if env::var_os("PKG_CONFIG").is_none() {
815 if let Some(pc) = me.paths.pkg_config_binary() {
816 env::set_var("PKG_CONFIG", pc);
817 }
818 }
819
820 me
821 }
822
823 pub fn probe(self) -> Result<Dependencies, Error> {
828 let target = self.env.get("TARGET");
829 let libraries = self.probe_full()?;
830 let flags = libraries.gen_flags(target.as_deref())?;
831
832 println!("{flags}");
834
835 for (name, _) in libraries.iter() {
836 println!("cargo:rustc-cfg=system_deps_have_{}", name.to_snake_case());
837 }
838
839 Ok(libraries)
840 }
841
842 pub fn add_build_internal<F>(mut self, name: &str, func: F) -> Self
855 where
856 F: 'static + FnOnce(&str, &str) -> std::result::Result<Library, BuildInternalClosureError>,
857 {
858 let mut build_internals = self.build_internals;
859 build_internals.insert(name.to_string(), Box::new(func));
860
861 self.build_internals = build_internals;
862 self
863 }
864
865 pub fn query_path(&self, _pkg: &str) -> Option<&'static Vec<PathBuf>> {
867 #[cfg(not(feature = "binary"))]
868 return None;
869
870 #[cfg(feature = "binary")]
871 self.env
872 .get(&EnvVariable::new_no_prebuilt(Some(_pkg)))
873 .or(self.env.get(&EnvVariable::new_no_prebuilt(None)))
874 .map_or_else(|| self.paths.get(_pkg), |_| None)
875 }
876
877 fn probe_full(mut self) -> Result<Dependencies, Error> {
878 let mut libraries = self.probe_pkg_config()?;
879 libraries.override_from_flags(&self.env);
880 Ok(libraries)
881 }
882
883 fn probe_pkg_config(&mut self) -> Result<Dependencies, Error> {
884 let dir = self
885 .env
886 .get("CARGO_MANIFEST_DIR")
887 .ok_or_else(|| Error::InvalidMetadata("$CARGO_MANIFEST_DIR not set".into()))?;
888 let mut path = PathBuf::from(dir);
889 path.push("Cargo.toml");
890
891 println!("cargo:rerun-if-changed={}", path.to_string_lossy());
892
893 let metadata = MetaData::from_file(&path)?;
894 let mut libraries = Dependencies::default();
895
896 let unconditional_keys: std::collections::HashSet<_> = metadata
899 .deps
900 .iter()
901 .filter(|d| d.cfg.is_none())
902 .map(|d| &d.key)
903 .collect();
904
905 for dep in metadata.deps.iter() {
906 if let Some(cfg) = &dep.cfg {
907 if !self.check_cfg(cfg)? {
909 continue;
910 }
911 if unconditional_keys.contains(&dep.key) {
915 continue;
916 }
917 }
918
919 let mut enabled_feature_overrides = Vec::new();
920
921 for o in dep.version_overrides.iter() {
922 if self.has_feature(&o.key) {
923 enabled_feature_overrides.push(o);
924 }
925 }
926
927 if let Some(feature) = dep.feature.as_ref() {
928 if !self.has_feature(feature) {
929 continue;
930 }
931 }
932
933 let version;
935 let lib_name;
936 let fallback_lib_names;
937 let optional;
938 if enabled_feature_overrides.is_empty() {
939 version = dep.version.as_deref();
940 lib_name = dep.lib_name();
941 fallback_lib_names = dep.fallback_names.as_deref().unwrap_or(&[]);
942 optional = dep.optional;
943 } else {
944 enabled_feature_overrides.sort_by(|a, b| {
945 fn min_version(r: metadata::VersionRange<'_>) -> &str {
946 match r.start_bound() {
947 std::ops::Bound::Unbounded => unreachable!(),
948 std::ops::Bound::Excluded(_) => unreachable!(),
949 std::ops::Bound::Included(b) => b,
950 }
951 }
952
953 let a = min_version(metadata::parse_version(&a.version));
954 let b = min_version(metadata::parse_version(&b.version));
955
956 version_compare::compare(a, b)
957 .expect("failed to compare versions")
958 .ord()
959 .expect("invalid version")
960 });
961 let highest = enabled_feature_overrides.into_iter().next_back().unwrap();
962
963 version = Some(highest.version.as_str());
964 lib_name = highest.name.as_deref().unwrap_or(dep.lib_name());
965 fallback_lib_names = highest
966 .fallback_names
967 .as_deref()
968 .or(dep.fallback_names.as_deref())
969 .unwrap_or(&[]);
970 optional = highest.optional.unwrap_or(dep.optional);
971 };
972
973 let version = version.ok_or_else(|| {
974 Error::InvalidMetadata(format!("No version defined for {}", dep.key))
975 })?;
976
977 let name = &dep.key;
978 let build_internal = self.get_build_internal_status(name)?;
979
980 let prebuilt_pkg_config_paths = self.query_path(name);
983
984 let statik = cfg!(feature = "binary")
986 || self
987 .env
988 .has_value(&EnvVariable::new_link(Some(name)), "static")
989 || self.env.has_value(&EnvVariable::new_link(None), "static");
990
991 let mut library = if self.env.contains(&EnvVariable::new_no_pkg_config(name)) {
992 Library::from_env_variables(name)
993 } else if build_internal == BuildInternal::Always {
994 self.call_build_internal(lib_name, version)?
995 } else {
996 let mut config = pkg_config::Config::new();
997 config
998 .print_system_libs(false)
999 .cargo_metadata(false)
1000 .range_version(metadata::parse_version(version))
1001 .statik(statik);
1002
1003 #[cfg(windows)]
1010 if prebuilt_pkg_config_paths.is_some() && pkg_config_accepts_dont_define_prefix() {
1011 config.arg("--dont-define-prefix");
1012 }
1013
1014 let probe = Library::wrap_pkg_config(prebuilt_pkg_config_paths, || {
1015 Self::probe_with_fallback(&config, lib_name, fallback_lib_names)
1016 });
1017
1018 match probe {
1019 Ok((lib_name, lib)) => Library::from_pkg_config(lib_name, lib),
1020 Err(e) => {
1021 if build_internal == BuildInternal::Auto {
1022 self.call_build_internal(name, version)?
1024 } else if optional {
1025 continue;
1027 } else {
1028 return Err(e.into());
1029 }
1030 }
1031 }
1032 };
1033
1034 library.statik = statik;
1035
1036 libraries.add(name, library);
1037 }
1038
1039 Ok(libraries)
1040 }
1041
1042 fn probe_with_fallback<'a>(
1043 config: &'a pkg_config::Config,
1044 name: &'a str,
1045 fallback_names: &'a [String],
1046 ) -> Result<(&'a str, pkg_config::Library), pkg_config::Error> {
1047 let error = match config.probe(name) {
1048 Ok(x) => return Ok((name, x)),
1049 Err(e) => e,
1050 };
1051 for name in fallback_names {
1052 if let Ok(library) = config.probe(name) {
1053 return Ok((name, library));
1054 }
1055 }
1056 Err(error)
1057 }
1058
1059 fn get_build_internal_env_var(&self, var: EnvVariable) -> Result<Option<BuildInternal>, Error> {
1060 match self.env.get(&var).as_deref() {
1061 Some(s) => {
1062 let b = BuildInternal::from_str(s).map_err(|_| {
1063 Error::BuildInternalInvalid(format!(
1064 "Invalid value in {var}: {s} (allowed: 'auto', 'always', 'never')"
1065 ))
1066 })?;
1067 Ok(Some(b))
1068 }
1069 None => Ok(None),
1070 }
1071 }
1072
1073 fn get_build_internal_status(&self, name: &str) -> Result<BuildInternal, Error> {
1074 match self.get_build_internal_env_var(EnvVariable::new_build_internal(Some(name)))? {
1075 Some(b) => Ok(b),
1076 None => Ok(self
1077 .get_build_internal_env_var(EnvVariable::new_build_internal(None))?
1078 .unwrap_or_default()),
1079 }
1080 }
1081
1082 fn call_build_internal(&mut self, name: &str, version_str: &str) -> Result<Library, Error> {
1083 let lib = match self.build_internals.remove(name) {
1084 Some(f) => f(name, version_str)
1085 .map_err(|e| Error::BuildInternalClosureError(name.into(), e))?,
1086 None => {
1087 return Err(Error::BuildInternalNoClosure(
1088 name.into(),
1089 version_str.into(),
1090 ))
1091 }
1092 };
1093
1094 let version = metadata::parse_version(version_str);
1096 fn min_version(r: metadata::VersionRange<'_>) -> &str {
1097 match r.start_bound() {
1098 std::ops::Bound::Unbounded => unreachable!(),
1099 std::ops::Bound::Excluded(_) => unreachable!(),
1100 std::ops::Bound::Included(b) => b,
1101 }
1102 }
1103 fn max_version(r: metadata::VersionRange<'_>) -> Option<&str> {
1104 match r.end_bound() {
1105 std::ops::Bound::Included(_) => unreachable!(),
1106 std::ops::Bound::Unbounded => None,
1107 std::ops::Bound::Excluded(b) => Some(*b),
1108 }
1109 }
1110
1111 let min = min_version(version.clone());
1112 if version_compare::compare(&lib.version, min) == Ok(version_compare::Cmp::Lt) {
1113 return Err(Error::BuildInternalWrongVersion(
1114 name.into(),
1115 lib.version,
1116 version_str.into(),
1117 ));
1118 }
1119
1120 if let Some(max) = max_version(version) {
1121 if version_compare::compare(&lib.version, max) == Ok(version_compare::Cmp::Ge) {
1122 return Err(Error::BuildInternalWrongVersion(
1123 name.into(),
1124 lib.version,
1125 version_str.into(),
1126 ));
1127 }
1128 }
1129
1130 Ok(lib)
1131 }
1132
1133 fn has_feature(&self, feature: &str) -> bool {
1134 let var: &str = &format!("CARGO_FEATURE_{}", feature.to_uppercase().replace('-', "_"));
1135 self.env.contains(var)
1136 }
1137
1138 fn check_cfg(&self, cfg: &cfg_expr::Expression) -> Result<bool, Error> {
1139 use cfg_expr::{targets::get_builtin_target_by_triple, Predicate};
1140
1141 let target = self
1142 .env
1143 .get("TARGET")
1144 .expect("no TARGET env variable defined");
1145
1146 let res = if let Some(target) = get_builtin_target_by_triple(&target) {
1147 cfg.eval(|pred| match pred {
1148 Predicate::Target(tp) => Some(tp.matches(target)),
1149 _ => None,
1150 })
1151 } else {
1152 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));
1154
1155 cfg.eval(|pred| match pred {
1156 Predicate::Target(tp) => Some(tp.matches(&triple)),
1157 _ => None,
1158 })
1159 };
1160
1161 res.ok_or_else(|| Error::UnsupportedCfg(cfg.original().to_string()))
1162 }
1163}
1164
1165#[derive(Debug, PartialEq, Eq)]
1166pub enum Source {
1168 PkgConfig,
1170 EnvVariables,
1172}
1173
1174#[derive(Debug, PartialEq, Eq)]
1175pub struct InternalLib {
1177 pub name: String,
1179 pub is_static_available: bool,
1181}
1182
1183impl InternalLib {
1184 const fn new(name: String, is_static_available: bool) -> Self {
1185 InternalLib {
1186 name,
1187 is_static_available,
1188 }
1189 }
1190}
1191
1192#[derive(Debug)]
1193pub struct Library {
1195 pub name: String,
1197 pub source: Source,
1199 pub libs: Vec<InternalLib>,
1201 pub link_paths: Vec<PathBuf>,
1203 pub link_files: Vec<PathBuf>,
1205 pub frameworks: Vec<String>,
1207 pub framework_paths: Vec<PathBuf>,
1209 pub include_paths: Vec<PathBuf>,
1211 pub ld_args: Vec<Vec<String>>,
1213 pub defines: HashMap<String, Option<String>>,
1215 pub version: String,
1217 pub statik: bool,
1219}
1220
1221impl Library {
1222 fn from_pkg_config(name: &str, l: pkg_config::Library) -> Self {
1223 let system_roots = if cfg!(target_os = "macos") {
1225 vec![PathBuf::from("/Library"), PathBuf::from("/System")]
1226 } else {
1227 let sysroot = env::var_os("PKG_CONFIG_SYSROOT_DIR")
1228 .or_else(|| env::var_os("SYSROOT"))
1229 .map(PathBuf::from);
1230
1231 if cfg!(target_os = "windows") {
1232 if let Some(sysroot) = sysroot {
1233 vec![sysroot]
1234 } else {
1235 vec![]
1236 }
1237 } else {
1238 vec![sysroot.unwrap_or_else(|| PathBuf::from("/usr"))]
1239 }
1240 };
1241
1242 let is_static_available = |name: &String| -> bool {
1243 if cfg!(all(target_os = "windows", target_env = "msvc")) {
1252 return false;
1253 }
1254 let libnames = {
1255 let mut names = vec![format!("lib{name}.a")];
1256 if cfg!(target_os = "windows") {
1257 names.push(format!("{name}.lib"));
1258 }
1259 names
1260 };
1261
1262 l.link_paths.iter().any(|dir| {
1263 let library_exists = libnames.iter().any(|libname| dir.join(libname).exists());
1264 library_exists && !system_roots.iter().any(|sys| dir.starts_with(sys))
1265 })
1266 };
1267
1268 Self {
1269 name: name.to_string(),
1270 source: Source::PkgConfig,
1271 libs: l
1272 .libs
1273 .iter()
1274 .map(|lib| InternalLib::new(lib.to_owned(), is_static_available(lib)))
1275 .collect(),
1276 link_paths: l.link_paths,
1277 link_files: l.link_files,
1278 include_paths: l.include_paths,
1279 ld_args: l.ld_args,
1280 frameworks: l.frameworks,
1281 framework_paths: l.framework_paths,
1282 defines: l.defines,
1283 version: l.version,
1284 statik: false,
1285 }
1286 }
1287
1288 fn from_env_variables(name: &str) -> Self {
1289 Self {
1290 name: name.to_string(),
1291 source: Source::EnvVariables,
1292 libs: Vec::new(),
1293 link_paths: Vec::new(),
1294 link_files: Vec::new(),
1295 include_paths: Vec::new(),
1296 ld_args: Vec::new(),
1297 frameworks: Vec::new(),
1298 framework_paths: Vec::new(),
1299 defines: HashMap::new(),
1300 version: String::new(),
1301 statik: false,
1302 }
1303 }
1304
1305 pub fn wrap_pkg_config<T, R>(
1308 pkg_config_paths: impl PathOrList<T>,
1309 f: impl FnOnce() -> Result<R, pkg_config::Error>,
1310 ) -> Result<R, pkg_config::Error> {
1311 let prev = env::var("PKG_CONFIG_PATH").ok();
1313
1314 let prev_paths = prev.iter().flat_map(env::split_paths).collect::<Vec<_>>();
1315 let joined_paths = pkg_config_paths.join_paths(prev_paths.as_slice());
1316
1317 #[cfg(windows)]
1321 let joined_paths = OsString::from(joined_paths.to_string_lossy().replace('\\', "/"));
1322
1323 env::set_var("PKG_CONFIG_PATH", joined_paths);
1324
1325 let res = f();
1326
1327 if let Some(prev) = prev {
1328 env::set_var("PKG_CONFIG_PATH", prev);
1329 }
1330
1331 res
1332 }
1333
1334 pub fn from_internal_pkg_config<T>(
1357 pkg_config_paths: impl PathOrList<T>,
1358 lib: &str,
1359 version: &str,
1360 ) -> Result<Self, BuildInternalClosureError> {
1361 let pkg_lib = Self::wrap_pkg_config(pkg_config_paths, || {
1362 pkg_config::Config::new()
1363 .atleast_version(version)
1364 .print_system_libs(false)
1365 .cargo_metadata(false)
1366 .statik(true)
1367 .probe(lib)
1368 })?;
1369
1370 let mut lib = Self::from_pkg_config(lib, pkg_lib);
1371 lib.statik = true;
1372 Ok(lib)
1373 }
1374}
1375
1376#[cfg(windows)]
1378fn pkg_config_accepts_dont_define_prefix() -> bool {
1379 use std::sync::OnceLock;
1380 static SUPPORTED: OnceLock<bool> = OnceLock::new();
1381 *SUPPORTED.get_or_init(|| {
1382 let exe = env::var_os("PKG_CONFIG").unwrap_or_else(|| "pkg-config".into());
1383 std::process::Command::new(exe)
1384 .args(["--dont-define-prefix", "--version"])
1385 .output()
1386 .map(|out| out.status.success())
1387 .unwrap_or(false)
1388 })
1389}
1390
1391pub trait PathOrList<T> {
1395 fn join_paths(&self, other: impl AsRef<[PathBuf]>) -> OsString;
1398}
1399
1400impl<T: AsRef<Path>> PathOrList<T> for T {
1401 fn join_paths(&self, other: impl AsRef<[PathBuf]>) -> OsString {
1402 let other = other.as_ref().iter().map(|p| p.as_path());
1403 env::join_paths(iter::once(self.as_ref()).chain(other))
1404 .expect("Path contains invalid character")
1405 }
1406}
1407
1408impl<T: AsRef<Path>, S: Borrow<[T]>> PathOrList<(T, S)> for &S {
1409 fn join_paths(&self, other: impl AsRef<[PathBuf]>) -> OsString {
1410 let slice: &[T] = (*self).borrow();
1411 let other = other.as_ref().iter().map(|p| p.as_path());
1412 env::join_paths(slice.iter().map(|p| p.as_ref()).chain(other))
1413 .expect("Path contains invalid character")
1414 }
1415}
1416
1417impl<T: PathOrList<S>, S> PathOrList<(T, S)> for Option<T> {
1418 fn join_paths(&self, other: impl AsRef<[PathBuf]>) -> OsString {
1419 match self {
1420 Some(s) => s.join_paths(other),
1421 None => env::join_paths(other.as_ref().iter().map(|p| p.as_os_str()))
1422 .expect("Path contains invalid character"),
1423 }
1424 }
1425}
1426
1427#[derive(Debug)]
1428enum EnvVariables {
1429 Environment,
1430 #[cfg(test)]
1431 Mock(HashMap<&'static str, String>),
1432}
1433
1434trait EnvVariablesExt<T> {
1435 fn contains(&self, var: T) -> bool {
1436 self.get(var).is_some()
1437 }
1438
1439 fn get(&self, var: T) -> Option<String>;
1440
1441 fn has_value(&self, var: T, val: &str) -> bool {
1442 match self.get(var) {
1443 Some(v) => v == val,
1444 None => false,
1445 }
1446 }
1447}
1448
1449impl EnvVariablesExt<&str> for EnvVariables {
1450 fn get(&self, var: &str) -> Option<String> {
1451 match self {
1452 EnvVariables::Environment => env::var(var).ok(),
1453 #[cfg(test)]
1454 EnvVariables::Mock(vars) => vars.get(var).cloned(),
1455 }
1456 }
1457}
1458
1459impl EnvVariablesExt<&EnvVariable> for EnvVariables {
1460 fn get(&self, var: &EnvVariable) -> Option<String> {
1461 let s = var.to_string();
1462 let var: &str = s.as_ref();
1463 self.get(var)
1464 }
1465}
1466
1467#[derive(Debug, PartialEq)]
1468enum BuildFlag {
1469 Include(String),
1470 SearchNative(String),
1471 SearchFramework(String),
1472 Lib(String, bool), LibFramework(String),
1474 RerunIfEnvChanged(EnvVariable),
1475 LinkArg(Vec<String>),
1476}
1477
1478impl fmt::Display for BuildFlag {
1479 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1480 match self {
1481 BuildFlag::Include(paths) => write!(f, "include={paths}"),
1482 BuildFlag::SearchNative(lib) => write!(f, "rustc-link-search=native={lib}"),
1483 BuildFlag::SearchFramework(lib) => write!(f, "rustc-link-search=framework={lib}"),
1484 BuildFlag::Lib(lib, statik) => {
1485 if *statik {
1486 write!(f, "rustc-link-lib=static={lib}")
1487 } else {
1488 write!(f, "rustc-link-lib={lib}")
1489 }
1490 }
1491 BuildFlag::LibFramework(lib) => write!(f, "rustc-link-lib=framework={lib}"),
1492 BuildFlag::RerunIfEnvChanged(env) => write!(f, "rerun-if-env-changed={env}"),
1493 BuildFlag::LinkArg(ld_option) => {
1494 write!(f, "rustc-link-arg=-Wl,{}", ld_option.join(","))
1495 }
1496 }
1497 }
1498}
1499
1500#[derive(Debug, PartialEq)]
1501struct BuildFlags(Vec<BuildFlag>);
1502
1503impl BuildFlags {
1504 const fn new() -> Self {
1505 Self(Vec::new())
1506 }
1507
1508 fn add(&mut self, flag: BuildFlag) {
1509 self.0.push(flag);
1510 }
1511}
1512
1513impl fmt::Display for BuildFlags {
1514 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1515 for flag in self.0.iter() {
1516 writeln!(f, "cargo:{flag}")?;
1517 }
1518 Ok(())
1519 }
1520}
1521
1522fn split_paths(value: &str) -> Vec<PathBuf> {
1523 if !value.is_empty() {
1524 let paths = env::split_paths(&value);
1525 paths.map(|p| Path::new(&p).into()).collect()
1526 } else {
1527 Vec::new()
1528 }
1529}
1530
1531fn split_string(value: &str) -> Vec<String> {
1532 if !value.is_empty() {
1533 value.split(' ').map(|s| s.to_string()).collect()
1534 } else {
1535 Vec::new()
1536 }
1537}
1538
1539#[derive(Debug, PartialEq, Default)]
1540enum BuildInternal {
1541 Auto,
1542 Always,
1543 #[default]
1544 Never,
1545}
1546
1547impl FromStr for BuildInternal {
1548 type Err = ParseError;
1549
1550 fn from_str(s: &str) -> Result<Self, Self::Err> {
1551 match s {
1552 "auto" => Ok(Self::Auto),
1553 "always" => Ok(Self::Always),
1554 "never" => Ok(Self::Never),
1555 v => Err(ParseError::VariantNotFound(v.to_owned())),
1556 }
1557 }
1558}
1559
1560#[derive(Debug, PartialEq)]
1561enum ParseError {
1562 VariantNotFound(String),
1563}
1564
1565impl std::error::Error for ParseError {}
1566
1567impl fmt::Display for ParseError {
1568 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1569 match self {
1570 Self::VariantNotFound(v) => write!(f, "Unknown variant: `{v}`"),
1571 }
1572 }
1573}