]> git.lizzy.rs Git - rust.git/blob - src/librustpkg/path_util.rs
fix fmt! usage
[rust.git] / src / librustpkg / path_util.rs
1 // Copyright 2013 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 // rustpkg utilities having to do with paths and directories
12
13 pub use package_path::{RemotePath, LocalPath, normalize};
14 pub use package_id::PkgId;
15 pub use target::{OutputType, Main, Lib, Test, Bench, Target, Build, Install};
16 pub use version::{Version, NoVersion, split_version_general};
17 use std::libc::consts::os::posix88::{S_IRUSR, S_IWUSR, S_IXUSR};
18 use std::os::mkdir_recursive;
19 use std::os;
20 use std::iterator::IteratorUtil;
21 use messages::*;
22 use package_id::*;
23
24 fn push_if_exists(vec: &mut ~[Path], p: &Path) {
25     let maybe_dir = p.push(".rust");
26     if os::path_exists(&maybe_dir) {
27         vec.push(maybe_dir);
28     }
29 }
30
31 #[cfg(windows)]
32 static PATH_ENTRY_SEPARATOR: &'static str = ";";
33 #[cfg(not(windows))]
34 static PATH_ENTRY_SEPARATOR: &'static str = ":";
35
36 /// Returns RUST_PATH as a string, without default paths added
37 pub fn get_rust_path() -> Option<~str> {
38     os::getenv("RUST_PATH")
39 }
40
41 /// Returns the value of RUST_PATH, as a list
42 /// of Paths. Includes default entries for, if they exist:
43 /// $HOME/.rust
44 /// DIR/.rust for any DIR that's the current working directory
45 /// or an ancestor of it
46 pub fn rust_path() -> ~[Path] {
47     let mut env_rust_path: ~[Path] = match get_rust_path() {
48         Some(env_path) => {
49             let env_path_components: ~[&str] =
50                 env_path.split_str_iter(PATH_ENTRY_SEPARATOR).collect();
51             env_path_components.map(|&s| Path(s))
52         }
53         None => ~[]
54     };
55     let cwd = os::getcwd();
56     // now add in default entries
57     env_rust_path.push(cwd.clone());
58     do cwd.each_parent() |p| { push_if_exists(&mut env_rust_path, p) };
59     let h = os::homedir();
60     // Avoid adding duplicates
61     // could still add dups if someone puts one of these in the RUST_PATH
62     // manually, though...
63     for h.iter().advance |hdir| {
64         if !(cwd.is_ancestor_of(hdir) || hdir.is_ancestor_of(&cwd)) {
65             push_if_exists(&mut env_rust_path, hdir);
66         }
67     }
68     env_rust_path
69 }
70
71 pub fn default_workspace() -> Path {
72     let p = rust_path();
73     if p.is_empty() {
74         fail!("Empty RUST_PATH");
75     }
76     let result = p[0];
77     if !os::path_is_dir(&result) {
78         os::mkdir_recursive(&result, U_RWX);
79     }
80     result
81 }
82
83 pub fn in_rust_path(p: &Path) -> bool {
84     rust_path().contains(p)
85 }
86
87 pub static U_RWX: i32 = (S_IRUSR | S_IWUSR | S_IXUSR) as i32;
88
89 /// Creates a directory that is readable, writeable,
90 /// and executable by the user. Returns true iff creation
91 /// succeeded.
92 pub fn make_dir_rwx(p: &Path) -> bool { os::make_dir(p, U_RWX) }
93
94 // n.b. The next three functions ignore the package version right
95 // now. Should fix that.
96
97 /// True if there's a directory in <workspace> with
98 /// pkgid's short name
99 pub fn workspace_contains_package_id(pkgid: &PkgId, workspace: &Path) -> bool {
100     let src_dir = workspace.push("src");
101     let dirs = os::list_dir(&src_dir);
102     for dirs.iter().advance |p| {
103         let p = Path((*p).clone());
104         debug!("=> p = %s", p.to_str());
105         if !os::path_is_dir(&src_dir.push_rel(&p)) {
106             loop;
107         }
108         debug!("p = %s, remote_path = %s", p.to_str(), pkgid.remote_path.to_str());
109
110         if p == *pkgid.remote_path {
111             return true;
112         }
113         else {
114             let pf = p.filename();
115             for pf.iter().advance |pf| {
116                 let f_ = (*pf).clone();
117                 let g = f_.to_str();
118                 match split_version_general(g, '-') {
119                     Some((ref might_match, ref vers)) => {
120                         debug!("might_match = %s, vers = %s", *might_match,
121                                vers.to_str());
122                         if *might_match == pkgid.short_name
123                             && (*vers == pkgid.version || pkgid.version == NoVersion)
124                         {
125                             return true;
126                         }
127                     }
128                     None => ()
129                 }
130             }
131         }
132     }
133     false
134 }
135
136 /// Returns a list of possible directories
137 /// for <pkgid>'s source files in <workspace>.
138 /// Doesn't check that any of them exist.
139 /// (for example, try both with and without the version)
140 pub fn pkgid_src_in_workspace(pkgid: &PkgId, workspace: &Path) -> ~[Path] {
141     let mut results = ~[];
142     let result = workspace.push("src").push(fmt!("%s-%s",
143                      pkgid.local_path.to_str(), pkgid.version.to_str()));
144     results.push(result);
145     results.push(workspace.push("src").push_rel(&*pkgid.remote_path));
146     results
147 }
148
149 /// Returns a src for pkgid that does exist -- None if none of them do
150 pub fn first_pkgid_src_in_workspace(pkgid: &PkgId, workspace: &Path) -> Option<Path> {
151     let rs = pkgid_src_in_workspace(pkgid, workspace);
152     for rs.iter().advance |p| {
153         if os::path_exists(p) {
154             return Some((*p).clone());
155         }
156     }
157     None
158 }
159
160 /// Figure out what the executable name for <pkgid> in <workspace>'s build
161 /// directory is, and if the file exists, return it.
162 pub fn built_executable_in_workspace(pkgid: &PkgId, workspace: &Path) -> Option<Path> {
163     let mut result = workspace.push("build");
164     // should use a target-specific subdirectory
165     result = mk_output_path(Main, Build, pkgid, &result);
166     debug!("built_executable_in_workspace: checking whether %s exists",
167            result.to_str());
168     if os::path_exists(&result) {
169         Some(result)
170     }
171     else {
172         // This is not an error, but it's worth logging it
173         error!(fmt!("built_executable_in_workspace: %s does not exist", result.to_str()));
174         None
175     }
176 }
177
178 /// Figure out what the test name for <pkgid> in <workspace>'s build
179 /// directory is, and if the file exists, return it.
180 pub fn built_test_in_workspace(pkgid: &PkgId, workspace: &Path) -> Option<Path> {
181     output_in_workspace(pkgid, workspace, Test)
182 }
183
184 /// Figure out what the test name for <pkgid> in <workspace>'s build
185 /// directory is, and if the file exists, return it.
186 pub fn built_bench_in_workspace(pkgid: &PkgId, workspace: &Path) -> Option<Path> {
187     output_in_workspace(pkgid, workspace, Bench)
188 }
189
190 fn output_in_workspace(pkgid: &PkgId, workspace: &Path, what: OutputType) -> Option<Path> {
191     let mut result = workspace.push("build");
192     // should use a target-specific subdirectory
193     result = mk_output_path(what, Build, pkgid, &result);
194     debug!("output_in_workspace: checking whether %s exists",
195            result.to_str());
196     if os::path_exists(&result) {
197         Some(result)
198     }
199     else {
200         error!(fmt!("output_in_workspace: %s does not exist", result.to_str()));
201         None
202     }
203 }
204
205 /// Figure out what the library name for <pkgid> in <workspace>'s build
206 /// directory is, and if the file exists, return it.
207 pub fn built_library_in_workspace(pkgid: &PkgId, workspace: &Path) -> Option<Path> {
208     library_in_workspace(&pkgid.local_path, pkgid.short_name,
209                          Build, workspace, "build")
210 }
211
212 /// Does the actual searching stuff
213 pub fn installed_library_in_workspace(short_name: &str, workspace: &Path) -> Option<Path> {
214     library_in_workspace(&normalize(RemotePath(Path(short_name))),
215                          short_name, Install, workspace, "lib")
216 }
217
218
219 /// This doesn't take a PkgId, so we can use it for `extern mod` inference, where we
220 /// don't know the entire package ID.
221 /// `workspace` is used to figure out the directory to search.
222 /// `short_name` is taken as the link name of the library.
223 pub fn library_in_workspace(path: &LocalPath, short_name: &str, where: Target,
224                         workspace: &Path, prefix: &str) -> Option<Path> {
225     debug!("library_in_workspace: checking whether a library named %s exists",
226            short_name);
227
228     // We don't know what the hash is, so we have to search through the directory
229     // contents
230
231     debug!("short_name = %s where = %? workspace = %s \
232             prefix = %s", short_name, where, workspace.to_str(), prefix);
233
234     let dir_to_search = match where {
235         Build => workspace.push(prefix).push_rel(&**path),
236         Install => workspace.push(prefix)
237     };
238     debug!("Listing directory %s", dir_to_search.to_str());
239     let dir_contents = os::list_dir(&dir_to_search);
240     debug!("dir has %? entries", dir_contents.len());
241
242     let lib_prefix = fmt!("%s%s", os::consts::DLL_PREFIX, short_name);
243     let lib_filetype = os::consts::DLL_SUFFIX;
244
245     debug!("lib_prefix = %s and lib_filetype = %s", lib_prefix, lib_filetype);
246
247     let mut result_filename = None;
248     for dir_contents.iter().advance |p| {
249         let mut which = 0;
250         let mut hash = None;
251         let p_path = Path((*p).clone());
252         let extension = p_path.filetype();
253         debug!("p = %s, p's extension is %?", p.to_str(), extension);
254         match extension {
255             Some(ref s) if lib_filetype == *s => (),
256             _ => loop
257         }
258         // Find a filename that matches the pattern: (lib_prefix)-hash-(version)(lib_suffix)
259         // and remember what the hash was
260         let f_name = match p_path.filename() {
261             Some(s) => s, None => loop
262         };
263         for f_name.split_iter('-').advance |piece| {
264             debug!("a piece = %s", piece);
265             if which == 0 && piece != lib_prefix {
266                 break;
267             }
268             else if which == 0 {
269                 which += 1;
270             }
271             else if which == 1 {
272                 hash = Some(piece.to_owned());
273                 break;
274             }
275             else {
276                 // something went wrong
277                 hash = None;
278                 break;
279             }
280         }
281         if hash.is_some() {
282             result_filename = Some(p_path);
283             break;
284         }
285     }
286
287     // Return the filename that matches, which we now know exists
288     // (if result_filename != None)
289     match result_filename {
290         None => {
291             warn(fmt!("library_in_workspace didn't find a library in %s for %s",
292                             dir_to_search.to_str(), short_name));
293             None
294         }
295         Some(result_filename) => {
296             let absolute_path = dir_to_search.push_rel(&result_filename);
297             debug!("result_filename = %s", absolute_path.to_str());
298             Some(absolute_path)
299         }
300     }
301 }
302
303 /// Returns the executable that would be installed for <pkgid>
304 /// in <workspace>
305 /// As a side effect, creates the bin-dir if it doesn't exist
306 pub fn target_executable_in_workspace(pkgid: &PkgId, workspace: &Path) -> Path {
307     target_file_in_workspace(pkgid, workspace, Main, Install)
308 }
309
310
311 /// Returns the executable that would be installed for <pkgid>
312 /// in <workspace>
313 /// As a side effect, creates the lib-dir if it doesn't exist
314 pub fn target_library_in_workspace(pkgid: &PkgId, workspace: &Path) -> Path {
315     use conditions::bad_path::cond;
316     if !os::path_is_dir(workspace) {
317         cond.raise(((*workspace).clone(),
318                     fmt!("Workspace supplied to target_library_in_workspace \
319                           is not a directory! %s", workspace.to_str())));
320     }
321     target_file_in_workspace(pkgid, workspace, Lib, Install)
322 }
323
324 /// Returns the test executable that would be installed for <pkgid>
325 /// in <workspace>
326 /// note that we *don't* install test executables, so this is just for unit testing
327 pub fn target_test_in_workspace(pkgid: &PkgId, workspace: &Path) -> Path {
328     target_file_in_workspace(pkgid, workspace, Test, Install)
329 }
330
331 /// Returns the bench executable that would be installed for <pkgid>
332 /// in <workspace>
333 /// note that we *don't* install bench executables, so this is just for unit testing
334 pub fn target_bench_in_workspace(pkgid: &PkgId, workspace: &Path) -> Path {
335     target_file_in_workspace(pkgid, workspace, Bench, Install)
336 }
337
338
339 /// Returns the path that pkgid `pkgid` would have if placed `where`
340 /// in `workspace`
341 fn target_file_in_workspace(pkgid: &PkgId, workspace: &Path,
342                             what: OutputType, where: Target) -> Path {
343     use conditions::bad_path::cond;
344
345     let subdir = match what {
346         Lib => "lib", Main | Test | Bench => "bin"
347     };
348     let result = workspace.push(subdir);
349     if !os::path_exists(&result) && !mkdir_recursive(&result, U_RWX) {
350         cond.raise((result.clone(), fmt!("target_file_in_workspace couldn't \
351             create the %s dir (pkgid=%s, workspace=%s, what=%?, where=%?",
352             subdir, pkgid.to_str(), workspace.to_str(), what, where)));
353     }
354     mk_output_path(what, where, pkgid, &result)
355 }
356
357 /// Return the directory for <pkgid>'s build artifacts in <workspace>.
358 /// Creates it if it doesn't exist.
359 pub fn build_pkg_id_in_workspace(pkgid: &PkgId, workspace: &Path) -> Path {
360     use conditions::bad_path::cond;
361
362     let mut result = workspace.push("build");
363     // n.b. Should actually use a target-specific
364     // subdirectory of build/
365     result = result.push_rel(&*pkgid.local_path);
366     if os::path_exists(&result) || os::mkdir_recursive(&result, U_RWX) {
367         result
368     }
369     else {
370         cond.raise((result, fmt!("Could not create directory for package %s", pkgid.to_str())))
371     }
372 }
373
374 /// Return the output file for a given directory name,
375 /// given whether we're building a library and whether we're building tests
376 pub fn mk_output_path(what: OutputType, where: Target,
377                       pkg_id: &PkgId, workspace: &Path) -> Path {
378     let short_name_with_version = fmt!("%s-%s", pkg_id.short_name,
379                                        pkg_id.version.to_str());
380     // Not local_path.dir_path()! For package foo/bar/blat/, we want
381     // the executable blat-0.5 to live under blat/
382     let dir = match where {
383         // If we're installing, it just goes under <workspace>...
384         Install => {
385             // bad copy, but I just couldn't make the borrow checker happy
386             (*workspace).clone()
387         }
388         // and if we're just building, it goes in a package-specific subdir
389         Build => workspace.push_rel(&*pkg_id.local_path)
390     };
391     debug!("[%?:%?] mk_output_path: short_name = %s, path = %s", what, where,
392            if what == Lib { short_name_with_version.clone() } else { pkg_id.short_name.clone() },
393            dir.to_str());
394     let mut output_path = match what {
395         // this code is duplicated from elsewhere; fix this
396         Lib => dir.push(os::dll_filename(short_name_with_version)),
397         // executable names *aren't* versioned
398         _ => dir.push(fmt!("%s%s%s", pkg_id.short_name,
399                            match what {
400                                Test => "test",
401                                Bench => "bench",
402                                _     => ""
403                            },
404                            os::EXE_SUFFIX))
405     };
406     if !output_path.is_absolute() {
407         output_path = os::getcwd().push_rel(&output_path).normalize();
408     }
409     debug!("mk_output_path: returning %s", output_path.to_str());
410     output_path
411 }
412
413 /// Removes files for the package `pkgid`, assuming it's installed in workspace `workspace`
414 pub fn uninstall_package_from(workspace: &Path, pkgid: &PkgId) {
415     let mut did_something = false;
416     let installed_bin = target_executable_in_workspace(pkgid, workspace);
417     if os::path_exists(&installed_bin) {
418         os::remove_file(&installed_bin);
419         did_something = true;
420     }
421     let installed_lib = target_library_in_workspace(pkgid, workspace);
422     if os::path_exists(&installed_lib) {
423         os::remove_file(&installed_lib);
424         did_something = true;
425     }
426     if !did_something {
427         warn(fmt!("Warning: there don't seem to be any files for %s installed in %s",
428              pkgid.to_str(), workspace.to_str()));
429     }
430
431 }