|
| 1 | +use std::collections::HashMap; |
| 2 | +use std::fs::File; |
| 3 | +use std::path::PathBuf; |
| 4 | + |
| 5 | +use crate::prelude::*; |
| 6 | + |
| 7 | +pub struct ArchiveConfig<'a> { |
| 8 | + pub sess: &'a Session, |
| 9 | + pub dst: PathBuf, |
| 10 | + pub src: Option<PathBuf>, |
| 11 | + pub lib_search_paths: Vec<PathBuf>, |
| 12 | +} |
| 13 | + |
| 14 | +pub struct ArchiveBuilder<'a> { |
| 15 | + cfg: ArchiveConfig<'a>, |
| 16 | + src_archive: Option<ar::Archive<File>>, |
| 17 | + src_entries: HashMap<String, usize>, |
| 18 | + builder: ar::Builder<File>, |
| 19 | + update_symbols: bool, |
| 20 | +} |
| 21 | + |
| 22 | +impl<'a> ArchiveBuilder<'a> { |
| 23 | + pub fn new(cfg: ArchiveConfig<'a>) -> Self { |
| 24 | + let (src_archive, src_entries) = if let Some(src) = &cfg.src { |
| 25 | + let mut archive = ar::Archive::new(File::open(src).unwrap()); |
| 26 | + let mut entries = HashMap::new(); |
| 27 | + |
| 28 | + let mut i = 0; |
| 29 | + while let Some(entry) = archive.next_entry() { |
| 30 | + let entry = entry.unwrap(); |
| 31 | + entries.insert( |
| 32 | + String::from_utf8(entry.header().identifier().to_vec()).unwrap(), |
| 33 | + i, |
| 34 | + ); |
| 35 | + i += 1; |
| 36 | + } |
| 37 | + |
| 38 | + (Some(archive), entries) |
| 39 | + } else { |
| 40 | + (None, HashMap::new()) |
| 41 | + }; |
| 42 | + |
| 43 | + let builder = ar::Builder::new(File::create(&cfg.dst).unwrap()); |
| 44 | + |
| 45 | + ArchiveBuilder { |
| 46 | + cfg, |
| 47 | + src_archive, |
| 48 | + src_entries, |
| 49 | + builder, |
| 50 | + update_symbols: false, |
| 51 | + } |
| 52 | + } |
| 53 | + |
| 54 | + pub fn src_files(&self) -> Vec<String> { |
| 55 | + self.src_entries.keys().cloned().collect() |
| 56 | + } |
| 57 | + |
| 58 | + pub fn remove_file(&mut self, name: &str) { |
| 59 | + assert!( |
| 60 | + self.src_entries.remove(name).is_some(), |
| 61 | + "Tried to remove file not existing in src archive", |
| 62 | + ); |
| 63 | + } |
| 64 | + |
| 65 | + pub fn update_symbols(&mut self) { |
| 66 | + self.update_symbols = true; |
| 67 | + } |
| 68 | + |
| 69 | + pub fn build(mut self) { |
| 70 | + // Add files from original archive |
| 71 | + if let Some(mut src_archive) = self.src_archive { |
| 72 | + for (_entry_name, entry_idx) in self.src_entries.into_iter() { |
| 73 | + let entry = src_archive.jump_to_entry(entry_idx).unwrap(); |
| 74 | + let orig_header = entry.header(); |
| 75 | + let mut header = |
| 76 | + ar::Header::new(orig_header.identifier().to_vec(), orig_header.size()); |
| 77 | + header.set_mtime(orig_header.mtime()); |
| 78 | + header.set_uid(orig_header.uid()); |
| 79 | + header.set_gid(orig_header.gid()); |
| 80 | + header.set_mode(orig_header.mode()); |
| 81 | + self.builder.append(&header, entry).unwrap(); |
| 82 | + } |
| 83 | + } |
| 84 | + |
| 85 | + // Finalize archive |
| 86 | + std::mem::drop(self.builder); |
| 87 | + |
| 88 | + // Run ranlib to be able to link the archive |
| 89 | + let status = std::process::Command::new("ranlib") |
| 90 | + .arg(self.cfg.dst) |
| 91 | + .status() |
| 92 | + .expect("Couldn't run ranlib"); |
| 93 | + assert!( |
| 94 | + status.success(), |
| 95 | + "Ranlib exited with code {:?}", |
| 96 | + status.code() |
| 97 | + ); |
| 98 | + } |
| 99 | +} |
0 commit comments