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 std::cast;
19 use std::io;
20 use std::io::{fs, TempDir};
21 use libc;
22 use std::os;
23 use std::io::process::{ProcessConfig, Process, ProcessOutput};
24 use std::str;
25 use std::raw;
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
43 let mut args = vec!(args.to_owned());
44 let paths = paths.iter().map(|p| p.as_str().unwrap().to_owned());
45 args.extend(paths);
46 debug!("{} {}", ar, args.connect(" "));
47 match cwd {
48 Some(p) => { debug!("inside {}", p.display()); }
49 None => {}
50 }
51 match Process::configure(ProcessConfig {
52 program: ar.as_slice(),
53 args: args.as_slice(),
54 cwd: cwd.map(|a| &*a),
55 .. ProcessConfig::new()
56 }) {
57 Ok(mut prog) => {
58 let o = prog.wait_with_output();
59 if !o.status.success() {
60 sess.err(format!("{} {} failed with: {}", ar, args.connect(" "),
61 o.status));
62 sess.note(format!("stdout ---\n{}",
63 str::from_utf8(o.output.as_slice()).unwrap()));
64 sess.note(format!("stderr ---\n{}",
65 str::from_utf8(o.error.as_slice()).unwrap()));
66 sess.abort_if_errors();
67 }
68 o
69 },
70 Err(e) => {
71 sess.err(format!("could not exec `{}`: {}", ar, e));
72 sess.abort_if_errors();
73 fail!("rustc::back::archive::run_ar() should not reach this point");
74 }
75 }
76 }
77
78 impl<'a> Archive<'a> {
79 /// Initializes a new static archive with the given object file
80 pub fn create<'b>(sess: &'a Session, dst: &'b Path,
81 initial_object: &'b Path) -> Archive<'a> {
82 run_ar(sess, "crus", None, [dst, initial_object]);
83 Archive { sess: sess, dst: dst.clone() }
84 }
85
86 /// Opens an existing static archive
87 pub fn open(sess: &'a Session, dst: Path) -> Archive<'a> {
88 assert!(dst.exists());
89 Archive { sess: sess, dst: dst }
90 }
91
92 /// Adds all of the contents of a native library to this archive. This will
93 /// search in the relevant locations for a library named `name`.
94 pub fn add_native_library(&mut self, name: &str) -> io::IoResult<()> {
95 let location = self.find_library(name);
96 self.add_archive(&location, name, [])
97 }
98
99 /// Adds all of the contents of the rlib at the specified path to this
100 /// archive.
101 ///
102 /// This ignores adding the bytecode from the rlib, and if LTO is enabled
103 /// then the object file also isn't added.
104 pub fn add_rlib(&mut self, rlib: &Path, name: &str,
105 lto: bool) -> io::IoResult<()> {
106 let object = format!("{}.o", name);
107 let bytecode = format!("{}.bc.deflate", name);
108 let mut ignore = vec!(METADATA_FILENAME, bytecode.as_slice());
109 if lto {
110 ignore.push(object.as_slice());
111 }
112 self.add_archive(rlib, name, ignore.as_slice())
113 }
114
115 /// Adds an arbitrary file to this archive
116 pub fn add_file(&mut self, file: &Path, has_symbols: bool) {
117 let cmd = if has_symbols {"r"} else {"rS"};
118 run_ar(self.sess, cmd, None, [&self.dst, file]);
119 }
120
121 /// Removes a file from this archive
122 pub fn remove_file(&mut self, file: &str) {
123 run_ar(self.sess, "d", None, [&self.dst, &Path::new(file)]);
124 }
125
126 /// Updates all symbols in the archive (runs 'ar s' over it)
127 pub fn update_symbols(&mut self) {
128 run_ar(self.sess, "s", None, [&self.dst]);
129 }
130
131 /// Lists all files in an archive
132 pub fn files(&self) -> Vec<~str> {
133 let output = run_ar(self.sess, "t", None, [&self.dst]);
134 let output = str::from_utf8(output.output.as_slice()).unwrap();
135 // use lines_any because windows delimits output with `\r\n` instead of
136 // just `\n`
137 output.lines_any().map(|s| s.to_owned()).collect()
138 }
139
140 fn add_archive(&mut self, archive: &Path, name: &str,
141 skip: &[&str]) -> io::IoResult<()> {
142 let loc = TempDir::new("rsar").unwrap();
143
144 // First, extract the contents of the archive to a temporary directory
145 let archive = os::make_absolute(archive);
146 run_ar(self.sess, "x", Some(loc.path()), [&archive]);
147
148 // Next, we must rename all of the inputs to "guaranteed unique names".
149 // The reason for this is that archives are keyed off the name of the
150 // files, so if two files have the same name they will override one
151 // another in the archive (bad).
152 //
153 // We skip any files explicitly desired for skipping, and we also skip
154 // all SYMDEF files as these are just magical placeholders which get
155 // re-created when we make a new archive anyway.
156 let files = try!(fs::readdir(loc.path()));
157 let mut inputs = Vec::new();
158 for file in files.iter() {
159 let filename = file.filename_str().unwrap();
160 if skip.iter().any(|s| *s == filename) { continue }
161 if filename.contains(".SYMDEF") { continue }
162
163 let filename = format!("r-{}-{}", name, filename);
164 let new_filename = file.with_filename(filename);
165 try!(fs::rename(file, &new_filename));
166 inputs.push(new_filename);
167 }
168 if inputs.len() == 0 { return Ok(()) }
169
170 // Finally, add all the renamed files to this archive
171 let mut args = vec!(&self.dst);
172 args.extend(inputs.iter());
173 run_ar(self.sess, "r", None, args.as_slice());
174 Ok(())
175 }
176
177 fn find_library(&self, name: &str) -> Path {
178 let (osprefix, osext) = match self.sess.targ_cfg.os {
179 abi::OsWin32 => ("", "lib"), _ => ("lib", "a"),
180 };
181 // On Windows, static libraries sometimes show up as libfoo.a and other
182 // times show up as foo.lib
183 let oslibname = format!("{}{}.{}", osprefix, name, osext);
184 let unixlibname = format!("lib{}.a", name);
185
186 let mut rustpath = filesearch::rust_path();
187 rustpath.push(self.sess.target_filesearch().get_lib_path());
188 let search = self.sess.opts.addl_lib_search_paths.borrow();
189 for path in search.iter().chain(rustpath.iter()) {
190 debug!("looking for {} inside {}", name, path.display());
191 let test = path.join(oslibname.as_slice());
192 if test.exists() { return test }
193 if oslibname != unixlibname {
194 let test = path.join(unixlibname.as_slice());
195 if test.exists() { return test }
196 }
197 }
198 self.sess.fatal(format!("could not find native static library `{}`, \
199 perhaps an -L flag is missing?", name));
200 }
201 }
202
203 impl ArchiveRO {
204 /// Opens a static archive for read-only purposes. This is more optimized
205 /// than the `open` method because it uses LLVM's internal `Archive` class
206 /// rather than shelling out to `ar` for everything.
207 ///
208 /// If this archive is used with a mutable method, then an error will be
209 /// raised.
210 pub fn open(dst: &Path) -> Option<ArchiveRO> {
211 unsafe {
212 let ar = dst.with_c_str(|dst| {
213 llvm::LLVMRustOpenArchive(dst)
214 });
215 if ar.is_null() {
216 None
217 } else {
218 Some(ArchiveRO { ptr: ar })
219 }
220 }
221 }
222
223 /// Reads a file in the archive
224 pub fn read<'a>(&'a self, file: &str) -> Option<&'a [u8]> {
225 unsafe {
226 let mut size = 0 as libc::size_t;
227 let ptr = file.with_c_str(|file| {
228 llvm::LLVMRustArchiveReadSection(self.ptr, file, &mut size)
229 });
230 if ptr.is_null() {
231 None
232 } else {
233 Some(cast::transmute(raw::Slice {
234 data: ptr,
235 len: size as uint,
236 }))
237 }
238 }
239 }
240 }
241
242 impl Drop for ArchiveRO {
243 fn drop(&mut self) {
244 unsafe {
245 llvm::LLVMRustDestroyArchive(self.ptr);
246 }
247 }
248 }
librustc/back/archive.rs:38:1-38:1 -fn- definition:
fn run_ar(sess: &Session, args: &str, cwd: Option<&Path>,
paths: &[&Path]) -> ProcessOutput {
let ar = get_ar_prog(sess);
references:- 7122: pub fn remove_file(&mut self, file: &str) {
123: run_ar(self.sess, "d", None, [&self.dst, &Path::new(file)]);
124: }
--
132: pub fn files(&self) -> Vec<~str> {
133: let output = run_ar(self.sess, "t", None, [&self.dst]);
134: let output = str::from_utf8(output.output.as_slice()).unwrap();
--
145: let archive = os::make_absolute(archive);
146: run_ar(self.sess, "x", Some(loc.path()), [&archive]);
--
172: args.extend(inputs.iter());
173: run_ar(self.sess, "r", None, args.as_slice());
174: Ok(())
librustc/back/archive.rs:34:1-34:1 -struct- definition:
pub struct ArchiveRO {
ptr: ArchiveRef,
}
references:- 6217: } else {
218: Some(ArchiveRO { ptr: ar })
219: }
--
242: impl Drop for ArchiveRO {
243: fn drop(&mut self) {
librustc/metadata/loader.rs:
88: pub struct ArchiveMetadata {
89: archive: ArchiveRO,
90: // See comments in ArchiveMetadata::new for why this is static
--
452: impl ArchiveMetadata {
453: fn new(ar: ArchiveRO) -> Option<ArchiveMetadata> {
454: let data: &'static [u8] = {
librustc/back/archive.rs:
209: /// raised.
210: pub fn open(dst: &Path) -> Option<ArchiveRO> {
211: unsafe {
librustc/back/archive.rs:29:1-29:1 -struct- definition:
pub struct Archive<'a> {
sess: &'a Session,
dst: Path,
references:- 688: assert!(dst.exists());
89: Archive { sess: sess, dst: dst }
90: }
librustc/back/link.rs:
905: obj_filename: &Path,
906: out_filename: &Path) -> Archive<'a> {
907: let mut a = Archive::create(sess, out_filename, obj_filename);
librustc/back/archive.rs:
86: /// Opens an existing static archive
87: pub fn open(sess: &'a Session, dst: Path) -> Archive<'a> {
88: assert!(dst.exists());