ultralight_build/
lib.rs

1use std::{
2    fs::create_dir_all,
3    path::{Path, PathBuf},
4};
5
6pub mod download;
7pub use download::*;
8mod validate;
9use ultralight_errors::{UltralightError, UltralightResult};
10use validate::*;
11mod utils;
12use utils::copy_dir_all;
13
14pub struct UltralightBuild {
15    version: Option<String>,
16    platform: Option<Platform>,
17    download_headers: bool,
18    download_resources: bool,
19    download_binaries: bool,
20    download_libs: bool,
21    headers_out_dir: Option<PathBuf>,
22    resources_out_dir: Option<PathBuf>,
23    binaries_out_dir: Option<PathBuf>,
24    libs_out_dir: Option<PathBuf>,
25}
26
27impl UltralightBuild {
28    pub fn new() -> Self {
29        Self {
30            version: None,
31            platform: None,
32            download_headers: false,
33            download_resources: false,
34            download_binaries: false,
35            download_libs: false,
36            headers_out_dir: None,
37            resources_out_dir: None,
38            binaries_out_dir: None,
39            libs_out_dir: None,
40        }
41    }
42
43    pub fn with_version(mut self, version: &str) -> Self {
44        self.version = Some(version.to_string());
45        self
46    }
47
48    pub fn with_platform(mut self, platform: Platform) -> Self {
49        self.platform = Some(platform);
50        self
51    }
52
53    pub fn download_headers(mut self) -> Self {
54        self.download_headers = true;
55        self
56    }
57
58    pub fn download_resources(mut self) -> Self {
59        self.download_resources = true;
60        self
61    }
62
63    pub fn download_binaries(mut self) -> Self {
64        self.download_binaries = true;
65        self
66    }
67
68    pub fn download_libs(mut self) -> Self {
69        self.download_libs = true;
70        self
71    }
72
73    pub fn with_headers_out_dir<P: AsRef<Path>>(mut self, out_dir: P) -> Self {
74        self.headers_out_dir = Some(out_dir.as_ref().to_path_buf());
75        self
76    }
77
78    pub fn with_resources_out_dir<P: AsRef<Path>>(mut self, out_dir: P) -> Self {
79        self.resources_out_dir = Some(out_dir.as_ref().to_path_buf());
80        self
81    }
82
83    pub fn with_binaries_out_dir<P: AsRef<Path>>(mut self, out_dir: P) -> Self {
84        self.binaries_out_dir = Some(out_dir.as_ref().to_path_buf());
85        self
86    }
87
88    pub fn with_libs_out_dir<P: AsRef<Path>>(mut self, out_dir: P) -> Self {
89        self.libs_out_dir = Some(out_dir.as_ref().to_path_buf());
90        self
91    }
92
93    pub fn build(&self) -> UltralightResult<()> {
94        let platform = self.platform.unwrap_or_else(|| {
95            if cfg!(target_os = "windows") {
96                Platform::Windows
97            } else if cfg!(target_os = "linux") {
98                Platform::Linux
99            } else if cfg!(target_os = "macos") {
100                Platform::MacOS
101            } else {
102                panic!("Unsupported platform!");
103            }
104        });
105
106        let version = self.version.as_deref().unwrap_or("latest");
107        let out_dir: PathBuf = std::env::var("OUT_DIR")?.into();
108
109        if self.need_download_any(platform)? {
110            let dl_dir = out_dir.join("ultralight-download");
111            create_dir_all(dl_dir.clone()).map_err(|e| UltralightError::IoError(e))?;
112            DownloadBuilder::new()
113                .with_platform(platform)
114                .with_version(version)
115                .with_out_dir(dl_dir.clone())
116                .start()?;
117
118            self.handle_new_resources(dl_dir, platform)?;
119        }
120
121        println!("cargo:rustc-link-search=native={}", out_dir.display());
122        println!("cargo:rustc-link-lib=Ultralight");
123        println!("cargo:rustc-link-lib=WebCore");
124        println!("cargo:rustc-link-lib=AppCore");
125
126        Ok(())
127    }
128
129    fn need_download_headers(&self) -> UltralightResult<bool> {
130        if self.download_headers {
131            let headers_out_dir = if let Some(headers_out_dir) = &self.headers_out_dir {
132                headers_out_dir.clone()
133            } else {
134                let out: PathBuf = std::env::var("OUT_DIR")?.into();
135                out.join("headers")
136            };
137
138            if !validate_directory_contents(
139                &headers_out_dir,
140                &[
141                    "./AppCore/CAPI.h",
142                    "./Ultralight/CAPI.h",
143                    "./Ultralight/CAPI/CAPI_Defines.h",
144                    "./Ultralight/CAPI/CAPI_Bitmap.h",
145                    "./Ultralight/CAPI/CAPI_Buffer.h",
146                    "./Ultralight/CAPI/CAPI_Clipboard.h",
147                    "./Ultralight/CAPI/CAPI_Config.h",
148                    "./Ultralight/CAPI/CAPI_FileSystem.h",
149                    "./Ultralight/CAPI/CAPI_FontFile.h",
150                    "./Ultralight/CAPI/CAPI_FontLoader.h",
151                    "./Ultralight/CAPI/CAPI_FontLoader.h",
152                    "./Ultralight/CAPI/CAPI_Geometry.h",
153                    "./Ultralight/CAPI/CAPI_Geometry.h",
154                    "./Ultralight/CAPI/CAPI_GPUDriver.h",
155                    "./Ultralight/CAPI/CAPI_KeyEvent.h",
156                    "./Ultralight/CAPI/CAPI_Logger.h",
157                    "./Ultralight/CAPI/CAPI_MouseEvent.h",
158                    "./Ultralight/CAPI/CAPI_Platform.h",
159                    "./Ultralight/CAPI/CAPI_Renderer.h",
160                    "./Ultralight/CAPI/CAPI_ScrollEvent.h",
161                    "./Ultralight/CAPI/CAPI_GamepadEvent.h",
162                    "./Ultralight/CAPI/CAPI_Session.h",
163                    "./Ultralight/CAPI/CAPI_String.h",
164                    "./Ultralight/CAPI/CAPI_Surface.h",
165                    "./Ultralight/CAPI/CAPI_View.h",
166                ],
167            ) {
168                return Ok(true);
169            }
170        }
171
172        Ok(false)
173    }
174
175    fn need_download_resources(&self) -> UltralightResult<bool> {
176        if self.download_resources {
177            let resources_out_dir = if let Some(resources_out_dir) = &self.resources_out_dir {
178                resources_out_dir.clone()
179            } else {
180                let out: PathBuf = std::env::var("OUT_DIR")?.into();
181                out.join("resources")
182            };
183
184            if !validate_directory_contents(&resources_out_dir, &["cacert.pem", "icudt67l.dat"]) {
185                return Ok(true);
186            }
187        }
188
189        Ok(false)
190    }
191
192    fn need_download_binaries(&self, platform: Platform) -> UltralightResult<bool> {
193        if self.download_binaries {
194            let binaries_out_dir = if let Some(binaries_out_dir) = &self.binaries_out_dir {
195                binaries_out_dir.clone()
196            } else {
197                let out: PathBuf = std::env::var("OUT_DIR")?.into();
198                out.join("binaries")
199            };
200
201            match platform {
202                Platform::Windows => {
203                    if !validate_directory_contents(
204                        &binaries_out_dir,
205                        &[
206                            "Ultralight.dll",
207                            "UltralightCore.dll",
208                            "AppCore.dll",
209                            "WebCore.dll",
210                        ],
211                    ) {
212                        return Ok(true);
213                    }
214                }
215                Platform::Linux => {
216                    if !validate_directory_contents(
217                        &binaries_out_dir,
218                        &[
219                            "libUltralight.so",
220                            "libUltralightCore.so",
221                            "libAppCore.so",
222                            "libWebCore.so",
223                        ],
224                    ) {
225                        return Ok(true);
226                    }
227                }
228                Platform::MacOS => {
229                    if !validate_directory_contents(
230                        &binaries_out_dir,
231                        &[
232                            "libUltralight.dylib",
233                            "libUltralightCore.dylib",
234                            "libAppCore.dylib",
235                            "libWebCore.dylib",
236                        ],
237                    ) {
238                        return Ok(true);
239                    }
240                }
241            }
242        }
243
244        Ok(false)
245    }
246
247    fn need_download_libs(&self, platform: Platform) -> UltralightResult<bool> {
248        if self.download_libs {
249            let libs_out_dir = if let Some(libs_out_dir) = &self.libs_out_dir {
250                libs_out_dir.clone()
251            } else {
252                let out: PathBuf = std::env::var("OUT_DIR")?.into();
253                out.join("libs")
254            };
255
256            if platform == Platform::Windows {
257                if !validate_directory_contents(
258                    &libs_out_dir,
259                    &[
260                        "Ultralight.lib",
261                        "UltralightCore.lib",
262                        "AppCore.lib",
263                        "WebCore.lib",
264                    ],
265                ) {
266                    return Ok(true);
267                }
268            }
269        }
270
271        Ok(false)
272    }
273
274    fn need_download_any(&self, platform: Platform) -> UltralightResult<bool> {
275        Ok(self.need_download_headers()?
276            || self.need_download_resources()?
277            || self.need_download_binaries(platform)?
278            || self.need_download_libs(platform)?)
279    }
280
281    fn handle_new_resources(&self, dl_dir: PathBuf, platform: Platform) -> UltralightResult<()> {
282        if self.download_headers {
283            self.fetch_headers(dl_dir.clone())?;
284        }
285
286        if self.download_resources {
287            self.fetch_resources(dl_dir.clone())?;
288        }
289
290        if self.download_binaries {
291            self.fetch_binaries(dl_dir.clone())?;
292        }
293
294        if self.download_libs {
295            self.fetch_libs(dl_dir.clone(), platform)?;
296        }
297
298        Ok(())
299    }
300
301    fn fetch_headers(&self, dl_dir: PathBuf) -> UltralightResult<()> {
302        let headers_out_dir = if let Some(headers_out_dir) = &self.headers_out_dir {
303            headers_out_dir.clone()
304        } else {
305            let out: PathBuf = std::env::var("OUT_DIR")?.into();
306            out.join("headers")
307        };
308
309        copy_dir_all(dl_dir.join("include"), headers_out_dir)?;
310
311        Ok(())
312    }
313
314    fn fetch_resources(&self, dl_dir: PathBuf) -> UltralightResult<()> {
315        let resources_out_dir = if let Some(resources_out_dir) = &self.resources_out_dir {
316            resources_out_dir.clone()
317        } else {
318            let out: PathBuf = std::env::var("OUT_DIR")?.into();
319            out.join("resources")
320        };
321
322        copy_dir_all(dl_dir.join("resources"), resources_out_dir)?;
323
324        Ok(())
325    }
326
327    fn fetch_binaries(&self, dl_dir: PathBuf) -> UltralightResult<()> {
328        let binaries_out_dir = if let Some(binaries_out_dir) = &self.binaries_out_dir {
329            binaries_out_dir.clone()
330        } else {
331            let out: PathBuf = std::env::var("OUT_DIR")?.into();
332            out.join("binaries")
333        };
334
335        copy_dir_all(dl_dir.join("bin"), binaries_out_dir)?;
336
337        Ok(())
338    }
339
340    fn fetch_libs(&self, dl_dir: PathBuf, platform: Platform) -> UltralightResult<()> {
341        if platform != Platform::Windows {
342            return Ok(());
343        }
344
345        let libs_out_dir = if let Some(libs_out_dir) = &self.libs_out_dir {
346            libs_out_dir.clone()
347        } else {
348            let out: PathBuf = std::env::var("OUT_DIR")?.into();
349            out.join("libs")
350        };
351
352        copy_dir_all(dl_dir.join("lib"), libs_out_dir)?;
353
354        Ok(())
355    }
356}