]> git.lizzy.rs Git - rust.git/blob - src/archive.rs
d749d763402b810ebd35508cbb5ec1b33ea2dfba
[rust.git] / src / archive.rs
1 use std::fs::File;
2 use std::path::{Path, PathBuf};
3
4 use rustc_session::Session;
5 use rustc_codegen_ssa::back::archive::ArchiveBuilder;
6
7 use rustc_data_structures::temp_dir::MaybeTempDir;
8 use rustc_middle::middle::cstore::DllImport;
9
10
11 struct ArchiveConfig<'a> {
12     sess: &'a Session,
13     dst: PathBuf,
14     use_native_ar: bool,
15     use_gnu_style_archive: bool,
16 }
17
18 #[derive(Debug)]
19 enum ArchiveEntry {
20     FromArchive {
21         archive_index: usize,
22         entry_index: usize,
23     },
24     File(PathBuf),
25 }
26
27 pub struct ArArchiveBuilder<'a> {
28     config: ArchiveConfig<'a>,
29     src_archives: Vec<(PathBuf, ar::Archive<File>)>,
30     // Don't use `HashMap` here, as the order is important. `rust.metadata.bin` must always be at
31     // the end of an archive for linkers to not get confused.
32     entries: Vec<(String, ArchiveEntry)>,
33 }
34
35 impl<'a> ArchiveBuilder<'a> for ArArchiveBuilder<'a> {
36     fn new(sess: &'a Session, output: &Path, input: Option<&Path>) -> Self {
37         let config = ArchiveConfig {
38             sess,
39             dst: output.to_path_buf(),
40             use_native_ar: false,
41             // FIXME test for linux and System V derivatives instead
42             use_gnu_style_archive: sess.target.options.archive_format == "gnu",
43         };
44
45         let (src_archives, entries) = if let Some(input) = input {
46             let mut archive = ar::Archive::new(File::open(input).unwrap());
47             let mut entries = Vec::new();
48
49             let mut i = 0;
50             while let Some(entry) = archive.next_entry() {
51                 let entry = entry.unwrap();
52                 entries.push((
53                     String::from_utf8(entry.header().identifier().to_vec()).unwrap(),
54                     ArchiveEntry::FromArchive {
55                         archive_index: 0,
56                         entry_index: i,
57                     },
58                 ));
59                 i += 1;
60             }
61
62             (vec![(input.to_owned(), archive)], entries)
63         } else {
64             (vec![], Vec::new())
65         };
66
67         ArArchiveBuilder {
68             config,
69             src_archives,
70             entries,
71         }
72     }
73
74     fn src_files(&mut self) -> Vec<String> {
75         self.entries.iter().map(|(name, _)| name.clone()).collect()
76     }
77
78     fn remove_file(&mut self, name: &str) {
79         let index = self
80             .entries
81             .iter()
82             .position(|(entry_name, _)| entry_name == name)
83             .expect("Tried to remove file not existing in src archive");
84         self.entries.remove(index);
85     }
86
87     fn add_file(&mut self, file: &Path) {
88         self.entries.push((
89             file.file_name().unwrap().to_str().unwrap().to_string(),
90             ArchiveEntry::File(file.to_owned()),
91         ));
92     }
93
94     fn add_archive<F>(&mut self, archive_path: &Path, mut skip: F) -> std::io::Result<()>
95     where
96         F: FnMut(&str) -> bool + 'static,
97     {
98         let mut archive = ar::Archive::new(std::fs::File::open(&archive_path)?);
99         let archive_index = self.src_archives.len();
100
101         let mut i = 0;
102         while let Some(entry) = archive.next_entry() {
103             let entry = entry?;
104             let file_name = String::from_utf8(entry.header().identifier().to_vec())
105                 .map_err(|err| std::io::Error::new(std::io::ErrorKind::InvalidData, err))?;
106             if !skip(&file_name) {
107                 self.entries
108                     .push((file_name, ArchiveEntry::FromArchive { archive_index, entry_index: i }));
109             }
110             i += 1;
111         }
112
113         self.src_archives.push((archive_path.to_owned(), archive));
114         Ok(())
115     }
116
117     fn update_symbols(&mut self) {
118     }
119
120     fn build(mut self) {
121         use std::process::Command;
122
123         fn add_file_using_ar(archive: &Path, file: &Path) {
124             Command::new("ar")
125                 .arg("r") // add or replace file
126                 .arg("-c") // silence created file message
127                 .arg(archive)
128                 .arg(&file)
129                 .status()
130                 .unwrap();
131         }
132
133         enum BuilderKind<'a> {
134             Bsd(ar::Builder<File>),
135             Gnu(ar::GnuBuilder<File>),
136             NativeAr(&'a Path),
137         }
138
139         let mut builder = if self.config.use_native_ar {
140             BuilderKind::NativeAr(&self.config.dst)
141         } else if self.config.use_gnu_style_archive {
142             BuilderKind::Gnu(ar::GnuBuilder::new(
143                 File::create(&self.config.dst).unwrap(),
144                 self.entries
145                     .iter()
146                     .map(|(name, _)| name.as_bytes().to_vec())
147                     .collect(),
148             ))
149         } else {
150             BuilderKind::Bsd(ar::Builder::new(File::create(&self.config.dst).unwrap()))
151         };
152
153         // Add all files
154         for (entry_name, entry) in self.entries.into_iter() {
155             match entry {
156                 ArchiveEntry::FromArchive {
157                     archive_index,
158                     entry_index,
159                 } => {
160                     let (ref src_archive_path, ref mut src_archive) =
161                         self.src_archives[archive_index];
162                     let entry = src_archive.jump_to_entry(entry_index).unwrap();
163                     let header = entry.header().clone();
164
165                     match builder {
166                         BuilderKind::Bsd(ref mut builder) => {
167                             builder.append(&header, entry).unwrap()
168                         }
169                         BuilderKind::Gnu(ref mut builder) => {
170                             builder.append(&header, entry).unwrap()
171                         }
172                         BuilderKind::NativeAr(archive_file) => {
173                             Command::new("ar")
174                                 .arg("x")
175                                 .arg(src_archive_path)
176                                 .arg(&entry_name)
177                                 .status()
178                                 .unwrap();
179                             add_file_using_ar(archive_file, Path::new(&entry_name));
180                             std::fs::remove_file(entry_name).unwrap();
181                         }
182                     }
183                 }
184                 ArchiveEntry::File(file) =>
185                     match builder {
186                         BuilderKind::Bsd(ref mut builder) => {
187                             builder
188                                 .append_file(entry_name.as_bytes(), &mut File::open(file).expect("file for bsd builder"))
189                                 .unwrap()
190                         },
191                         BuilderKind::Gnu(ref mut builder) => {
192                             builder
193                                 .append_file(entry_name.as_bytes(), &mut File::open(&file).expect(&format!("file {:?} for gnu builder", file)))
194                                 .unwrap()
195                         },
196                         BuilderKind::NativeAr(archive_file) => add_file_using_ar(archive_file, &file),
197                     },
198             }
199         }
200
201         // Finalize archive
202         std::mem::drop(builder);
203
204         // Run ranlib to be able to link the archive
205         let status = std::process::Command::new("ranlib")
206             .arg(self.config.dst)
207             .status()
208             .expect("Couldn't run ranlib");
209
210         if !status.success() {
211             self.config.sess.fatal(&format!("Ranlib exited with code {:?}", status.code()));
212         }
213     }
214
215     fn inject_dll_import_lib(&mut self, _lib_name: &str, _dll_imports: &[DllImport], _tmpdir: &MaybeTempDir) {
216         unimplemented!();
217     }
218 }