Skip to content

Commit d5be171

Browse files
committed
Improve error message when library not found
Mostly just small tweaks to existing output, including the following new output: - Potential packages to install - Items in PKG_CONFIG_PATH - Command invocation
1 parent 576f550 commit d5be171

File tree

1 file changed

+91
-5
lines changed

1 file changed

+91
-5
lines changed

src/lib.rs

Lines changed: 91 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -208,12 +208,72 @@ impl fmt::Display for Error {
208208
ref command,
209209
ref output,
210210
} => {
211-
write!(
211+
let crate_name =
212+
env::var("CARGO_PKG_NAME").unwrap_or(String::from("<NO CRATE NAME>"));
213+
214+
writeln!(f, "")?;
215+
216+
// Give a short explanation of what the error is
217+
writeln!(
212218
f,
213-
"`{}` did not exit successfully: {}\nerror: could not find system library '{}' required by the '{}' crate\n",
214-
command, output.status, name, env::var("CARGO_PKG_NAME").unwrap_or_default(),
219+
"pkg-config {}",
220+
match output.status.code() {
221+
Some(code) => format!("exited with status code {}", code),
222+
None => "was terminated by signal".to_string(),
223+
}
215224
)?;
216-
format_output(output, f)
225+
226+
// Give the command run so users can reproduce the error
227+
writeln!(f, "> {}\n", command)?;
228+
229+
// Explain how it was caused
230+
writeln!(
231+
f,
232+
"The system library `{}` required by crate `{}` was not found.",
233+
name, crate_name
234+
)?;
235+
writeln!(
236+
f,
237+
"The file `{}.pc` needs to be installed and the PKG_CONFIG_PATH environment variable must contain its parent directory.",
238+
name
239+
)?;
240+
241+
// There will be no status code if terminated by signal
242+
if let Some(_code) = output.status.code() {
243+
// NixOS uses a wrapper script for pkg-config that sets the custom
244+
// environment variable PKG_CONFIG_PATH_FOR_TARGET
245+
let search_path =
246+
if let Ok(path_for_target) = env::var("PKG_CONFIG_PATH_FOR_TARGET") {
247+
Some(path_for_target)
248+
} else if let Ok(path) = env::var("PKG_CONFIG_PATH") {
249+
Some(path)
250+
} else {
251+
None
252+
};
253+
254+
// Guess the most reasonable course of action
255+
let hint = if let Some(search_path) = search_path {
256+
writeln!(
257+
f,
258+
"PKG_CONFIG_PATH contains the following:\n{}",
259+
search_path
260+
.split(':')
261+
.map(|path| format!(" - {}", path))
262+
.collect::<Vec<String>>()
263+
.join("\n"),
264+
)?;
265+
266+
format!("you may need to install a package such as {name}, {name}-dev or {name}-devel.", name=name)
267+
} else {
268+
writeln!(f, "PKG_CONFIG_PATH environment variable is not set")?;
269+
format!("If you have installed the library, try adding its parent directory to your PATH.")
270+
};
271+
272+
// Try and nudge the user in the right direction so they don't get stuck
273+
writeln!(f, "\nHINT: {}", hint)?;
274+
}
275+
276+
Ok(())
217277
}
218278
Error::Failure {
219279
ref command,
@@ -498,8 +558,34 @@ impl Config {
498558
if output.status.success() {
499559
Ok(output.stdout)
500560
} else {
561+
// Collect all explicitly-defined environment variables
562+
// this is used to display the equivalent pkg-config shell invocation
563+
let envs = cmd
564+
.get_envs()
565+
.map(|(env, optional_arg)| {
566+
if let Some(arg) = optional_arg {
567+
format!("{}={}", env.to_string_lossy(), arg.to_string_lossy())
568+
} else {
569+
env.to_string_lossy().to_string()
570+
}
571+
})
572+
.collect::<Vec<String>>();
573+
574+
// Collect arguments for the same reason
575+
let args = cmd
576+
.get_args()
577+
.map(|arg| arg.to_string_lossy().to_string())
578+
.collect::<Vec<String>>();
579+
580+
// This will look something like:
581+
// PKG_CONFIG_ALLOW_SYSTEM_CFLAGS=1 PKG_CONFIG_ALLOW_SYSTEM_LIBS=1 pkg-config --libs --cflags {library}
501582
Err(Error::Failure {
502-
command: format!("{:?}", cmd),
583+
command: format!(
584+
"{} {} {}",
585+
envs.join(" "),
586+
cmd.get_program().to_string_lossy(),
587+
args.join(" ")
588+
),
503589
output,
504590
})
505591
}

0 commit comments

Comments
 (0)