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) -> Result<BuildFlags, Error> {
513 let mut flags = BuildFlags::new();
514 let mut include_paths = Vec::new();
515
516 for (name, lib) in self.iter() {
517 include_paths.extend(lib.include_paths.clone());
518
519 if lib.source == Source::EnvVariables
520 && lib.libs.is_empty()
521 && lib.frameworks.is_empty()
522 {
523 return Err(Error::MissingLib(name.to_string()));
524 }
525
526 lib.link_paths
527 .iter()
528 .for_each(|l| flags.add(BuildFlag::SearchNative(l.to_string_lossy().to_string())));
529 lib.framework_paths.iter().for_each(|f| {
530 flags.add(BuildFlag::SearchFramework(f.to_string_lossy().to_string()))
531 });
532 lib.libs.iter().for_each(|l| {
533 flags.add(BuildFlag::Lib(
534 l.name.clone(),
535 lib.statik && l.is_static_available,
536 ))
537 });
538 lib.frameworks
539 .iter()
540 .for_each(|f| flags.add(BuildFlag::LibFramework(f.clone())));
541 lib.ld_args
542 .iter()
543 .for_each(|f| flags.add(BuildFlag::LinkArg(f.clone())))
544 }
545
546 if !include_paths.is_empty() {
549 if let Ok(paths) = std::env::join_paths(include_paths) {
550 flags.add(BuildFlag::Include(paths.to_string_lossy().to_string()));
551 }
552 }
553
554 flags.add(BuildFlag::RerunIfEnvChanged(
556 EnvVariable::new_build_internal(None),
557 ));
558 flags.add(BuildFlag::RerunIfEnvChanged(EnvVariable::new_link(None)));
559
560 for name in self.libs.keys() {
561 EnvVariable::set_rerun_if_changed_for_all_variants(&mut flags, name);
562 }
563
564 Ok(flags)
565 }
566}
567
568#[derive(Debug)]
569pub enum BuildInternalClosureError {
571 PkgConfig(pkg_config::Error),
573 Failed(String),
575}
576
577impl From<pkg_config::Error> for BuildInternalClosureError {
578 fn from(err: pkg_config::Error) -> Self {
579 Self::PkgConfig(err)
580 }
581}
582
583impl BuildInternalClosureError {
584 pub fn failed(details: &str) -> Self {
591 Self::Failed(details.to_string())
592 }
593}
594
595impl std::error::Error for BuildInternalClosureError {
596 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
597 match self {
598 Self::PkgConfig(e) => Some(e),
599 _ => None,
600 }
601 }
602}
603
604impl fmt::Display for BuildInternalClosureError {
605 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
606 match self {
607 Self::PkgConfig(e) => write!(f, "{e}"),
608 Self::Failed(s) => write!(f, "{s}"),
609 }
610 }
611}
612
613#[derive(Debug, PartialEq)]
615enum EnvVariable {
616 Lib(String),
617 LibFramework(String),
618 SearchNative(String),
619 SearchFramework(String),
620 Include(String),
621 NoPkgConfig(String),
622 BuildInternal(Option<String>),
623 Link(Option<String>),
624 LinkerArgs(String),
625 NoPrebuilt(Option<String>),
626}
627
628impl EnvVariable {
629 fn new_lib(lib: &str) -> Self {
630 Self::Lib(lib.to_string())
631 }
632
633 fn new_lib_framework(lib: &str) -> Self {
634 Self::LibFramework(lib.to_string())
635 }
636
637 fn new_search_native(lib: &str) -> Self {
638 Self::SearchNative(lib.to_string())
639 }
640
641 fn new_search_framework(lib: &str) -> Self {
642 Self::SearchFramework(lib.to_string())
643 }
644
645 fn new_include(lib: &str) -> Self {
646 Self::Include(lib.to_string())
647 }
648
649 fn new_linker_args(lib: &str) -> Self {
650 Self::LinkerArgs(lib.to_string())
651 }
652
653 fn new_no_pkg_config(lib: &str) -> Self {
654 Self::NoPkgConfig(lib.to_string())
655 }
656
657 fn new_build_internal(lib: Option<&str>) -> Self {
658 Self::BuildInternal(lib.map(|l| l.to_string()))
659 }
660
661 fn new_link(lib: Option<&str>) -> Self {
662 Self::Link(lib.map(|l| l.to_string()))
663 }
664
665 fn new_no_prebuilt(lib: Option<&str>) -> Self {
666 Self::NoPrebuilt(lib.map(|l| l.to_string()))
667 }
668
669 const fn suffix(&self) -> &'static str {
670 match self {
671 EnvVariable::Lib(_) => "LIB",
672 EnvVariable::LibFramework(_) => "LIB_FRAMEWORK",
673 EnvVariable::SearchNative(_) => "SEARCH_NATIVE",
674 EnvVariable::SearchFramework(_) => "SEARCH_FRAMEWORK",
675 EnvVariable::Include(_) => "INCLUDE",
676 EnvVariable::NoPkgConfig(_) => "NO_PKG_CONFIG",
677 EnvVariable::BuildInternal(_) => "BUILD_INTERNAL",
678 EnvVariable::Link(_) => "LINK",
679 EnvVariable::LinkerArgs(_) => "LDFLAGS",
680 EnvVariable::NoPrebuilt(_) => "NO_PREBUILT",
681 }
682 }
683
684 fn set_rerun_if_changed_for_all_variants(flags: &mut BuildFlags, name: &str) {
685 #[inline]
686 fn add_to_flags(flags: &mut BuildFlags, var: EnvVariable) {
687 flags.add(BuildFlag::RerunIfEnvChanged(var));
688 }
689 add_to_flags(flags, EnvVariable::new_lib(name));
690 add_to_flags(flags, EnvVariable::new_lib_framework(name));
691 add_to_flags(flags, EnvVariable::new_search_native(name));
692 add_to_flags(flags, EnvVariable::new_search_framework(name));
693 add_to_flags(flags, EnvVariable::new_include(name));
694 add_to_flags(flags, EnvVariable::new_linker_args(name));
695 add_to_flags(flags, EnvVariable::new_no_pkg_config(name));
696 add_to_flags(flags, EnvVariable::new_build_internal(Some(name)));
697 add_to_flags(flags, EnvVariable::new_link(Some(name)));
698 add_to_flags(flags, EnvVariable::new_no_prebuilt(Some(name)));
699 }
700}
701
702impl fmt::Display for EnvVariable {
703 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
704 let suffix = match self {
705 EnvVariable::Lib(lib)
706 | EnvVariable::LibFramework(lib)
707 | EnvVariable::SearchNative(lib)
708 | EnvVariable::SearchFramework(lib)
709 | EnvVariable::Include(lib)
710 | EnvVariable::LinkerArgs(lib)
711 | EnvVariable::NoPkgConfig(lib)
712 | EnvVariable::BuildInternal(Some(lib))
713 | EnvVariable::Link(Some(lib))
714 | EnvVariable::NoPrebuilt(Some(lib)) => {
715 format!("{}_{}", lib.to_shouty_snake_case(), self.suffix())
716 }
717 EnvVariable::BuildInternal(None)
718 | EnvVariable::Link(None)
719 | EnvVariable::NoPrebuilt(None) => self.suffix().to_string(),
720 };
721 write!(f, "SYSTEM_DEPS_{suffix}")
722 }
723}
724
725type FnBuildInternal =
726 dyn FnOnce(&str, &str) -> std::result::Result<Library, BuildInternalClosureError>;
727
728pub struct Config {
730 env: EnvVariables,
731 build_internals: HashMap<String, Box<FnBuildInternal>>,
732 #[cfg(feature = "binary")]
733 paths: &'static system_deps_meta::binary::Paths,
734}
735
736impl Default for Config {
737 fn default() -> Self {
738 Self::new_with_env(EnvVariables::Environment)
739 }
740}
741
742impl Config {
743 pub fn new() -> Self {
745 Self::default()
746 }
747
748 fn new_with_env(env: EnvVariables) -> Self {
749 let me = Self {
750 env,
751 build_internals: HashMap::new(),
752 #[cfg(feature = "binary")]
753 paths: {
754 const CONTENT: &str = include_str!(env!("SYSTEM_DEPS_BINARY_PATHS"));
756 static PATHS: std::sync::OnceLock<system_deps_meta::binary::Paths> =
757 std::sync::OnceLock::new();
758 PATHS.get_or_init(|| {
759 toml::from_str(CONTENT)
760 .expect("The build script should output valid serialization")
761 })
762 },
763 };
764
765 #[cfg(feature = "binary")]
773 if env::var_os("PKG_CONFIG").is_none() {
774 if let Some(pc) = me.paths.pkg_config_binary() {
775 env::set_var("PKG_CONFIG", pc);
776 }
777 }
778
779 me
780 }
781
782 pub fn probe(self) -> Result<Dependencies, Error> {
787 let libraries = self.probe_full()?;
788 let flags = libraries.gen_flags()?;
789
790 println!("{flags}");
792
793 for (name, _) in libraries.iter() {
794 println!("cargo:rustc-cfg=system_deps_have_{}", name.to_snake_case());
795 }
796
797 Ok(libraries)
798 }
799
800 pub fn add_build_internal<F>(mut self, name: &str, func: F) -> Self
813 where
814 F: 'static + FnOnce(&str, &str) -> std::result::Result<Library, BuildInternalClosureError>,
815 {
816 let mut build_internals = self.build_internals;
817 build_internals.insert(name.to_string(), Box::new(func));
818
819 self.build_internals = build_internals;
820 self
821 }
822
823 pub fn query_path(&self, _pkg: &str) -> Option<&'static Vec<PathBuf>> {
825 #[cfg(not(feature = "binary"))]
826 return None;
827
828 #[cfg(feature = "binary")]
829 self.env
830 .get(&EnvVariable::new_no_prebuilt(Some(_pkg)))
831 .or(self.env.get(&EnvVariable::new_no_prebuilt(None)))
832 .map_or_else(|| self.paths.get(_pkg), |_| None)
833 }
834
835 fn probe_full(mut self) -> Result<Dependencies, Error> {
836 let mut libraries = self.probe_pkg_config()?;
837 libraries.override_from_flags(&self.env);
838 Ok(libraries)
839 }
840
841 fn probe_pkg_config(&mut self) -> Result<Dependencies, Error> {
842 let dir = self
843 .env
844 .get("CARGO_MANIFEST_DIR")
845 .ok_or_else(|| Error::InvalidMetadata("$CARGO_MANIFEST_DIR not set".into()))?;
846 let mut path = PathBuf::from(dir);
847 path.push("Cargo.toml");
848
849 println!("cargo:rerun-if-changed={}", path.to_string_lossy());
850
851 let metadata = MetaData::from_file(&path)?;
852 let mut libraries = Dependencies::default();
853
854 let unconditional_keys: std::collections::HashSet<_> = metadata
857 .deps
858 .iter()
859 .filter(|d| d.cfg.is_none())
860 .map(|d| &d.key)
861 .collect();
862
863 for dep in metadata.deps.iter() {
864 if let Some(cfg) = &dep.cfg {
865 if !self.check_cfg(cfg)? {
867 continue;
868 }
869 if unconditional_keys.contains(&dep.key) {
873 continue;
874 }
875 }
876
877 let mut enabled_feature_overrides = Vec::new();
878
879 for o in dep.version_overrides.iter() {
880 if self.has_feature(&o.key) {
881 enabled_feature_overrides.push(o);
882 }
883 }
884
885 if let Some(feature) = dep.feature.as_ref() {
886 if !self.has_feature(feature) {
887 continue;
888 }
889 }
890
891 let version;
893 let lib_name;
894 let fallback_lib_names;
895 let optional;
896 if enabled_feature_overrides.is_empty() {
897 version = dep.version.as_deref();
898 lib_name = dep.lib_name();
899 fallback_lib_names = dep.fallback_names.as_deref().unwrap_or(&[]);
900 optional = dep.optional;
901 } else {
902 enabled_feature_overrides.sort_by(|a, b| {
903 fn min_version(r: metadata::VersionRange<'_>) -> &str {
904 match r.start_bound() {
905 std::ops::Bound::Unbounded => unreachable!(),
906 std::ops::Bound::Excluded(_) => unreachable!(),
907 std::ops::Bound::Included(b) => b,
908 }
909 }
910
911 let a = min_version(metadata::parse_version(&a.version));
912 let b = min_version(metadata::parse_version(&b.version));
913
914 version_compare::compare(a, b)
915 .expect("failed to compare versions")
916 .ord()
917 .expect("invalid version")
918 });
919 let highest = enabled_feature_overrides.into_iter().next_back().unwrap();
920
921 version = Some(highest.version.as_str());
922 lib_name = highest.name.as_deref().unwrap_or(dep.lib_name());
923 fallback_lib_names = highest
924 .fallback_names
925 .as_deref()
926 .or(dep.fallback_names.as_deref())
927 .unwrap_or(&[]);
928 optional = highest.optional.unwrap_or(dep.optional);
929 };
930
931 let version = version.ok_or_else(|| {
932 Error::InvalidMetadata(format!("No version defined for {}", dep.key))
933 })?;
934
935 let name = &dep.key;
936 let build_internal = self.get_build_internal_status(name)?;
937
938 let prebuilt_pkg_config_paths = self.query_path(name);
941
942 let statik = cfg!(feature = "binary")
944 || self
945 .env
946 .has_value(&EnvVariable::new_link(Some(name)), "static")
947 || self.env.has_value(&EnvVariable::new_link(None), "static");
948
949 let mut library = if self.env.contains(&EnvVariable::new_no_pkg_config(name)) {
950 Library::from_env_variables(name)
951 } else if build_internal == BuildInternal::Always {
952 self.call_build_internal(lib_name, version)?
953 } else {
954 let mut config = pkg_config::Config::new();
955 config
956 .print_system_libs(false)
957 .cargo_metadata(false)
958 .range_version(metadata::parse_version(version))
959 .statik(statik);
960
961 #[cfg(windows)]
968 if prebuilt_pkg_config_paths.is_some() && pkg_config_accepts_dont_define_prefix() {
969 config.arg("--dont-define-prefix");
970 }
971
972 let probe = Library::wrap_pkg_config(prebuilt_pkg_config_paths, || {
973 Self::probe_with_fallback(&config, lib_name, fallback_lib_names)
974 });
975
976 match probe {
977 Ok((lib_name, lib)) => Library::from_pkg_config(lib_name, lib),
978 Err(e) => {
979 if build_internal == BuildInternal::Auto {
980 self.call_build_internal(name, version)?
982 } else if optional {
983 continue;
985 } else {
986 return Err(e.into());
987 }
988 }
989 }
990 };
991
992 library.statik = statik;
993
994 libraries.add(name, library);
995 }
996
997 Ok(libraries)
998 }
999
1000 fn probe_with_fallback<'a>(
1001 config: &'a pkg_config::Config,
1002 name: &'a str,
1003 fallback_names: &'a [String],
1004 ) -> Result<(&'a str, pkg_config::Library), pkg_config::Error> {
1005 let error = match config.probe(name) {
1006 Ok(x) => return Ok((name, x)),
1007 Err(e) => e,
1008 };
1009 for name in fallback_names {
1010 if let Ok(library) = config.probe(name) {
1011 return Ok((name, library));
1012 }
1013 }
1014 Err(error)
1015 }
1016
1017 fn get_build_internal_env_var(&self, var: EnvVariable) -> Result<Option<BuildInternal>, Error> {
1018 match self.env.get(&var).as_deref() {
1019 Some(s) => {
1020 let b = BuildInternal::from_str(s).map_err(|_| {
1021 Error::BuildInternalInvalid(format!(
1022 "Invalid value in {var}: {s} (allowed: 'auto', 'always', 'never')"
1023 ))
1024 })?;
1025 Ok(Some(b))
1026 }
1027 None => Ok(None),
1028 }
1029 }
1030
1031 fn get_build_internal_status(&self, name: &str) -> Result<BuildInternal, Error> {
1032 match self.get_build_internal_env_var(EnvVariable::new_build_internal(Some(name)))? {
1033 Some(b) => Ok(b),
1034 None => Ok(self
1035 .get_build_internal_env_var(EnvVariable::new_build_internal(None))?
1036 .unwrap_or_default()),
1037 }
1038 }
1039
1040 fn call_build_internal(&mut self, name: &str, version_str: &str) -> Result<Library, Error> {
1041 let lib = match self.build_internals.remove(name) {
1042 Some(f) => f(name, version_str)
1043 .map_err(|e| Error::BuildInternalClosureError(name.into(), e))?,
1044 None => {
1045 return Err(Error::BuildInternalNoClosure(
1046 name.into(),
1047 version_str.into(),
1048 ))
1049 }
1050 };
1051
1052 let version = metadata::parse_version(version_str);
1054 fn min_version(r: metadata::VersionRange<'_>) -> &str {
1055 match r.start_bound() {
1056 std::ops::Bound::Unbounded => unreachable!(),
1057 std::ops::Bound::Excluded(_) => unreachable!(),
1058 std::ops::Bound::Included(b) => b,
1059 }
1060 }
1061 fn max_version(r: metadata::VersionRange<'_>) -> Option<&str> {
1062 match r.end_bound() {
1063 std::ops::Bound::Included(_) => unreachable!(),
1064 std::ops::Bound::Unbounded => None,
1065 std::ops::Bound::Excluded(b) => Some(*b),
1066 }
1067 }
1068
1069 let min = min_version(version.clone());
1070 if version_compare::compare(&lib.version, min) == Ok(version_compare::Cmp::Lt) {
1071 return Err(Error::BuildInternalWrongVersion(
1072 name.into(),
1073 lib.version,
1074 version_str.into(),
1075 ));
1076 }
1077
1078 if let Some(max) = max_version(version) {
1079 if version_compare::compare(&lib.version, max) == Ok(version_compare::Cmp::Ge) {
1080 return Err(Error::BuildInternalWrongVersion(
1081 name.into(),
1082 lib.version,
1083 version_str.into(),
1084 ));
1085 }
1086 }
1087
1088 Ok(lib)
1089 }
1090
1091 fn has_feature(&self, feature: &str) -> bool {
1092 let var: &str = &format!("CARGO_FEATURE_{}", feature.to_uppercase().replace('-', "_"));
1093 self.env.contains(var)
1094 }
1095
1096 fn check_cfg(&self, cfg: &cfg_expr::Expression) -> Result<bool, Error> {
1097 use cfg_expr::{targets::get_builtin_target_by_triple, Predicate};
1098
1099 let target = self
1100 .env
1101 .get("TARGET")
1102 .expect("no TARGET env variable defined");
1103
1104 let res = if let Some(target) = get_builtin_target_by_triple(&target) {
1105 cfg.eval(|pred| match pred {
1106 Predicate::Target(tp) => Some(tp.matches(target)),
1107 _ => None,
1108 })
1109 } else {
1110 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));
1112
1113 cfg.eval(|pred| match pred {
1114 Predicate::Target(tp) => Some(tp.matches(&triple)),
1115 _ => None,
1116 })
1117 };
1118
1119 res.ok_or_else(|| Error::UnsupportedCfg(cfg.original().to_string()))
1120 }
1121}
1122
1123#[derive(Debug, PartialEq, Eq)]
1124pub enum Source {
1126 PkgConfig,
1128 EnvVariables,
1130}
1131
1132#[derive(Debug, PartialEq, Eq)]
1133pub struct InternalLib {
1135 pub name: String,
1137 pub is_static_available: bool,
1139}
1140
1141impl InternalLib {
1142 const fn new(name: String, is_static_available: bool) -> Self {
1143 InternalLib {
1144 name,
1145 is_static_available,
1146 }
1147 }
1148}
1149
1150#[derive(Debug)]
1151pub struct Library {
1153 pub name: String,
1155 pub source: Source,
1157 pub libs: Vec<InternalLib>,
1159 pub link_paths: Vec<PathBuf>,
1161 pub frameworks: Vec<String>,
1163 pub framework_paths: Vec<PathBuf>,
1165 pub include_paths: Vec<PathBuf>,
1167 pub ld_args: Vec<Vec<String>>,
1169 pub defines: HashMap<String, Option<String>>,
1171 pub version: String,
1173 pub statik: bool,
1175}
1176
1177impl Library {
1178 fn from_pkg_config(name: &str, l: pkg_config::Library) -> Self {
1179 let system_roots = if cfg!(target_os = "macos") {
1181 vec![PathBuf::from("/Library"), PathBuf::from("/System")]
1182 } else {
1183 let sysroot = env::var_os("PKG_CONFIG_SYSROOT_DIR")
1184 .or_else(|| env::var_os("SYSROOT"))
1185 .map(PathBuf::from);
1186
1187 if cfg!(target_os = "windows") {
1188 if let Some(sysroot) = sysroot {
1189 vec![sysroot]
1190 } else {
1191 vec![]
1192 }
1193 } else {
1194 vec![sysroot.unwrap_or_else(|| PathBuf::from("/usr"))]
1195 }
1196 };
1197
1198 let is_static_available = |name: &String| -> bool {
1199 if cfg!(all(target_os = "windows", target_env = "msvc")) {
1208 return false;
1209 }
1210 let libnames = {
1211 let mut names = vec![format!("lib{name}.a")];
1212 if cfg!(target_os = "windows") {
1213 names.push(format!("{name}.lib"));
1214 }
1215 names
1216 };
1217
1218 l.link_paths.iter().any(|dir| {
1219 let library_exists = libnames.iter().any(|libname| dir.join(libname).exists());
1220 library_exists && !system_roots.iter().any(|sys| dir.starts_with(sys))
1221 })
1222 };
1223
1224 Self {
1225 name: name.to_string(),
1226 source: Source::PkgConfig,
1227 libs: l
1228 .libs
1229 .iter()
1230 .map(|lib| InternalLib::new(lib.to_owned(), is_static_available(lib)))
1231 .collect(),
1232 link_paths: l.link_paths,
1233 include_paths: l.include_paths,
1234 ld_args: l.ld_args,
1235 frameworks: l.frameworks,
1236 framework_paths: l.framework_paths,
1237 defines: l.defines,
1238 version: l.version,
1239 statik: false,
1240 }
1241 }
1242
1243 fn from_env_variables(name: &str) -> Self {
1244 Self {
1245 name: name.to_string(),
1246 source: Source::EnvVariables,
1247 libs: Vec::new(),
1248 link_paths: Vec::new(),
1249 include_paths: Vec::new(),
1250 ld_args: Vec::new(),
1251 frameworks: Vec::new(),
1252 framework_paths: Vec::new(),
1253 defines: HashMap::new(),
1254 version: String::new(),
1255 statik: false,
1256 }
1257 }
1258
1259 pub fn wrap_pkg_config<T, R>(
1262 pkg_config_paths: impl PathOrList<T>,
1263 f: impl FnOnce() -> Result<R, pkg_config::Error>,
1264 ) -> Result<R, pkg_config::Error> {
1265 let prev = env::var("PKG_CONFIG_PATH").ok();
1267
1268 let prev_paths = prev.iter().flat_map(env::split_paths).collect::<Vec<_>>();
1269 let joined_paths = pkg_config_paths.join_paths(prev_paths.as_slice());
1270
1271 #[cfg(windows)]
1275 let joined_paths = OsString::from(joined_paths.to_string_lossy().replace('\\', "/"));
1276
1277 env::set_var("PKG_CONFIG_PATH", joined_paths);
1278
1279 let res = f();
1280
1281 if let Some(prev) = prev {
1282 env::set_var("PKG_CONFIG_PATH", prev);
1283 }
1284
1285 res
1286 }
1287
1288 pub fn from_internal_pkg_config<T>(
1311 pkg_config_paths: impl PathOrList<T>,
1312 lib: &str,
1313 version: &str,
1314 ) -> Result<Self, BuildInternalClosureError> {
1315 let pkg_lib = Self::wrap_pkg_config(pkg_config_paths, || {
1316 pkg_config::Config::new()
1317 .atleast_version(version)
1318 .print_system_libs(false)
1319 .cargo_metadata(false)
1320 .statik(true)
1321 .probe(lib)
1322 })?;
1323
1324 let mut lib = Self::from_pkg_config(lib, pkg_lib);
1325 lib.statik = true;
1326 Ok(lib)
1327 }
1328}
1329
1330#[cfg(windows)]
1332fn pkg_config_accepts_dont_define_prefix() -> bool {
1333 use std::sync::OnceLock;
1334 static SUPPORTED: OnceLock<bool> = OnceLock::new();
1335 *SUPPORTED.get_or_init(|| {
1336 let exe = env::var_os("PKG_CONFIG").unwrap_or_else(|| "pkg-config".into());
1337 std::process::Command::new(exe)
1338 .args(["--dont-define-prefix", "--version"])
1339 .output()
1340 .map(|out| out.status.success())
1341 .unwrap_or(false)
1342 })
1343}
1344
1345pub trait PathOrList<T> {
1349 fn join_paths(&self, other: impl AsRef<[PathBuf]>) -> OsString;
1352}
1353
1354impl<T: AsRef<Path>> PathOrList<T> for T {
1355 fn join_paths(&self, other: impl AsRef<[PathBuf]>) -> OsString {
1356 let other = other.as_ref().iter().map(|p| p.as_path());
1357 env::join_paths(iter::once(self.as_ref()).chain(other))
1358 .expect("Path contains invalid character")
1359 }
1360}
1361
1362impl<T: AsRef<Path>, S: Borrow<[T]>> PathOrList<(T, S)> for &S {
1363 fn join_paths(&self, other: impl AsRef<[PathBuf]>) -> OsString {
1364 let slice: &[T] = (*self).borrow();
1365 let other = other.as_ref().iter().map(|p| p.as_path());
1366 env::join_paths(slice.iter().map(|p| p.as_ref()).chain(other))
1367 .expect("Path contains invalid character")
1368 }
1369}
1370
1371impl<T: PathOrList<S>, S> PathOrList<(T, S)> for Option<T> {
1372 fn join_paths(&self, other: impl AsRef<[PathBuf]>) -> OsString {
1373 match self {
1374 Some(s) => s.join_paths(other),
1375 None => env::join_paths(other.as_ref().iter().map(|p| p.as_os_str()))
1376 .expect("Path contains invalid character"),
1377 }
1378 }
1379}
1380
1381#[derive(Debug)]
1382enum EnvVariables {
1383 Environment,
1384 #[cfg(test)]
1385 Mock(HashMap<&'static str, String>),
1386}
1387
1388trait EnvVariablesExt<T> {
1389 fn contains(&self, var: T) -> bool {
1390 self.get(var).is_some()
1391 }
1392
1393 fn get(&self, var: T) -> Option<String>;
1394
1395 fn has_value(&self, var: T, val: &str) -> bool {
1396 match self.get(var) {
1397 Some(v) => v == val,
1398 None => false,
1399 }
1400 }
1401}
1402
1403impl EnvVariablesExt<&str> for EnvVariables {
1404 fn get(&self, var: &str) -> Option<String> {
1405 match self {
1406 EnvVariables::Environment => env::var(var).ok(),
1407 #[cfg(test)]
1408 EnvVariables::Mock(vars) => vars.get(var).cloned(),
1409 }
1410 }
1411}
1412
1413impl EnvVariablesExt<&EnvVariable> for EnvVariables {
1414 fn get(&self, var: &EnvVariable) -> Option<String> {
1415 let s = var.to_string();
1416 let var: &str = s.as_ref();
1417 self.get(var)
1418 }
1419}
1420
1421#[derive(Debug, PartialEq)]
1422enum BuildFlag {
1423 Include(String),
1424 SearchNative(String),
1425 SearchFramework(String),
1426 Lib(String, bool), LibFramework(String),
1428 RerunIfEnvChanged(EnvVariable),
1429 LinkArg(Vec<String>),
1430}
1431
1432impl fmt::Display for BuildFlag {
1433 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1434 match self {
1435 BuildFlag::Include(paths) => write!(f, "include={paths}"),
1436 BuildFlag::SearchNative(lib) => write!(f, "rustc-link-search=native={lib}"),
1437 BuildFlag::SearchFramework(lib) => write!(f, "rustc-link-search=framework={lib}"),
1438 BuildFlag::Lib(lib, statik) => {
1439 if *statik {
1440 write!(f, "rustc-link-lib=static={lib}")
1441 } else {
1442 write!(f, "rustc-link-lib={lib}")
1443 }
1444 }
1445 BuildFlag::LibFramework(lib) => write!(f, "rustc-link-lib=framework={lib}"),
1446 BuildFlag::RerunIfEnvChanged(env) => write!(f, "rerun-if-env-changed={env}"),
1447 BuildFlag::LinkArg(ld_option) => {
1448 write!(f, "rustc-link-arg=-Wl,{}", ld_option.join(","))
1449 }
1450 }
1451 }
1452}
1453
1454#[derive(Debug, PartialEq)]
1455struct BuildFlags(Vec<BuildFlag>);
1456
1457impl BuildFlags {
1458 const fn new() -> Self {
1459 Self(Vec::new())
1460 }
1461
1462 fn add(&mut self, flag: BuildFlag) {
1463 self.0.push(flag);
1464 }
1465}
1466
1467impl fmt::Display for BuildFlags {
1468 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1469 for flag in self.0.iter() {
1470 writeln!(f, "cargo:{flag}")?;
1471 }
1472 Ok(())
1473 }
1474}
1475
1476fn split_paths(value: &str) -> Vec<PathBuf> {
1477 if !value.is_empty() {
1478 let paths = env::split_paths(&value);
1479 paths.map(|p| Path::new(&p).into()).collect()
1480 } else {
1481 Vec::new()
1482 }
1483}
1484
1485fn split_string(value: &str) -> Vec<String> {
1486 if !value.is_empty() {
1487 value.split(' ').map(|s| s.to_string()).collect()
1488 } else {
1489 Vec::new()
1490 }
1491}
1492
1493#[derive(Debug, PartialEq, Default)]
1494enum BuildInternal {
1495 Auto,
1496 Always,
1497 #[default]
1498 Never,
1499}
1500
1501impl FromStr for BuildInternal {
1502 type Err = ParseError;
1503
1504 fn from_str(s: &str) -> Result<Self, Self::Err> {
1505 match s {
1506 "auto" => Ok(Self::Auto),
1507 "always" => Ok(Self::Always),
1508 "never" => Ok(Self::Never),
1509 v => Err(ParseError::VariantNotFound(v.to_owned())),
1510 }
1511 }
1512}
1513
1514#[derive(Debug, PartialEq)]
1515enum ParseError {
1516 VariantNotFound(String),
1517}
1518
1519impl std::error::Error for ParseError {}
1520
1521impl fmt::Display for ParseError {
1522 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1523 match self {
1524 Self::VariantNotFound(v) => write!(f, "Unknown variant: `{v}`"),
1525 }
1526 }
1527}