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.
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.
11 // rustpkg utilities having to do with paths and directories
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;
20 use std::iterator::IteratorUtil;
24 fn push_if_exists(vec: &mut ~[Path], p: &Path) {
25 let maybe_dir = p.push(".rust");
26 if os::path_exists(&maybe_dir) {
32 static PATH_ENTRY_SEPARATOR: &'static str = ";";
34 static PATH_ENTRY_SEPARATOR: &'static str = ":";
36 /// Returns RUST_PATH as a string, without default paths added
37 pub fn get_rust_path() -> Option<~str> {
38 os::getenv("RUST_PATH")
41 /// Returns the value of RUST_PATH, as a list
42 /// of Paths. Includes default entries for, if they exist:
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() {
49 let env_path_components: ~[&str] =
50 env_path.split_str_iter(PATH_ENTRY_SEPARATOR).collect();
51 env_path_components.map(|&s| Path(s))
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);
71 pub fn default_workspace() -> Path {
74 fail!("Empty RUST_PATH");
77 if !os::path_is_dir(&result) {
78 os::mkdir_recursive(&result, U_RWX);
83 pub fn in_rust_path(p: &Path) -> bool {
84 rust_path().contains(p)
87 pub static U_RWX: i32 = (S_IRUSR | S_IWUSR | S_IXUSR) as i32;
89 /// Creates a directory that is readable, writeable,
90 /// and executable by the user. Returns true iff creation
92 pub fn make_dir_rwx(p: &Path) -> bool { os::make_dir(p, U_RWX) }
94 // n.b. The next three functions ignore the package version right
95 // now. Should fix that.
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)) {
108 debug!("p = %s, remote_path = %s", p.to_str(), pkgid.remote_path.to_str());
110 if p == *pkgid.remote_path {
114 let pf = p.filename();
115 for pf.iter().advance |pf| {
116 let f_ = (*pf).clone();
118 match split_version_general(g, '-') {
119 Some((ref might_match, ref vers)) => {
120 debug!("might_match = %s, vers = %s", *might_match,
122 if *might_match == pkgid.short_name
123 && (*vers == pkgid.version || pkgid.version == NoVersion)
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));
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());
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",
168 if os::path_exists(&result) {
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()));
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)
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)
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",
196 if os::path_exists(&result) {
200 error!(fmt!("output_in_workspace: %s does not exist", result.to_str()));
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")
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")
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",
228 // We don't know what the hash is, so we have to search through the directory
231 debug!("short_name = %s where = %? workspace = %s \
232 prefix = %s", short_name, where, workspace.to_str(), prefix);
234 let dir_to_search = match where {
235 Build => workspace.push(prefix).push_rel(&**path),
236 Install => workspace.push(prefix)
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());
242 let lib_prefix = fmt!("%s%s", os::consts::DLL_PREFIX, short_name);
243 let lib_filetype = os::consts::DLL_SUFFIX;
245 debug!("lib_prefix = %s and lib_filetype = %s", lib_prefix, lib_filetype);
247 let mut result_filename = None;
248 for dir_contents.iter().advance |p| {
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);
255 Some(ref s) if lib_filetype == *s => (),
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
263 for f_name.split_iter('-').advance |piece| {
264 debug!("a piece = %s", piece);
265 if which == 0 && piece != lib_prefix {
272 hash = Some(piece.to_owned());
276 // something went wrong
282 result_filename = Some(p_path);
287 // Return the filename that matches, which we now know exists
288 // (if result_filename != None)
289 match result_filename {
291 warn(fmt!("library_in_workspace didn't find a library in %s for %s",
292 dir_to_search.to_str(), short_name));
295 Some(result_filename) => {
296 let absolute_path = dir_to_search.push_rel(&result_filename);
297 debug!("result_filename = %s", absolute_path.to_str());
303 /// Returns the executable that would be installed for <pkgid>
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)
311 /// Returns the executable that would be installed for <pkgid>
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())));
321 target_file_in_workspace(pkgid, workspace, Lib, Install)
324 /// Returns the test executable that would be installed for <pkgid>
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)
331 /// Returns the bench executable that would be installed for <pkgid>
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)
339 /// Returns the path that pkgid `pkgid` would have if placed `where`
341 fn target_file_in_workspace(pkgid: &PkgId, workspace: &Path,
342 what: OutputType, where: Target) -> Path {
343 use conditions::bad_path::cond;
345 let subdir = match what {
346 Lib => "lib", Main | Test | Bench => "bin"
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)));
354 mk_output_path(what, where, pkgid, &result)
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;
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) {
370 cond.raise((result, fmt!("Could not create directory for package %s", pkgid.to_str())))
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>...
385 // bad copy, but I just couldn't make the borrow checker happy
388 // and if we're just building, it goes in a package-specific subdir
389 Build => workspace.push_rel(&*pkg_id.local_path)
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() },
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,
406 if !output_path.is_absolute() {
407 output_path = os::getcwd().push_rel(&output_path).normalize();
409 debug!("mk_output_path: returning %s", output_path.to_str());
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;
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;
427 warn(fmt!("Warning: there don't seem to be any files for %s installed in %s",
428 pkgid.to_str(), workspace.to_str()));