diff --git a/crates/pixi_build_frontend/src/backend/in_memory/passthrough.rs b/crates/pixi_build_frontend/src/backend/in_memory/passthrough.rs index b1c1fc3226..598e6343f5 100644 --- a/crates/pixi_build_frontend/src/backend/in_memory/passthrough.rs +++ b/crates/pixi_build_frontend/src/backend/in_memory/passthrough.rs @@ -13,7 +13,7 @@ use pixi_build_types::{ initialize::InitializeParams, }, }; -use rattler_conda_types::{Platform, Version, package::IndexJson}; +use rattler_conda_types::{PackageName, Platform, Version, package::IndexJson}; use serde::Deserialize; use crate::{ @@ -64,7 +64,19 @@ impl InMemoryBackend for PassthroughBackend { Ok(CondaOutputsResult { outputs: vec![CondaOutput { metadata: CondaOutputMetadata { - name: self.project_model.name.parse().unwrap(), + name: self + .project_model + .name + .as_ref() + .map(|name| PackageName::try_from(name.as_str()).unwrap()) + .unwrap_or_else(|| { + self.index_json + .as_ref() + .map(|j| j.name.clone()) + .unwrap_or_else(|| { + PackageName::try_from("pixi-package_name").unwrap() + }) + }), version: self .project_model .version diff --git a/crates/pixi_build_type_conversions/src/project_model.rs b/crates/pixi_build_type_conversions/src/project_model.rs index 33a70c3908..4040ca850a 100644 --- a/crates/pixi_build_type_conversions/src/project_model.rs +++ b/crates/pixi_build_type_conversions/src/project_model.rs @@ -173,7 +173,7 @@ pub fn to_project_model_v1( ) -> Result { let project = pbt::ProjectModelV1 { name: manifest.package.name.clone(), - version: Some(manifest.package.version.clone()), + version: manifest.package.version.clone(), description: manifest.package.description.clone(), authors: manifest.package.authors.clone(), license: manifest.package.license.clone(), diff --git a/crates/pixi_build_types/src/lib.rs b/crates/pixi_build_types/src/lib.rs index 6e152317cd..efe9e8d992 100644 --- a/crates/pixi_build_types/src/lib.rs +++ b/crates/pixi_build_types/src/lib.rs @@ -23,13 +23,17 @@ use rattler_conda_types::{ }; use serde::{Deserialize, Serialize}; +// Version 0: Initial version +// Version 1: Added conda/outputs and conda/build_v1 +// Version 2: Name in project models can be `None`. + /// The constraint for the pixi build api version package /// Adding this constraint when solving a pixi build backend environment ensures /// that a backend is selected that uses the same interface version as Pixi does pub static PIXI_BUILD_API_VERSION_NAME: LazyLock = LazyLock::new(|| PackageName::new_unchecked("pixi-build-api-version")); pub const PIXI_BUILD_API_VERSION_LOWER: u64 = 0; -pub const PIXI_BUILD_API_VERSION_CURRENT: u64 = 1; +pub const PIXI_BUILD_API_VERSION_CURRENT: u64 = 2; pub const PIXI_BUILD_API_VERSION_UPPER: u64 = PIXI_BUILD_API_VERSION_CURRENT + 1; pub static PIXI_BUILD_API_VERSION_SPEC: LazyLock = LazyLock::new(|| { VersionSpec::Group( @@ -85,9 +89,17 @@ impl PixiBuildApiVersion { provides_conda_build_v1: Some(true), ..Self(0).expected_backend_capabilities() }, + 2 => BackendCapabilities { + ..Self(1).expected_backend_capabilities() + }, _ => BackendCapabilities::default(), } } + + /// Returns true if this version of the protocol supports the name field in the project model to be `None`. + pub fn supports_name_none(&self) -> bool { + self.0 >= 2 + } } impl Display for PixiBuildApiVersion { diff --git a/crates/pixi_build_types/src/project_model.rs b/crates/pixi_build_types/src/project_model.rs index 45dab31eae..ec7c5f654f 100644 --- a/crates/pixi_build_types/src/project_model.rs +++ b/crates/pixi_build_types/src/project_model.rs @@ -74,7 +74,7 @@ pub type SourcePackageName = String; #[serde(rename_all = "camelCase")] pub struct ProjectModelV1 { /// The name of the project - pub name: String, + pub name: Option, /// The version of the project pub version: Option, @@ -176,17 +176,18 @@ impl TargetsV1 { } impl IsDefault for TargetsV1 { - fn is_default(&self) -> bool { - self.is_empty() + type Item = Self; + + fn is_non_default(&self) -> Option<&Self::Item> { + if !self.is_empty() { Some(self) } else { None } } } impl IsDefault for Option { - fn is_default(&self) -> bool { - match self { - None => true, - Some(value) => value.is_default(), - } + type Item = T::Item; + + fn is_non_default(&self) -> Option<&Self::Item> { + self.as_ref()?.is_non_default() } } @@ -219,8 +220,10 @@ impl TargetV1 { } impl IsDefault for TargetV1 { - fn is_default(&self) -> bool { - self.is_empty() + type Item = Self; + + fn is_non_default(&self) -> Option<&Self::Item> { + if !self.is_empty() { Some(self) } else { None } } } @@ -601,8 +604,10 @@ impl Hash for GitReferenceV1 { } impl IsDefault for GitReferenceV1 { - fn is_default(&self) -> bool { - false // Never skip GitReferenceV1 fields + type Item = Self; + + fn is_non_default(&self) -> Option<&Self::Item> { + Some(self) // Never skip GitReferenceV1 fields } } @@ -642,7 +647,7 @@ mod tests { fn test_hash_stability_with_default_values() { // Create a minimal ProjectModelV1 instance let mut project_model = ProjectModelV1 { - name: "test-project".to_string(), + name: Some("test-project".to_string()), version: None, description: None, authors: None, @@ -698,7 +703,7 @@ mod tests { fn test_hash_changes_with_meaningful_values() { // Create a minimal ProjectModelV1 instance let mut project_model = ProjectModelV1 { - name: "test-project".to_string(), + name: Some("test-project".to_string()), version: None, description: None, authors: None, @@ -993,14 +998,14 @@ mod tests { fn test_hash_collision_bug_project_model() { // Test the same issue in ProjectModelV1 let project1 = ProjectModelV1 { - name: "test".to_string(), + name: Some("test".to_string()), description: Some("test description".to_string()), license: None, ..Default::default() }; let project2 = ProjectModelV1 { - name: "test".to_string(), + name: Some("test".to_string()), description: None, license: Some("test description".to_string()), ..Default::default() diff --git a/crates/pixi_build_types/src/stable_hash.rs b/crates/pixi_build_types/src/stable_hash.rs index ca6c789a55..506f00e94d 100644 --- a/crates/pixi_build_types/src/stable_hash.rs +++ b/crates/pixi_build_types/src/stable_hash.rs @@ -35,7 +35,9 @@ impl Hash for FieldDiscriminant { /// Trait to determine if a value should be considered "default" and thus skipped in hash calculations. /// This helps maintain forward/backward compatibility by only including discriminants for meaningful values. pub(crate) trait IsDefault { - fn is_default(&self) -> bool; + type Item; + + fn is_non_default(&self) -> Option<&Self::Item>; } /// A dyn-compatible hashing trait that works with any hasher type @@ -66,9 +68,12 @@ impl<'a, H: std::hash::Hasher> StableHashBuilder<'a, H> { /// Add a field to the hash if it's not in its default state. /// Fields will be automatically sorted alphabetically before hashing. - pub fn field(mut self, name: &'static str, value: &'a T) -> Self { - if !value.is_default() { - self.fields.insert(name, value); + pub fn field(mut self, name: &'static str, value: &'a T) -> Self + where + T::Item: Hash, + { + if let Some(item) = value.is_non_default() { + self.fields.insert(name, item); } self } @@ -83,61 +88,81 @@ impl<'a, H: std::hash::Hasher> StableHashBuilder<'a, H> { } impl IsDefault for OrderMap { - fn is_default(&self) -> bool { - self.is_empty() + type Item = Self; + + fn is_non_default(&self) -> Option<&Self::Item> { + if !self.is_empty() { Some(self) } else { None } } } impl IsDefault for String { - fn is_default(&self) -> bool { - false // Never skip required string fields + type Item = Self; + + fn is_non_default(&self) -> Option<&Self::Item> { + Some(self) // Never skip required string fields } } impl IsDefault for url::Url { - fn is_default(&self) -> bool { - false // Never skip required URL fields + type Item = Self; + + fn is_non_default(&self) -> Option<&Self::Item> { + Some(self) // Never skip required URL fields } } impl IsDefault for std::path::PathBuf { - fn is_default(&self) -> bool { - false // Never skip PathBuf fields + type Item = Self; + + fn is_non_default(&self) -> Option<&Self::Item> { + Some(self) // Never skip PathBuf fields } } impl IsDefault for Vec { - fn is_default(&self) -> bool { - self.is_empty() + type Item = Self; + + fn is_non_default(&self) -> Option<&Self::Item> { + if !self.is_empty() { Some(self) } else { None } } } impl IsDefault for rattler_conda_types::Version { - fn is_default(&self) -> bool { - false // Never skip version fields + type Item = Self; + + fn is_non_default(&self) -> Option<&Self::Item> { + Some(self) // Never skip version fields } } impl IsDefault for rattler_conda_types::StringMatcher { - fn is_default(&self) -> bool { - false // Never skip StringMatcher fields + type Item = Self; + + fn is_non_default(&self) -> Option<&Self::Item> { + Some(self) // Never skip StringMatcher fields } } impl IsDefault for rattler_conda_types::BuildNumberSpec { - fn is_default(&self) -> bool { - false // Never skip BuildNumberSpec fields + type Item = Self; + + fn is_non_default(&self) -> Option<&Self::Item> { + Some(self) // Never skip BuildNumberSpec fields } } impl IsDefault for rattler_conda_types::VersionSpec { - fn is_default(&self) -> bool { - false // Never skip VersionSpec fields + type Item = Self; + + fn is_non_default(&self) -> Option<&Self::Item> { + Some(self) // Never skip VersionSpec fields } } impl> IsDefault for GenericArray { - fn is_default(&self) -> bool { - false // Never skip digest output fields + type Item = Self; + + fn is_non_default(&self) -> Option<&Self::Item> { + Some(self) // Never skip digest output fields } } diff --git a/crates/pixi_command_dispatcher/src/command_dispatcher/instantiate_backend.rs b/crates/pixi_command_dispatcher/src/command_dispatcher/instantiate_backend.rs index 46df117d37..505e6b82dd 100644 --- a/crates/pixi_command_dispatcher/src/command_dispatcher/instantiate_backend.rs +++ b/crates/pixi_command_dispatcher/src/command_dispatcher/instantiate_backend.rs @@ -151,6 +151,19 @@ impl CommandDispatcher { api_version, ); + // Make sure that the project model is compatible with the API version. + if !api_version.supports_name_none() + && spec + .init_params + .project_model + .as_ref() + .is_some_and(|p| p.name.is_none()) + { + return Err(CommandDispatcherError::Failed( + InstantiateBackendError::SpecConversionError(SpecConversionError::MissingName), + )); + } + JsonRpcBackend::setup( source_dir, spec.init_params.manifest_path, diff --git a/crates/pixi_core/src/lock_file/satisfiability/mod.rs b/crates/pixi_core/src/lock_file/satisfiability/mod.rs index 82be451cf0..742fe69a43 100644 --- a/crates/pixi_core/src/lock_file/satisfiability/mod.rs +++ b/crates/pixi_core/src/lock_file/satisfiability/mod.rs @@ -1142,6 +1142,9 @@ pub(crate) async fn verify_package_platform_satisfiability( ParseChannelError::InvalidPath(p).into() } SpecConversionError::InvalidChannel(_name, p) => p.into(), + SpecConversionError::MissingName => { + ParseMatchSpecError::MissingPackageName + } }; return Err(Box::new(PlatformUnsat::FailedToParseMatchSpec( name.as_source().to_string(), @@ -1951,7 +1954,6 @@ mod tests { #[rstest] #[tokio::test] - #[cfg_attr(not(feature = "slow_integration_tests"), ignore)] async fn test_example_satisfiability( #[files("../../examples/**/p*.toml")] manifest_path: PathBuf, ) { diff --git a/crates/pixi_core/src/lock_file/satisfiability/snapshots/pixi_core__lock_file__satisfiability__tests__failing_satisiability@source-dependency-changed-project-model.snap b/crates/pixi_core/src/lock_file/satisfiability/snapshots/pixi_core__lock_file__satisfiability__tests__failing_satisiability@source-dependency-changed-project-model.snap index 50d0fdb3ad..813a838e08 100644 --- a/crates/pixi_core/src/lock_file/satisfiability/snapshots/pixi_core__lock_file__satisfiability__tests__failing_satisiability@source-dependency-changed-project-model.snap +++ b/crates/pixi_core/src/lock_file/satisfiability/snapshots/pixi_core__lock_file__satisfiability__tests__failing_satisiability@source-dependency-changed-project-model.snap @@ -1,7 +1,8 @@ --- -source: src/lock_file/satisfiability/mod.rs +source: crates/pixi_core/src/lock_file/satisfiability/mod.rs expression: s +snapshot_kind: text --- environment 'default' does not satisfy the requirements of the project for platform 'linux-64' Diagnostic severity: error - Caused by: the input hash for 'child-package' (f0d841a0cfd56becd0041a6bbcd215a19ce9c34c08530a98b642381af8ebd32a) does not match the hash in the lock-file (64a8e9eb88c4f6e944a37279a38a47fe7872454fb62bcafad63d200aef4b4a05) + Caused by: the input hash for 'child-package' (6a7fcb5c9f728504123fff40635207fb963b77f26b1a2dffb2993efa7f81559d) does not match the hash in the lock-file (64a8e9eb88c4f6e944a37279a38a47fe7872454fb62bcafad63d200aef4b4a05) diff --git a/crates/pixi_glob/src/snapshots/pixi_glob__glob_hash__test__glob_hash_case_1_satisfiability.snap b/crates/pixi_glob/src/snapshots/pixi_glob__glob_hash__test__glob_hash_case_1_satisfiability.snap index 6f4d79766a..30d9dc7ece 100644 --- a/crates/pixi_glob/src/snapshots/pixi_glob__glob_hash__test__glob_hash_case_1_satisfiability.snap +++ b/crates/pixi_glob/src/snapshots/pixi_glob__glob_hash__test__glob_hash_case_1_satisfiability.snap @@ -1,10 +1,11 @@ --- source: crates/pixi_glob/src/glob_hash.rs expression: snapshot +snapshot_kind: text --- Globs: - tests/data/satisfiability/source-dependency/**/* -Hash: 4ae275fa46ad1fa335806cd37767a8fe530046fb38aea40b24527fddf3a769b8 +Hash: 15638d2bfdb15971bb19d0593ee872437fb6a5c88b143e01f3044e473888e39c Matched files: - tests/data/satisfiability/source-dependency/child-package/pixi.toml - tests/data/satisfiability/source-dependency/pixi.lock diff --git a/crates/pixi_manifest/src/discovery.rs b/crates/pixi_manifest/src/discovery.rs index ae260fa845..e2b49cb4b6 100644 --- a/crates/pixi_manifest/src/discovery.rs +++ b/crates/pixi_manifest/src/discovery.rs @@ -631,7 +631,14 @@ mod test { writeln!( &mut snapshot, "Package: {} @ {}", - &package.value.package.name, &package.value.package.version, + &package.clone().value.package.name.unwrap_or("None".into()), + &package + .value + .package + .version + .as_ref() + .map(|v| v.to_string()) + .unwrap_or_else(|| "None".to_string()), ) .unwrap(); } @@ -656,7 +663,6 @@ mod test { #[case::missing_table_pixi_manifest("missing-tables/pixi.toml")] #[case::missing_table_pyproject_manifest("missing-tables-pyproject/pyproject.toml")] #[case::split_package("split_package/good/package")] - #[case::split_package("split_package/bad/package")] fn test_explicit_workspace_discoverer(#[case] subdir: &str) { let test_data_root = dunce::canonicalize( Path::new(env!("CARGO_MANIFEST_DIR")).join("../../tests/data/workspace-discovery"), @@ -696,7 +702,14 @@ mod test { writeln!( &mut snapshot, "Package: {} @ {}", - &package.value.package.name, &package.value.package.version, + &package.clone().value.package.name.unwrap_or("None".into()), + &package + .value + .package + .version + .as_ref() + .map(|v| v.to_string()) + .unwrap_or_else(|| "None".to_string()), ) .unwrap(); } diff --git a/crates/pixi_manifest/src/package.rs b/crates/pixi_manifest/src/package.rs index 66bd0d2562..2ea120d39e 100644 --- a/crates/pixi_manifest/src/package.rs +++ b/crates/pixi_manifest/src/package.rs @@ -7,10 +7,10 @@ use url::Url; #[derive(Debug, Clone)] pub struct Package { /// The name of the project - pub name: String, + pub name: Option, /// The version of the project - pub version: Version, + pub version: Option, /// An optional project description pub description: Option, @@ -39,7 +39,7 @@ pub struct Package { impl Package { /// Creates a new package with the given name and version. - pub fn new(name: String, version: Version) -> Self { + pub fn new(name: Option, version: Option) -> Self { Self { name, version, diff --git a/crates/pixi_manifest/src/snapshots/pixi_manifest__discovery__test__explicit_workspace_discoverer@split_package_bad_package.snap b/crates/pixi_manifest/src/snapshots/pixi_manifest__discovery__test__explicit_workspace_discoverer@split_package_bad_package.snap deleted file mode 100644 index 4fadbb9458..0000000000 --- a/crates/pixi_manifest/src/snapshots/pixi_manifest__discovery__test__explicit_workspace_discoverer@split_package_bad_package.snap +++ /dev/null @@ -1,11 +0,0 @@ ---- -source: crates/pixi_manifest/src/discovery.rs -expression: snapshot ---- - × missing field 'name' in table - ╭─[/tests/data/workspace-discovery/split_package/bad/package/pixi.toml:6:1] - 5 │ - 6 │ ╭─▶ [package.build.backend] - 7 │ │ name = "pixi-build-rattler-build" - 8 │ ╰─▶ version = "0.1.*" - ╰──── diff --git a/crates/pixi_manifest/src/toml/manifest.rs b/crates/pixi_manifest/src/toml/manifest.rs index 17af3995f1..698d843528 100644 --- a/crates/pixi_manifest/src/toml/manifest.rs +++ b/crates/pixi_manifest/src/toml/manifest.rs @@ -617,52 +617,6 @@ mod test { )); } - #[test] - fn test_missing_version() { - assert_snapshot!(expect_parse_failure( - r#" - [workspace] - name = "foo" - channels = [] - platforms = [] - preview = ["pixi-build"] - - [package] - name = { workspace = true } - - [package.build] - backend = { name = "foobar", version = "*" } - "#, - )); - } - - #[test] - fn test_missing_package_name() { - assert_snapshot!(expect_parse_failure( - r#" - [workspace] - channels = [] - platforms = [] - preview = ["pixi-build"] - - [package] - # Since workspace doesnt define a name we expect an error here. - - [package.build] - backend = { name = "foobar", version = "*" } - "#, - ), @r###" - × missing field 'name' in table - ╭─[pixi.toml:7:9] - 6 │ - 7 │ [package] - · ────────── - 8 │ # Since workspace doesnt define a name we expect an error here. - 9 │ - ╰──── - "###); - } - #[test] fn test_workspace_name_from_package() { let workspace_manifest = WorkspaceManifest::from_toml_str( diff --git a/crates/pixi_manifest/src/toml/package.rs b/crates/pixi_manifest/src/toml/package.rs index 7b0dc597ea..3f22ac19c8 100644 --- a/crates/pixi_manifest/src/toml/package.rs +++ b/crates/pixi_manifest/src/toml/package.rs @@ -5,7 +5,7 @@ pub use pixi_toml::TomlFromStr; use pixi_toml::{DeserializeAs, Same, TomlIndexMap, TomlWith}; use rattler_conda_types::Version; use thiserror::Error; -use toml_span::{DeserError, Error, ErrorKind, Span, Spanned, Value, de_helpers::TableHelper}; +use toml_span::{DeserError, Span, Spanned, Value, de_helpers::TableHelper}; use url::Url; use crate::{ @@ -270,43 +270,6 @@ pub enum PackageError { } impl TomlPackage { - /// Helper function to resolve a required field with 3-tier hierarchy: - /// 1. Direct value (from package) - /// 2. Workspace inheritance (from workspace) - /// 3. Package defaults (from [project] section if the manifest is a - /// `pyproject.toml`) - /// 4. Error if missing at all levels - fn resolve_required_field_with_defaults( - field: Option>, - workspace_value: Option, - default_value: Option, - field_name: &'static str, - package_span: Span, - ) -> Result { - match field { - Some(WorkspaceInheritableField::Value(v)) => Ok(v), - Some(WorkspaceInheritableField::Workspace(span)) => workspace_value.ok_or_else(|| { - GenericError::new(format!("the workspace does not define a '{}'", field_name)) - .with_span(span.into()) - .into() - }), - Some(WorkspaceInheritableField::NotWorkspace(span)) => { - Err(workspace_cannot_be_false().with_span(span.into()).into()) - } - None => { - // Fall back to package defaults - default_value.ok_or( - Error { - kind: ErrorKind::MissingField(field_name), - span: package_span, - line_info: None, - } - .into(), - ) - } - } - } - /// Helper function to resolve an optional field with 3-tier hierarchy: /// 1. Direct value (from package) /// 2. Workspace inheritance (from workspace) - ERROR if explicitly @@ -358,19 +321,17 @@ impl TomlPackage { // Resolve fields with 3-tier hierarchy: direct → workspace → package defaults → // error - let name = Self::resolve_required_field_with_defaults( + let name = Self::resolve_optional_field_with_defaults( self.name, workspace.name, package_defaults.name, "name", - self.span, )?; - let version = Self::resolve_required_field_with_defaults( + let version = Self::resolve_optional_field_with_defaults( self.version, workspace.version, package_defaults.version, "version", - self.span, )?; let default_package_target = TomlPackageTarget { @@ -642,8 +603,8 @@ mod test { None, ) .unwrap(); - assert_eq!(manifest.value.package.name, "workspace-name"); - assert_eq!(manifest.value.package.version.to_string(), "1.0.0"); + assert_eq!(manifest.value.package.name.unwrap(), "workspace-name"); + assert_eq!(manifest.value.package.version.unwrap().to_string(), "1.0.0"); assert_eq!( manifest.value.package.description, Some("Package description".to_string()) @@ -667,29 +628,6 @@ mod test { ); } - #[test] - fn test_missing_name_no_inheritance() { - let input = r#" - version = "1.0.0" - - [build] - backend = { name = "bla", version = "1.0" } - "#; - - let package = TomlPackage::from_toml_str(input).unwrap(); - let workspace = WorkspacePackageProperties::default(); - - let parse_error = package - .into_manifest( - workspace, - PackageDefaults::default(), - &Preview::default(), - None, - ) - .unwrap_err(); - assert_snapshot!(format_parse_error(input, parse_error)); - } - #[test] fn test_mixed_inheritance_and_direct_values() { let input = r#" @@ -719,8 +657,8 @@ mod test { None, ) .unwrap(); - assert_eq!(manifest.value.package.name, "workspace-name"); - assert_eq!(manifest.value.package.version.to_string(), "2.0.0"); + assert_eq!(manifest.value.package.name.unwrap(), "workspace-name"); + assert_eq!(manifest.value.package.version.unwrap().to_string(), "2.0.0"); assert_eq!( manifest.value.package.description, Some("Workspace description".to_string()) @@ -810,8 +748,8 @@ mod test { .into_manifest(workspace, package_defaults, &Preview::default(), None) .unwrap(); // Should use package defaults for name and version - assert_eq!(manifest.value.package.name, "default-name"); - assert_eq!(manifest.value.package.version.to_string(), "2.0.0"); + assert_eq!(manifest.value.package.name.unwrap(), "default-name"); + assert_eq!(manifest.value.package.version.unwrap().to_string(), "2.0.0"); // Should use direct value for description assert_eq!( manifest.value.package.description, @@ -851,8 +789,8 @@ mod test { .into_manifest(workspace, package_defaults, &Preview::default(), None) .unwrap(); // Should use workspace values for name and version (overrides defaults) - assert_eq!(manifest.value.package.name, "workspace-name"); - assert_eq!(manifest.value.package.version.to_string(), "3.0.0"); + assert_eq!(manifest.value.package.name.unwrap(), "workspace-name"); + assert_eq!(manifest.value.package.version.unwrap().to_string(), "3.0.0"); // Should use package defaults for description (not specified anywhere else) assert_eq!( manifest.value.package.description, @@ -860,25 +798,6 @@ mod test { ); } - #[test] - fn test_missing_required_field_no_defaults_no_workspace() { - let input = r#" - version = "1.0.0" - - [build] - backend = { name = "bla", version = "1.0" } - "#; - - let package = TomlPackage::from_toml_str(input).unwrap(); - let workspace = WorkspacePackageProperties::default(); // Empty workspace - let package_defaults = PackageDefaults::default(); // Empty defaults - - let parse_error = package - .into_manifest(workspace, package_defaults, &Preview::default(), None) - .unwrap_err(); - assert_snapshot!(format_parse_error(input, parse_error)); - } - #[test] fn test_optional_workspace_inheritance_missing_workspace_value() { let input = r#" diff --git a/crates/pixi_spec/src/lib.rs b/crates/pixi_spec/src/lib.rs index 50480d6023..bb0b30752d 100644 --- a/crates/pixi_spec/src/lib.rs +++ b/crates/pixi_spec/src/lib.rs @@ -48,6 +48,10 @@ pub enum SpecConversionError { /// Encountered an invalid channel url or path #[error("the channel '{0}' could not be resolved")] InvalidChannel(String, #[source] ParseChannelError), + + /// The `name` field is missing in the spec. + #[error("the `package.name` must be provided in versions of pixi-build-api-version <2")] + MissingName, } /// A package specification for pixi. diff --git a/examples/pixi-build/array-api-extra/pixi.lock b/examples/pixi-build/array-api-extra/pixi.lock index 1487d7b7f2..69953da686 100644 --- a/examples/pixi-build/array-api-extra/pixi.lock +++ b/examples/pixi-build/array-api-extra/pixi.lock @@ -125,7 +125,7 @@ packages: - python license: MIT input: - hash: cbfd3f78b3a0171befaa5d56f67f0c365b2ac9d7f0518fe7c080bd239fa6f59a + hash: 1101422a995a18b9c73ca5617a726f185782e5329c2739d754b921baaf16b7ca globs: - recipe.yaml - conda: https://prefix.dev/conda-forge/linux-64/bzip2-1.0.8-h4bc722e_7.conda diff --git a/examples/pixi-build/cpp-sdl/pixi.lock b/examples/pixi-build/cpp-sdl/pixi.lock index 8d6e739c87..9d6da7fedf 100644 --- a/examples/pixi-build/cpp-sdl/pixi.lock +++ b/examples/pixi-build/cpp-sdl/pixi.lock @@ -111,7 +111,7 @@ environments: - conda: https://prefix.dev/conda-forge/win-64/vc14_runtime-14.44.35208-h818238b_31.conda - conda: https://prefix.dev/conda-forge/win-64/vcomp14-14.44.35208-h818238b_31.conda - conda: . - build: h9352c13_0 + subdir: win-64 packages: - conda: https://prefix.dev/conda-forge/linux-64/_libgcc_mutex-0.1-conda_forge.tar.bz2 sha256: fe51de6107f9edc7aa4f786a70f4a883943bc9d39b3bb7307c04c41410990726 @@ -1031,49 +1031,49 @@ packages: - conda: . name: sdl_example version: 0.1.0 - build: h9352c13_0 - subdir: win-64 + build: hbf21a9e_0 + subdir: linux-64 depends: - - vc >=14.2,<15 - - vc14_runtime >=14.29.30139 - - ucrt >=10.0.20348.0 + - libstdcxx >=15 + - libgcc >=15 - sdl2 >=2.32.54,<3.0a0 input: - hash: 31ce6534707ac3b31b85d64591e46cb491b3da9ce64fcc66e64b8952d8d810f2 + hash: edfac7bd3233d95d1173e54bb2b8900757a662b8a553d89225c1a81ea98796af globs: [] - conda: . name: sdl_example version: 0.1.0 build: hbf21a9e_0 - subdir: linux-64 + subdir: osx-64 depends: - - libstdcxx >=15 - - libgcc >=15 + - libcxx >=20 - sdl2 >=2.32.54,<3.0a0 input: - hash: 31ce6534707ac3b31b85d64591e46cb491b3da9ce64fcc66e64b8952d8d810f2 + hash: edfac7bd3233d95d1173e54bb2b8900757a662b8a553d89225c1a81ea98796af globs: [] - conda: . name: sdl_example version: 0.1.0 build: hbf21a9e_0 - subdir: osx-64 + subdir: osx-arm64 depends: - libcxx >=20 - sdl2 >=2.32.54,<3.0a0 input: - hash: 31ce6534707ac3b31b85d64591e46cb491b3da9ce64fcc66e64b8952d8d810f2 + hash: edfac7bd3233d95d1173e54bb2b8900757a662b8a553d89225c1a81ea98796af globs: [] - conda: . name: sdl_example version: 0.1.0 build: hbf21a9e_0 - subdir: osx-arm64 + subdir: win-64 depends: - - libcxx >=20 + - vc >=14.2,<15 + - vc14_runtime >=14.29.30139 + - ucrt >=10.0.20348.0 - sdl2 >=2.32.54,<3.0a0 input: - hash: 31ce6534707ac3b31b85d64591e46cb491b3da9ce64fcc66e64b8952d8d810f2 + hash: edfac7bd3233d95d1173e54bb2b8900757a662b8a553d89225c1a81ea98796af globs: [] - conda: https://prefix.dev/conda-forge/win-64/ucrt-10.0.22621.0-h57928b3_1.conda sha256: db8dead3dd30fb1a032737554ce91e2819b43496a0db09927edf01c32b577450 diff --git a/examples/pixi-build/recursive-run-dependencies/pixi.lock b/examples/pixi-build/recursive-run-dependencies/pixi.lock index 9c6f1edf7e..a9e63899da 100644 --- a/examples/pixi-build/recursive-run-dependencies/pixi.lock +++ b/examples/pixi-build/recursive-run-dependencies/pixi.lock @@ -154,7 +154,7 @@ packages: - boltons - python input: - hash: 63bd47ad782aa0bd359bb4b735616e17f2f859ec603f845aba29cf04fd7b1120 + hash: c33f206dfc06551f0242c7037fe4d8f019bf8fbb3473b626f808d8947b8671f7 globs: [] - conda: https://prefix.dev/conda-forge/linux-64/ld_impl_linux-64-2.43-h712a8e2_4.conda sha256: db73f38155d901a610b2320525b9dd3b31e4949215c870685fd92ea61b5ce472 @@ -564,7 +564,7 @@ packages: - depend - python input: - hash: fa14d594a1a54f7dc413f2cc4d5f3e072d961439d8390695b40fb4c36291fa4a + hash: d74786cfcf078d97d1c9d5e38440e52418fc4650ee65f4adc27d02afcf50c8d0 globs: [] sources: depend: diff --git a/tests/data/satisfiability/source-dependency/pixi.lock b/tests/data/satisfiability/source-dependency/pixi.lock index 5450d956b2..1d6a13065a 100644 --- a/tests/data/satisfiability/source-dependency/pixi.lock +++ b/tests/data/satisfiability/source-dependency/pixi.lock @@ -41,7 +41,7 @@ packages: - libstdcxx >=15 - libgcc >=15 input: - hash: 64a8e9eb88c4f6e944a37279a38a47fe7872454fb62bcafad63d200aef4b4a05 + hash: 25e63e74a216901367b58ef438098792c9516ebd940e2f07d870b38ac24d470e globs: [] - conda: https://prefix.dev/conda-forge/linux-64/libgcc-15.1.0-h767d61c_2.conda sha256: 0024f9ab34c09629621aefd8603ef77bf9d708129b0dd79029e502c39ffc2195 diff --git a/tests/data/workspace-discovery/split_package/bad/package/pixi.toml b/tests/data/workspace-discovery/split_package/bad/package/pixi.toml deleted file mode 100644 index 5013a7eb4b..0000000000 --- a/tests/data/workspace-discovery/split_package/bad/package/pixi.toml +++ /dev/null @@ -1,8 +0,0 @@ -# This file should error because the workspace doesnt define a name. -# [package] -# name = "test_build_conda_package" -# version = "1.0.0" - -[package.build.backend] -name = "pixi-build-rattler-build" -version = "0.1.*" diff --git a/tests/data/workspace-discovery/split_package/bad/pixi.toml b/tests/data/workspace-discovery/split_package/bad/pixi.toml deleted file mode 100644 index 81ff8d65ea..0000000000 --- a/tests/data/workspace-discovery/split_package/bad/pixi.toml +++ /dev/null @@ -1,12 +0,0 @@ -[workspace] -channels = [ - "https://prefix.dev/pixi-build-backends", - "https://prefix.dev/conda-forge", -] -platforms = ["linux-64"] -preview = ["pixi-build"] -# name = "test_build_conda_package" -version = "1.0.0" - -[dependencies.test_build_conda_package] -path = "package"