]> git.lizzy.rs Git - rust.git/blob - src/librustc/back/archive.rs
doc/guide-ffi: A few minor typo/language fixes
[rust.git] / src / librustc / back / archive.rs
1 // Copyright 2013-2014 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
4 //
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
10
11 //! A helper class for dealing with static archives
12
13 use back::link::{get_ar_prog};
14 use driver::session::Session;
15 use metadata::filesearch;
16 use lib::llvm::{ArchiveRef, llvm};
17
18 use libc;
19 use std::io::process::{Command, ProcessOutput};
20 use std::io::{fs, TempDir};
21 use std::io;
22 use std::mem;
23 use std::os;
24 use std::raw;
25 use std::str;
26 use syntax::abi;
27
28 pub static METADATA_FILENAME: &'static str = "rust.metadata.bin";
29
30 pub struct Archive<'a> {
31     sess: &'a Session,
32     dst: Path,
33 }
34
35 pub struct ArchiveRO {
36     ptr: ArchiveRef,
37 }
38
39 fn run_ar(sess: &Session, args: &str, cwd: Option<&Path>,
40           paths: &[&Path]) -> ProcessOutput {
41     let ar = get_ar_prog(sess);
42     let mut cmd = Command::new(ar.as_slice());
43
44     cmd.arg(args).args(paths);
45     debug!("{}", cmd);
46
47     match cwd {
48         Some(p) => {
49             cmd.cwd(p);
50             debug!("inside {}", p.display());
51         }
52         None => {}
53     }
54
55     match cmd.spawn() {
56         Ok(prog) => {
57             let o = prog.wait_with_output().unwrap();
58             if !o.status.success() {
59                 sess.err(format!("{} failed with: {}",
60                                  cmd,
61                                  o.status).as_slice());
62                 sess.note(format!("stdout ---\n{}",
63                                   str::from_utf8(o.output
64                                                   .as_slice()).unwrap())
65                           .as_slice());
66                 sess.note(format!("stderr ---\n{}",
67                                   str::from_utf8(o.error
68                                                   .as_slice()).unwrap())
69                           .as_slice());
70                 sess.abort_if_errors();
71             }
72             o
73         },
74         Err(e) => {
75             sess.err(format!("could not exec `{}`: {}", ar.as_slice(),
76                              e).as_slice());
77             sess.abort_if_errors();
78             fail!("rustc::back::archive::run_ar() should not reach this point");
79         }
80     }
81 }
82
83 impl<'a> Archive<'a> {
84     /// Initializes a new static archive with the given object file
85     pub fn create<'b>(sess: &'a Session, dst: &'b Path,
86                       initial_object: &'b Path) -> Archive<'a> {
87         run_ar(sess, "crus", None, [dst, initial_object]);
88         Archive { sess: sess, dst: dst.clone() }
89     }
90
91     /// Opens an existing static archive
92     pub fn open(sess: &'a Session, dst: Path) -> Archive<'a> {
93         assert!(dst.exists());
94         Archive { sess: sess, dst: dst }
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) -> io::IoResult<()> {
100         let location = self.find_library(name);
101         self.add_archive(&location, name, [])
102     }
103
104     /// Adds all of the contents of the rlib at the specified path to this
105     /// archive.
106     ///
107     /// This ignores adding the bytecode from the rlib, and if LTO is enabled
108     /// then the object file also isn't added.
109     pub fn add_rlib(&mut self, rlib: &Path, name: &str,
110                     lto: bool) -> io::IoResult<()> {
111         let object = format!("{}.o", name);
112         let bytecode = format!("{}.bytecode.deflate", name);
113         let mut ignore = vec!(bytecode.as_slice(), METADATA_FILENAME);
114         if lto {
115             ignore.push(object.as_slice());
116         }
117         self.add_archive(rlib, name, ignore.as_slice())
118     }
119
120     /// Adds an arbitrary file to this archive
121     pub fn add_file(&mut self, file: &Path, has_symbols: bool) {
122         let cmd = if has_symbols {"r"} else {"rS"};
123         run_ar(self.sess, cmd, None, [&self.dst, file]);
124     }
125
126     /// Removes a file from this archive
127     pub fn remove_file(&mut self, file: &str) {
128         run_ar(self.sess, "d", None, [&self.dst, &Path::new(file)]);
129     }
130
131     /// Updates all symbols in the archive (runs 'ar s' over it)
132     pub fn update_symbols(&mut self) {
133         run_ar(self.sess, "s", None, [&self.dst]);
134     }
135
136     /// Lists all files in an archive
137     pub fn files(&self) -> Vec<String> {
138         let output = run_ar(self.sess, "t", None, [&self.dst]);
139         let output = str::from_utf8(output.output.as_slice()).unwrap();
140         // use lines_any because windows delimits output with `\r\n` instead of
141         // just `\n`
142         output.lines_any().map(|s| s.to_string()).collect()
143     }
144
145     fn add_archive(&mut self, archive: &Path, name: &str,
146                    skip: &[&str]) -> io::IoResult<()> {
147         let loc = TempDir::new("rsar").unwrap();
148
149         // First, extract the contents of the archive to a temporary directory
150         let archive = os::make_absolute(archive);
151         run_ar(self.sess, "x", Some(loc.path()), [&archive]);
152
153         // Next, we must rename all of the inputs to "guaranteed unique names".
154         // The reason for this is that archives are keyed off the name of the
155         // files, so if two files have the same name they will override one
156         // another in the archive (bad).
157         //
158         // We skip any files explicitly desired for skipping, and we also skip
159         // all SYMDEF files as these are just magical placeholders which get
160         // re-created when we make a new archive anyway.
161         let files = try!(fs::readdir(loc.path()));
162         let mut inputs = Vec::new();
163         for file in files.iter() {
164             let filename = file.filename_str().unwrap();
165             if skip.iter().any(|s| *s == filename) { continue }
166             if filename.contains(".SYMDEF") { continue }
167
168             let filename = format!("r-{}-{}", name, filename);
169             // LLDB (as mentioned in back::link) crashes on filenames of exactly
170             // 16 bytes in length. If we're including an object file with
171             // exactly 16-bytes of characters, give it some prefix so that it's
172             // not 16 bytes.
173             let filename = if filename.len() == 16 {
174                 format!("lldb-fix-{}", filename)
175             } else {
176                 filename
177             };
178             let new_filename = file.with_filename(filename);
179             try!(fs::rename(file, &new_filename));
180             inputs.push(new_filename);
181         }
182         if inputs.len() == 0 { return Ok(()) }
183
184         // Finally, add all the renamed files to this archive
185         let mut args = vec!(&self.dst);
186         args.extend(inputs.iter());
187         run_ar(self.sess, "r", None, args.as_slice());
188         Ok(())
189     }
190
191     fn find_library(&self, name: &str) -> Path {
192         let (osprefix, osext) = match self.sess.targ_cfg.os {
193             abi::OsWin32 => ("", "lib"), _ => ("lib", "a"),
194         };
195         // On Windows, static libraries sometimes show up as libfoo.a and other
196         // times show up as foo.lib
197         let oslibname = format!("{}{}.{}", osprefix, name, osext);
198         let unixlibname = format!("lib{}.a", name);
199
200         let mut rustpath = filesearch::rust_path();
201         rustpath.push(self.sess.target_filesearch().get_lib_path());
202         let search = self.sess.opts.addl_lib_search_paths.borrow();
203         for path in search.iter().chain(rustpath.iter()) {
204             debug!("looking for {} inside {}", name, path.display());
205             let test = path.join(oslibname.as_slice());
206             if test.exists() { return test }
207             if oslibname != unixlibname {
208                 let test = path.join(unixlibname.as_slice());
209                 if test.exists() { return test }
210             }
211         }
212         self.sess.fatal(format!("could not find native static library `{}`, \
213                                  perhaps an -L flag is missing?",
214                                 name).as_slice());
215     }
216 }
217
218 impl ArchiveRO {
219     /// Opens a static archive for read-only purposes. This is more optimized
220     /// than the `open` method because it uses LLVM's internal `Archive` class
221     /// rather than shelling out to `ar` for everything.
222     ///
223     /// If this archive is used with a mutable method, then an error will be
224     /// raised.
225     pub fn open(dst: &Path) -> Option<ArchiveRO> {
226         unsafe {
227             let ar = dst.with_c_str(|dst| {
228                 llvm::LLVMRustOpenArchive(dst)
229             });
230             if ar.is_null() {
231                 None
232             } else {
233                 Some(ArchiveRO { ptr: ar })
234             }
235         }
236     }
237
238     /// Reads a file in the archive
239     pub fn read<'a>(&'a self, file: &str) -> Option<&'a [u8]> {
240         unsafe {
241             let mut size = 0 as libc::size_t;
242             let ptr = file.with_c_str(|file| {
243                 llvm::LLVMRustArchiveReadSection(self.ptr, file, &mut size)
244             });
245             if ptr.is_null() {
246                 None
247             } else {
248                 Some(mem::transmute(raw::Slice {
249                     data: ptr,
250                     len: size as uint,
251                 }))
252             }
253         }
254     }
255 }
256
257 impl Drop for ArchiveRO {
258     fn drop(&mut self) {
259         unsafe {
260             llvm::LLVMRustDestroyArchive(self.ptr);
261         }
262     }
263 }