diff --git a/crates/pixi_manifest/src/snapshots/pixi_manifest__system_requirements__tests__serialization.snap b/crates/pixi_manifest/src/snapshots/pixi_manifest__system_requirements__tests__serialization.snap new file mode 100644 index 000000000..ad72095c6 --- /dev/null +++ b/crates/pixi_manifest/src/snapshots/pixi_manifest__system_requirements__tests__serialization.snap @@ -0,0 +1,10 @@ +--- +source: crates/pixi_manifest/src/system_requirements.rs +expression: serialized +snapshot_kind: text +--- +macos = "10.15" +linux = "5.11" +cuda = "12.2" +libc = "2.12" +archspec = "x86_64" diff --git a/crates/pixi_manifest/src/snapshots/pixi_manifest__system_requirements__tests__serialization_other_family.snap b/crates/pixi_manifest/src/snapshots/pixi_manifest__system_requirements__tests__serialization_other_family.snap new file mode 100644 index 000000000..234b0ce2b --- /dev/null +++ b/crates/pixi_manifest/src/snapshots/pixi_manifest__system_requirements__tests__serialization_other_family.snap @@ -0,0 +1,13 @@ +--- +source: crates/pixi_manifest/src/system_requirements.rs +expression: serialized +snapshot_kind: text +--- +macos = "10.15" +linux = "5.11" +cuda = "12.2" +archspec = "x86_64" + +[libc] +family = "glibc" +version = "2.12" diff --git a/crates/pixi_manifest/src/system_requirements.rs b/crates/pixi_manifest/src/system_requirements.rs index 303ac3a76..d345893d8 100644 --- a/crates/pixi_manifest/src/system_requirements.rs +++ b/crates/pixi_manifest/src/system_requirements.rs @@ -1,8 +1,10 @@ use miette::Diagnostic; use rattler_conda_types::Version; use rattler_virtual_packages::{Cuda, LibC, Linux, Osx, VirtualPackage}; -use serde::Deserialize; +use serde::{Deserialize, Serialize}; +use serde_value::Value; use serde_with::{serde_as, DisplayFromStr}; +use std::collections::BTreeMap; use std::str::FromStr; use thiserror::Error; @@ -10,7 +12,7 @@ const GLIBC_FAMILY: &str = "glibc"; /// Describes the minimal system requirements to be able to run a certain environment. #[serde_as] -#[derive(Debug, Clone, Deserialize, Default, PartialEq, Eq)] +#[derive(Debug, Clone, Deserialize, Serialize, Default, PartialEq, Eq)] #[serde(deny_unknown_fields)] pub struct SystemRequirements { /// Dictates the minimum version of macOS required. @@ -124,6 +126,15 @@ impl SystemRequirements { archspec, }) } + + /// Returns true if the system requirements are empty, meaning that no requirements were specified. + pub fn is_empty(&self) -> bool { + self.linux.is_none() + && self.cuda.is_none() + && self.macos.is_none() + && self.libc.is_none() + && self.archspec.is_none() + } } #[derive(Debug, Clone, Error, Diagnostic)] @@ -183,8 +194,33 @@ impl LibCSystemRequirement { } } +impl Serialize for LibCSystemRequirement { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + match self { + LibCSystemRequirement::GlibC(version) => serializer.serialize_str(&version.to_string()), + LibCSystemRequirement::OtherFamily(LibCFamilyAndVersion { family, version }) => { + let mut map = BTreeMap::new(); // Initialize the BTreeMap + if let Some(fam) = family { + map.insert( + Value::String("family".to_string()), + Value::String(fam.clone()), + ); + } + map.insert( + Value::String("version".to_string()), + Value::String(version.to_string()), + ); + Value::Map(map).serialize(serializer) // Wrap the map in Value::Map + } + } + } +} + #[serde_as] -#[derive(Debug, Clone, Deserialize)] +#[derive(Debug, Clone, Deserialize, Serialize)] #[serde(deny_unknown_fields)] pub struct LibCFamilyAndVersion { /// The libc family, e.g. glibc @@ -225,6 +261,7 @@ mod tests { use rattler_virtual_packages::{Cuda, LibC, Linux, Osx, VirtualPackage}; use serde::Deserialize; use std::str::FromStr; + use toml_edit::ser::to_string_pretty; #[test] fn system_requirements_works() { @@ -471,4 +508,37 @@ mod tests { assert_matches!(eglibc_2_17.union(&glibc_2_12).unwrap_err(), SystemRequirementsUnionError::DifferentLibcFamilies(fam_a, fam_b) if fam_a == "eglibc" && fam_b == "glibc"); } + + #[test] + fn test_serialization() { + let system_requirements = SystemRequirements { + macos: Some(Version::from_str("10.15").unwrap()), + linux: Some(Version::from_str("5.11").unwrap()), + cuda: Some(Version::from_str("12.2").unwrap()), + libc: Some(LibCSystemRequirement::GlibC( + Version::from_str("2.12").unwrap(), + )), + archspec: Some("x86_64".to_string()), + }; + + let serialized = to_string_pretty(&system_requirements).unwrap(); + assert_snapshot!(serialized); + } + + #[test] + fn test_serialization_other_family() { + let system_requirements = SystemRequirements { + macos: Some(Version::from_str("10.15").unwrap()), + linux: Some(Version::from_str("5.11").unwrap()), + cuda: Some(Version::from_str("12.2").unwrap()), + libc: Some(LibCSystemRequirement::OtherFamily(LibCFamilyAndVersion { + family: Some("glibc".to_string()), + version: Version::from_str("2.12").unwrap(), + })), + archspec: Some("x86_64".to_string()), + }; + + let serialized = to_string_pretty(&system_requirements).unwrap(); + assert_snapshot!(serialized); + } } diff --git a/src/cli/info.rs b/src/cli/info.rs index 82b905f00..3b413653b 100644 --- a/src/cli/info.rs +++ b/src/cli/info.rs @@ -1,12 +1,13 @@ use std::{fmt::Display, path::PathBuf}; +use crate::cli::cli_config::ProjectConfig; use chrono::{DateTime, Local}; use clap::Parser; use itertools::Itertools; use miette::IntoDiagnostic; use pixi_config; use pixi_consts::consts; -use pixi_manifest::{EnvironmentName, FeatureName}; +use pixi_manifest::{EnvironmentName, FeatureName, SystemRequirements}; use pixi_manifest::{FeaturesExt, HasFeaturesIter}; use pixi_progress::await_in_progress; use rattler_conda_types::{GenericVirtualPackage, Platform}; @@ -15,8 +16,7 @@ use rattler_virtual_packages::{VirtualPackage, VirtualPackageOverrides}; use serde::Serialize; use serde_with::{serde_as, DisplayFromStr}; use tokio::task::spawn_blocking; - -use crate::cli::cli_config::ProjectConfig; +use toml_edit::ser::to_string; use crate::{ global, @@ -26,7 +26,7 @@ use crate::{ }; use fancy_display::FancyDisplay; -static WIDTH: usize = 18; +static WIDTH: usize = 19; /// Information about the system, project and environments for the current /// machine. @@ -65,6 +65,7 @@ pub struct EnvironmentInfo { tasks: Vec, channels: Vec, prefix: PathBuf, + system_requirements: SystemRequirements, } impl Display for EnvironmentInfo { @@ -145,6 +146,27 @@ impl Display for EnvironmentInfo { platform_list )?; } + + if !self.system_requirements.is_empty() { + let serialized = to_string(&self.system_requirements).unwrap(); + let indented = serialized + .lines() + .enumerate() + .map(|(i, line)| { + if i == 0 { + // First line includes the label + format!("{:>WIDTH$}: {}", bold.apply_to("System requirements"), line) + } else { + // Subsequent lines are indented to align + format!("{:>WIDTH$} {}", "", line) + } + }) + .collect::>() + .join("\n"); + + writeln!(f, "{}", indented)?; + } + if !self.tasks.is_empty() { let tasks_list = self .tasks @@ -403,6 +425,7 @@ pub async fn execute(args: Args) -> miette::Result<()> { .map(|(name, _p)| name.as_source().to_string()) .collect(), platforms: env.platforms().into_iter().collect(), + system_requirements: env.system_requirements().clone(), channels: env.channels().into_iter().map(|c| c.to_string()).collect(), prefix: env.dir(), tasks,