mirror of https://github.com/astral-sh/uv
Respect relative paths in `uv build` sources (#8237)
## Summary Right now, `uv build` will fail if a package depends on a local source in `build-system.requires`.
This commit is contained in:
parent
b4dca669b4
commit
999b3f06a4
|
|
@ -245,6 +245,7 @@ impl SourceBuild {
|
||||||
pub async fn setup(
|
pub async fn setup(
|
||||||
source: &Path,
|
source: &Path,
|
||||||
subdirectory: Option<&Path>,
|
subdirectory: Option<&Path>,
|
||||||
|
install_path: &Path,
|
||||||
fallback_package_name: Option<&PackageName>,
|
fallback_package_name: Option<&PackageName>,
|
||||||
fallback_package_version: Option<&Version>,
|
fallback_package_version: Option<&Version>,
|
||||||
interpreter: &Interpreter,
|
interpreter: &Interpreter,
|
||||||
|
|
@ -273,6 +274,7 @@ impl SourceBuild {
|
||||||
// Check if we have a PEP 517 build backend.
|
// Check if we have a PEP 517 build backend.
|
||||||
let (pep517_backend, project) = Self::extract_pep517_backend(
|
let (pep517_backend, project) = Self::extract_pep517_backend(
|
||||||
&source_tree,
|
&source_tree,
|
||||||
|
install_path,
|
||||||
fallback_package_name,
|
fallback_package_name,
|
||||||
locations,
|
locations,
|
||||||
source_strategy,
|
source_strategy,
|
||||||
|
|
@ -368,6 +370,7 @@ impl SourceBuild {
|
||||||
create_pep517_build_environment(
|
create_pep517_build_environment(
|
||||||
&runner,
|
&runner,
|
||||||
&source_tree,
|
&source_tree,
|
||||||
|
install_path,
|
||||||
&venv,
|
&venv,
|
||||||
&pep517_backend,
|
&pep517_backend,
|
||||||
build_context,
|
build_context,
|
||||||
|
|
@ -436,6 +439,7 @@ impl SourceBuild {
|
||||||
/// Extract the PEP 517 backend from the `pyproject.toml` or `setup.py` file.
|
/// Extract the PEP 517 backend from the `pyproject.toml` or `setup.py` file.
|
||||||
async fn extract_pep517_backend(
|
async fn extract_pep517_backend(
|
||||||
source_tree: &Path,
|
source_tree: &Path,
|
||||||
|
install_path: &Path,
|
||||||
package_name: Option<&PackageName>,
|
package_name: Option<&PackageName>,
|
||||||
locations: &IndexLocations,
|
locations: &IndexLocations,
|
||||||
source_strategy: SourceStrategy,
|
source_strategy: SourceStrategy,
|
||||||
|
|
@ -469,7 +473,7 @@ impl SourceBuild {
|
||||||
};
|
};
|
||||||
let requires_dist = RequiresDist::from_project_maybe_workspace(
|
let requires_dist = RequiresDist::from_project_maybe_workspace(
|
||||||
requires_dist,
|
requires_dist,
|
||||||
source_tree,
|
install_path,
|
||||||
locations,
|
locations,
|
||||||
source_strategy,
|
source_strategy,
|
||||||
LowerBound::Allow,
|
LowerBound::Allow,
|
||||||
|
|
@ -803,6 +807,7 @@ fn escape_path_for_python(path: &Path) -> String {
|
||||||
async fn create_pep517_build_environment(
|
async fn create_pep517_build_environment(
|
||||||
runner: &PythonRunner,
|
runner: &PythonRunner,
|
||||||
source_tree: &Path,
|
source_tree: &Path,
|
||||||
|
install_path: &Path,
|
||||||
venv: &PythonEnvironment,
|
venv: &PythonEnvironment,
|
||||||
pep517_backend: &Pep517Backend,
|
pep517_backend: &Pep517Backend,
|
||||||
build_context: &impl BuildContext,
|
build_context: &impl BuildContext,
|
||||||
|
|
@ -921,7 +926,7 @@ async fn create_pep517_build_environment(
|
||||||
};
|
};
|
||||||
let requires_dist = RequiresDist::from_project_maybe_workspace(
|
let requires_dist = RequiresDist::from_project_maybe_workspace(
|
||||||
requires_dist,
|
requires_dist,
|
||||||
source_tree,
|
install_path,
|
||||||
locations,
|
locations,
|
||||||
source_strategy,
|
source_strategy,
|
||||||
LowerBound::Allow,
|
LowerBound::Allow,
|
||||||
|
|
|
||||||
|
|
@ -318,6 +318,7 @@ impl<'a> BuildContext for BuildDispatch<'a> {
|
||||||
&'data self,
|
&'data self,
|
||||||
source: &'data Path,
|
source: &'data Path,
|
||||||
subdirectory: Option<&'data Path>,
|
subdirectory: Option<&'data Path>,
|
||||||
|
install_path: &'data Path,
|
||||||
version_id: Option<String>,
|
version_id: Option<String>,
|
||||||
dist: Option<&'data SourceDist>,
|
dist: Option<&'data SourceDist>,
|
||||||
sources: SourceStrategy,
|
sources: SourceStrategy,
|
||||||
|
|
@ -352,6 +353,7 @@ impl<'a> BuildContext for BuildDispatch<'a> {
|
||||||
let builder = SourceBuild::setup(
|
let builder = SourceBuild::setup(
|
||||||
source,
|
source,
|
||||||
subdirectory,
|
subdirectory,
|
||||||
|
install_path,
|
||||||
dist_name,
|
dist_name,
|
||||||
dist_version,
|
dist_version,
|
||||||
self.interpreter,
|
self.interpreter,
|
||||||
|
|
|
||||||
|
|
@ -1712,6 +1712,7 @@ impl<'a, T: BuildContext> SourceDistributionBuilder<'a, T> {
|
||||||
.setup_build(
|
.setup_build(
|
||||||
source_root,
|
source_root,
|
||||||
subdirectory,
|
subdirectory,
|
||||||
|
source_root,
|
||||||
Some(source.to_string()),
|
Some(source.to_string()),
|
||||||
source.as_dist(),
|
source.as_dist(),
|
||||||
source_strategy,
|
source_strategy,
|
||||||
|
|
@ -1756,6 +1757,7 @@ impl<'a, T: BuildContext> SourceDistributionBuilder<'a, T> {
|
||||||
.setup_build(
|
.setup_build(
|
||||||
source_root,
|
source_root,
|
||||||
subdirectory,
|
subdirectory,
|
||||||
|
source_root,
|
||||||
Some(source.to_string()),
|
Some(source.to_string()),
|
||||||
source.as_dist(),
|
source.as_dist(),
|
||||||
source_strategy,
|
source_strategy,
|
||||||
|
|
|
||||||
|
|
@ -111,6 +111,7 @@ pub trait BuildContext {
|
||||||
&'a self,
|
&'a self,
|
||||||
source: &'a Path,
|
source: &'a Path,
|
||||||
subdirectory: Option<&'a Path>,
|
subdirectory: Option<&'a Path>,
|
||||||
|
install_path: &'a Path,
|
||||||
version_id: Option<String>,
|
version_id: Option<String>,
|
||||||
dist: Option<&'a SourceDist>,
|
dist: Option<&'a SourceDist>,
|
||||||
sources: SourceStrategy,
|
sources: SourceStrategy,
|
||||||
|
|
|
||||||
|
|
@ -535,9 +535,9 @@ async fn build_package(
|
||||||
};
|
};
|
||||||
|
|
||||||
// Prepare some common arguments for the build.
|
// Prepare some common arguments for the build.
|
||||||
|
let dist = None;
|
||||||
let subdirectory = None;
|
let subdirectory = None;
|
||||||
let version_id = source.path().file_name().and_then(|name| name.to_str());
|
let version_id = source.path().file_name().and_then(|name| name.to_str());
|
||||||
let dist = None;
|
|
||||||
|
|
||||||
let build_output = match printer {
|
let build_output = match printer {
|
||||||
Printer::Default | Printer::NoProgress | Printer::Verbose => {
|
Printer::Default | Printer::NoProgress | Printer::Verbose => {
|
||||||
|
|
@ -563,6 +563,7 @@ async fn build_package(
|
||||||
.setup_build(
|
.setup_build(
|
||||||
source.path(),
|
source.path(),
|
||||||
subdirectory,
|
subdirectory,
|
||||||
|
source.path(),
|
||||||
version_id.map(ToString::to_string),
|
version_id.map(ToString::to_string),
|
||||||
dist,
|
dist,
|
||||||
sources,
|
sources,
|
||||||
|
|
@ -601,6 +602,7 @@ async fn build_package(
|
||||||
.setup_build(
|
.setup_build(
|
||||||
&extracted,
|
&extracted,
|
||||||
subdirectory,
|
subdirectory,
|
||||||
|
source.path(),
|
||||||
version_id.map(ToString::to_string),
|
version_id.map(ToString::to_string),
|
||||||
dist,
|
dist,
|
||||||
sources,
|
sources,
|
||||||
|
|
@ -623,6 +625,7 @@ async fn build_package(
|
||||||
.setup_build(
|
.setup_build(
|
||||||
source.path(),
|
source.path(),
|
||||||
subdirectory,
|
subdirectory,
|
||||||
|
source.path(),
|
||||||
version_id.map(ToString::to_string),
|
version_id.map(ToString::to_string),
|
||||||
dist,
|
dist,
|
||||||
sources,
|
sources,
|
||||||
|
|
@ -645,6 +648,7 @@ async fn build_package(
|
||||||
.setup_build(
|
.setup_build(
|
||||||
source.path(),
|
source.path(),
|
||||||
subdirectory,
|
subdirectory,
|
||||||
|
source.path(),
|
||||||
version_id.map(ToString::to_string),
|
version_id.map(ToString::to_string),
|
||||||
dist,
|
dist,
|
||||||
sources,
|
sources,
|
||||||
|
|
@ -666,6 +670,7 @@ async fn build_package(
|
||||||
.setup_build(
|
.setup_build(
|
||||||
source.path(),
|
source.path(),
|
||||||
subdirectory,
|
subdirectory,
|
||||||
|
source.path(),
|
||||||
version_id.map(ToString::to_string),
|
version_id.map(ToString::to_string),
|
||||||
dist,
|
dist,
|
||||||
sources,
|
sources,
|
||||||
|
|
@ -684,6 +689,7 @@ async fn build_package(
|
||||||
.setup_build(
|
.setup_build(
|
||||||
source.path(),
|
source.path(),
|
||||||
subdirectory,
|
subdirectory,
|
||||||
|
source.path(),
|
||||||
version_id.map(ToString::to_string),
|
version_id.map(ToString::to_string),
|
||||||
dist,
|
dist,
|
||||||
sources,
|
sources,
|
||||||
|
|
@ -724,6 +730,7 @@ async fn build_package(
|
||||||
.setup_build(
|
.setup_build(
|
||||||
&extracted,
|
&extracted,
|
||||||
subdirectory,
|
subdirectory,
|
||||||
|
source.path(),
|
||||||
version_id.map(ToString::to_string),
|
version_id.map(ToString::to_string),
|
||||||
dist,
|
dist,
|
||||||
sources,
|
sources,
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@ use crate::common::{uv_snapshot, TestContext};
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use assert_fs::prelude::*;
|
use assert_fs::prelude::*;
|
||||||
use fs_err::File;
|
use fs_err::File;
|
||||||
|
use indoc::indoc;
|
||||||
use insta::assert_snapshot;
|
use insta::assert_snapshot;
|
||||||
use predicates::prelude::predicate;
|
use predicates::prelude::predicate;
|
||||||
use zip::ZipArchive;
|
use zip::ZipArchive;
|
||||||
|
|
@ -1766,6 +1767,159 @@ fn build_no_build_logs() -> Result<()> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn tool_uv_sources() -> Result<()> {
|
||||||
|
let context = TestContext::new("3.12");
|
||||||
|
let filters = context
|
||||||
|
.filters()
|
||||||
|
.into_iter()
|
||||||
|
.chain([
|
||||||
|
(r"exit code: 1", "exit status: 1"),
|
||||||
|
(r"bdist\.[^/\\\s]+-[^/\\\s]+", "bdist.linux-x86_64"),
|
||||||
|
(r"\\\.", ""),
|
||||||
|
])
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
|
let build = context.temp_dir.child("backend");
|
||||||
|
build.child("pyproject.toml").write_str(
|
||||||
|
r#"
|
||||||
|
[project]
|
||||||
|
name = "backend"
|
||||||
|
version = "0.1.0"
|
||||||
|
requires-python = ">=3.12"
|
||||||
|
dependencies = ["typing-extensions>=3.10"]
|
||||||
|
|
||||||
|
[build-system]
|
||||||
|
requires = ["setuptools>=42"]
|
||||||
|
build-backend = "setuptools.build_meta"
|
||||||
|
"#,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
build
|
||||||
|
.child("src")
|
||||||
|
.child("backend")
|
||||||
|
.child("__init__.py")
|
||||||
|
.write_str(indoc! { r#"
|
||||||
|
def hello() -> str:
|
||||||
|
return "Hello, world!"
|
||||||
|
"#})?;
|
||||||
|
build.child("README.md").touch()?;
|
||||||
|
|
||||||
|
let project = context.temp_dir.child("project");
|
||||||
|
|
||||||
|
project.child("pyproject.toml").write_str(
|
||||||
|
r#"
|
||||||
|
[project]
|
||||||
|
name = "project"
|
||||||
|
version = "0.1.0"
|
||||||
|
requires-python = ">=3.12"
|
||||||
|
dependencies = ["iniconfig>1"]
|
||||||
|
|
||||||
|
[build-system]
|
||||||
|
requires = ["setuptools>=42", "backend==0.1.0"]
|
||||||
|
build-backend = "setuptools.build_meta"
|
||||||
|
|
||||||
|
[tool.uv.sources]
|
||||||
|
backend = { path = "../backend" }
|
||||||
|
"#,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
project.child("setup.py").write_str(indoc! {r"
|
||||||
|
from setuptools import setup
|
||||||
|
|
||||||
|
from backend import hello
|
||||||
|
|
||||||
|
hello()
|
||||||
|
|
||||||
|
setup()
|
||||||
|
",
|
||||||
|
})?;
|
||||||
|
|
||||||
|
uv_snapshot!(filters, context.build().current_dir(project.path()), @r###"
|
||||||
|
success: true
|
||||||
|
exit_code: 0
|
||||||
|
----- stdout -----
|
||||||
|
|
||||||
|
----- stderr -----
|
||||||
|
Building source distribution...
|
||||||
|
running egg_info
|
||||||
|
creating project.egg-info
|
||||||
|
writing project.egg-info/PKG-INFO
|
||||||
|
writing dependency_links to project.egg-info/dependency_links.txt
|
||||||
|
writing requirements to project.egg-info/requires.txt
|
||||||
|
writing top-level names to project.egg-info/top_level.txt
|
||||||
|
writing manifest file 'project.egg-info/SOURCES.txt'
|
||||||
|
reading manifest file 'project.egg-info/SOURCES.txt'
|
||||||
|
writing manifest file 'project.egg-info/SOURCES.txt'
|
||||||
|
running sdist
|
||||||
|
running egg_info
|
||||||
|
writing project.egg-info/PKG-INFO
|
||||||
|
writing dependency_links to project.egg-info/dependency_links.txt
|
||||||
|
writing requirements to project.egg-info/requires.txt
|
||||||
|
writing top-level names to project.egg-info/top_level.txt
|
||||||
|
reading manifest file 'project.egg-info/SOURCES.txt'
|
||||||
|
writing manifest file 'project.egg-info/SOURCES.txt'
|
||||||
|
warning: sdist: standard file not found: should have one of README, README.rst, README.txt, README.md
|
||||||
|
|
||||||
|
running check
|
||||||
|
creating project-0.1.0
|
||||||
|
creating project-0.1.0/project.egg-info
|
||||||
|
copying files to project-0.1.0...
|
||||||
|
copying pyproject.toml -> project-0.1.0
|
||||||
|
copying setup.py -> project-0.1.0
|
||||||
|
copying project.egg-info/PKG-INFO -> project-0.1.0/project.egg-info
|
||||||
|
copying project.egg-info/SOURCES.txt -> project-0.1.0/project.egg-info
|
||||||
|
copying project.egg-info/dependency_links.txt -> project-0.1.0/project.egg-info
|
||||||
|
copying project.egg-info/requires.txt -> project-0.1.0/project.egg-info
|
||||||
|
copying project.egg-info/top_level.txt -> project-0.1.0/project.egg-info
|
||||||
|
copying project.egg-info/SOURCES.txt -> project-0.1.0/project.egg-info
|
||||||
|
Writing project-0.1.0/setup.cfg
|
||||||
|
Creating tar archive
|
||||||
|
removing 'project-0.1.0' (and everything under it)
|
||||||
|
Building wheel from source distribution...
|
||||||
|
running egg_info
|
||||||
|
writing project.egg-info/PKG-INFO
|
||||||
|
writing dependency_links to project.egg-info/dependency_links.txt
|
||||||
|
writing requirements to project.egg-info/requires.txt
|
||||||
|
writing top-level names to project.egg-info/top_level.txt
|
||||||
|
reading manifest file 'project.egg-info/SOURCES.txt'
|
||||||
|
writing manifest file 'project.egg-info/SOURCES.txt'
|
||||||
|
running bdist_wheel
|
||||||
|
running build
|
||||||
|
installing to build/bdist.linux-x86_64/wheel
|
||||||
|
running install
|
||||||
|
running install_egg_info
|
||||||
|
running egg_info
|
||||||
|
writing project.egg-info/PKG-INFO
|
||||||
|
writing dependency_links to project.egg-info/dependency_links.txt
|
||||||
|
writing requirements to project.egg-info/requires.txt
|
||||||
|
writing top-level names to project.egg-info/top_level.txt
|
||||||
|
reading manifest file 'project.egg-info/SOURCES.txt'
|
||||||
|
writing manifest file 'project.egg-info/SOURCES.txt'
|
||||||
|
Copying project.egg-info to build/bdist.linux-x86_64/wheel/project-0.1.0-py3.12.egg-info
|
||||||
|
running install_scripts
|
||||||
|
creating build/bdist.linux-x86_64/wheel/project-0.1.0.dist-info/WHEEL
|
||||||
|
creating '[TEMP_DIR]/project/dist/[TMP]/wheel' to it
|
||||||
|
adding 'project-0.1.0.dist-info/METADATA'
|
||||||
|
adding 'project-0.1.0.dist-info/WHEEL'
|
||||||
|
adding 'project-0.1.0.dist-info/top_level.txt'
|
||||||
|
adding 'project-0.1.0.dist-info/RECORD'
|
||||||
|
removing build/bdist.linux-x86_64/wheel
|
||||||
|
Successfully built dist/project-0.1.0.tar.gz and dist/project-0.1.0-py3-none-any.whl
|
||||||
|
"###);
|
||||||
|
|
||||||
|
project
|
||||||
|
.child("dist")
|
||||||
|
.child("project-0.1.0.tar.gz")
|
||||||
|
.assert(predicate::path::is_file());
|
||||||
|
project
|
||||||
|
.child("dist")
|
||||||
|
.child("project-0.1.0-py3-none-any.whl")
|
||||||
|
.assert(predicate::path::is_file());
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
/// Check that we have a working git boundary for builds from source dist to wheel in `dist/`.
|
/// Check that we have a working git boundary for builds from source dist to wheel in `dist/`.
|
||||||
#[test]
|
#[test]
|
||||||
fn git_boundary_in_dist_build() -> Result<()> {
|
fn git_boundary_in_dist_build() -> Result<()> {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue