Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
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
20 changes: 20 additions & 0 deletions Cargo.lock

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

23 changes: 23 additions & 0 deletions crates/pixi-build-pixi/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
[package]
name = "pixi-build-pixi"
version = "0.3.3"
description = "Pixi build backend for Pixi"
edition.workspace = true

[dependencies]
indexmap = { workspace = true }
miette = { workspace = true }
minijinja = { workspace = true }
pixi-build-backend = { workspace = true }
pixi_build_types = { workspace = true }
rattler-build = { workspace = true }
rattler_conda_types = { workspace = true }
recipe-stage0 = { workspace = true }
serde = { workspace = true, features = ["derive"] }
serde_json = { workspace = true }
tokio = { workspace = true, features = ["macros"] }

[dev-dependencies]
insta = { workspace = true, features = ["yaml", "redactions", "filters"] }
rstest = { workspace = true }
strum = { workspace = true }
10 changes: 10 additions & 0 deletions crates/pixi-build-pixi/pixi.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
[package.build.backend]
name = "pixi-build-rust"
version = "*"
channels = [
"https://prefix.dev/pixi-build-backends",
"https://prefix.dev/conda-forge",
]

[package.run-dependencies]
pixi-build-api-version = ">=2,<3"
17 changes: 17 additions & 0 deletions crates/pixi-build-pixi/src/build_script.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
use serde::Serialize;

#[derive(Serialize)]
pub struct BuildScriptContext {
pub build_task: String,
pub manifest_root: std::path::PathBuf,
}

impl BuildScriptContext {
pub fn render(&self) -> String {
format!(
"pixi run --as-is --manifest-path {} {}",
self.manifest_root.to_string_lossy(),
self.build_task
)
}
}
63 changes: 63 additions & 0 deletions crates/pixi-build-pixi/src/config.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
use std::path::{Path, PathBuf};

use indexmap::IndexMap;
use pixi_build_backend::generated_recipe::BackendConfig;
use serde::{Deserialize, Serialize};

#[derive(Debug, Default, Deserialize, Serialize, Clone)]
#[serde(rename_all = "kebab-case", deny_unknown_fields)]
pub struct PixiBackendConfig {
/// Environment Variables
#[serde(default)]
pub env: IndexMap<String, String>,
/// If set, internal state will be logged as files in that directory
pub debug_dir: Option<PathBuf>,
/// Extra input globs to include in addition to the default ones
#[serde(default)]
pub extra_input_globs: Vec<String>,
/// Name of the build task in pixi.toml (defaults to "build")
#[serde(default = "default_build_task")]
pub build_task: String,
}

fn default_build_task() -> String {
"build".to_string()
}

impl BackendConfig for PixiBackendConfig {
fn debug_dir(&self) -> Option<&Path> {
self.debug_dir.as_deref()
}

/// Merge this configuration with a target-specific configuration.
/// Target-specific values override base values using the following rules:
/// - extra_args: Platform-specific completely replaces base
/// - env: Platform env vars override base, others merge
/// - debug_dir: Not allowed to have target specific value
/// - extra_input_globs: Platform-specific completely replaces base
/// - compilers: Platform-specific completely replaces base
fn merge_with_target_config(&self, target_config: &Self) -> miette::Result<Self> {
if target_config.debug_dir.is_some() {
miette::bail!("`debug_dir` cannot have a target specific value");
}

Ok(Self {
env: {
let mut merged_env = self.env.clone();
merged_env.extend(target_config.env.clone());
merged_env
},
debug_dir: self.debug_dir.clone(),
extra_input_globs: if target_config.extra_input_globs.is_empty() {
self.extra_input_globs.clone()
} else {
target_config.extra_input_globs.clone()
},
build_task: if target_config.build_task == default_build_task() {
self.build_task.clone()
} else {
target_config.build_task.clone()
},
})
}
}
105 changes: 105 additions & 0 deletions crates/pixi-build-pixi/src/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
mod build_script;
mod config;

use build_script::BuildScriptContext;
use config::PixiBackendConfig;
use miette::IntoDiagnostic;
use pixi_build_backend::{
generated_recipe::{DefaultMetadataProvider, GenerateRecipe, GeneratedRecipe, PythonParams},
intermediate_backend::IntermediateBackendInstantiator,
};
use rattler_build::{NormalizedKey, recipe::variable::Variable};
use rattler_conda_types::{PackageName, Platform};
use recipe_stage0::recipe::{ConditionalRequirements, Script};
use std::collections::HashSet;
use std::{
collections::{BTreeMap, BTreeSet},
path::Path,
sync::Arc,
};

#[derive(Default, Clone)]
pub struct PixiGenerator {}

impl GenerateRecipe for PixiGenerator {
type Config = PixiBackendConfig;

fn generate_recipe(
&self,
model: &pixi_build_types::ProjectModelV1,
config: &Self::Config,
manifest_root: std::path::PathBuf,
host_platform: rattler_conda_types::Platform,
_python_params: Option<PythonParams>,
_variants: &HashSet<NormalizedKey>,
) -> miette::Result<GeneratedRecipe> {
let mut generated_recipe =
GeneratedRecipe::from_model(model.clone(), &mut DefaultMetadataProvider)
.into_diagnostic()?;

let requirements = &mut generated_recipe.recipe.requirements;

let resolved_requirements = ConditionalRequirements::resolve(
requirements.build.as_ref(),
requirements.host.as_ref(),
requirements.run.as_ref(),
requirements.run_constraints.as_ref(),
Some(host_platform),
);

// Add pixi as a build dependency
let pixi_name = PackageName::new_unchecked("pixi");
if !resolved_requirements.build.contains_key(&pixi_name) {
requirements.build.push("pixi".parse().into_diagnostic()?);
}

let build_script = BuildScriptContext {
build_task: config.build_task.clone(),
manifest_root,
}
.render();

generated_recipe.recipe.build.script = Script {
content: build_script,
env: config.env.clone(),
..Default::default()
};

Ok(generated_recipe)
}

fn extract_input_globs_from_build(
&self,
config: &Self::Config,
_workdir: impl AsRef<Path>,
_editable: bool,
) -> miette::Result<BTreeSet<String>> {
Ok(["pixi.toml", "pixi.lock"]
.iter()
.map(|s: &&str| s.to_string())
.chain(config.extra_input_globs.clone())
.collect())
}

fn default_variants(
&self,
_host_platform: Platform,
) -> miette::Result<BTreeMap<NormalizedKey, Vec<Variable>>> {
let variants = BTreeMap::new();

// No default variants needed for pixi builds
Ok(variants)
}
}

#[tokio::main]
pub async fn main() {
if let Err(err) = pixi_build_backend::cli::main(|log| {
IntermediateBackendInstantiator::<PixiGenerator>::new(log, Arc::default())
})
.await
{
eprintln!("{err:?}");
std::process::exit(1);
}
}
1 change: 1 addition & 0 deletions pixi.toml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ test = [
]
generate-matrix = "python scripts/generate-matrix.py"

install-pixi-build-pixi = { cmd = "cargo install --path crates/pixi-build-pixi --locked --force" }
install-pixi-build-python = { cmd = "cargo install --path crates/pixi-build-python --locked --force" }
install-pixi-build-cmake = { cmd = "cargo install --path crates/pixi-build-cmake --locked --force" }
install-pixi-build-rattler-build = { cmd = "cargo install --path crates/pixi-build-rattler-build --locked --force" }
Expand Down
Loading