]>
Commit | Line | Data |
---|---|---|
29967ef6 XL |
1 | //! Creation of ar archives like for the lib and staticlib crate type |
2 | ||
3 | use std::collections::BTreeMap; | |
4 | use std::fs::File; | |
5 | use std::path::{Path, PathBuf}; | |
6 | ||
7 | use rustc_codegen_ssa::back::archive::{find_library, ArchiveBuilder}; | |
8 | use rustc_codegen_ssa::METADATA_FILENAME; | |
9 | use rustc_session::Session; | |
10 | ||
fc512014 | 11 | use object::{Object, ObjectSymbol, SymbolKind}; |
29967ef6 XL |
12 | |
13 | #[derive(Debug)] | |
14 | enum ArchiveEntry { | |
6a06907d | 15 | FromArchive { archive_index: usize, entry_index: usize }, |
29967ef6 XL |
16 | File(PathBuf), |
17 | } | |
18 | ||
19 | pub(crate) struct ArArchiveBuilder<'a> { | |
20 | sess: &'a Session, | |
21 | dst: PathBuf, | |
22 | lib_search_paths: Vec<PathBuf>, | |
23 | use_gnu_style_archive: bool, | |
24 | no_builtin_ranlib: bool, | |
25 | ||
26 | src_archives: Vec<(PathBuf, ar::Archive<File>)>, | |
27 | // Don't use `HashMap` here, as the order is important. `rust.metadata.bin` must always be at | |
28 | // the end of an archive for linkers to not get confused. | |
29 | entries: Vec<(String, ArchiveEntry)>, | |
29967ef6 XL |
30 | } |
31 | ||
32 | impl<'a> ArchiveBuilder<'a> for ArArchiveBuilder<'a> { | |
33 | fn new(sess: &'a Session, output: &Path, input: Option<&Path>) -> Self { | |
34 | use rustc_codegen_ssa::back::link::archive_search_paths; | |
35 | ||
36 | let (src_archives, entries) = if let Some(input) = input { | |
37 | let mut archive = ar::Archive::new(File::open(input).unwrap()); | |
38 | let mut entries = Vec::new(); | |
39 | ||
40 | let mut i = 0; | |
41 | while let Some(entry) = archive.next_entry() { | |
42 | let entry = entry.unwrap(); | |
43 | entries.push(( | |
44 | String::from_utf8(entry.header().identifier().to_vec()).unwrap(), | |
6a06907d | 45 | ArchiveEntry::FromArchive { archive_index: 0, entry_index: i }, |
29967ef6 XL |
46 | )); |
47 | i += 1; | |
48 | } | |
49 | ||
50 | (vec![(input.to_owned(), archive)], entries) | |
51 | } else { | |
52 | (vec![], Vec::new()) | |
53 | }; | |
54 | ||
55 | ArArchiveBuilder { | |
56 | sess, | |
57 | dst: output.to_path_buf(), | |
58 | lib_search_paths: archive_search_paths(sess), | |
59 | use_gnu_style_archive: sess.target.archive_format == "gnu", | |
60 | // FIXME fix builtin ranlib on macOS | |
61 | no_builtin_ranlib: sess.target.is_like_osx, | |
62 | ||
63 | src_archives, | |
64 | entries, | |
29967ef6 XL |
65 | } |
66 | } | |
67 | ||
68 | fn src_files(&mut self) -> Vec<String> { | |
69 | self.entries.iter().map(|(name, _)| name.clone()).collect() | |
70 | } | |
71 | ||
72 | fn remove_file(&mut self, name: &str) { | |
73 | let index = self | |
74 | .entries | |
75 | .iter() | |
76 | .position(|(entry_name, _)| entry_name == name) | |
77 | .expect("Tried to remove file not existing in src archive"); | |
78 | self.entries.remove(index); | |
79 | } | |
80 | ||
81 | fn add_file(&mut self, file: &Path) { | |
82 | self.entries.push(( | |
83 | file.file_name().unwrap().to_str().unwrap().to_string(), | |
84 | ArchiveEntry::File(file.to_owned()), | |
85 | )); | |
86 | } | |
87 | ||
88 | fn add_native_library(&mut self, name: rustc_span::symbol::Symbol) { | |
89 | let location = find_library(name, &self.lib_search_paths, self.sess); | |
6a06907d XL |
90 | self.add_archive(location.clone(), |_| false).unwrap_or_else(|e| { |
91 | panic!("failed to add native library {}: {}", location.to_string_lossy(), e); | |
92 | }); | |
29967ef6 XL |
93 | } |
94 | ||
95 | fn add_rlib( | |
96 | &mut self, | |
97 | rlib: &Path, | |
98 | name: &str, | |
99 | lto: bool, | |
100 | skip_objects: bool, | |
101 | ) -> std::io::Result<()> { | |
102 | let obj_start = name.to_owned(); | |
103 | ||
104 | self.add_archive(rlib.to_owned(), move |fname: &str| { | |
105 | // Ignore metadata files, no matter the name. | |
106 | if fname == METADATA_FILENAME { | |
107 | return true; | |
108 | } | |
109 | ||
110 | // Don't include Rust objects if LTO is enabled | |
111 | if lto && fname.starts_with(&obj_start) && fname.ends_with(".o") { | |
112 | return true; | |
113 | } | |
114 | ||
115 | // Otherwise if this is *not* a rust object and we're skipping | |
116 | // objects then skip this file | |
117 | if skip_objects && (!fname.starts_with(&obj_start) || !fname.ends_with(".o")) { | |
118 | return true; | |
119 | } | |
120 | ||
121 | // ok, don't skip this | |
122 | false | |
123 | }) | |
124 | } | |
125 | ||
6a06907d | 126 | fn update_symbols(&mut self) {} |
29967ef6 XL |
127 | |
128 | fn build(mut self) { | |
129 | enum BuilderKind { | |
130 | Bsd(ar::Builder<File>), | |
131 | Gnu(ar::GnuBuilder<File>), | |
132 | } | |
133 | ||
134 | let sess = self.sess; | |
135 | ||
136 | let mut symbol_table = BTreeMap::new(); | |
137 | ||
138 | let mut entries = Vec::new(); | |
139 | ||
140 | for (entry_name, entry) in self.entries { | |
141 | // FIXME only read the symbol table of the object files to avoid having to keep all | |
142 | // object files in memory at once, or read them twice. | |
143 | let data = match entry { | |
6a06907d | 144 | ArchiveEntry::FromArchive { archive_index, entry_index } => { |
29967ef6 XL |
145 | // FIXME read symbols from symtab |
146 | use std::io::Read; | |
147 | let (ref _src_archive_path, ref mut src_archive) = | |
148 | self.src_archives[archive_index]; | |
149 | let mut entry = src_archive.jump_to_entry(entry_index).unwrap(); | |
150 | let mut data = Vec::new(); | |
151 | entry.read_to_end(&mut data).unwrap(); | |
152 | data | |
153 | } | |
154 | ArchiveEntry::File(file) => std::fs::read(file).unwrap_or_else(|err| { | |
155 | sess.fatal(&format!( | |
156 | "error while reading object file during archive building: {}", | |
157 | err | |
158 | )); | |
159 | }), | |
160 | }; | |
161 | ||
162 | if !self.no_builtin_ranlib { | |
163 | match object::File::parse(&data) { | |
164 | Ok(object) => { | |
165 | symbol_table.insert( | |
166 | entry_name.as_bytes().to_vec(), | |
167 | object | |
168 | .symbols() | |
fc512014 | 169 | .filter_map(|symbol| { |
29967ef6 XL |
170 | if symbol.is_undefined() |
171 | || symbol.is_local() | |
172 | || symbol.kind() != SymbolKind::Data | |
173 | && symbol.kind() != SymbolKind::Text | |
174 | && symbol.kind() != SymbolKind::Tls | |
175 | { | |
176 | None | |
177 | } else { | |
fc512014 | 178 | symbol.name().map(|name| name.as_bytes().to_vec()).ok() |
29967ef6 XL |
179 | } |
180 | }) | |
181 | .collect::<Vec<_>>(), | |
182 | ); | |
183 | } | |
184 | Err(err) => { | |
185 | let err = err.to_string(); | |
186 | if err == "Unknown file magic" { | |
187 | // Not an object file; skip it. | |
188 | } else { | |
189 | sess.fatal(&format!( | |
190 | "error parsing `{}` during archive creation: {}", | |
191 | entry_name, err | |
192 | )); | |
193 | } | |
194 | } | |
195 | } | |
196 | } | |
197 | ||
198 | entries.push((entry_name, data)); | |
199 | } | |
200 | ||
201 | let mut builder = if self.use_gnu_style_archive { | |
202 | BuilderKind::Gnu( | |
203 | ar::GnuBuilder::new( | |
204 | File::create(&self.dst).unwrap_or_else(|err| { | |
205 | sess.fatal(&format!( | |
206 | "error opening destination during archive building: {}", | |
207 | err | |
208 | )); | |
209 | }), | |
6a06907d | 210 | entries.iter().map(|(name, _)| name.as_bytes().to_vec()).collect(), |
29967ef6 XL |
211 | ar::GnuSymbolTableFormat::Size32, |
212 | symbol_table, | |
213 | ) | |
214 | .unwrap(), | |
215 | ) | |
216 | } else { | |
217 | BuilderKind::Bsd( | |
218 | ar::Builder::new( | |
219 | File::create(&self.dst).unwrap_or_else(|err| { | |
220 | sess.fatal(&format!( | |
221 | "error opening destination during archive building: {}", | |
222 | err | |
223 | )); | |
224 | }), | |
225 | symbol_table, | |
226 | ) | |
227 | .unwrap(), | |
228 | ) | |
229 | }; | |
230 | ||
231 | // Add all files | |
232 | for (entry_name, data) in entries.into_iter() { | |
233 | let header = ar::Header::new(entry_name.into_bytes(), data.len() as u64); | |
234 | match builder { | |
235 | BuilderKind::Bsd(ref mut builder) => builder.append(&header, &mut &*data).unwrap(), | |
236 | BuilderKind::Gnu(ref mut builder) => builder.append(&header, &mut &*data).unwrap(), | |
237 | } | |
238 | } | |
239 | ||
240 | // Finalize archive | |
241 | std::mem::drop(builder); | |
242 | ||
243 | if self.no_builtin_ranlib { | |
244 | let ranlib = crate::toolchain::get_toolchain_binary(self.sess, "ranlib"); | |
245 | ||
246 | // Run ranlib to be able to link the archive | |
247 | let status = std::process::Command::new(ranlib) | |
248 | .arg(self.dst) | |
249 | .status() | |
250 | .expect("Couldn't run ranlib"); | |
251 | ||
252 | if !status.success() { | |
6a06907d | 253 | self.sess.fatal(&format!("Ranlib exited with code {:?}", status.code())); |
29967ef6 XL |
254 | } |
255 | } | |
256 | } | |
257 | } | |
258 | ||
259 | impl<'a> ArArchiveBuilder<'a> { | |
260 | fn add_archive<F>(&mut self, archive_path: PathBuf, mut skip: F) -> std::io::Result<()> | |
261 | where | |
262 | F: FnMut(&str) -> bool + 'static, | |
263 | { | |
264 | let mut archive = ar::Archive::new(std::fs::File::open(&archive_path)?); | |
265 | let archive_index = self.src_archives.len(); | |
266 | ||
267 | let mut i = 0; | |
268 | while let Some(entry) = archive.next_entry() { | |
269 | let entry = entry?; | |
270 | let file_name = String::from_utf8(entry.header().identifier().to_vec()) | |
271 | .map_err(|err| std::io::Error::new(std::io::ErrorKind::InvalidData, err))?; | |
272 | if !skip(&file_name) { | |
6a06907d XL |
273 | self.entries |
274 | .push((file_name, ArchiveEntry::FromArchive { archive_index, entry_index: i })); | |
29967ef6 XL |
275 | } |
276 | i += 1; | |
277 | } | |
278 | ||
279 | self.src_archives.push((archive_path, archive)); | |
280 | Ok(()) | |
281 | } | |
282 | } |