Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
9427c56
feat: accept inline package definitions in package dependency tables
Hofer-Julian Jul 3, 2026
2426577
feat: carry inline package definitions on the [workspace.dependencies…
Hofer-Julian Jul 3, 2026
b50eb88
feat: honor package-level inline definitions throughout the solve
Hofer-Julian Jul 3, 2026
adc91d7
feat: honor package-level inline definitions at build and verify time
Hofer-Julian Jul 3, 2026
a284248
docs: document and test inline definitions in package dependency tables
Hofer-Julian Jul 3, 2026
2a36d9f
fix: resolve nested workspace markers against the real pool and skip …
Hofer-Julian Jul 6, 2026
22a2075
fix: keep the solve's seed-first inline definition choice at install …
Hofer-Julian Jul 6, 2026
f9d6f21
fix: verify transitively declared inline definitions during satisfiab…
Hofer-Julian Jul 6, 2026
9d5c6a5
test: extend integration coverage for inline package definitions
Hofer-Julian Jul 6, 2026
8fd68d3
fix: resolve nested workspace markers in dependency pool inline defin…
Hofer-Julian Jul 7, 2026
a408bda
fix: detect removed inline definitions during satisfiability
Hofer-Julian Jul 7, 2026
277a0b6
fix: classify satisfiability seeds at solve-group scope
Hofer-Julian Jul 7, 2026
ba6dd8c
fix: query declaring parents before their packages when satisfiabilit…
Hofer-Julian Jul 7, 2026
e0a8894
fix: match inline definitions by name and source location during sati…
Hofer-Julian Jul 7, 2026
7448c63
fix: keep the nested solve's seed choice when installing build and ho…
Hofer-Julian Jul 7, 2026
dde5a39
fix: re-discover records whose inline definition arrives in a later h…
Hofer-Julian Jul 7, 2026
b305473
refactor: discover inline-definition backends concurrently and hoist …
Hofer-Julian Jul 7, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions crates/pixi/tests/integration_rust/install_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1471,6 +1471,7 @@ async fn test_multiple_prefix_update() {
None,
command_dispatcher,
Default::default(),
Default::default(),
);

let pixi_records = Vec::from([
Expand Down
1 change: 1 addition & 0 deletions crates/pixi_build_discovery/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ version = "0.1.0"

[dependencies]
dunce = { workspace = true }
indexmap = { workspace = true }
itertools = { workspace = true }
miette = { workspace = true }
ordermap = { workspace = true }
Expand Down
53 changes: 50 additions & 3 deletions crates/pixi_build_discovery/src/discovery.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
use std::{
ffi::OsStr,
path::{Path, PathBuf},
sync::Arc,
};

use indexmap::IndexMap;
use itertools::Itertools;
use miette::Diagnostic;
use ordermap::OrderMap;
Expand All @@ -13,12 +15,13 @@ use pixi_consts::consts::{
KNOWN_MANIFEST_FILES, RATTLER_BUILD_DIRS, RATTLER_BUILD_FILE_NAMES, ROS_BACKEND_FILE_NAMES,
};
use pixi_manifest::{
DiscoveryStart, ExplicitManifestError, PackageManifest, PrioritizedChannel, WithProvenance,
WorkspaceDiscoverer, WorkspaceDiscoveryError, WorkspaceManifest,
DiscoveryStart, ExplicitManifestError, InlinePackageManifest, PackageManifest,
PrioritizedChannel, WithProvenance, WorkspaceDiscoverer, WorkspaceDiscoveryError,
WorkspaceManifest,
};
use pixi_spec::{SourceLocationSpec, SpecConversionError};
use pixi_spec_containers::DependencyMap;
use rattler_conda_types::ChannelConfig;
use rattler_conda_types::{ChannelConfig, PackageName};
use thiserror::Error;

use crate::{
Expand All @@ -37,6 +40,28 @@ pub struct DiscoveredBackend {

/// The parameters used to initialize the backend.
pub init_params: BackendInitializationParams,

/// Inline package definitions declared by the discovered package's
/// dependency tables, if any. These describe how to build *dependencies*
/// of the discovered package that carry no manifest of their own; the
/// consumer matches them by name against the dependencies the backend
/// reports. Not serialized: the parsed manifests only matter to the
/// in-process build pipeline.
#[cfg_attr(feature = "serde", serde(skip))]
pub inline_packages: Option<Arc<DiscoveredInlinePackages>>,
}

/// Inline package definitions found on a discovered package, together with the
/// workspace manifest that provides their context (channels, workspace root)
/// when a backend is built for them.
#[derive(Debug, Clone)]
pub struct DiscoveredInlinePackages {
/// The definitions, keyed by dependency name and merged across the
/// package's targets.
pub packages: IndexMap<PackageName, InlinePackageManifest>,

/// The workspace manifest the definitions were declared under.
pub workspace: Arc<WorkspaceManifest>,
}

/// The parameters used to initialize a build backend
Expand Down Expand Up @@ -227,6 +252,7 @@ impl DiscoveredBackend {
configuration: None,
target_configuration: None,
},
inline_packages: None,
})
}

Expand Down Expand Up @@ -295,6 +321,25 @@ impl DiscoveredBackend {
// Construct the project model from the manifest
let project_model = to_project_model_v1(package_manifest, channel_config)?;

// Collect the inline package definitions the package declares for its
// own dependencies. They stay pixi-side (the project model carries only
// the source specs) and are matched by name against the dependencies
// the backend reports. The workspace manifest travels along so a
// backend can later be built for each definition.
let inline_packages = {
let merged: IndexMap<PackageName, InlinePackageManifest> = package_manifest
.combined_inline_packages()
.into_iter()
.map(|(name, inline)| (name, inline.clone()))
.collect();
(!merged.is_empty()).then(|| {
Arc::new(DiscoveredInlinePackages {
packages: merged,
workspace: Arc::new(workspace_manifest.clone()),
})
})
};

// Determine the build system requirements.
let build_system = package_manifest.build.clone();
let requirement = (
Expand Down Expand Up @@ -359,6 +404,7 @@ impl DiscoveredBackend {
}),
target_configuration,
},
inline_packages,
})
}

Expand Down Expand Up @@ -453,6 +499,7 @@ impl DiscoveredBackend {
configuration: None,
target_configuration: None,
},
inline_packages: None,
})
}
}
Expand Down
2 changes: 1 addition & 1 deletion crates/pixi_cli/src/publish.rs
Original file line number Diff line number Diff line change
Expand Up @@ -731,7 +731,7 @@ pub async fn execute(args: Args) -> miette::Result<()> {
.await
.map_err_into_dispatcher(std::convert::identity)
.into_diagnostic()?;
resolved_records.extend(records.iter().cloned());
resolved_records.extend(records.records.iter().cloned());
}

// `--clean` nukes the per-package artifact + workspace caches so
Expand Down
13 changes: 12 additions & 1 deletion crates/pixi_command_dispatcher/src/build_backend_metadata/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use once_cell::sync::Lazy;
use pixi_build_types::procedures::conda_outputs::CondaOutputsParams;
use pixi_record::{CanonicalSourceLocation, PinnedBuildSourceSpec, PinnedSourceSpec, VariantValue};
use pixi_spec::{ResolvedExcludeNewer, SourceAnchor, SourceLocationSpec};
use rattler_conda_types::ChannelUrl;
use rattler_conda_types::{ChannelUrl, PackageName};
use std::time::SystemTime;
use std::{
collections::{BTreeMap, HashSet},
Expand Down Expand Up @@ -233,6 +233,11 @@ pub struct BuildBackendMetadata {
/// The cache key string that was used to store/look up this metadata.
pub cache_key: CacheKeyString<BuildBackendMetadataCache>,

/// Inline package definitions this package declares for its own
/// dependencies, keyed by dependency name. Produced by backend discovery
/// (which runs on cache hits too), never by the metadata cache itself.
pub inline_packages: Arc<BTreeMap<PackageName, InlinePackage>>,

/// The metadata that was acquired from the build backend.
pub metadata: CacheEntry<BuildBackendMetadataCache>,

Expand Down Expand Up @@ -911,6 +916,9 @@ impl BuildBackendMetadataInner {
Ok(CacheProbe::Hit(BuildBackendMetadata {
source: manifest_source_location,
cache_key: cache_key.key(),
inline_packages: crate::inline_package::inline_packages_from_backend(
&checkouts.discovered_backend,
),
metadata: fresh,
skip_cache,
}))
Expand Down Expand Up @@ -1082,6 +1090,9 @@ impl BuildBackendMetadataInner {
Ok(BuildBackendMetadata {
source: manifest_source_location,
cache_key: cache_key.key(),
inline_packages: crate::inline_package::inline_packages_from_backend(
&checkouts.discovered_backend,
),
metadata,
skip_cache,
})
Expand Down
17 changes: 17 additions & 0 deletions crates/pixi_command_dispatcher/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -258,6 +258,23 @@ pub enum SolvePixiEnvironmentError {
#[error(transparent)]
#[diagnostic(transparent)]
SourceMetadata(SourceMetadataError),

/// Two packages in the same solve declare different inline package
/// definitions (or one declares a definition and the other none) for the
/// same dependency at the same source location. There is no priority
/// order between arbitrary packages, so the ambiguity is an error.
#[error(
"the packages '{first_parent}' and '{second_parent}' declare conflicting inline definitions for '{package}' at '{source_location}'"
)]
#[diagnostic(help(
"Declare '{package}' with one inline definition in the workspace's dependency tables; the workspace-level definition overrides package-level ones."
))]
ConflictingInlineDefinitions {
package: Box<str>,
source_location: Box<str>,
first_parent: Box<str>,
second_parent: Box<str>,
},
}

impl From<SourceMetadataError> for SolvePixiEnvironmentError {
Expand Down
33 changes: 32 additions & 1 deletion crates/pixi_command_dispatcher/src/inline_package.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
//! whenever the definition is edited.

use std::{
collections::BTreeMap,
hash::{Hash, Hasher},
path::Path,
sync::Arc,
Expand All @@ -24,7 +25,7 @@ use pixi_build_discovery::{DiscoveredBackend, DiscoveryError};
use pixi_compute_engine::ComputeCtx;
use pixi_manifest::{InlineContentHash, PackageManifest, WorkspaceManifest};
use pixi_spec::SpecConversionError;
use rattler_conda_types::ChannelConfig;
use rattler_conda_types::{ChannelConfig, PackageName};

use crate::{discovered_backend::DiscoveredBackendKey, injected_config::ChannelConfigKey};

Expand Down Expand Up @@ -102,6 +103,36 @@ pub(crate) fn serialize_optional_content_hash<S: serde::Serializer>(
}
}

/// Convert the inline package definitions carried on a discovered backend into
/// dispatcher [`InlinePackage`]s, keyed by dependency name.
///
/// These are the definitions the discovered package declares for its *own*
/// dependencies; the caller matches them by name against the dependencies the
/// backend reports (nested build/host env seeds and transitive run deps).
pub(crate) fn inline_packages_from_backend(
backend: &DiscoveredBackend,
) -> Arc<BTreeMap<PackageName, InlinePackage>> {
let Some(discovered) = backend.inline_packages.as_ref() else {
return Arc::new(BTreeMap::new());
};
Arc::new(
discovered
.packages
.iter()
.map(|(name, inline)| {
(
name.clone(),
InlinePackage {
manifest: Arc::new(inline.manifest.clone()),
workspace: discovered.workspace.clone(),
content_hash: inline.content_hash,
},
)
})
.collect(),
)
}

/// Discover the build backend for a checked-out source, honoring an inline
/// package definition.
///
Expand Down
Loading
Loading