1 use std::path::PathBuf; 2 3 use crate::executor::source::{ArchiveSource, GitSource, LocalSource}; 4 use dadk_config::{ 5 common::{ 6 target_arch::TargetArch, 7 task::{ 8 BuildConfig, CleanConfig, Dependency, InstallConfig, Source, TaskEnv, TaskSource, 9 TaskSourceType, 10 }, 11 }, 12 user::UserConfigFile, 13 }; 14 use serde::{Deserialize, Serialize}; 15 16 use anyhow::{Ok, Result}; 17 18 // 对于生成的包名和版本号,需要进行替换的字符。 19 pub static NAME_VERSION_REPLACE_TABLE: [(&str, &str); 6] = [ 20 (" ", "_"), 21 ("\t", "_"), 22 ("-", "_"), 23 (".", "_"), 24 ("+", "_"), 25 ("*", "_"), 26 ]; 27 28 #[derive(Debug, Clone, Serialize, Deserialize)] 29 pub struct DADKTask { 30 /// 包名 31 pub name: String, 32 /// 版本 33 pub version: String, 34 /// 包的描述 35 pub description: String, 36 /// 任务类型 37 pub task_type: TaskType, 38 /// 依赖的包 39 pub depends: Vec<Dependency>, 40 /// 构建配置 41 pub build: BuildConfig, 42 /// 安装配置 43 pub install: InstallConfig, 44 /// 清理配置 45 pub clean: CleanConfig, 46 /// 环境变量 47 pub envs: Option<Vec<TaskEnv>>, 48 49 /// (可选) 是否只构建一次,如果为true,DADK会在构建成功后,将构建结果缓存起来,下次构建时,直接使用缓存的构建结果。 50 #[serde(default)] 51 pub build_once: bool, 52 53 /// (可选) 是否只安装一次,如果为true,DADK会在安装成功后,不再重复安装。 54 #[serde(default)] 55 pub install_once: bool, 56 57 #[serde(default = "DADKTask::default_target_arch_vec")] 58 pub target_arch: Vec<TargetArch>, 59 } 60 61 impl DADKTask { 62 #[allow(dead_code)] new( name: String, version: String, description: String, task_type: TaskType, depends: Vec<Dependency>, build: BuildConfig, install: InstallConfig, clean: CleanConfig, envs: Option<Vec<TaskEnv>>, build_once: bool, install_once: bool, target_arch: Option<Vec<TargetArch>>, ) -> Self63 pub fn new( 64 name: String, 65 version: String, 66 description: String, 67 task_type: TaskType, 68 depends: Vec<Dependency>, 69 build: BuildConfig, 70 install: InstallConfig, 71 clean: CleanConfig, 72 envs: Option<Vec<TaskEnv>>, 73 build_once: bool, 74 install_once: bool, 75 target_arch: Option<Vec<TargetArch>>, 76 ) -> Self { 77 Self { 78 name, 79 version, 80 description, 81 task_type, 82 depends, 83 build, 84 install, 85 clean, 86 envs, 87 build_once, 88 install_once, 89 target_arch: target_arch.unwrap_or_else(Self::default_target_arch_vec), 90 } 91 } 92 93 /// 默认的目标处理器架构 94 /// 95 /// 从环境变量`ARCH`中获取,如果没有设置,则默认为`x86_64` default_target_arch() -> TargetArch96 pub fn default_target_arch() -> TargetArch { 97 let s = std::env::var("ARCH").unwrap_or("x86_64".to_string()); 98 return TargetArch::try_from(s.as_str()).unwrap(); 99 } 100 default_target_arch_vec() -> Vec<TargetArch>101 fn default_target_arch_vec() -> Vec<TargetArch> { 102 vec![Self::default_target_arch()] 103 } 104 validate(&mut self) -> Result<()>105 pub fn validate(&mut self) -> Result<()> { 106 if self.name.is_empty() { 107 return Err(anyhow::Error::msg("name is empty")); 108 } 109 if self.version.is_empty() { 110 return Err(anyhow::Error::msg("version is empty")); 111 } 112 self.task_type.validate()?; 113 self.build.validate()?; 114 self.validate_build_type()?; 115 self.install.validate()?; 116 self.clean.validate()?; 117 self.validate_depends()?; 118 self.validate_envs()?; 119 self.validate_target_arch()?; 120 121 return Ok(()); 122 } 123 trim(&mut self)124 pub fn trim(&mut self) { 125 self.name = self.name.trim().to_string(); 126 self.version = self.version.trim().to_string(); 127 self.description = self.description.trim().to_string(); 128 self.task_type.trim(); 129 self.build.trim(); 130 self.install.trim(); 131 self.clean.trim(); 132 self.trim_depends(); 133 self.trim_envs(); 134 } 135 validate_depends(&self) -> Result<()>136 fn validate_depends(&self) -> Result<()> { 137 for depend in &self.depends { 138 depend.validate()?; 139 } 140 return Ok(()); 141 } 142 trim_depends(&mut self)143 fn trim_depends(&mut self) { 144 for depend in &mut self.depends { 145 depend.trim(); 146 } 147 } 148 validate_envs(&self) -> Result<()>149 fn validate_envs(&self) -> Result<()> { 150 if let Some(envs) = &self.envs { 151 for env in envs { 152 env.validate()?; 153 } 154 } 155 return Ok(()); 156 } 157 validate_target_arch(&self) -> Result<()>158 fn validate_target_arch(&self) -> Result<()> { 159 if self.target_arch.is_empty() { 160 return Err(anyhow::Error::msg("target_arch is empty")); 161 } 162 return Ok(()); 163 } 164 trim_envs(&mut self)165 fn trim_envs(&mut self) { 166 if let Some(envs) = &mut self.envs { 167 for env in envs { 168 env.trim(); 169 } 170 } 171 } 172 173 /// 验证任务类型与构建配置是否匹配 validate_build_type(&self) -> Result<()>174 fn validate_build_type(&self) -> Result<()> { 175 match &self.task_type { 176 TaskType::BuildFromSource(_) => { 177 if self.build.build_command.is_none() { 178 return Err(anyhow::Error::msg("build command is empty")); 179 } 180 } 181 TaskType::InstallFromPrebuilt(_) => { 182 if self.build.build_command.is_some() { 183 return Err(anyhow::Error::msg( 184 "build command should be empty when install from prebuilt", 185 )); 186 } 187 } 188 } 189 return Ok(()); 190 } 191 name_version(&self) -> String192 pub fn name_version(&self) -> String { 193 let mut name_version = format!("{}-{}", self.name, self.version); 194 for (src, dst) in &NAME_VERSION_REPLACE_TABLE { 195 name_version = name_version.replace(src, dst); 196 } 197 return name_version; 198 } 199 name_version_env(&self) -> String200 pub fn name_version_env(&self) -> String { 201 return Self::name_version_uppercase(&self.name, &self.version); 202 } 203 name_version_uppercase(name: &str, version: &str) -> String204 pub fn name_version_uppercase(name: &str, version: &str) -> String { 205 let mut name_version = format!("{}-{}", name, version).to_ascii_uppercase(); 206 for (src, dst) in &NAME_VERSION_REPLACE_TABLE { 207 name_version = name_version.replace(src, dst); 208 } 209 return name_version; 210 } 211 212 /// # 获取源码目录 213 /// 214 /// 如果从本地路径构建,则返回本地路径。否则返回None。 source_path(&self) -> Option<PathBuf>215 pub fn source_path(&self) -> Option<PathBuf> { 216 match &self.task_type { 217 TaskType::BuildFromSource(cs) => match cs { 218 CodeSource::Local(lc) => { 219 return Some(lc.path().clone()); 220 } 221 _ => { 222 return None; 223 } 224 }, 225 TaskType::InstallFromPrebuilt(ps) => match ps { 226 PrebuiltSource::Local(lc) => { 227 return Some(lc.path().clone()); 228 } 229 _ => { 230 return None; 231 } 232 }, 233 } 234 } 235 } 236 237 impl TryFrom<UserConfigFile> for DADKTask { 238 type Error = anyhow::Error; 239 try_from(user_config: UserConfigFile) -> Result<Self>240 fn try_from(user_config: UserConfigFile) -> Result<Self> { 241 Ok(DADKTask { 242 name: user_config.name, 243 version: user_config.version, 244 description: user_config.description, 245 task_type: TaskType::try_from(user_config.task_source)?, 246 depends: user_config.depends, 247 build: user_config.build, 248 install: user_config.install, 249 clean: user_config.clean, 250 envs: Some(user_config.envs), 251 build_once: user_config.build_once, 252 install_once: user_config.install_once, 253 target_arch: user_config.target_arch, 254 }) 255 } 256 } 257 258 /// # 任务类型 259 #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] 260 pub enum TaskType { 261 /// 从源码构建 262 BuildFromSource(CodeSource), 263 /// 从预编译包安装 264 InstallFromPrebuilt(PrebuiltSource), 265 } 266 267 impl TaskType { validate(&mut self) -> Result<()>268 pub fn validate(&mut self) -> Result<()> { 269 match self { 270 TaskType::BuildFromSource(source) => source.validate(), 271 TaskType::InstallFromPrebuilt(source) => source.validate(), 272 } 273 } 274 trim(&mut self)275 pub fn trim(&mut self) { 276 match self { 277 TaskType::BuildFromSource(source) => source.trim(), 278 TaskType::InstallFromPrebuilt(source) => source.trim(), 279 } 280 } 281 } 282 283 impl TryFrom<TaskSource> for TaskType { 284 type Error = anyhow::Error; try_from(task_source: TaskSource) -> Result<Self>285 fn try_from(task_source: TaskSource) -> Result<Self> { 286 match task_source.source_type { 287 TaskSourceType::BuildFromSource => match task_source.source { 288 Source::Git => Ok(TaskType::BuildFromSource(CodeSource::Git(GitSource::new( 289 task_source.source_path, 290 task_source.branch, 291 task_source.revision, 292 )))), 293 Source::Local => Ok(TaskType::BuildFromSource(CodeSource::Local( 294 LocalSource::new(PathBuf::from(task_source.source_path)), 295 ))), 296 Source::Archive => Ok(TaskType::BuildFromSource(CodeSource::Archive( 297 ArchiveSource::new(task_source.source_path), 298 ))), 299 }, 300 TaskSourceType::InstallFromPrebuilt => match task_source.source { 301 Source::Git => Err(anyhow::Error::msg( 302 "InstallFromPrebuild doesn't support Git", 303 )), 304 Source::Local => Ok(TaskType::InstallFromPrebuilt(PrebuiltSource::Local( 305 LocalSource::new(PathBuf::from(task_source.source_path)), 306 ))), 307 Source::Archive => Ok(TaskType::InstallFromPrebuilt(PrebuiltSource::Archive( 308 ArchiveSource::new(task_source.source_path), 309 ))), 310 }, 311 } 312 } 313 } 314 315 /// # 代码源 316 #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] 317 pub enum CodeSource { 318 /// 从Git仓库获取 319 Git(GitSource), 320 /// 从本地目录获取 321 Local(LocalSource), 322 /// 从在线压缩包获取 323 Archive(ArchiveSource), 324 } 325 326 impl CodeSource { validate(&mut self) -> Result<()>327 pub fn validate(&mut self) -> Result<()> { 328 match self { 329 CodeSource::Git(source) => source.validate(), 330 CodeSource::Local(source) => source.validate(Some(false)), 331 CodeSource::Archive(source) => source.validate(), 332 } 333 } trim(&mut self)334 pub fn trim(&mut self) { 335 match self { 336 CodeSource::Git(source) => source.trim(), 337 CodeSource::Local(source) => source.trim(), 338 CodeSource::Archive(source) => source.trim(), 339 } 340 } 341 } 342 343 /// # 预编译包源 344 #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] 345 pub enum PrebuiltSource { 346 /// 从在线压缩包获取 347 Archive(ArchiveSource), 348 /// 从本地目录/文件获取 349 Local(LocalSource), 350 } 351 352 impl PrebuiltSource { validate(&self) -> Result<()>353 pub fn validate(&self) -> Result<()> { 354 match self { 355 PrebuiltSource::Archive(source) => source.validate(), 356 PrebuiltSource::Local(source) => source.validate(None), 357 } 358 } 359 trim(&mut self)360 pub fn trim(&mut self) { 361 match self { 362 PrebuiltSource::Archive(source) => source.trim(), 363 PrebuiltSource::Local(source) => source.trim(), 364 } 365 } 366 } 367