1#![deny(missing_docs)]
2
3pub mod cuda;
36pub mod libc;
37pub mod linux;
38pub mod osx;
39pub mod win;
40
41use std::{
42 borrow::Cow,
43 env, fmt,
44 fmt::Display,
45 hash::{Hash, Hasher},
46 str::FromStr,
47 sync::Arc,
48};
49
50use archspec::cpu::Microarchitecture;
51use libc::DetectLibCError;
52use linux::ParseLinuxVersionError;
53use rattler_conda_types::{
54 GenericVirtualPackage, PackageName, ParseVersionError, Platform, Version,
55};
56use serde::{Deserialize, Deserializer, Serialize, Serializer};
57
58use crate::osx::ParseOsxVersionError;
59
60#[derive(Clone, Debug, PartialEq, Default)]
62pub enum Override {
63 #[default]
65 DefaultEnvVar,
66 EnvVar(String),
68 String(String),
70}
71
72pub trait EnvOverride: Sized {
75 fn parse_version(value: &str) -> Result<Self, ParseVersionError>;
77
78 fn parse_version_opt(value: &str) -> Result<Option<Self>, DetectVirtualPackageError> {
81 if value.is_empty() {
82 Ok(None)
83 } else {
84 Ok(Some(Self::parse_version(value)?))
85 }
86 }
87
88 fn from_env_var_name_or<F>(
95 env_var_name: &str,
96 fallback: F,
97 ) -> Result<Option<Self>, DetectVirtualPackageError>
98 where
99 F: FnOnce() -> Result<Option<Self>, DetectVirtualPackageError>,
100 {
101 match env::var(env_var_name) {
102 Ok(var) => Self::parse_version_opt(&var),
103 Err(env::VarError::NotPresent) => fallback(),
104 Err(e) => Err(DetectVirtualPackageError::VarError(e)),
105 }
106 }
107
108 const DEFAULT_ENV_NAME: &'static str;
111
112 fn detect_from_host() -> Result<Option<Self>, DetectVirtualPackageError>;
118
119 fn detect_with_fallback<F>(
122 ov: &Override,
123 fallback: F,
124 ) -> Result<Option<Self>, DetectVirtualPackageError>
125 where
126 F: FnOnce() -> Result<Option<Self>, DetectVirtualPackageError>,
127 {
128 match ov {
129 Override::String(str) => Self::parse_version_opt(str),
130 Override::DefaultEnvVar => Self::from_env_var_name_or(Self::DEFAULT_ENV_NAME, fallback),
131 Override::EnvVar(name) => Self::from_env_var_name_or(name, fallback),
132 }
133 }
134
135 fn detect(ov: Option<&Override>) -> Result<Option<Self>, DetectVirtualPackageError> {
138 ov.map_or_else(Self::detect_from_host, |ov| {
139 Self::detect_with_fallback(ov, Self::detect_from_host)
140 })
141 }
142}
143
144#[derive(Clone, Eq, PartialEq, Hash, Debug)]
146pub enum VirtualPackage {
147 Win(Windows),
149
150 Unix,
152
153 Linux(Linux),
155
156 Osx(Osx),
158
159 LibC(LibC),
161
162 Cuda(Cuda),
164
165 Archspec(Archspec),
167}
168
169#[derive(Debug, Clone, Default)]
171pub struct VirtualPackages {
172 pub win: Option<Windows>,
174
175 pub unix: bool,
177
178 pub linux: Option<Linux>,
180
181 pub osx: Option<Osx>,
183
184 pub libc: Option<LibC>,
186
187 pub cuda: Option<Cuda>,
189
190 pub archspec: Option<Archspec>,
192}
193
194impl VirtualPackages {
195 pub fn into_virtual_packages(self) -> impl Iterator<Item = VirtualPackage> {
197 let Self {
198 win,
199 unix,
200 linux,
201 osx,
202 libc,
203 cuda,
204 archspec,
205 } = self;
206
207 [
208 win.map(VirtualPackage::Win),
209 unix.then_some(VirtualPackage::Unix),
210 linux.map(VirtualPackage::Linux),
211 osx.map(VirtualPackage::Osx),
212 libc.map(VirtualPackage::LibC),
213 cuda.map(VirtualPackage::Cuda),
214 archspec.map(VirtualPackage::Archspec),
215 ]
216 .into_iter()
217 .flatten()
218 }
219
220 pub fn into_generic_virtual_packages(self) -> impl Iterator<Item = GenericVirtualPackage> {
222 self.into_virtual_packages().map(Into::into)
223 }
224
225 pub fn detect(overrides: &VirtualPackageOverrides) -> Result<Self, DetectVirtualPackageError> {
228 Ok(Self {
229 win: Windows::detect(overrides.win.as_ref())?,
230 unix: Platform::current().is_unix(),
231 linux: Linux::detect(overrides.linux.as_ref())?,
232 osx: Osx::detect(overrides.osx.as_ref())?,
233 libc: LibC::detect(overrides.libc.as_ref())?,
234 cuda: Cuda::detect(overrides.cuda.as_ref())?,
235 archspec: Archspec::detect(overrides.archspec.as_ref())?,
236 })
237 }
238
239 pub fn detect_for_platform(
259 platform: Platform,
260 overrides: &VirtualPackageOverrides,
261 ) -> Result<Self, DetectVirtualPackageError> {
262 let virtual_packages = Self::detect(overrides)?;
263 if platform == Platform::current() {
264 Ok(virtual_packages)
266 } else {
267 let win = if platform.is_windows() {
269 virtual_packages
271 .win
272 .or_else(|| Some(Windows { version: None }))
273 } else {
274 None
275 };
276
277 let linux = if platform.is_linux() {
278 virtual_packages.linux.or_else(|| {
279 Some(Linux {
280 version: Version::major(0),
281 })
282 })
283 } else {
284 None
285 };
286
287 let osx = if platform.is_osx() {
288 virtual_packages.osx.or_else(|| {
290 Some(Osx {
291 version: Version::major(0),
292 })
293 })
294 } else {
295 None
296 };
297
298 let libc = if platform.is_linux() {
299 virtual_packages.libc.or_else(|| {
301 Some(LibC {
302 family: "glibc".into(),
303 version: Version::major(0),
304 })
305 })
306 } else {
307 None
308 };
309
310 let archspec = Archspec::detect_with_fallback(
311 overrides
312 .archspec
313 .as_ref()
314 .unwrap_or(&Override::DefaultEnvVar),
315 || Ok(Archspec::from_platform(platform)),
316 )?;
317
318 Ok(Self {
319 win,
320 unix: platform.is_unix(),
321 linux,
322 osx,
323 libc,
324 cuda: virtual_packages.cuda,
325 archspec,
326 })
327 }
328 }
329}
330
331impl From<VirtualPackage> for GenericVirtualPackage {
332 fn from(package: VirtualPackage) -> Self {
333 match package {
334 VirtualPackage::Unix => GenericVirtualPackage {
335 name: PackageName::new_unchecked("__unix"),
336 version: Version::major(0),
337 build_string: "0".into(),
338 },
339 VirtualPackage::Win(windows) => windows.into(),
340 VirtualPackage::Linux(linux) => linux.into(),
341 VirtualPackage::Osx(osx) => osx.into(),
342 VirtualPackage::LibC(libc) => libc.into(),
343 VirtualPackage::Cuda(cuda) => cuda.into(),
344 VirtualPackage::Archspec(spec) => spec.into(),
345 }
346 }
347}
348
349impl VirtualPackage {
350 #[deprecated(
353 since = "1.1.0",
354 note = "Use `VirtualPackage::detect(&VirtualPackageOverrides::default())` instead."
355 )]
356 pub fn current() -> Result<Vec<Self>, DetectVirtualPackageError> {
357 Self::detect(&VirtualPackageOverrides::default())
358 }
359
360 pub fn detect(
363 overrides: &VirtualPackageOverrides,
364 ) -> Result<Vec<Self>, DetectVirtualPackageError> {
365 Ok(VirtualPackages::detect(overrides)?
366 .into_virtual_packages()
367 .collect())
368 }
369}
370
371#[derive(Debug, thiserror::Error)]
373#[allow(missing_docs)]
374pub enum DetectVirtualPackageError {
375 #[error(transparent)]
376 ParseLinuxVersion(#[from] ParseLinuxVersionError),
377
378 #[error(transparent)]
379 ParseMacOsVersion(#[from] ParseOsxVersionError),
380
381 #[error(transparent)]
382 DetectLibC(#[from] DetectLibCError),
383
384 #[error(transparent)]
385 VarError(#[from] env::VarError),
386
387 #[error(transparent)]
388 VersionParseError(#[from] ParseVersionError),
389}
390#[derive(Default, Clone, Debug)]
398pub struct VirtualPackageOverrides {
399 pub win: Option<Override>,
401 pub osx: Option<Override>,
403 pub linux: Option<Override>,
405 pub libc: Option<Override>,
407 pub cuda: Option<Override>,
409 pub archspec: Option<Override>,
411}
412
413impl VirtualPackageOverrides {
414 pub fn all(ov: Override) -> Self {
417 Self {
418 win: Some(ov.clone()),
419 osx: Some(ov.clone()),
420 linux: Some(ov.clone()),
421 libc: Some(ov.clone()),
422 cuda: Some(ov.clone()),
423 archspec: Some(ov),
424 }
425 }
426
427 pub fn from_env() -> Self {
430 Self::all(Override::DefaultEnvVar)
431 }
432}
433
434#[derive(Clone, Eq, PartialEq, Hash, Debug, Deserialize)]
436pub struct Linux {
437 pub version: Version,
439}
440
441impl Linux {
442 pub fn current() -> Result<Option<Self>, ParseLinuxVersionError> {
448 Ok(linux::linux_version()?.map(|version| Self { version }))
449 }
450}
451
452impl From<Linux> for GenericVirtualPackage {
453 fn from(linux: Linux) -> Self {
454 GenericVirtualPackage {
455 name: PackageName::new_unchecked("__linux"),
456 version: linux.version,
457 build_string: "0".into(),
458 }
459 }
460}
461
462impl From<Linux> for VirtualPackage {
463 fn from(linux: Linux) -> Self {
464 VirtualPackage::Linux(linux)
465 }
466}
467
468impl From<Version> for Linux {
469 fn from(version: Version) -> Self {
470 Linux { version }
471 }
472}
473
474impl EnvOverride for Linux {
475 const DEFAULT_ENV_NAME: &'static str = "CONDA_OVERRIDE_LINUX";
476
477 fn parse_version(env_var_value: &str) -> Result<Self, ParseVersionError> {
478 Version::from_str(env_var_value).map(Self::from)
479 }
480
481 fn detect_from_host() -> Result<Option<Self>, DetectVirtualPackageError> {
482 Ok(Self::current()?)
483 }
484}
485
486#[derive(Clone, Eq, PartialEq, Hash, Debug, Deserialize)]
488pub struct LibC {
489 pub family: String,
491
492 pub version: Version,
494}
495
496impl LibC {
497 pub fn current() -> Result<Option<Self>, DetectLibCError> {
503 Ok(libc::libc_family_and_version()?.map(|(family, version)| Self { family, version }))
504 }
505}
506
507#[allow(clippy::fallible_impl_from)]
508impl From<LibC> for GenericVirtualPackage {
509 fn from(libc: LibC) -> Self {
510 GenericVirtualPackage {
511 name: format!(
512 "__{}",
513 libc.family.to_lowercase().replace(
514 |c: char| !c.is_ascii_alphanumeric() && c != '-' && c != '_',
515 "_"
516 )
517 )
518 .try_into()
519 .unwrap(),
520 version: libc.version,
521 build_string: "0".into(),
522 }
523 }
524}
525
526impl From<LibC> for VirtualPackage {
527 fn from(libc: LibC) -> Self {
528 VirtualPackage::LibC(libc)
529 }
530}
531
532impl EnvOverride for LibC {
533 const DEFAULT_ENV_NAME: &'static str = "CONDA_OVERRIDE_GLIBC";
534
535 fn parse_version(env_var_value: &str) -> Result<Self, ParseVersionError> {
536 Version::from_str(env_var_value).map(|version| Self {
537 family: "glibc".into(),
538 version,
539 })
540 }
541
542 fn detect_from_host() -> Result<Option<Self>, DetectVirtualPackageError> {
543 Ok(Self::current()?)
544 }
545}
546
547impl fmt::Display for LibC {
548 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
549 write!(f, "{}={}", self.family, self.version)
550 }
551}
552
553#[derive(Clone, Eq, PartialEq, Hash, Debug, Deserialize)]
555pub struct Cuda {
556 pub version: Version,
558}
559
560impl Cuda {
561 pub fn current() -> Option<Self> {
563 cuda::cuda_version().map(|version| Self { version })
564 }
565}
566
567impl From<Version> for Cuda {
568 fn from(version: Version) -> Self {
569 Self { version }
570 }
571}
572
573impl EnvOverride for Cuda {
574 fn parse_version(env_var_value: &str) -> Result<Self, ParseVersionError> {
575 Version::from_str(env_var_value).map(|version| Self { version })
576 }
577 fn detect_from_host() -> Result<Option<Self>, DetectVirtualPackageError> {
578 Ok(Self::current())
579 }
580 const DEFAULT_ENV_NAME: &'static str = "CONDA_OVERRIDE_CUDA";
581}
582
583impl From<Cuda> for GenericVirtualPackage {
584 fn from(cuda: Cuda) -> Self {
585 GenericVirtualPackage {
586 name: PackageName::new_unchecked("__cuda"),
587 version: cuda.version,
588 build_string: "0".into(),
589 }
590 }
591}
592
593impl From<Cuda> for VirtualPackage {
594 fn from(cuda: Cuda) -> Self {
595 VirtualPackage::Cuda(cuda)
596 }
597}
598
599#[derive(Clone, Debug)]
601pub enum Archspec {
602 Microarchitecture(Arc<Microarchitecture>),
604
605 Unknown,
607}
608
609impl Serialize for Archspec {
610 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
611 where
612 S: Serializer,
613 {
614 self.as_str().serialize(serializer)
615 }
616}
617
618impl<'de> Deserialize<'de> for Archspec {
619 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
620 where
621 D: Deserializer<'de>,
622 {
623 let name = Cow::<'de, str>::deserialize(deserializer)?;
624 if name == "0" {
625 Ok(Self::Unknown)
626 } else {
627 Ok(Self::from_name(&name))
628 }
629 }
630}
631
632impl Hash for Archspec {
633 fn hash<H: Hasher>(&self, state: &mut H) {
634 self.as_str().hash(state);
635 }
636}
637
638impl PartialEq<Self> for Archspec {
639 fn eq(&self, other: &Self) -> bool {
640 self.as_str() == other.as_str()
641 }
642}
643
644impl Eq for Archspec {}
645
646impl From<Arc<Microarchitecture>> for Archspec {
647 fn from(arch: Arc<Microarchitecture>) -> Self {
648 Self::Microarchitecture(arch)
649 }
650}
651
652impl Display for Archspec {
653 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
654 write!(f, "{}", self.as_str())
655 }
656}
657
658impl Archspec {
659 pub fn as_str(&self) -> &str {
661 match self {
662 Archspec::Microarchitecture(arch) => arch.name(),
663 Archspec::Unknown => "0",
664 }
665 }
666
667 pub fn current() -> Self {
670 archspec::cpu::host()
671 .ok()
672 .map(Into::into)
673 .or_else(|| Self::from_platform(Platform::current()))
674 .unwrap_or(Archspec::Unknown)
675 }
676
677 #[allow(clippy::match_same_arms)]
680 pub fn from_platform(platform: Platform) -> Option<Self> {
681 let archspec_name = match platform {
684 Platform::NoArch | Platform::Unknown => return None,
685 Platform::EmscriptenWasm32 | Platform::WasiWasm32 => return None,
686 Platform::Win32 | Platform::Linux32 => "x86",
687 Platform::Win64 | Platform::Osx64 | Platform::Linux64 => "x86_64",
688 Platform::LinuxAarch64 | Platform::LinuxArmV6l | Platform::LinuxArmV7l => "aarch64",
689 Platform::LinuxLoongArch64 => "loongarch64",
690 Platform::LinuxPpc64le => "ppc64le",
691 Platform::LinuxPpc64 => "ppc64",
692 Platform::LinuxPpc => "ppc",
693 Platform::LinuxS390X => "s390x",
694 Platform::LinuxRiscv32 => "riscv32",
695 Platform::LinuxRiscv64 => "riscv64",
696 Platform::ZosZ => return None,
698
699 Platform::WinArm64 => "aarch64",
701
702 Platform::OsxArm64 => "m1",
704
705 _ => return None,
707 };
708
709 Some(Self::from_name(archspec_name))
710 }
711
712 pub fn from_name(archspec_name: &str) -> Self {
715 Microarchitecture::known_targets()
716 .get(archspec_name)
717 .cloned()
718 .unwrap_or_else(|| Arc::new(archspec::cpu::Microarchitecture::generic(archspec_name)))
719 .into()
720 }
721}
722
723impl From<Archspec> for GenericVirtualPackage {
724 fn from(archspec: Archspec) -> Self {
725 GenericVirtualPackage {
726 name: PackageName::new_unchecked("__archspec"),
727 version: Version::major(1),
728 build_string: archspec.to_string(),
729 }
730 }
731}
732
733impl From<Archspec> for VirtualPackage {
734 fn from(archspec: Archspec) -> Self {
735 VirtualPackage::Archspec(archspec)
736 }
737}
738
739impl EnvOverride for Archspec {
740 fn parse_version(value: &str) -> Result<Self, ParseVersionError> {
741 if value == "0" {
742 Ok(Archspec::Unknown)
743 } else {
744 Ok(Self::from_name(value))
745 }
746 }
747
748 const DEFAULT_ENV_NAME: &'static str = "CONDA_OVERRIDE_ARCHSPEC";
749
750 fn detect_from_host() -> Result<Option<Self>, DetectVirtualPackageError> {
751 Ok(Some(Self::current()))
752 }
753}
754
755#[derive(Clone, Eq, PartialEq, Hash, Debug, Deserialize)]
757pub struct Osx {
758 pub version: Version,
760}
761
762impl Osx {
763 pub fn current() -> Result<Option<Self>, ParseOsxVersionError> {
768 Ok(osx::osx_version()?.map(|version| Self { version }))
769 }
770}
771
772impl From<Osx> for GenericVirtualPackage {
773 fn from(osx: Osx) -> Self {
774 GenericVirtualPackage {
775 name: PackageName::new_unchecked("__osx"),
776 version: osx.version,
777 build_string: "0".into(),
778 }
779 }
780}
781
782impl From<Osx> for VirtualPackage {
783 fn from(osx: Osx) -> Self {
784 VirtualPackage::Osx(osx)
785 }
786}
787
788impl From<Version> for Osx {
789 fn from(version: Version) -> Self {
790 Self { version }
791 }
792}
793
794impl EnvOverride for Osx {
795 fn parse_version(env_var_value: &str) -> Result<Self, ParseVersionError> {
796 Version::from_str(env_var_value).map(|version| Self { version })
797 }
798 fn detect_from_host() -> Result<Option<Self>, DetectVirtualPackageError> {
799 Ok(Self::current()?)
800 }
801 const DEFAULT_ENV_NAME: &'static str = "CONDA_OVERRIDE_OSX";
802}
803
804#[derive(Clone, Eq, PartialEq, Hash, Debug, Deserialize)]
806pub struct Windows {
807 pub version: Option<Version>,
809}
810
811impl Windows {
812 pub fn current() -> Option<Self> {
816 if cfg!(target_os = "windows") {
817 Some(Self {
818 version: win::windows_version(),
819 })
820 } else {
821 None
822 }
823 }
824}
825
826impl From<Windows> for GenericVirtualPackage {
827 fn from(windows: Windows) -> Self {
828 GenericVirtualPackage {
829 name: PackageName::new_unchecked("__win"),
830 version: windows.version.unwrap_or_else(|| Version::major(0)),
831 build_string: "0".into(),
832 }
833 }
834}
835
836impl From<Windows> for VirtualPackage {
837 fn from(windows: Windows) -> Self {
838 VirtualPackage::Win(windows)
839 }
840}
841
842impl From<Version> for Windows {
843 fn from(version: Version) -> Self {
844 Self {
845 version: Some(version),
846 }
847 }
848}
849
850impl EnvOverride for Windows {
851 fn parse_version(env_var_value: &str) -> Result<Self, ParseVersionError> {
852 Version::from_str(env_var_value).map(|version| Self {
853 version: Some(version),
854 })
855 }
856 fn detect_from_host() -> Result<Option<Self>, DetectVirtualPackageError> {
857 Ok(Self::current())
858 }
859 const DEFAULT_ENV_NAME: &'static str = "CONDA_OVERRIDE_WIN";
860}
861
862#[cfg(test)]
863mod test {
864 use std::{env, str::FromStr};
865
866 use rattler_conda_types::Version;
867
868 use super::*;
869
870 #[test]
871 fn doesnt_crash() {
872 let virtual_packages =
873 VirtualPackages::detect(&VirtualPackageOverrides::default()).unwrap();
874 println!("{virtual_packages:#?}");
875 }
876 #[test]
877 fn parse_libc() {
878 let v = "1.23";
879 let res = LibC {
880 version: Version::from_str(v).unwrap(),
881 family: "glibc".into(),
882 };
883 let env_var_name = format!("{}_{}", LibC::DEFAULT_ENV_NAME, "12345511231");
884 env::set_var(env_var_name.clone(), v);
885 assert_eq!(
886 LibC::detect(Some(&Override::EnvVar(env_var_name.clone())))
887 .unwrap()
888 .unwrap(),
889 res
890 );
891 env::set_var(env_var_name.clone(), "");
892 assert_eq!(
893 LibC::detect(Some(&Override::EnvVar(env_var_name.clone()))).unwrap(),
894 None
895 );
896 env::remove_var(env_var_name.clone());
897 assert_eq!(
898 LibC::detect_with_fallback(&Override::DefaultEnvVar, || Ok(Some(res.clone())))
899 .unwrap()
900 .unwrap(),
901 res
902 );
903 assert_eq!(
904 LibC::detect_with_fallback(&Override::String(v.to_string()), || Ok(None))
905 .unwrap()
906 .unwrap(),
907 res
908 );
909 }
910
911 #[test]
912 fn parse_libc_invalid_family_chars() {
913 let libc = LibC {
914 family: "glibc 2.34 (Ubuntu)".into(),
915 version: Version::from_str("2.34").unwrap(),
916 };
917 let pkg: GenericVirtualPackage = libc.into();
918 assert_eq!(pkg.name.as_normalized(), "__glibc_2_34__ubuntu_");
919 }
920
921 #[test]
922 fn parse_cuda() {
923 let v = "1.234";
924 let res = Cuda {
925 version: Version::from_str(v).unwrap(),
926 };
927 let env_var_name = format!("{}_{}", Cuda::DEFAULT_ENV_NAME, "12345511231");
928 env::set_var(env_var_name.clone(), v);
929 assert_eq!(
930 Cuda::detect(Some(&Override::EnvVar(env_var_name.clone())))
931 .unwrap()
932 .unwrap(),
933 res
934 );
935 assert_eq!(
936 Cuda::detect(None).map_err(|_x| 1),
937 <Cuda as EnvOverride>::detect_from_host().map_err(|_x| 1)
938 );
939 env::remove_var(env_var_name.clone());
940 assert_eq!(
941 Cuda::detect(Some(&Override::String(v.to_string())))
942 .unwrap()
943 .unwrap(),
944 res
945 );
946 }
947
948 #[test]
949 fn parse_osx() {
950 let v = "2.345";
951 let res = Osx {
952 version: Version::from_str(v).unwrap(),
953 };
954 let env_var_name = format!("{}_{}", Osx::DEFAULT_ENV_NAME, "12345511231");
955 env::set_var(env_var_name.clone(), v);
956 assert_eq!(
957 Osx::detect(Some(&Override::EnvVar(env_var_name.clone())))
958 .unwrap()
959 .unwrap(),
960 res
961 );
962 }
963
964 #[test]
965 fn test_cross_platform_virtual_packages() {
966 let overrides = VirtualPackageOverrides::default();
968
969 let linux_packages =
971 VirtualPackages::detect_for_platform(Platform::Linux64, &overrides).unwrap();
972 let linux_names: Vec<String> = linux_packages
973 .into_generic_virtual_packages()
974 .map(|pkg| pkg.name.as_normalized().to_string())
975 .collect();
976 assert!(linux_names.contains(&"__linux".to_string()));
977 assert!(linux_names.contains(&"__glibc".to_string()));
978 assert!(linux_names.contains(&"__archspec".to_string()));
979 assert!(linux_names.contains(&"__unix".to_string()));
980
981 let osx_packages =
983 VirtualPackages::detect_for_platform(Platform::OsxArm64, &overrides).unwrap();
984 let osx_names: Vec<String> = osx_packages
985 .into_generic_virtual_packages()
986 .map(|pkg| pkg.name.as_normalized().to_string())
987 .collect();
988 assert!(osx_names.contains(&"__osx".to_string()));
989 assert!(osx_names.contains(&"__archspec".to_string()));
990 assert!(osx_names.contains(&"__unix".to_string()));
991
992 let win_packages =
994 VirtualPackages::detect_for_platform(Platform::Win64, &overrides).unwrap();
995 let win_names: Vec<String> = win_packages
996 .into_generic_virtual_packages()
997 .map(|pkg| pkg.name.as_normalized().to_string())
998 .collect();
999 assert!(win_names.contains(&"__win".to_string()));
1000 assert!(!win_names.contains(&"__unix".to_string()));
1001 assert!(win_names.contains(&"__archspec".to_string()));
1002 }
1003}