xref: /DADK/dadk-user/src/executor/mod.rs (revision e0934047cd911bf12b893ec79fa0f5c8ee6402b9)
1 use std::{
2     collections::{BTreeMap, VecDeque},
3     env::Vars,
4     path::PathBuf,
5     process::{Command, Stdio},
6     sync::{Arc, RwLock},
7     time::SystemTime,
8 };
9 
10 use chrono::{DateTime, Utc};
11 use dadk_config::user::UserCleanLevel;
12 use log::{debug, error, info, warn};
13 
14 use crate::{
15     context::{Action, DadkUserExecuteContext},
16     executor::cache::CacheDir,
17     parser::{
18         task::{CodeSource, PrebuiltSource, TaskType},
19         task_log::{BuildStatus, InstallStatus, TaskLog},
20     },
21     scheduler::{SchedEntities, SchedEntity},
22     utils::{file::FileUtils, path::abs_path},
23 };
24 
25 use dadk_config::common::task::TaskEnv;
26 
27 use self::cache::{CacheDirType, TaskDataDir};
28 
29 pub mod cache;
30 pub mod source;
31 #[cfg(test)]
32 mod tests;
33 
34 lazy_static! {
35     // 全局环境变量的列表
36     pub static ref ENV_LIST: RwLock<EnvMap> = RwLock::new(EnvMap::new());
37 }
38 
39 #[derive(Debug, Clone)]
40 pub struct Executor {
41     entity: Arc<SchedEntity>,
42     action: Action,
43     local_envs: EnvMap,
44     /// 任务构建结果输出到的目录
45     build_dir: CacheDir,
46     /// 如果任务需要源文件缓存,则此字段为 Some(CacheDir),否则为 None(使用本地源文件路径)
47     source_dir: Option<CacheDir>,
48     /// 任务数据目录
49     task_data_dir: TaskDataDir,
50     /// DragonOS sysroot的路径
51     dragonos_sysroot: PathBuf,
52 }
53 
54 impl Executor {
55     /// # 创建执行器
56     ///
57     /// 用于执行一个任务
58     ///
59     /// ## 参数
60     ///
61     /// * `entity` - 任务调度实体
62     ///
63     /// ## 返回值
64     ///
65     /// * `Ok(Executor)` - 创建成功
66     /// * `Err(ExecutorError)` - 创建失败
new( entity: Arc<SchedEntity>, action: Action, dragonos_sysroot: PathBuf, ) -> Result<Self, ExecutorError>67     pub fn new(
68         entity: Arc<SchedEntity>,
69         action: Action,
70         dragonos_sysroot: PathBuf,
71     ) -> Result<Self, ExecutorError> {
72         let local_envs = EnvMap::new();
73         let build_dir = CacheDir::new(entity.clone(), CacheDirType::Build)?;
74         let task_data_dir = TaskDataDir::new(entity.clone())?;
75 
76         let source_dir = if CacheDir::need_source_cache(&entity) {
77             Some(CacheDir::new(entity.clone(), CacheDirType::Source)?)
78         } else {
79             None
80         };
81 
82         let result: Executor = Self {
83             action,
84             entity,
85             local_envs,
86             build_dir,
87             source_dir,
88             task_data_dir,
89             dragonos_sysroot,
90         };
91 
92         return Ok(result);
93     }
94 
95     /// # 执行任务
96     ///
97     /// 创建执行器后,调用此方法执行任务。
98     /// 该方法会执行以下步骤:
99     ///
100     /// 1. 创建工作线程
101     /// 2. 准备环境变量
102     /// 3. 拉取数据(可选)
103     /// 4. 执行构建
execute(&mut self) -> Result<(), ExecutorError>104     pub fn execute(&mut self) -> Result<(), ExecutorError> {
105         info!("Execute task: {}", self.entity.task().name_version());
106 
107         let r = self.do_execute();
108         self.save_task_data(r.clone());
109         info!("Task {} finished", self.entity.task().name_version());
110         return r;
111     }
112 
113     /// # 保存任务数据
save_task_data(&self, r: Result<(), ExecutorError>)114     fn save_task_data(&self, r: Result<(), ExecutorError>) {
115         let mut task_log = self.task_data_dir.task_log();
116         match self.action {
117             Action::Build => {
118                 if r.is_ok() {
119                     task_log.set_build_status(BuildStatus::Success);
120                 } else {
121                     task_log.set_build_status(BuildStatus::Failed);
122                 }
123 
124                 task_log.set_build_time_now();
125             }
126 
127             Action::Install => {
128                 if r.is_ok() {
129                     task_log.set_install_status(InstallStatus::Success);
130                 } else {
131                     task_log.set_install_status(InstallStatus::Failed);
132                 }
133                 task_log.set_install_time_now();
134             }
135 
136             Action::Clean(_) => {
137                 task_log.clean_build_status();
138                 task_log.clean_install_status();
139             }
140         }
141 
142         self.task_data_dir
143             .save_task_log(&task_log)
144             .expect("Failed to save task log");
145     }
146 
do_execute(&mut self) -> Result<(), ExecutorError>147     fn do_execute(&mut self) -> Result<(), ExecutorError> {
148         // 准备本地环境变量
149         self.prepare_local_env()?;
150 
151         match self.action {
152             Action::Build => {
153                 // 构建前的工作
154                 self.pre_build()?;
155                 // 构建任务
156                 self.build()?;
157                 // 构建完毕后的工作
158                 self.post_build()?;
159             }
160             Action::Install => {
161                 // 把构建结果安装到DragonOS
162                 self.install()?;
163             }
164             Action::Clean(_) => {
165                 // 清理构建结果
166                 let r = self.clean();
167                 if let Err(e) = r {
168                     error!(
169                         "Failed to clean task {}: {:?}",
170                         self.entity.task().name_version(),
171                         e
172                     );
173                 }
174             }
175         }
176 
177         return Ok(());
178     }
179 
pre_build(&mut self) -> Result<(), ExecutorError>180     fn pre_build(&mut self) -> Result<(), ExecutorError> {
181         if let Some(pre_build) = self.entity.task().build.pre_build {
182             let output = Command::new(pre_build)
183                 .output()
184                 .expect("Failed to execute pre_build script");
185 
186             // 检查脚本执行结果
187             if output.status.success() {
188                 info!("Pre-build script executed successfully");
189             } else {
190                 error!("Pre-build script failed");
191                 return Err(ExecutorError::TaskFailed(
192                     "Pre-build script failed".to_string(),
193                 ));
194             }
195         }
196         Ok(())
197     }
198 
build(&mut self) -> Result<(), ExecutorError>199     fn build(&mut self) -> Result<(), ExecutorError> {
200         if let Some(status) = self.task_log().build_status() {
201             if let Some(build_time) = self.task_log().build_time() {
202                 let mut last_modified = last_modified_time(&self.entity.file_path(), build_time)?;
203                 last_modified = core::cmp::max(
204                     last_modified,
205                     last_modified_time(&self.src_work_dir(), build_time)?,
206                 );
207 
208                 if *status == BuildStatus::Success
209                     && (self.entity.task().build_once || last_modified < *build_time)
210                 {
211                     info!(
212                         "Task {} has been built successfully, skip build.",
213                         self.entity.task().name_version()
214                     );
215                     return Ok(());
216                 }
217             }
218         }
219 
220         return self.do_build();
221     }
222 
post_build(&mut self) -> Result<(), ExecutorError>223     fn post_build(&mut self) -> Result<(), ExecutorError> {
224         if let Some(post_build) = self.entity.task().build.post_build {
225             let output = Command::new(post_build)
226                 .output()
227                 .expect("Failed to execute post_build script");
228 
229             // 检查脚本执行结果
230             if output.status.success() {
231                 info!("Post-build script executed successfully");
232             } else {
233                 error!("Post-build script failed");
234                 return Err(ExecutorError::TaskFailed(
235                     "Post-build script failed".to_string(),
236                 ));
237             }
238         }
239         Ok(())
240     }
241 
242     /// # 执行build操作
do_build(&mut self) -> Result<(), ExecutorError>243     fn do_build(&mut self) -> Result<(), ExecutorError> {
244         // 确认源文件就绪
245         self.prepare_input()?;
246 
247         let command: Option<Command> = self.create_command()?;
248         if let Some(cmd) = command {
249             self.run_command(cmd)?;
250         }
251 
252         // 检查构建结果,如果为空,则抛出警告
253         if self.build_dir.is_empty()? {
254             warn!(
255                 "Task {}: build result is empty, do you forget to copy the result to [$DADK_CURRENT_BUILD_DIR]?",
256                 self.entity.task().name_version(),
257             );
258         }
259         return Ok(());
260     }
261 
install(&self) -> Result<(), ExecutorError>262     fn install(&self) -> Result<(), ExecutorError> {
263         log::trace!("dadk-user: install {}", self.entity.task().name_version());
264         if let Some(status) = self.task_log().install_status() {
265             if let Some(install_time) = self.task_log().install_time() {
266                 let last_modified = last_modified_time(&self.build_dir.path, install_time)?;
267                 let last_modified = core::cmp::max(
268                     last_modified,
269                     last_modified_time(&self.entity.file_path(), install_time)?,
270                 );
271 
272                 if *status == InstallStatus::Success
273                     && (self.entity.task().install_once || last_modified < *install_time)
274                 {
275                     info!(
276                         "install: Task {} not changed.",
277                         self.entity.task().name_version()
278                     );
279                     return Ok(());
280                 }
281             }
282         }
283         log::trace!(
284             "dadk-user: to do install {}",
285             self.entity.task().name_version()
286         );
287         return self.do_install();
288     }
289 
290     /// # 执行安装操作,把构建结果安装到DragonOS
do_install(&self) -> Result<(), ExecutorError>291     fn do_install(&self) -> Result<(), ExecutorError> {
292         let binding = self.entity.task();
293         let in_dragonos_path = binding.install.in_dragonos_path.as_ref();
294         // 如果没有指定安装路径,则不执行安装
295         if in_dragonos_path.is_none() {
296             return Ok(());
297         }
298         info!("Installing task: {}", self.entity.task().name_version());
299         let mut in_dragonos_path = in_dragonos_path.unwrap().to_string_lossy().to_string();
300 
301         debug!("in_dragonos_path: {}", in_dragonos_path);
302         // 去除开头的斜杠
303         {
304             let count_leading_slashes = in_dragonos_path.chars().take_while(|c| *c == '/').count();
305             in_dragonos_path = in_dragonos_path[count_leading_slashes..].to_string();
306         }
307         // 拼接最终的安装路径
308         let install_path = abs_path(&self.dragonos_sysroot.join(in_dragonos_path));
309         debug!("install_path: {:?}", install_path);
310         // 创建安装路径
311         std::fs::create_dir_all(&install_path).map_err(|e| {
312             ExecutorError::InstallError(format!("Failed to create install path: {}", e.to_string()))
313         })?;
314 
315         // 拷贝构建结果到安装路径
316         let build_dir: PathBuf = self.build_dir.path.clone();
317         FileUtils::copy_dir_all(&build_dir, &install_path)
318             .map_err(|e| ExecutorError::InstallError(e))?;
319         info!("Task {} installed.", self.entity.task().name_version());
320 
321         return Ok(());
322     }
323 
clean(&self) -> Result<(), ExecutorError>324     fn clean(&self) -> Result<(), ExecutorError> {
325         let level = if let Action::Clean(l) = self.action {
326             l
327         } else {
328             panic!(
329                 "BUG: clean() called with non-clean action. executor details: {:?}",
330                 self
331             );
332         };
333         info!(
334             "Cleaning task: {}, level={level:?}",
335             self.entity.task().name_version()
336         );
337 
338         let r: Result<(), ExecutorError> = match level {
339             UserCleanLevel::All => self.clean_all(),
340             UserCleanLevel::InSrc => self.clean_src(),
341             UserCleanLevel::Output => {
342                 self.clean_target()?;
343                 self.clean_cache()
344             }
345         };
346 
347         if let Err(e) = r {
348             error!(
349                 "Failed to clean task: {}, error message: {:?}",
350                 self.entity.task().name_version(),
351                 e
352             );
353             return Err(e);
354         }
355 
356         return Ok(());
357     }
358 
clean_all(&self) -> Result<(), ExecutorError>359     fn clean_all(&self) -> Result<(), ExecutorError> {
360         // 在源文件目录执行清理
361         self.clean_src()?;
362         // 清理构建结果
363         self.clean_target()?;
364         // 清理缓存
365         self.clean_cache()?;
366         return Ok(());
367     }
368 
369     /// 在源文件目录执行清理
clean_src(&self) -> Result<(), ExecutorError>370     fn clean_src(&self) -> Result<(), ExecutorError> {
371         let cmd: Option<Command> = self.create_command()?;
372         if cmd.is_none() {
373             // 如果这里没有命令,则认为用户不需要在源文件目录执行清理
374             return Ok(());
375         }
376         info!(
377             "{}: Cleaning in source directory: {:?}",
378             self.entity.task().name_version(),
379             self.src_work_dir()
380         );
381 
382         let cmd = cmd.unwrap();
383         self.run_command(cmd)?;
384         return Ok(());
385     }
386 
387     /// 清理构建输出目录
clean_target(&self) -> Result<(), ExecutorError>388     fn clean_target(&self) -> Result<(), ExecutorError> {
389         info!(
390             "{}: Cleaning build target directory: {:?}",
391             self.entity.task().name_version(),
392             self.build_dir.path
393         );
394 
395         return self.build_dir.remove_self_recursive();
396     }
397 
398     /// 清理下载缓存
clean_cache(&self) -> Result<(), ExecutorError>399     fn clean_cache(&self) -> Result<(), ExecutorError> {
400         let cache_dir = self.source_dir.as_ref();
401         if cache_dir.is_none() {
402             // 如果没有缓存目录,则认为用户不需要清理缓存
403             return Ok(());
404         }
405         info!(
406             "{}: Cleaning cache directory: {}",
407             self.entity.task().name_version(),
408             self.src_work_dir().display()
409         );
410         return cache_dir.unwrap().remove_self_recursive();
411     }
412 
413     /// 获取源文件的工作目录
src_work_dir(&self) -> PathBuf414     fn src_work_dir(&self) -> PathBuf {
415         if let Some(local_path) = self.entity.task().source_path() {
416             return local_path;
417         }
418         return self.source_dir.as_ref().unwrap().path.clone();
419     }
420 
task_log(&self) -> TaskLog421     fn task_log(&self) -> TaskLog {
422         return self.task_data_dir.task_log();
423     }
424 
425     /// 为任务创建命令
create_command(&self) -> Result<Option<Command>, ExecutorError>426     fn create_command(&self) -> Result<Option<Command>, ExecutorError> {
427         // 获取命令
428         let raw_cmd = match self.entity.task().task_type {
429             TaskType::BuildFromSource(_) => match self.action {
430                 Action::Build => self.entity.task().build.build_command.clone(),
431                 Action::Clean(_) => self.entity.task().clean.clean_command.clone(),
432                 _ => unimplemented!(
433                     "create_command: Action {:?} not supported yet.",
434                     self.action
435                 ),
436             },
437 
438             TaskType::InstallFromPrebuilt(_) => match self.action {
439                 Action::Build => self.entity.task().build.build_command.clone(),
440                 Action::Clean(_) => self.entity.task().clean.clean_command.clone(),
441                 _ => unimplemented!(
442                     "create_command: Action {:?} not supported yet.",
443                     self.action
444                 ),
445             },
446         };
447 
448         if raw_cmd.is_none() {
449             return Ok(None);
450         }
451 
452         let raw_cmd = raw_cmd.unwrap();
453 
454         let mut command = Command::new("bash");
455         command.current_dir(self.src_work_dir());
456 
457         // 设置参数
458         command.arg("-c");
459         command.arg(raw_cmd);
460 
461         // 设置环境变量
462         let env_list = ENV_LIST.read().unwrap();
463         for (key, value) in env_list.envs.iter() {
464             // if key.starts_with("DADK") {
465             //     debug!("DADK env found: {}={}", key, value.value);
466             // }
467             command.env(key, value.value.clone());
468         }
469         drop(env_list);
470         for (key, value) in self.local_envs.envs.iter() {
471             debug!("Local env found: {}={}", key, value.value);
472             command.env(key, value.value.clone());
473         }
474 
475         return Ok(Some(command));
476     }
477 
478     /// # 准备工作线程本地环境变量
prepare_local_env(&mut self) -> Result<(), ExecutorError>479     fn prepare_local_env(&mut self) -> Result<(), ExecutorError> {
480         let binding = self.entity.task();
481         let task_envs: Option<&Vec<TaskEnv>> = binding.envs.as_ref();
482 
483         if let Some(task_envs) = task_envs {
484             for tv in task_envs.iter() {
485                 self.local_envs
486                     .add(EnvVar::new(tv.key().to_string(), tv.value().to_string()));
487             }
488         }
489 
490         // 添加`DADK_CURRENT_BUILD_DIR`环境变量,便于构建脚本把构建结果拷贝到这里
491         self.local_envs.add(EnvVar::new(
492             "DADK_CURRENT_BUILD_DIR".to_string(),
493             self.build_dir.path.to_str().unwrap().to_string(),
494         ));
495 
496         return Ok(());
497     }
498 
prepare_input(&self) -> Result<(), ExecutorError>499     fn prepare_input(&self) -> Result<(), ExecutorError> {
500         // 拉取源文件
501         let task = self.entity.task();
502         match &task.task_type {
503             TaskType::BuildFromSource(cs) => {
504                 if self.source_dir.is_none() {
505                     return Ok(());
506                 }
507                 let source_dir = self.source_dir.as_ref().unwrap();
508                 match cs {
509                     CodeSource::Git(git) => {
510                         git.prepare(source_dir)
511                             .map_err(|e| ExecutorError::PrepareEnvError(e))?;
512                     }
513                     // 本地源文件,不需要拉取
514                     CodeSource::Local(_) => return Ok(()),
515                     // 在线压缩包,需要下载
516                     CodeSource::Archive(archive) => {
517                         archive
518                             .download_unzip(source_dir)
519                             .map_err(|e| ExecutorError::PrepareEnvError(e))?;
520                     }
521                 }
522             }
523             TaskType::InstallFromPrebuilt(pb) => {
524                 match pb {
525                     // 本地源文件,不需要拉取
526                     PrebuiltSource::Local(local_source) => {
527                         let local_path = local_source.path();
528                         let target_path = &self.build_dir.path;
529                         FileUtils::copy_dir_all(&local_path, &target_path)
530                             .map_err(|e| ExecutorError::TaskFailed(e))?; // let mut cmd = "cp -r ".to_string();
531                         return Ok(());
532                     }
533                     // 在线压缩包,需要下载
534                     PrebuiltSource::Archive(archive) => {
535                         archive
536                             .download_unzip(&self.build_dir)
537                             .map_err(|e| ExecutorError::PrepareEnvError(e))?;
538                     }
539                 }
540             }
541         }
542 
543         return Ok(());
544     }
545 
run_command(&self, mut command: Command) -> Result<(), ExecutorError>546     fn run_command(&self, mut command: Command) -> Result<(), ExecutorError> {
547         let mut child = command
548             .stdin(Stdio::inherit())
549             .spawn()
550             .map_err(|e| ExecutorError::IoError(e.to_string()))?;
551 
552         // 等待子进程结束
553         let r = child
554             .wait()
555             .map_err(|e| ExecutorError::IoError(e.to_string()));
556         debug!("Command finished: {:?}", r);
557         if r.is_ok() {
558             let r = r.unwrap();
559             if r.success() {
560                 return Ok(());
561             } else {
562                 // 执行失败,获取最后100行stderr输出
563                 let errmsg = format!(
564                     "Task {} failed, exit code = {}",
565                     self.entity.task().name_version(),
566                     r.code().unwrap()
567                 );
568                 error!("{errmsg}");
569                 let command_opt = command.output();
570                 if command_opt.is_err() {
571                     return Err(ExecutorError::TaskFailed(
572                         "Failed to get command output".to_string(),
573                     ));
574                 }
575                 let command_opt = command_opt.unwrap();
576                 let command_output = String::from_utf8_lossy(&command_opt.stderr);
577                 let mut last_100_outputs = command_output
578                     .lines()
579                     .rev()
580                     .take(100)
581                     .collect::<Vec<&str>>();
582                 last_100_outputs.reverse();
583                 error!("Last 100 lines msg of stderr:");
584                 for line in last_100_outputs {
585                     error!("{}", line);
586                 }
587                 return Err(ExecutorError::TaskFailed(errmsg));
588             }
589         } else {
590             let errmsg = format!(
591                 "Task {} failed, msg = {:?}",
592                 self.entity.task().name_version(),
593                 r.err().unwrap()
594             );
595             error!("{errmsg}");
596             return Err(ExecutorError::TaskFailed(errmsg));
597         }
598     }
599 }
600 
601 #[derive(Debug, Clone)]
602 pub struct EnvMap {
603     pub envs: BTreeMap<String, EnvVar>,
604 }
605 
606 impl EnvMap {
new() -> Self607     pub fn new() -> Self {
608         Self {
609             envs: BTreeMap::new(),
610         }
611     }
612 
add(&mut self, env: EnvVar)613     pub fn add(&mut self, env: EnvVar) {
614         self.envs.insert(env.key.clone(), env);
615     }
616 
617     #[allow(dead_code)]
get(&self, key: &str) -> Option<&EnvVar>618     pub fn get(&self, key: &str) -> Option<&EnvVar> {
619         self.envs.get(key)
620     }
621 
add_vars(&mut self, vars: Vars)622     pub fn add_vars(&mut self, vars: Vars) {
623         for (key, value) in vars {
624             self.add(EnvVar::new(key, value));
625         }
626     }
627 }
628 
629 /// # 环境变量
630 #[derive(Debug, PartialEq, PartialOrd, Eq, Ord, Clone)]
631 pub struct EnvVar {
632     pub key: String,
633     pub value: String,
634 }
635 
636 impl EnvVar {
new(key: String, value: String) -> Self637     pub fn new(key: String, value: String) -> Self {
638         Self { key, value }
639     }
640 }
641 
642 /// # 任务执行器错误枚举
643 #[allow(dead_code)]
644 #[derive(Debug, Clone)]
645 pub enum ExecutorError {
646     /// 准备执行环境错误
647     PrepareEnvError(String),
648     IoError(String),
649     /// 构建执行错误
650     TaskFailed(String),
651     /// 安装错误
652     InstallError(String),
653     /// 清理错误
654     CleanError(String),
655 }
656 
657 /// # 准备全局环境变量
prepare_env( sched_entities: &SchedEntities, execute_ctx: &Arc<DadkUserExecuteContext>, ) -> Result<(), ExecutorError>658 pub fn prepare_env(
659     sched_entities: &SchedEntities,
660     execute_ctx: &Arc<DadkUserExecuteContext>,
661 ) -> Result<(), ExecutorError> {
662     info!("Preparing environment variables...");
663     let env_list = create_global_env_list(sched_entities, execute_ctx)?;
664     // 写入全局环境变量列表
665     let mut global_env_list = ENV_LIST.write().unwrap();
666     *global_env_list = env_list;
667     return Ok(());
668 }
669 
670 /// # 创建全局环境变量列表
create_global_env_list( sched_entities: &SchedEntities, execute_ctx: &Arc<DadkUserExecuteContext>, ) -> Result<EnvMap, ExecutorError>671 fn create_global_env_list(
672     sched_entities: &SchedEntities,
673     execute_ctx: &Arc<DadkUserExecuteContext>,
674 ) -> Result<EnvMap, ExecutorError> {
675     let mut env_list = EnvMap::new();
676     let envs: Vars = std::env::vars();
677     env_list.add_vars(envs);
678 
679     // 为每个任务创建特定的环境变量
680     for entity in sched_entities.entities().iter() {
681         // 导出任务的构建目录环境变量
682         let build_dir = CacheDir::build_dir(entity.clone())?;
683 
684         let build_dir_key = CacheDir::build_dir_env_key(&entity)?;
685         env_list.add(EnvVar::new(
686             build_dir_key,
687             build_dir.to_str().unwrap().to_string(),
688         ));
689 
690         // 如果需要源码缓存目录,则导出
691         if CacheDir::need_source_cache(entity) {
692             let source_dir = CacheDir::source_dir(entity.clone())?;
693             let source_dir_key = CacheDir::source_dir_env_key(&entity)?;
694             env_list.add(EnvVar::new(
695                 source_dir_key,
696                 source_dir.to_str().unwrap().to_string(),
697             ));
698         }
699     }
700 
701     // 创建ARCH环境变量
702     let target_arch = execute_ctx.target_arch();
703     env_list.add(EnvVar::new("ARCH".to_string(), (*target_arch).into()));
704 
705     return Ok(env_list);
706 }
707 
708 /// # 获取文件最后的更新时间
709 ///
710 /// ## 参数
711 /// * `path` - 文件路径
712 /// * `last_modified` - 最后的更新时间
713 /// * `build_time` - 构建时间
last_modified_time( path: &PathBuf, build_time: &DateTime<Utc>, ) -> Result<DateTime<Utc>, ExecutorError>714 fn last_modified_time(
715     path: &PathBuf,
716     build_time: &DateTime<Utc>,
717 ) -> Result<DateTime<Utc>, ExecutorError> {
718     let mut queue = VecDeque::new();
719     queue.push_back(path.clone());
720 
721     let mut last_modified = DateTime::<Utc>::from(SystemTime::UNIX_EPOCH);
722 
723     while let Some(current_path) = queue.pop_front() {
724         let metadata = current_path
725             .metadata()
726             .map_err(|e| ExecutorError::InstallError(e.to_string()))?;
727 
728         if metadata.is_dir() {
729             for r in std::fs::read_dir(&current_path).unwrap() {
730                 if let Ok(entry) = r {
731                     // 忽略编译产物目录
732                     if entry.file_name() == "target" {
733                         continue;
734                     }
735 
736                     let entry_path = entry.path();
737                     let entry_metadata = entry.metadata().unwrap();
738                     // 比较文件的修改时间和last_modified,取最大值
739                     let file_modified = DateTime::<Utc>::from(entry_metadata.modified().unwrap());
740                     last_modified = std::cmp::max(last_modified, file_modified);
741 
742                     // 如果其中某一个文件的修改时间在build_time之后,则直接返回,不用继续搜索
743                     if last_modified > *build_time {
744                         return Ok(last_modified);
745                     }
746 
747                     if entry_metadata.is_dir() {
748                         // 如果是子目录,则将其加入队列
749                         queue.push_back(entry_path);
750                     }
751                 }
752             }
753         } else {
754             // 如果是文件,直接比较修改时间
755             let file_modified = DateTime::<Utc>::from(metadata.modified().unwrap());
756             last_modified = std::cmp::max(last_modified, file_modified);
757 
758             // 如果其中某一个文件的修改时间在build_time之后,则直接返回,不用继续递归
759             if last_modified > *build_time {
760                 return Ok(last_modified);
761             }
762         }
763     }
764 
765     if last_modified == DateTime::<Utc>::from(SystemTime::UNIX_EPOCH) {
766         return Err(ExecutorError::InstallError(format!(
767             "Failed to get last modified time for path: {}",
768             path.display()
769         )));
770     }
771     Ok(last_modified)
772 }
773