Skip to content
Open
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
33 changes: 32 additions & 1 deletion crates/pixi_build_frontend/src/backend/json_rpc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -153,13 +153,44 @@ impl JsonRpcBackend {
cache_dir: Option<PathBuf>,
workspace_scratch_directory: Option<PathBuf>,
tool: Tool,
) -> Result<Self, InitializeError> {
Self::setup_with_verbosity(
source_dir,
manifest_path,
workspace_root,
checkout_root,
package_manifest,
configuration,
target_configuration,
cache_dir,
workspace_scratch_directory,
tool,
crate::tool::BackendVerbosity::default(),
)
.await
}

/// Set up a new protocol instance with explicit backend verbosity.
#[allow(clippy::too_many_arguments)]
pub async fn setup_with_verbosity(
source_dir: PathBuf,
manifest_path: PathBuf,
workspace_root: PathBuf,
checkout_root: Option<PathBuf>,
package_manifest: Option<ProjectModel>,
configuration: Option<serde_json::Value>,
target_configuration: Option<OrderMap<TargetSelector, serde_json::Value>>,
cache_dir: Option<PathBuf>,
workspace_scratch_directory: Option<PathBuf>,
tool: Tool,
backend_verbosity: crate::tool::BackendVerbosity,
) -> Result<Self, InitializeError> {
debug_assert!(source_dir.is_absolute());
debug_assert!(manifest_path.is_absolute());
debug_assert!(workspace_root.is_absolute());
debug_assert!(checkout_root.as_ref().is_none_or(|p| p.is_absolute()));
// Spawn the tool and capture stdin/stdout.
let command = tool.command();
let command = tool.command_with_verbosity(backend_verbosity);
let program_name = command.get_program().to_string_lossy().into_owned();
let mut process = match tokio::process::Command::from(command)
.stdout(std::process::Stdio::piped())
Expand Down
91 changes: 89 additions & 2 deletions crates/pixi_build_frontend/src/tool.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,35 @@
use rattler_conda_types::VersionWithSource;
use std::{collections::HashMap, path::PathBuf};

/// Verbosity flags to pass to a build backend process.
#[derive(Clone, Copy, Debug, Default, Eq, Hash, PartialEq)]
pub struct BackendVerbosity {
quiet: u8,
verbose: u8,
}

impl BackendVerbosity {
/// Construct backend verbosity from Pixi's CLI flag counts.
pub fn from_cli(quiet: u8, verbose: u8) -> Self {
Self { quiet, verbose }
}

fn args(self) -> Vec<&'static str> {
if self.quiet > 0 {
return vec!["-q"; 3];
}

match self.verbose {
// Preserve the backend's environment/default when Pixi received no
// explicit verbosity option.
0 => Vec::new(),
1 => Vec::new(),
2 => vec!["-v"],
_ => vec!["-v"; 2],
}
}
}

/// A tool that can be invoked.
#[derive(Debug)]
pub enum Tool {
Expand Down Expand Up @@ -110,14 +139,72 @@ impl Tool {
/// Construct a new command that enables invocation of the tool.
/// TODO: whether to inject proxy config
pub fn command(&self) -> std::process::Command {
match self {
self.command_with_verbosity(BackendVerbosity::default())
}

/// Construct a new command with explicit backend verbosity.
pub fn command_with_verbosity(&self, verbosity: BackendVerbosity) -> std::process::Command {
let mut command = match self {
Tool::Isolated(tool) => {
let mut cmd = std::process::Command::new(&tool.command);
cmd.envs(tool.activation_scripts.clone());

cmd
}
Tool::System(tool) => std::process::Command::new(&tool.command),
}
};

command.args(verbosity.args());
command
}
}

#[cfg(test)]
mod tests {
use std::{collections::HashMap, ffi::OsStr};

use super::{BackendVerbosity, IsolatedTool, SystemTool, Tool};

#[test]
fn backend_verbosity_matches_pixi_cli_levels() {
assert!(BackendVerbosity::from_cli(0, 0).args().is_empty());
assert!(BackendVerbosity::from_cli(0, 1).args().is_empty());
assert_eq!(BackendVerbosity::from_cli(0, 2).args(), ["-v"]);
assert_eq!(BackendVerbosity::from_cli(0, 3).args(), ["-v", "-v"]);
assert_eq!(BackendVerbosity::from_cli(0, 4).args(), ["-v", "-v"]);
assert_eq!(BackendVerbosity::from_cli(1, 4).args(), ["-q", "-q", "-q"]);
assert_eq!(BackendVerbosity::from_cli(4, 1).args(), ["-q", "-q", "-q"]);
}

#[test]
fn tool_commands_include_verbosity() {
let tool = Tool::from(SystemTool::new("backend"));
assert_eq!(
tool.command_with_verbosity(BackendVerbosity::from_cli(0, 3))
.get_args()
.collect::<Vec<_>>(),
[OsStr::new("-v"), OsStr::new("-v")]
);

let tool = Tool::from(IsolatedTool::new(
"backend",
None,
"/prefix",
HashMap::new(),
));
assert_eq!(
tool.command_with_verbosity(BackendVerbosity::from_cli(1, 4))
.get_args()
.collect::<Vec<_>>(),
[OsStr::new("-q"), OsStr::new("-q"), OsStr::new("-q")]
);

assert!(
Tool::from(SystemTool::new("backend"))
.command()
.get_args()
.next()
.is_none()
);
}
}
6 changes: 5 additions & 1 deletion crates/pixi_build_python/src/build_script.j2
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
{% set PYTHON="%PYTHON%" if build_platform == "windows" else "$PYTHON" -%}
{%- set OPTIONS = [
"-vv",
"--no-deps",
"--no-build-isolation",
"--no-index"
] + extra_args + (["--editable"] if editable else [])
-%}
{%- if verbosity == 1 -%}
{% set OPTIONS = ["-v"] + OPTIONS -%}
{%- elif verbosity >= 2 -%}
{% set OPTIONS = ["-vv"] + OPTIONS -%}
{%- endif -%}

{% if build_platform == "windows" -%}
{% set OPTIONS = OPTIONS | join(" ^\n ") -%}
Expand Down
88 changes: 88 additions & 0 deletions crates/pixi_build_python/src/build_script.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,17 @@ pub struct BuildScriptContext {
pub editable: bool,
pub extra_args: Vec<String>,
pub manifest_root: PathBuf,
pub verbosity: u8,
}

pub fn verbosity(debug_enabled: bool, trace_enabled: bool) -> u8 {
if trace_enabled {
2
} else if debug_enabled {
1
} else {
0
}
}

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please can we make this generic over the installer? I think it can just be called verbosity and passed to pip the same as uv.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated in 08cd4e3: the build script context now uses generic verbosity, and both uv and pip receive the same derived -v/-vv flags. Added pip coverage for default/debug/trace behavior.


/// The tool used to install the built wheel into the prefix.
Expand Down Expand Up @@ -47,3 +58,80 @@ impl BuildScriptContext {
template.render(self).unwrap().trim().to_string()
}
}

#[cfg(test)]
mod tests {
use super::{BuildPlatform, BuildScriptContext, Installer, verbosity};

fn context() -> BuildScriptContext {
BuildScriptContext {
installer: Installer::Uv,
build_platform: BuildPlatform::Unix,
editable: false,
extra_args: Vec::new(),
manifest_root: "/source".into(),
verbosity: 0,
}
}

#[test]
fn installer_is_quiet_without_explicit_verbosity() {
let script = context().render();
assert!(!script.contains(" -v"), "unexpected verbose flag: {script}");
}

#[test]
fn uv_installer_verbosity_follows_backend_logging() {
let mut context = context();
context.verbosity = 1;
assert!(context.render().contains("--reinstall -v "));

context.verbosity = 2;
assert!(context.render().contains("--reinstall -vv "));
}

#[test]
fn pip_installer_verbosity_follows_backend_logging() {
let mut context = context();
context.installer = Installer::Pip;
assert!(
!context.render().contains(" -v"),
"unexpected default verbose flag: {}",
context.render()
);

context.verbosity = 1;
assert!(
context
.render()
.contains("pip install --force-reinstall -v ")
);

context.verbosity = 2;
assert!(
context
.render()
.contains("pip install --force-reinstall -vv ")
);
}

#[test]
fn verbosity_is_derived_from_backend_logging() {
assert_eq!(verbosity(false, false), 0);
assert_eq!(verbosity(true, false), 1);
assert_eq!(verbosity(true, true), 2);
}

#[test]
fn pip_extra_args_are_preserved() {
let mut context = context();
context.installer = Installer::Pip;
context.extra_args.push("--config-settings=foo=bar".into());
assert!(
context
.render()
.contains("pip install --force-reinstall --no-deps")
);
assert!(context.render().contains("--config-settings=foo=bar"));
}
}
6 changes: 5 additions & 1 deletion crates/pixi_build_python/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ mod config;
mod metadata;
mod pypi_mapping;

use build_script::{BuildPlatform, BuildScriptContext, Installer};
use build_script::{BuildPlatform, BuildScriptContext, Installer, verbosity};
use config::PythonBackendConfig;
use fs_err as fs;
use miette::IntoDiagnostic;
Expand Down Expand Up @@ -426,6 +426,10 @@ impl GenerateRecipe for PythonGenerator {
editable,
extra_args: config.extra_args.clone(),
manifest_root: manifest_root.clone(),
verbosity: verbosity(
tracing::enabled!(target: "rattler_build", tracing::Level::DEBUG),
tracing::enabled!(target: "rattler_build", tracing::Level::TRACE),
),
}
.render();

Expand Down
9 changes: 8 additions & 1 deletion crates/pixi_cli/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -278,7 +278,14 @@ pub async fn execute() -> miette::Result<()> {
};

// Execute the command
execute_command(command, &global_options).await
pixi_command_dispatcher::scope_backend_verbosity(
pixi_build_frontend::tool::BackendVerbosity::from_cli(
global_options.quiet,
global_options.verbose,
),
execute_command(command, &global_options),
)
.await
}

#[cfg(feature = "console-subscriber")]
Expand Down
Loading
Loading