1use std::path::{Path, PathBuf};
2
3use anyhow::{Result, bail, ensure};
4
5#[derive(Debug, Clone)]
6pub struct Probe {
7 pub prefix: PathBuf,
8 pub include_dirs: IncludeDirs,
9 pub lib_dirs: LibDirs,
10}
11
12#[derive(Debug, Clone)]
13pub struct IncludeDirs {
14 dirs: Vec<PathBuf>,
15}
16
17#[derive(Debug, Clone)]
18pub struct LibDirs {
19 dirs: Vec<PathBuf>,
20}
21
22impl IncludeDirs {
23 pub fn into_vec(self) -> Vec<PathBuf> {
24 self.dirs
25 }
26}
27
28impl LibDirs {
29 pub fn into_vec(self) -> Vec<PathBuf> {
30 self.dirs
31 }
32}
33
34pub fn probe<P>(carla_src_dir: P) -> Result<Probe>
35where
36 P: AsRef<Path>,
37{
38 let carla_version = std::env::var("CARLA_VERSION").unwrap_or_else(|_| "0.10.0".to_string());
39 match carla_version.as_str() {
40 "0.9.14" | "0.9.15" | "0.9.16" => probe_09x(carla_src_dir, &carla_version),
41 "0.10.0" => probe_0_10_0(carla_src_dir),
42 _ => bail!(
43 "Unsupported CARLA version: {}. Supported versions: 0.9.14, 0.9.15, 0.9.16, 0.10.0",
44 carla_version
45 ),
46 }
47}
48
49fn probe_09x<P>(carla_src_dir: P, carla_version: &str) -> Result<Probe>
50where
51 P: AsRef<Path>,
52{
53 let find_match = |pattern: &str| -> Result<_> {
54 let mut iter = glob::glob(pattern)?;
55
56 let path = match iter.next() {
57 Some(Ok(path)) => path,
58 Some(Err(err)) => return Err(err.into()),
59 None => bail!("Unable to match '{pattern}'"),
60 };
61 ensure!(
62 iter.next().is_none(),
63 "'{pattern}' matches more than one file"
64 );
65 Ok(path)
66 };
67
68 let carla_src_dir = carla_src_dir.as_ref();
69 let carla_source_dir = carla_src_dir.join("LibCarla").join("source");
70 let carla_third_party_dir = carla_source_dir.join("third-party");
71 let build_dir = carla_src_dir.join("Build");
72
73 let (boost_pattern, recast_pattern, rpclib_pattern) = match carla_version {
74 "0.9.14" => (
75 "boost-1.80.0-c*-install",
76 "recast-0b13b0-c*-install",
77 "rpclib-v2.2.1_c5-c*-libstdcxx-install",
78 ),
79 "0.9.15" => (
80 "boost-1.80.0-c*-install",
81 "recast-c*-install",
82 "rpclib-v2.2.1_c5-c*-libstdcxx-install",
83 ),
84 "0.9.16" => (
85 "boost-1.84.0-c*-install",
86 "recast-c*-install",
87 "rpclib-v2.2.1_c5-c*-libstdcxx-install",
88 ),
89 _ => unreachable!(),
90 };
91
92 let recast_dir = find_match(build_dir.join(recast_pattern).to_str().unwrap())?;
93 let rpclib_dir = find_match(build_dir.join(rpclib_pattern).to_str().unwrap())?;
94 let boost_dir = find_match(build_dir.join(boost_pattern).to_str().unwrap())?;
95
96 let libpng_dir = build_dir.join("libpng-1.6.37-install");
97 ensure!(
98 libpng_dir.exists(),
99 "Unable to find '{}'",
100 libpng_dir.display()
101 );
102
103 let libcarla_client_lib_dir = build_dir
104 .join("libcarla-client-build.release")
105 .join("LibCarla")
106 .join("cmake")
107 .join("client");
108 ensure!(
109 libcarla_client_lib_dir.exists(),
110 "Unable to find '{}'",
111 libcarla_client_lib_dir.display()
112 );
113
114 let include_dirs = IncludeDirs {
115 dirs: vec![
116 recast_dir.join("include"),
117 rpclib_dir.join("include"),
118 libpng_dir.join("include"),
119 boost_dir.join("include"),
120 carla_source_dir,
121 carla_third_party_dir,
122 ],
123 };
124 let lib_dirs = LibDirs {
125 dirs: vec![
126 recast_dir.join("lib"),
127 rpclib_dir.join("lib"),
128 libpng_dir.join("lib"),
129 boost_dir.join("lib"),
130 libcarla_client_lib_dir,
131 ],
132 };
133
134 Ok(Probe {
135 prefix: carla_src_dir.to_path_buf(),
136 include_dirs,
137 lib_dirs,
138 })
139}
140
141fn probe_0_10_0<P>(carla_src_dir: P) -> Result<Probe>
142where
143 P: AsRef<Path>,
144{
145 let carla_src_dir = carla_src_dir.as_ref();
146 let build_dir = carla_src_dir.join("Build/libstdcxx-release");
147 let deps_dir = build_dir.join("_deps");
148
149 ensure!(
150 build_dir.exists(),
151 "Build directory not found: '{}'. Run the 0.10.0 CMake build first.",
152 build_dir.display()
153 );
154
155 let carla_source_dir = carla_src_dir.join("LibCarla/source");
156 let carla_third_party_dir = carla_source_dir.join("third-party");
157
158 let libcarla_client = build_dir.join("LibCarla/libcarla-client.a");
160 ensure!(
161 libcarla_client.exists(),
162 "libcarla-client.a not found at '{}'",
163 libcarla_client.display()
164 );
165
166 let mut include_dirs = vec![carla_source_dir, carla_third_party_dir];
168
169 let boost_include_pattern = deps_dir
171 .join("boost-src/libs/*/include")
172 .to_str()
173 .unwrap()
174 .to_string();
175 for entry in glob::glob(&boost_include_pattern)? {
176 include_dirs.push(entry?);
177 }
178
179 for component in &["Recast", "Detour", "DetourCrowd"] {
181 let dir = deps_dir.join(format!("recastnavigation-src/{component}/Include"));
182 ensure!(
183 dir.exists(),
184 "Recast include dir not found: '{}'",
185 dir.display()
186 );
187 include_dirs.push(dir);
188 }
189
190 let rpclib_include = deps_dir.join("rpclib-src/include");
192 ensure!(
193 rpclib_include.exists(),
194 "rpclib include dir not found: '{}'",
195 rpclib_include.display()
196 );
197 include_dirs.push(rpclib_include);
198
199 let libpng_src = deps_dir.join("libpng-src");
201 ensure!(
202 libpng_src.join("png.h").exists(),
203 "png.h not found in '{}'",
204 libpng_src.display()
205 );
206 include_dirs.push(libpng_src);
207
208 let libpng_build = deps_dir.join("libpng-build");
209 if libpng_build.join("pnglibconf.h").exists() {
210 include_dirs.push(libpng_build.clone());
211 }
212
213 let lib_dirs = LibDirs {
215 dirs: vec![
216 build_dir.join("LibCarla"),
217 deps_dir.join("boost-build/libs/filesystem"),
218 deps_dir.join("recastnavigation-build/Recast"),
219 deps_dir.join("recastnavigation-build/Detour"),
220 deps_dir.join("recastnavigation-build/DetourCrowd"),
221 deps_dir.join("rpclib-build"),
222 libpng_build,
223 deps_dir.join("zlib-build"),
224 ],
225 };
226
227 Ok(Probe {
228 prefix: carla_src_dir.to_path_buf(),
229 include_dirs: IncludeDirs { dirs: include_dirs },
230 lib_dirs,
231 })
232}