diff --git a/crates/pixi_core/src/lock_file/resolve/build_dispatch.rs b/crates/pixi_core/src/lock_file/resolve/build_dispatch.rs index c9ec5a4596..b9a3c40787 100644 --- a/crates/pixi_core/src/lock_file/resolve/build_dispatch.rs +++ b/crates/pixi_core/src/lock_file/resolve/build_dispatch.rs @@ -18,6 +18,7 @@ //! needed. use std::cell::Cell; use std::collections::HashSet; +use std::sync::Arc; use std::{collections::HashMap, path::Path}; use crate::environment::{CondaPrefixUpdated, CondaPrefixUpdater}; @@ -218,6 +219,10 @@ pub struct LazyBuildDispatch<'a> { workspace_cache: WorkspaceCache, pub ignore_packages: Option>, + + /// Shared error holder for storing initialization errors that can be retrieved + /// after the LazyBuildDispatch is consumed (e.g., in catch_unwind scenarios) + pub last_error: Arc>, } /// These are resources for the [`BuildDispatch`] that need to be lazily @@ -242,8 +247,6 @@ pub struct LazyBuildDispatchDependencies { extra_build_variables: OnceCell, /// Package-specific configuration settings package_config_settings: OnceCell, - /// The last initialization error that occurred - last_error: OnceCell, } #[derive(Debug, thiserror::Error, miette::Diagnostic)] @@ -276,11 +279,6 @@ impl IsBuildBackendError for LazyBuildDispatchError { } impl<'a> LazyBuildDispatch<'a> { - /// Get the last initialization error if available - pub fn last_initialization_error(&self) -> Option<&LazyBuildDispatchError> { - self.lazy_deps.last_error.get() - } - /// Create a new `PixiBuildDispatch` instance. #[allow(clippy::too_many_arguments)] pub fn new( @@ -293,6 +291,7 @@ impl<'a> LazyBuildDispatch<'a> { lazy_deps: &'a LazyBuildDispatchDependencies, ignore_packages: Option>, disallow_install_conda_prefix: bool, + last_error: Arc>, ) -> Self { Self { params, @@ -307,6 +306,7 @@ impl<'a> LazyBuildDispatch<'a> { disallow_install_conda_prefix, workspace_cache: WorkspaceCache::default(), ignore_packages, + last_error, } } @@ -441,7 +441,7 @@ impl BuildContext for LazyBuildDispatch<'_> { Ok(dispatch) => dispatch.interpreter().await, Err(e) => { // Store the error for later retrieval - let _ = self.lazy_deps.last_error.set(e); + let _ = self.last_error.set(e); panic!("could not initialize build dispatch correctly") } } diff --git a/crates/pixi_core/src/lock_file/resolve/pypi.rs b/crates/pixi_core/src/lock_file/resolve/pypi.rs index 04adb026ca..c01c52636c 100644 --- a/crates/pixi_core/src/lock_file/resolve/pypi.rs +++ b/crates/pixi_core/src/lock_file/resolve/pypi.rs @@ -10,6 +10,8 @@ use std::{ sync::Arc, }; +use once_cell::sync::OnceCell; + use futures::FutureExt; use chrono::{DateTime, Utc}; @@ -230,6 +232,10 @@ pub enum SolveError { #[error(transparent)] #[diagnostic(transparent)] LookAhead(Box), + + #[error(transparent)] + #[diagnostic(transparent)] + Locking(Box), } /// Creates a custom `SolveError` from a `ResolveError`. @@ -549,6 +555,7 @@ pub async fn resolve_pypi( .with_concurrency(context.concurrency); let lazy_build_dispatch_dependencies = LazyBuildDispatchDependencies::default(); + let last_error = Arc::new(OnceCell::new()); let lazy_build_dispatch = LazyBuildDispatch::new( build_params, prefix_updater, @@ -559,6 +566,7 @@ pub async fn resolve_pypi( &lazy_build_dispatch_dependencies, None, disallow_install_conda_prefix, + Arc::clone(&last_error), ); // Constrain the conda packages to the specific python packages @@ -747,18 +755,45 @@ pub async fn resolve_pypi( UvReporterOptions::new().with_existing(pb.clone()), )); - resolver + let resolution = resolver .resolve() .await - .map_err(|e| create_solve_error(e, &conda_python_packages)) + .map_err(|e| create_solve_error(e, &conda_python_packages))?; + + let resolution = Resolution::from(resolution); + + // Print the overridden package requests + print_overridden_requests(package_requests.borrow().deref()); + + // Print any diagnostics + for diagnostic in resolution.diagnostics() { + tracing::warn!("{}", diagnostic.message()); + } + + let locked_packages = lock_pypi_packages( + conda_python_packages, + &lazy_build_dispatch, + ®istry_client, + resolution, + &context.capabilities, + context.concurrency.downloads, + project_root, + &original_git_references, + ) + .await + .map_err(|e| SolveError::Locking(e.into()))?; + + let conda_task = lazy_build_dispatch.conda_task; + + Ok::<_, SolveError>((locked_packages, conda_task)) }); // We try to distinguish between build dispatch panics and any other panics that occur - let resolution = match resolution_future.catch_unwind().await { + let (locked_packages, conda_task) = match resolution_future.catch_unwind().await { Ok(result) => result?, Err(panic_payload) => { - // Try to get the stored initialization error from the lazy build dispatch - if let Some(stored_error) = lazy_build_dispatch.last_initialization_error() { + // Try to get the stored initialization error from the last_error holder + if let Some(stored_error) = last_error.get() { return Err(SolveError::BuildDispatchPanic { message: format!("{stored_error}"), } @@ -780,30 +815,6 @@ pub async fn resolve_pypi( } } }; - let resolution = Resolution::from(resolution); - - // Print the overridden package requests - print_overridden_requests(package_requests.borrow().deref()); - - // Print any diagnostics - for diagnostic in resolution.diagnostics() { - tracing::warn!("{}", diagnostic.message()); - } - - // Collect resolution into locked packages - let locked_packages = lock_pypi_packages( - conda_python_packages, - &lazy_build_dispatch, - ®istry_client, - resolution, - &context.capabilities, - context.concurrency.downloads, - project_root, - &original_git_references, - ) - .await?; - - let conda_task = lazy_build_dispatch.conda_task; Ok((locked_packages, conda_task)) }