From 6f81a58a03d4d4a20090779b2edecc995267df52 Mon Sep 17 00:00:00 2001 From: jiangjianfeng Date: Fri, 17 Sep 2021 14:10:06 +0800 Subject: [PATCH] Find all included bom files recursively --- tools/copy_bom/src/bom.rs | 65 ++++++++++++++++++++++++++++++++++++++ tools/copy_bom/src/util.rs | 45 ++++++++++++++++++++++++++ 2 files changed, 110 insertions(+) create mode 100644 tools/copy_bom/src/util.rs diff --git a/tools/copy_bom/src/bom.rs b/tools/copy_bom/src/bom.rs index d677da44..c5494696 100644 --- a/tools/copy_bom/src/bom.rs +++ b/tools/copy_bom/src/bom.rs @@ -206,3 +206,68 @@ impl BomManagement { .for_each(|(src, dest)| copy_shared_object(src, dest, dry_run)); } } + +/// This function will return all included bom files in the order to deal with. +/// This function operates in such a way: It starts from putting the root bom into a queue, +/// In each iteration of the loop, it will fetch the first bom from the head of the queue, +/// Then it find all included files of the bom file. The all included bom files will put into the queue as well as a vector(sorted boms). +/// The loop will end if there's no more elements in the queue. +/// There is also a max_iteration bound. If the loop exceeds the bound and the queue is not empty, the function will abort the program. +/// Because excess of the bound often means there's a reference cycles in the bom tree, which is an invalid case. +/// After we visit all boms in the queue, we will get all boms sorted in the order of being included in the vector. +/// Then we will remove redudant boms in the vector. For a bom file that may exist more than one time, +/// only the last one will be kept in the final result. To remove redundancy, we will reverse the vector, +/// and only keep the first one for each duplicate bom. +fn find_all_included_bom_files(bom_file: &str, included_dirs: &Vec) -> Vec { + let mut boms = VecDeque::new(); + let mut sorted_boms = Vec::new(); + const MAX_ITERATION: usize = 100; + + boms.push_back(bom_file.to_string()); + sorted_boms.push(bom_file.to_string()); + for _ in 0..MAX_ITERATION { + if boms.is_empty() { + break; + } + // This unwrap can never fail + let current_bom = boms.pop_front().unwrap(); + let bom = Bom::from_yaml_file(¤t_bom); + // find includes for current bom + if let Some(includes) = bom.includes { + includes.into_iter().for_each(|include| { + let included_bom_file = + find_included_bom_file(&include, ¤t_bom, included_dirs); + boms.push_back(included_bom_file.clone()); + sorted_boms.push(included_bom_file); + }); + } + } + if !boms.is_empty() { + // The iteration exceeds the MAX_ITERATION and there still are elements in the queue. + error!("The bom file number exceeds the MAX_ITERATION bound. Please check if there is including cycle."); + std::process::exit(INVALID_BOM_FILE_ERROR); + } + // remove redundant boms in sorted boms + sorted_boms.reverse(); + let mut res = remove_redundant_items_in_vec(&sorted_boms, Vec::new().iter()); + res.reverse(); + res +} + +// remove redundant items in a vec. For duplicate items, only the first item will be reserved +fn remove_redundant_items_in_vec(raw: &Vec, excludes: Iter<'_, T>) -> Vec +where + T: Hash + Eq + Clone, +{ + let mut exists = HashSet::new(); + for item in excludes { + exists.insert(item.clone()); + } + let mut res = Vec::new(); + for item in raw { + if exists.insert(item.clone()) { + res.push(item.clone()); + } + } + res +} diff --git a/tools/copy_bom/src/util.rs b/tools/copy_bom/src/util.rs new file mode 100644 index 00000000..e415ab80 --- /dev/null +++ b/tools/copy_bom/src/util.rs @@ -0,0 +1,45 @@ +use crate::error::{ + COPY_DIR_ERROR, COPY_FILE_ERROR, CREATE_DIR_ERROR, CREATE_SYMLINK_ERROR, FILE_NOT_EXISTS_ERROR, + INCORRECT_HASH_ERROR, +}; +use data_encoding::HEXUPPER; +use regex::Regex; +use sha2::{Digest, Sha256}; +use std::collections::{HashMap, HashSet}; +use std::path::PathBuf; +use std::process::{Command, Output}; +use std::vec; + +/// find an included file in the file system. If we can find the bom file, return the path +/// otherwise, the process exit with error +/// if included dir is relative path, if will be viewed as path relative to the `current` path (where we execute command) +pub fn find_included_bom_file( + included_file: &str, + bom_file: &str, + included_dirs: &Vec, +) -> String { + let bom_file_path = PathBuf::from(bom_file); + let bom_file_dir_path = bom_file_path + .parent() + .map_or(PathBuf::from("."), |p| p.to_path_buf()); + // first, we find the included bom file in the current dir of the bom file + let included_file_path = bom_file_dir_path.join(included_file); + if included_file_path.is_file() { + return included_file_path.to_string_lossy().to_string(); + } + // Then, we find the bom file in each included dir. + for included_dir in included_dirs { + let included_dir_path = std::env::current_dir().unwrap().join(included_dir); + let included_file_path = included_dir_path.join(included_file); + if included_file_path.is_file() { + return included_file_path.to_string_lossy().to_string(); + } + } + // fail to find the bom file + error!( + "cannot find included bom file {} in {}.", + included_file, bom_file + ); + std::process::exit(FILE_NOT_EXISTS_ERROR); +} +