-
Notifications
You must be signed in to change notification settings - Fork 206
/
virtual_packages.rs
229 lines (201 loc) · 8.7 KB
/
virtual_packages.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
use super::manifest::{LibCSystemRequirement, SystemRequirements};
use crate::project::errors::UnsupportedPlatformError;
use crate::project::Environment;
use itertools::Itertools;
use miette::Diagnostic;
use rattler_conda_types::{GenericVirtualPackage, Platform, Version};
use rattler_virtual_packages::{
Archspec, Cuda, DetectVirtualPackageError, LibC, Linux, Osx, VirtualPackage,
};
use std::collections::HashMap;
use thiserror::Error;
/// The default GLIBC version to use. This is used when no system requirements are specified.
pub fn default_glibc_version() -> Version {
"2.17".parse().unwrap()
}
/// The default linux version to use. This is used when no system requirements are specified.
pub fn default_linux_version() -> Version {
"5.10".parse().unwrap()
}
/// Returns the default Mac OS version for the specified platform. The platform must refer to a
/// MacOS platform.
pub fn default_mac_os_version(platform: Platform) -> Version {
match platform {
Platform::OsxArm64 => "11.0".parse().unwrap(),
Platform::Osx64 => "10.15".parse().unwrap(),
_ => panic!(
"default_mac_os_version() called with non-osx platform: {}",
platform
),
}
}
/// Returns a reasonable modern set of virtual packages that should be safe enough to assume.
/// At the time of writing, this is in sync with the conda-lock set of minimal virtual packages.
/// <https://github.com/conda/conda-lock/blob/3d36688278ebf4f65281de0846701d61d6017ed2/conda_lock/virtual_package.py#L175>
///
/// The method also takes into account system requirements specified in the project manifest.
pub fn get_minimal_virtual_packages(
platform: Platform,
system_requirements: &SystemRequirements,
) -> Vec<VirtualPackage> {
// TODO: How to add a default cuda requirements
let mut virtual_packages: Vec<VirtualPackage> = vec![];
// Match high level platforms
if platform.is_unix() {
virtual_packages.push(VirtualPackage::Unix);
}
if platform.is_linux() {
let version = system_requirements
.linux
.clone()
.unwrap_or(default_linux_version());
virtual_packages.push(VirtualPackage::Linux(Linux { version }));
let (family, version) = system_requirements
.libc
.as_ref()
.map(LibCSystemRequirement::family_and_version)
.map(|(family, version)| (family.to_string(), version.clone()))
.unwrap_or(("glibc".parse().unwrap(), default_glibc_version()));
virtual_packages.push(VirtualPackage::LibC(LibC { family, version }));
}
if platform.is_windows() {
virtual_packages.push(VirtualPackage::Win);
}
// Add platform specific packages
if platform.is_osx() {
let version = system_requirements
.macos
.clone()
.unwrap_or_else(|| default_mac_os_version(platform));
virtual_packages.push(VirtualPackage::Osx(Osx { version }));
}
// Cuda
if let Some(version) = system_requirements.cuda.clone() {
virtual_packages.push(VirtualPackage::Cuda(Cuda { version }));
}
// Archspec is only based on the platform for now
if let Some(spec) = Archspec::from_platform(platform) {
virtual_packages.push(VirtualPackage::Archspec(spec));
}
virtual_packages
}
impl Environment<'_> {
/// Returns the set of virtual packages to use for the specified platform. This method
/// takes into account the system requirements specified in the project manifest.
pub fn virtual_packages(&self, platform: Platform) -> Vec<VirtualPackage> {
get_minimal_virtual_packages(platform, &self.system_requirements())
}
}
/// An error that occurs when the current platform does not satisfy the minimal virtual package
/// requirements.
#[derive(Debug, Error, Diagnostic)]
pub enum VerifyCurrentPlatformError {
#[error("The current platform does not satisfy the minimal virtual package requirements")]
UnsupportedPlatform(#[from] Box<UnsupportedPlatformError>),
#[error(transparent)]
DetectionVirtualPackagesError(#[from] DetectVirtualPackageError),
#[error("The current system has a mismatching virtual package. The project requires '{required}' to be on build '{required_build_string}' but the system has build '{local_build_string}'")]
MismatchingBuildString {
required: String,
required_build_string: String,
local_build_string: String,
},
#[error("The current system has a mismatching virtual package. The project requires '{required}' to be at least version '{required_version}' but the system has version '{local_version}'")]
MismatchingVersion {
required: String,
required_version: Box<Version>,
local_version: Box<Version>,
},
#[error("The platform you are running on should at least have the virtual package {required} on version {required_version}, build_string: {required_build_string}")]
MissingVirtualPackage {
required: String,
required_version: Box<Version>,
required_build_string: String,
},
}
/// Verifies if the current platform satisfies the minimal virtual package requirements.
pub fn verify_current_platform_has_required_virtual_packages(
environment: &Environment<'_>,
) -> Result<(), VerifyCurrentPlatformError> {
let current_platform = Platform::current();
// Is the current platform in the list of supported platforms?
if !environment.platforms().contains(¤t_platform) {
return Err(VerifyCurrentPlatformError::from(Box::new(
UnsupportedPlatformError {
environments_platforms: environment.platforms().into_iter().collect_vec(),
platform: current_platform,
environment: environment.name().clone(),
},
)));
}
let system_virtual_packages = VirtualPackage::current()?
.iter()
.cloned()
.map(GenericVirtualPackage::from)
.map(|vpkg| (vpkg.name.clone(), vpkg))
.collect::<HashMap<_, _>>();
let required_pkgs = environment
.virtual_packages(current_platform)
.into_iter()
.map(GenericVirtualPackage::from);
// Check for every local minimum package if it is available and on the correct version.
for req_pkg in required_pkgs {
if req_pkg.name.as_normalized() == "__archspec" {
// Skip archspec packages completely for now.
continue;
}
if let Some(local_vpkg) = system_virtual_packages.get(&req_pkg.name) {
if req_pkg.build_string != local_vpkg.build_string {
return Err(VerifyCurrentPlatformError::MismatchingBuildString {
required: req_pkg.name.as_source().to_string(),
required_build_string: req_pkg.build_string.clone(),
local_build_string: local_vpkg.build_string.clone(),
});
}
if req_pkg.version > local_vpkg.version {
// This case can simply happen because the default system requirements in get_minimal_virtual_packages() is higher than required.
return Err(VerifyCurrentPlatformError::MismatchingVersion {
required: req_pkg.name.as_source().to_string(),
required_version: Box::from(req_pkg.version),
local_version: Box::from(local_vpkg.version.clone()),
});
}
} else {
return Err(VerifyCurrentPlatformError::MissingVirtualPackage {
required: req_pkg.name.as_source().to_string(),
required_version: Box::from(req_pkg.version),
required_build_string: req_pkg.build_string.clone(),
});
}
}
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
use crate::project::manifest::SystemRequirements;
use insta::assert_debug_snapshot;
use rattler_conda_types::Platform;
// Regression test on the virtual packages so there is not accidental changes
#[test]
fn test_get_minimal_virtual_packages() {
let platforms = vec![
Platform::NoArch,
Platform::Linux64,
Platform::LinuxAarch64,
Platform::LinuxPpc64le,
Platform::Osx64,
Platform::OsxArm64,
Platform::Win64,
];
let system_requirements = SystemRequirements::default();
for platform in platforms {
let packages = get_minimal_virtual_packages(platform, &system_requirements)
.into_iter()
.map(GenericVirtualPackage::from)
.collect_vec();
let snapshot_name = format!("test_get_minimal_virtual_packages.{}", platform);
assert_debug_snapshot!(snapshot_name, packages);
}
}
}