mirror of
https://github.com/astral-sh/uv.git
synced 2025-07-07 21:35:00 +00:00
Special case missing header build errors (on linux) (#354)
One of the most common errors i observed are build failures due to missing header files. On ubuntu, this generally means that you need to install some `<...>-dev` package that the documentation tells you about, e.g. [mysqlclient](https://github.com/PyMySQL/mysqlclient#linux) needs `default-libmysqlclient-dev`, [some psycopg versions](https://www.psycopg.org/psycopg3/docs/basic/install.html#local-installation) (i remember that this was always required at some earlier point) require `libpq-dev` and pygraphviz wants `graphviz-dev`. This is quite common for many scientific packages (where conda has an advantage because they can provide those package as a dependency). The error message can be completely inscrutable if you're just a python programmer (or user) and not a c programmer (example: pygraphviz): ``` warning: no files found matching '*.png' under directory 'doc' warning: no files found matching '*.txt' under directory 'doc' warning: no files found matching '*.css' under directory 'doc' warning: no previously-included files matching '*~' found anywhere in distribution warning: no previously-included files matching '*.pyc' found anywhere in distribution warning: no previously-included files matching '.svn' found anywhere in distribution no previously-included directories found matching 'doc/build' pygraphviz/graphviz_wrap.c:3020:10: fatal error: graphviz/cgraph.h: No such file or directory 3020 | #include "graphviz/cgraph.h" | ^~~~~~~~~~~~~~~~~~~ compilation terminated. error: command '/usr/bin/gcc' failed with exit code 1 ``` The only relevant part is `Fatal error: graphviz/cgraph.h: No such file or directory`. Why is this file not there and how do i get it to be there? This is even harder to spot in pip's output, where it's 11 lines above the last line:  I've special cased missing headers and made sure that the last line tells you the important information: We're missing some header, please check the documentation of {package} {version} for what to install:  Scrolling up:  The difference gets even clearer with a default ubuntu terminal with its 80 columns:  --- Note that the situation is better for a missing compiler, there i get: ``` [...] warning: no previously-included files matching '*~' found anywhere in distribution warning: no previously-included files matching '*.pyc' found anywhere in distribution warning: no previously-included files matching '.svn' found anywhere in distribution no previously-included directories found matching 'doc/build' error: command 'gcc' failed: No such file or directory --- ``` Putting the last line into google, the first two results tell me to `sudo apt-get install gcc`, the third even tells me about `sudo apt install build-essential`
This commit is contained in:
parent
2ebe40b986
commit
d407bbbee6
10 changed files with 186 additions and 13 deletions
3
Cargo.lock
generated
3
Cargo.lock
generated
|
@ -2176,11 +2176,14 @@ dependencies = [
|
||||||
"fs-err",
|
"fs-err",
|
||||||
"gourgeist",
|
"gourgeist",
|
||||||
"indoc",
|
"indoc",
|
||||||
|
"insta",
|
||||||
|
"once_cell",
|
||||||
"pep508_rs",
|
"pep508_rs",
|
||||||
"platform-host",
|
"platform-host",
|
||||||
"puffin-interpreter",
|
"puffin-interpreter",
|
||||||
"puffin-traits",
|
"puffin-traits",
|
||||||
"pyproject-toml",
|
"pyproject-toml",
|
||||||
|
"regex",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"tar",
|
"tar",
|
||||||
|
|
|
@ -61,7 +61,7 @@ pub fn install_wheel(
|
||||||
let wheel_file_path = wheel
|
let wheel_file_path = wheel
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.join(format!("{dist_info_prefix}.dist-info/WHEEL"));
|
.join(format!("{dist_info_prefix}.dist-info/WHEEL"));
|
||||||
let wheel_text = std::fs::read_to_string(&wheel_file_path)?;
|
let wheel_text = std::fs::read_to_string(wheel_file_path)?;
|
||||||
parse_wheel_version(&wheel_text)?;
|
parse_wheel_version(&wheel_text)?;
|
||||||
|
|
||||||
// > 1.c If Root-Is-Purelib == ‘true’, unpack archive into purelib (site-packages).
|
// > 1.c If Root-Is-Purelib == ‘true’, unpack archive into purelib (site-packages).
|
||||||
|
|
|
@ -21,7 +21,9 @@ anyhow = { workspace = true }
|
||||||
flate2 = { workspace = true }
|
flate2 = { workspace = true }
|
||||||
fs-err = { workspace = true }
|
fs-err = { workspace = true }
|
||||||
indoc = { workspace = true }
|
indoc = { workspace = true }
|
||||||
|
once_cell = { workspace = true }
|
||||||
pyproject-toml = { workspace = true }
|
pyproject-toml = { workspace = true }
|
||||||
|
regex = { workspace = true }
|
||||||
serde = { workspace = true }
|
serde = { workspace = true }
|
||||||
serde_json = { workspace = true }
|
serde_json = { workspace = true }
|
||||||
tar = { workspace = true }
|
tar = { workspace = true }
|
||||||
|
@ -32,3 +34,6 @@ toml = { workspace = true }
|
||||||
tracing = { workspace = true }
|
tracing = { workspace = true }
|
||||||
which = { workspace = true}
|
which = { workspace = true}
|
||||||
zip = { workspace = true }
|
zip = { workspace = true }
|
||||||
|
|
||||||
|
[dev-dependencies]
|
||||||
|
insta = { version = "1.34.0" }
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
//!
|
//!
|
||||||
//! <https://packaging.python.org/en/latest/specifications/source-distribution-format/>
|
//! <https://packaging.python.org/en/latest/specifications/source-distribution-format/>
|
||||||
|
|
||||||
|
use std::fmt::{Display, Formatter};
|
||||||
use std::io;
|
use std::io;
|
||||||
use std::io::BufRead;
|
use std::io::BufRead;
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
|
@ -13,7 +14,9 @@ use flate2::read::GzDecoder;
|
||||||
use fs_err as fs;
|
use fs_err as fs;
|
||||||
use fs_err::{DirEntry, File};
|
use fs_err::{DirEntry, File};
|
||||||
use indoc::formatdoc;
|
use indoc::formatdoc;
|
||||||
|
use once_cell::sync::Lazy;
|
||||||
use pyproject_toml::{BuildSystem, Project};
|
use pyproject_toml::{BuildSystem, Project};
|
||||||
|
use regex::Regex;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use tar::Archive;
|
use tar::Archive;
|
||||||
use tempfile::{tempdir, TempDir};
|
use tempfile::{tempdir, TempDir};
|
||||||
|
@ -26,6 +29,14 @@ use pep508_rs::Requirement;
|
||||||
use puffin_interpreter::{InterpreterInfo, Virtualenv};
|
use puffin_interpreter::{InterpreterInfo, Virtualenv};
|
||||||
use puffin_traits::BuildContext;
|
use puffin_traits::BuildContext;
|
||||||
|
|
||||||
|
/// e.g. `pygraphviz/graphviz_wrap.c:3020:10: fatal error: graphviz/cgraph.h: No such file or directory`
|
||||||
|
static MISSING_HEADER_RE: Lazy<Regex> = Lazy::new(|| {
|
||||||
|
Regex::new(
|
||||||
|
r".*\.(c|c..|h|h..):\d+:\d+: fatal error: (?<header>.*\.(h|h..)): No such file or directory"
|
||||||
|
)
|
||||||
|
.unwrap()
|
||||||
|
});
|
||||||
|
|
||||||
#[derive(Error, Debug)]
|
#[derive(Error, Debug)]
|
||||||
pub enum Error {
|
pub enum Error {
|
||||||
#[error(transparent)]
|
#[error(transparent)]
|
||||||
|
@ -50,14 +61,62 @@ pub enum Error {
|
||||||
stdout: String,
|
stdout: String,
|
||||||
stderr: String,
|
stderr: String,
|
||||||
},
|
},
|
||||||
|
/// Nudge the user towards installing the missing dev library
|
||||||
|
#[error("{message}:\n--- stdout:\n{stdout}\n--- stderr:\n{stderr}\n---")]
|
||||||
|
MissingHeader {
|
||||||
|
message: String,
|
||||||
|
stdout: String,
|
||||||
|
stderr: String,
|
||||||
|
#[source]
|
||||||
|
missing_header_cause: MissingHeaderCause,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Error)]
|
||||||
|
pub struct MissingHeaderCause {
|
||||||
|
header: String,
|
||||||
|
// I've picked this over the better readable package name to make clear that you need to
|
||||||
|
// look for the build dependencies of that version or git commit respectively
|
||||||
|
package_id: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for MissingHeaderCause {
|
||||||
|
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||||
|
write!(
|
||||||
|
f,
|
||||||
|
"This error likely indicates that you need to install a library that provides \"{}\" for {}",
|
||||||
|
self.header, self.package_id
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Error {
|
impl Error {
|
||||||
fn from_command_output(message: String, output: &Output) -> Self {
|
fn from_command_output(message: String, output: &Output, package_id: &str) -> Self {
|
||||||
|
let stdout = String::from_utf8_lossy(&output.stdout).trim().to_string();
|
||||||
|
let stderr = String::from_utf8_lossy(&output.stderr).trim().to_string();
|
||||||
|
|
||||||
|
// In the cases i've seen it was the 5th last line (see test case), 10 seems like a
|
||||||
|
// reasonable cutoff
|
||||||
|
if let Some(header) =
|
||||||
|
stderr.lines().rev().take(10).find_map(|line| {
|
||||||
|
Some(MISSING_HEADER_RE.captures(line.trim())?["header"].to_string())
|
||||||
|
})
|
||||||
|
{
|
||||||
|
return Self::MissingHeader {
|
||||||
|
message,
|
||||||
|
stdout,
|
||||||
|
stderr,
|
||||||
|
missing_header_cause: MissingHeaderCause {
|
||||||
|
header,
|
||||||
|
package_id: package_id.to_string(),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
Self::BuildBackend {
|
Self::BuildBackend {
|
||||||
message,
|
message,
|
||||||
stdout: String::from_utf8_lossy(&output.stdout).trim().to_string(),
|
stdout,
|
||||||
stderr: String::from_utf8_lossy(&output.stderr).trim().to_string(),
|
stderr,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -124,17 +183,22 @@ pub struct SourceBuild {
|
||||||
/// > directory created by prepare_metadata_for_build_wheel, including any unrecognized files
|
/// > directory created by prepare_metadata_for_build_wheel, including any unrecognized files
|
||||||
/// > it created.
|
/// > it created.
|
||||||
metadata_directory: Option<PathBuf>,
|
metadata_directory: Option<PathBuf>,
|
||||||
|
/// Package id such as `foo-1.2.3`, for error reporting
|
||||||
|
package_id: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SourceBuild {
|
impl SourceBuild {
|
||||||
/// Create a virtual environment in which to build a source distribution, extracting the
|
/// Create a virtual environment in which to build a source distribution, extracting the
|
||||||
/// contents from an archive if necessary.
|
/// contents from an archive if necessary.
|
||||||
|
///
|
||||||
|
/// `package_id` is for error reporting only.
|
||||||
pub async fn setup(
|
pub async fn setup(
|
||||||
source: &Path,
|
source: &Path,
|
||||||
subdirectory: Option<&Path>,
|
subdirectory: Option<&Path>,
|
||||||
interpreter_info: &InterpreterInfo,
|
interpreter_info: &InterpreterInfo,
|
||||||
build_context: &impl BuildContext,
|
build_context: &impl BuildContext,
|
||||||
source_build_context: SourceBuildContext,
|
source_build_context: SourceBuildContext,
|
||||||
|
package_id: &str,
|
||||||
) -> Result<SourceBuild, Error> {
|
) -> Result<SourceBuild, Error> {
|
||||||
let temp_dir = tempdir()?;
|
let temp_dir = tempdir()?;
|
||||||
|
|
||||||
|
@ -232,8 +296,14 @@ impl SourceBuild {
|
||||||
};
|
};
|
||||||
|
|
||||||
if let Some(pep517_backend) = &pep517_backend {
|
if let Some(pep517_backend) = &pep517_backend {
|
||||||
create_pep517_build_environment(&source_tree, &venv, pep517_backend, build_context)
|
create_pep517_build_environment(
|
||||||
.await?;
|
&source_tree,
|
||||||
|
&venv,
|
||||||
|
pep517_backend,
|
||||||
|
build_context,
|
||||||
|
package_id,
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
} else {
|
} else {
|
||||||
if !source_tree.join("setup.py").is_file() {
|
if !source_tree.join("setup.py").is_file() {
|
||||||
return Err(Error::InvalidSourceDistribution(
|
return Err(Error::InvalidSourceDistribution(
|
||||||
|
@ -249,6 +319,7 @@ impl SourceBuild {
|
||||||
pep517_backend,
|
pep517_backend,
|
||||||
venv,
|
venv,
|
||||||
metadata_directory: None,
|
metadata_directory: None,
|
||||||
|
package_id: package_id.to_string(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -284,7 +355,7 @@ impl SourceBuild {
|
||||||
return Err(Error::from_command_output(
|
return Err(Error::from_command_output(
|
||||||
"Build backend failed to determine metadata through `prepare_metadata_for_build_wheel`".to_string(),
|
"Build backend failed to determine metadata through `prepare_metadata_for_build_wheel`".to_string(),
|
||||||
&output,
|
&output,
|
||||||
));
|
&self.package_id));
|
||||||
}
|
}
|
||||||
let message = output
|
let message = output
|
||||||
.stdout
|
.stdout
|
||||||
|
@ -301,6 +372,7 @@ impl SourceBuild {
|
||||||
`prepare_metadata_for_build_wheel`: {err}"
|
`prepare_metadata_for_build_wheel`: {err}"
|
||||||
),
|
),
|
||||||
&output,
|
&output,
|
||||||
|
&self.package_id,
|
||||||
)
|
)
|
||||||
})?;
|
})?;
|
||||||
if message.is_empty() {
|
if message.is_empty() {
|
||||||
|
@ -336,6 +408,7 @@ impl SourceBuild {
|
||||||
return Err(Error::from_command_output(
|
return Err(Error::from_command_output(
|
||||||
"Failed building wheel through setup.py".to_string(),
|
"Failed building wheel through setup.py".to_string(),
|
||||||
&output,
|
&output,
|
||||||
|
&self.package_id,
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
let dist = fs::read_dir(self.source_tree.join("dist"))?;
|
let dist = fs::read_dir(self.source_tree.join("dist"))?;
|
||||||
|
@ -346,7 +419,7 @@ impl SourceBuild {
|
||||||
"Expected exactly wheel in `dist/` after invoking setup.py, found {dist_dir:?}"
|
"Expected exactly wheel in `dist/` after invoking setup.py, found {dist_dir:?}"
|
||||||
),
|
),
|
||||||
&output,
|
&output,
|
||||||
));
|
&self.package_id));
|
||||||
};
|
};
|
||||||
// TODO(konstin): Faster copy such as reflink? Or maybe don't really let the user pick the target dir
|
// TODO(konstin): Faster copy such as reflink? Or maybe don't really let the user pick the target dir
|
||||||
let wheel = wheel_dir.join(dist_wheel.file_name());
|
let wheel = wheel_dir.join(dist_wheel.file_name());
|
||||||
|
@ -382,6 +455,7 @@ impl SourceBuild {
|
||||||
return Err(Error::from_command_output(
|
return Err(Error::from_command_output(
|
||||||
"Build backend failed to build wheel through `build_wheel()` ".to_string(),
|
"Build backend failed to build wheel through `build_wheel()` ".to_string(),
|
||||||
&output,
|
&output,
|
||||||
|
&self.package_id,
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
let stdout = String::from_utf8_lossy(&output.stdout);
|
let stdout = String::from_utf8_lossy(&output.stdout);
|
||||||
|
@ -393,6 +467,7 @@ impl SourceBuild {
|
||||||
"Build backend did not return the wheel filename through `build_wheel()`"
|
"Build backend did not return the wheel filename through `build_wheel()`"
|
||||||
.to_string(),
|
.to_string(),
|
||||||
&output,
|
&output,
|
||||||
|
&self.package_id,
|
||||||
));
|
));
|
||||||
};
|
};
|
||||||
Ok(distribution_filename.to_string())
|
Ok(distribution_filename.to_string())
|
||||||
|
@ -411,6 +486,7 @@ async fn create_pep517_build_environment(
|
||||||
venv: &Virtualenv,
|
venv: &Virtualenv,
|
||||||
pep517_backend: &Pep517Backend,
|
pep517_backend: &Pep517Backend,
|
||||||
build_context: &impl BuildContext,
|
build_context: &impl BuildContext,
|
||||||
|
package_id: &str,
|
||||||
) -> Result<(), Error> {
|
) -> Result<(), Error> {
|
||||||
debug!(
|
debug!(
|
||||||
"Calling `{}.get_requires_for_build_wheel()`",
|
"Calling `{}.get_requires_for_build_wheel()`",
|
||||||
|
@ -438,6 +514,7 @@ async fn create_pep517_build_environment(
|
||||||
"Build backend failed to determine extras requires with `get_requires_for_build_wheel`"
|
"Build backend failed to determine extras requires with `get_requires_for_build_wheel`"
|
||||||
.to_string(),
|
.to_string(),
|
||||||
&output,
|
&output,
|
||||||
|
package_id,
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
let extra_requires = output
|
let extra_requires = output
|
||||||
|
@ -456,6 +533,7 @@ async fn create_pep517_build_environment(
|
||||||
`get_requires_for_build_wheel`: {err}"
|
`get_requires_for_build_wheel`: {err}"
|
||||||
),
|
),
|
||||||
&output,
|
&output,
|
||||||
|
package_id,
|
||||||
)
|
)
|
||||||
})?;
|
})?;
|
||||||
// Some packages (such as tqdm 4.66.1) list only extra requires that have already been part of
|
// Some packages (such as tqdm 4.66.1) list only extra requires that have already been part of
|
||||||
|
@ -541,3 +619,69 @@ fn run_python_script(
|
||||||
.output()
|
.output()
|
||||||
.map_err(|err| Error::CommandFailed(python_interpreter.to_path_buf(), err))
|
.map_err(|err| Error::CommandFailed(python_interpreter.to_path_buf(), err))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test {
|
||||||
|
use std::process::{ExitStatus, Output};
|
||||||
|
|
||||||
|
use crate::Error;
|
||||||
|
use indoc::indoc;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn missing_header() {
|
||||||
|
let output = Output {
|
||||||
|
status: ExitStatus::default(), // This is wrong but `from_raw` is platform gated
|
||||||
|
stdout: indoc!(r#"
|
||||||
|
running bdist_wheel
|
||||||
|
running build
|
||||||
|
[...]
|
||||||
|
creating build/temp.linux-x86_64-cpython-39/pygraphviz
|
||||||
|
gcc -Wno-unused-result -Wsign-compare -DNDEBUG -g -fwrapv -O3 -Wall -DOPENSSL_NO_SSL3 -fPIC -DSWIG_PYTHON_STRICT_BYTE_CHAR -I/tmp/.tmpy6vVes/.venv/include -I/home/konsti/.pyenv/versions/3.9.18/include/python3.9 -c pygraphviz/graphviz_wrap.c -o build/temp.linux-x86_64-cpython-39/pygraphviz/graphviz_wrap.o
|
||||||
|
"#
|
||||||
|
).as_bytes().to_vec(),
|
||||||
|
stderr: indoc!(r#"
|
||||||
|
warning: no files found matching '*.png' under directory 'doc'
|
||||||
|
warning: no files found matching '*.txt' under directory 'doc'
|
||||||
|
[...]
|
||||||
|
no previously-included directories found matching 'doc/build'
|
||||||
|
pygraphviz/graphviz_wrap.c:3020:10: fatal error: graphviz/cgraph.h: No such file or directory
|
||||||
|
3020 | #include "graphviz/cgraph.h"
|
||||||
|
| ^~~~~~~~~~~~~~~~~~~
|
||||||
|
compilation terminated.
|
||||||
|
error: command '/usr/bin/gcc' failed with exit code 1
|
||||||
|
"#
|
||||||
|
).as_bytes().to_vec(),
|
||||||
|
};
|
||||||
|
|
||||||
|
let err = Error::from_command_output(
|
||||||
|
"Failed building wheel through setup.py".to_string(),
|
||||||
|
&output,
|
||||||
|
"pygraphviz-1.11",
|
||||||
|
);
|
||||||
|
assert!(matches!(err, Error::MissingHeader { .. }));
|
||||||
|
insta::assert_display_snapshot!(err, @r###"
|
||||||
|
Failed building wheel through setup.py:
|
||||||
|
--- stdout:
|
||||||
|
running bdist_wheel
|
||||||
|
running build
|
||||||
|
[...]
|
||||||
|
creating build/temp.linux-x86_64-cpython-39/pygraphviz
|
||||||
|
gcc -Wno-unused-result -Wsign-compare -DNDEBUG -g -fwrapv -O3 -Wall -DOPENSSL_NO_SSL3 -fPIC -DSWIG_PYTHON_STRICT_BYTE_CHAR -I/tmp/.tmpy6vVes/.venv/include -I/home/konsti/.pyenv/versions/3.9.18/include/python3.9 -c pygraphviz/graphviz_wrap.c -o build/temp.linux-x86_64-cpython-39/pygraphviz/graphviz_wrap.o
|
||||||
|
--- stderr:
|
||||||
|
warning: no files found matching '*.png' under directory 'doc'
|
||||||
|
warning: no files found matching '*.txt' under directory 'doc'
|
||||||
|
[...]
|
||||||
|
no previously-included directories found matching 'doc/build'
|
||||||
|
pygraphviz/graphviz_wrap.c:3020:10: fatal error: graphviz/cgraph.h: No such file or directory
|
||||||
|
3020 | #include "graphviz/cgraph.h"
|
||||||
|
| ^~~~~~~~~~~~~~~~~~~
|
||||||
|
compilation terminated.
|
||||||
|
error: command '/usr/bin/gcc' failed with exit code 1
|
||||||
|
---
|
||||||
|
"###);
|
||||||
|
insta::assert_display_snapshot!(
|
||||||
|
std::error::Error::source(&err).unwrap(),
|
||||||
|
@r###"This error likely indicates that you need to install a library that provides "graphviz/cgraph.h" for pygraphviz-1.11"###
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -54,7 +54,13 @@ pub(crate) async fn build(args: BuildArgs) -> Result<PathBuf> {
|
||||||
false,
|
false,
|
||||||
);
|
);
|
||||||
let wheel = build_dispatch
|
let wheel = build_dispatch
|
||||||
.build_source(&args.sdist, args.subdirectory.as_deref(), &wheel_dir)
|
.build_source(
|
||||||
|
&args.sdist,
|
||||||
|
args.subdirectory.as_deref(),
|
||||||
|
&wheel_dir,
|
||||||
|
// Good enough for the dev command
|
||||||
|
&args.sdist.display().to_string(),
|
||||||
|
)
|
||||||
.await?;
|
.await?;
|
||||||
Ok(wheel_dir.join(wheel))
|
Ok(wheel_dir.join(wheel))
|
||||||
}
|
}
|
||||||
|
|
|
@ -91,9 +91,12 @@ impl BuildContext for BuildDispatch {
|
||||||
&self.client,
|
&self.client,
|
||||||
self,
|
self,
|
||||||
);
|
);
|
||||||
let resolution_graph = resolver.resolve().await.context(
|
let resolution_graph = resolver.resolve().await.with_context(|| {
|
||||||
"No solution found when resolving build dependencies for source distribution:",
|
format!(
|
||||||
)?;
|
"No solution found when resolving build dependencies for source distribution: {}",
|
||||||
|
requirements.iter().map(ToString::to_string).join(", "),
|
||||||
|
)
|
||||||
|
})?;
|
||||||
Ok(resolution_graph.requirements())
|
Ok(resolution_graph.requirements())
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -236,6 +239,7 @@ impl BuildContext for BuildDispatch {
|
||||||
source: &'a Path,
|
source: &'a Path,
|
||||||
subdirectory: Option<&'a Path>,
|
subdirectory: Option<&'a Path>,
|
||||||
wheel_dir: &'a Path,
|
wheel_dir: &'a Path,
|
||||||
|
package_id: &'a str,
|
||||||
) -> Pin<Box<dyn Future<Output = Result<String>> + Send + 'a>> {
|
) -> Pin<Box<dyn Future<Output = Result<String>> + Send + 'a>> {
|
||||||
Box::pin(async move {
|
Box::pin(async move {
|
||||||
if self.no_build {
|
if self.no_build {
|
||||||
|
@ -247,6 +251,7 @@ impl BuildContext for BuildDispatch {
|
||||||
&self.interpreter_info,
|
&self.interpreter_info,
|
||||||
self,
|
self,
|
||||||
self.source_build_context.clone(),
|
self.source_build_context.clone(),
|
||||||
|
package_id,
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
Ok(builder.build(wheel_dir)?)
|
Ok(builder.build(wheel_dir)?)
|
||||||
|
|
|
@ -90,6 +90,7 @@ async fn build_sdist<T: BuildContext + Send + Sync>(
|
||||||
&distribution.sdist_file,
|
&distribution.sdist_file,
|
||||||
distribution.subdirectory.as_deref(),
|
distribution.subdirectory.as_deref(),
|
||||||
&wheel_dir,
|
&wheel_dir,
|
||||||
|
&distribution.remote.to_string(),
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
let wheel_filename = wheel_dir.join(disk_filename);
|
let wheel_filename = wheel_dir.join(disk_filename);
|
||||||
|
|
|
@ -144,7 +144,12 @@ impl<'a, T: BuildContext> SourceDistributionFetcher<'a, T> {
|
||||||
// Build the wheel.
|
// Build the wheel.
|
||||||
let disk_filename = self
|
let disk_filename = self
|
||||||
.build_context
|
.build_context
|
||||||
.build_source(&sdist_file, subdirectory.as_deref(), &wheel_dir)
|
.build_source(
|
||||||
|
&sdist_file,
|
||||||
|
subdirectory.as_deref(),
|
||||||
|
&wheel_dir,
|
||||||
|
&distribution.to_string(),
|
||||||
|
)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
// Read the metadata from the wheel.
|
// Read the metadata from the wheel.
|
||||||
|
|
|
@ -54,6 +54,7 @@ impl BuildContext for DummyContext {
|
||||||
_sdist: &'a Path,
|
_sdist: &'a Path,
|
||||||
_subdirectory: Option<&'a Path>,
|
_subdirectory: Option<&'a Path>,
|
||||||
_wheel_dir: &'a Path,
|
_wheel_dir: &'a Path,
|
||||||
|
_package_id: &'a str,
|
||||||
) -> Pin<Box<dyn Future<Output = Result<String>> + Send + 'a>> {
|
) -> Pin<Box<dyn Future<Output = Result<String>> + Send + 'a>> {
|
||||||
panic!("The test should not need to build source distributions")
|
panic!("The test should not need to build source distributions")
|
||||||
}
|
}
|
||||||
|
|
|
@ -81,10 +81,13 @@ pub trait BuildContext {
|
||||||
/// Build a source distribution into a wheel from an archive.
|
/// Build a source distribution into a wheel from an archive.
|
||||||
///
|
///
|
||||||
/// Returns the filename of the built wheel inside the given `wheel_dir`.
|
/// Returns the filename of the built wheel inside the given `wheel_dir`.
|
||||||
|
///
|
||||||
|
/// `package_id` is for error reporting only.
|
||||||
fn build_source<'a>(
|
fn build_source<'a>(
|
||||||
&'a self,
|
&'a self,
|
||||||
source: &'a Path,
|
source: &'a Path,
|
||||||
subdirectory: Option<&'a Path>,
|
subdirectory: Option<&'a Path>,
|
||||||
wheel_dir: &'a Path,
|
wheel_dir: &'a Path,
|
||||||
|
package_id: &'a str,
|
||||||
) -> Pin<Box<dyn Future<Output = anyhow::Result<String>> + Send + 'a>>;
|
) -> Pin<Box<dyn Future<Output = anyhow::Result<String>> + Send + 'a>>;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue