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