]> git.proxmox.com Git - rustc.git/blame - src/librustc_back/archive.rs
Imported Upstream version 1.2.0+dfsg1
[rustc.git] / src / librustc_back / archive.rs
CommitLineData
1a4d82fc
JJ
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
85aaf69f 13use std::env;
62682a34 14use std::ffi::OsString;
d9579d0f 15use std::fs::{self, File};
c34b1796
AL
16use std::io::prelude::*;
17use std::io;
18use std::path::{Path, PathBuf};
19use std::process::{Command, Output, Stdio};
1a4d82fc
JJ
20use std::str;
21use syntax::diagnostic::Handler as ErrorHandler;
d9579d0f 22use rustc_llvm::archive_ro::ArchiveRO;
1a4d82fc 23
c34b1796
AL
24use tempdir::TempDir;
25
26pub const METADATA_FILENAME: &'static str = "rust.metadata.bin";
1a4d82fc
JJ
27
28pub struct ArchiveConfig<'a> {
29 pub handler: &'a ErrorHandler,
c34b1796
AL
30 pub dst: PathBuf,
31 pub lib_search_paths: Vec<PathBuf>,
1a4d82fc
JJ
32 pub slib_prefix: String,
33 pub slib_suffix: String,
62682a34
SL
34 pub ar_prog: String,
35 pub command_path: OsString,
1a4d82fc
JJ
36}
37
38pub struct Archive<'a> {
62682a34 39 config: ArchiveConfig<'a>,
1a4d82fc
JJ
40}
41
42/// Helper for adding many files to an archive with a single invocation of
43/// `ar`.
44#[must_use = "must call build() to finish building the archive"]
45pub struct ArchiveBuilder<'a> {
46 archive: Archive<'a>,
47 work_dir: TempDir,
48 /// Filename of each member that should be added to the archive.
c34b1796 49 members: Vec<PathBuf>,
1a4d82fc
JJ
50 should_update_symbols: bool,
51}
52
62682a34
SL
53enum Action<'a> {
54 Remove(&'a Path),
55 AddObjects(&'a [&'a PathBuf], bool),
56 UpdateSymbols,
1a4d82fc
JJ
57}
58
59pub fn find_library(name: &str, osprefix: &str, ossuffix: &str,
c34b1796
AL
60 search_paths: &[PathBuf],
61 handler: &ErrorHandler) -> PathBuf {
1a4d82fc
JJ
62 // On Windows, static libraries sometimes show up as libfoo.a and other
63 // times show up as foo.lib
64 let oslibname = format!("{}{}{}", osprefix, name, ossuffix);
65 let unixlibname = format!("lib{}.a", name);
66
85aaf69f 67 for path in search_paths {
c34b1796 68 debug!("looking for {} inside {:?}", name, path);
85aaf69f 69 let test = path.join(&oslibname[..]);
1a4d82fc
JJ
70 if test.exists() { return test }
71 if oslibname != unixlibname {
85aaf69f 72 let test = path.join(&unixlibname[..]);
1a4d82fc
JJ
73 if test.exists() { return test }
74 }
75 }
76 handler.fatal(&format!("could not find native static library `{}`, \
77 perhaps an -L flag is missing?",
c34b1796 78 name));
1a4d82fc
JJ
79}
80
81impl<'a> Archive<'a> {
82 fn new(config: ArchiveConfig<'a>) -> Archive<'a> {
62682a34 83 Archive { config: config }
1a4d82fc
JJ
84 }
85
86 /// Opens an existing static archive
87 pub fn open(config: ArchiveConfig<'a>) -> Archive<'a> {
88 let archive = Archive::new(config);
62682a34 89 assert!(archive.config.dst.exists());
1a4d82fc
JJ
90 archive
91 }
92
93 /// Removes a file from this archive
94 pub fn remove_file(&mut self, file: &str) {
62682a34 95 self.run(None, Action::Remove(Path::new(file)));
1a4d82fc
JJ
96 }
97
98 /// Lists all files in an archive
99 pub fn files(&self) -> Vec<String> {
62682a34
SL
100 let archive = match ArchiveRO::open(&self.config.dst) {
101 Some(ar) => ar,
102 None => return Vec::new(),
103 };
104 let ret = archive.iter().filter_map(|child| child.name())
105 .map(|name| name.to_string())
106 .collect();
107 return ret;
1a4d82fc
JJ
108 }
109
110 /// Creates an `ArchiveBuilder` for adding files to this archive.
111 pub fn extend(self) -> ArchiveBuilder<'a> {
112 ArchiveBuilder::new(self)
113 }
62682a34
SL
114
115 fn run(&self, cwd: Option<&Path>, action: Action) -> Output {
116 let abs_dst = env::current_dir().unwrap().join(&self.config.dst);
117 let ar = &self.config.ar_prog;
118 let mut cmd = Command::new(ar);
119 cmd.env("PATH", &self.config.command_path);
120 cmd.stdout(Stdio::piped()).stderr(Stdio::piped());
121 self.prepare_ar_action(&mut cmd, &abs_dst, action);
122 info!("{:?}", cmd);
123
124 if let Some(p) = cwd {
125 cmd.current_dir(p);
126 info!("inside {:?}", p.display());
127 }
128
129 let handler = &self.config.handler;
130 match cmd.spawn() {
131 Ok(prog) => {
132 let o = prog.wait_with_output().unwrap();
133 if !o.status.success() {
134 handler.err(&format!("{:?} failed with: {}", cmd, o.status));
135 handler.note(&format!("stdout ---\n{}",
136 str::from_utf8(&o.stdout).unwrap()));
137 handler.note(&format!("stderr ---\n{}",
138 str::from_utf8(&o.stderr).unwrap()));
139 handler.abort_if_errors();
140 }
141 o
142 },
143 Err(e) => {
144 handler.err(&format!("could not exec `{}`: {}",
145 self.config.ar_prog, e));
146 handler.abort_if_errors();
147 panic!("rustc::back::archive::run() should not reach this point");
148 }
149 }
150 }
151
152 fn prepare_ar_action(&self, cmd: &mut Command, dst: &Path, action: Action) {
153 match action {
154 Action::Remove(file) => {
155 cmd.arg("d").arg(dst).arg(file);
156 }
157 Action::AddObjects(objs, update_symbols) => {
158 cmd.arg(if update_symbols {"crs"} else {"crS"})
159 .arg(dst)
160 .args(objs);
161 }
162 Action::UpdateSymbols => {
163 cmd.arg("s").arg(dst);
164 }
165 }
166 }
1a4d82fc
JJ
167}
168
169impl<'a> ArchiveBuilder<'a> {
170 fn new(archive: Archive<'a>) -> ArchiveBuilder<'a> {
171 ArchiveBuilder {
172 archive: archive,
173 work_dir: TempDir::new("rsar").unwrap(),
174 members: vec![],
175 should_update_symbols: false,
176 }
177 }
178
179 /// Create a new static archive, ready for adding files.
180 pub fn create(config: ArchiveConfig<'a>) -> ArchiveBuilder<'a> {
181 let archive = Archive::new(config);
182 ArchiveBuilder::new(archive)
183 }
184
185 /// Adds all of the contents of a native library to this archive. This will
186 /// search in the relevant locations for a library named `name`.
c34b1796 187 pub fn add_native_library(&mut self, name: &str) -> io::Result<()> {
1a4d82fc 188 let location = find_library(name,
62682a34
SL
189 &self.archive.config.slib_prefix,
190 &self.archive.config.slib_suffix,
191 &self.archive.config.lib_search_paths,
192 self.archive.config.handler);
1a4d82fc
JJ
193 self.add_archive(&location, name, |_| false)
194 }
195
196 /// Adds all of the contents of the rlib at the specified path to this
197 /// archive.
198 ///
199 /// This ignores adding the bytecode from the rlib, and if LTO is enabled
200 /// then the object file also isn't added.
201 pub fn add_rlib(&mut self, rlib: &Path, name: &str,
c34b1796 202 lto: bool) -> io::Result<()> {
1a4d82fc
JJ
203 // Ignoring obj file starting with the crate name
204 // as simple comparison is not enough - there
205 // might be also an extra name suffix
206 let obj_start = format!("{}", name);
85aaf69f 207 let obj_start = &obj_start[..];
1a4d82fc
JJ
208 // Ignoring all bytecode files, no matter of
209 // name
210 let bc_ext = ".bytecode.deflate";
211
85aaf69f 212 self.add_archive(rlib, &name[..], |fname: &str| {
1a4d82fc
JJ
213 let skip_obj = lto && fname.starts_with(obj_start)
214 && fname.ends_with(".o");
215 skip_obj || fname.ends_with(bc_ext) || fname == METADATA_FILENAME
216 })
217 }
218
219 /// Adds an arbitrary file to this archive
c34b1796
AL
220 pub fn add_file(&mut self, file: &Path) -> io::Result<()> {
221 let filename = Path::new(file.file_name().unwrap());
1a4d82fc
JJ
222 let new_file = self.work_dir.path().join(&filename);
223 try!(fs::copy(file, &new_file));
c34b1796 224 self.members.push(filename.to_path_buf());
1a4d82fc
JJ
225 Ok(())
226 }
227
228 /// Indicate that the next call to `build` should updates all symbols in
229 /// the archive (run 'ar s' over it).
230 pub fn update_symbols(&mut self) {
231 self.should_update_symbols = true;
232 }
233
234 /// Combine the provided files, rlibs, and native libraries into a single
235 /// `Archive`.
236 pub fn build(self) -> Archive<'a> {
237 // Get an absolute path to the destination, so `ar` will work even
238 // though we run it from `self.work_dir`.
62682a34
SL
239 let mut objects = Vec::new();
240 let mut total_len = self.archive.config.dst.to_string_lossy().len();
1a4d82fc
JJ
241
242 if self.members.is_empty() {
1a4d82fc 243 if self.should_update_symbols {
62682a34
SL
244 self.archive.run(Some(self.work_dir.path()),
245 Action::UpdateSymbols);
1a4d82fc
JJ
246 }
247 return self.archive;
248 }
249
250 // Don't allow the total size of `args` to grow beyond 32,000 bytes.
251 // Windows will raise an error if the argument string is longer than
252 // 32,768, and we leave a bit of extra space for the program name.
c34b1796 253 const ARG_LENGTH_LIMIT: usize = 32_000;
1a4d82fc 254
85aaf69f 255 for member_name in &self.members {
c34b1796 256 let len = member_name.to_string_lossy().len();
1a4d82fc
JJ
257
258 // `len + 1` to account for the space that's inserted before each
259 // argument. (Windows passes command-line arguments as a single
260 // string, not an array of strings.)
261 if total_len + len + 1 > ARG_LENGTH_LIMIT {
262 // Add the archive members seen so far, without updating the
62682a34
SL
263 // symbol table.
264 self.archive.run(Some(self.work_dir.path()),
265 Action::AddObjects(&objects, false));
1a4d82fc 266
62682a34
SL
267 objects.clear();
268 total_len = self.archive.config.dst.to_string_lossy().len();
1a4d82fc
JJ
269 }
270
62682a34 271 objects.push(member_name);
1a4d82fc
JJ
272 total_len += len + 1;
273 }
274
275 // Add the remaining archive members, and update the symbol table if
276 // necessary.
62682a34
SL
277 self.archive.run(Some(self.work_dir.path()),
278 Action::AddObjects(&objects, self.should_update_symbols));
1a4d82fc
JJ
279
280 self.archive
281 }
282
85aaf69f 283 fn add_archive<F>(&mut self, archive: &Path, name: &str,
c34b1796 284 mut skip: F) -> io::Result<()>
85aaf69f 285 where F: FnMut(&str) -> bool,
1a4d82fc 286 {
d9579d0f
AL
287 let archive = match ArchiveRO::open(archive) {
288 Some(ar) => ar,
289 None => return Err(io::Error::new(io::ErrorKind::Other,
290 "failed to open archive")),
291 };
1a4d82fc
JJ
292
293 // Next, we must rename all of the inputs to "guaranteed unique names".
d9579d0f 294 // We write each file into `self.work_dir` under its new unique name.
1a4d82fc
JJ
295 // The reason for this renaming is that archives are keyed off the name
296 // of the files, so if two files have the same name they will override
297 // one another in the archive (bad).
298 //
299 // We skip any files explicitly desired for skipping, and we also skip
300 // all SYMDEF files as these are just magical placeholders which get
301 // re-created when we make a new archive anyway.
d9579d0f
AL
302 for file in archive.iter() {
303 let filename = match file.name() {
304 Some(s) => s,
305 None => continue,
306 };
1a4d82fc 307 if filename.contains(".SYMDEF") { continue }
d9579d0f 308 if skip(filename) { continue }
62682a34
SL
309 let filename = Path::new(filename).file_name().unwrap()
310 .to_str().unwrap();
1a4d82fc 311
d9579d0f
AL
312 // Archives on unix systems typically do not have slashes in
313 // filenames as the `ar` utility generally only uses the last
314 // component of a path for the filename list in the archive. On
315 // Windows, however, archives assembled with `lib.exe` will preserve
316 // the full path to the file that was placed in the archive,
317 // including path separators.
318 //
319 // The code below is munging paths so it'll go wrong pretty quickly
320 // if there's some unexpected slashes in the filename, so here we
321 // just chop off everything but the filename component. Note that
322 // this can cause duplicate filenames, but that's also handled below
323 // as well.
324 let filename = Path::new(filename).file_name().unwrap()
325 .to_str().unwrap();
326
327 // An archive can contain files of the same name multiple times, so
328 // we need to be sure to not have them overwrite one another when we
329 // extract them. Consequently we need to find a truly unique file
330 // name for us!
331 let mut new_filename = String::new();
332 for n in 0.. {
333 let n = if n == 0 {String::new()} else {format!("-{}", n)};
334 new_filename = format!("r{}-{}-{}", n, name, filename);
335
336 // LLDB (as mentioned in back::link) crashes on filenames of
337 // exactly
338 // 16 bytes in length. If we're including an object file with
339 // exactly 16-bytes of characters, give it some prefix so
340 // that it's not 16 bytes.
341 new_filename = if new_filename.len() == 16 {
342 format!("lldb-fix-{}", new_filename)
343 } else {
344 new_filename
345 };
346
347 let present = self.members.iter().filter_map(|p| {
348 p.file_name().and_then(|f| f.to_str())
349 }).any(|s| s == new_filename);
350 if !present {
351 break
352 }
353 }
354 let dst = self.work_dir.path().join(&new_filename);
355 try!(try!(File::create(&dst)).write_all(file.data()));
356 self.members.push(PathBuf::from(new_filename));
1a4d82fc 357 }
d9579d0f 358
1a4d82fc
JJ
359 Ok(())
360 }
361}