mirror of
https://github.com/astral-sh/uv.git
synced 2025-09-27 12:39:09 +00:00
Add source distribution support to uv-build
crate (#6896)
## Summary Just exposes the correct PEP 517 hooks.
This commit is contained in:
parent
ea0a0db7a7
commit
42a4d80a63
3 changed files with 81 additions and 50 deletions
|
@ -79,8 +79,8 @@ static DEFAULT_BACKEND: LazyLock<Pep517Backend> = LazyLock::new(|| Pep517Backend
|
||||||
pub enum Error {
|
pub enum Error {
|
||||||
#[error(transparent)]
|
#[error(transparent)]
|
||||||
Io(#[from] io::Error),
|
Io(#[from] io::Error),
|
||||||
#[error("Invalid source distribution: {0}")]
|
#[error("{} does not appear to be a Python project, as neither `pyproject.toml` nor `setup.py` is present in the directory", _0.simplified_display())]
|
||||||
InvalidSourceDist(String),
|
InvalidSourceDist(PathBuf),
|
||||||
#[error("Invalid `pyproject.toml`")]
|
#[error("Invalid `pyproject.toml`")]
|
||||||
InvalidPyprojectToml(#[from] toml::de::Error),
|
InvalidPyprojectToml(#[from] toml::de::Error),
|
||||||
#[error("Editable installs with setup.py legacy builds are unsupported, please specify a build backend in pyproject.toml")]
|
#[error("Editable installs with setup.py legacy builds are unsupported, please specify a build backend in pyproject.toml")]
|
||||||
|
@ -353,7 +353,7 @@ pub struct SourceBuildContext {
|
||||||
default_resolution: Rc<Mutex<Option<Resolution>>>,
|
default_resolution: Rc<Mutex<Option<Resolution>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Holds the state through a series of PEP 517 frontend to backend calls or a single setup.py
|
/// Holds the state through a series of PEP 517 frontend to backend calls or a single `setup.py`
|
||||||
/// invocation.
|
/// invocation.
|
||||||
///
|
///
|
||||||
/// This keeps both the temp dir and the result of a potential `prepare_metadata_for_build_wheel`
|
/// This keeps both the temp dir and the result of a potential `prepare_metadata_for_build_wheel`
|
||||||
|
@ -595,8 +595,7 @@ impl SourceBuild {
|
||||||
// We require either a `pyproject.toml` or a `setup.py` file at the top level.
|
// We require either a `pyproject.toml` or a `setup.py` file at the top level.
|
||||||
if !source_tree.join("setup.py").is_file() {
|
if !source_tree.join("setup.py").is_file() {
|
||||||
return Err(Box::new(Error::InvalidSourceDist(
|
return Err(Box::new(Error::InvalidSourceDist(
|
||||||
"The archive contains neither a `pyproject.toml` nor a `setup.py` file at the top level"
|
source_tree.to_path_buf(),
|
||||||
.to_string(),
|
|
||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -713,19 +712,19 @@ impl SourceBuild {
|
||||||
Ok(self.metadata_directory.clone())
|
Ok(self.metadata_directory.clone())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Build a source distribution from an archive (`.zip` or `.tar.gz`), return the location of the
|
/// Build a distribution from an archive (`.zip` or `.tar.gz`) or source tree, and return the
|
||||||
/// built wheel.
|
/// location of the built distribution.
|
||||||
///
|
///
|
||||||
/// The location will be inside `temp_dir`, i.e. you must use the wheel before dropping the temp
|
/// The location will be inside `temp_dir`, i.e., you must use the distribution before dropping
|
||||||
/// dir.
|
/// the temporary directory.
|
||||||
///
|
///
|
||||||
/// <https://packaging.python.org/en/latest/specifications/source-distribution-format/>
|
/// <https://packaging.python.org/en/latest/specifications/source-distribution-format/>
|
||||||
#[instrument(skip_all, fields(version_id = self.version_id))]
|
#[instrument(skip_all, fields(version_id = self.version_id))]
|
||||||
pub async fn build_wheel(&self, wheel_dir: &Path) -> Result<String, Error> {
|
pub async fn build(&self, wheel_dir: &Path) -> Result<String, Error> {
|
||||||
// The build scripts run with the extracted root as cwd, so they need the absolute path.
|
// The build scripts run with the extracted root as cwd, so they need the absolute path.
|
||||||
let wheel_dir = fs::canonicalize(wheel_dir)?;
|
let wheel_dir = std::path::absolute(wheel_dir)?;
|
||||||
|
|
||||||
// Prevent clashes from two uv processes building wheels in parallel.
|
// Prevent clashes from two uv processes building distributions in parallel.
|
||||||
let tmp_dir = tempdir_in(&wheel_dir)?;
|
let tmp_dir = tempdir_in(&wheel_dir)?;
|
||||||
let filename = self
|
let filename = self
|
||||||
.pep517_build(tmp_dir.path(), &self.pep517_backend)
|
.pep517_build(tmp_dir.path(), &self.pep517_backend)
|
||||||
|
@ -737,47 +736,76 @@ impl SourceBuild {
|
||||||
Ok(filename)
|
Ok(filename)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Perform a PEP 517 build for a wheel or source distribution (sdist).
|
||||||
async fn pep517_build(
|
async fn pep517_build(
|
||||||
&self,
|
&self,
|
||||||
wheel_dir: &Path,
|
output_dir: &Path,
|
||||||
pep517_backend: &Pep517Backend,
|
pep517_backend: &Pep517Backend,
|
||||||
) -> Result<String, Error> {
|
) -> Result<String, Error> {
|
||||||
let metadata_directory = self
|
|
||||||
.metadata_directory
|
|
||||||
.as_deref()
|
|
||||||
.map_or("None".to_string(), |path| {
|
|
||||||
format!(r#""{}""#, path.escape_for_python())
|
|
||||||
});
|
|
||||||
|
|
||||||
// Write the hook output to a file so that we can read it back reliably.
|
// Write the hook output to a file so that we can read it back reliably.
|
||||||
let outfile = self
|
let outfile = self
|
||||||
.temp_dir
|
.temp_dir
|
||||||
.path()
|
.path()
|
||||||
.join(format!("build_{}.txt", self.build_kind));
|
.join(format!("build_{}.txt", self.build_kind));
|
||||||
|
|
||||||
debug!(
|
// Construct the appropriate build script based on the build kind.
|
||||||
r#"Calling `{}.build_{}("{}", {}, {})`"#,
|
let script = match self.build_kind {
|
||||||
pep517_backend.backend,
|
BuildKind::Sdist => {
|
||||||
self.build_kind,
|
debug!(
|
||||||
wheel_dir.escape_for_python(),
|
r#"Calling `{}.build_{}("{}", {})`"#,
|
||||||
self.config_settings.escape_for_python(),
|
pep517_backend.backend,
|
||||||
metadata_directory,
|
self.build_kind,
|
||||||
);
|
output_dir.escape_for_python(),
|
||||||
let script = formatdoc! {
|
self.config_settings.escape_for_python(),
|
||||||
r#"
|
);
|
||||||
{}
|
formatdoc! {
|
||||||
|
r#"
|
||||||
|
{}
|
||||||
|
|
||||||
wheel_filename = backend.build_{}("{}", {}, {})
|
sdist_filename = backend.build_{}("{}", {})
|
||||||
with open("{}", "w") as fp:
|
with open("{}", "w") as fp:
|
||||||
fp.write(wheel_filename)
|
fp.write(sdist_filename)
|
||||||
"#,
|
"#,
|
||||||
pep517_backend.backend_import(),
|
pep517_backend.backend_import(),
|
||||||
self.build_kind,
|
self.build_kind,
|
||||||
wheel_dir.escape_for_python(),
|
output_dir.escape_for_python(),
|
||||||
self.config_settings.escape_for_python(),
|
self.config_settings.escape_for_python(),
|
||||||
metadata_directory,
|
outfile.escape_for_python()
|
||||||
outfile.escape_for_python()
|
}
|
||||||
|
}
|
||||||
|
BuildKind::Wheel | BuildKind::Editable => {
|
||||||
|
let metadata_directory = self
|
||||||
|
.metadata_directory
|
||||||
|
.as_deref()
|
||||||
|
.map_or("None".to_string(), |path| {
|
||||||
|
format!(r#""{}""#, path.escape_for_python())
|
||||||
|
});
|
||||||
|
debug!(
|
||||||
|
r#"Calling `{}.build_{}("{}", {}, {})`"#,
|
||||||
|
pep517_backend.backend,
|
||||||
|
self.build_kind,
|
||||||
|
output_dir.escape_for_python(),
|
||||||
|
self.config_settings.escape_for_python(),
|
||||||
|
metadata_directory,
|
||||||
|
);
|
||||||
|
formatdoc! {
|
||||||
|
r#"
|
||||||
|
{}
|
||||||
|
|
||||||
|
wheel_filename = backend.build_{}("{}", {}, {})
|
||||||
|
with open("{}", "w") as fp:
|
||||||
|
fp.write(wheel_filename)
|
||||||
|
"#,
|
||||||
|
pep517_backend.backend_import(),
|
||||||
|
self.build_kind,
|
||||||
|
output_dir.escape_for_python(),
|
||||||
|
self.config_settings.escape_for_python(),
|
||||||
|
metadata_directory,
|
||||||
|
outfile.escape_for_python()
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let span = info_span!(
|
let span = info_span!(
|
||||||
"run_python_script",
|
"run_python_script",
|
||||||
script=format!("build_{}", self.build_kind),
|
script=format!("build_{}", self.build_kind),
|
||||||
|
@ -797,8 +825,8 @@ impl SourceBuild {
|
||||||
if !output.status.success() {
|
if !output.status.success() {
|
||||||
return Err(Error::from_command_output(
|
return Err(Error::from_command_output(
|
||||||
format!(
|
format!(
|
||||||
"Build backend failed to build wheel through `build_{}()`",
|
"Build backend failed to build {} through `build_{}()`",
|
||||||
self.build_kind
|
self.build_kind, self.build_kind,
|
||||||
),
|
),
|
||||||
&output,
|
&output,
|
||||||
&self.version_id,
|
&self.version_id,
|
||||||
|
@ -806,11 +834,11 @@ impl SourceBuild {
|
||||||
}
|
}
|
||||||
|
|
||||||
let distribution_filename = fs::read_to_string(&outfile)?;
|
let distribution_filename = fs::read_to_string(&outfile)?;
|
||||||
if !wheel_dir.join(&distribution_filename).is_file() {
|
if !output_dir.join(&distribution_filename).is_file() {
|
||||||
return Err(Error::from_command_output(
|
return Err(Error::from_command_output(
|
||||||
format!(
|
format!(
|
||||||
"Build backend failed to produce wheel through `build_{}()`: `{distribution_filename}` not found",
|
"Build backend failed to produce {} through `build_{}()`: `{distribution_filename}` not found",
|
||||||
self.build_kind
|
self.build_kind, self.build_kind,
|
||||||
),
|
),
|
||||||
&output,
|
&output,
|
||||||
&self.version_id,
|
&self.version_id,
|
||||||
|
@ -826,7 +854,7 @@ impl SourceBuildTrait for SourceBuild {
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn wheel<'a>(&'a self, wheel_dir: &'a Path) -> anyhow::Result<String> {
|
async fn wheel<'a>(&'a self, wheel_dir: &'a Path) -> anyhow::Result<String> {
|
||||||
Ok(self.build_wheel(wheel_dir).await?)
|
Ok(self.build(wheel_dir).await?)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -6,10 +6,12 @@ use crate::{PackageNameSpecifier, PackageNameSpecifiers};
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)]
|
#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)]
|
||||||
pub enum BuildKind {
|
pub enum BuildKind {
|
||||||
/// A regular PEP 517 wheel build
|
/// A PEP 517 wheel build.
|
||||||
#[default]
|
#[default]
|
||||||
Wheel,
|
Wheel,
|
||||||
/// A PEP 660 editable installation wheel build
|
/// A PEP 517 source distribution build.
|
||||||
|
Sdist,
|
||||||
|
/// A PEP 660 editable installation wheel build.
|
||||||
Editable,
|
Editable,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -17,6 +19,7 @@ impl Display for BuildKind {
|
||||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||||
match self {
|
match self {
|
||||||
Self::Wheel => f.write_str("wheel"),
|
Self::Wheel => f.write_str("wheel"),
|
||||||
|
Self::Sdist => f.write_str("sdist"),
|
||||||
Self::Editable => f.write_str("editable"),
|
Self::Editable => f.write_str("editable"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -110,5 +110,5 @@ pub(crate) async fn build(args: BuildArgs) -> Result<PathBuf> {
|
||||||
concurrency.builds,
|
concurrency.builds,
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
Ok(wheel_dir.join(builder.build_wheel(&wheel_dir).await?))
|
Ok(wheel_dir.join(builder.build(&wheel_dir).await?))
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue