浏览代码

target管理 (#14)

* target管理

* 以dadk文件路径作key

* TargetManager

* 改善TargetManager
Jomo 1 年之前
父节点
当前提交
743e9fdaaa

+ 19 - 2
src/console/new_config.rs

@@ -15,7 +15,7 @@ use crate::{
 };
 
 use super::{
-    elements::Input,
+    elements::{ChooseYesOrNo, Input},
     interactive::{InputFunc, InteractiveCommand},
     ConsoleError,
 };
@@ -98,7 +98,7 @@ impl NewConfigCommand {
             "name: {}, version: {}, description: {}",
             name, version, description
         );
-
+        let rust_target = self.input_rust_target()?;
         let task_type: TaskType = TaskTypeInput::new().input()?;
         debug!("task_type: {:?}", task_type);
 
@@ -121,6 +121,7 @@ impl NewConfigCommand {
             name,
             version,
             description,
+            rust_target,
             task_type,
             dep,
             build_config,
@@ -165,6 +166,22 @@ impl NewConfigCommand {
 
         return Ok(description);
     }
+
+    // 输入编译target
+    fn input_rust_target(&self) -> Result<Option<String>, ConsoleError> {
+        let choice = ChooseYesOrNo::new("Input rust_target?".to_string()).choose_until_valid()?;
+
+        if choice {
+            let rust_target = Input::new(
+                Some("Please input the [rust_target] of the task:".to_string()),
+                None,
+            )
+            .input()?;
+            return Ok(Some(rust_target));
+        }
+
+        return Ok(None);
+    }
 }
 
 #[derive(Debug)]

+ 35 - 1
src/executor/mod.rs

@@ -21,6 +21,7 @@ use self::cache::CacheDirType;
 
 pub mod cache;
 pub mod source;
+pub mod target;
 
 lazy_static! {
     // 全局环境变量的列表
@@ -124,6 +125,8 @@ impl Executor {
 
     /// # 执行build操作
     fn build(&mut self) -> Result<(), ExecutorError> {
+        self.mv_target_to_tmp()?;
+
         // 确认源文件就绪
         self.prepare_input()?;
 
@@ -173,6 +176,11 @@ impl Executor {
             .map_err(|e| ExecutorError::InstallError(e))?;
         info!("Task {} installed.", self.entity.task().name_version());
 
+        // 安装完后,删除临时target文件
+        if let Some(target) = self.entity.target() {
+            target.clean_tmpdadk()?;
+        }
+
         return Ok(());
     }
 
@@ -244,6 +252,7 @@ impl Executor {
             self.entity.task().name_version(),
             self.build_dir.path
         );
+
         return self.build_dir.remove_self_recursive();
     }
 
@@ -259,7 +268,6 @@ impl Executor {
             self.entity.task().name_version(),
             self.src_work_dir().display()
         );
-
         return cache_dir.unwrap().remove_self_recursive();
     }
 
@@ -326,6 +334,8 @@ impl Executor {
     /// # 准备工作线程本地环境变量
     fn prepare_local_env(&mut self) -> Result<(), ExecutorError> {
         // 设置本地环境变量
+        self.prepare_target_env()?;
+
         let task_envs: Option<&Vec<TaskEnv>> = self.entity.task().envs.as_ref();
         if task_envs.is_none() {
             return Ok(());
@@ -437,6 +447,30 @@ impl Executor {
             return Err(ExecutorError::TaskFailed(errmsg));
         }
     }
+
+    pub fn mv_target_to_tmp(&mut self) -> Result<(), ExecutorError> {
+        if let Some(rust_target) = self.entity.task().rust_target.clone() {
+            // 将target文件拷贝至 /tmp 下对应的dadk文件的临时target文件中
+            self.entity
+                .target()
+                .as_ref()
+                .unwrap()
+                .cp_to_tmp(&rust_target)?;
+        }
+        return Ok(());
+    }
+
+    pub fn prepare_target_env(&mut self) -> Result<(), ExecutorError> {
+        if self.entity.task().rust_target.is_some() {
+            // 如果有dadk任务有rust_target字段,需要设置DADK_RUST_TARGET_FILE环境变量,值为临时target文件路径
+            self.entity
+                .target()
+                .as_ref()
+                .unwrap()
+                .prepare_env(&mut self.local_envs);
+        }
+        return Ok(());
+    }
 }
 
 #[derive(Debug, Clone)]

+ 212 - 0
src/executor/target.rs

@@ -0,0 +1,212 @@
+use ::std::{
+    collections::hash_map::DefaultHasher,
+    fs,
+    hash::{Hash, Hasher},
+    path::PathBuf,
+};
+use log::error;
+use std::io::Write;
+
+use crate::{
+    executor::{EnvMap, EnvVar},
+    static_resources::INLINE_TARGETS,
+};
+
+use crate::executor::ExecutorError;
+
+/// Target用于管理target文件
+#[derive(Debug, Clone)]
+pub struct Target {
+    /// 临时target文件路径
+    tmp_target_path: PathBuf,
+}
+
+impl Target {
+    /// 创建target管理器
+    ///
+    /// ## 参数
+    ///
+    /// - `path` : 临时target文件路径
+    ///
+    /// ## 返回值
+    ///
+    /// target管理器
+    pub fn new(path: PathBuf) -> Target {
+        Target {
+            tmp_target_path: path,
+        }
+    }
+
+    /// 将用户的target文件或用户使用的内置target文件拷贝到临时target文件
+    ///
+    /// ## 参数
+    ///
+    /// - `rust_target` : dadk任务的rust_target字段值
+    ///
+    /// ## 返回值
+    ///
+    /// Ok(()) 拷贝成功
+    /// Err(ExecutorError) 拷贝失败
+    pub fn cp_to_tmp(&self, rust_target: &str) -> Result<(), ExecutorError> {
+        // 创建临时target文件
+        if Self::is_user_target(rust_target) {
+            // 如果是用户的target文件,则从源target文件路径from拷贝
+            let from = Self::user_target_path(&rust_target).unwrap();
+            self.copy_to_tmp(&from)?;
+        } else {
+            // 如果使用的是内置target文件,则将默认的target文件写入临时target文件中
+            self.write_to_tmp(rust_target)?;
+        }
+        return Ok(());
+    }
+
+    pub fn copy_to_tmp(&self, from: &PathBuf) -> Result<(), ExecutorError> {
+        //创建临时target文件
+        self.create_tmp_target()?;
+        if let Err(e) = fs::copy(from, &self.tmp_target_path) {
+            return Err(ExecutorError::PrepareEnvError(format!("{}", e)));
+        }
+        return Ok(());
+    }
+
+    pub fn write_to_tmp(&self, rust_target: &str) -> Result<(), ExecutorError> {
+        // 创建临时target文件
+        let file = self.create_tmp_target()?;
+        let data = INLINE_TARGETS.lock().unwrap().get(rust_target)?;
+        // 将target文件的二进制变量写入临时target文件中
+        if file.is_some() {
+            if let Err(e) = file.unwrap().write_all(&data) {
+                return Err(ExecutorError::PrepareEnvError(format!("{}", e)));
+            }
+        }
+        return Ok(());
+    }
+
+    /// 获取用户的target文件路径
+    ///
+    /// ## 参数
+    ///
+    /// - `rust_target` : dadk任务的rust_target字段值
+    ///
+    /// ## 返回值
+    ///
+    /// Ok(PathBuf) 用户target文件路径
+    /// Err(ExecutorError) 用户target文件路径无效
+    pub fn user_target_path(rust_target: &str) -> Result<PathBuf, ExecutorError> {
+        // 如果是个路径,说明是用户自己的编译target文件,就判断文件是否有效
+        let path = PathBuf::from(rust_target);
+        if path.exists() {
+            return Ok(path);
+        } else {
+            let path = path.as_path().to_str().unwrap();
+            let errmsg = format!("Can not find the rust_target file: {}", path);
+            error!("{errmsg}");
+            return Err(ExecutorError::PrepareEnvError(errmsg));
+        }
+    }
+
+    /// 通过dadk任务的路径生成相应的临时dadk目录路径
+    ///
+    /// ## 参数
+    ///
+    /// - `file_str` : dadk任务文件路径的字符串值
+    ///
+    /// ## 返回值
+    ///
+    /// 临时dadk目录路径
+    pub fn tmp_dadk(file_str: &str) -> PathBuf {
+        let mut hasher = DefaultHasher::new();
+        file_str.hash(&mut hasher);
+        let hash_string = format!("{:x}", hasher.finish());
+        // 在/tmp文件夹下,创建当前DADK任务文件夹用于临时存放target
+        let tmp_dadk = format!("/tmp/dadk{}/", hash_string);
+        return PathBuf::from(tmp_dadk);
+    }
+
+    /// 创建临时target文件
+    ///
+    /// ## 参数
+    ///
+    /// - `tmp_target_path` : 临时target文件路径
+    ///
+    /// ## 返回值
+    ///
+    /// Ok(Some(fs::File)) 创建成功后的文件
+    /// Ok(None) 临时target文件已经存在,不需要再创建
+    /// Err(ExecutorError) 创建失败
+    pub fn create_tmp_target(&self) -> Result<Option<fs::File>, ExecutorError> {
+        // 先创建用于存放临时target文件的临时dadk目录
+        let dir = Self::dir(&self.tmp_target_path);
+        if fs::metadata(dir.clone()).is_err() {
+            if let Err(e) = fs::create_dir(dir.clone()) {
+                return Err(ExecutorError::PrepareEnvError(format!(
+                    "{}{}",
+                    dir.display(),
+                    e
+                )));
+            }
+        }
+
+        // 如果临时target文件已经存在,则不需要返回文件,返回None即可
+        if fs::metadata(&self.tmp_target_path).is_err() {
+            if let Ok(file) = fs::File::create(&self.tmp_target_path) {
+                return Ok(Some(file));
+            }
+        }
+
+        return Ok(None);
+    }
+
+    /// 设置DADK_RUST_TARGET_FILE环境变量
+    ///
+    /// ## 参数
+    ///
+    /// - `local_envs` : 当前任务的环境变量列表
+    ///
+    /// ## 返回值
+    ///
+    /// 无
+    pub fn prepare_env(&self, local_envs: &mut EnvMap) {
+        let path = self
+            .tmp_target_path()
+            .as_path()
+            .to_str()
+            .unwrap()
+            .to_string();
+        local_envs.add(EnvVar::new("DADK_RUST_TARGET_FILE".to_string(), path));
+    }
+
+    /// 清理生成的临时dadk目录
+    pub fn clean_tmpdadk(&self) -> Result<(), ExecutorError> {
+        if self.tmp_target_path.exists() {
+            let dir = Self::dir(&self.tmp_target_path);
+            std::fs::remove_dir_all(&dir)
+                .map_err(|e| ExecutorError::CleanError(format!("{}{}", dir.display(), e)))?;
+        }
+        return Ok(());
+    }
+
+    /// 获取文件所在的目录路径
+    ///
+    /// ## 参数
+    ///
+    /// - `path` : 文件路径
+    ///
+    /// ## 返回值
+    ///
+    /// 文件所在目录路径
+    pub fn dir(path: &PathBuf) -> PathBuf {
+        let path_str = path.as_path().to_str().unwrap();
+        let index = path_str.rfind('/').unwrap();
+        return PathBuf::from(path_str[..index + 1].to_string());
+    }
+
+    pub fn is_user_target(rust_target: &str) -> bool {
+        // 如果包含.的话,说明用户使用的是自己的target文件,因为带有.json这样的字符
+        return rust_target.contains('.');
+    }
+
+    pub fn tmp_target_path(&self) -> &PathBuf {
+        return &self.tmp_target_path;
+    }
+}

+ 1 - 0
src/main.rs

@@ -110,6 +110,7 @@ mod console;
 mod executor;
 mod parser;
 mod scheduler;
+pub mod static_resources;
 mod utils;
 
 fn main() {

+ 7 - 0
src/parser/task.rs

@@ -22,6 +22,8 @@ pub struct DADKTask {
     pub version: String,
     /// 包的描述
     pub description: String,
+    /// 编译target
+    pub rust_target: Option<String>,
     /// 任务类型
     pub task_type: TaskType,
     /// 依赖的包
@@ -42,6 +44,7 @@ impl DADKTask {
         name: String,
         version: String,
         description: String,
+        rust_target: Option<String>,
         task_type: TaskType,
         depends: Vec<Dependency>,
         build: BuildConfig,
@@ -53,6 +56,7 @@ impl DADKTask {
             name,
             version,
             description,
+            rust_target,
             task_type,
             depends,
             build,
@@ -84,6 +88,9 @@ impl DADKTask {
         self.name = self.name.trim().to_string();
         self.version = self.version.trim().to_string();
         self.description = self.description.trim().to_string();
+        if let Some(target) = &self.rust_target {
+            self.rust_target = Some(target.trim().to_string());
+        };
         self.task_type.trim();
         self.build.trim();
         self.install.trim();

+ 53 - 1
src/scheduler/mod.rs

@@ -9,7 +9,11 @@ use std::{
 
 use log::{error, info};
 
-use crate::{console::Action, executor::Executor, parser::task::DADKTask};
+use crate::{
+    console::Action,
+    executor::{target::Target, Executor},
+    parser::task::DADKTask,
+};
 
 /// # 调度实体
 #[derive(Debug, Clone)]
@@ -19,6 +23,8 @@ pub struct SchedEntity {
     file_path: PathBuf,
     /// 任务
     task: DADKTask,
+    /// target管理
+    target: Option<Target>,
 }
 
 impl PartialEq for SchedEntity {
@@ -47,6 +53,16 @@ impl SchedEntity {
     pub fn task_mut(&mut self) -> &mut DADKTask {
         &mut self.task
     }
+
+    #[allow(dead_code)]
+    pub fn target(&self) -> &Option<Target> {
+        &self.target
+    }
+
+    #[allow(dead_code)]
+    pub fn target_mut(&mut self) -> &mut Option<Target> {
+        &mut self.target
+    }
 }
 
 /// # 调度实体列表
@@ -252,10 +268,12 @@ impl Scheduler {
     /// 添加任务到调度器中,如果任务已经存在,则返回错误
     pub fn add_task(&mut self, path: PathBuf, task: DADKTask) -> Result<(), SchedulerError> {
         let id: i32 = self.generate_task_id();
+        let target = self.generate_task_target(&path, &task.rust_target)?;
         let entity = Rc::new(SchedEntity {
             id,
             task,
             file_path: path.clone(),
+            target,
         });
         let name_version = (entity.task.name.clone(), entity.task.version.clone());
 
@@ -283,6 +301,40 @@ impl Scheduler {
         return TASK_ID.fetch_add(1, Ordering::SeqCst);
     }
 
+    fn generate_task_target(
+        &self,
+        path: &PathBuf,
+        rust_target: &Option<String>,
+    ) -> Result<Option<Target>, SchedulerError> {
+        if let Some(rust_target) = rust_target {
+            // 如果rust_target字段不为none,说明需要target管理
+            // 获取dadk任务路径,用于生成临时dadk文件名
+            let file_str = path.as_path().to_str().unwrap();
+            let tmp_dadk_path = Target::tmp_dadk(file_str);
+            let tmp_dadk_str = tmp_dadk_path.as_path().to_str().unwrap();
+
+            if Target::is_user_target(rust_target) {
+                // 如果target文件是用户自己的
+                if let Ok(target_path) = Target::user_target_path(rust_target) {
+                    let target_path_str = target_path.as_path().to_str().unwrap();
+                    let index = target_path_str.rfind('/').unwrap();
+                    let target_name = target_path_str[index + 1..].to_string();
+                    let tmp_target = PathBuf::from(format!("{}{}", tmp_dadk_str, target_name));
+                    return Ok(Some(Target::new(tmp_target)));
+                } else {
+                    return Err(SchedulerError::TaskError(
+                        "The path of target file is invalid.".to_string(),
+                    ));
+                }
+            } else {
+                // 如果target文件是内置的
+                let tmp_target = PathBuf::from(format!("{}{}.json", tmp_dadk_str, rust_target));
+                return Ok(Some(Target::new(tmp_target)));
+            }
+        }
+        return Ok(None);
+    }
+
     /// # 执行调度器中的所有任务
     pub fn run(&self) -> Result<(), SchedulerError> {
         // 准备全局环境变量

+ 49 - 0
src/static_resources/mod.rs

@@ -0,0 +1,49 @@
+use std::sync::Mutex;
+
+use log::error;
+
+use crate::executor::ExecutorError;
+
+lazy_static! {
+    // 全局内置target
+    pub static ref INLINE_TARGETS: Mutex<InlineTargets> = Mutex::new(InlineTargets::new());
+}
+
+/// #内置target
+pub struct InlineTargets {
+    /// 内置target文件列表
+    inline_list: Vec<(String, &'static [u8])>,
+}
+
+impl InlineTargets {
+    pub fn new() -> InlineTargets {
+        let mut inline_targets = InlineTargets {
+            inline_list: Vec::new(),
+        };
+        inline_targets.init();
+        return inline_targets;
+    }
+
+    pub fn init(&mut self) {
+        // 后续如果有新的内置target文件,只需要在inline_list中加入(目标三元组,binary数据)
+        let x86_64_unknown_dragonos: &'static [u8] =
+            include_bytes!("targets/rust/x86_64-unknown-dragonos.json");
+        self.inline_list.push((
+            "x86_64-unknown-dragonos".to_string(),
+            x86_64_unknown_dragonos,
+        ));
+    }
+
+    pub fn get(&self, rust_target: &str) -> Result<&'static [u8], ExecutorError> {
+        // 通过rust_target找到对应的binary数据
+        for (name, data) in &self.inline_list {
+            if name == rust_target {
+                return Ok(data);
+            }
+        }
+
+        let errmsg = format!("无效的内置target文件: {}", rust_target);
+        error!("{errmsg}");
+        return Err(ExecutorError::PrepareEnvError(errmsg));
+    }
+}

+ 35 - 0
src/static_resources/targets/rust/x86_64-unknown-dragonos.json

@@ -0,0 +1,35 @@
+{
+  "arch": "x86_64",
+  "code-model": "kernel",
+  "cpu": "x86-64",
+  "os": "dragonos",
+  "target-endian": "little",
+  "target-pointer-width": "64",
+  "target-family": [
+      "unix"
+  ],
+  "env": "musl",
+  "target-c-int-width": "32",
+  "data-layout": "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128",
+  "disable-redzone": true,
+  "features": "-3dnow,-3dnowa,-avx,-avx2",
+  "linker": "rust-lld",
+  "linker-flavor": "ld.lld",
+  "llvm-target": "x86_64-unknown-none",
+  "max-atomic-width": 64,
+  "panic-strategy": "abort",
+  "position-independent-executables": true,
+  "relro-level": "full",
+  "stack-probes": {
+      "kind": "inline-or-call",
+      "min-llvm-version-for-inline": [
+          16,
+          0,
+          0
+      ]
+  },
+  "static-position-independent-executables": true,
+  "supported-sanitizers": [
+      "kcfi"
+  ]
+}