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!("__{}", libc.family.to_lowercase())
514 .try_into()
515 .unwrap(),
516 version: libc.version,
517 build_string: "0".into(),
518 }
519 }
520}
521
522impl From<LibC> for VirtualPackage {
523 fn from(libc: LibC) -> Self {
524 VirtualPackage::LibC(libc)
525 }
526}
527
528impl EnvOverride for LibC {
529 const DEFAULT_ENV_NAME: &'static str = "CONDA_OVERRIDE_GLIBC";
530
531 fn parse_version(env_var_value: &str) -> Result<Self, ParseVersionError> {
532 Version::from_str(env_var_value).map(|version| Self {
533 family: "glibc".into(),
534 version,
535 })
536 }
537
538 fn detect_from_host() -> Result<Option<Self>, DetectVirtualPackageError> {
539 Ok(Self::current()?)
540 }
541}
542
543impl fmt::Display for LibC {
544 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
545 write!(f, "{}={}", self.family, self.version)
546 }
547}
548
549#[derive(Clone, Eq, PartialEq, Hash, Debug, Deserialize)]
551pub struct Cuda {
552 pub version: Version,
554}
555
556impl Cuda {
557 pub fn current() -> Option<Self> {
559 cuda::cuda_version().map(|version| Self { version })
560 }
561}
562
563impl From<Version> for Cuda {
564 fn from(version: Version) -> Self {
565 Self { version }
566 }
567}
568
569impl EnvOverride for Cuda {
570 fn parse_version(env_var_value: &str) -> Result<Self, ParseVersionError> {
571 Version::from_str(env_var_value).map(|version| Self { version })
572 }
573 fn detect_from_host() -> Result<Option<Self>, DetectVirtualPackageError> {
574 Ok(Self::current())
575 }
576 const DEFAULT_ENV_NAME: &'static str = "CONDA_OVERRIDE_CUDA";
577}
578
579impl From<Cuda> for GenericVirtualPackage {
580 fn from(cuda: Cuda) -> Self {
581 GenericVirtualPackage {
582 name: PackageName::new_unchecked("__cuda"),
583 version: cuda.version,
584 build_string: "0".into(),
585 }
586 }
587}
588
589impl From<Cuda> for VirtualPackage {
590 fn from(cuda: Cuda) -> Self {
591 VirtualPackage::Cuda(cuda)
592 }
593}
594
595#[derive(Clone, Debug)]
597pub enum Archspec {
598 Microarchitecture(Arc<Microarchitecture>),
600
601 Unknown,
603}
604
605impl Serialize for Archspec {
606 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
607 where
608 S: Serializer,
609 {
610 self.as_str().serialize(serializer)
611 }
612}
613
614impl<'de> Deserialize<'de> for Archspec {
615 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
616 where
617 D: Deserializer<'de>,
618 {
619 let name = Cow::<'de, str>::deserialize(deserializer)?;
620 if name == "0" {
621 Ok(Self::Unknown)
622 } else {
623 Ok(Self::from_name(&name))
624 }
625 }
626}
627
628impl Hash for Archspec {
629 fn hash<H: Hasher>(&self, state: &mut H) {
630 self.as_str().hash(state);
631 }
632}
633
634impl PartialEq<Self> for Archspec {
635 fn eq(&self, other: &Self) -> bool {
636 self.as_str() == other.as_str()
637 }
638}
639
640impl Eq for Archspec {}
641
642impl From<Arc<Microarchitecture>> for Archspec {
643 fn from(arch: Arc<Microarchitecture>) -> Self {
644 Self::Microarchitecture(arch)
645 }
646}
647
648impl Display for Archspec {
649 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
650 write!(f, "{}", self.as_str())
651 }
652}
653
654impl Archspec {
655 pub fn as_str(&self) -> &str {
657 match self {
658 Archspec::Microarchitecture(arch) => arch.name(),
659 Archspec::Unknown => "0",
660 }
661 }
662
663 pub fn current() -> Self {
666 archspec::cpu::host()
667 .ok()
668 .map(Into::into)
669 .or_else(|| Self::from_platform(Platform::current()))
670 .unwrap_or(Archspec::Unknown)
671 }
672
673 #[allow(clippy::match_same_arms)]
676 pub fn from_platform(platform: Platform) -> Option<Self> {
677 let archspec_name = match platform {
680 Platform::NoArch | Platform::Unknown => return None,
681 Platform::EmscriptenWasm32 | Platform::WasiWasm32 => return None,
682 Platform::Win32 | Platform::Linux32 => "x86",
683 Platform::Win64 | Platform::Osx64 | Platform::Linux64 => "x86_64",
684 Platform::LinuxAarch64 | Platform::LinuxArmV6l | Platform::LinuxArmV7l => "aarch64",
685 Platform::LinuxLoong64 => "loong64",
686 Platform::LinuxPpc64le => "ppc64le",
687 Platform::LinuxPpc64 => "ppc64",
688 Platform::LinuxPpc => "ppc",
689 Platform::LinuxS390X => "s390x",
690 Platform::LinuxRiscv32 => "riscv32",
691 Platform::LinuxRiscv64 => "riscv64",
692 Platform::ZosZ => return None,
694
695 Platform::WinArm64 => "aarch64",
697
698 Platform::OsxArm64 => "m1",
700
701 _ => return None,
703 };
704
705 Some(Self::from_name(archspec_name))
706 }
707
708 pub fn from_name(archspec_name: &str) -> Self {
711 Microarchitecture::known_targets()
712 .get(archspec_name)
713 .cloned()
714 .unwrap_or_else(|| Arc::new(archspec::cpu::Microarchitecture::generic(archspec_name)))
715 .into()
716 }
717}
718
719impl From<Archspec> for GenericVirtualPackage {
720 fn from(archspec: Archspec) -> Self {
721 GenericVirtualPackage {
722 name: PackageName::new_unchecked("__archspec"),
723 version: Version::major(1),
724 build_string: archspec.to_string(),
725 }
726 }
727}
728
729impl From<Archspec> for VirtualPackage {
730 fn from(archspec: Archspec) -> Self {
731 VirtualPackage::Archspec(archspec)
732 }
733}
734
735impl EnvOverride for Archspec {
736 fn parse_version(value: &str) -> Result<Self, ParseVersionError> {
737 if value == "0" {
738 Ok(Archspec::Unknown)
739 } else {
740 Ok(Self::from_name(value))
741 }
742 }
743
744 const DEFAULT_ENV_NAME: &'static str = "CONDA_OVERRIDE_ARCHSPEC";
745
746 fn detect_from_host() -> Result<Option<Self>, DetectVirtualPackageError> {
747 Ok(Some(Self::current()))
748 }
749}
750
751#[derive(Clone, Eq, PartialEq, Hash, Debug, Deserialize)]
753pub struct Osx {
754 pub version: Version,
756}
757
758impl Osx {
759 pub fn current() -> Result<Option<Self>, ParseOsxVersionError> {
764 Ok(osx::osx_version()?.map(|version| Self { version }))
765 }
766}
767
768impl From<Osx> for GenericVirtualPackage {
769 fn from(osx: Osx) -> Self {
770 GenericVirtualPackage {
771 name: PackageName::new_unchecked("__osx"),
772 version: osx.version,
773 build_string: "0".into(),
774 }
775 }
776}
777
778impl From<Osx> for VirtualPackage {
779 fn from(osx: Osx) -> Self {
780 VirtualPackage::Osx(osx)
781 }
782}
783
784impl From<Version> for Osx {
785 fn from(version: Version) -> Self {
786 Self { version }
787 }
788}
789
790impl EnvOverride for Osx {
791 fn parse_version(env_var_value: &str) -> Result<Self, ParseVersionError> {
792 Version::from_str(env_var_value).map(|version| Self { version })
793 }
794 fn detect_from_host() -> Result<Option<Self>, DetectVirtualPackageError> {
795 Ok(Self::current()?)
796 }
797 const DEFAULT_ENV_NAME: &'static str = "CONDA_OVERRIDE_OSX";
798}
799
800#[derive(Clone, Eq, PartialEq, Hash, Debug, Deserialize)]
802pub struct Windows {
803 pub version: Option<Version>,
805}
806
807impl Windows {
808 pub fn current() -> Option<Self> {
812 if cfg!(target_os = "windows") {
813 Some(Self {
814 version: win::windows_version(),
815 })
816 } else {
817 None
818 }
819 }
820}
821
822impl From<Windows> for GenericVirtualPackage {
823 fn from(windows: Windows) -> Self {
824 GenericVirtualPackage {
825 name: PackageName::new_unchecked("__win"),
826 version: windows.version.unwrap_or_else(|| Version::major(0)),
827 build_string: "0".into(),
828 }
829 }
830}
831
832impl From<Windows> for VirtualPackage {
833 fn from(windows: Windows) -> Self {
834 VirtualPackage::Win(windows)
835 }
836}
837
838impl From<Version> for Windows {
839 fn from(version: Version) -> Self {
840 Self {
841 version: Some(version),
842 }
843 }
844}
845
846impl EnvOverride for Windows {
847 fn parse_version(env_var_value: &str) -> Result<Self, ParseVersionError> {
848 Version::from_str(env_var_value).map(|version| Self {
849 version: Some(version),
850 })
851 }
852 fn detect_from_host() -> Result<Option<Self>, DetectVirtualPackageError> {
853 Ok(Self::current())
854 }
855 const DEFAULT_ENV_NAME: &'static str = "CONDA_OVERRIDE_WIN";
856}
857
858#[cfg(test)]
859mod test {
860 use std::{env, str::FromStr};
861
862 use rattler_conda_types::Version;
863
864 use super::*;
865
866 #[test]
867 fn doesnt_crash() {
868 let virtual_packages =
869 VirtualPackages::detect(&VirtualPackageOverrides::default()).unwrap();
870 println!("{virtual_packages:#?}");
871 }
872 #[test]
873 fn parse_libc() {
874 let v = "1.23";
875 let res = LibC {
876 version: Version::from_str(v).unwrap(),
877 family: "glibc".into(),
878 };
879 let env_var_name = format!("{}_{}", LibC::DEFAULT_ENV_NAME, "12345511231");
880 env::set_var(env_var_name.clone(), v);
881 assert_eq!(
882 LibC::detect(Some(&Override::EnvVar(env_var_name.clone())))
883 .unwrap()
884 .unwrap(),
885 res
886 );
887 env::set_var(env_var_name.clone(), "");
888 assert_eq!(
889 LibC::detect(Some(&Override::EnvVar(env_var_name.clone()))).unwrap(),
890 None
891 );
892 env::remove_var(env_var_name.clone());
893 assert_eq!(
894 LibC::detect_with_fallback(&Override::DefaultEnvVar, || Ok(Some(res.clone())))
895 .unwrap()
896 .unwrap(),
897 res
898 );
899 assert_eq!(
900 LibC::detect_with_fallback(&Override::String(v.to_string()), || Ok(None))
901 .unwrap()
902 .unwrap(),
903 res
904 );
905 }
906
907 #[test]
908 fn parse_cuda() {
909 let v = "1.234";
910 let res = Cuda {
911 version: Version::from_str(v).unwrap(),
912 };
913 let env_var_name = format!("{}_{}", Cuda::DEFAULT_ENV_NAME, "12345511231");
914 env::set_var(env_var_name.clone(), v);
915 assert_eq!(
916 Cuda::detect(Some(&Override::EnvVar(env_var_name.clone())))
917 .unwrap()
918 .unwrap(),
919 res
920 );
921 assert_eq!(
922 Cuda::detect(None).map_err(|_x| 1),
923 <Cuda as EnvOverride>::detect_from_host().map_err(|_x| 1)
924 );
925 env::remove_var(env_var_name.clone());
926 assert_eq!(
927 Cuda::detect(Some(&Override::String(v.to_string())))
928 .unwrap()
929 .unwrap(),
930 res
931 );
932 }
933
934 #[test]
935 fn parse_osx() {
936 let v = "2.345";
937 let res = Osx {
938 version: Version::from_str(v).unwrap(),
939 };
940 let env_var_name = format!("{}_{}", Osx::DEFAULT_ENV_NAME, "12345511231");
941 env::set_var(env_var_name.clone(), v);
942 assert_eq!(
943 Osx::detect(Some(&Override::EnvVar(env_var_name.clone())))
944 .unwrap()
945 .unwrap(),
946 res
947 );
948 }
949
950 #[test]
951 fn test_cross_platform_virtual_packages() {
952 let overrides = VirtualPackageOverrides::default();
954
955 let linux_packages =
957 VirtualPackages::detect_for_platform(Platform::Linux64, &overrides).unwrap();
958 let linux_names: Vec<String> = linux_packages
959 .into_generic_virtual_packages()
960 .map(|pkg| pkg.name.as_normalized().to_string())
961 .collect();
962 assert!(linux_names.contains(&"__linux".to_string()));
963 assert!(linux_names.contains(&"__glibc".to_string()));
964 assert!(linux_names.contains(&"__archspec".to_string()));
965 assert!(linux_names.contains(&"__unix".to_string()));
966
967 let osx_packages =
969 VirtualPackages::detect_for_platform(Platform::OsxArm64, &overrides).unwrap();
970 let osx_names: Vec<String> = osx_packages
971 .into_generic_virtual_packages()
972 .map(|pkg| pkg.name.as_normalized().to_string())
973 .collect();
974 assert!(osx_names.contains(&"__osx".to_string()));
975 assert!(osx_names.contains(&"__archspec".to_string()));
976 assert!(osx_names.contains(&"__unix".to_string()));
977
978 let win_packages =
980 VirtualPackages::detect_for_platform(Platform::Win64, &overrides).unwrap();
981 let win_names: Vec<String> = win_packages
982 .into_generic_virtual_packages()
983 .map(|pkg| pkg.name.as_normalized().to_string())
984 .collect();
985 assert!(win_names.contains(&"__win".to_string()));
986 assert!(!win_names.contains(&"__unix".to_string()));
987 assert!(win_names.contains(&"__archspec".to_string()));
988 }
989}