Skip to content

Commit

Permalink
feat: split cli into lib
Browse files Browse the repository at this point in the history
  • Loading branch information
baszalmstra committed Sep 17, 2024
1 parent 6aff4ed commit 53d8a7c
Show file tree
Hide file tree
Showing 13 changed files with 144 additions and 53 deletions.
2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ tempfile = "3.10.1"
clap-verbosity-flag = "2.2.1"
tracing-subscriber = "0.3.18"
serde_yaml = "0.9.33"
serde = "1.0"
minijinja = "2.3.0"

parking_lot = "0.12.3"

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
[package]
name = "pixi-build-python"
name = "pixi-build-backend"
version = "0.1.0"
edition.workspace = true

Expand All @@ -24,6 +24,8 @@ tempfile = { workspace = true }
clap-verbosity-flag = { workspace = true }
tracing-subscriber = { workspace = true }
serde_yaml = { workspace = true }
serde = { workspace = true, features = ["derive"] }
minijinja = { workspace = true }

parking_lot = { workspace = true }

Expand Down
12 changes: 12 additions & 0 deletions crates/pixi-build/src/bin/pixi-build-python/build_script.j2
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{% set PYTHON="%PYTHON%" if build_platform == "windows" else "$PYTHON" -%}
{% set SRC_DIR="%SRC_DIR%" if build_platform == "windows" else "$SRC_DIR" -%}

{% if installer == "uv" -%}
uv pip install --python {{ PYTHON }} -vv --no-deps --no-build-isolation {{ SRC_DIR }}
{% else %}
{{ PYTHON }} -m pip install -vv --ignore-installed --no-deps --no-build-isolation {{ SRC_DIR }}
{% endif -%}

{% if build_platform == "windows" -%}
if errorlevel 1 exit 1
{% endif %}
43 changes: 43 additions & 0 deletions crates/pixi-build/src/bin/pixi-build-python/build_script.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
use minijinja::Environment;
use serde::Serialize;

#[derive(Serialize)]
pub struct BuildScriptContext {
pub installer: Installer,
pub build_platform: BuildPlatform,
}

#[derive(Default, Serialize)]
#[serde(rename_all = "kebab-case")]
pub enum Installer {
Uv,
#[default]
Pip,
}

impl Installer {
pub fn package_name(&self) -> &str {
match self {
Installer::Uv => "uv",
Installer::Pip => "pip",
}
}
}

#[derive(Serialize)]
#[serde(rename_all = "kebab-case")]
pub enum BuildPlatform {
Windows,
Unix,
}

impl BuildScriptContext {
pub fn render(&self) -> Vec<String> {
let env = Environment::new();
let template = env
.template_from_str(include_str!("build_script.j2"))
.unwrap();
let rendered = template.render(self).unwrap().to_string();
rendered.split("\n").map(|s| s.to_string()).collect()
}
}
12 changes: 12 additions & 0 deletions crates/pixi-build/src/bin/pixi-build-python/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
mod build_script;
mod python;

use python::PythonBuildBackend;

#[tokio::main]
pub async fn main() {
if let Err(err) = pixi_build_backend::cli::main(PythonBuildBackend::factory).await {
eprintln!("{err:?}");
std::process::exit(1);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,20 @@ use std::{collections::BTreeMap, path::Path, str::FromStr, sync::Arc};

use chrono::Utc;
use miette::{Context, IntoDiagnostic};
use pixi_build_types::procedures::conda_build::CondaBuildParams;
use pixi_build_types::procedures::conda_metadata::{CondaMetadataParams, CondaMetadataResult};
use pixi_build_backend::{
protocol::{Protocol, ProtocolFactory},
utils::TemporaryRenderedRecipe,
};
use pixi_build_types::{
procedures::{
conda_build::CondaBuildResult,
conda_build::{CondaBuildParams, CondaBuildResult},
conda_metadata::{CondaMetadataParams, CondaMetadataResult},
initialize::{InitializeParams, InitializeResult},
},
BackendCapabilities, CondaPackageMetadata, FrontendCapabilities,
};
use pixi_manifest::{Dependencies, Manifest, SpecType};
use pixi_spec::PixiSpec;
use rattler_build::render::resolved_dependencies::DependencyInfo;
use rattler_build::{
build::run_build,
console_utils::LoggingOutputHandler,
Expand All @@ -23,6 +25,7 @@ use rattler_build::{
parser::{Build, Dependency, Package, PathSource, Requirements, ScriptContent, Source},
Recipe,
},
render::resolved_dependencies::DependencyInfo,
tool_configuration::Configuration,
};
use rattler_conda_types::{
Expand All @@ -32,10 +35,7 @@ use rattler_package_streaming::write::CompressionLevel;
use reqwest::Url;
use tempfile::tempdir;

use crate::{
protocol::{Protocol, ProtocolFactory},
temporary_recipe::TemporaryRenderedRecipe,
};
use crate::build_script::{BuildPlatform, BuildScriptContext, Installer};

pub struct PythonBuildBackend {
logging_output_handler: LoggingOutputHandler,
Expand Down Expand Up @@ -103,7 +103,7 @@ impl PythonBuildBackend {

/// Returns the requirements of the project that should be used for a
/// recipe.
fn requirements(&self, channel_config: &ChannelConfig) -> Requirements {
fn requirements(&self, channel_config: &ChannelConfig) -> (Requirements, Installer) {
fn dependencies_into_matchspecs(
deps: Dependencies<PackageName, PixiSpec>,
channel_config: &ChannelConfig,
Expand Down Expand Up @@ -137,8 +137,18 @@ impl PythonBuildBackend {
.filter_map(|f| f.dependencies(Some(SpecType::Build), None)),
);

// Determine the installer to use
let installer = if host_dependencies.contains_key("uv")
|| run_dependencies.contains_key("uv")
|| build_dependencies.contains_key("uv")
{
Installer::Uv
} else {
Installer::Pip
};

// Ensure python and pip are available in the host dependencies section.
for pkg_name in ["pip", "python"] {
for pkg_name in [installer.package_name(), "python"] {
if host_dependencies.contains_key(pkg_name) {
// If the host dependencies already contain the package, we don't need to add it
// again.
Expand Down Expand Up @@ -171,7 +181,7 @@ impl PythonBuildBackend {
.map(Dependency::Spec)
.collect();

requirements
(requirements, installer)
}

/// Constructs a [`Recipe`] from the current manifest.
Expand Down Expand Up @@ -202,10 +212,20 @@ impl PythonBuildBackend {
let noarch_type = NoArchType::python();

// TODO: Read from config / project.
let requirements = self.requirements(channel_config);
let (requirements, installer) = self.requirements(channel_config);
let build_platform = Platform::current();
let build_number = 0;

let build_script = BuildScriptContext {
installer,
build_platform: if build_platform.is_windows() {
BuildPlatform::Windows
} else {
BuildPlatform::Unix
},
}
.render();

Ok(Recipe {
schema_version: 1,
package: Package {
Expand All @@ -228,15 +248,7 @@ impl PythonBuildBackend {
string: Default::default(),

// skip: Default::default(),
script: ScriptContent::Commands(
if build_platform.is_windows() {
vec![
"%PYTHON% -m pip install --ignore-installed --no-deps --no-build-isolation . -vv".to_string(),
"if errorlevel 1 exit 1".to_string()]
} else {
vec!["$PYTHON -m pip install --ignore-installed --no-deps --no-build-isolation . -vv".to_string()]
})
.into(),
script: ScriptContent::Commands(build_script).into(),
noarch: noarch_type,

// TODO: Python is not exposed properly
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,3 @@
mod consts;
mod logging;
mod protocol;
mod python;
mod server;
mod temporary_recipe;

use std::path::{Path, PathBuf};

use clap::{Parser, Subcommand};
Expand All @@ -14,16 +7,17 @@ use pixi_build_types::{
procedures::{
conda_build::{CondaBuildParams, CondaOutputIdentifier},
conda_metadata::{CondaMetadataParams, CondaMetadataResult},
initialize::InitializeParams,
},
ChannelConfiguration,
ChannelConfiguration, FrontendCapabilities,
};
use rattler_build::console_utils::{get_default_env_filter, LoggingOutputHandler};
use rattler_conda_types::ChannelConfig;
use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt};

use crate::{
consts,
protocol::{Protocol, ProtocolFactory},
python::PythonBuildBackend,
server::Server,
};

Expand All @@ -50,20 +44,12 @@ pub enum Commands {
#[clap(env, long, env = "PIXI_PROJECT_MANIFEST", default_value = consts::PROJECT_MANIFEST)]
manifest_path: PathBuf,
},
Build {
CondaBuild {
#[clap(env, long, env = "PIXI_PROJECT_MANIFEST", default_value = consts::PROJECT_MANIFEST)]
manifest_path: PathBuf,
},
}

#[tokio::main]
pub async fn main() {
if let Err(err) = actual_main().await {
eprintln!("{err:?}");
std::process::exit(1);
}
}

async fn run_server<T: ProtocolFactory>(port: Option<u16>, protocol: T) -> miette::Result<()> {
let server = Server::new(protocol);
if let Some(port) = port {
Expand All @@ -73,7 +59,9 @@ async fn run_server<T: ProtocolFactory>(port: Option<u16>, protocol: T) -> miett
}
}

async fn actual_main() -> miette::Result<()> {
pub async fn main<T: ProtocolFactory, F: FnOnce(LoggingOutputHandler) -> T>(
factory: F,
) -> miette::Result<()> {
let args = App::parse();

// Setup logging
Expand All @@ -82,19 +70,21 @@ async fn actual_main() -> miette::Result<()> {
.with(get_default_env_filter(args.verbose.log_level_filter()).into_diagnostic()?);
registry.with(log_handler.clone()).init();

let factory = factory(log_handler);

match args.command {
None => run_server(args.http_port, PythonBuildBackend::factory(log_handler)).await,
Some(Commands::Build { manifest_path }) => build(log_handler, &manifest_path).await,
None => run_server(args.http_port, factory).await,
Some(Commands::CondaBuild { manifest_path }) => build(factory, &manifest_path).await,
Some(Commands::GetCondaMetadata { manifest_path }) => {
let metadata = get_conda_metadata(log_handler, &manifest_path).await?;
let metadata = get_conda_metadata(factory, &manifest_path).await?;
println!("{}", serde_yaml::to_string(&metadata).unwrap());
Ok(())
}
}
}

async fn get_conda_metadata(
logging_output_handler: LoggingOutputHandler,
factory: impl ProtocolFactory,
manifest_path: &Path,
) -> miette::Result<CondaMetadataResult> {
let channel_config = ChannelConfig::default_with_root_dir(
Expand All @@ -104,8 +94,14 @@ async fn get_conda_metadata(
.to_path_buf(),
);

let backend = PythonBuildBackend::new(manifest_path, logging_output_handler)?;
backend
let (protocol, _initialize_result) = factory
.initialize(InitializeParams {
manifest_path: manifest_path.to_path_buf(),
capabilities: FrontendCapabilities {},
})
.await?;

protocol
.get_conda_metadata(CondaMetadataParams {
target_platform: None,
channel_base_urls: None,
Expand All @@ -116,19 +112,22 @@ async fn get_conda_metadata(
.await
}

async fn build(
logging_output_handler: LoggingOutputHandler,
manifest_path: &Path,
) -> miette::Result<()> {
async fn build(factory: impl ProtocolFactory, manifest_path: &Path) -> miette::Result<()> {
let channel_config = ChannelConfig::default_with_root_dir(
manifest_path
.parent()
.expect("manifest should always reside in a directory")
.to_path_buf(),
);

let backend = PythonBuildBackend::new(manifest_path, logging_output_handler)?;
let result = backend
let (protocol, _initialize_result) = factory
.initialize(InitializeParams {
manifest_path: manifest_path.to_path_buf(),
capabilities: FrontendCapabilities {},
})
.await?;

let result = protocol
.build_conda(CondaBuildParams {
target_platform: None,
channel_base_urls: None,
Expand Down
File renamed without changes.
6 changes: 6 additions & 0 deletions crates/pixi-build/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
pub mod cli;
pub mod protocol;
pub mod server;

mod consts;
pub mod utils;
File renamed without changes.
File renamed without changes.
3 changes: 3 additions & 0 deletions crates/pixi-build/src/utils/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
mod temporary_recipe;

pub use temporary_recipe::TemporaryRenderedRecipe;

0 comments on commit 53d8a7c

Please sign in to comment.