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}