mirror of
https://github.com/astral-sh/uv.git
synced 2025-12-23 09:19:48 +00:00
Add build backend scaffolding (#7662)
This commit is contained in:
parent
9a6f455cbf
commit
c4c5378c0b
11 changed files with 308 additions and 42 deletions
|
|
@ -389,6 +389,15 @@ pub enum Commands {
|
|||
Build(BuildArgs),
|
||||
/// Upload distributions to an index.
|
||||
Publish(PublishArgs),
|
||||
/// The implementation of the build backend.
|
||||
///
|
||||
/// These commands are not directly exposed to the user, instead users invoke their build
|
||||
/// frontend (PEP 517) which calls the Python shims which calls back into uv with this method.
|
||||
#[command(hide = true)]
|
||||
BuildBackend {
|
||||
#[command(subcommand)]
|
||||
command: BuildBackendCommand,
|
||||
},
|
||||
/// Manage uv's cache.
|
||||
#[command(
|
||||
after_help = "Use `uv help cache` for more details.",
|
||||
|
|
@ -4389,3 +4398,33 @@ pub struct PublishArgs {
|
|||
)]
|
||||
pub allow_insecure_host: Option<Vec<Maybe<TrustedHost>>>,
|
||||
}
|
||||
|
||||
/// See [PEP 517](https://peps.python.org/pep-0517/) and
|
||||
/// [PEP 660](https://peps.python.org/pep-0660/) for specifications of the parameters.
|
||||
#[derive(Subcommand)]
|
||||
pub enum BuildBackendCommand {
|
||||
/// PEP 517 hook `build_sdist`.
|
||||
BuildSdist { sdist_directory: PathBuf },
|
||||
/// PEP 517 hook `build_wheel`.
|
||||
BuildWheel {
|
||||
wheel_directory: PathBuf,
|
||||
#[arg(long)]
|
||||
metadata_directory: Option<PathBuf>,
|
||||
},
|
||||
/// PEP 660 hook `build_editable`.
|
||||
BuildEditable {
|
||||
wheel_directory: PathBuf,
|
||||
#[arg(long)]
|
||||
metadata_directory: Option<PathBuf>,
|
||||
},
|
||||
/// PEP 517 hook `get_requires_for_build_sdist`.
|
||||
GetRequiresForBuildSdist,
|
||||
/// PEP 517 hook `get_requires_for_build_wheel`.
|
||||
GetRequiresForBuildWheel,
|
||||
/// PEP 517 hook `prepare_metadata_for_build_wheel`.
|
||||
PrepareMetadataForBuildWheel { wheel_directory: PathBuf },
|
||||
/// PEP 660 hook `get_requires_for_build_editable`.
|
||||
GetRequiresForBuildEditable,
|
||||
/// PEP 660 hook `prepare_metadata_for_build_editable`.
|
||||
PrepareMetadataForBuildEditable { wheel_directory: PathBuf },
|
||||
}
|
||||
|
|
|
|||
41
crates/uv/src/commands/build_backend.rs
Normal file
41
crates/uv/src/commands/build_backend.rs
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
use crate::commands::ExitStatus;
|
||||
use anyhow::Result;
|
||||
use std::path::Path;
|
||||
|
||||
pub(crate) fn build_sdist(_sdist_directory: &Path) -> Result<ExitStatus> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
pub(crate) fn build_wheel(
|
||||
_wheel_directory: &Path,
|
||||
_metadata_directory: Option<&Path>,
|
||||
) -> Result<ExitStatus> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
pub(crate) fn build_editable(
|
||||
_wheel_directory: &Path,
|
||||
_metadata_directory: Option<&Path>,
|
||||
) -> Result<ExitStatus> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
pub(crate) fn get_requires_for_build_sdist() -> Result<ExitStatus> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
pub(crate) fn get_requires_for_build_wheel() -> Result<ExitStatus> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
pub(crate) fn prepare_metadata_for_build_wheel(_wheel_directory: &Path) -> Result<ExitStatus> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
pub(crate) fn get_requires_for_build_editable() -> Result<ExitStatus> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
pub(crate) fn prepare_metadata_for_build_editable(_wheel_directory: &Path) -> Result<ExitStatus> {
|
||||
todo!()
|
||||
}
|
||||
|
|
@ -60,20 +60,20 @@ pub(crate) use version::version;
|
|||
|
||||
use crate::printer::Printer;
|
||||
|
||||
mod build;
|
||||
pub(crate) mod build_backend;
|
||||
mod cache_clean;
|
||||
mod cache_dir;
|
||||
mod cache_prune;
|
||||
mod help;
|
||||
pub(crate) mod pip;
|
||||
mod project;
|
||||
mod publish;
|
||||
mod python;
|
||||
pub(crate) mod reporters;
|
||||
mod tool;
|
||||
|
||||
mod build;
|
||||
mod publish;
|
||||
#[cfg(feature = "self-update")]
|
||||
mod self_update;
|
||||
mod tool;
|
||||
mod venv;
|
||||
mod version;
|
||||
|
||||
|
|
|
|||
|
|
@ -15,8 +15,8 @@ use tracing::{debug, instrument};
|
|||
use uv_cache::{Cache, Refresh};
|
||||
use uv_cache_info::Timestamp;
|
||||
use uv_cli::{
|
||||
compat::CompatArgs, CacheCommand, CacheNamespace, Cli, Commands, PipCommand, PipNamespace,
|
||||
ProjectCommand,
|
||||
compat::CompatArgs, BuildBackendCommand, CacheCommand, CacheNamespace, Cli, Commands,
|
||||
PipCommand, PipNamespace, ProjectCommand,
|
||||
};
|
||||
use uv_cli::{PythonCommand, PythonNamespace, ToolCommand, ToolNamespace, TopLevelArgs};
|
||||
#[cfg(feature = "self-update")]
|
||||
|
|
@ -1118,6 +1118,40 @@ async fn run(cli: Cli) -> Result<ExitStatus> {
|
|||
)
|
||||
.await
|
||||
}
|
||||
Commands::BuildBackend { command } => match command {
|
||||
BuildBackendCommand::BuildSdist { sdist_directory } => {
|
||||
commands::build_backend::build_sdist(&sdist_directory)
|
||||
}
|
||||
BuildBackendCommand::BuildWheel {
|
||||
wheel_directory,
|
||||
metadata_directory,
|
||||
} => commands::build_backend::build_wheel(
|
||||
&wheel_directory,
|
||||
metadata_directory.as_deref(),
|
||||
),
|
||||
BuildBackendCommand::BuildEditable {
|
||||
wheel_directory,
|
||||
metadata_directory,
|
||||
} => commands::build_backend::build_editable(
|
||||
&wheel_directory,
|
||||
metadata_directory.as_deref(),
|
||||
),
|
||||
BuildBackendCommand::GetRequiresForBuildSdist => {
|
||||
commands::build_backend::get_requires_for_build_sdist()
|
||||
}
|
||||
BuildBackendCommand::GetRequiresForBuildWheel => {
|
||||
commands::build_backend::get_requires_for_build_wheel()
|
||||
}
|
||||
BuildBackendCommand::PrepareMetadataForBuildWheel { wheel_directory } => {
|
||||
commands::build_backend::prepare_metadata_for_build_wheel(&wheel_directory)
|
||||
}
|
||||
BuildBackendCommand::GetRequiresForBuildEditable => {
|
||||
commands::build_backend::get_requires_for_build_editable()
|
||||
}
|
||||
BuildBackendCommand::PrepareMetadataForBuildEditable { wheel_directory } => {
|
||||
commands::build_backend::prepare_metadata_for_build_editable(&wheel_directory)
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,41 +1,30 @@
|
|||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
import sys
|
||||
import sysconfig
|
||||
|
||||
if os.environ.get("UV_PREVIEW"):
|
||||
from ._build_backend import *
|
||||
from ._find_uv import find_uv_bin
|
||||
|
||||
def find_uv_bin() -> str:
|
||||
"""Return the uv binary path."""
|
||||
|
||||
uv_exe = "uv" + sysconfig.get_config_var("EXE")
|
||||
|
||||
path = os.path.join(sysconfig.get_path("scripts"), uv_exe)
|
||||
if os.path.isfile(path):
|
||||
return path
|
||||
|
||||
if sys.version_info >= (3, 10):
|
||||
user_scheme = sysconfig.get_preferred_scheme("user")
|
||||
elif os.name == "nt":
|
||||
user_scheme = "nt_user"
|
||||
elif sys.platform == "darwin" and sys._framework:
|
||||
user_scheme = "osx_framework_user"
|
||||
else:
|
||||
user_scheme = "posix_user"
|
||||
|
||||
path = os.path.join(sysconfig.get_path("scripts", scheme=user_scheme), uv_exe)
|
||||
if os.path.isfile(path):
|
||||
return path
|
||||
|
||||
# Search in `bin` adjacent to package root (as created by `pip install --target`).
|
||||
pkg_root = os.path.dirname(os.path.dirname(__file__))
|
||||
target_path = os.path.join(pkg_root, "bin", uv_exe)
|
||||
if os.path.isfile(target_path):
|
||||
return target_path
|
||||
|
||||
raise FileNotFoundError(path)
|
||||
|
||||
|
||||
__all__ = [
|
||||
"find_uv_bin",
|
||||
]
|
||||
if os.environ.get("UV_PREVIEW"):
|
||||
__all__ = [
|
||||
"find_uv_bin",
|
||||
# PEP 517 hook `build_sdist`.
|
||||
"build_sdist",
|
||||
# PEP 517 hook `build_wheel`.
|
||||
"build_wheel",
|
||||
# PEP 660 hook `build_editable`.
|
||||
"build_editable",
|
||||
# PEP 517 hook `get_requires_for_build_sdist`.
|
||||
"get_requires_for_build_sdist",
|
||||
# PEP 517 hook `get_requires_for_build_wheel`.
|
||||
"get_requires_for_build_wheel",
|
||||
# PEP 517 hook `prepare_metadata_for_build_wheel`.
|
||||
"prepare_metadata_for_build_wheel",
|
||||
# PEP 660 hook `get_requires_for_build_editable`.
|
||||
"get_requires_for_build_editable",
|
||||
# PEP 660 hook `prepare_metadata_for_build_editable`.
|
||||
"prepare_metadata_for_build_editable",
|
||||
]
|
||||
else:
|
||||
__all__ = ["find_uv_bin"]
|
||||
|
|
|
|||
110
python/uv/_build_backend.py
Normal file
110
python/uv/_build_backend.py
Normal file
|
|
@ -0,0 +1,110 @@
|
|||
"""
|
||||
Python shims for the PEP 517 and PEP 660 build backend.
|
||||
|
||||
Major imports in this module are required to be lazy:
|
||||
```
|
||||
$ hyperfine \
|
||||
"/usr/bin/python3 -c \"print('hi')\"" \
|
||||
"/usr/bin/python3 -c \"from subprocess import check_call; print('hi')\""
|
||||
Base: Time (mean ± σ): 11.0 ms ± 1.7 ms [User: 8.5 ms, System: 2.5 ms]
|
||||
With import: Time (mean ± σ): 15.2 ms ± 2.0 ms [User: 12.3 ms, System: 2.9 ms]
|
||||
Base 1.38 ± 0.28 times faster than with import
|
||||
```
|
||||
|
||||
The same thing goes for the typing module, so we use Python 3.10 type annotations that
|
||||
don't require importing typing but then quote them so earlier Python version ignore
|
||||
them while IDEs and type checker can see through the quotes.
|
||||
"""
|
||||
|
||||
|
||||
def warn_config_settings(config_settings: "dict | None" = None):
|
||||
import sys
|
||||
|
||||
if config_settings:
|
||||
print("Warning: Config settings are not supported", file=sys.stderr)
|
||||
|
||||
|
||||
def call(args: "list[str]", config_settings: "dict | None" = None) -> str:
|
||||
"""Invoke a uv subprocess and return the filename from stdout."""
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
from ._find_uv import find_uv_bin
|
||||
|
||||
warn_config_settings(config_settings)
|
||||
# Forward stderr, capture stdout for the filename
|
||||
result = subprocess.run([find_uv_bin()] + args, stdout=subprocess.PIPE)
|
||||
if result.returncode != 0:
|
||||
sys.exit(result.returncode)
|
||||
# If there was extra stdout, forward it (there should not be extra stdout)
|
||||
result = result.stdout.decode("utf-8").strip().splitlines(keepends=True)
|
||||
sys.stdout.writelines(result[:-1])
|
||||
# Fail explicitly instead of an irrelevant stacktrace
|
||||
if not result:
|
||||
print("uv subprocess did not return a filename on stdout", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
return result[-1].strip()
|
||||
|
||||
|
||||
def build_sdist(sdist_directory: str, config_settings: "dict | None" = None):
|
||||
"""PEP 517 hook `build_sdist`."""
|
||||
args = ["build-backend", "build-sdist", sdist_directory]
|
||||
return call(args, config_settings)
|
||||
|
||||
|
||||
def build_wheel(
|
||||
wheel_directory: str,
|
||||
config_settings: "dict | None" = None,
|
||||
metadata_directory: "str | None" = None,
|
||||
):
|
||||
"""PEP 517 hook `build_wheel`."""
|
||||
args = ["build-backend", "build-wheel", wheel_directory]
|
||||
if metadata_directory:
|
||||
args.extend(["--metadata-directory", metadata_directory])
|
||||
return call(args, config_settings)
|
||||
|
||||
|
||||
def get_requires_for_build_sdist(config_settings: "dict | None" = None):
|
||||
"""PEP 517 hook `get_requires_for_build_sdist`."""
|
||||
warn_config_settings(config_settings)
|
||||
return []
|
||||
|
||||
|
||||
def get_requires_for_build_wheel(config_settings: "dict | None" = None):
|
||||
"""PEP 517 hook `get_requires_for_build_wheel`."""
|
||||
warn_config_settings(config_settings)
|
||||
return []
|
||||
|
||||
|
||||
def prepare_metadata_for_build_wheel(
|
||||
metadata_directory: str, config_settings: "dict | None" = None
|
||||
):
|
||||
"""PEP 517 hook `prepare_metadata_for_build_wheel`."""
|
||||
args = ["build-backend", "prepare-metadata-for-build-wheel", metadata_directory]
|
||||
return call(args, config_settings)
|
||||
|
||||
|
||||
def build_editable(
|
||||
wheel_directory: str,
|
||||
config_settings: "dict | None" = None,
|
||||
metadata_directory: "str | None" = None,
|
||||
):
|
||||
"""PEP 660 hook `build_editable`."""
|
||||
args = ["build-backend", "build-editable", wheel_directory]
|
||||
if metadata_directory:
|
||||
args.extend(["--metadata-directory", metadata_directory])
|
||||
return call(args, config_settings)
|
||||
|
||||
|
||||
def get_requires_for_build_editable(config_settings: "dict | None" = None):
|
||||
"""PEP 660 hook `get_requires_for_build_editable`."""
|
||||
warn_config_settings(config_settings)
|
||||
return []
|
||||
|
||||
|
||||
def prepare_metadata_for_build_editable(
|
||||
metadata_directory: str, config_settings: "dict | None" = None
|
||||
):
|
||||
"""PEP 660 hook `prepare_metadata_for_build_editable`."""
|
||||
args = ["build-backend", "prepare-metadata-for-build-editable", metadata_directory]
|
||||
return call(args, config_settings)
|
||||
36
python/uv/_find_uv.py
Normal file
36
python/uv/_find_uv.py
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
import sys
|
||||
import sysconfig
|
||||
|
||||
|
||||
def find_uv_bin() -> str:
|
||||
"""Return the uv binary path."""
|
||||
|
||||
uv_exe = "uv" + sysconfig.get_config_var("EXE")
|
||||
|
||||
path = os.path.join(sysconfig.get_path("scripts"), uv_exe)
|
||||
if os.path.isfile(path):
|
||||
return path
|
||||
|
||||
if sys.version_info >= (3, 10):
|
||||
user_scheme = sysconfig.get_preferred_scheme("user")
|
||||
elif os.name == "nt":
|
||||
user_scheme = "nt_user"
|
||||
elif sys.platform == "darwin" and sys._framework:
|
||||
user_scheme = "osx_framework_user"
|
||||
else:
|
||||
user_scheme = "posix_user"
|
||||
|
||||
path = os.path.join(sysconfig.get_path("scripts", scheme=user_scheme), uv_exe)
|
||||
if os.path.isfile(path):
|
||||
return path
|
||||
|
||||
# Search in `bin` adjacent to package root (as created by `pip install --target`).
|
||||
pkg_root = os.path.dirname(os.path.dirname(__file__))
|
||||
target_path = os.path.join(pkg_root, "bin", uv_exe)
|
||||
if os.path.isfile(target_path):
|
||||
return target_path
|
||||
|
||||
raise FileNotFoundError(path)
|
||||
1
scripts/packages/uv_backend/.gitignore
vendored
Normal file
1
scripts/packages/uv_backend/.gitignore
vendored
Normal file
|
|
@ -0,0 +1 @@
|
|||
dist/
|
||||
3
scripts/packages/uv_backend/README.md
Normal file
3
scripts/packages/uv_backend/README.md
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
# uv_backend
|
||||
|
||||
A simple package to be built with the uv build backend.
|
||||
11
scripts/packages/uv_backend/pyproject.toml
Normal file
11
scripts/packages/uv_backend/pyproject.toml
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
[project]
|
||||
name = "uv-backend"
|
||||
version = "0.1.0"
|
||||
description = "Add your description here"
|
||||
readme = "README.md"
|
||||
requires-python = ">=3.12"
|
||||
dependencies = []
|
||||
|
||||
[build-system]
|
||||
requires = ["uv>=0.4.15,<5"]
|
||||
build-backend = "uv"
|
||||
2
scripts/packages/uv_backend/src/uv_backend/__init__.py
Normal file
2
scripts/packages/uv_backend/src/uv_backend/__init__.py
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
def greet():
|
||||
print("Hello 👋")
|
||||
Loading…
Add table
Add a link
Reference in a new issue