]> git.lizzy.rs Git - rust.git/blob - src/librustc_codegen_llvm/back/archive.rs
Add riscv64gc-unknown-none-elf target
[rust.git] / src / librustc_codegen_llvm / back / archive.rs
1 //! A helper class for dealing with static archives
2
3 use std::ffi::{CString, CStr};
4 use std::io;
5 use std::mem;
6 use std::path::{Path, PathBuf};
7 use std::ptr;
8 use std::str;
9
10 use back::bytecode::RLIB_BYTECODE_EXTENSION;
11 use rustc_codegen_ssa::back::archive::find_library;
12 use libc;
13 use llvm::archive_ro::{ArchiveRO, Child};
14 use llvm::{self, ArchiveKind};
15 use metadata::METADATA_FILENAME;
16 use rustc::session::Session;
17
18 pub struct ArchiveConfig<'a> {
19     pub sess: &'a Session,
20     pub dst: PathBuf,
21     pub src: Option<PathBuf>,
22     pub lib_search_paths: Vec<PathBuf>,
23 }
24
25 /// Helper for adding many files to an archive.
26 #[must_use = "must call build() to finish building the archive"]
27 pub struct ArchiveBuilder<'a> {
28     config: ArchiveConfig<'a>,
29     removals: Vec<String>,
30     additions: Vec<Addition>,
31     should_update_symbols: bool,
32     src_archive: Option<Option<ArchiveRO>>,
33 }
34
35 enum Addition {
36     File {
37         path: PathBuf,
38         name_in_archive: String,
39     },
40     Archive {
41         archive: ArchiveRO,
42         skip: Box<dyn FnMut(&str) -> bool>,
43     },
44 }
45
46 fn is_relevant_child(c: &Child) -> bool {
47     match c.name() {
48         Some(name) => !name.contains("SYMDEF"),
49         None => false,
50     }
51 }
52
53 impl<'a> ArchiveBuilder<'a> {
54     /// Create a new static archive, ready for modifying the archive specified
55     /// by `config`.
56     pub fn new(config: ArchiveConfig<'a>) -> ArchiveBuilder<'a> {
57         ArchiveBuilder {
58             config,
59             removals: Vec::new(),
60             additions: Vec::new(),
61             should_update_symbols: false,
62             src_archive: None,
63         }
64     }
65
66     /// Removes a file from this archive
67     pub fn remove_file(&mut self, file: &str) {
68         self.removals.push(file.to_string());
69     }
70
71     /// Lists all files in an archive
72     pub fn src_files(&mut self) -> Vec<String> {
73         if self.src_archive().is_none() {
74             return Vec::new()
75         }
76
77         let archive = self.src_archive.as_ref().unwrap().as_ref().unwrap();
78
79         archive.iter()
80                .filter_map(|child| child.ok())
81                .filter(is_relevant_child)
82                .filter_map(|child| child.name())
83                .filter(|name| !self.removals.iter().any(|x| x == name))
84                .map(|name| name.to_owned())
85                .collect()
86     }
87
88     fn src_archive(&mut self) -> Option<&ArchiveRO> {
89         if let Some(ref a) = self.src_archive {
90             return a.as_ref()
91         }
92         let src = self.config.src.as_ref()?;
93         self.src_archive = Some(ArchiveRO::open(src).ok());
94         self.src_archive.as_ref().unwrap().as_ref()
95     }
96
97     /// Adds all of the contents of a native library to this archive. This will
98     /// search in the relevant locations for a library named `name`.
99     pub fn add_native_library(&mut self, name: &str) {
100         let location = find_library(name, &self.config.lib_search_paths,
101                                     self.config.sess);
102         self.add_archive(&location, |_| false).unwrap_or_else(|e| {
103             self.config.sess.fatal(&format!("failed to add native library {}: {}",
104                                             location.to_string_lossy(), e));
105         });
106     }
107
108     /// Adds all of the contents of the rlib at the specified path to this
109     /// archive.
110     ///
111     /// This ignores adding the bytecode from the rlib, and if LTO is enabled
112     /// then the object file also isn't added.
113     pub fn add_rlib(&mut self,
114                     rlib: &Path,
115                     name: &str,
116                     lto: bool,
117                     skip_objects: bool) -> io::Result<()> {
118         // Ignoring obj file starting with the crate name
119         // as simple comparison is not enough - there
120         // might be also an extra name suffix
121         let obj_start = name.to_owned();
122
123         self.add_archive(rlib, move |fname: &str| {
124             // Ignore bytecode/metadata files, no matter the name.
125             if fname.ends_with(RLIB_BYTECODE_EXTENSION) || fname == METADATA_FILENAME {
126                 return true
127             }
128
129             // Don't include Rust objects if LTO is enabled
130             if lto && fname.starts_with(&obj_start) && fname.ends_with(".o") {
131                 return true
132             }
133
134             // Otherwise if this is *not* a rust object and we're skipping
135             // objects then skip this file
136             if skip_objects && (!fname.starts_with(&obj_start) || !fname.ends_with(".o")) {
137                 return true
138             }
139
140             // ok, don't skip this
141             return false
142         })
143     }
144
145     fn add_archive<F>(&mut self, archive: &Path, skip: F)
146                       -> io::Result<()>
147         where F: FnMut(&str) -> bool + 'static
148     {
149         let archive = match ArchiveRO::open(archive) {
150             Ok(ar) => ar,
151             Err(e) => return Err(io::Error::new(io::ErrorKind::Other, e)),
152         };
153         self.additions.push(Addition::Archive {
154             archive,
155             skip: Box::new(skip),
156         });
157         Ok(())
158     }
159
160     /// Adds an arbitrary file to this archive
161     pub fn add_file(&mut self, file: &Path) {
162         let name = file.file_name().unwrap().to_str().unwrap();
163         self.additions.push(Addition::File {
164             path: file.to_path_buf(),
165             name_in_archive: name.to_owned(),
166         });
167     }
168
169     /// Indicate that the next call to `build` should update all symbols in
170     /// the archive (equivalent to running 'ar s' over it).
171     pub fn update_symbols(&mut self) {
172         self.should_update_symbols = true;
173     }
174
175     /// Combine the provided files, rlibs, and native libraries into a single
176     /// `Archive`.
177     pub fn build(&mut self) {
178         let kind = self.llvm_archive_kind().unwrap_or_else(|kind|
179             self.config.sess.fatal(&format!("Don't know how to build archive of type: {}", kind)));
180
181         if let Err(e) = self.build_with_llvm(kind) {
182             self.config.sess.fatal(&format!("failed to build archive: {}", e));
183         }
184
185     }
186
187     fn llvm_archive_kind(&self) -> Result<ArchiveKind, &str> {
188         let kind = &*self.config.sess.target.target.options.archive_format;
189         kind.parse().map_err(|_| kind)
190     }
191
192     fn build_with_llvm(&mut self, kind: ArchiveKind) -> io::Result<()> {
193         let removals = mem::replace(&mut self.removals, Vec::new());
194         let mut additions = mem::replace(&mut self.additions, Vec::new());
195         let mut strings = Vec::new();
196         let mut members = Vec::new();
197
198         let dst = CString::new(self.config.dst.to_str().unwrap())?;
199         let should_update_symbols = self.should_update_symbols;
200
201         unsafe {
202             if let Some(archive) = self.src_archive() {
203                 for child in archive.iter() {
204                     let child = child.map_err(string_to_io_error)?;
205                     let child_name = match child.name() {
206                         Some(s) => s,
207                         None => continue,
208                     };
209                     if removals.iter().any(|r| r == child_name) {
210                         continue
211                     }
212
213                     let name = CString::new(child_name)?;
214                     members.push(llvm::LLVMRustArchiveMemberNew(ptr::null(),
215                                                                 name.as_ptr(),
216                                                                 Some(child.raw)));
217                     strings.push(name);
218                 }
219             }
220             for addition in &mut additions {
221                 match addition {
222                     Addition::File { path, name_in_archive } => {
223                         let path = CString::new(path.to_str().unwrap())?;
224                         let name = CString::new(name_in_archive.clone())?;
225                         members.push(llvm::LLVMRustArchiveMemberNew(path.as_ptr(),
226                                                                     name.as_ptr(),
227                                                                     None));
228                         strings.push(path);
229                         strings.push(name);
230                     }
231                     Addition::Archive { archive, skip } => {
232                         for child in archive.iter() {
233                             let child = child.map_err(string_to_io_error)?;
234                             if !is_relevant_child(&child) {
235                                 continue
236                             }
237                             let child_name = child.name().unwrap();
238                             if skip(child_name) {
239                                 continue
240                             }
241
242                             // It appears that LLVM's archive writer is a little
243                             // buggy if the name we pass down isn't just the
244                             // filename component, so chop that off here and
245                             // pass it in.
246                             //
247                             // See LLVM bug 25877 for more info.
248                             let child_name = Path::new(child_name)
249                                                   .file_name().unwrap()
250                                                   .to_str().unwrap();
251                             let name = CString::new(child_name)?;
252                             let m = llvm::LLVMRustArchiveMemberNew(ptr::null(),
253                                                                    name.as_ptr(),
254                                                                    Some(child.raw));
255                             members.push(m);
256                             strings.push(name);
257                         }
258                     }
259                 }
260             }
261
262             let r = llvm::LLVMRustWriteArchive(dst.as_ptr(),
263                                                members.len() as libc::size_t,
264                                                members.as_ptr() as *const &_,
265                                                should_update_symbols,
266                                                kind);
267             let ret = if r.into_result().is_err() {
268                 let err = llvm::LLVMRustGetLastError();
269                 let msg = if err.is_null() {
270                     "failed to write archive".into()
271                 } else {
272                     String::from_utf8_lossy(CStr::from_ptr(err).to_bytes())
273                 };
274                 Err(io::Error::new(io::ErrorKind::Other, msg))
275             } else {
276                 Ok(())
277             };
278             for member in members {
279                 llvm::LLVMRustArchiveMemberFree(member);
280             }
281             ret
282         }
283     }
284 }
285
286 fn string_to_io_error(s: String) -> io::Error {
287     io::Error::new(io::ErrorKind::Other, format!("bad archive: {}", s))
288 }