Skip to content

Instantly share code, notes, and snippets.

@jwinarske
Last active October 16, 2022 19:08
Show Gist options
  • Save jwinarske/69e23129c0d822f0a7eb72335e95459d to your computer and use it in GitHub Desktop.
Save jwinarske/69e23129c0d822f0a7eb72335e95459d to your computer and use it in GitHub Desktop.
pattern to get all Yocto crate dependencies
use std::env;
use std::path::Path;
use std::path::PathBuf;
use cargo_metadata::CargoOpt;
use clap::Parser;
use git2::{Cred, Oid, RemoteCallbacks};
use glob::glob;
use indexmap::IndexSet;
use tempfile::tempdir;
#[derive(Parser)]
#[command(name = "CargoDeps")]
#[command(author = "Joel Winarske <[email protected]>")]
#[command(version = "1.0")]
#[command(about = "Lists Yocto Crate Dependencies of a Root Package Workspace", long_about = None)]
struct Cli {
#[arg(long)]
manifest_path: String,
}
#[derive(Eq, Hash, PartialEq)]
struct GitRepo {
url: String,
commit: String,
}
fn dump_metadata(path: impl Into<PathBuf>, crates: &mut IndexSet<String>, git: &mut IndexSet<GitRepo>) -> Vec<String> {
let mut file_list = Vec::new();
let _metadata = cargo_metadata::MetadataCommand::new()
.manifest_path(path)
.features(CargoOpt::AllFeatures)
.exec()
.unwrap();
println!("workspace_root: {}", _metadata.workspace_root);
println!("target_directory: {}", _metadata.target_directory);
//let _members = _metadata.workspace_members;
//for _member in _members.iter() {
// println!("member: {}", _member.repr);
//}
let _resolve = _metadata.resolve.unwrap();
let _nodes = _resolve.nodes;
for _node in _nodes.iter() {
let iter: Vec<_> = _node.id.repr.split_whitespace().collect();
if iter[2] == "(registry+https://github.com/rust-lang/crates.io-index)" {
let mut crate_repo: String = "crate://crates.io/".to_owned();
let crate_name: String = iter[0].to_owned();
let crate_version: String = iter[1].to_owned();
crate_repo.push_str(&crate_name);
crate_repo.push_str(&*"/".to_owned());
crate_repo.push_str(&crate_version);
crates.insert(crate_repo);
} else if iter[2].contains("(path+") {
let repo: Vec<_> = iter[2].split('+').collect();
let repository = repo[1].replace(")", "");
let path: Vec<_> = repository.split("file://").collect();
file_list.push(path[1].to_owned());
} else if iter[2].contains("(git+") {
let repo: Vec<_> = iter[2].split('+').collect();
let repository = repo[1].replace(")", "");
let elements: Vec<_> = repository.split(&['?', '#'][..]).collect();
let url = elements[0].to_owned();
let commit;
if elements.len() > 2 {
commit = elements[2].to_owned();
} else {
commit = elements[1].to_owned();
}
let git_repo = GitRepo {
url,
commit,
};
git.insert(git_repo);
} else {
println!("[else] {}", iter[2]);
}
}
return file_list;
}
fn get_repo_folder_name(url: String) -> String {
let last = url.split('/')
.last()
.unwrap()
.to_string();
let res: Vec<_> = last.split(".git").collect();
return res[0].to_string();
}
fn main() {
let cli = Cli::parse();
println!("manifest-path: {:?}", cli.manifest_path);
let mut crate_list = IndexSet::new();
let mut git_list = IndexSet::new();
let file_list = dump_metadata(cli.manifest_path, &mut crate_list, &mut git_list);
println!();
for _file in file_list {
println!("{}", _file);
}
println!();
let dir = tempdir().unwrap();
// Prepare callbacks.
let mut callbacks = RemoteCallbacks::new();
callbacks.credentials(|_url, username_from_url, _allowed_types| {
Cred::ssh_key(
username_from_url.unwrap(),
None,
Path::new(&format!("{}/.ssh/id_rsa", env::var("HOME").unwrap())),
None,
)
});
// Prepare fetch options.
let mut fo = git2::FetchOptions::new();
fo.remote_callbacks(callbacks);
// Prepare builder.
let mut builder = git2::build::RepoBuilder::new();
builder.fetch_options(fo);
for _git in git_list.iter() {
let sub_folder = get_repo_folder_name(_git.url.to_string());
let folder = dir.path().join(sub_folder);
let repo = builder.clone(&_git.url, Path::new(&folder)).expect("failed to clone repository");
let oid = Oid::from_str(&_git.commit).unwrap();
let commit = repo.find_commit(oid).unwrap();
let _ = repo.branch(
&_git.commit,
&commit,
false,
);
let obj = repo.revparse_single(&("refs/heads/".to_owned() + &_git.commit)).unwrap();
let _ = repo.checkout_tree(
&obj,
None,
);
let _ = repo.set_head(&("refs/heads/".to_owned() + &_git.commit));
let _glob = String::from(folder.join("**/Cargo.toml").to_string_lossy());
for entry in glob(&_glob).unwrap() {
match entry {
Ok(manifest) => {
// println!("{:?}", manifest);
let mut tmp_crate_list = IndexSet::new();
let mut tmp_git_list = IndexSet::new();
let _ = dump_metadata(manifest, &mut tmp_crate_list, &mut tmp_git_list);
}
Err(e) => println!("Err: {:?}", e),
}
}
}
dir.close().unwrap();
println!();
let mut count = 0;
for _crate in crate_list.iter() {
println!(" {} \\", _crate);
count = count + 1;
}
println!("\nCount: {}", count);
}
@jwinarske
Copy link
Author

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment