1 use std::path::PathBuf; 2 3 use dadk_config::common::target_arch::TargetArch; 4 use serde::{de::Error, Deserialize, Serialize}; 5 6 use crate::executor::source::{ArchiveSource, GitSource, LocalSource}; 7 8 use super::{ 9 config::{ 10 DADKUserBuildConfig, DADKUserCleanConfig, DADKUserConfigKey, DADKUserInstallConfig, 11 DADKUserTaskType, 12 }, 13 InnerParserError, ParserError, 14 }; 15 16 // 对于生成的包名和版本号,需要进行替换的字符。 17 pub static NAME_VERSION_REPLACE_TABLE: [(&str, &str); 6] = [ 18 (" ", "_"), 19 ("\t", "_"), 20 ("-", "_"), 21 (".", "_"), 22 ("+", "_"), 23 ("*", "_"), 24 ]; 25 26 #[derive(Debug, Clone, Serialize, Deserialize)] 27 pub struct DADKTask { 28 /// 包名 29 pub name: String, 30 /// 版本 31 pub version: String, 32 /// 包的描述 33 pub description: String, 34 /// 编译target 35 pub rust_target: Option<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)] 63 pub fn new( 64 name: String, 65 version: String, 66 description: String, 67 rust_target: Option<String>, 68 task_type: TaskType, 69 depends: Vec<Dependency>, 70 build: BuildConfig, 71 install: InstallConfig, 72 clean: CleanConfig, 73 envs: Option<Vec<TaskEnv>>, 74 build_once: bool, 75 install_once: bool, 76 target_arch: Option<Vec<TargetArch>>, 77 ) -> Self { 78 Self { 79 name, 80 version, 81 description, 82 rust_target, 83 task_type, 84 depends, 85 build, 86 install, 87 clean, 88 envs, 89 build_once, 90 install_once, 91 target_arch: target_arch.unwrap_or_else(Self::default_target_arch_vec), 92 } 93 } 94 95 /// 默认的目标处理器架构 96 /// 97 /// 从环境变量`ARCH`中获取,如果没有设置,则默认为`x86_64` 98 pub fn default_target_arch() -> TargetArch { 99 let s = std::env::var("ARCH").unwrap_or("x86_64".to_string()); 100 return TargetArch::try_from(s.as_str()).unwrap(); 101 } 102 103 fn default_target_arch_vec() -> Vec<TargetArch> { 104 vec![Self::default_target_arch()] 105 } 106 107 pub fn validate(&mut self) -> Result<(), String> { 108 if self.name.is_empty() { 109 return Err("name is empty".to_string()); 110 } 111 if self.version.is_empty() { 112 return Err("version is empty".to_string()); 113 } 114 self.task_type.validate()?; 115 self.build.validate()?; 116 self.validate_build_type()?; 117 self.install.validate()?; 118 self.clean.validate()?; 119 self.validate_depends()?; 120 self.validate_envs()?; 121 self.validate_target_arch()?; 122 123 return Ok(()); 124 } 125 126 pub fn trim(&mut self) { 127 self.name = self.name.trim().to_string(); 128 self.version = self.version.trim().to_string(); 129 self.description = self.description.trim().to_string(); 130 if let Some(target) = &self.rust_target { 131 self.rust_target = Some(target.trim().to_string()); 132 }; 133 self.task_type.trim(); 134 self.build.trim(); 135 self.install.trim(); 136 self.clean.trim(); 137 self.trim_depends(); 138 self.trim_envs(); 139 } 140 141 fn validate_depends(&self) -> Result<(), String> { 142 for depend in &self.depends { 143 depend.validate()?; 144 } 145 return Ok(()); 146 } 147 148 fn trim_depends(&mut self) { 149 for depend in &mut self.depends { 150 depend.trim(); 151 } 152 } 153 154 fn validate_envs(&self) -> Result<(), String> { 155 if let Some(envs) = &self.envs { 156 for env in envs { 157 env.validate()?; 158 } 159 } 160 return Ok(()); 161 } 162 163 fn validate_target_arch(&self) -> Result<(), String> { 164 if self.target_arch.is_empty() { 165 return Err("target_arch is empty".to_string()); 166 } 167 return Ok(()); 168 } 169 170 fn trim_envs(&mut self) { 171 if let Some(envs) = &mut self.envs { 172 for env in envs { 173 env.trim(); 174 } 175 } 176 } 177 178 /// 验证任务类型与构建配置是否匹配 179 fn validate_build_type(&self) -> Result<(), String> { 180 match &self.task_type { 181 TaskType::BuildFromSource(_) => { 182 if self.build.build_command.is_none() { 183 return Err("build command is empty".to_string()); 184 } 185 } 186 TaskType::InstallFromPrebuilt(_) => { 187 if self.build.build_command.is_some() { 188 return Err( 189 "build command should be empty when install from prebuilt".to_string() 190 ); 191 } 192 } 193 } 194 return Ok(()); 195 } 196 197 pub fn name_version(&self) -> String { 198 let mut name_version = format!("{}-{}", self.name, self.version); 199 for (src, dst) in &NAME_VERSION_REPLACE_TABLE { 200 name_version = name_version.replace(src, dst); 201 } 202 return name_version; 203 } 204 205 pub fn name_version_env(&self) -> String { 206 return Self::name_version_uppercase(&self.name, &self.version); 207 } 208 209 pub fn name_version_uppercase(name: &str, version: &str) -> String { 210 let mut name_version = format!("{}-{}", name, version).to_ascii_uppercase(); 211 for (src, dst) in &NAME_VERSION_REPLACE_TABLE { 212 name_version = name_version.replace(src, dst); 213 } 214 return name_version; 215 } 216 217 /// # 获取源码目录 218 /// 219 /// 如果从本地路径构建,则返回本地路径。否则返回None。 220 pub fn source_path(&self) -> Option<PathBuf> { 221 match &self.task_type { 222 TaskType::BuildFromSource(cs) => match cs { 223 CodeSource::Local(lc) => { 224 return Some(lc.path().clone()); 225 } 226 _ => { 227 return None; 228 } 229 }, 230 TaskType::InstallFromPrebuilt(ps) => match ps { 231 PrebuiltSource::Local(lc) => { 232 return Some(lc.path().clone()); 233 } 234 _ => { 235 return None; 236 } 237 }, 238 } 239 } 240 } 241 242 impl PartialEq for DADKTask { 243 fn eq(&self, other: &Self) -> bool { 244 self.name == other.name 245 && self.version == other.version 246 && self.description == other.description 247 && self.rust_target == other.rust_target 248 && self.build_once == other.build_once 249 && self.install_once == other.install_once 250 && self.target_arch == other.target_arch 251 && self.task_type == other.task_type 252 && self.build == other.build 253 && self.install == other.install 254 && self.clean == other.clean 255 && self.depends == other.depends 256 && self.envs == other.envs 257 } 258 } 259 260 /// @brief 构建配置 261 #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] 262 pub struct BuildConfig { 263 /// 构建命令 264 pub build_command: Option<String>, 265 } 266 267 impl BuildConfig { 268 #[allow(dead_code)] 269 pub fn new(build_command: Option<String>) -> Self { 270 Self { build_command } 271 } 272 273 pub fn validate(&self) -> Result<(), String> { 274 return Ok(()); 275 } 276 277 pub fn trim(&mut self) { 278 if let Some(build_command) = &mut self.build_command { 279 *build_command = build_command.trim().to_string(); 280 } 281 } 282 } 283 284 impl From<DADKUserBuildConfig> for BuildConfig { 285 fn from(value: DADKUserBuildConfig) -> Self { 286 return BuildConfig { 287 build_command: value.build_command, 288 }; 289 } 290 } 291 292 #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] 293 pub struct InstallConfig { 294 /// 安装到DragonOS内的目录 295 pub in_dragonos_path: Option<PathBuf>, 296 } 297 298 impl InstallConfig { 299 #[allow(dead_code)] 300 pub fn new(in_dragonos_path: Option<PathBuf>) -> Self { 301 Self { in_dragonos_path } 302 } 303 304 pub fn validate(&self) -> Result<(), String> { 305 if self.in_dragonos_path.is_none() { 306 return Ok(()); 307 } 308 if self.in_dragonos_path.as_ref().unwrap().is_relative() { 309 return Err("InstallConfig: in_dragonos_path should be an Absolute path".to_string()); 310 } 311 return Ok(()); 312 } 313 314 pub fn trim(&mut self) {} 315 } 316 317 impl From<DADKUserInstallConfig> for InstallConfig { 318 fn from(value: DADKUserInstallConfig) -> Self { 319 return InstallConfig { 320 in_dragonos_path: (value.in_dragonos_path), 321 }; 322 } 323 } 324 325 /// # 清理配置 326 #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] 327 pub struct CleanConfig { 328 /// 清理命令 329 pub clean_command: Option<String>, 330 } 331 332 impl CleanConfig { 333 #[allow(dead_code)] 334 pub fn new(clean_command: Option<String>) -> Self { 335 Self { clean_command } 336 } 337 338 pub fn validate(&self) -> Result<(), String> { 339 return Ok(()); 340 } 341 342 pub fn trim(&mut self) { 343 if let Some(clean_command) = &mut self.clean_command { 344 *clean_command = clean_command.trim().to_string(); 345 } 346 } 347 } 348 349 impl From<DADKUserCleanConfig> for CleanConfig { 350 fn from(value: DADKUserCleanConfig) -> Self { 351 return CleanConfig { 352 clean_command: value.clean_command, 353 }; 354 } 355 } 356 357 /// @brief 依赖项 358 #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)] 359 pub struct Dependency { 360 pub name: String, 361 pub version: String, 362 } 363 364 impl Dependency { 365 #[allow(dead_code)] 366 pub fn new(name: String, version: String) -> Self { 367 Self { name, version } 368 } 369 370 pub fn validate(&self) -> Result<(), String> { 371 if self.name.is_empty() { 372 return Err("name is empty".to_string()); 373 } 374 if self.version.is_empty() { 375 return Err("version is empty".to_string()); 376 } 377 return Ok(()); 378 } 379 380 pub fn trim(&mut self) { 381 self.name = self.name.trim().to_string(); 382 self.version = self.version.trim().to_string(); 383 } 384 385 pub fn name_version(&self) -> String { 386 return format!("{}-{}", self.name, self.version); 387 } 388 } 389 390 /// # 任务类型 391 #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] 392 pub enum TaskType { 393 /// 从源码构建 394 BuildFromSource(CodeSource), 395 /// 从预编译包安装 396 InstallFromPrebuilt(PrebuiltSource), 397 } 398 399 impl TaskType { 400 pub fn validate(&mut self) -> Result<(), String> { 401 match self { 402 TaskType::BuildFromSource(source) => source.validate(), 403 TaskType::InstallFromPrebuilt(source) => source.validate(), 404 } 405 } 406 407 pub fn trim(&mut self) { 408 match self { 409 TaskType::BuildFromSource(source) => source.trim(), 410 TaskType::InstallFromPrebuilt(source) => source.trim(), 411 } 412 } 413 } 414 415 impl TryFrom<DADKUserTaskType> for TaskType { 416 type Error = ParserError; 417 fn try_from(dadk_user_task_type: DADKUserTaskType) -> Result<Self, Self::Error> { 418 let task_type = DADKUserConfigKey::try_from(dadk_user_task_type.task_type.as_str()) 419 .map_err(|mut e| { 420 e.config_file = Some(dadk_user_task_type.config_file.clone()); 421 e 422 })?; 423 424 let source = 425 DADKUserConfigKey::try_from(dadk_user_task_type.source.as_str()).map_err(|mut e| { 426 e.config_file = Some(dadk_user_task_type.config_file.clone()); 427 e 428 })?; 429 430 match task_type { 431 DADKUserConfigKey::BuildFromSource => match source { 432 DADKUserConfigKey::Git => { 433 Ok(TaskType::BuildFromSource(CodeSource::Git(GitSource::new( 434 dadk_user_task_type.source_path, 435 dadk_user_task_type.branch, 436 dadk_user_task_type.revision, 437 )))) 438 } 439 DADKUserConfigKey::Local => Ok(TaskType::BuildFromSource(CodeSource::Local( 440 LocalSource::new(PathBuf::from(dadk_user_task_type.source_path)), 441 ))), 442 DADKUserConfigKey::Archive => Ok(TaskType::BuildFromSource(CodeSource::Archive( 443 ArchiveSource::new(dadk_user_task_type.source_path), 444 ))), 445 _ => Err(ParserError { 446 config_file: Some(dadk_user_task_type.config_file), 447 error: InnerParserError::TomlError(toml::de::Error::custom(format!( 448 "Unknown source: {}", 449 dadk_user_task_type.source 450 ))), 451 }), 452 }, 453 DADKUserConfigKey::InstallFromPrebuilt => match source { 454 DADKUserConfigKey::Local => { 455 Ok(TaskType::InstallFromPrebuilt(PrebuiltSource::Local( 456 LocalSource::new(PathBuf::from(dadk_user_task_type.source_path)), 457 ))) 458 } 459 DADKUserConfigKey::Archive => Ok(TaskType::InstallFromPrebuilt( 460 PrebuiltSource::Archive(ArchiveSource::new(dadk_user_task_type.source_path)), 461 )), 462 _ => Err(ParserError { 463 config_file: Some(dadk_user_task_type.config_file), 464 error: InnerParserError::TomlError(toml::de::Error::custom(format!( 465 "Unknown source: {}", 466 dadk_user_task_type.source 467 ))), 468 }), 469 }, 470 _ => Err(ParserError { 471 config_file: Some(dadk_user_task_type.config_file), 472 error: InnerParserError::TomlError(toml::de::Error::custom(format!( 473 "Unknown task type: {}", 474 dadk_user_task_type.task_type 475 ))), 476 }), 477 } 478 } 479 } 480 481 /// # 代码源 482 #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] 483 pub enum CodeSource { 484 /// 从Git仓库获取 485 Git(GitSource), 486 /// 从本地目录获取 487 Local(LocalSource), 488 /// 从在线压缩包获取 489 Archive(ArchiveSource), 490 } 491 492 impl CodeSource { 493 pub fn validate(&mut self) -> Result<(), String> { 494 match self { 495 CodeSource::Git(source) => source.validate(), 496 CodeSource::Local(source) => source.validate(Some(false)), 497 CodeSource::Archive(source) => source.validate(), 498 } 499 } 500 pub fn trim(&mut self) { 501 match self { 502 CodeSource::Git(source) => source.trim(), 503 CodeSource::Local(source) => source.trim(), 504 CodeSource::Archive(source) => source.trim(), 505 } 506 } 507 } 508 509 /// # 预编译包源 510 #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] 511 pub enum PrebuiltSource { 512 /// 从在线压缩包获取 513 Archive(ArchiveSource), 514 /// 从本地目录/文件获取 515 Local(LocalSource), 516 } 517 518 impl PrebuiltSource { 519 pub fn validate(&self) -> Result<(), String> { 520 match self { 521 PrebuiltSource::Archive(source) => source.validate(), 522 PrebuiltSource::Local(source) => source.validate(None), 523 } 524 } 525 526 pub fn trim(&mut self) { 527 match self { 528 PrebuiltSource::Archive(source) => source.trim(), 529 PrebuiltSource::Local(source) => source.trim(), 530 } 531 } 532 } 533 534 /// # 任务环境变量 535 /// 536 /// 任务执行时的环境变量.这个环境变量是在当前任务执行时设置的,不会影响到其他任务 537 #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)] 538 pub struct TaskEnv { 539 pub key: String, 540 pub value: String, 541 } 542 543 impl TaskEnv { 544 #[allow(dead_code)] 545 pub fn new(key: String, value: String) -> Self { 546 Self { key, value } 547 } 548 549 pub fn key(&self) -> &str { 550 &self.key 551 } 552 553 pub fn value(&self) -> &str { 554 &self.value 555 } 556 557 pub fn trim(&mut self) { 558 self.key = self.key.trim().to_string(); 559 self.value = self.value.trim().to_string(); 560 } 561 562 pub fn validate(&self) -> Result<(), String> { 563 if self.key.is_empty() { 564 return Err("Env: key is empty".to_string()); 565 } 566 return Ok(()); 567 } 568 } 569