Skip to main content

carla_src/
probe.rs

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    // Verify key artifacts exist
159    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    // Collect include dirs
167    let mut include_dirs = vec![carla_source_dir, carla_third_party_dir];
168
169    // Boost: headers are scattered across libs/*/include
170    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    // Recast navigation includes
180    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    // rpclib includes
191    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    // libpng includes (png.h at top level of source, pnglibconf.h in build dir)
200    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    // Collect lib dirs
214    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}