Skip to content
Closed
Show file tree
Hide file tree
Changes from 21 commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
ba69812
feat: add type for pixi build api
baszalmstra Jun 23, 2025
55ba704
wip
baszalmstra Jun 24, 2025
cad9fed
wip: split fetching backend metadata from source metadata
baszalmstra Jun 24, 2025
901295a
feat: extract run dependencies from outputs
baszalmstra Jun 24, 2025
e0813e1
wip
baszalmstra Jun 24, 2025
e0882c3
feat: finish
baszalmstra Jun 26, 2025
c0a5190
simplify run export conversion
baszalmstra Jun 26, 2025
596d31f
wip
baszalmstra Jun 26, 2025
fac9102
fix: small fixes
baszalmstra Jun 27, 2025
c45d91e
fix: source anchor
baszalmstra Jun 27, 2025
4a11e8e
feat: read capatbilities from backend
baszalmstra Jun 27, 2025
23477da
remove cache for now
baszalmstra Jun 30, 2025
053f020
more small fixes
baszalmstra Jun 30, 2025
4076790
fix: revert platform removals
baszalmstra Jun 30, 2025
1b7d612
Merge remote-tracking branch 'upstream/main' into feat/split-protocol
baszalmstra Jun 30, 2025
72dd129
fix build error
baszalmstra Jun 30, 2025
c6da81f
fmt
baszalmstra Jun 30, 2025
24d09ff
update snapshots
baszalmstra Jun 30, 2025
3c1300d
small fixes
baszalmstra Jun 30, 2025
7cdfb7a
Apply suggestions from code review
baszalmstra Jul 1, 2025
f7f48ab
Adapt comments
Hofer-Julian Jul 1, 2025
cb38383
Update crates/pixi_command_dispatcher/src/command_dispatcher_processo…
baszalmstra Jul 1, 2025
151ff42
Merge remote-tracking branch 'upstream/main' into feat/split-protocol
baszalmstra Jul 1, 2025
7f3d7ab
Merge remote-tracking branch 'upstream/main' into feat/split-protocol
baszalmstra Jul 1, 2025
380990b
fix: rename identifier to metadata
baszalmstra Jul 3, 2025
4d2405b
Merge remote-tracking branch 'upstream/main' into feat/split-protocol
baszalmstra Jul 3, 2025
b941579
fmt
baszalmstra Jul 3, 2025
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
14 changes: 8 additions & 6 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 Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ pep508_rs = "0.9.2"
percent-encoding = "2.3.1"
pin-project-lite = "0.2.16"
pyproject-toml = "0.13.4"
rand = { version = "0.9.1", default-features = false }
regex = "1.11.1"
reqwest = { version = "0.12.12", default-features = false }
reqwest-middleware = "0.4"
Expand Down
14 changes: 6 additions & 8 deletions crates/pixi_build_discovery/src/backend_spec.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
use std::str::FromStr;

use pixi_spec::BinarySpec;
use pixi_spec_containers::DependencyMap;
use rattler_conda_types::{Channel, ChannelConfig, ChannelUrl, NamelessMatchSpec};
use rattler_conda_types::{Channel, ChannelConfig, ChannelUrl};
use url::Url;

/// Describes how a backend should be instantiated.
Expand Down Expand Up @@ -54,21 +55,21 @@ pub struct SystemCommandSpec {
#[cfg_attr(feature = "serde", serde(rename_all = "kebab-case"))]
pub struct EnvironmentSpec {
/// The main requirement
pub requirement: (rattler_conda_types::PackageName, NamelessMatchSpec),
pub requirement: (rattler_conda_types::PackageName, BinarySpec),

/// The requirements for the environment.
#[cfg_attr(
feature = "serde",
serde(skip_serializing_if = "DependencyMap::is_empty")
)]
pub additional_requirements: DependencyMap<rattler_conda_types::PackageName, NamelessMatchSpec>,
pub additional_requirements: DependencyMap<rattler_conda_types::PackageName, BinarySpec>,

/// Additional constraints to apply to the environment
#[cfg_attr(
feature = "serde",
serde(skip_serializing_if = "DependencyMap::is_empty")
)]
pub constraints: DependencyMap<rattler_conda_types::PackageName, NamelessMatchSpec>,
pub constraints: DependencyMap<rattler_conda_types::PackageName, BinarySpec>,

/// The channels to use for solving
pub channels: Vec<ChannelUrl>,
Expand All @@ -92,10 +93,7 @@ impl JsonRpcBackendSpec {
Self {
name: DEFAULT_BUILD_TOOL.to_string(),
command: CommandSpec::EnvironmentSpec(Box::new(EnvironmentSpec {
requirement: (
DEFAULT_BUILD_TOOL.parse().unwrap(),
NamelessMatchSpec::default(),
),
requirement: (DEFAULT_BUILD_TOOL.parse().unwrap(), BinarySpec::any()),
additional_requirements: Default::default(),
constraints: Default::default(),
channels: vec![conda_forge_channel, backends_channel],
Expand Down
13 changes: 2 additions & 11 deletions crates/pixi_build_discovery/src/discovery.rs
Original file line number Diff line number Diff line change
Expand Up @@ -193,21 +193,12 @@ impl DiscoveredBackend {
let build_system = package_manifest.build.clone();
let requirement = (
build_system.backend.name.clone(),
build_system
.backend
.spec
.clone()
.try_into_nameless_match_spec(channel_config)?,
build_system.backend.spec.clone(),
);
let additional_requirements = build_system
.additional_dependencies
.iter()
.map(|(name, spec)| {
Ok((
name.clone(),
spec.clone().try_into_nameless_match_spec(channel_config)?,
))
})
.map(|(name, spec)| Ok((name.clone(), spec.clone())))
.collect::<Result<_, SpecConversionError>>()?;

// Figure out the channels to use
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ backend-spec:
type: environment-spec
requirement:
- pixi-build-rattler-build
- {}
- "*"
channels:
- "https://conda.anaconda.org/conda-forge"
- "https://prefix.dev/pixi-build-backends"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ backend-spec:
type: environment-spec
requirement:
- pixi-build-rattler-build
- {}
- "*"
channels:
- "https://conda.anaconda.org/conda-forge"
- "https://prefix.dev/pixi-build-backends"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ backend-spec:
type: environment-spec
requirement:
- pixi-build-rattler-build
- {}
- "*"
channels:
- "https://conda.anaconda.org/conda-forge"
- "https://prefix.dev/pixi-build-backends"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ backend-spec:
type: environment-spec
requirement:
- pixi-build-rattler-build
- {}
- "*"
channels:
- "https://conda.anaconda.org/conda-forge"
- "https://prefix.dev/pixi-build-backends"
Expand Down
79 changes: 69 additions & 10 deletions crates/pixi_build_frontend/src/backend/json_rpc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,13 @@ use std::{
sync::Arc,
};

use super::stderr::{stderr_buffer, stream_stderr};
use crate::{
backend::BackendOutputStream,
error::BackendError,
jsonrpc::{RpcParams, stdio_transport},
tool::Tool,
};
use jsonrpsee::{
async_client::{Client, ClientBuilder},
core::{
Expand All @@ -12,8 +19,9 @@ use jsonrpsee::{
types::ErrorCode,
};
use miette::Diagnostic;
use pixi_build_types::procedures::conda_outputs::{CondaOutputsParams, CondaOutputsResult};
use pixi_build_types::{
FrontendCapabilities, ProjectModelV1, VersionedProjectModel, procedures,
BackendCapabilities, FrontendCapabilities, ProjectModelV1, VersionedProjectModel, procedures,
procedures::{
conda_build::{CondaBuildParams, CondaBuildResult},
conda_metadata::{CondaMetadataParams, CondaMetadataResult},
Expand All @@ -28,14 +36,6 @@ use tokio::{
sync::{Mutex, oneshot},
};

use super::stderr::{stderr_buffer, stream_stderr};
use crate::{
backend::BackendOutputStream,
error::BackendError,
jsonrpc::{RpcParams, stdio_transport},
tool::Tool,
};

#[derive(Debug, Error, Diagnostic)]
pub enum BuildBackendSetupError {
#[error("an unexpected io error occurred while communicating with the pixi build backend")]
Expand Down Expand Up @@ -120,6 +120,8 @@ impl CommunicationError {
pub struct JsonRpcBackend {
/// The identifier of the backend.
backend_identifier: String,
/// The capabilities of the backend.
backend_capabilities: BackendCapabilities,
/// The JSON-RPC client to communicate with the backend.
client: Client,
/// The path to the manifest that is passed to the backend.
Expand Down Expand Up @@ -209,7 +211,7 @@ impl JsonRpcBackend {
.build_with_tokio(sender, receiver);

// Negotiate the capabilities with the backend.
let _negotiate_result: NegotiateCapabilitiesResult = client
let negotiate_result: NegotiateCapabilitiesResult = client
.request(
procedures::negotiate_capabilities::METHOD_NAME,
RpcParams::from(NegotiateCapabilitiesParams {
Expand Down Expand Up @@ -253,6 +255,7 @@ impl JsonRpcBackend {
Ok(Self {
client,
backend_identifier,
backend_capabilities: negotiate_result.capabilities,
manifest_path,
stderr: stderr.map(Mutex::new).map(Arc::new),
})
Expand Down Expand Up @@ -360,8 +363,64 @@ impl JsonRpcBackend {
})
}

/// Call the `conda/outputs` method on the backend.
pub async fn conda_outputs(
&self,
request: CondaOutputsParams,
) -> Result<CondaOutputsResult, CommunicationError> {
// Capture all of stderr and discard it
let stderr = self.stderr.as_ref().map(|stderr| {
// Cancellation signal
let (cancel_tx, cancel_rx) = oneshot::channel();
// Spawn the stderr forwarding task
let handle = tokio::spawn(stderr_buffer(stderr.clone(), cancel_rx));
(cancel_tx, handle)
});

let result = self
.client
.request(
procedures::conda_outputs::METHOD_NAME,
RpcParams::from(request),
)
.await;

// Wait for the stderr sink to finish, by signaling it to stop
let backend_output = if let Some((cancel_tx, handle)) = stderr {
// Cancel the stderr forwarding. Ignore any error because that means the
// tasks also finished.
let _err = cancel_tx.send(());
let lines = handle.await.map_or_else(
|e| match e.try_into_panic() {
Ok(panic) => std::panic::resume_unwind(panic),
Err(_) => Err(CommunicationError::StdErrPipeStopped),
},
|e| e.map_err(|_| CommunicationError::StdErrPipeStopped),
)?;

Some(lines)
} else {
None
};

result.map_err(|err| {
CommunicationError::from_client_error(
self.backend_identifier.clone(),
err,
procedures::conda_metadata::METHOD_NAME,
self.manifest_path.parent().unwrap_or(&self.manifest_path),
backend_output,
)
})
}

/// Returns the backend identifier.
pub fn identifier(&self) -> &str {
&self.backend_identifier
}

/// Returns the advertised capabilities of the backend.
pub fn capabilities(&self) -> &BackendCapabilities {
&self.backend_capabilities
}
}
65 changes: 55 additions & 10 deletions crates/pixi_build_frontend/src/backend/mod.rs
Original file line number Diff line number Diff line change
@@ -1,31 +1,60 @@
use pixi_build_types::procedures::{
conda_build::{CondaBuildParams, CondaBuildResult},
conda_metadata::{CondaMetadataParams, CondaMetadataResult},
use pixi_build_types::{
BackendCapabilities, PixiBuildApiVersion,
procedures::{
conda_build::{CondaBuildParams, CondaBuildResult},
conda_metadata::{CondaMetadataParams, CondaMetadataResult},
conda_outputs::{CondaOutputsParams, CondaOutputsResult},
},
};

mod stderr;

use crate::json_rpc::CommunicationError;

pub mod json_rpc;

pub enum Backend {
pub struct Backend {
/// The backend that is used to communicate with the build server.
inner: BackendImplementation,

/// The API version that the backend supports.
pub api_version: PixiBuildApiVersion,
}

pub enum BackendImplementation {
/// The backend is a JSON-RPC backend.
JsonRpc(json_rpc::JsonRpcBackend),
}

impl From<json_rpc::JsonRpcBackend> for BackendImplementation {
fn from(json_rpc: json_rpc::JsonRpcBackend) -> Self {
BackendImplementation::JsonRpc(json_rpc)
}
}

impl Backend {
pub fn new(inner: BackendImplementation, api_version: PixiBuildApiVersion) -> Self {
Self { inner, api_version }
}

pub fn identifier(&self) -> String {
match self {
Backend::JsonRpc(json_rpc) => json_rpc.identifier().to_string(),
match &self.inner {
BackendImplementation::JsonRpc(json_rpc) => json_rpc.identifier().to_string(),
}
}

pub fn capabilities(&self) -> &BackendCapabilities {
match &self.inner {
BackendImplementation::JsonRpc(json_rpc) => json_rpc.capabilities(),
}
}

pub async fn conda_get_metadata(
&self,
params: CondaMetadataParams,
) -> Result<CondaMetadataResult, CommunicationError> {
match self {
Backend::JsonRpc(json_rpc) => json_rpc.conda_get_metadata(params).await,
match &self.inner {
BackendImplementation::JsonRpc(json_rpc) => json_rpc.conda_get_metadata(params).await,
}
}

Expand All @@ -34,8 +63,24 @@ impl Backend {
params: CondaBuildParams,
output_stream: W,
) -> Result<CondaBuildResult, CommunicationError> {
match self {
Backend::JsonRpc(json_rpc) => json_rpc.conda_build(params, output_stream).await,
match &self.inner {
BackendImplementation::JsonRpc(json_rpc) => {
json_rpc.conda_build(params, output_stream).await
}
}
}

/// Returns the outputs that this backend can produce.
pub async fn conda_outputs(
&self,
params: CondaOutputsParams,
) -> Result<CondaOutputsResult, CommunicationError> {
assert!(
self.api_version.supports_conda_outputs(),
"This backend does not support the conda outputs procedure"
);
match &self.inner {
BackendImplementation::JsonRpc(json_rpc) => json_rpc.conda_outputs(params).await,
}
}
}
Expand Down
Loading
Loading