1use anyhow::{anyhow, Result};
2use std::collections::HashMap;
3use std::path::PathBuf;
4use std::process::Command;
5use std::sync::OnceLock;
6
7use crate::pkg_config::PkgConfigFile;
8
9fn get_multiarch_lib_path_iter() -> &'static [PathBuf] {
10 static MULTIARCH_PATH: OnceLock<Vec<PathBuf>> = OnceLock::new();
11 MULTIARCH_PATH.get_or_init(|| {
12 Command::new("gcc")
13 .arg("-dumpmachine")
14 .output()
15 .map(|o| String::from_utf8(o.stdout).unwrap_or_default())
16 .map_or(vec![], |arch| {
17 vec![PathBuf::from(format!("/usr/lib/{}", arch.trim()))]
18 })
19 })
20}
21
22pub fn find_library(library: &str, extension: &str, search_paths: &[PathBuf]) -> Result<String> {
23 let filepaths: Vec<_> = search_paths
24 .iter()
25 .chain(get_multiarch_lib_path_iter())
26 .map(|base| base.join(format!("lib{}.{}", library, extension)))
27 .collect();
28
29 let error = anyhow!(
30 "Could not find required library `{}` at paths: `{:?}`",
31 library,
32 &filepaths
33 );
34 Ok(filepaths
35 .into_iter()
36 .find(|path| path.exists())
37 .ok_or(error)?
38 .into_os_string()
39 .into_string()
40 .unwrap())
41}
42
43#[derive(Debug)]
44pub enum LibraryLocation {
45 Archive(String),
46 Dylib(String),
47 Both { archive: String, dylib: String },
48}
49
50impl LibraryLocation {
51 pub fn find(library: &str, search_paths: &[PathBuf]) -> Result<Self> {
52 let dylib = find_library(library, "so", search_paths);
53 let archive = find_library(library, "a", search_paths);
54
55 match (dylib, archive) {
56 (Ok(dylib), Err(_)) => Ok(Self::Dylib(dylib)),
57 (Err(_), Ok(archive)) => Ok(Self::Archive(archive)),
58 (Ok(dylib), Ok(archive)) => Ok(Self::Both { archive, dylib }),
59 (Err(dylib_error), Err(archive_error)) => {
60 Err(anyhow!("{}\n{}", dylib_error, archive_error))
61 }
62 }
63 }
64}
65
66pub fn find_locations(pkg_config: &PkgConfigFile) -> Result<HashMap<String, LibraryLocation>> {
67 let search_paths = pkg_config
68 .link_locations
69 .iter()
70 .map(PathBuf::from)
71 .collect::<Vec<_>>();
72
73 Ok(pkg_config
74 .link_libraries
75 .iter()
76 .map(|name| -> Result<(String, LibraryLocation)> {
77 let location = LibraryLocation::find(name, &search_paths)?;
78 Ok((name.clone(), location))
79 })
80 .collect::<Result<Vec<_>>>()?
81 .into_iter()
82 .collect())
83}