|
1 | 1 | use std::collections::{HashMap, HashSet}; |
2 | 2 | use std::path::{Path, PathBuf}; |
3 | 3 | use std::process::Command; |
| 4 | +use std::time::SystemTime; |
4 | 5 |
|
5 | 6 | use color_eyre::{ |
6 | 7 | Section, |
@@ -235,6 +236,82 @@ fn copy_dir(src: impl AsRef<Path>, dst: impl AsRef<Path>) -> Result<()> { |
235 | 236 | Ok(()) |
236 | 237 | } |
237 | 238 |
|
| 239 | +fn file_with_extension_exists(dir: &Path, extension: &str) -> bool { |
| 240 | + if let Ok(entries) = fs::read_dir(dir) { |
| 241 | + for entry in entries.filter_map(Result::ok) { |
| 242 | + let path = entry.path(); |
| 243 | + if path.is_file() && path.extension().and_then(|ext| ext.to_str()) == Some(extension) { |
| 244 | + return true; |
| 245 | + } |
| 246 | + } |
| 247 | + } |
| 248 | + false |
| 249 | +} |
| 250 | + |
| 251 | +#[instrument(level = "trace", skip_all)] |
| 252 | +fn get_most_recent_modified_time( |
| 253 | + dir: &Path, |
| 254 | + exclude_files: &HashSet<&str>, |
| 255 | + exclude_extensions: &HashSet<&str>, |
| 256 | + exclude_dirs: &HashSet<&str>, |
| 257 | +) -> Result<(Option<SystemTime>, Option<SystemTime>)> { |
| 258 | + let mut most_recent: Option<SystemTime> = None; |
| 259 | + let mut most_recent_excluded: Option<SystemTime> = None; |
| 260 | + |
| 261 | + for entry in fs::read_dir(dir)? { |
| 262 | + let entry = entry?; |
| 263 | + let path = entry.path(); |
| 264 | + |
| 265 | + let file_name = path.file_name().unwrap_or_default().to_str().unwrap_or_default(); |
| 266 | + |
| 267 | + if exclude_files.contains(file_name) { |
| 268 | + let file_time = get_file_modified_time(&path)?; |
| 269 | + most_recent_excluded = Some(most_recent_excluded.map_or(file_time, |t| t.max(file_time))); |
| 270 | + continue; |
| 271 | + } |
| 272 | + |
| 273 | + if path.is_dir() { |
| 274 | + let dir_name = path.file_name().unwrap_or_default().to_str().unwrap_or_default(); |
| 275 | + if exclude_dirs.contains(dir_name) { |
| 276 | + continue; |
| 277 | + } |
| 278 | + |
| 279 | + let (sub_time, sub_time_excluded) = get_most_recent_modified_time( |
| 280 | + &path, |
| 281 | + exclude_files, |
| 282 | + exclude_extensions, |
| 283 | + exclude_dirs, |
| 284 | + )?; |
| 285 | + |
| 286 | + if let Some(st) = sub_time { |
| 287 | + most_recent = Some(most_recent.map_or(st, |t| t.max(st))); |
| 288 | + } |
| 289 | + if let Some(ste) = sub_time_excluded { |
| 290 | + most_recent_excluded = Some(most_recent_excluded.map_or(ste, |t| t.max(ste))); |
| 291 | + } |
| 292 | + } else { |
| 293 | + if let Some(extension) = path.extension() { |
| 294 | + if exclude_extensions.contains(&extension.to_str().unwrap_or_default()) { |
| 295 | + let file_time = get_file_modified_time(&path)?; |
| 296 | + most_recent_excluded = Some(most_recent_excluded.map_or(file_time, |t| t.max(file_time))); |
| 297 | + continue; |
| 298 | + } |
| 299 | + } |
| 300 | + |
| 301 | + let file_time = get_file_modified_time(&path)?; |
| 302 | + most_recent = Some(most_recent.map_or(file_time, |t| t.max(file_time))); |
| 303 | + } |
| 304 | + } |
| 305 | + |
| 306 | + Ok((most_recent, most_recent_excluded)) |
| 307 | +} |
| 308 | + |
| 309 | +#[instrument(level = "trace", skip_all)] |
| 310 | +fn get_file_modified_time(file_path: &Path) -> Result<SystemTime> { |
| 311 | + let metadata = fs::metadata(file_path)?; |
| 312 | + Ok(metadata.modified()?) |
| 313 | +} |
| 314 | + |
238 | 315 | #[instrument(level = "trace", skip_all)] |
239 | 316 | async fn compile_javascript_wasm_process( |
240 | 317 | process_dir: &Path, |
@@ -908,6 +985,33 @@ pub async fn execute( |
908 | 985 | ) |
909 | 986 | .with_suggestion(|| "Please re-run targeting a package.")); |
910 | 987 | } |
| 988 | + let build_with_features_path = package_dir.join("target").join("build_with_features.txt"); |
| 989 | + let old_features = fs::read_to_string(&build_with_features_path).ok(); |
| 990 | + if old_features == Some(features.to_string()) |
| 991 | + && package_dir.join("Cargo.lock").exists() |
| 992 | + && package_dir.join("pkg").exists() |
| 993 | + && package_dir.join("pkg").join("api.zip").exists() |
| 994 | + && file_with_extension_exists(&package_dir.join("pkg"), "wasm") |
| 995 | + { |
| 996 | + let (source_time, build_time) = get_most_recent_modified_time( |
| 997 | + package_dir, |
| 998 | + &HashSet::from(["Cargo.lock", "api.zip"]), |
| 999 | + &HashSet::from(["wasm"]), |
| 1000 | + &HashSet::from(["target"]), |
| 1001 | + )?; |
| 1002 | + if let Some(source_time) = source_time { |
| 1003 | + if let Some(build_time) = build_time { |
| 1004 | + if build_time.duration_since(source_time).is_ok() { |
| 1005 | + // build_time - source_time >= 0 |
| 1006 | + // -> current build is up-to-date: don't rebuild |
| 1007 | + info!("Build up-to-date."); |
| 1008 | + return Ok(()); |
| 1009 | + } |
| 1010 | + } |
| 1011 | + } |
| 1012 | + } |
| 1013 | + fs::create_dir_all(package_dir.join("target"))?; |
| 1014 | + fs::write(&build_with_features_path, features)?; |
911 | 1015 |
|
912 | 1016 | let ui_dir = package_dir.join("ui"); |
913 | 1017 | if !ui_dir.exists() { |
|
0 commit comments