|
| 1 | +use std::collections::BTreeMap; |
| 2 | + |
| 3 | +use miette::Diagnostic; |
| 4 | +use pixi_build_discovery::EnabledProtocols; |
| 5 | +use pixi_build_types::procedures::conda_outputs::{CondaOutput, CondaOutputDependencies}; |
| 6 | +use pixi_record::PinnedSourceSpec; |
| 7 | +use rattler_conda_types::{ChannelConfig, ChannelUrl, PackageName}; |
| 8 | +use thiserror::Error; |
| 9 | +use tracing::instrument; |
| 10 | + |
| 11 | +use crate::{ |
| 12 | + BuildBackendMetadataError, BuildBackendMetadataSpec, BuildEnvironment, CommandDispatcher, |
| 13 | + CommandDispatcherError, CommandDispatcherErrorResultExt, |
| 14 | + build::source_metadata_cache::MetadataKind, |
| 15 | +}; |
| 16 | + |
| 17 | +/// A specification for retrieving the dependencies of a specific output from a |
| 18 | +/// source package. |
| 19 | +#[derive(Debug, Clone, Eq, PartialEq, Hash, serde::Serialize)] |
| 20 | +pub struct GetOutputDependenciesSpec { |
| 21 | + /// The source specification. This should be a pinned source (e.g., a |
| 22 | + /// specific git commit) to ensure reproducibility. |
| 23 | + pub source: PinnedSourceSpec, |
| 24 | + |
| 25 | + /// The name of the output to retrieve dependencies for. |
| 26 | + pub output_name: PackageName, |
| 27 | + |
| 28 | + /// The channel configuration to use for the build backend. |
| 29 | + pub channel_config: ChannelConfig, |
| 30 | + |
| 31 | + /// The channels to use for solving. |
| 32 | + #[serde(skip_serializing_if = "Vec::is_empty")] |
| 33 | + pub channels: Vec<ChannelUrl>, |
| 34 | + |
| 35 | + /// Information about the build environment. |
| 36 | + pub build_environment: BuildEnvironment, |
| 37 | + |
| 38 | + /// Variant configuration |
| 39 | + pub variants: Option<BTreeMap<String, Vec<String>>>, |
| 40 | + |
| 41 | + /// The protocols that are enabled for this source |
| 42 | + #[serde(skip_serializing_if = "crate::is_default")] |
| 43 | + pub enabled_protocols: EnabledProtocols, |
| 44 | +} |
| 45 | + |
| 46 | +/// The dependencies of a specific output from a source package. |
| 47 | +#[derive(Debug, Clone)] |
| 48 | +pub struct OutputDependencies { |
| 49 | + /// The build dependencies of the package. These refer to the packages that |
| 50 | + /// should be installed in the "build" environment. |
| 51 | + pub build_dependencies: Option<CondaOutputDependencies>, |
| 52 | + |
| 53 | + /// The "host" dependencies of the package. These refer to the packages that |
| 54 | + /// should be installed to be able to refer to them from the build process |
| 55 | + /// but not run them. |
| 56 | + pub host_dependencies: Option<CondaOutputDependencies>, |
| 57 | + |
| 58 | + /// The dependencies for the run environment of the package. |
| 59 | + pub run_dependencies: CondaOutputDependencies, |
| 60 | +} |
| 61 | + |
| 62 | +impl GetOutputDependenciesSpec { |
| 63 | + #[instrument( |
| 64 | + skip_all, |
| 65 | + name = "output-dependencies", |
| 66 | + fields( |
| 67 | + source = %self.source, |
| 68 | + output = %self.output_name.as_source(), |
| 69 | + platform = %self.build_environment.host_platform, |
| 70 | + ) |
| 71 | + )] |
| 72 | + pub(crate) async fn request( |
| 73 | + self, |
| 74 | + command_dispatcher: CommandDispatcher, |
| 75 | + ) -> Result<OutputDependencies, CommandDispatcherError<GetOutputDependenciesError>> { |
| 76 | + // Get the metadata from the build backend. |
| 77 | + let backend_metadata_spec = BuildBackendMetadataSpec { |
| 78 | + source: self.source.clone(), |
| 79 | + channel_config: self.channel_config, |
| 80 | + channels: self.channels, |
| 81 | + build_environment: self.build_environment, |
| 82 | + variants: self.variants, |
| 83 | + enabled_protocols: self.enabled_protocols, |
| 84 | + }; |
| 85 | + |
| 86 | + let build_backend_metadata = command_dispatcher |
| 87 | + .build_backend_metadata(backend_metadata_spec) |
| 88 | + .await |
| 89 | + .map_err_with(GetOutputDependenciesError::BuildBackendMetadata)?; |
| 90 | + |
| 91 | + // Extract the outputs from the metadata. |
| 92 | + let outputs = match &build_backend_metadata.metadata.metadata { |
| 93 | + MetadataKind::Outputs { outputs } => outputs, |
| 94 | + MetadataKind::GetMetadata { .. } => { |
| 95 | + return Err(CommandDispatcherError::Failed( |
| 96 | + GetOutputDependenciesError::UnsupportedProtocol, |
| 97 | + )); |
| 98 | + } |
| 99 | + }; |
| 100 | + |
| 101 | + // Find the output with the matching name. |
| 102 | + let output = outputs |
| 103 | + .iter() |
| 104 | + .find(|output| output.metadata.name == self.output_name) |
| 105 | + .ok_or_else(|| { |
| 106 | + CommandDispatcherError::Failed(GetOutputDependenciesError::OutputNotFound { |
| 107 | + output_name: self.output_name.clone(), |
| 108 | + available_outputs: outputs.iter().map(|o| o.metadata.name.clone()).collect(), |
| 109 | + }) |
| 110 | + })?; |
| 111 | + |
| 112 | + // Extract and return the dependencies. |
| 113 | + Ok(extract_dependencies(output)) |
| 114 | + } |
| 115 | +} |
| 116 | + |
| 117 | +/// Extracts the dependencies from a CondaOutput. |
| 118 | +fn extract_dependencies(output: &CondaOutput) -> OutputDependencies { |
| 119 | + OutputDependencies { |
| 120 | + build_dependencies: output.build_dependencies.clone(), |
| 121 | + host_dependencies: output.host_dependencies.clone(), |
| 122 | + run_dependencies: output.run_dependencies.clone(), |
| 123 | + } |
| 124 | +} |
| 125 | + |
| 126 | +#[derive(Debug, Error, Diagnostic)] |
| 127 | +pub enum GetOutputDependenciesError { |
| 128 | + #[error(transparent)] |
| 129 | + #[diagnostic(transparent)] |
| 130 | + BuildBackendMetadata(#[from] BuildBackendMetadataError), |
| 131 | + |
| 132 | + #[error( |
| 133 | + "the build backend does not support the `conda/outputs` procedure, which is required to retrieve output-specific dependencies" |
| 134 | + )] |
| 135 | + UnsupportedProtocol, |
| 136 | + |
| 137 | + #[error( |
| 138 | + "the output '{}' was not found in the source package. Available outputs: {}", |
| 139 | + output_name.as_source(), |
| 140 | + available_outputs.iter().map(|n| n.as_source()).collect::<Vec<_>>().join(", ") |
| 141 | + )] |
| 142 | + OutputNotFound { |
| 143 | + output_name: PackageName, |
| 144 | + available_outputs: Vec<PackageName>, |
| 145 | + }, |
| 146 | +} |
0 commit comments