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"
30 changes: 30 additions & 0 deletions crates/pixi-build-pixi/src/build_script.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
use crate::config::BuildTask;
use serde::Serialize;

#[derive(Serialize)]
pub struct BuildScriptContext {
pub build_tasks: Vec<BuildTask>,
pub manifest_root: std::path::PathBuf,
}

impl BuildScriptContext {
pub fn render(&self) -> String {
self.build_tasks
.iter()
.map(|build_task| {
let env_arg = build_task
.environment
.as_ref()
.map(|env| format!(" -e {}", env))
.unwrap_or_default();
format!(
"pixi run --as-is --manifest-path {}{} {}",
self.manifest_root.to_string_lossy(),
env_arg,
build_task.task
)
})
.collect::<Vec<_>>()
.join("\n")
}
}
76 changes: 76 additions & 0 deletions crates/pixi-build-pixi/src/config.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
use std::path::{Path, PathBuf};

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

/// A build task with optional environment specification
#[derive(Debug, Deserialize, Serialize, Clone)]
#[serde(rename_all = "kebab-case", deny_unknown_fields)]
pub struct BuildTask {
/// Name of the task to run
pub task: String,
/// Optional environment to run the task in
pub environment: Option<String>,
}

#[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>,
/// Build tasks to run in order, each with optional environment
#[serde(default)]
pub build_tasks: Vec<BuildTask>,
/// List of compilers to use (e.g., ["c", "cxx", "cuda"])
/// If not specified, no compilers will be added
pub compilers: Option<Vec<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_tasks: if target_config.build_tasks.is_empty() {
self.build_tasks.clone()
} else {
target_config.build_tasks.clone()
},
compilers: target_config
.compilers
.clone()
.or_else(|| self.compilers.clone()),
})
}
}
109 changes: 109 additions & 0 deletions crates/pixi-build-pixi/src/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
mod build_script;
mod config;

use build_script::BuildScriptContext;
use config::PixiBackendConfig;
use miette::IntoDiagnostic;
use pixi_build_backend::{
ProjectModel,
generated_recipe::{DefaultMetadataProvider, GenerateRecipe, GeneratedRecipe, PythonParams},
intermediate_backend::IntermediateBackendInstantiator,
};
use rattler_build::{NormalizedKey, recipe::variable::Variable};
use rattler_conda_types::{ChannelUrl, Platform};
use recipe_stage0::recipe::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>,
_channels: Vec<ChannelUrl>,
) -> miette::Result<GeneratedRecipe> {
let mut generated_recipe =
GeneratedRecipe::from_model(model.clone(), &mut DefaultMetadataProvider)
.into_diagnostic()?;

let requirements = &mut generated_recipe.recipe.requirements;

// Add compilers if configured
if let Some(compilers) = &config.compilers {
let model_dependencies = model.dependencies(Some(host_platform));
pixi_build_backend::compilers::add_compilers_to_requirements(
compilers,
&mut requirements.build,
&model_dependencies,
&host_platform,
);
pixi_build_backend::compilers::add_stdlib_to_requirements(
compilers,
&mut requirements.build,
variants,
);
}

let build_script = BuildScriptContext {
build_tasks: config.build_tasks.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);
}
}
2 changes: 1 addition & 1 deletion pixi.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ doctest = "cargo test --doc"
test = [{ task = "nextest" }, { task = "doctest" }]
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