diff --git a/Cargo.lock b/Cargo.lock index ef4ec03655..681d0d6117 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4445,14 +4445,10 @@ dependencies = [ "rattler_shell", "rattler_solve", "rattler_virtual_packages", - "regex", "reqwest-middleware", "rstest", "serde", "serde_json", - "serde_with", - "serde_yaml", - "sha1", "tempfile", "thiserror 2.0.11", "tokio", diff --git a/crates/pixi_build_frontend/Cargo.toml b/crates/pixi_build_frontend/Cargo.toml index 6caeada318..149324f081 100644 --- a/crates/pixi_build_frontend/Cargo.toml +++ b/crates/pixi_build_frontend/Cargo.toml @@ -30,13 +30,9 @@ rattler_repodata_gateway = { workspace = true, features = ["gateway"] } rattler_shell = { workspace = true } rattler_solve = { workspace = true, features = ["resolvo"] } rattler_virtual_packages = { workspace = true } -regex = { workspace = true } reqwest-middleware = { workspace = true } serde = { workspace = true, features = ["derive"] } serde_json = { workspace = true } -serde_with = { workspace = true } -serde_yaml = { workspace = true } -sha1 = { workspace = true } thiserror = { workspace = true } tokio = { workspace = true, features = ["process", "io-std"] } tokio-util = { workspace = true, features = ["codec"] } diff --git a/crates/pixi_build_frontend/src/build_frontend.rs b/crates/pixi_build_frontend/src/build_frontend.rs index d025bfa161..dd90b52936 100644 --- a/crates/pixi_build_frontend/src/build_frontend.rs +++ b/crates/pixi_build_frontend/src/build_frontend.rs @@ -7,7 +7,8 @@ use rattler_conda_types::ChannelConfig; use crate::{ protocol, protocol_builder::{EnabledProtocols, ProtocolBuilder}, - Protocol, SetupRequest, ToolContext, + protocols::JsonRPCBuildProtocol, + SetupRequest, ToolContext, }; /// The frontend for building packages. @@ -94,12 +95,12 @@ impl BuildFrontend { } } - /// Constructs a new [`Protocol`] for the given request. This object can be + /// Constructs a new [`JsonRPCBuildProtocol`] for the given request. This object can be /// used to build the package. pub async fn setup_protocol( &self, request: SetupRequest, - ) -> Result { + ) -> Result { // Determine the build protocol to use for the source directory. let protocol = ProtocolBuilder::discover(&request.source_dir, &self.enabled_protocols)? .with_channel_config(self.channel_config.clone()) diff --git a/crates/pixi_build_frontend/src/lib.rs b/crates/pixi_build_frontend/src/lib.rs index e2d1d6f6b6..72e29df7cf 100644 --- a/crates/pixi_build_frontend/src/lib.rs +++ b/crates/pixi_build_frontend/src/lib.rs @@ -6,7 +6,8 @@ mod protocols; use std::fmt::{Debug, Formatter}; -pub use protocols::builders::{conda_protocol, pixi_protocol, rattler_build_protocol}; +pub use protocols::builders::{pixi_protocol, rattler_build_protocol}; +pub use protocols::JsonRPCBuildProtocol; mod protocol_builder; mod reporters; @@ -21,8 +22,6 @@ use tokio::io::{AsyncRead, AsyncWrite}; pub use tool::{IsolatedToolSpec, SystemToolSpec, ToolContext, ToolSpec}; use url::Url; -pub use crate::protocol::Protocol; - pub use backend_override::BackendOverride; pub use protocol_builder::EnabledProtocols; diff --git a/crates/pixi_build_frontend/src/protocol.rs b/crates/pixi_build_frontend/src/protocol.rs index 39a926f4ce..020de50ac6 100644 --- a/crates/pixi_build_frontend/src/protocol.rs +++ b/crates/pixi_build_frontend/src/protocol.rs @@ -1,18 +1,8 @@ -use std::{path::PathBuf, sync::Arc}; +use std::path::PathBuf; -use miette::{Diagnostic, IntoDiagnostic}; -use pixi_build_types::procedures::{ - conda_build::{CondaBuildParams, CondaBuildResult}, - conda_metadata::{CondaMetadataParams, CondaMetadataResult}, -}; +use miette::Diagnostic; -use crate::{ - protocols::{ - builders::{conda_protocol, pixi_protocol, rattler_build_protocol}, - JsonRPCBuildProtocol, - }, - CondaBuildReporter, CondaMetadataReporter, -}; +use crate::protocols::builders::{pixi_protocol, rattler_build_protocol}; /// Top-level error type for protocol errors. #[derive(Debug, thiserror::Error, Diagnostic)] @@ -21,10 +11,6 @@ pub enum FinishError { #[diagnostic(transparent)] Pixi(#[from] pixi_protocol::FinishError), - #[error(transparent)] - #[diagnostic(transparent)] - CondaBuild(#[from] conda_protocol::FinishError), - #[error(transparent)] #[diagnostic(transparent)] RattlerBuild(#[from] rattler_build_protocol::FinishError), @@ -54,95 +40,3 @@ pub enum DiscoveryError { #[diagnostic(transparent)] RattlerBuild(#[from] rattler_build_protocol::ProtocolBuildError), } - -/// A protocol describes how to communicate with a build backend. A build -/// backend is a tool that is invoked to generate certain output. -/// -/// The frontend can support multiple backends, and the protocol is used to -/// determine which backend to use for a given source directory and how to -/// communicate with it. -/// -/// -/// The [`Protocol`] protocol is a generic implementation that uses a -/// client-server JSON-RPC interface to communicate with another tool. -/// -/// Using this JSON-RPC interface means we can evolve the backend and frontend -/// tools as long as both tools can establish a shared protocol. The JSON-RPC -/// protocol is engineered in such a way that this is possible. This allows a -/// much newer frontend to still be able to interact with a very old backend -/// which is important if you want to be able to use very old packages in the -/// far future. -/// -/// The conda-build and rattler-build implementations are a hard-coded -/// implementation and do not use a client-server model. Although technically -/// they could also be implemented using the client-server model it is more -/// ergonomic to add their implementation directly into the frontend because no -/// bridge executable is needed. We can always add this later too using the -/// existing protocol. -// I think because we mostly have a single variant in use, boxing does not make -// sense here. -#[allow(clippy::large_enum_variant)] -#[derive(Debug)] -pub enum Protocol { - PixiBuild(JsonRPCBuildProtocol), - // It should be more like subprocess protocol - // as we invoke the tool directly - CondaBuild(conda_protocol::Protocol), -} - -impl From for Protocol { - fn from(value: JsonRPCBuildProtocol) -> Self { - Self::PixiBuild(value) - } -} - -impl From for Protocol { - fn from(value: conda_protocol::Protocol) -> Self { - Self::CondaBuild(value) - } -} - -impl Protocol { - /// Returns the root manifest files of the source directory. These indicate - /// the files that are used to determine the build configuration. - pub fn manifests(&self) -> Vec { - match self { - Self::PixiBuild(protocol) => protocol.manifests(), - Self::CondaBuild(protocol) => protocol.manifests(), - } - } - - pub async fn conda_get_metadata( - &self, - request: &CondaMetadataParams, - reporter: Arc, - ) -> miette::Result { - match self { - Self::PixiBuild(protocol) => Ok(protocol - .conda_get_metadata(request, reporter.as_ref()) - .await?), - Self::CondaBuild(protocol) => protocol.conda_get_metadata(request), - } - } - - pub async fn conda_build( - &self, - request: &CondaBuildParams, - reporter: Arc, - ) -> miette::Result { - match self { - Self::PixiBuild(protocol) => protocol - .conda_build(request, reporter.as_ref()) - .await - .into_diagnostic(), - Self::CondaBuild(_) => unreachable!(), - } - } - - pub fn identifier(&self) -> &str { - match self { - Self::PixiBuild(protocol) => protocol.backend_identifier(), - Self::CondaBuild(protocol) => protocol.backend_identifier(), - } - } -} diff --git a/crates/pixi_build_frontend/src/protocol_builder.rs b/crates/pixi_build_frontend/src/protocol_builder.rs index 168d8010cb..a5ac173ecc 100644 --- a/crates/pixi_build_frontend/src/protocol_builder.rs +++ b/crates/pixi_build_frontend/src/protocol_builder.rs @@ -8,9 +8,10 @@ use rattler_conda_types::ChannelConfig; use crate::{ backend_override::BackendOverride, - conda_protocol, pixi_protocol, + pixi_protocol, protocol::{DiscoveryError, FinishError}, - rattler_build_protocol, BuildFrontendError, Protocol, ToolContext, + protocols::JsonRPCBuildProtocol, + rattler_build_protocol, BuildFrontendError, ToolContext, }; /// Configuration to enable or disable certain protocols discovery. @@ -20,8 +21,6 @@ pub struct EnabledProtocols { pub enable_rattler_build: bool, /// Enable the pixi protocol. pub enable_pixi: bool, - /// Enable the conda-build protocol. - pub enable_conda_build: bool, } impl Default for EnabledProtocols { @@ -30,19 +29,17 @@ impl Default for EnabledProtocols { Self { enable_rattler_build: true, enable_pixi: true, - enable_conda_build: true, } } } #[derive(Debug)] +// for some reason, the clippy calculates wrong the size +// for this enum variants, so we need to disable the warning +#[allow(clippy::large_enum_variant)] pub(crate) enum ProtocolBuilder { /// A pixi project. - Pixi(Box), - - /// A directory containing a `meta.yaml` that can be interpreted by - /// conda-build. - CondaBuild(conda_protocol::ProtocolBuilder), + Pixi(pixi_protocol::ProtocolBuilder), /// A directory containing a `recipe.yaml` that can be built with /// rattler-build. @@ -51,13 +48,7 @@ pub(crate) enum ProtocolBuilder { impl From for ProtocolBuilder { fn from(value: pixi_protocol::ProtocolBuilder) -> Self { - Self::Pixi(Box::new(value)) - } -} - -impl From for ProtocolBuilder { - fn from(value: conda_protocol::ProtocolBuilder) -> Self { - Self::CondaBuild(value) + Self::Pixi(value) } } @@ -110,14 +101,6 @@ impl ProtocolBuilder { } } - // Try to discover as a conda build project - if enabled_protocols.enable_conda_build { - if let Some(protocol) = conda_protocol::ProtocolBuilder::discover(source_path).unwrap() - { - return Ok(protocol.into()); - } - } - // TODO: Add additional formats later Err(DiscoveryError::UnsupportedFormat) } @@ -125,12 +108,7 @@ impl ProtocolBuilder { /// Sets the channel configuration used by the protocol. pub fn with_channel_config(self, channel_config: ChannelConfig) -> Self { match self { - Self::Pixi(protocol) => { - Self::Pixi(Box::new(protocol.with_channel_config(channel_config))) - } - Self::CondaBuild(protocol) => { - Self::CondaBuild(protocol.with_channel_config(channel_config)) - } + Self::Pixi(protocol) => Self::Pixi(protocol.with_channel_config(channel_config)), Self::RattlerBuild(protocol) => { Self::RattlerBuild(protocol.with_channel_config(channel_config)) } @@ -141,10 +119,7 @@ impl ProtocolBuilder { if let Some(backend_override) = backend_override { match self { Self::Pixi(protocol) => { - Self::Pixi(Box::new(protocol.with_backend_override(backend_override))) - } - Self::CondaBuild(protocol) => { - Self::CondaBuild(protocol.with_backend_override(backend_override)) + Self::Pixi(protocol.with_backend_override(backend_override)) } Self::RattlerBuild(protocol) => { Self::RattlerBuild(protocol.with_backend_override(backend_override)) @@ -158,12 +133,7 @@ impl ProtocolBuilder { /// Sets the cache directory to use for any caching. pub fn with_opt_cache_dir(self, cache_directory: Option) -> Self { match self { - Self::Pixi(protocol) => { - Self::Pixi(Box::new(protocol.with_opt_cache_dir(cache_directory))) - } - Self::CondaBuild(protocol) => { - Self::CondaBuild(protocol.with_opt_cache_dir(cache_directory)) - } + Self::Pixi(protocol) => Self::Pixi(protocol.with_opt_cache_dir(cache_directory)), Self::RattlerBuild(protocol) => { Self::RattlerBuild(protocol.with_opt_cache_dir(cache_directory)) } @@ -174,7 +144,6 @@ impl ProtocolBuilder { pub fn name(&self) -> &str { match self { Self::Pixi(_) => "pixi", - Self::CondaBuild(_) => "conda-build", Self::RattlerBuild(_) => "rattler-build", } } @@ -184,23 +153,16 @@ impl ProtocolBuilder { self, tool_context: Arc, build_id: usize, - ) -> Result { + ) -> Result { match self { Self::Pixi(protocol) => Ok(protocol .finish(tool_context, build_id) .await - .map_err(FinishError::Pixi)? - .into()), - Self::CondaBuild(protocol) => Ok(protocol - .finish(tool_context, build_id) - .await - .map_err(FinishError::CondaBuild)? - .into()), + .map_err(FinishError::Pixi)?), Self::RattlerBuild(protocol) => Ok(protocol .finish(tool_context, build_id) .await - .map_err(FinishError::RattlerBuild)? - .into()), + .map_err(FinishError::RattlerBuild)?), } } } diff --git a/crates/pixi_build_frontend/src/protocols/builders/conda_build/mod.rs b/crates/pixi_build_frontend/src/protocols/builders/conda_build/mod.rs deleted file mode 100644 index 5a311d5cbc..0000000000 --- a/crates/pixi_build_frontend/src/protocols/builders/conda_build/mod.rs +++ /dev/null @@ -1,120 +0,0 @@ -mod protocol; - -use std::{ - convert::Infallible, - path::{Path, PathBuf}, - sync::Arc, -}; - -use miette::Diagnostic; -pub use protocol::Protocol; -use rattler_conda_types::ChannelConfig; -use thiserror::Error; - -use crate::{ - backend_override::BackendOverride, - tool::{IsolatedToolSpec, ToolCacheError, ToolSpec}, - ToolContext, -}; - -#[derive(Debug, Error, Diagnostic)] -pub enum FinishError { - #[error(transparent)] - Tool(#[from] ToolCacheError), -} - -/// A builder for constructing a [`protocol::Protocol`] instance. -#[derive(Debug)] -pub struct ProtocolBuilder { - /// The directory that contains the source files. - source_dir: PathBuf, - - /// The directory that contains the `meta.yaml` in the source directory. - recipe_dir: PathBuf, - - /// The backend tool to install. - backend_spec: ToolSpec, - - /// The channel configuration used by this instance. - channel_config: ChannelConfig, - - /// The cache directory the backend should use. (not used atm) - _cache_dir: Option, -} - -impl ProtocolBuilder { - /// Discovers the protocol for the given source directory. - pub fn discover(source_dir: &Path) -> Result, Infallible> { - let recipe_dir = source_dir.join("recipe"); - let protocol = if source_dir.join("meta.yaml").is_file() { - Self::new(source_dir, source_dir) - } else if recipe_dir.join("meta.yaml").is_file() { - Self::new(source_dir, &recipe_dir) - } else { - return Ok(None); - }; - - Ok(Some(protocol)) - } - - /// Constructs a new instance from a manifest. - pub fn new(source_dir: &Path, recipe_dir: &Path) -> Self { - let backend_spec = ToolSpec::Isolated(IsolatedToolSpec { - specs: vec!["conda-build".parse().unwrap()], - command: "conda-build".to_string(), - channels: vec!["conda-forge".parse().unwrap()], - }); - - Self { - source_dir: source_dir.to_path_buf(), - recipe_dir: recipe_dir.to_path_buf(), - backend_spec, - channel_config: ChannelConfig::default_with_root_dir(PathBuf::new()), - _cache_dir: None, - } - } - - /// Sets an optional backend override. - pub fn with_backend_override(self, backend_override: BackendOverride) -> Self { - if let Some(tool) = backend_override.overridden_tool("conda-build") { - Self { - backend_spec: tool.as_spec(), - ..self - } - } else { - self - } - } - - /// Sets the channel configuration used by this instance. - pub fn with_channel_config(self, channel_config: ChannelConfig) -> Self { - Self { - channel_config, - ..self - } - } - - /// Sets the cache directory the backend should use. - pub fn with_opt_cache_dir(self, cache_dir: Option) -> Self { - Self { - _cache_dir: cache_dir, - ..self - } - } - - pub async fn finish( - self, - tool: Arc, - _build_id: usize, - ) -> Result { - let tool = tool - .instantiate(self.backend_spec, &self.channel_config) - .await?; - Ok(Protocol { - _channel_config: self.channel_config, - tool, - source_dir: self.source_dir, - recipe_dir: self.recipe_dir, - }) - } -} diff --git a/crates/pixi_build_frontend/src/protocols/builders/conda_build/protocol.rs b/crates/pixi_build_frontend/src/protocols/builders/conda_build/protocol.rs deleted file mode 100644 index 0013e3df44..0000000000 --- a/crates/pixi_build_frontend/src/protocols/builders/conda_build/protocol.rs +++ /dev/null @@ -1,274 +0,0 @@ -use std::{path::PathBuf, sync::OnceLock}; - -use miette::{Context, IntoDiagnostic}; -use pixi_build_types::{ - procedures::conda_metadata::{CondaMetadataParams, CondaMetadataResult}, - CondaPackageMetadata, -}; -use rattler_conda_types::{ChannelConfig, NoArchType, PackageName, Platform, VersionWithSource}; -use regex::Regex; -use serde::{Deserialize, Serialize}; -use serde_with::serde_as; -use sha1::{Digest, Sha1}; - -use crate::tool::Tool; - -#[derive(Debug)] -pub struct Protocol { - pub(super) _channel_config: ChannelConfig, - pub(super) tool: Tool, - pub(super) source_dir: PathBuf, - pub(super) recipe_dir: PathBuf, -} - -impl Protocol { - /// Returns a unique identifier for the backend. - pub fn backend_identifier(&self) -> &str { - "conda-build" - } - - /// Returns the relative path from the source directory to the recipe. - pub fn manifests(&self) -> Vec { - self.recipe_dir - .strip_prefix(&self.source_dir) - .unwrap_or(&self.recipe_dir) - .join("meta.yaml") - .to_str() - .map(|s| s.to_string()) - .into_iter() - .collect() - } - - // Extract metadata from the recipe. - pub fn conda_get_metadata( - &self, - request: &CondaMetadataParams, - ) -> miette::Result { - // Construct a new tool that can be used to invoke conda-render instead of the - // original tool. - let conda_render_executable = String::from("conda-render"); - let conda_render_executable = if cfg!(windows) { - format!("{}.exe", conda_render_executable) - } else { - conda_render_executable - }; - - let conda_render_tool = self.tool.with_executable(conda_render_executable); - - // TODO: Properly pass channels - // TODO: Setup --exclusive-config-files - - let channels = request - .channel_base_urls - .iter() - .flatten() - .flat_map(|url| ["--channel", url.as_str()]); - - let output = conda_render_tool - .command() - // .arg("--verbose") - // This is currently apparently broken in conda-build.. - // .arg("--use-channeldata") - .arg("--override-channels") - .args(channels) - .arg(&self.recipe_dir) - .stderr(std::process::Stdio::inherit()) - .stdout(std::process::Stdio::piped()) - .output() - .into_diagnostic() - .context("failed to spawn conda-render executable")?; - - // Try to parse the contents of the output. - let stdout = String::from_utf8(output.stdout) - .into_diagnostic() - .context("failed to convert the output of conda-render to a valid utf-8 string")?; - - // Fail if the process did not exit successfully. - if !output.status.success() { - miette::bail!( - "conda-render returned with a non-zero exit code:\n{}", - stdout - ); - } - - // Parse the output of conda-render. - let rendered_recipes = extract_rendered_recipes(&stdout)?; - - let metadata = CondaMetadataResult { - packages: rendered_recipes - .into_iter() - .map(|(recipe, meta_yaml)| { - convert_conda_render_output(recipe).with_context(|| { - format!( - "failed to extract metadata from conda-render output:\n{}", - meta_yaml - ) - }) - }) - .collect::>()?, - input_globs: None, - }; - - Ok(metadata) - } -} - -/// Given output from `conda-render`, parse it into one or more -/// [`CondaRenderRecipe`]s. -fn extract_rendered_recipes( - rendered_recipe: &str, -) -> miette::Result> { - static OUTPUT_REGEX: OnceLock = OnceLock::new(); - let output_regex = OUTPUT_REGEX.get_or_init(|| { - Regex::new(r#"(?sR)Hash contents:\r?\n-{14}\r?\n(.+?)-{10}\r?\nmeta.yaml:\r?\n-{10}\r?\n(.+?)(?:-{14}|$)"#) - .unwrap() - }); - - let mut iter = output_regex.captures_iter(rendered_recipe).peekable(); - if iter.peek().is_none() { - miette::bail!( - "could not find metadata in conda-render output:\n{}", - rendered_recipe - ) - } - - iter.map(|captures| { - let hash = captures.get(1).unwrap().as_str().trim(); - let meta_yaml = captures.get(2).unwrap().as_str().trim(); - serde_yaml::from_str(meta_yaml) - .map(|recipe| { - ( - CondaRenderRecipe { - hash_content: hash.to_string().replace("\r\n", "\n"), - recipe, - }, - meta_yaml, - ) - }) - .into_diagnostic() - .with_context(|| format!("failed to parse the rendered recipe:\n{meta_yaml}")) - }) - .collect() -} - -/// Converts a [`CondaRenderRecipe`] output into a [`CondaPackageMetadata`]. -fn convert_conda_render_output(recipe: CondaRenderRecipe) -> miette::Result { - Ok(CondaPackageMetadata { - build: recipe.hash(), - name: recipe.recipe.package.name, - version: recipe.recipe.package.version, - build_number: recipe.recipe.build.number.unwrap_or(0), - subdir: if recipe.recipe.build.noarch.is_none() { - Platform::current() - } else { - Platform::NoArch - }, - depends: recipe.recipe.requirements.run, - constraints: recipe.recipe.requirements.run_constrained, - license: recipe.recipe.about.license, - license_family: recipe.recipe.about.license_family, - noarch: recipe.recipe.build.noarch, - }) -} - -#[derive(Debug, Deserialize, Serialize)] -struct CondaRenderRecipe { - hash_content: String, - recipe: RenderedRecipe, -} - -impl CondaRenderRecipe { - /// Determine the hash of the recipe. This is based on the user specified - /// hash or the hash computed from the hash content. - pub fn hash(&self) -> String { - // TODO: Verify if this logic is actually correct. - - if let Some(hash) = &self.recipe.build.string { - return hash.clone(); - } - - let mut hasher = Sha1::new(); - hasher.update(self.hash_content.as_bytes()); - let result = hasher.finalize(); - - const HASH_LENGTH: usize = 7; - - let res = format!("{:x}", result); - res[..HASH_LENGTH].to_string() - } -} - -#[derive(Debug, Deserialize, Serialize)] -struct RenderedRecipe { - package: RenderedPackage, - build: RenderedBuild, - requirements: RenderedRequirements, - about: RenderedAbout, -} - -#[derive(Debug, Deserialize, Serialize)] -struct RenderedPackage { - name: PackageName, - version: VersionWithSource, -} - -#[serde_as] -#[derive(Debug, Deserialize, Serialize)] -struct RenderedBuild { - #[serde_as(as = "Option>")] - #[serde(skip_serializing_if = "Option::is_none")] - number: Option, - #[serde(skip_serializing_if = "Option::is_none")] - string: Option, - #[serde(default, skip_serializing_if = "NoArchType::is_none")] - noarch: NoArchType, -} - -#[derive(Debug, Deserialize, Serialize)] -struct RenderedRequirements { - #[serde(default)] - run: Vec, - #[serde(default)] - run_constrained: Vec, -} - -#[derive(Debug, Deserialize, Serialize)] -struct RenderedAbout { - #[serde(skip_serializing_if = "Option::is_none")] - license: Option, - #[serde(skip_serializing_if = "Option::is_none")] - license_family: Option, -} - -#[cfg(test)] -mod test { - use std::path::Path; - - use itertools::Itertools; - use rstest::*; - - use super::*; - - #[rstest] - #[case::pinject("conda-render/pinject.txt")] - #[case::microarch("conda-render/microarch-level.txt")] - fn test_extract_rendered_recipe(#[case] path: &str) { - let rendered_recipe = fs_err::read_to_string( - Path::new(env!("CARGO_MANIFEST_DIR")) - .join("test-data") - .join(path), - ) - .unwrap(); - let rendered_recipe = extract_rendered_recipes(&rendered_recipe) - .into_iter() - .flatten() - .format_with("\n===\n", |(recipe, meta_yaml), formatter| { - formatter(&format_args!( - "{meta_yaml}\n---\n{}", - serde_yaml::to_string(&recipe).unwrap() - )) - }) - .to_string(); - insta::assert_snapshot!(path, rendered_recipe); - } -} diff --git a/crates/pixi_build_frontend/src/protocols/builders/conda_build/snapshots/pixi_build_frontend__protocols__builders__conda_build__protocol__test__conda-render__microarch-level.txt.snap b/crates/pixi_build_frontend/src/protocols/builders/conda_build/snapshots/pixi_build_frontend__protocols__builders__conda_build__protocol__test__conda-render__microarch-level.txt.snap deleted file mode 100644 index fe4a1aadc4..0000000000 --- a/crates/pixi_build_frontend/src/protocols/builders/conda_build/snapshots/pixi_build_frontend__protocols__builders__conda_build__protocol__test__conda-render__microarch-level.txt.snap +++ /dev/null @@ -1,144 +0,0 @@ ---- -source: crates/pixi_build_frontend/src/protocols/builders/conda_build/protocol.rs -expression: rendered_recipe ---- -package: - name: ppc64le-microarch-level - version: '8' -build: - noarch: generic - number: 2 - run_exports: - strong: - - _ppc64le-microarch-level >=8 - string: '2' -requirements: - build: [] - run: - - __unix -about: - description: 'Use the meta-package ppc64le-microarch-level in requirements/build - in conda - - recipes to set up the compiler flags and set up the virtual package - - requirements in the run requirements. - - - When building packages on CI, level=4 will not be guaranteed, so - - you can only use level<=3 to build. - - - The run_exports only has a lower bound and therefore a level=2 - - build can be installed on a level=3 user system. A tighter bound - - is not added because we want to be able to test both level=2 and - - level=3 on a CI machine with level=3. - - Therefore in order to prioritise the highest level, use the build - - number to prioritise the level. - - - Only supported on Linux and macOS. - - ' - home: https://github.com/conda-forge/microarch-level-feedstock - license: BSD-3-Clause - license_file: LICENSE.txt - summary: Meta package to build conda recipes with microarchitecture levels -extra: - copy_test_source_files: true - feedstock-name: microarch-level - final: true - parent_recipe: - name: microarch-level-split - path: F:\projects\microarch-level-feedstock\recipe - version: '8' - recipe-maintainers: - - isuruf ---- -hash_content: |- - {'__unix': '__unix', - 'channel_targets': 'conda-forge main', - 'family': 'ppc64le', - 'level': '8'} -recipe: - package: - name: ppc64le-microarch-level - version: '8' - build: - number: 2 - string: '2' - noarch: generic - requirements: - run: - - __unix - run_constrained: [] - about: - license: BSD-3-Clause - -=== -package: - name: _x86_64-microarch-level - version: '1' -build: - noarch: generic - number: 2 - string: 2_x86_64 -requirements: - build: [] - run: - - __archspec 1=x86_64 -about: - description: 'The meta-package _x86_64-microarch-level enforces the microarchitecture - in the - - user system. - - - Note that a user would need the archspec conda package installed - - in the base environment where conda/mamba is run from. - - - See x86_64-microarch-level for using this in conda recipes - - ' - home: https://github.com/conda-forge/microarch-level-feedstock - license: BSD-3-Clause - license_file: LICENSE.txt - summary: Meta package to build conda recipes with microarchitecture levels -extra: - copy_test_source_files: true - feedstock-name: microarch-level - final: true - parent_recipe: - name: microarch-level-split - path: F:\projects\microarch-level-feedstock\recipe - version: '1' - recipe-maintainers: - - isuruf ---- -hash_content: |- - {'__archspec': '__archspec 1=x86_64', - 'channel_targets': 'conda-forge main', - 'family': 'x86_64', - 'microarchitecture': 'x86_64'} -recipe: - package: - name: _x86_64-microarch-level - version: '1' - build: - number: 2 - string: 2_x86_64 - noarch: generic - requirements: - run: - - __archspec 1=x86_64 - run_constrained: [] - about: - license: BSD-3-Clause diff --git a/crates/pixi_build_frontend/src/protocols/builders/conda_build/snapshots/pixi_build_frontend__protocols__builders__conda_build__protocol__test__conda-render__pinject.txt.snap b/crates/pixi_build_frontend/src/protocols/builders/conda_build/snapshots/pixi_build_frontend__protocols__builders__conda_build__protocol__test__conda-render__pinject.txt.snap deleted file mode 100644 index d90b4838fc..0000000000 --- a/crates/pixi_build_frontend/src/protocols/builders/conda_build/snapshots/pixi_build_frontend__protocols__builders__conda_build__protocol__test__conda-render__pinject.txt.snap +++ /dev/null @@ -1,73 +0,0 @@ ---- -source: crates/pixi_build_frontend/src/protocols/builders/conda_build/protocol.rs -expression: rendered_recipe ---- -package: - name: pinject - version: 0.14.1 -source: - sha256: 0f0a0b14f9df87a85b529a21cdaf530269b1f24fb303d418583a12bb53f69382 - url: https://pypi.io/packages/source/p/pinject/pinject-0.14.1.tar.gz -build: - noarch: python - number: '0' - script: C:\\Users\\zalms\\conda-bld\\pinject_1723465624118\\_h_env\\python.exe -m - pip install . -vv -requirements: - host: - - ca-certificates 2024.7.4 h56e8100_0 - - libexpat 2.6.2 h63175ca_0 - - tzdata 2024a h0c530f3_0 - - ucrt 10.0.22621.0 h57928b3_0 - - vc14_runtime 14.40.33810 ha82c5b3_20 - - vc 14.3 h8a93ad2_20 - - vs2015_runtime 14.40.33810 h3bf8584_20 - - bzip2 1.0.8 h2466b09_7 - - libffi 3.4.2 h8ffe710_5 - - libsqlite 3.46.0 h2466b09_0 - - libzlib 1.3.1 h2466b09_1 - - openssl 3.3.1 h2466b09_2 - - tk 8.6.13 h5226925_1 - - xz 5.2.6 h8d14728_0 - - python 3.12.5 h889d299_0_cpython - - setuptools 72.1.0 pyhd8ed1ab_0 - - wheel 0.44.0 pyhd8ed1ab_0 - - pip 24.2 pyhd8ed1ab_0 - run: - - python >=3.4 - - six >=1.7.3 - - decorator >=4.3.0 -test: - commands: - - pip check - imports: - - pinject - requires: - - pip -about: - home: https://github.com/google/pinject - license: Apache-2.0 - license_file: LICENSE - summary: A pythonic dependency injection library -extra: - copy_test_source_files: true - final: true - recipe-maintainers: - - baszalmstra ---- -hash_content: '{}' -recipe: - package: - name: pinject - version: 0.14.1 - build: - number: 0 - noarch: python - requirements: - run: - - python >=3.4 - - six >=1.7.3 - - decorator >=4.3.0 - run_constrained: [] - about: - license: Apache-2.0 diff --git a/crates/pixi_build_frontend/src/protocols/builders/mod.rs b/crates/pixi_build_frontend/src/protocols/builders/mod.rs index 9392b4b8e8..37da5d701d 100644 --- a/crates/pixi_build_frontend/src/protocols/builders/mod.rs +++ b/crates/pixi_build_frontend/src/protocols/builders/mod.rs @@ -1,7 +1,5 @@ -pub mod conda_build; pub mod pixi; pub mod rattler_build; -pub use conda_build as conda_protocol; pub use pixi as pixi_protocol; pub use rattler_build as rattler_build_protocol; diff --git a/crates/pixi_build_frontend/src/protocols/builders/rattler_build.rs b/crates/pixi_build_frontend/src/protocols/builders/rattler_build.rs index 83453e3a17..4e54459e70 100644 --- a/crates/pixi_build_frontend/src/protocols/builders/rattler_build.rs +++ b/crates/pixi_build_frontend/src/protocols/builders/rattler_build.rs @@ -40,7 +40,7 @@ pub enum ProtocolBuildError { FailedToBuildPixi(#[from] PixiProtocolBuildError), } -/// A builder for constructing a [`crate::protocol::Protocol`] instance. +/// A builder for constructing a [`JsonRPCBuildProtocol`] instance. #[derive(Debug)] pub struct ProtocolBuilder { /// The directory that contains the source files. diff --git a/crates/pixi_build_frontend/src/protocols/mod.rs b/crates/pixi_build_frontend/src/protocols/mod.rs index 744e2d9164..01786f90f8 100644 --- a/crates/pixi_build_frontend/src/protocols/mod.rs +++ b/crates/pixi_build_frontend/src/protocols/mod.rs @@ -114,6 +114,13 @@ impl ProtocolError { /// protocol. This protocol is generic over the manifest what are passed to the /// build backends. This means that, for rattler-build, the manifest is a /// recipe.yaml file, and for pixi it's a pixi.toml or a pyproject.toml file. +/// +/// Using this JSON-RPC interface means we can evolve the backend and frontend +/// tools as long as both tools can establish a shared protocol. The JSON-RPC +/// protocol is engineered in such a way that this is possible. This allows a +/// much newer frontend to still be able to interact with a very old backend +/// which is important if you want to be able to use very old packages in the +/// far future. #[derive(Debug)] pub struct JsonRPCBuildProtocol { /// The identifier of the backend. diff --git a/pixi_docs/Cargo.lock b/pixi_docs/Cargo.lock index 6d0550adbb..eb03d1fa75 100644 --- a/pixi_docs/Cargo.lock +++ b/pixi_docs/Cargo.lock @@ -4285,13 +4285,9 @@ dependencies = [ "rattler_shell", "rattler_solve", "rattler_virtual_packages", - "regex", "reqwest-middleware", "serde", "serde_json", - "serde_with", - "serde_yaml", - "sha1", "thiserror 2.0.12", "tokio", "tokio-util", diff --git a/src/build/mod.rs b/src/build/mod.rs index 07a39d3e6a..fb8e47b38d 100644 --- a/src/build/mod.rs +++ b/src/build/mod.rs @@ -6,7 +6,7 @@ use chrono::Utc; use itertools::Itertools; use miette::Diagnostic; use miette::IntoDiagnostic; -use pixi_build_frontend::{BackendOverride, Protocol, SetupRequest, ToolContext}; +use pixi_build_frontend::{BackendOverride, JsonRPCBuildProtocol, SetupRequest, ToolContext}; use pixi_build_types::{ procedures::{ conda_build::{CondaBuildParams, CondaOutputIdentifier}, @@ -321,13 +321,13 @@ impl BuildContext { WorkDirKey { source: source_checkout.clone(), host_platform, - build_backend: protocol.identifier().to_string(), + build_backend: protocol.backend_identifier().to_string(), } .key(), ), variant_configuration: Some(self.resolve_variant(host_platform)), }, - build_reporter.as_conda_build_reporter(), + build_reporter.as_conda_build_reporter().as_ref(), ) .await .map_err(|e| BuildError::BackendError(e.into()))?; @@ -657,13 +657,13 @@ impl BuildContext { WorkDirKey { source: source.clone(), host_platform, - build_backend: protocol.identifier().to_string(), + build_backend: protocol.backend_identifier().to_string(), } .key(), ), variant_configuration: Some(variant_configuration), }, - metadata_reporter.as_conda_metadata_reporter().clone(), + metadata_reporter.as_conda_metadata_reporter().as_ref(), ) .await .map_err(|e| BuildError::BackendError(e.into()))?; @@ -716,7 +716,7 @@ impl BuildContext { &self, source: &SourceCheckout, build_id: usize, - ) -> Result { + ) -> Result { // The RAYON_INITIALIZE is required to ensure that rayon is explicitly initialized. LazyLock::force(&RAYON_INITIALIZE); diff --git a/src/cli/build.rs b/src/cli/build.rs index 78d105077b..c371d8e12d 100644 --- a/src/cli/build.rs +++ b/src/cli/build.rs @@ -142,7 +142,7 @@ pub async fn execute(args: Args) -> miette::Result<()> { }), ), args.target_platform, - protocol.identifier().to_string(), + protocol.backend_identifier().to_string(), ) .key(); (None, workspace.pixi_dir().join(key)) @@ -158,7 +158,8 @@ pub async fn execute(args: Args) -> miette::Result<()> { (Some(tmp), work_dir) }; - let progress = Arc::new(ProgressReporter::new(workspace.name())); + let progress = ProgressReporter::new(workspace.name()); + // Build platform virtual packages let build_platform_virtual_packages: Vec = workspace .default_environment() @@ -203,7 +204,7 @@ pub async fn execute(args: Args) -> miette::Result<()> { work_directory: work_dir, variant_configuration: Some(build_context.resolve_variant(args.target_platform)), }, - progress.clone(), + &progress, ) .await .wrap_err("during the building of the project the following error occurred")?;