]> git.lizzy.rs Git - rust.git/blob - src/librustc_back/archive.rs
auto merge of #15604 : mrmonday/rust/raw-socket-libc, r=alexcrichton
[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 std::io::process::{Command, ProcessOutput};
14 use std::io::{fs, TempDir};
15 use std::io;
16 use std::os;
17 use std::str;
18 use syntax::abi;
19 use ErrorHandler = syntax::diagnostic::Handler;
20
21 pub static METADATA_FILENAME: &'static str = "rust.metadata.bin";
22
23 pub struct ArchiveConfig<'a> {
24     pub handler: &'a ErrorHandler,
25     pub dst: Path,
26     pub lib_search_paths: Vec<Path>,
27     pub os: abi::Os,
28     pub maybe_ar_prog: Option<String>
29 }
30
31 pub struct Archive<'a> {
32     handler: &'a ErrorHandler,
33     dst: Path,
34     lib_search_paths: Vec<Path>,
35     os: abi::Os,
36     maybe_ar_prog: Option<String>
37 }
38
39 fn run_ar(handler: &ErrorHandler, maybe_ar_prog: &Option<String>,
40           args: &str, cwd: Option<&Path>,
41           paths: &[&Path]) -> ProcessOutput {
42     let ar = match *maybe_ar_prog {
43         Some(ref ar) => ar.as_slice(),
44         None => "ar"
45     };
46     let mut cmd = Command::new(ar);
47
48     cmd.arg(args).args(paths);
49     debug!("{}", cmd);
50
51     match cwd {
52         Some(p) => {
53             cmd.cwd(p);
54             debug!("inside {}", p.display());
55         }
56         None => {}
57     }
58
59     match cmd.spawn() {
60         Ok(prog) => {
61             let o = prog.wait_with_output().unwrap();
62             if !o.status.success() {
63                 handler.err(format!("{} failed with: {}",
64                                  cmd,
65                                  o.status).as_slice());
66                 handler.note(format!("stdout ---\n{}",
67                                   str::from_utf8(o.output
68                                                   .as_slice()).unwrap())
69                           .as_slice());
70                 handler.note(format!("stderr ---\n{}",
71                                   str::from_utf8(o.error
72                                                   .as_slice()).unwrap())
73                           .as_slice());
74                 handler.abort_if_errors();
75             }
76             o
77         },
78         Err(e) => {
79             handler.err(format!("could not exec `{}`: {}", ar.as_slice(),
80                              e).as_slice());
81             handler.abort_if_errors();
82             fail!("rustc::back::archive::run_ar() should not reach this point");
83         }
84     }
85 }
86
87 impl<'a> Archive<'a> {
88     /// Initializes a new static archive with the given object file
89     pub fn create<'b>(config: ArchiveConfig<'a>, initial_object: &'b Path) -> Archive<'a> {
90         let ArchiveConfig { handler, dst, lib_search_paths, os, maybe_ar_prog } = config;
91         run_ar(handler, &maybe_ar_prog, "crus", None, [&dst, initial_object]);
92         Archive {
93             handler: handler,
94             dst: dst,
95             lib_search_paths: lib_search_paths,
96             os: os,
97             maybe_ar_prog: maybe_ar_prog
98         }
99     }
100
101     /// Opens an existing static archive
102     pub fn open(config: ArchiveConfig<'a>) -> Archive<'a> {
103         let ArchiveConfig { handler, dst, lib_search_paths, os, maybe_ar_prog } = config;
104         assert!(dst.exists());
105         Archive {
106             handler: handler,
107             dst: dst,
108             lib_search_paths: lib_search_paths,
109             os: os,
110             maybe_ar_prog: maybe_ar_prog
111         }
112     }
113
114     /// Adds all of the contents of a native library to this archive. This will
115     /// search in the relevant locations for a library named `name`.
116     pub fn add_native_library(&mut self, name: &str) -> io::IoResult<()> {
117         let location = self.find_library(name);
118         self.add_archive(&location, name, [])
119     }
120
121     /// Adds all of the contents of the rlib at the specified path to this
122     /// archive.
123     ///
124     /// This ignores adding the bytecode from the rlib, and if LTO is enabled
125     /// then the object file also isn't added.
126     pub fn add_rlib(&mut self, rlib: &Path, name: &str,
127                     lto: bool) -> io::IoResult<()> {
128         let object = format!("{}.o", name);
129         let bytecode = format!("{}.bytecode.deflate", name);
130         let mut ignore = vec!(bytecode.as_slice(), METADATA_FILENAME);
131         if lto {
132             ignore.push(object.as_slice());
133         }
134         self.add_archive(rlib, name, ignore.as_slice())
135     }
136
137     /// Adds an arbitrary file to this archive
138     pub fn add_file(&mut self, file: &Path, has_symbols: bool) {
139         let cmd = if has_symbols {"r"} else {"rS"};
140         run_ar(self.handler, &self.maybe_ar_prog, cmd, None, [&self.dst, file]);
141     }
142
143     /// Removes a file from this archive
144     pub fn remove_file(&mut self, file: &str) {
145         run_ar(self.handler, &self.maybe_ar_prog, "d", None, [&self.dst, &Path::new(file)]);
146     }
147
148     /// Updates all symbols in the archive (runs 'ar s' over it)
149     pub fn update_symbols(&mut self) {
150         run_ar(self.handler, &self.maybe_ar_prog, "s", None, [&self.dst]);
151     }
152
153     /// Lists all files in an archive
154     pub fn files(&self) -> Vec<String> {
155         let output = run_ar(self.handler, &self.maybe_ar_prog, "t", None, [&self.dst]);
156         let output = str::from_utf8(output.output.as_slice()).unwrap();
157         // use lines_any because windows delimits output with `\r\n` instead of
158         // just `\n`
159         output.lines_any().map(|s| s.to_string()).collect()
160     }
161
162     fn add_archive(&mut self, archive: &Path, name: &str,
163                    skip: &[&str]) -> io::IoResult<()> {
164         let loc = TempDir::new("rsar").unwrap();
165
166         // First, extract the contents of the archive to a temporary directory
167         let archive = os::make_absolute(archive);
168         run_ar(self.handler, &self.maybe_ar_prog, "x", Some(loc.path()), [&archive]);
169
170         // Next, we must rename all of the inputs to "guaranteed unique names".
171         // The reason for this is that archives are keyed off the name of the
172         // files, so if two files have the same name they will override one
173         // another in the archive (bad).
174         //
175         // We skip any files explicitly desired for skipping, and we also skip
176         // all SYMDEF files as these are just magical placeholders which get
177         // re-created when we make a new archive anyway.
178         let files = try!(fs::readdir(loc.path()));
179         let mut inputs = Vec::new();
180         for file in files.iter() {
181             let filename = file.filename_str().unwrap();
182             if skip.iter().any(|s| *s == filename) { continue }
183             if filename.contains(".SYMDEF") { continue }
184
185             let filename = format!("r-{}-{}", name, filename);
186             // LLDB (as mentioned in back::link) crashes on filenames of exactly
187             // 16 bytes in length. If we're including an object file with
188             // exactly 16-bytes of characters, give it some prefix so that it's
189             // not 16 bytes.
190             let filename = if filename.len() == 16 {
191                 format!("lldb-fix-{}", filename)
192             } else {
193                 filename
194             };
195             let new_filename = file.with_filename(filename);
196             try!(fs::rename(file, &new_filename));
197             inputs.push(new_filename);
198         }
199         if inputs.len() == 0 { return Ok(()) }
200
201         // Finally, add all the renamed files to this archive
202         let mut args = vec!(&self.dst);
203         args.extend(inputs.iter());
204         run_ar(self.handler, &self.maybe_ar_prog, "r", None, args.as_slice());
205         Ok(())
206     }
207
208     fn find_library(&self, name: &str) -> Path {
209         let (osprefix, osext) = match self.os {
210             abi::OsWin32 => ("", "lib"), _ => ("lib", "a"),
211         };
212         // On Windows, static libraries sometimes show up as libfoo.a and other
213         // times show up as foo.lib
214         let oslibname = format!("{}{}.{}", osprefix, name, osext);
215         let unixlibname = format!("lib{}.a", name);
216
217         for path in self.lib_search_paths.iter() {
218             debug!("looking for {} inside {}", name, path.display());
219             let test = path.join(oslibname.as_slice());
220             if test.exists() { return test }
221             if oslibname != unixlibname {
222                 let test = path.join(unixlibname.as_slice());
223                 if test.exists() { return test }
224             }
225         }
226         self.handler.fatal(format!("could not find native static library `{}`, \
227                                  perhaps an -L flag is missing?",
228                                 name).as_slice());
229     }
230 }
231