use std::fmt::{Debug, Display, Formatter};
use std::future::Future;
use std::ops::Deref;
use std::path::{Path, PathBuf};
use anyhow::Result;
use rustc_hash::FxHashSet;
use uv_cache::Cache;
use uv_configuration::{BuildKind, BuildOptions, BuildOutput, NoSources};
use uv_distribution_filename::DistFilename;
use uv_distribution_types::{
CachedDist, ConfigSettings, DependencyMetadata, DistributionId, ExtraBuildRequires,
ExtraBuildVariables, IndexCapabilities, IndexLocations, InstalledDist, IsBuildBackendError,
PackageConfigSettings, Requirement, Resolution, SourceDist,
};
use uv_git::GitResolver;
use uv_normalize::PackageName;
use uv_python::{Interpreter, PythonEnvironment};
use uv_workspace::WorkspaceCache;
use crate::{BuildArena, BuildIsolation};
#[derive(Debug, Clone, Copy, Default, Eq, PartialEq)]
pub enum SourceTreeEditablePolicy {
#[default]
Project,
Tool,
}
impl SourceTreeEditablePolicy {
pub fn workspace_member_editable(self, explicit: Option<bool>) -> bool {
match self {
Self::Project => true,
Self::Tool => explicit.unwrap_or(false),
}
}
}
pub trait BuildContext {
type SourceDistBuilder: SourceBuildTrait;
fn interpreter(&self) -> impl Future<Output = &Interpreter> + '_;
fn cache(&self) -> &Cache;
fn git(&self) -> &GitResolver;
fn build_arena(&self) -> &BuildArena<Self::SourceDistBuilder>;
fn capabilities(&self) -> &IndexCapabilities;
fn dependency_metadata(&self) -> &DependencyMetadata;
fn build_options(&self) -> &BuildOptions;
fn build_isolation(&self) -> BuildIsolation<'_>;
fn config_settings(&self) -> &ConfigSettings;
fn config_settings_package(&self) -> &PackageConfigSettings;
fn sources(&self) -> &NoSources;
fn source_tree_editable_policy(&self) -> SourceTreeEditablePolicy {
SourceTreeEditablePolicy::Project
}
fn locations(&self) -> &IndexLocations;
fn workspace_cache(&self) -> &WorkspaceCache;
fn extra_build_requires(&self) -> &ExtraBuildRequires;
fn extra_build_variables(&self) -> &ExtraBuildVariables;
fn resolve<'a>(
&'a self,
requirements: &'a [Requirement],
build_stack: &'a BuildStack,
) -> impl Future<Output = Result<Resolution, impl IsBuildBackendError>> + 'a;
fn install<'a>(
&'a self,
resolution: &'a Resolution,
venv: &'a PythonEnvironment,
build_stack: &'a BuildStack,
) -> impl Future<Output = Result<Vec<CachedDist>, impl IsBuildBackendError>> + 'a;
fn setup_build<'a>(
&'a self,
source: &'a Path,
subdirectory: Option<&'a Path>,
install_path: &'a Path,
version_id: Option<&'a str>,
dist: Option<&'a SourceDist>,
sources: &'a NoSources,
build_kind: BuildKind,
build_output: BuildOutput,
build_stack: BuildStack,
) -> impl Future<Output = Result<Self::SourceDistBuilder, impl IsBuildBackendError>> + 'a;
fn direct_build<'a>(
&'a self,
source: &'a Path,
subdirectory: Option<&'a Path>,
output_dir: &'a Path,
sources: NoSources,
build_kind: BuildKind,
version_id: Option<&'a str>,
) -> impl Future<Output = Result<Option<DistFilename>, impl IsBuildBackendError>> + 'a;
}
pub trait SourceBuildTrait {
fn metadata(&mut self) -> impl Future<Output = Result<Option<PathBuf>, AnyErrorBuild>>;
fn wheel<'a>(
&'a self,
wheel_dir: &'a Path,
) -> impl Future<Output = Result<String, AnyErrorBuild>> + 'a;
}
pub trait InstalledPackagesProvider: Clone + Send + Sync + 'static {
fn iter(&self) -> impl Iterator<Item = &InstalledDist>;
fn get_packages(&self, name: &PackageName) -> Vec<&InstalledDist>;
}
#[derive(Clone)]
pub struct EmptyInstalledPackages;
impl InstalledPackagesProvider for EmptyInstalledPackages {
fn iter(&self) -> impl Iterator<Item = &InstalledDist> {
std::iter::empty()
}
fn get_packages(&self, _name: &PackageName) -> Vec<&InstalledDist> {
Vec::new()
}
}
pub struct AnyErrorBuild(Box<dyn IsBuildBackendError>);
impl Debug for AnyErrorBuild {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
Debug::fmt(&self.0, f)
}
}
impl Display for AnyErrorBuild {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
Display::fmt(&self.0, f)
}
}
impl std::error::Error for AnyErrorBuild {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
self.0.source()
}
#[allow(deprecated)]
fn description(&self) -> &str {
self.0.description()
}
#[allow(deprecated)]
fn cause(&self) -> Option<&dyn std::error::Error> {
self.0.cause()
}
}
impl<T: IsBuildBackendError> From<T> for AnyErrorBuild {
fn from(err: T) -> Self {
Self(Box::new(err))
}
}
impl Deref for AnyErrorBuild {
type Target = dyn IsBuildBackendError;
fn deref(&self) -> &Self::Target {
&*self.0
}
}
#[derive(Debug, Clone, Default)]
pub struct BuildStack(FxHashSet<DistributionId>);
impl BuildStack {
pub fn empty() -> Self {
Self(FxHashSet::default())
}
pub fn contains(&self, id: &DistributionId) -> bool {
self.0.contains(id)
}
pub fn insert(&mut self, id: DistributionId) -> bool {
self.0.insert(id)
}
}