Pārlūkot izejas kodu

Xuzihao/feat dadk user (#71)

* 命令行参数解析:dadk_user -> dadk

* feat: 解析toml格式的用户程序配置文件

* feat: 添加toml格式的用户程序配置文件模版

* 增加用户程序配置解析的测试方法

* 将dadk-user中测试方法的dadk用户程序配置文件都改为toml格式的
Jomo 5 mēneši atpakaļ
vecāks
revīzija
5129c63bfa

+ 75 - 0
crates/test_base/src/dadk_user.rs

@@ -0,0 +1,75 @@
+use std::path::PathBuf;
+
+use test_context::TestContext;
+
+#[derive(Debug, Clone)]
+pub struct DadkUserTestContext {
+    /// 项目的根目录
+    test_base_path: PathBuf,
+}
+
+impl DadkUserTestContext {
+    /// 获取项目的根目录
+    pub fn test_base_path(&self) -> &PathBuf {
+        &self.test_base_path
+    }
+
+    /// 获取项目目录下的文件的的绝对路径
+    pub fn abs_path(&self, relative_path: &str) -> PathBuf {
+        self.test_base_path.join(relative_path)
+    }
+
+    /// 获取 dadk配置模版的路径
+    pub fn templates_dir(&self) -> PathBuf {
+        const TEMPLATES_CONFIG_DIR: &str = "templates/config";
+        self.abs_path(TEMPLATES_CONFIG_DIR)
+    }
+}
+
+impl TestContext for DadkUserTestContext {
+    fn setup() -> Self {
+        env_logger::try_init_from_env(env_logger::Env::default().default_filter_or("info")).ok();
+
+        // 获取dadk-user包的根目录
+        let mut test_base_path: PathBuf = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
+        test_base_path.pop();
+        test_base_path.pop();
+        test_base_path.push("dadk-user");
+        log::debug!(
+            "DadkUserTestContext setup: project_base_path={:?}",
+            test_base_path
+        );
+        // 设置workdir
+        std::env::set_current_dir(&test_base_path).expect("Failed to setup test base path");
+
+        let r = DadkUserTestContext { test_base_path };
+
+        r
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+    use std::env;
+
+    #[test]
+    fn test_test_base_path() {
+        let test_context = DadkUserTestContext::setup();
+        let expected_path = PathBuf::from(env!("CARGO_MANIFEST_DIR"))
+            .parent()
+            .unwrap()
+            .parent()
+            .unwrap()
+            .join("dadk-user");
+        assert_eq!(test_context.test_base_path(), &expected_path);
+    }
+
+    #[test]
+    fn test_abs_path() {
+        let test_context = DadkUserTestContext::setup();
+        let relative_path = "some_relative_path";
+        let expected_path = test_context.test_base_path().join(relative_path);
+        assert_eq!(test_context.abs_path(relative_path), expected_path);
+    }
+}

+ 5 - 0
crates/test_base/src/global.rs

@@ -10,6 +10,7 @@ pub struct BaseGlobalTestContext {
 
 impl BaseGlobalTestContext {
     const CONFIG_V1_DIR: &'static str = "tests/data/dadk_config_v1";
+    const CONFIG_V2_DIR: &'static str = "tests/data/dadk_config_v2";
     const FAKE_DRAGONOS_SYSROOT: &'static str = "tests/data/fake_dragonos_sysroot";
     const FAKE_DADK_CACHE_ROOT: &'static str = "tests/data/fake_dadk_cache_root";
 
@@ -28,6 +29,10 @@ impl BaseGlobalTestContext {
         self.abs_path(Self::CONFIG_V1_DIR)
     }
 
+    pub fn config_v2_dir(&self) -> PathBuf {
+        self.abs_path(Self::CONFIG_V2_DIR)
+    }
+
     fn ensure_fake_dragonos_dir_exist(&self) {
         let fake_dragonos_dir = self.fake_dragonos_sysroot();
         if !fake_dragonos_dir.exists() {

+ 1 - 0
crates/test_base/src/lib.rs

@@ -1,4 +1,5 @@
 pub extern crate test_context;
 
 pub mod dadk_config;
+pub mod dadk_user;
 pub mod global;

+ 4 - 4
dadk-user/src/executor/tests.rs

@@ -48,8 +48,8 @@ fn setup_executor<T: TestContextExt>(config_file: PathBuf, ctx: &T) -> Executor
 fn set_local_env(ctx: &DadkExecuteContextTestBuildX86_64V1) {
     let config_file_path = ctx
         .base_context()
-        .config_v1_dir()
-        .join("app_normal_with_env_0_1_0.dadk");
+        .config_v2_dir()
+        .join("app_normal_with_env_0_2_0.toml");
     let mut executor = setup_executor(config_file_path, ctx);
 
     let r = executor.prepare_local_env();
@@ -70,8 +70,8 @@ fn set_local_env(ctx: &DadkExecuteContextTestBuildX86_64V1) {
 fn execute_should_capture_error(ctx: &DadkExecuteContextTestBuildX86_64V1) {
     let config_file_path = ctx
         .base_context()
-        .config_v1_dir()
-        .join("app_normal_with_env_fail_0_1_0.dadk");
+        .config_v2_dir()
+        .join("app_normal_with_env_fail_0_2_0.toml");
     let mut executor = setup_executor(config_file_path, ctx);
 
     let r = executor.prepare_local_env();

+ 4 - 9
dadk-user/src/lib.rs

@@ -88,6 +88,7 @@
 
 #[macro_use]
 extern crate lazy_static;
+pub extern crate clap;
 extern crate log;
 extern crate serde;
 extern crate serde_json;
@@ -97,8 +98,6 @@ extern crate test_base;
 
 use std::{path::PathBuf, process::exit, sync::Arc};
 
-use clap::Parser;
-
 use log::info;
 use parser::task::DADKTask;
 
@@ -106,19 +105,15 @@ use crate::{
     console::CommandLineArgs, context::DadkUserExecuteContextBuilder, scheduler::Scheduler,
 };
 
-mod console;
+pub mod console;
 mod context;
-mod executor;
+pub mod executor;
 pub mod parser;
 mod scheduler;
 pub mod static_resources;
 mod utils;
 
-pub fn dadk_user_main() {
-    let args = CommandLineArgs::parse();
-
-    info!("DADK run with args: {:?}", &args);
-
+pub fn dadk_user_main(args: CommandLineArgs) {
     let context = DadkUserExecuteContextBuilder::default()
         .sysroot_dir(args.sysroot_dir)
         .config_dir(args.config_dir)

+ 477 - 0
dadk-user/src/parser/config.rs

@@ -0,0 +1,477 @@
+use std::path::PathBuf;
+
+use serde::de::Error;
+use toml::Value;
+
+use super::{
+    task::{Dependency, TargetArch, TaskEnv},
+    InnerParserError, ParserError,
+};
+
+// DADK用户配置关键字
+pub(super) enum DADKUserConfigKey {
+    Name,
+    Version,
+    Description,
+    BuildOnce,
+    InstallOnce,
+    RustTarget,
+    TargetArch,
+    TaskType,
+    Type,
+    Source,
+    SourcePath,
+    Revision,
+    Branch,
+    Build,
+    BuildCommand,
+    Install,
+    InDragonosPath,
+    Clean,
+    CleanCommand,
+    Depends,
+    Envs,
+    BuildFromSource,
+    InstallFromPrebuilt,
+    Git,
+    Local,
+    Archive,
+}
+
+impl Into<&str> for DADKUserConfigKey {
+    fn into(self) -> &'static str {
+        match self {
+            DADKUserConfigKey::Name => "name",
+            DADKUserConfigKey::Version => "version",
+            DADKUserConfigKey::Description => "description",
+            DADKUserConfigKey::BuildOnce => "build-once",
+            DADKUserConfigKey::InstallOnce => "install-once",
+            DADKUserConfigKey::RustTarget => "rust-target",
+            DADKUserConfigKey::TargetArch => "target-arch",
+            DADKUserConfigKey::TaskType => "task-type",
+            DADKUserConfigKey::Type => "type",
+            DADKUserConfigKey::Source => "source",
+            DADKUserConfigKey::SourcePath => "source-path",
+            DADKUserConfigKey::Revision => "revison",
+            DADKUserConfigKey::Branch => "branch",
+            DADKUserConfigKey::Build => "build",
+            DADKUserConfigKey::BuildCommand => "build-command",
+            DADKUserConfigKey::Install => "install",
+            DADKUserConfigKey::InDragonosPath => "in-dragonos-path",
+            DADKUserConfigKey::Clean => "clean",
+            DADKUserConfigKey::CleanCommand => "clean-command",
+            DADKUserConfigKey::Depends => "depends",
+            DADKUserConfigKey::Envs => "envs",
+            DADKUserConfigKey::BuildFromSource => "build_from_source",
+            DADKUserConfigKey::InstallFromPrebuilt => "install_from_prebuilt",
+            DADKUserConfigKey::Archive => "archive",
+            DADKUserConfigKey::Git => "git",
+            DADKUserConfigKey::Local => "local",
+        }
+    }
+}
+
+impl TryFrom<&str> for DADKUserConfigKey {
+    type Error = ParserError;
+    fn try_from(value: &str) -> Result<Self, Self::Error> {
+        match value {
+            "name" => Ok(DADKUserConfigKey::Name),
+            "version" => Ok(DADKUserConfigKey::Version),
+            "description" => Ok(DADKUserConfigKey::Description),
+            "build-once" => Ok(DADKUserConfigKey::BuildOnce),
+            "install-once" => Ok(DADKUserConfigKey::InstallOnce),
+            "rust-target" => Ok(DADKUserConfigKey::RustTarget),
+            "target-arch" => Ok(DADKUserConfigKey::TargetArch),
+            "task-type" => Ok(DADKUserConfigKey::TaskType),
+            "type" => Ok(DADKUserConfigKey::Type),
+            "source" => Ok(DADKUserConfigKey::Source),
+            "source-path" => Ok(DADKUserConfigKey::SourcePath),
+            "revison" => Ok(DADKUserConfigKey::Revision),
+            "branch" => Ok(DADKUserConfigKey::Branch),
+            "build" => Ok(DADKUserConfigKey::Build),
+            "build-command" => Ok(DADKUserConfigKey::BuildCommand),
+            "install" => Ok(DADKUserConfigKey::Install),
+            "in-dragonos-path" => Ok(DADKUserConfigKey::InDragonosPath),
+            "clean" => Ok(DADKUserConfigKey::Clean),
+            "clean-command" => Ok(DADKUserConfigKey::CleanCommand),
+            "depends" => Ok(DADKUserConfigKey::Depends),
+            "envs" => Ok(DADKUserConfigKey::Envs),
+            "build_from_source" => Ok(DADKUserConfigKey::BuildFromSource),
+            "install_from_prebuilt" => Ok(DADKUserConfigKey::InstallFromPrebuilt),
+            "archive" => Ok(DADKUserConfigKey::Archive),
+            "git" => Ok(DADKUserConfigKey::Git),
+            "local" => Ok(DADKUserConfigKey::Local),
+            _ => Err(ParserError {
+                config_file: None,
+                error: InnerParserError::TomlError(toml::de::Error::custom(format!(
+                    "Unknown dadk_user_config_key: {}",
+                    value
+                ))),
+            }),
+        }
+    }
+}
+
+pub(super) struct DADKUserConfig {
+    pub(super) standard_config: DADKUserStandardConfig,
+    pub(super) task_type_config: DADKUserTaskType,
+    pub(super) build_config: DADKUserBuildConfig,
+    pub(super) install_config: DADKUserInstallConfig,
+    pub(super) clean_config: DADKUserCleanConfig,
+    pub(super) depends_config: DADKUserDependsConfig,
+    pub(super) envs_config: DADKUserEnvsConfig,
+}
+
+impl DADKUserConfig {
+    pub(super) fn parse(
+        config_file: &PathBuf,
+        table: &toml::value::Table,
+    ) -> Result<DADKUserConfig, ParserError> {
+        Ok(Self {
+            standard_config: DADKUserStandardConfig::parse(config_file, table)?,
+            task_type_config: DADKUserTaskType::parse(config_file, table)?,
+            build_config: DADKUserBuildConfig::parse(config_file, table)?,
+            install_config: DADKUserInstallConfig::parse(config_file, table)?,
+            clean_config: DADKUserCleanConfig::parse(config_file, table)?,
+            depends_config: DADKUserDependsConfig::parse(config_file, table)?,
+            envs_config: DADKUserEnvsConfig::parse(config_file, table)?,
+        })
+    }
+}
+
+/// 标准信息配置
+#[derive(Debug)]
+pub(super) struct DADKUserStandardConfig {
+    pub(super) name: String,
+    pub(super) version: String,
+    pub(super) description: String,
+    pub(super) build_once: bool,
+    pub(super) install_once: bool,
+    pub(super) rust_target: Option<String>,
+    pub(super) target_arch: Vec<TargetArch>,
+}
+
+impl DADKUserStandardConfig {
+    fn parse(
+        config_file: &PathBuf,
+        table: &toml::value::Table,
+    ) -> Result<DADKUserStandardConfig, ParserError> {
+        let name: String =
+            TomlValueParser::parse_string(config_file, table, DADKUserConfigKey::Name.into())?;
+        let version =
+            TomlValueParser::parse_string(config_file, table, DADKUserConfigKey::Version.into())?;
+        let description = TomlValueParser::parse_string(
+            config_file,
+            table,
+            DADKUserConfigKey::Description.into(),
+        )?;
+        let build_once =
+            TomlValueParser::parse_bool(config_file, table, DADKUserConfigKey::BuildOnce.into())?;
+        let install_once =
+            TomlValueParser::parse_bool(config_file, table, DADKUserConfigKey::InstallOnce.into())?;
+        let rust_target =
+            TomlValueParser::parse_option_string(table, DADKUserConfigKey::RustTarget.into());
+        let target_arch: Vec<TargetArch> = match TomlValueParser::parse_option_array(
+            table,
+            DADKUserConfigKey::TargetArch.into(),
+        ) {
+            Some(value_vec) => {
+                let mut target_arch_vec = Vec::new();
+                for value in value_vec {
+                    let target_arch =
+                        TargetArch::try_from(value.as_str().unwrap()).map_err(|e| ParserError {
+                            config_file: Some(config_file.clone()),
+                            error: InnerParserError::TomlError(toml::de::Error::custom(e)),
+                        })?;
+                    target_arch_vec.push(target_arch);
+                }
+                target_arch_vec
+            }
+            None => vec![TargetArch::X86_64],
+        };
+
+        Ok(Self {
+            name,
+            version,
+            description,
+            build_once,
+            install_once,
+            rust_target,
+            target_arch,
+        })
+    }
+}
+
+/// task-type配置
+#[derive(Debug)]
+pub(super) struct DADKUserTaskType {
+    pub(super) config_file: PathBuf,
+    pub(super) task_type: String,
+    pub(super) source: String,
+    pub(super) source_path: String,
+    // git独有
+    pub(super) revision: Option<String>,
+    pub(super) branch: Option<String>,
+}
+
+impl DADKUserTaskType {
+    fn parse(
+        config_file: &PathBuf,
+        table: &toml::value::Table,
+    ) -> Result<DADKUserTaskType, ParserError> {
+        let task_type_table =
+            TomlValueParser::parse_table(config_file, table, DADKUserConfigKey::TaskType.into())?;
+        let task_type = TomlValueParser::parse_string(
+            config_file,
+            &task_type_table,
+            DADKUserConfigKey::Type.into(),
+        )?;
+        let source = TomlValueParser::parse_string(
+            config_file,
+            &task_type_table,
+            DADKUserConfigKey::Source.into(),
+        )?;
+
+        let source_path = TomlValueParser::parse_string(
+            config_file,
+            &task_type_table,
+            DADKUserConfigKey::SourcePath.into(),
+        )?;
+
+        let (branch, revision) =
+            if source.to_lowercase().trim() == Into::<&str>::into(DADKUserConfigKey::Git) {
+                let branch = TomlValueParser::parse_option_string(
+                    &task_type_table,
+                    DADKUserConfigKey::Branch.into(),
+                );
+                let revision = TomlValueParser::parse_option_string(
+                    &task_type_table,
+                    DADKUserConfigKey::Revision.into(),
+                );
+                (branch, revision)
+            } else {
+                (None, None)
+            };
+
+        Ok(Self {
+            config_file: config_file.clone(),
+            task_type,
+            source,
+            source_path,
+            revision,
+            branch,
+        })
+    }
+}
+
+/// build配置
+#[derive(Debug)]
+pub(super) struct DADKUserBuildConfig {
+    pub(super) build_command: Option<String>,
+}
+
+impl DADKUserBuildConfig {
+    fn parse(
+        config_file: &PathBuf,
+        table: &toml::value::Table,
+    ) -> Result<DADKUserBuildConfig, ParserError> {
+        let build_table =
+            TomlValueParser::parse_table(config_file, table, DADKUserConfigKey::Build.into())?;
+        let build_command = TomlValueParser::parse_option_string(
+            &build_table,
+            DADKUserConfigKey::BuildCommand.into(),
+        );
+        Ok(Self { build_command })
+    }
+}
+
+/// install配置
+#[derive(Debug)]
+pub(super) struct DADKUserInstallConfig {
+    pub(super) in_dragonos_path: Option<PathBuf>,
+}
+
+impl DADKUserInstallConfig {
+    fn parse(
+        config_file: &PathBuf,
+        table: &toml::value::Table,
+    ) -> Result<DADKUserInstallConfig, ParserError> {
+        let install_table =
+            TomlValueParser::parse_table(config_file, table, DADKUserConfigKey::Install.into())?;
+        let in_dragonos_path = TomlValueParser::parse_option_string(
+            &install_table,
+            DADKUserConfigKey::InDragonosPath.into(),
+        )
+        .map(|path| PathBuf::from(path));
+
+        Ok(Self { in_dragonos_path })
+    }
+}
+
+/// clean配置
+#[derive(Debug)]
+pub(super) struct DADKUserCleanConfig {
+    pub(super) clean_command: Option<String>,
+}
+
+impl DADKUserCleanConfig {
+    fn parse(
+        config_file: &PathBuf,
+        table: &toml::value::Table,
+    ) -> Result<DADKUserCleanConfig, ParserError> {
+        let clean_table =
+            TomlValueParser::parse_table(config_file, table, DADKUserConfigKey::Clean.into())?;
+        let clean_command = TomlValueParser::parse_option_string(
+            &clean_table,
+            DADKUserConfigKey::CleanCommand.into(),
+        );
+        Ok(Self { clean_command })
+    }
+}
+
+/// depends配置
+#[derive(Debug)]
+pub(super) struct DADKUserDependsConfig {
+    pub(super) depends: Vec<Dependency>,
+}
+
+impl DADKUserDependsConfig {
+    fn parse(
+        config_file: &PathBuf,
+        table: &toml::value::Table,
+    ) -> Result<DADKUserDependsConfig, ParserError> {
+        let depends_table =
+            TomlValueParser::parse_table(config_file, table, DADKUserConfigKey::Depends.into())?;
+        let depends = depends_table
+            .iter()
+            .map(|(key, value)| Dependency {
+                name: key.clone(),
+                version: value.as_str().unwrap().to_string(),
+            })
+            .collect::<Vec<Dependency>>();
+        Ok(Self { depends })
+    }
+}
+/// envs配置
+#[derive(Debug)]
+pub(super) struct DADKUserEnvsConfig {
+    pub(super) envs: Option<Vec<TaskEnv>>,
+}
+
+impl DADKUserEnvsConfig {
+    fn parse(
+        config_file: &PathBuf,
+        table: &toml::value::Table,
+    ) -> Result<DADKUserEnvsConfig, ParserError> {
+        let envs_table: toml::map::Map<String, Value> =
+            TomlValueParser::parse_table(config_file, table, DADKUserConfigKey::Envs.into())?;
+        let envs_vec = if !envs_table.is_empty() {
+            Some(
+                envs_table
+                    .iter()
+                    .map(|(key, value)| TaskEnv {
+                        key: key.clone(),
+                        value: value.as_str().unwrap().to_string(),
+                    })
+                    .collect::<Vec<TaskEnv>>(),
+            )
+        } else {
+            None
+        };
+
+        Ok(DADKUserEnvsConfig { envs: envs_vec })
+    }
+}
+
+struct TomlValueParser;
+
+impl TomlValueParser {
+    // 解析String类型的值
+    fn parse_string(
+        config_file: &PathBuf,
+        table: &toml::value::Table,
+        key: &'static str,
+    ) -> Result<String, ParserError> {
+        let value = table.get(key).ok_or(ParserError {
+            config_file: Some(config_file.clone()),
+            error: InnerParserError::TomlError(toml::de::Error::missing_field(key)),
+        })?;
+        Ok(value.as_str().unwrap().to_string())
+    }
+
+    // 解析Option<String>类型的值
+    fn parse_option_string(table: &toml::value::Table, key: &'static str) -> Option<String> {
+        let value = table.get(key);
+        value.map(|v| v.as_str().unwrap().to_string())
+    }
+
+    // 解析Table类型的值
+    fn parse_table(
+        config_file: &PathBuf,
+        table: &toml::value::Table,
+        key: &'static str,
+    ) -> Result<toml::value::Table, ParserError> {
+        let value = table.get(key).ok_or(ParserError {
+            config_file: Some(config_file.clone()),
+            error: InnerParserError::TomlError(toml::de::Error::missing_field(key)),
+        })?;
+        let table = value.as_table().ok_or(ParserError {
+            config_file: Some(config_file.clone()),
+            error: InnerParserError::TomlError(toml::de::Error::custom(format!(
+                "{} is not a table",
+                key
+            ))),
+        })?;
+        Ok(table.clone())
+    }
+
+    #[allow(dead_code)]
+    // 解析Array类型的值
+    fn parse_array(
+        config_file: &PathBuf,
+        table: &toml::value::Table,
+        key: &'static str,
+    ) -> Result<Vec<toml::Value>, ParserError> {
+        let value = table.get(key).ok_or(ParserError {
+            config_file: Some(config_file.clone()),
+            error: InnerParserError::TomlError(toml::de::Error::missing_field(key)),
+        })?;
+        let array = value.as_array().ok_or(ParserError {
+            config_file: Some(config_file.clone()),
+            error: InnerParserError::TomlError(toml::de::Error::custom(format!(
+                "{} is not an array",
+                key
+            ))),
+        })?;
+        Ok(array.clone())
+    }
+
+    // 解析Option<Array>类型的值
+    fn parse_option_array(
+        table: &toml::value::Table,
+        key: &'static str,
+    ) -> Option<Vec<toml::Value>> {
+        let value = table.get(key);
+        value.map(|v| v.as_array().unwrap().clone())
+    }
+
+    // 解析Boolean类型的值
+    fn parse_bool(
+        config_file: &PathBuf,
+        table: &toml::value::Table,
+        key: &'static str,
+    ) -> Result<bool, ParserError> {
+        let value = table.get(key).ok_or(ParserError {
+            config_file: Some(config_file.clone()),
+            error: InnerParserError::TomlError(toml::de::Error::missing_field(key)),
+        })?;
+        let boolean = value.as_bool().ok_or(ParserError {
+            config_file: Some(config_file.clone()),
+            error: InnerParserError::TomlError(toml::de::Error::custom(format!(
+                "{} is not a boolean",
+                key
+            ))),
+        })?;
+        Ok(boolean)
+    }
+}

+ 75 - 31
dadk-user/src/parser/mod.rs

@@ -3,7 +3,7 @@
 //! 用于解析配置文件,生成任务列表
 //!
 //! 您需要指定一个配置文件目录,解析器会自动解析该目录下的所有配置文件。
-//! 软件包的配置文件必须以`.dadk`作为后缀名,内容格式为json。
+//! 软件包的配置文件格式为toml
 //!
 //! ## 简介
 //!
@@ -11,31 +11,52 @@
 //!
 //! ## 配置文件格式
 //!
-//! ```json
-//! {
-//!     "name": "软件包名称",
-//!     "version": "软件包版本",
-//!     "description": "软件包描述",
-//!     "task_type": {任务类型(该部分详见`TaskType`的文档)},
-//!     "depends": [{依赖项(该部分详见Dependency的文档)}],
-//!     "build": {构建配置(该部分详见BuildConfig的文档)},
-//!     "install": {安装配置(该部分详见InstallConfig的文档)},
-//!     "envs" : [{ "key": "环境变量名", "value": "环境变量值" }]
-//!     "build_once": (可选) 是否只构建一次,如果为true,DADK会在构建成功后,将构建结果缓存起来,下次构建时,直接使用缓存的构建结果。
-//! }
+//! ```toml
+//! name = "test_git"
+//! version = "0.1.0"
+//! description = ""
+//! build_once = true
+//! install_once = true
+//! target_arch = ["x86_64"]
+//!
+//! [task_type]
+//! type = "build_from_source"
+//! source = "git"
+//! source_path = "https://git.mirrors.dragonos.org.cn/DragonOS-Community/test_git.git"
+//! revison = "01cdc56863"
+//! branch = "test"
+//!
+//! [build]
+//! build-command = "make instal"
+//!
+//! [install]
+//! in_dragonos_path = "/bin"
+//!
+//! [clean]
+//! clean-command = "make clean"
+//!
+//! [depends]
+//! depend1 = "0.1.1"
+//! depend2 = "0.1.2"
+//!
+//! [envs]
+//! PATH = "/usr/bin"
+//! LD_LIBRARY_PATH = "/usr/lib"
+
 use std::{
     fmt::Debug,
     fs::{DirEntry, ReadDir},
     path::PathBuf,
 };
 
-use log::{debug, error, info};
-
 use self::task::DADKTask;
+use config::DADKUserConfig;
+use log::{debug, error, info};
+use task::{BuildConfig, CleanConfig, InstallConfig, TaskType};
+use toml::Table;
+mod config;
 pub mod task;
 pub mod task_log;
-#[cfg(test)]
-mod tests;
 
 /// # 配置解析器
 ///
@@ -67,16 +88,16 @@ impl Debug for ParserError {
                     write!(f, "IO Error while parsing config files: {}", e)
                 }
             }
-            InnerParserError::JsonError(e) => {
+            InnerParserError::TomlError(e) => {
                 if let Some(config_file) = &self.config_file {
                     write!(
                         f,
-                        "Json Error while parsing config file {}: {}",
+                        "Toml Error while parsing config file {}: {}",
                         config_file.display(),
                         e
                     )
                 } else {
-                    write!(f, "Json Error while parsing config file: {}", e)
+                    write!(f, "Toml Error while parsing config file: {}", e)
                 }
             }
             InnerParserError::TaskError(e) => {
@@ -98,7 +119,7 @@ impl Debug for ParserError {
 #[derive(Debug)]
 pub enum InnerParserError {
     IoError(std::io::Error),
-    JsonError(serde_json::Error),
+    TomlError(toml::de::Error),
     TaskError(String),
 }
 
@@ -203,16 +224,8 @@ impl Parser {
     /// * `Ok(DADKTask)` - 生成好的任务
     /// * `Err(ParserError)` - 解析错误
     pub(super) fn parse_config_file(&self, config_file: &PathBuf) -> Result<DADKTask, ParserError> {
-        let content = std::fs::read_to_string(config_file).map_err(|e| ParserError {
-            config_file: Some(config_file.clone()),
-            error: InnerParserError::IoError(e),
-        })?;
-
-        // 从json字符串中解析出DADKTask
-        let mut task: DADKTask = serde_json::from_str(&content).map_err(|e| ParserError {
-            config_file: Some(config_file.clone()),
-            error: InnerParserError::JsonError(e),
-        })?;
+        // 从toml文件中解析出DADKTask
+        let mut task: DADKTask = Self::parse_toml_file(config_file)?;
 
         debug!("Parsed config file {}: {:?}", config_file.display(), task);
 
@@ -227,4 +240,35 @@ impl Parser {
 
         return Ok(task);
     }
+
+    /// 解析toml文件,生成DADKTask
+    pub fn parse_toml_file(config_file: &PathBuf) -> Result<DADKTask, ParserError> {
+        let content = std::fs::read_to_string(config_file).map_err(|e| ParserError {
+            config_file: Some(config_file.clone()),
+            error: InnerParserError::IoError(e),
+        })?;
+
+        let table = content.parse::<Table>().map_err(|e| ParserError {
+            config_file: Some(config_file.clone()),
+            error: InnerParserError::TomlError(e),
+        })?;
+
+        let dadk_user_config = DADKUserConfig::parse(config_file, &table)?;
+
+        Ok(DADKTask {
+            name: dadk_user_config.standard_config.name,
+            version: dadk_user_config.standard_config.version,
+            description: dadk_user_config.standard_config.description,
+            rust_target: dadk_user_config.standard_config.rust_target,
+            task_type: TaskType::try_from(dadk_user_config.task_type_config)?,
+            depends: dadk_user_config.depends_config.depends,
+            build: BuildConfig::from(dadk_user_config.build_config),
+            install: InstallConfig::from(dadk_user_config.install_config),
+            clean: CleanConfig::from(dadk_user_config.clean_config),
+            envs: dadk_user_config.envs_config.envs,
+            build_once: dadk_user_config.standard_config.build_once,
+            install_once: dadk_user_config.standard_config.install_once,
+            target_arch: dadk_user_config.standard_config.target_arch,
+        })
+    }
 }

+ 120 - 4
dadk-user/src/parser/task.rs

@@ -1,9 +1,17 @@
 use std::path::PathBuf;
 
-use serde::{Deserialize, Deserializer, Serialize};
+use serde::{de::Error, Deserialize, Deserializer, Serialize};
 
 use crate::executor::source::{ArchiveSource, GitSource, LocalSource};
 
+use super::{
+    config::{
+        DADKUserBuildConfig, DADKUserCleanConfig, DADKUserConfigKey, DADKUserInstallConfig,
+        DADKUserTaskType,
+    },
+    InnerParserError, ParserError,
+};
+
 // 对于生成的包名和版本号,需要进行替换的字符。
 pub static NAME_VERSION_REPLACE_TABLE: [(&str, &str); 6] = [
     (" ", "_"),
@@ -230,6 +238,24 @@ impl DADKTask {
     }
 }
 
+impl PartialEq for DADKTask {
+    fn eq(&self, other: &Self) -> bool {
+        self.name == other.name
+            && self.version == other.version
+            && self.description == other.description
+            && self.rust_target == other.rust_target
+            && self.build_once == other.build_once
+            && self.install_once == other.install_once
+            && self.target_arch == other.target_arch
+            && self.task_type == other.task_type
+            && self.build == other.build
+            && self.install == other.install
+            && self.clean == other.clean
+            && self.depends == other.depends
+            && self.envs == other.envs
+    }
+}
+
 /// @brief 构建配置
 #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
 pub struct BuildConfig {
@@ -254,6 +280,14 @@ impl BuildConfig {
     }
 }
 
+impl From<DADKUserBuildConfig> for BuildConfig {
+    fn from(value: DADKUserBuildConfig) -> Self {
+        return BuildConfig {
+            build_command: value.build_command,
+        };
+    }
+}
+
 #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
 pub struct InstallConfig {
     /// 安装到DragonOS内的目录
@@ -279,6 +313,14 @@ impl InstallConfig {
     pub fn trim(&mut self) {}
 }
 
+impl From<DADKUserInstallConfig> for InstallConfig {
+    fn from(value: DADKUserInstallConfig) -> Self {
+        return InstallConfig {
+            in_dragonos_path: (value.in_dragonos_path),
+        };
+    }
+}
+
 /// # 清理配置
 #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
 pub struct CleanConfig {
@@ -303,8 +345,16 @@ impl CleanConfig {
     }
 }
 
+impl From<DADKUserCleanConfig> for CleanConfig {
+    fn from(value: DADKUserCleanConfig) -> Self {
+        return CleanConfig {
+            clean_command: value.clean_command,
+        };
+    }
+}
+
 /// @brief 依赖项
-#[derive(Debug, Clone, Serialize, Deserialize)]
+#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)]
 pub struct Dependency {
     pub name: String,
     pub version: String,
@@ -361,6 +411,72 @@ impl TaskType {
     }
 }
 
+impl TryFrom<DADKUserTaskType> for TaskType {
+    type Error = ParserError;
+    fn try_from(dadk_user_task_type: DADKUserTaskType) -> Result<Self, Self::Error> {
+        let task_type = DADKUserConfigKey::try_from(dadk_user_task_type.task_type.as_str())
+            .map_err(|mut e| {
+                e.config_file = Some(dadk_user_task_type.config_file.clone());
+                e
+            })?;
+
+        let source =
+            DADKUserConfigKey::try_from(dadk_user_task_type.source.as_str()).map_err(|mut e| {
+                e.config_file = Some(dadk_user_task_type.config_file.clone());
+                e
+            })?;
+
+        match task_type {
+            DADKUserConfigKey::BuildFromSource => match source {
+                DADKUserConfigKey::Git => {
+                    Ok(TaskType::BuildFromSource(CodeSource::Git(GitSource::new(
+                        dadk_user_task_type.source_path,
+                        dadk_user_task_type.branch,
+                        dadk_user_task_type.revision,
+                    ))))
+                }
+                DADKUserConfigKey::Local => Ok(TaskType::BuildFromSource(CodeSource::Local(
+                    LocalSource::new(PathBuf::from(dadk_user_task_type.source_path)),
+                ))),
+                DADKUserConfigKey::Archive => Ok(TaskType::BuildFromSource(CodeSource::Archive(
+                    ArchiveSource::new(dadk_user_task_type.source_path),
+                ))),
+                _ => Err(ParserError {
+                    config_file: Some(dadk_user_task_type.config_file),
+                    error: InnerParserError::TomlError(toml::de::Error::custom(format!(
+                        "Unknown source: {}",
+                        dadk_user_task_type.source
+                    ))),
+                }),
+            },
+            DADKUserConfigKey::InstallFromPrebuilt => match source {
+                DADKUserConfigKey::Local => {
+                    Ok(TaskType::InstallFromPrebuilt(PrebuiltSource::Local(
+                        LocalSource::new(PathBuf::from(dadk_user_task_type.source_path)),
+                    )))
+                }
+                DADKUserConfigKey::Archive => Ok(TaskType::InstallFromPrebuilt(
+                    PrebuiltSource::Archive(ArchiveSource::new(dadk_user_task_type.source_path)),
+                )),
+                _ => Err(ParserError {
+                    config_file: Some(dadk_user_task_type.config_file),
+                    error: InnerParserError::TomlError(toml::de::Error::custom(format!(
+                        "Unknown source: {}",
+                        dadk_user_task_type.source
+                    ))),
+                }),
+            },
+            _ => Err(ParserError {
+                config_file: Some(dadk_user_task_type.config_file),
+                error: InnerParserError::TomlError(toml::de::Error::custom(format!(
+                    "Unknown task type: {}",
+                    dadk_user_task_type.task_type
+                ))),
+            }),
+        }
+    }
+}
+
 /// # 代码源
 #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
 pub enum CodeSource {
@@ -417,7 +533,7 @@ impl PrebuiltSource {
 /// # 任务环境变量
 ///
 /// 任务执行时的环境变量.这个环境变量是在当前任务执行时设置的,不会影响到其他任务
-#[derive(Debug, Clone, Serialize, Deserialize)]
+#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)]
 pub struct TaskEnv {
     pub key: String,
     pub value: String,
@@ -451,7 +567,7 @@ impl TaskEnv {
 }
 
 /// 目标处理器架构
-#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
 pub enum TargetArch {
     Aarch64,
     X86_64,

+ 0 - 104
dadk-user/src/parser/tests.rs

@@ -1,104 +0,0 @@
-use test_base::{
-    global::BaseGlobalTestContext,
-    test_context::{self as test_context, test_context},
-};
-use tests::task::{BuildConfig, TargetArch, TaskType};
-
-use crate::executor::source::LocalSource;
-
-use super::*;
-
-#[test_context(BaseGlobalTestContext)]
-#[test]
-fn parse_normal_v1(ctx: &mut BaseGlobalTestContext) {
-    let parser = Parser::new(ctx.config_v1_dir());
-    let config_file = ctx.config_v1_dir().join("app_normal_0_1_0.dadk");
-    let result = parser.parse_config_file(&config_file);
-
-    assert!(result.is_ok(), "Error: {:?}", result);
-
-    let result = result.unwrap();
-
-    assert_eq!(result.name, "app_normal");
-    assert_eq!(result.version, "0.1.0");
-    assert_eq!(result.description, "A normal app");
-
-    let expected_task_type = TaskType::BuildFromSource(task::CodeSource::Local(LocalSource::new(
-        PathBuf::from("tests/data/apps/app_normal"),
-    )));
-
-    assert_eq!(result.task_type, expected_task_type,);
-
-    assert_eq!(result.depends.len(), 0);
-
-    let expected_build_config: BuildConfig = BuildConfig::new(Some("bash build.sh".to_string()));
-    assert_eq!(result.build, expected_build_config);
-
-    let expected_install_config = task::InstallConfig::new(Some(PathBuf::from("/")));
-
-    assert_eq!(result.install, expected_install_config);
-    let expected_clean_config = task::CleanConfig::new(None);
-
-    assert_eq!(result.clean, expected_clean_config);
-
-    assert!(result.envs.is_some());
-    assert_eq!(result.envs.as_ref().unwrap().len(), 0);
-
-    assert_eq!(result.build_once, false);
-    assert_eq!(result.install_once, false);
-}
-
-#[test_context(BaseGlobalTestContext)]
-#[test]
-fn target_arch_field_has_one_v1(ctx: &mut BaseGlobalTestContext) {
-    let parser = Parser::new(ctx.config_v1_dir());
-    let config_file = ctx
-        .config_v1_dir()
-        .join("app_target_arch_x86_64_0_1_0.dadk");
-    let result = parser.parse_config_file(&config_file);
-
-    assert!(result.is_ok(), "Error: {:?}", result);
-
-    let result = result.unwrap();
-
-    assert_eq!(result.name, "app_target_arch_x86_64");
-    assert_eq!(result.version, "0.1.0");
-
-    assert_eq!(result.target_arch.len(), 1);
-    assert_eq!(result.target_arch[0], TargetArch::X86_64);
-}
-
-#[test_context(BaseGlobalTestContext)]
-#[test]
-fn target_arch_field_has_one_uppercase_v1(ctx: &mut BaseGlobalTestContext) {
-    let parser = Parser::new(ctx.config_v1_dir());
-    let config_file = ctx
-        .config_v1_dir()
-        .join("app_target_arch_x86_64_uppercase_0_1_0.dadk");
-    let result = parser.parse_config_file(&config_file);
-
-    assert!(result.is_ok(), "Error: {:?}", result);
-
-    let result = result.unwrap();
-
-    assert_eq!(result.name, "app_target_arch_x86_64_uppercase");
-    assert_eq!(result.version, "0.1.0");
-
-    assert_eq!(result.target_arch.len(), 1);
-    assert_eq!(result.target_arch[0], TargetArch::X86_64);
-}
-
-#[test_context(BaseGlobalTestContext)]
-#[test]
-fn target_arch_field_empty_should_failed_v1(ctx: &mut BaseGlobalTestContext) {
-    let parser = Parser::new(ctx.config_v1_dir());
-    let config_file = ctx
-        .config_v1_dir()
-        .join("app_target_arch_empty_should_fail_0_1_0.dadk");
-    let result = parser.parse_config_file(&config_file);
-
-    assert!(
-        result.is_err(),
-        "parse_config_file should return error when target_arch field in config file is empty"
-    );
-}

+ 10 - 10
dadk-user/src/scheduler/tests.rs

@@ -18,8 +18,8 @@ use super::*;
 fn should_not_run_task_only_riscv64_on_x86_64(ctx: &DadkExecuteContextTestBuildX86_64V1) {
     let config_file = ctx
         .base_context()
-        .config_v1_dir()
-        .join("app_target_arch_riscv64_only_0_1_0.dadk");
+        .config_v2_dir()
+        .join("app_target_arch_riscv64_only_0_2_0.toml");
     let task = Parser::new(ctx.base_context().config_v1_dir()).parse_config_file(&config_file);
     assert!(task.is_ok(), "parse error: {:?}", task);
     let task = task.unwrap();
@@ -59,8 +59,8 @@ fn should_not_run_task_only_riscv64_on_x86_64(ctx: &DadkExecuteContextTestBuildX
 fn should_not_run_task_only_x86_64_on_riscv64(ctx: &DadkExecuteContextTestBuildRiscV64V1) {
     let config_file = ctx
         .base_context()
-        .config_v1_dir()
-        .join("app_target_arch_x86_64_only_0_1_0.dadk");
+        .config_v2_dir()
+        .join("app_target_arch_x86_64_only_0_2_0.toml");
     let task = Parser::new(ctx.base_context().config_v1_dir()).parse_config_file(&config_file);
     assert!(task.is_ok(), "parse error: {:?}", task);
     let task = task.unwrap();
@@ -100,8 +100,8 @@ fn should_not_run_task_only_x86_64_on_riscv64(ctx: &DadkExecuteContextTestBuildR
 fn should_run_task_include_x86_64_on_x86_64(ctx: &DadkExecuteContextTestBuildX86_64V1) {
     let config_file = ctx
         .base_context()
-        .config_v1_dir()
-        .join("app_all_target_arch_0_1_0.dadk");
+        .config_v2_dir()
+        .join("app_all_target_arch_0_2_0.toml");
     let task = Parser::new(ctx.base_context().config_v1_dir()).parse_config_file(&config_file);
     assert!(task.is_ok(), "parse error: {:?}", task);
     let task = task.unwrap();
@@ -133,8 +133,8 @@ fn should_run_task_include_x86_64_on_x86_64(ctx: &DadkExecuteContextTestBuildX86
 fn should_run_task_include_riscv64_on_riscv64(ctx: &DadkExecuteContextTestBuildRiscV64V1) {
     let config_file = ctx
         .base_context()
-        .config_v1_dir()
-        .join("app_all_target_arch_0_1_0.dadk");
+        .config_v2_dir()
+        .join("app_all_target_arch_0_2_0.toml");
     let task = Parser::new(ctx.base_context().config_v1_dir()).parse_config_file(&config_file);
     assert!(task.is_ok(), "parse error: {:?}", task);
     let task = task.unwrap();
@@ -160,11 +160,11 @@ fn should_run_task_include_riscv64_on_riscv64(ctx: &DadkExecuteContextTestBuildR
     assert!(entity.is_ok(), "Add task should return ok: {:?}", entity);
 }
 
-/// 确保文件 app_all_target_arch_0_1_0.dadk 包含了所有的目标架构
+/// 确保文件 app_all_target_arch_0_2_0.toml 包含了所有的目标架构
 #[test_context(BaseGlobalTestContext)]
 #[test]
 fn ensure_all_target_arch_testcase_v1(ctx: &BaseGlobalTestContext) {
-    let config_file = ctx.config_v1_dir().join("app_all_target_arch_0_1_0.dadk");
+    let config_file = ctx.config_v2_dir().join("app_all_target_arch_0_2_0.toml");
     let task = Parser::new(ctx.config_v1_dir()).parse_config_file(&config_file);
     assert!(task.is_ok(), "parse error: {:?}", task);
     let task = task.unwrap();

+ 28 - 0
dadk-user/templates/config/build_from_source/test_archive.toml

@@ -0,0 +1,28 @@
+name = "test_archive"
+version = "0.2.0"
+description = ""
+build-once = true
+install-once = true
+target-arch = ["x86_64"]
+
+[task-type]
+type = "build_from_source"
+source = "archive"
+source-path = "https://url"
+
+[build]
+build-command = "make install"
+
+[install]
+in-dragonos-path = "/bin"
+
+[clean]
+clean-command = "make clean"
+
+[depends]
+depend1 = "0.1.1"
+depend2 = "0.1.2"
+
+[envs]
+PATH = "/usr/bin"
+LD_LIBRARY_PATH = "/usr/lib"

+ 30 - 0
dadk-user/templates/config/build_from_source/test_git.toml

@@ -0,0 +1,30 @@
+name = "test_git"
+version = "0.2.0"
+description = ""
+build-once = true
+install-once = true
+target-arch = ["x86_64"]
+
+[task-type]
+type = "build_from_source"
+source = "git"
+source-path = "https://git.mirrors.dragonos.org.cn/DragonOS-Community/test_git.git"
+revison = "01cdc56863"
+branch = "test"
+
+[build]
+build-command = "make install"
+
+[install]
+in-dragonos-path = "/bin"
+
+[clean]
+clean-command = "make clean"
+
+[depends]
+depend1 = "0.1.1"
+depend2 = "0.1.2"
+
+[envs]
+PATH = "/usr/bin"
+LD_LIBRARY_PATH = "/usr/lib"

+ 28 - 0
dadk-user/templates/config/build_from_source/test_local.toml

@@ -0,0 +1,28 @@
+name = "test_local"
+version = "0.2.0"
+description = ""
+build-once = true
+install-once = true
+target-arch = ["x86_64"]
+
+[task-type]
+type = "build_from_source"
+source = "local"
+source-path = "apps/test"
+
+[build]
+build-command = "make install"
+
+[install]
+in-dragonos-path = "/bin"
+
+[clean]
+clean-command = "make clean"
+
+[depends]
+depend1 = "0.1.1"
+depend2 = "0.1.2"
+
+[envs]
+PATH = "/usr/bin"
+LD_LIBRARY_PATH = "/usr/lib"

+ 29 - 0
dadk-user/templates/config/install_from_prebuilt/test_archive.toml

@@ -0,0 +1,29 @@
+name = "test_archive"
+version = "0.2.0"
+description = ""
+build-once = true
+install-once = true
+target-arch = ["x86_64"]
+
+[task-type]
+type = "install_from_prebuilt"
+source = "archive"
+source-path = "https://url"
+
+
+[build]
+build-command = "make install"
+
+[install]
+in-dragonos-path = "/bin"
+
+[clean]
+clean-command = "make clean"
+
+[depends]
+depend1 = "0.1.1"
+depend2 = "0.1.2"
+
+[envs]
+PATH = "/usr/bin"
+LD_LIBRARY_PATH = "/usr/lib"

+ 29 - 0
dadk-user/templates/config/install_from_prebuilt/test_local.toml

@@ -0,0 +1,29 @@
+name = "test_local"
+version = "0.2.0"
+description = ""
+build-once = true
+install-once = true
+target-arch = ["x86_64"]
+
+[task-type]
+type = "install_from_prebuilt"
+source = "local"
+source-path = "/home/dev/demo"
+
+
+[build]
+build-command = "make install"
+
+[install]
+in-dragonos-path = "/bin"
+
+[clean]
+clean-command = "make clean"
+
+[depends]
+depend1 = "0.1.1"
+depend2 = "0.1.2"
+
+[envs]
+PATH = "/usr/bin"
+LD_LIBRARY_PATH = "/usr/lib"

+ 285 - 0
dadk-user/tests/test_parse_dadk_user_config.rs

@@ -0,0 +1,285 @@
+use std::path::PathBuf;
+
+use dadk_user::{
+    executor::source::{ArchiveSource, GitSource, LocalSource},
+    parser::{
+        task::{
+            BuildConfig, CleanConfig, CodeSource, DADKTask, Dependency, InstallConfig,
+            PrebuiltSource, TargetArch, TaskEnv, TaskType,
+        },
+        Parser,
+    },
+};
+use test_base::{
+    dadk_user::DadkUserTestContext,
+    test_context::{self as test_context, test_context},
+};
+
+const DADK_USER_TEST_BUILD_LOCAL: &str = "build_from_source/test_local.toml";
+const DADK_USER_TEST_BUILD_GIT: &str = "build_from_source/test_git.toml";
+const DADK_USER_TEST_BUILD_ARCHIVE: &str = "build_from_source/test_archive.toml";
+const DADK_USER_TEST_INSTALL_LOCAL: &str = "install_from_prebuilt/test_local.toml";
+const DADK_USER_TEST_INSTALL_ARCHIVE: &str = "install_from_prebuilt/test_archive.toml";
+
+/// 测试解析DADK用户配置文件
+#[test_context(DadkUserTestContext)]
+#[test]
+fn test_parse_dadk_user_config_build_local(ctx: &mut DadkUserTestContext) {
+    let config_file = ctx.templates_dir().join(DADK_USER_TEST_BUILD_LOCAL);
+    assert!(config_file.exists());
+    assert!(config_file.is_file());
+    let r = Parser::parse_toml_file(&config_file);
+    assert!(r.is_ok());
+    let mut parsed_dadk_task = r.unwrap();
+    let mut expected_dadk_task = DADKTask {
+        name: "test_local".to_string(),
+        version: "0.2.0".to_string(),
+        description: "".to_string(),
+        build_once: true,
+        install_once: true,
+        task_type: TaskType::BuildFromSource(CodeSource::Local(LocalSource::new(PathBuf::from(
+            "apps/test",
+        )))),
+        rust_target: None,
+        depends: vec![
+            Dependency {
+                name: "depend1".to_string(),
+                version: "0.1.1".to_string(),
+            },
+            Dependency {
+                name: "depend2".to_string(),
+                version: "0.1.2".to_string(),
+            },
+        ],
+        build: BuildConfig::new(Some("make install".to_string())),
+        install: InstallConfig::new(Some(PathBuf::from("/bin"))),
+        clean: CleanConfig::new(Some("make clean".to_string())),
+        envs: Some(vec![
+            TaskEnv::new("PATH".to_string(), "/usr/bin".to_string()),
+            TaskEnv::new("LD_LIBRARY_PATH".to_string(), "/usr/lib".to_string()),
+        ]),
+        target_arch: vec![TargetArch::try_from("x86_64").unwrap()],
+    };
+
+    parsed_dadk_task.target_arch.sort();
+    expected_dadk_task.target_arch.sort();
+    parsed_dadk_task.depends.sort();
+    expected_dadk_task.depends.sort();
+    if let Some(envs) = &mut parsed_dadk_task.envs {
+        envs.sort();
+    }
+    if let Some(envs) = &mut expected_dadk_task.envs {
+        envs.sort();
+    }
+    assert_eq!(parsed_dadk_task, expected_dadk_task)
+}
+
+#[test_context(DadkUserTestContext)]
+#[test]
+fn test_parse_dadk_user_config_build_git(ctx: &mut DadkUserTestContext) {
+    let config_file = ctx.templates_dir().join(DADK_USER_TEST_BUILD_GIT);
+    assert!(config_file.exists());
+    assert!(config_file.is_file());
+    let r = Parser::parse_toml_file(&config_file);
+    assert!(r.is_ok());
+    let mut parsed_dadk_task = r.unwrap();
+    let mut expected_dadk_task = DADKTask {
+        name: "test_git".to_string(),
+        version: "0.2.0".to_string(),
+        description: "".to_string(),
+        build_once: true,
+        install_once: true,
+        task_type: TaskType::BuildFromSource(CodeSource::Git(GitSource::new(
+            "https://git.mirrors.dragonos.org.cn/DragonOS-Community/test_git.git".to_string(),
+            Some("test".to_string()),
+            Some("01cdc56863".to_string()),
+        ))),
+        rust_target: None,
+        depends: vec![
+            Dependency {
+                name: "depend1".to_string(),
+                version: "0.1.1".to_string(),
+            },
+            Dependency {
+                name: "depend2".to_string(),
+                version: "0.1.2".to_string(),
+            },
+        ],
+        build: BuildConfig::new(Some("make install".to_string())),
+        install: InstallConfig::new(Some(PathBuf::from("/bin"))),
+        clean: CleanConfig::new(Some("make clean".to_string())),
+        envs: Some(vec![
+            TaskEnv::new("PATH".to_string(), "/usr/bin".to_string()),
+            TaskEnv::new("LD_LIBRARY_PATH".to_string(), "/usr/lib".to_string()),
+        ]),
+        target_arch: vec![TargetArch::try_from("x86_64").unwrap()],
+    };
+
+    parsed_dadk_task.target_arch.sort();
+    expected_dadk_task.target_arch.sort();
+    parsed_dadk_task.depends.sort();
+    expected_dadk_task.depends.sort();
+    if let Some(envs) = &mut parsed_dadk_task.envs {
+        envs.sort();
+    }
+    if let Some(envs) = &mut expected_dadk_task.envs {
+        envs.sort();
+    }
+    assert_eq!(parsed_dadk_task, expected_dadk_task)
+}
+
+#[test_context(DadkUserTestContext)]
+#[test]
+fn test_parse_dadk_user_config_build_archive(ctx: &mut DadkUserTestContext) {
+    let config_file = ctx.templates_dir().join(DADK_USER_TEST_BUILD_ARCHIVE);
+    assert!(config_file.exists());
+    assert!(config_file.is_file());
+    let r = Parser::parse_toml_file(&config_file);
+    assert!(r.is_ok());
+    let mut parsed_dadk_task = r.unwrap();
+    let mut expected_dadk_task = DADKTask {
+        name: "test_archive".to_string(),
+        version: "0.2.0".to_string(),
+        description: "".to_string(),
+        build_once: true,
+        install_once: true,
+        task_type: TaskType::BuildFromSource(CodeSource::Archive(ArchiveSource::new(
+            "https://url".to_string(),
+        ))),
+        rust_target: None,
+        depends: vec![
+            Dependency {
+                name: "depend1".to_string(),
+                version: "0.1.1".to_string(),
+            },
+            Dependency {
+                name: "depend2".to_string(),
+                version: "0.1.2".to_string(),
+            },
+        ],
+        build: BuildConfig::new(Some("make install".to_string())),
+        install: InstallConfig::new(Some(PathBuf::from("/bin"))),
+        clean: CleanConfig::new(Some("make clean".to_string())),
+        envs: Some(vec![
+            TaskEnv::new("PATH".to_string(), "/usr/bin".to_string()),
+            TaskEnv::new("LD_LIBRARY_PATH".to_string(), "/usr/lib".to_string()),
+        ]),
+        target_arch: vec![TargetArch::try_from("x86_64").unwrap()],
+    };
+
+    parsed_dadk_task.target_arch.sort();
+    expected_dadk_task.target_arch.sort();
+    parsed_dadk_task.depends.sort();
+    expected_dadk_task.depends.sort();
+    if let Some(envs) = &mut parsed_dadk_task.envs {
+        envs.sort();
+    }
+    if let Some(envs) = &mut expected_dadk_task.envs {
+        envs.sort();
+    }
+    assert_eq!(parsed_dadk_task, expected_dadk_task)
+}
+
+#[test_context(DadkUserTestContext)]
+#[test]
+fn test_parse_dadk_user_config_install_local(ctx: &mut DadkUserTestContext) {
+    let config_file = ctx.templates_dir().join(DADK_USER_TEST_INSTALL_LOCAL);
+    assert!(config_file.exists());
+    assert!(config_file.is_file());
+    let r = Parser::parse_toml_file(&config_file);
+    assert!(r.is_ok());
+    let mut parsed_dadk_task = r.unwrap();
+    let mut expected_dadk_task = DADKTask {
+        name: "test_local".to_string(),
+        version: "0.2.0".to_string(),
+        description: "".to_string(),
+        build_once: true,
+        install_once: true,
+        task_type: TaskType::InstallFromPrebuilt(PrebuiltSource::Local(LocalSource::new(
+            PathBuf::from("/home/dev/demo"),
+        ))),
+        rust_target: None,
+        depends: vec![
+            Dependency {
+                name: "depend1".to_string(),
+                version: "0.1.1".to_string(),
+            },
+            Dependency {
+                name: "depend2".to_string(),
+                version: "0.1.2".to_string(),
+            },
+        ],
+        build: BuildConfig::new(Some("make install".to_string())),
+        install: InstallConfig::new(Some(PathBuf::from("/bin"))),
+        clean: CleanConfig::new(Some("make clean".to_string())),
+        envs: Some(vec![
+            TaskEnv::new("PATH".to_string(), "/usr/bin".to_string()),
+            TaskEnv::new("LD_LIBRARY_PATH".to_string(), "/usr/lib".to_string()),
+        ]),
+        target_arch: vec![TargetArch::try_from("x86_64").unwrap()],
+    };
+
+    parsed_dadk_task.target_arch.sort();
+    expected_dadk_task.target_arch.sort();
+    parsed_dadk_task.depends.sort();
+    expected_dadk_task.depends.sort();
+    if let Some(envs) = &mut parsed_dadk_task.envs {
+        envs.sort();
+    }
+    if let Some(envs) = &mut expected_dadk_task.envs {
+        envs.sort();
+    }
+    assert_eq!(parsed_dadk_task, expected_dadk_task)
+}
+
+#[test_context(DadkUserTestContext)]
+#[test]
+fn test_parse_dadk_user_config_install_archive(ctx: &mut DadkUserTestContext) {
+    let config_file = ctx.templates_dir().join(DADK_USER_TEST_INSTALL_ARCHIVE);
+    assert!(config_file.exists());
+    assert!(config_file.is_file());
+    let r = Parser::parse_toml_file(&config_file);
+    assert!(r.is_ok());
+    let mut parsed_dadk_task = r.unwrap();
+    let mut expected_dadk_task = DADKTask {
+        name: "test_archive".to_string(),
+        version: "0.2.0".to_string(),
+        description: "".to_string(),
+        build_once: true,
+        install_once: true,
+        task_type: TaskType::InstallFromPrebuilt(PrebuiltSource::Archive(ArchiveSource::new(
+            "https://url".to_string(),
+        ))),
+        rust_target: None,
+        depends: vec![
+            Dependency {
+                name: "depend1".to_string(),
+                version: "0.1.1".to_string(),
+            },
+            Dependency {
+                name: "depend2".to_string(),
+                version: "0.1.2".to_string(),
+            },
+        ],
+        build: BuildConfig::new(Some("make install".to_string())),
+        install: InstallConfig::new(Some(PathBuf::from("/bin"))),
+        clean: CleanConfig::new(Some("make clean".to_string())),
+        envs: Some(vec![
+            TaskEnv::new("PATH".to_string(), "/usr/bin".to_string()),
+            TaskEnv::new("LD_LIBRARY_PATH".to_string(), "/usr/lib".to_string()),
+        ]),
+        target_arch: vec![TargetArch::try_from("x86_64").unwrap()],
+    };
+
+    parsed_dadk_task.target_arch.sort();
+    expected_dadk_task.target_arch.sort();
+    parsed_dadk_task.depends.sort();
+    expected_dadk_task.depends.sort();
+    if let Some(envs) = &mut parsed_dadk_task.envs {
+        envs.sort();
+    }
+    if let Some(envs) = &mut expected_dadk_task.envs {
+        envs.sort();
+    }
+    assert_eq!(parsed_dadk_task, expected_dadk_task)
+}

+ 6 - 0
dadk/src/console.rs

@@ -0,0 +1,6 @@
+use dadk_user::clap::Parser;
+use dadk_user::console::CommandLineArgs;
+
+pub fn parse_commandline_args() -> CommandLineArgs {
+    CommandLineArgs::parse()
+}

+ 0 - 0
dadk/src/console/mod.rs


+ 8 - 1
dadk/src/lib.rs

@@ -1,7 +1,14 @@
+use console::parse_commandline_args;
 use dadk_user::dadk_user_main;
+use log::info;
 
 extern crate anyhow;
 
+mod console;
+
 pub fn dadk_main() {
-    dadk_user_main();
+    let args = parse_commandline_args();
+    info!("DADK run with args: {:?}", &args);
+
+    dadk_user_main(args);
 }

+ 26 - 0
tests/data/dadk_config_v2/app_all_target_arch_0_2_0.toml

@@ -0,0 +1,26 @@
+name = "app_all_target_arch"
+version = "0.2.0"
+description = "An app targets to all arch"
+build-once = false
+install-once = false
+target-arch = ["riscv64", "riscv32", "x86_64", "aarch64"]
+
+[task-type]
+type = "build_from_source"
+source = "git"
+source-path = "1"
+branch = "1"
+revision = ""
+
+[build]
+build-command = "1"
+
+[install]
+in-dragonos-path = "/"
+
+[clean]
+clean-command = "1"
+
+[depends]
+
+[envs]

+ 25 - 0
tests/data/dadk_config_v2/app_normal_with_env_0_2_0.toml

@@ -0,0 +1,25 @@
+name = "app_normal_with_env"
+version = "0.2.0"
+description = "A normal app with env"
+build-once = false
+install-once = false
+target-arch = ["x86_64"]              # Assuming this is the default target architecture
+
+[task-type]
+type = "build_from_source"
+source = "local"
+source-path = "tests/data/apps/app_normal_with_env"
+
+[build]
+build-command = "bash build.sh"
+
+[install]
+in-dragonos-path = "/"
+
+[clean]
+clean-command = ""
+
+[depends]
+
+[envs]
+CC = "abc-gcc"

+ 25 - 0
tests/data/dadk_config_v2/app_normal_with_env_fail_0_2_0.toml

@@ -0,0 +1,25 @@
+name = "app_normal_with_env_fail"
+version = "0.2.0"
+description = "A normal app with env which should failed"
+build-once = false
+install-once = false
+target-arch = ["x86_64"]              # Assuming this is the default target architecture
+
+[task-type]
+type = "build_from_source"
+source = "local"
+source-path = "tests/data/apps/app_normal_with_env_fail"
+
+[build]
+build-command = "bash build.sh"
+
+[install]
+in-dragonos-path = "/"
+
+[clean]
+clean-command = ""
+
+[depends]
+
+[envs]
+CC = "abc-gcc1"

+ 26 - 0
tests/data/dadk_config_v2/app_target_arch_riscv64_only_0_2_0.toml

@@ -0,0 +1,26 @@
+name = "app_target_arch_riscv64_only"
+version = "0.2.0"
+description = "An app only target to riscv64"
+build-once = false
+install-once = false
+target-arch = ["riscv64"]
+
+[task-type]
+type = "build_from_source"
+source = "git"
+source-path = "1"
+branch = "1"
+revision = ""
+
+[build]
+build-command = "1"
+
+[install]
+in-dragonos-path = "/"
+
+[clean]
+clean-command = "1"
+
+[depends]
+
+[envs]

+ 26 - 0
tests/data/dadk_config_v2/app_target_arch_x86_64_only_0_2_0.toml

@@ -0,0 +1,26 @@
+name = "app_target_arch_x86_64_only"
+version = "0.2.0"
+description = "An app only target to x86_64"
+build-once = false
+install-once = false
+target-arch = ["x86_64"]
+
+[task-type]
+type = "build_from_source"
+source = "git"
+source-path = "1"
+branch = "1"
+revision = ""
+
+[build]
+build-command = "1"
+
+[install]
+in-dragonos-path = "/"
+
+[clean]
+clean-command = "1"
+
+[depends]
+
+[envs]