浏览代码

增加“只编译一次”和“只安装一次”的功能 (#31)

LoGin 1 年之前
父节点
当前提交
8c191e9b2c
共有 8 个文件被更改,包括 177 次插入8 次删除
  1. 3 1
      Cargo.toml
  2. 26 0
      src/console/elements.rs
  3. 8 1
      src/console/new_config.rs
  4. 49 2
      src/executor/cache.rs
  5. 76 3
      src/executor/mod.rs
  6. 2 0
      src/parser/mod.rs
  7. 12 0
      src/parser/task.rs
  8. 1 1
      src/utils/file.rs

+ 3 - 1
Cargo.toml

@@ -1,7 +1,7 @@
 [package]
 name = "dadk"
 authors = ["longjin <longjin@DragonOS.org>", "chikejian <chikejian@DragonOS.org>"]
-version = "0.1.9"
+version = "0.1.10"
 edition = "2021"
 description = "DragonOS Application Development Kit\nDragonOS应用开发工具"
 license = "GPL-2.0-only"
@@ -16,6 +16,7 @@ doc = true
 # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
 
 [dependencies]
+chrono = { version = "=0.4.35", features = ["serde"] }
 clap = { version = "=4.2.4", features = ["derive"] }
 lazy_static = "1.4.0"
 log = "0.4.17"
@@ -24,4 +25,5 @@ reqwest = { version = "0.11", features = ["blocking", "json"] }
 serde = { version = "1.0.160", features = ["serde_derive"] }
 serde_json = "1.0.96"
 simple_logger = { version = "4.1.0", features = ["stderr"] }
+toml = "0.8.12"
 zip = "0.6"

+ 26 - 0
src/console/elements.rs

@@ -373,3 +373,29 @@ impl<T: Debug> VecInput<T> {
         return Ok(&self.results);
     }
 }
+
+#[derive(Debug)]
+pub struct BoolInput {
+    input: Input,
+}
+impl BoolInput {
+    pub fn new(mut pre_tips: String, post_tips: Option<String>) -> Self {
+        pre_tips.push_str(" (y/n)");
+        Self {
+            input: Input::new(Some(pre_tips), post_tips),
+        }
+    }
+}
+
+impl InputFunc<bool> for BoolInput {
+    fn input(&mut self) -> Result<bool, ConsoleError> {
+        let x = self.input.input()?;
+
+        let x = x.trim().to_lowercase();
+        if x.starts_with("y") {
+            return Ok(true);
+        } else {
+            return Ok(false);
+        }
+    }
+}

+ 8 - 1
src/console/new_config.rs

@@ -3,7 +3,7 @@ use std::{cell::RefCell, fmt::Debug, path::PathBuf, rc::Rc};
 use log::{debug, error, info};
 
 use crate::{
-    console::elements::{OptionalChoice, VecInput},
+    console::elements::{BoolInput, OptionalChoice, VecInput},
     executor::{
         cache::CacheDir,
         source::{ArchiveSource, GitSource, LocalSource},
@@ -117,6 +117,11 @@ impl NewConfigCommand {
         let task_env: Option<Vec<TaskEnv>> = TaskEnvInput::new().input()?;
         debug!("task_env: {:?}", task_env);
 
+        let build_once = BoolInput::new("Run this task once?".to_string(), None).input()?;
+        debug!("build_once: {:?}", build_once);
+        let install_once = BoolInput::new("Install this task once?".to_string(), None).input()?;
+        debug!("install_once: {:?}", install_once);
+
         let mut dadk: DADKTask = DADKTask::new(
             name,
             version,
@@ -128,6 +133,8 @@ impl NewConfigCommand {
             install_config,
             clean_config,
             task_env,
+            build_once,
+            install_once,
         );
 
         dadk.trim();

+ 49 - 2
src/executor/cache.rs

@@ -3,7 +3,10 @@ use std::{path::PathBuf, sync::Arc};
 use log::info;
 
 use crate::{
-    parser::task::{CodeSource, DADKTask, TaskType},
+    parser::{
+        task::{CodeSource, DADKTask, TaskType},
+        task_log::TaskLog,
+    },
     scheduler::SchedEntity,
     utils::lazy_init::Lazy,
 };
@@ -38,7 +41,7 @@ pub fn cache_root_init(path: Option<PathBuf>) -> Result<(), ExecutorError> {
             }
             let cwd = cwd.unwrap();
 
-            cache_root = format!("{}/.cache", cwd);
+            cache_root = format!("{}/dadk_cache", cwd);
         }
     } else {
         // 如果有设置缓存根目录,则使用设置的值
@@ -77,8 +80,12 @@ pub fn cache_root_init(path: Option<PathBuf>) -> Result<(), ExecutorError> {
 
 #[derive(Debug, Clone, Copy)]
 pub enum CacheDirType {
+    /// 构建缓存目录
     Build,
+    /// 源码缓存目录
     Source,
+    /// 每个任务执行数据缓存目录
+    TaskData,
 }
 
 #[derive(Debug, Clone)]
@@ -117,6 +124,13 @@ impl CacheDir {
             CacheDirType::Source => {
                 format!("{}/source/{}", cache_root.to_str().unwrap(), name_version)
             }
+            CacheDirType::TaskData => {
+                format!(
+                    "{}/task_data/{}",
+                    cache_root.to_str().unwrap(),
+                    name_version
+                )
+            }
         };
 
         return PathBuf::from(cache_dir);
@@ -210,3 +224,36 @@ impl CacheDir {
         return Ok(());
     }
 }
+
+#[derive(Debug, Clone)]
+pub struct TaskDataDir {
+    dir: CacheDir,
+}
+
+impl TaskDataDir {
+    const TASK_LOG_FILE_NAME: &'static str = "task_log.toml";
+    pub fn new(entity: Arc<SchedEntity>) -> Result<Self, ExecutorError> {
+        let dir = CacheDir::new(entity.clone(), CacheDirType::TaskData)?;
+        return Ok(Self { dir });
+    }
+
+    /// # 获取任务日志
+    pub fn task_log(&self) -> TaskLog {
+        let path = self.dir.path.join(Self::TASK_LOG_FILE_NAME);
+        if path.exists() {
+            let content = std::fs::read_to_string(&path).unwrap();
+            let task_log: TaskLog = toml::from_str(&content).unwrap();
+            return task_log;
+        } else {
+            return TaskLog::new();
+        }
+    }
+
+    /// # 设置任务日志
+    pub fn save_task_log(&self, task_log: &TaskLog) -> Result<(), ExecutorError> {
+        let path = self.dir.path.join(Self::TASK_LOG_FILE_NAME);
+        let content = toml::to_string(task_log).unwrap();
+        std::fs::write(&path, content).map_err(|e| ExecutorError::IoError(e))?;
+        return Ok(());
+    }
+}

+ 76 - 3
src/executor/mod.rs

@@ -11,12 +11,15 @@ use log::{debug, error, info, warn};
 use crate::{
     console::{clean::CleanLevel, Action},
     executor::cache::CacheDir,
-    parser::task::{CodeSource, PrebuiltSource, TaskEnv, TaskType},
+    parser::{
+        task::{CodeSource, PrebuiltSource, TaskEnv, TaskType},
+        task_log::{BuildStatus, InstallStatus, TaskLog},
+    },
     scheduler::{SchedEntities, SchedEntity},
     utils::file::FileUtils,
 };
 
-use self::cache::CacheDirType;
+use self::cache::{CacheDirType, TaskDataDir};
 
 pub mod cache;
 pub mod source;
@@ -36,6 +39,8 @@ pub struct Executor {
     build_dir: CacheDir,
     /// 如果任务需要源文件缓存,则此字段为 Some(CacheDir),否则为 None(使用本地源文件路径)
     source_dir: Option<CacheDir>,
+    /// 任务数据目录
+    task_data_dir: TaskDataDir,
     /// DragonOS sysroot的路径
     dragonos_sysroot: PathBuf,
 }
@@ -60,6 +65,7 @@ impl Executor {
     ) -> Result<Self, ExecutorError> {
         let local_envs = EnvMap::new();
         let build_dir = CacheDir::new(entity.clone(), CacheDirType::Build)?;
+        let task_data_dir = TaskDataDir::new(entity.clone())?;
 
         let source_dir = if CacheDir::need_source_cache(&entity) {
             Some(CacheDir::new(entity.clone(), CacheDirType::Source)?)
@@ -73,6 +79,7 @@ impl Executor {
             local_envs,
             build_dir,
             source_dir,
+            task_data_dir,
             dragonos_sysroot,
         };
 
@@ -91,6 +98,48 @@ impl Executor {
     pub fn execute(&mut self) -> Result<(), ExecutorError> {
         info!("Execute task: {}", self.entity.task().name_version());
 
+        let r = self.do_execute();
+        self.save_task_data(r);
+        info!("Task {} finished", self.entity.task().name_version());
+        return Ok(());
+    }
+
+    /// # 保存任务数据
+    fn save_task_data(&self, r: Result<(), ExecutorError>) {
+        let mut task_log = self.task_data_dir.task_log();
+        match self.action {
+            Action::Build => {
+                if r.is_ok() {
+                    task_log.set_build_status(BuildStatus::Success);
+                } else {
+                    task_log.set_build_status(BuildStatus::Failed);
+                }
+
+                task_log.set_build_time_now();
+            }
+
+            Action::Install => {
+                if r.is_ok() {
+                    task_log.set_install_status(InstallStatus::Success);
+                } else {
+                    task_log.set_install_status(InstallStatus::Failed);
+                }
+            }
+
+            Action::Clean(_) => {
+                task_log.clean_build_status();
+                task_log.clean_install_status();
+            }
+
+            _ => {}
+        }
+
+        self.task_data_dir
+            .save_task_log(&task_log)
+            .expect("Failed to save task log");
+    }
+
+    fn do_execute(&mut self) -> Result<(), ExecutorError> {
         // 准备本地环境变量
         self.prepare_local_env()?;
 
@@ -118,12 +167,22 @@ impl Executor {
                 error!("Unsupported action: {:?}", self.action);
             }
         }
-        info!("Task {} finished", self.entity.task().name_version());
+
         return Ok(());
     }
 
     /// # 执行build操作
     fn build(&mut self) -> Result<(), ExecutorError> {
+        if let Some(status) = self.task_log().build_status() {
+            if *status == BuildStatus::Success && self.entity.task().build_once {
+                info!(
+                    "Task {} has been built successfully, skip build.",
+                    self.entity.task().name_version()
+                );
+                return Ok(());
+            }
+        }
+
         self.mv_target_to_tmp()?;
 
         // 确认源文件就绪
@@ -146,6 +205,16 @@ impl Executor {
 
     /// # 执行安装操作,把构建结果安装到DragonOS
     fn install(&self) -> Result<(), ExecutorError> {
+        if let Some(status) = self.task_log().install_status() {
+            if *status == InstallStatus::Success && self.entity.task().install_once {
+                info!(
+                    "Task {} has been installed successfully, skip install.",
+                    self.entity.task().name_version()
+                );
+                return Ok(());
+            }
+        }
+
         let binding = self.entity.task();
         let in_dragonos_path = binding.install.in_dragonos_path.as_ref();
         // 如果没有指定安装路径,则不执行安装
@@ -278,6 +347,10 @@ impl Executor {
         return self.source_dir.as_ref().unwrap().path.clone();
     }
 
+    fn task_log(&self) -> TaskLog {
+        return self.task_data_dir.task_log();
+    }
+
     /// 为任务创建命令
     fn create_command(&self) -> Result<Option<Command>, ExecutorError> {
         // 获取命令

+ 2 - 0
src/parser/mod.rs

@@ -21,6 +21,7 @@
 //!     "build": {构建配置(该部分详见BuildConfig的文档)},
 //!     "install": {安装配置(该部分详见InstallConfig的文档)},
 //!     "envs" : [{ "key": "环境变量名", "value": "环境变量值" }]
+//!     "build_once": (可选) 是否只构建一次,如果为true,DADK会在构建成功后,将构建结果缓存起来,下次构建时,直接使用缓存的构建结果。
 //! }
 use std::{
     fmt::Debug,
@@ -32,6 +33,7 @@ use log::{debug, error, info};
 
 use self::task::DADKTask;
 pub mod task;
+pub mod task_log;
 
 /// # 配置解析器
 ///

+ 12 - 0
src/parser/task.rs

@@ -36,6 +36,14 @@ pub struct DADKTask {
     pub clean: CleanConfig,
     /// 环境变量
     pub envs: Option<Vec<TaskEnv>>,
+
+    /// (可选) 是否只构建一次,如果为true,DADK会在构建成功后,将构建结果缓存起来,下次构建时,直接使用缓存的构建结果。
+    #[serde(default)]
+    pub build_once: bool,
+
+    /// (可选) 是否只安装一次,如果为true,DADK会在安装成功后,不再重复安装。
+    #[serde(default)]
+    pub install_once: bool,
 }
 
 impl DADKTask {
@@ -51,6 +59,8 @@ impl DADKTask {
         install: InstallConfig,
         clean: CleanConfig,
         envs: Option<Vec<TaskEnv>>,
+        build_once: bool,
+        install_once: bool,
     ) -> Self {
         Self {
             name,
@@ -63,6 +73,8 @@ impl DADKTask {
             install,
             clean,
             envs,
+            build_once,
+            install_once,
         }
     }
 

+ 1 - 1
src/utils/file.rs

@@ -47,7 +47,7 @@ impl FileUtils {
     /// 递归地复制给定目录下所有文件到另一个文件夹中
     pub fn copy_dir_all(src: &Path, dst: &Path) -> Result<(), String> {
         let mut cmd = Command::new("cp");
-        cmd.arg("-rf").arg("./").arg(dst);
+        cmd.arg("-r").arg("-f").arg("./").arg(dst);
 
         cmd.current_dir(src);