Skip to content

extra::tempfile: replace mkdtemp with an RAII wrapper (Closes #9763) #9802

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Oct 11, 2013
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
64 changes: 55 additions & 9 deletions src/libextra/tempfile.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,17 +15,63 @@ use std::os;
use std::rand::Rng;
use std::rand;

/// Attempts to make a temporary directory inside of `tmpdir` whose name will
/// have the suffix `suffix`. If no directory can be created, None is returned.
pub fn mkdtemp(tmpdir: &Path, suffix: &str) -> Option<Path> {
let mut r = rand::rng();
for _ in range(0u, 1000) {
let p = tmpdir.push(r.gen_ascii_str(16) + suffix);
if os::make_dir(&p, 0x1c0) { // 700
return Some(p);
/// A wrapper for a path to temporary directory implementing automatic
/// scope-pased deletion.
pub struct TempDir {
priv path: Option<Path>
}

impl TempDir {
/// Attempts to make a temporary directory inside of `tmpdir` whose name
/// will have the suffix `suffix`. The directory will be automatically
/// deleted once the returned wrapper is destroyed.
///
/// If no directory can be created, None is returned.
pub fn new_in(tmpdir: &Path, suffix: &str) -> Option<TempDir> {
if !tmpdir.is_absolute() {
let abs_tmpdir = os::make_absolute(tmpdir);
return TempDir::new_in(&abs_tmpdir, suffix);
}

let mut r = rand::rng();
for _ in range(0u, 1000) {
let p = tmpdir.push(r.gen_ascii_str(16) + suffix);
if os::make_dir(&p, 0x1c0) { // 700
return Some(TempDir { path: Some(p) });
}
}
None
}

/// Attempts to make a temporary directory inside of `os::tmpdir()` whose
/// name will have the suffix `suffix`. The directory will be automatically
/// deleted once the returned wrapper is destroyed.
///
/// If no directory can be created, None is returned.
pub fn new(suffix: &str) -> Option<TempDir> {
TempDir::new_in(&os::tmpdir(), suffix)
}

/// Unwrap the wrapped `std::path::Path` from the `TempDir` wrapper.
/// This discards the wrapper so that the automatic deletion of the
/// temporary directory is prevented.
pub fn unwrap(self) -> Path {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Out of curiosity, why is self passed by value, instead of by reference?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@hatahet I figured it was easier to have unwrap discard the TempDir rather than keeping the "disarmed" TempDir around. So it is passed by value so that it is consumed by call to unwrap and you get compile-time errors if you try to use your TempDir object after unwrapping the path from it, instead of runtime errors when it turns out the Option value has been set to None.

I hope that this way it is easier to use than if for example TempDir just had a Path field and a delete_on_drop: bool field that could be set to false instead of using unwrap, and it felt rather usable when I was working on the tests. Of course it might be different in "real" code that wants to keep temporary directories around for longer, and maybe there is a better way. :)

let mut tmpdir = self;
tmpdir.path.take_unwrap()
}

/// Access the wrapped `std::path::Path` to the temporary directory.
pub fn path<'a>(&'a self) -> &'a Path {
self.path.get_ref()
}
}

impl Drop for TempDir {
fn drop(&mut self) {
for path in self.path.iter() {
os::remove_dir_recursive(path);
}
}
None
}

// the tests for this module need to change the path using change_dir,
Expand Down
10 changes: 3 additions & 7 deletions src/libextra/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1148,8 +1148,7 @@ mod tests {
use test::{TestOpts, run_test};

use std::comm::{stream, SharedChan};
use tempfile;
use std::os;
use tempfile::TempDir;

#[test]
pub fn do_not_run_ignored_tests() {
Expand Down Expand Up @@ -1392,9 +1391,8 @@ mod tests {

pub fn ratchet_test() {

let dpth = tempfile::mkdtemp(&os::tmpdir(),
"test-ratchet").expect("missing test for ratchet");
let pth = dpth.push("ratchet.json");
let dpth = TempDir::new("test-ratchet").expect("missing test for ratchet");
let pth = dpth.path().push("ratchet.json");

let mut m1 = MetricMap::new();
m1.insert_metric("runtime", 1000.0, 2.0);
Expand Down Expand Up @@ -1432,7 +1430,5 @@ mod tests {
assert_eq!(m4.len(), 2);
assert_eq!(*(m4.find(&~"runtime").unwrap()), Metric { value: 1100.0, noise: 2.0 });
assert_eq!(*(m4.find(&~"throughput").unwrap()), Metric { value: 50.0, noise: 2.0 });

os::remove_dir_recursive(&dpth);
}
}
3 changes: 0 additions & 3 deletions src/librustpkg/package_source.rs
Original file line number Diff line number Diff line change
Expand Up @@ -239,9 +239,6 @@ impl PkgSrc {
pub fn fetch_git(local: &Path, pkgid: &PkgId) -> Option<Path> {
use conditions::git_checkout_failed::cond;

// We use a temporary directory because if the git clone fails,
// it creates the target directory anyway and doesn't delete it

debug2!("Checking whether {} (path = {}) exists locally. Cwd = {}, does it? {:?}",
pkgid.to_str(), pkgid.path.to_str(),
os::getcwd().to_str(),
Expand Down
18 changes: 9 additions & 9 deletions src/librustpkg/source_control.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@

use std::{io, os, run, str};
use std::run::{ProcessOutput, ProcessOptions, Process};
use extra::tempfile;
use extra::tempfile::TempDir;
use version::*;
use path_util::chmod_read_only;

Expand All @@ -22,14 +22,6 @@ use path_util::chmod_read_only;
/// directory (that the callee may use, for example, to check out remote sources into).
/// Returns `CheckedOutSources` if the clone succeeded.
pub fn safe_git_clone(source: &Path, v: &Version, target: &Path) -> CloneResult {
use conditions::failed_to_create_temp_dir::cond;

let scratch_dir = tempfile::mkdtemp(&os::tmpdir(), "rustpkg");
let clone_target = match scratch_dir {
Some(d) => d.push("rustpkg_temp"),
None => cond.raise(~"Failed to create temporary directory for fetching git sources")
};

if os::path_exists(source) {
debug2!("{} exists locally! Cloning it into {}",
source.to_str(), target.to_str());
Expand Down Expand Up @@ -77,6 +69,14 @@ pub fn safe_git_clone(source: &Path, v: &Version, target: &Path) -> CloneResult
}
CheckedOutSources
} else {
use conditions::failed_to_create_temp_dir::cond;

let scratch_dir = TempDir::new("rustpkg");
let clone_target = match scratch_dir {
Some(d) => d.unwrap().push("rustpkg_temp"),
None => cond.raise(~"Failed to create temporary directory for fetching git sources")
};

DirToUse(clone_target)
}
}
Expand Down
Loading