]> git.lizzy.rs Git - rust.git/blob - src/librustc/metadata/filesearch.rs
Register new snapshots.
[rust.git] / src / librustc / metadata / filesearch.rs
1 // Copyright 2012-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 #![allow(non_camel_case_types)]
12
13 pub use self::FileMatch::*;
14
15 use std::collections::HashSet;
16 use std::io::fs::PathExtensions;
17 use std::io::fs;
18 use std::os;
19
20 use util::fs as myfs;
21 use session::search_paths::{SearchPaths, PathKind};
22
23 #[derive(Copy)]
24 pub enum FileMatch {
25     FileMatches,
26     FileDoesntMatch,
27 }
28
29 // A module for searching for libraries
30 // FIXME (#2658): I'm not happy how this module turned out. Should
31 // probably just be folded into cstore.
32
33 pub struct FileSearch<'a> {
34     pub sysroot: &'a Path,
35     pub search_paths: &'a SearchPaths,
36     pub triple: &'a str,
37     pub kind: PathKind,
38 }
39
40 impl<'a> FileSearch<'a> {
41     pub fn for_each_lib_search_path<F>(&self, mut f: F) where
42         F: FnMut(&Path, PathKind) -> FileMatch,
43     {
44         let mut visited_dirs = HashSet::new();
45         let mut found = false;
46
47         for (path, kind) in self.search_paths.iter(self.kind) {
48             match f(path, kind) {
49                 FileMatches => found = true,
50                 FileDoesntMatch => ()
51             }
52             visited_dirs.insert(path.as_vec().to_vec());
53         }
54
55         debug!("filesearch: searching lib path");
56         let tlib_path = make_target_lib_path(self.sysroot,
57                                              self.triple);
58         if !visited_dirs.contains(tlib_path.as_vec()) {
59             match f(&tlib_path, PathKind::All) {
60                 FileMatches => found = true,
61                 FileDoesntMatch => ()
62             }
63         }
64
65         visited_dirs.insert(tlib_path.as_vec().to_vec());
66         // Try RUST_PATH
67         if !found {
68             let rustpath = rust_path();
69             for path in rustpath.iter() {
70                 let tlib_path = make_rustpkg_lib_path(
71                     self.sysroot, path, self.triple);
72                 debug!("is {} in visited_dirs? {}", tlib_path.display(),
73                         visited_dirs.contains(&tlib_path.as_vec().to_vec()));
74
75                 if !visited_dirs.contains(tlib_path.as_vec()) {
76                     visited_dirs.insert(tlib_path.as_vec().to_vec());
77                     // Don't keep searching the RUST_PATH if one match turns up --
78                     // if we did, we'd get a "multiple matching crates" error
79                     match f(&tlib_path, PathKind::All) {
80                        FileMatches => {
81                            break;
82                        }
83                        FileDoesntMatch => ()
84                     }
85                 }
86             }
87         }
88     }
89
90     pub fn get_lib_path(&self) -> Path {
91         make_target_lib_path(self.sysroot, self.triple)
92     }
93
94     pub fn search<F>(&self, mut pick: F)
95         where F: FnMut(&Path, PathKind) -> FileMatch
96     {
97         self.for_each_lib_search_path(|lib_search_path, kind| {
98             debug!("searching {}", lib_search_path.display());
99             match fs::readdir(lib_search_path) {
100                 Ok(files) => {
101                     let mut rslt = FileDoesntMatch;
102                     fn is_rlib(p: & &Path) -> bool {
103                         p.extension_str() == Some("rlib")
104                     }
105                     // Reading metadata out of rlibs is faster, and if we find both
106                     // an rlib and a dylib we only read one of the files of
107                     // metadata, so in the name of speed, bring all rlib files to
108                     // the front of the search list.
109                     let files1 = files.iter().filter(|p| is_rlib(p));
110                     let files2 = files.iter().filter(|p| !is_rlib(p));
111                     for path in files1.chain(files2) {
112                         debug!("testing {}", path.display());
113                         let maybe_picked = pick(path, kind);
114                         match maybe_picked {
115                             FileMatches => {
116                                 debug!("picked {}", path.display());
117                                 rslt = FileMatches;
118                             }
119                             FileDoesntMatch => {
120                                 debug!("rejected {}", path.display());
121                             }
122                         }
123                     }
124                     rslt
125                 }
126                 Err(..) => FileDoesntMatch,
127             }
128         });
129     }
130
131     pub fn new(sysroot: &'a Path,
132                triple: &'a str,
133                search_paths: &'a SearchPaths,
134                kind: PathKind) -> FileSearch<'a> {
135         debug!("using sysroot = {}, triple = {}", sysroot.display(), triple);
136         FileSearch {
137             sysroot: sysroot,
138             search_paths: search_paths,
139             triple: triple,
140             kind: kind,
141         }
142     }
143
144     // Returns a list of directories where target-specific dylibs might be located.
145     pub fn get_dylib_search_paths(&self) -> Vec<Path> {
146         let mut paths = Vec::new();
147         self.for_each_lib_search_path(|lib_search_path, _| {
148             paths.push(lib_search_path.clone());
149             FileDoesntMatch
150         });
151         paths
152     }
153
154     // Returns a list of directories where target-specific tool binaries are located.
155     pub fn get_tools_search_paths(&self) -> Vec<Path> {
156         let mut p = Path::new(self.sysroot);
157         p.push(find_libdir(self.sysroot));
158         p.push(rustlibdir());
159         p.push(self.triple);
160         p.push("bin");
161         vec![p]
162     }
163 }
164
165 pub fn relative_target_lib_path(sysroot: &Path, target_triple: &str) -> Path {
166     let mut p = Path::new(find_libdir(sysroot));
167     assert!(p.is_relative());
168     p.push(rustlibdir());
169     p.push(target_triple);
170     p.push("lib");
171     p
172 }
173
174 fn make_target_lib_path(sysroot: &Path,
175                         target_triple: &str) -> Path {
176     sysroot.join(&relative_target_lib_path(sysroot, target_triple))
177 }
178
179 fn make_rustpkg_lib_path(sysroot: &Path,
180                          dir: &Path,
181                          triple: &str) -> Path {
182     let mut p = dir.join(find_libdir(sysroot));
183     p.push(triple);
184     p
185 }
186
187 pub fn get_or_default_sysroot() -> Path {
188     // Follow symlinks.  If the resolved path is relative, make it absolute.
189     fn canonicalize(path: Option<Path>) -> Option<Path> {
190         path.and_then(|path|
191             match myfs::realpath(&path) {
192                 Ok(canon) => Some(canon),
193                 Err(e) => panic!("failed to get realpath: {}", e),
194             })
195     }
196
197     match canonicalize(os::self_exe_name()) {
198         Some(mut p) => { p.pop(); p.pop(); p }
199         None => panic!("can't determine value for sysroot")
200     }
201 }
202
203 #[cfg(windows)]
204 static PATH_ENTRY_SEPARATOR: &'static str = ";";
205 #[cfg(not(windows))]
206 static PATH_ENTRY_SEPARATOR: &'static str = ":";
207
208 /// Returns RUST_PATH as a string, without default paths added
209 pub fn get_rust_path() -> Option<String> {
210     os::getenv("RUST_PATH").map(|x| x.to_string())
211 }
212
213 /// Returns the value of RUST_PATH, as a list
214 /// of Paths. Includes default entries for, if they exist:
215 /// $HOME/.rust
216 /// DIR/.rust for any DIR that's the current working directory
217 /// or an ancestor of it
218 pub fn rust_path() -> Vec<Path> {
219     let mut env_rust_path: Vec<Path> = match get_rust_path() {
220         Some(env_path) => {
221             let env_path_components =
222                 env_path.split_str(PATH_ENTRY_SEPARATOR);
223             env_path_components.map(|s| Path::new(s)).collect()
224         }
225         None => Vec::new()
226     };
227     let mut cwd = os::getcwd().unwrap();
228     // now add in default entries
229     let cwd_dot_rust = cwd.join(".rust");
230     if !env_rust_path.contains(&cwd_dot_rust) {
231         env_rust_path.push(cwd_dot_rust);
232     }
233     if !env_rust_path.contains(&cwd) {
234         env_rust_path.push(cwd.clone());
235     }
236     loop {
237         if { let f = cwd.filename(); f.is_none() || f.unwrap() == b".." } {
238             break
239         }
240         cwd.set_filename(".rust");
241         if !env_rust_path.contains(&cwd) && cwd.exists() {
242             env_rust_path.push(cwd.clone());
243         }
244         cwd.pop();
245     }
246     let h = os::homedir();
247     for h in h.iter() {
248         let p = h.join(".rust");
249         if !env_rust_path.contains(&p) && p.exists() {
250             env_rust_path.push(p);
251         }
252     }
253     env_rust_path
254 }
255
256 // The name of the directory rustc expects libraries to be located.
257 // On Unix should be "lib", on windows "bin"
258 #[cfg(unix)]
259 fn find_libdir(sysroot: &Path) -> String {
260     // FIXME: This is a quick hack to make the rustc binary able to locate
261     // Rust libraries in Linux environments where libraries might be installed
262     // to lib64/lib32. This would be more foolproof by basing the sysroot off
263     // of the directory where librustc is located, rather than where the rustc
264     // binary is.
265     //If --libdir is set during configuration to the value other than
266     // "lib" (i.e. non-default), this value is used (see issue #16552).
267
268     match option_env!("CFG_LIBDIR_RELATIVE") {
269         Some(libdir) if libdir != "lib" => return libdir.to_string(),
270         _ => if sysroot.join(primary_libdir_name()).join(rustlibdir()).exists() {
271             return primary_libdir_name();
272         } else {
273             return secondary_libdir_name();
274         }
275     }
276
277     #[cfg(target_pointer_width = "64")]
278     fn primary_libdir_name() -> String {
279         "lib64".to_string()
280     }
281
282     #[cfg(target_pointer_width = "32")]
283     fn primary_libdir_name() -> String {
284         "lib32".to_string()
285     }
286
287     fn secondary_libdir_name() -> String {
288         "lib".to_string()
289     }
290 }
291
292 #[cfg(windows)]
293 fn find_libdir(_sysroot: &Path) -> String {
294     "bin".to_string()
295 }
296
297 // The name of rustc's own place to organize libraries.
298 // Used to be "rustc", now the default is "rustlib"
299 pub fn rustlibdir() -> String {
300     "rustlib".to_string()
301 }