Skip to content
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
f656ee6
feat: pixi-build package.xml manifest
remimimimimi Oct 22, 2025
fe4befc
misc: enable providing build manifest for pixi build
nichmor Oct 24, 2025
e5d8a90
misc: remove unused constant
nichmor Oct 24, 2025
b93be0e
misc: cleanup
nichmor Oct 24, 2025
619d125
misc: regenerate docs
nichmor Oct 24, 2025
4bd5bbd
Merge branch 'main' into feat/pixi-build-package-xml-manifest
nichmor Oct 24, 2025
fe22f5f
misc: add error messages
nichmor Oct 26, 2025
44c2316
misc: rebuild docs
nichmor Oct 26, 2025
5b5254d
misc: add unit test for discovery start
nichmor Nov 3, 2025
28bccd1
misc: rename package-manifest to just path
nichmor Nov 4, 2025
5f5fda4
Merge branch 'main' into feat/pixi-build-package-xml-manifest
nichmor Nov 4, 2025
a2e39b4
misc: use only --path inside build
nichmor Nov 4, 2025
6a722c1
Merge branch 'main' into feat/pixi-build-package-xml-manifest
nichmor Nov 5, 2025
7333360
misc: update integration python to use --path
nichmor Nov 5, 2025
cf515a5
Merge branch 'feat/pixi-build-package-xml-manifest' of https://github…
nichmor Nov 5, 2025
2a5a15d
misc: remove panic
nichmor Nov 5, 2025
29f5389
misc: remove unecessary tests
nichmor Nov 5, 2025
5c3a867
misc: pass the dir
nichmor Nov 6, 2025
eea71b3
misc: pass dir to the backend
nichmor Nov 6, 2025
c0b52a2
misc: modify the test
nichmor Nov 6, 2025
c53ec8e
Merge branch 'main' into feat/pixi-build-package-xml-manifest
nichmor Nov 6, 2025
517b5e7
misc: remove tests are they present pixi-build-testsuite
nichmor Nov 6, 2025
c2c8630
misc: update snapshot
nichmor Nov 6, 2025
d37fc8a
misc: update option path
nichmor Nov 6, 2025
05aa150
misc: set correctly debug dir
nichmor Nov 6, 2025
2316a4a
misc: remove commented code
nichmor Nov 6, 2025
f4acfda
misc: temporary point to the PR with new tests
nichmor Nov 7, 2025
f099bfd
misc: remove commented code
nichmor Nov 7, 2025
24c14c5
Merge branch 'main' into feat/pixi-build-package-xml-manifest
nichmor Nov 17, 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
1 change: 1 addition & 0 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 crates/pixi_build_discovery/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ rattler_conda_types = { workspace = true }
pixi_build_type_conversions = { workspace = true }
pixi_build_types = { workspace = true }
pixi_config = { workspace = true }
pixi_consts = { workspace = true }
pixi_manifest = { workspace = true }
pixi_spec = { workspace = true }
pixi_spec_containers = { workspace = true }
Expand Down
5 changes: 1 addition & 4 deletions crates/pixi_build_discovery/src/discovery.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ use ordermap::OrderMap;
use pixi_build_type_conversions::{to_project_model_v1, to_target_selector_v1};
use pixi_build_types::{ProjectModelV1, TargetSelectorV1};
use pixi_config::Config;
use pixi_consts::consts::{RATTLER_BUILD_DIRS, RATTLER_BUILD_FILE_NAMES, ROS_BACKEND_FILE_NAMES};
use pixi_manifest::{
DiscoveryStart, ExplicitManifestError, PackageManifest, PrioritizedChannel, WithProvenance,
WorkspaceDiscoverer, WorkspaceDiscoveryError, WorkspaceManifest,
Expand All @@ -23,10 +24,6 @@ use crate::{
backend_spec::{CommandSpec, EnvironmentSpec, JsonRpcBackendSpec},
};

const RATTLER_BUILD_FILE_NAMES: [&str; 2] = ["recipe.yaml", "recipe.yml"];
const RATTLER_BUILD_DIRS: [&str; 2] = ["", "recipe"];
const ROS_BACKEND_FILE_NAMES: [&str; 1] = ["package.xml"];

/// Describes a backend discovered for a given source location.
#[derive(Debug, Clone)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
Expand Down
64 changes: 59 additions & 5 deletions crates/pixi_cli/src/build.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use std::path::PathBuf;
use std::{ffi::OsStr, path::PathBuf};

use clap::Parser;
use indicatif::ProgressBar;
Expand All @@ -7,6 +7,7 @@ use pixi_command_dispatcher::{
BuildBackendMetadataSpec, BuildEnvironment, BuildProfile, CacheDirs, SourceBuildSpec,
};
use pixi_config::ConfigCli;
use pixi_consts::consts::{RATTLER_BUILD_FILE_NAMES, ROS_BACKEND_FILE_NAMES, WORKSPACE_MANIFEST};
use pixi_core::WorkspaceLocator;
use pixi_manifest::FeaturesExt;
use pixi_progress::global_multi_progress;
Expand Down Expand Up @@ -46,6 +47,50 @@ pub struct Args {
/// Whether to clean the build directory before building.
#[clap(long, short)]
pub clean: bool,

/// The path to `package.xml`, `recipe.yaml`, or `pixi.toml` that can be built.
#[arg(long)]
pub build_manifest: Option<PathBuf>,
}

/// Validate that the full path of build manifest exists and is a supported format.
fn validate_build_manifest(path: &PathBuf) -> miette::Result<()> {
if path.is_dir() {
miette::bail!(
"the build manifest path '{}' is a directory, please provide the path to the manifest file",
path.display()
);
}

let filename = path
.file_name()
.and_then(OsStr::to_str)
.ok_or_else(|| miette::miette!("Failed to extract file name from {:?}", path))?;

let supported_file_names: Vec<&str> = [
&ROS_BACKEND_FILE_NAMES[..],
&RATTLER_BUILD_FILE_NAMES[..],
// manifest that can contain a package section in it
&[WORKSPACE_MANIFEST],
]
.concat();

if !supported_file_names
.iter()
.any(|names| names.contains(filename))
{
miette::bail!(
"the build manifest file '{}' is not a supported format.\n Supported formats are: {}",
path.display(),
supported_file_names
.iter()
.map(|name| format!("'{name}'"))
.collect::<Vec<_>>()
.join(", ")
);
}

Ok(())
}

pub async fn execute(args: Args) -> miette::Result<()> {
Expand Down Expand Up @@ -105,18 +150,27 @@ pub async fn execute(args: Args) -> miette::Result<()> {
let Ok(manifest_path) = workspace_locator.path() else {
miette::bail!("could not determine the current working directory to locate the workspace");
};
let manifest_path_canonical = dunce::canonicalize(&manifest_path)

let build_manifest_path = match args.build_manifest {
Some(path) => {
validate_build_manifest(&path)?;
path
}
None => manifest_path.clone(),
};

let build_manifest_path_canonical = dunce::canonicalize(&build_manifest_path)
.into_diagnostic()
.with_context(|| {
format!(
"failed to canonicalize manifest path '{}'",
manifest_path.display()
build_manifest_path.display()
)
})?;
// Store the manifest location relative to the workspace root when possible to
// keep the pinned path relocatable and avoid double-prefixing during resolution.
let manifest_spec_path = pathdiff::diff_paths(&manifest_path_canonical, workspace.root())
.unwrap_or(manifest_path_canonical.clone());
let manifest_spec_path = pathdiff::diff_paths(&build_manifest_path_canonical, workspace.root())
.unwrap_or(build_manifest_path_canonical.clone());
let channel_config = workspace.channel_config();
let channels = workspace
.default_environment()
Expand Down
5 changes: 5 additions & 0 deletions crates/pixi_consts/src/consts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,11 @@ pub const CLAP_GIT_OPTIONS: &str = "Git Options";
pub const CLAP_GLOBAL_OPTIONS: &str = "Global Options";
pub const CLAP_UPDATE_OPTIONS: &str = "Update Options";

// Build backends constants
pub const RATTLER_BUILD_FILE_NAMES: [&str; 2] = ["recipe.yaml", "recipe.yml"];
pub const RATTLER_BUILD_DIRS: [&str; 2] = ["", "recipe"];
pub const ROS_BACKEND_FILE_NAMES: [&str; 1] = ["package.xml"];

pub static TASK_STYLE: LazyLock<Style> = LazyLock::new(|| Style::new().blue());
pub static TASK_ERROR_STYLE: LazyLock<Style> = LazyLock::new(|| Style::new().red());
pub static PLATFORM_STYLE: LazyLock<Style> = LazyLock::new(|| Style::new().yellow());
Expand Down
1 change: 0 additions & 1 deletion crates/pixi_manifest/src/discovery.rs
Original file line number Diff line number Diff line change
Expand Up @@ -479,7 +479,6 @@ impl WorkspaceDiscoverer {
source: source.clone(),
}),
);

// Incorporate the workspace information into the package manifest.
let closest_package_manifest = match closest_package_manifest {
None => {
Expand Down
6 changes: 1 addition & 5 deletions crates/pixi_manifest/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -138,11 +138,7 @@ impl Display for TomlError {
TomlError::Error(err) => write!(f, "{}", err.message()),
TomlError::TomlError(err) => write!(f, "{err}"),
TomlError::NoPixiTable(manifest_kind, detail) => {
let filename = match manifest_kind {
ManifestKind::Pyproject => "pyproject.toml",
ManifestKind::Pixi => "pixi.toml",
ManifestKind::MojoProject => "mojoproject.toml",
};
let filename = manifest_kind.file_name();
if let Some(detail) = detail {
write!(f, "Missing table in manifest {filename}:\n{detail}")
} else {
Expand Down
2 changes: 2 additions & 0 deletions docs/reference/cli/pixi/build.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ pixi build [OPTIONS]
: The directory to use for incremental builds artifacts
- <a id="arg---clean" href="#arg---clean">`--clean (-c)`</a>
: Whether to clean the build directory before building
- <a id="arg---build-manifest" href="#arg---build-manifest">`--build-manifest <BUILD_MANIFEST>`</a>
: The path to `package.xml`, `recipe.yaml`, or `pixi.toml` that can be built

## Config Options
- <a id="arg---auth-file" href="#arg---auth-file">`--auth-file <AUTH_FILE>`</a>
Expand Down
Loading