1use pkg_config::{Config, Error as PkgConfigError};
25use shlex::Shlex;
26use std::io::Error as IoError;
27use std::path::{Path, PathBuf};
28use std::process::Command;
29use std::str::FromStr;
30use thiserror::Error;
31
32pub struct Folly {
45 pub lib_dirs: Vec<PathBuf>,
46 pub include_paths: Vec<PathBuf>,
47 pub other_cflags: Vec<String>,
48 _priv: (),
49}
50
51#[derive(Error, Debug)]
52pub enum FollyError {
53 #[error("`fmt` dependency couldn't be located")]
54 FmtDependency(PkgConfigError),
55 #[error("`gflags` dependency couldn't be located")]
56 GflagsDependency(PkgConfigError),
57 #[error("main `folly` package couldn't be located")]
58 MainPackage(IoError),
59 #[error("could not find `boost_context`; make sure either `libboost_context.a` or \
60 `libboost_context-mt.a` is located in the same directory as Folly")]
61 BoostContext,
62}
63
64pub fn probe_folly() -> Result<Folly, FollyError> {
65 Config::new()
67 .statik(true)
68 .probe("fmt")
69 .map_err(FollyError::FmtDependency)?;
70 Config::new()
71 .statik(true)
72 .probe("gflags")
73 .map_err(FollyError::GflagsDependency)?;
74
75 let mut folly = Folly::new();
79 let output = Command::new("pkg-config")
80 .args(&["--static", "--libs", "libfolly"])
81 .output()
82 .map_err(FollyError::MainPackage)?;
83 let output = String::from_utf8(output.stdout).expect("`pkg-config --libs` wasn't UTF-8!");
84 for arg in Shlex::new(&output) {
85 if arg.starts_with('-') {
86 if let Some(rest) = arg.strip_prefix("-L") {
87 folly.lib_dirs.push(PathBuf::from(rest));
88 } else if let Some(rest) = arg.strip_prefix("-l") {
89 println!("cargo:rustc-link-lib={}", rest);
90 }
91 continue;
92 }
93
94 let path = PathBuf::from_str(&arg).unwrap();
95 let (parent, lib_name) = match (path.parent(), path.file_stem()) {
96 (Some(parent), Some(lib_name)) => (parent, lib_name),
97 _ => continue,
98 };
99 let lib_name = lib_name.to_string_lossy();
100 if let Some(rest) = lib_name.strip_prefix("lib") {
101 println!("cargo:rustc-link-search={}", parent.display());
102 println!("cargo:rustc-link-lib={}", rest);
103 }
104 }
105
106 let mut found_boost_context = false;
111 for lib_dir in &folly.lib_dirs {
112 println!("cargo:rustc-link-search={}", lib_dir.display());
113
114 if found_boost_context {
115 continue;
116 }
117 for possible_lib_name in &["boost_context", "boost_context-mt"] {
118 let mut lib_dir = (*lib_dir).clone();
119 lib_dir.push(&format!("lib{}.a", possible_lib_name));
120 if !lib_dir.exists() {
121 continue;
122 }
123 println!("cargo:rustc-link-lib={}", possible_lib_name);
124 found_boost_context = true;
125 break;
126 }
127 }
128 if !found_boost_context {
129 return Err(FollyError::BoostContext);
130 }
131
132 let output = Command::new("pkg-config")
133 .args(&["--static", "--cflags", "libfolly"])
134 .output()
135 .map_err(FollyError::MainPackage)?;
136 let output = String::from_utf8(output.stdout).expect("`pkg-config --libs` wasn't UTF-8!");
137
138 for arg in output.split_whitespace() {
139 if let Some(rest) = arg.strip_prefix("-I") {
140 let path = Path::new(rest);
141 if path.starts_with("/Library/Developer/CommandLineTools/SDKs")
142 && path.ends_with("usr/include")
143 {
144 let sysroot = path.parent().unwrap().parent().unwrap();
150 folly.other_cflags.push("-isysroot".to_owned());
151 folly.other_cflags.push(sysroot.to_string_lossy().into_owned());
152 } else {
153 folly.include_paths.push(path.to_owned());
154 }
155 }
156 }
157
158 Ok(folly)
159}
160
161impl Folly {
162 fn new() -> Self {
163 Self {
164 lib_dirs: vec![],
165 include_paths: vec![],
166 other_cflags: vec![],
167 _priv: (),
168 }
169 }
170}