about summary refs log tree commit diff
diff options
context:
space:
mode:
authorDaniel Micay <danielmicay@gmail.com>2013-07-13 19:18:22 -0700
committerDaniel Micay <danielmicay@gmail.com>2013-07-13 19:18:22 -0700
commita1f4843895d1152527f94e73fc64bf744d97d255 (patch)
treee713a712cc4bc8a228d1c9989260f81e389f56a9
parent8d0feb58e7f5ae67546db9c3cd7fdf4ab792d839 (diff)
parent563172a73be64aa2b0f528346095aba566753d14 (diff)
downloadrust-a1f4843895d1152527f94e73fc64bf744d97d255.tar.gz
rust-a1f4843895d1152527f94e73fc64bf744d97d255.zip
Merge pull request #7749 from catamorphism/rustpkg-list-uninstall
rustpkg: Implement `uninstall` and `list` commands
-rw-r--r--src/librustpkg/conditions.rs4
-rw-r--r--src/librustpkg/installed_packages.rs40
-rw-r--r--src/librustpkg/package_id.rs9
-rw-r--r--src/librustpkg/path_util.rs27
-rw-r--r--src/librustpkg/rustpkg.rs27
-rw-r--r--src/librustpkg/tests.rs110
-rw-r--r--src/librustpkg/testsuite/pass/src/install-paths/lib.rs2
-rw-r--r--src/librustpkg/testsuite/pass/src/install-paths/main.rs8
-rw-r--r--src/librustpkg/usage.rs8
-rw-r--r--src/librustpkg/util.rs11
-rw-r--r--src/librustpkg/version.rs14
11 files changed, 226 insertions, 34 deletions
diff --git a/src/librustpkg/conditions.rs b/src/librustpkg/conditions.rs
index c44563d8703..4f192fd1d92 100644
--- a/src/librustpkg/conditions.rs
+++ b/src/librustpkg/conditions.rs
@@ -32,3 +32,7 @@ condition! {
 condition! {
     bad_pkg_id: (super::Path, ~str) -> super::PkgId;
 }
+
+condition! {
+    no_rust_path: (~str) -> super::Path;
+}
diff --git a/src/librustpkg/installed_packages.rs b/src/librustpkg/installed_packages.rs
new file mode 100644
index 00000000000..980b00d3864
--- /dev/null
+++ b/src/librustpkg/installed_packages.rs
@@ -0,0 +1,40 @@
+// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// Listing installed packages
+
+use path_util::*;
+use std::os;
+
+pub fn list_installed_packages(f: &fn(&PkgId) -> bool) -> bool  {
+    let workspaces = rust_path();
+    for workspaces.iter().advance |p| {
+        let binfiles = os::list_dir(&p.push("bin"));
+        for binfiles.iter().advance() |exec| {
+            f(&PkgId::new(*exec));
+        }
+        let libfiles = os::list_dir(&p.push("lib"));
+        for libfiles.iter().advance() |lib| {
+            f(&PkgId::new(*lib));
+        }
+    }
+    true
+}
+
+pub fn package_is_installed(p: &PkgId) -> bool {
+    let mut is_installed = false;
+    do list_installed_packages() |installed| {
+        if installed == p {
+            is_installed = true;
+        }
+        false
+    };
+    is_installed
+}
\ No newline at end of file
diff --git a/src/librustpkg/package_id.rs b/src/librustpkg/package_id.rs
index ebe2aa6f92a..b11f9820960 100644
--- a/src/librustpkg/package_id.rs
+++ b/src/librustpkg/package_id.rs
@@ -30,6 +30,15 @@ pub struct PkgId {
     version: Version
 }
 
+impl Eq for PkgId {
+    fn eq(&self, p: &PkgId) -> bool {
+        *p.local_path == *self.local_path && p.version == self.version
+    }
+    fn ne(&self, p: &PkgId) -> bool {
+        !(self.eq(p))
+    }
+}
+
 impl PkgId {
     pub fn new(s: &str) -> PkgId {
         use conditions::bad_pkg_id::cond;
diff --git a/src/librustpkg/path_util.rs b/src/librustpkg/path_util.rs
index c6f7735b204..44bbe36feb8 100644
--- a/src/librustpkg/path_util.rs
+++ b/src/librustpkg/path_util.rs
@@ -33,13 +33,18 @@ static PATH_ENTRY_SEPARATOR: &'static str = ";";
 #[cfg(not(windows))]
 static PATH_ENTRY_SEPARATOR: &'static str = ":";
 
+/// Returns RUST_PATH as a string, without default paths added
+pub fn get_rust_path() -> Option<~str> {
+    os::getenv("RUST_PATH")
+}
+
 /// Returns the value of RUST_PATH, as a list
 /// of Paths. Includes default entries for, if they exist:
 /// $HOME/.rust
 /// DIR/.rust for any DIR that's the current working directory
 /// or an ancestor of it
 pub fn rust_path() -> ~[Path] {
-    let mut env_rust_path: ~[Path] = match os::getenv("RUST_PATH") {
+    let mut env_rust_path: ~[Path] = match get_rust_path() {
         Some(env_path) => {
             let env_path_components: ~[&str] =
                 env_path.split_str_iter(PATH_ENTRY_SEPARATOR).collect();
@@ -378,3 +383,23 @@ pub fn mk_output_path(what: OutputType, where: Target,
     debug!("mk_output_path: returning %s", output_path.to_str());
     output_path
 }
+
+/// Removes files for the package `pkgid`, assuming it's installed in workspace `workspace`
+pub fn uninstall_package_from(workspace: &Path, pkgid: &PkgId) {
+    let mut did_something = false;
+    let installed_bin = target_executable_in_workspace(pkgid, workspace);
+    if os::path_exists(&installed_bin) {
+        os::remove_file(&installed_bin);
+        did_something = true;
+    }
+    let installed_lib = target_library_in_workspace(pkgid, workspace);
+    if os::path_exists(&installed_lib) {
+        os::remove_file(&installed_lib);
+        did_something = true;
+    }
+    if !did_something {
+        warn(fmt!("Warning: there don't seem to be any files for %s installed in %s",
+             pkgid.to_str(), workspace.to_str()));
+    }
+
+}
diff --git a/src/librustpkg/rustpkg.rs b/src/librustpkg/rustpkg.rs
index 4e4570961e7..5e9b9ffa788 100644
--- a/src/librustpkg/rustpkg.rs
+++ b/src/librustpkg/rustpkg.rs
@@ -50,6 +50,7 @@ pub mod api;
 mod conditions;
 mod context;
 mod crate;
+mod installed_packages;
 mod messages;
 mod package_id;
 mod package_path;
@@ -248,6 +249,14 @@ impl CtxMethods for Ctx {
                     }
                 }
             }
+            "list" => {
+                io::println("Installed packages:");
+                for installed_packages::list_installed_packages |pkg_id| {
+                    io::println(fmt!("%s-%s",
+                                     pkg_id.local_path.to_str(),
+                                     pkg_id.version.to_str()));
+                }
+            }
             "prefer" => {
                 if args.len() < 1 {
                     return usage::uninstall();
@@ -263,11 +272,24 @@ impl CtxMethods for Ctx {
                     return usage::uninstall();
                 }
 
-                self.uninstall(args[0], None);
+                let pkgid = PkgId::new(args[0]);
+                if !installed_packages::package_is_installed(&pkgid) {
+                    warn(fmt!("Package %s doesn't seem to be installed! Doing nothing.", args[0]));
+                    return;
+                }
+                else {
+                    let rp = rust_path();
+                    assert!(!rp.is_empty());
+                    for each_pkg_parent_workspace(&pkgid) |workspace| {
+                        path_util::uninstall_package_from(workspace, &pkgid);
+                        note(fmt!("Uninstalled package %s (was installed in %s)",
+                                  pkgid.to_str(), workspace.to_str()));
+                    }
+                }
             }
             "unprefer" => {
                 if args.len() < 1 {
-                    return usage::uninstall();
+                    return usage::unprefer();
                 }
 
                 self.unprefer(args[0], None);
@@ -447,6 +469,7 @@ pub fn main() {
             ~"do" => usage::do_cmd(),
             ~"info" => usage::info(),
             ~"install" => usage::install(),
+            ~"list"    => usage::list(),
             ~"prefer" => usage::prefer(),
             ~"test" => usage::test(),
             ~"uninstall" => usage::uninstall(),
diff --git a/src/librustpkg/tests.rs b/src/librustpkg/tests.rs
index 251783577df..c1bf72e8509 100644
--- a/src/librustpkg/tests.rs
+++ b/src/librustpkg/tests.rs
@@ -12,9 +12,10 @@
 
 use context::Ctx;
 use std::hashmap::HashMap;
-use std::{io, libc, os, result, run, str, vec};
+use std::{io, libc, os, result, run, str};
 use extra::tempfile::mkdtemp;
 use std::run::ProcessOutput;
+use installed_packages::list_installed_packages;
 use package_path::*;
 use package_id::{PkgId};
 use package_source::*;
@@ -128,20 +129,27 @@ fn test_sysroot() -> Path {
     self_path.pop()
 }
 
+fn command_line_test(args: &[~str], cwd: &Path) -> ProcessOutput {
+    command_line_test_with_env(args, cwd, None)
+}
+
 /// Runs `rustpkg` (based on the directory that this executable was
 /// invoked from) with the given arguments, in the given working directory.
 /// Returns the process's output.
-fn command_line_test(args: &[~str], cwd: &Path) -> ProcessOutput {
+fn command_line_test_with_env(args: &[~str], cwd: &Path, env: Option<~[(~str, ~str)]>)
+    -> ProcessOutput {
     let cmd = test_sysroot().push("bin").push("rustpkg").to_str();
     let cwd = normalize(RemotePath(copy *cwd));
     debug!("About to run command: %? %? in %s", cmd, args, cwd.to_str());
     assert!(os::path_is_dir(&*cwd));
-    let mut prog = run::Process::new(cmd, args, run::ProcessOptions { env: None,
-                                                           dir: Some(&*cwd),
-                                                           in_fd: None,
-                                                           out_fd: None,
-                                                           err_fd: None
-                                                          });
+    let cwd = cwd.clone();
+    let mut prog = run::Process::new(cmd, args, run::ProcessOptions {
+        env: env.map(|v| v.slice(0, v.len())),
+        dir: Some(&cwd),
+        in_fd: None,
+        out_fd: None,
+        err_fd: None
+    });
     let output = prog.finish_with_output();
     debug!("Output from command %s with args %? was %s {%s}[%?]",
                     cmd, args, str::from_bytes(output.output),
@@ -252,6 +260,16 @@ fn command_line_test_output(args: &[~str]) -> ~[~str] {
     result
 }
 
+fn command_line_test_output_with_env(args: &[~str], env: ~[(~str, ~str)]) -> ~[~str] {
+    let mut result = ~[];
+    let p_output = command_line_test_with_env(args, &os::getcwd(), Some(env));
+    let test_output = str::from_bytes(p_output.output);
+    for test_output.split_iter('\n').advance |s| {
+        result.push(s.to_owned());
+    }
+    result
+}
+
 // assumes short_name and local_path are one and the same -- I should fix
 fn lib_output_file_name(workspace: &Path, parent: &str, short_name: &str) -> Path {
     debug!("lib_output_file_name: given %s and parent %s and short name %s",
@@ -476,8 +494,9 @@ fn test_package_version() {
                     push("test_pkg_version")));
 }
 
-// FIXME #7006: Fails on linux/mac for some reason
-#[test] #[ignore]
+// FIXME #7006: Fails on linux for some reason
+#[test]
+#[ignore]
 fn test_package_request_version() {
     let temp_pkg_id = PkgId::new("github.com/catamorphism/test_pkg_version#0.3");
     let temp = mk_empty_workspace(&LocalPath(Path("test_pkg_version")), &ExactRevision(~"0.3"));
@@ -613,7 +632,33 @@ fn rust_path_parse() {
 }
 
 #[test]
-#[ignore(reason = "Package database not yet implemented")]
+fn test_list() {
+    let foo = PkgId::new("foo");
+    let dir = mkdtemp(&os::tmpdir(), "test_list").expect("test_list failed");
+    create_local_package_in(&foo, &dir);
+    let bar = PkgId::new("bar");
+    create_local_package_in(&bar, &dir);
+    let quux = PkgId::new("quux");
+    create_local_package_in(&quux, &dir);
+
+    command_line_test([~"install", ~"foo"], &dir);
+    let env_arg = ~[(~"RUST_PATH", dir.to_str())];
+    let list_output = command_line_test_output_with_env([~"list"], env_arg.clone());
+    assert!(list_output.iter().any(|x| x.starts_with("foo-")));
+
+    command_line_test([~"install", ~"bar"], &dir);
+    let list_output = command_line_test_output_with_env([~"list"], env_arg.clone());
+    assert!(list_output.iter().any(|x| x.starts_with("foo-")));
+    assert!(list_output.iter().any(|x| x.starts_with("bar-")));
+
+    command_line_test([~"install", ~"quux"], &dir);
+    let list_output = command_line_test_output_with_env([~"list"], env_arg);
+    assert!(list_output.iter().any(|x| x.starts_with("foo-")));
+    assert!(list_output.iter().any(|x| x.starts_with("bar-")));
+    assert!(list_output.iter().any(|x| x.starts_with("quux-")));
+}
+
+#[test]
 fn install_remove() {
     let foo = PkgId::new("foo");
     let bar = PkgId::new("bar");
@@ -622,18 +667,43 @@ fn install_remove() {
     create_local_package_in(&foo, &dir);
     create_local_package_in(&bar, &dir);
     create_local_package_in(&quux, &dir);
+    let rust_path_to_use = ~[(~"RUST_PATH", dir.to_str())];
     command_line_test([~"install", ~"foo"], &dir);
     command_line_test([~"install", ~"bar"], &dir);
     command_line_test([~"install", ~"quux"], &dir);
-    let list_output = command_line_test_output([~"list"]);
-    assert!(list_output.iter().any(|x| x == &~"foo"));
-    assert!(list_output.iter().any(|x| x == &~"bar"));
-    assert!(list_output.iter().any(|x| x == &~"quux"));
-    command_line_test([~"remove", ~"foo"], &dir);
-    let list_output = command_line_test_output([~"list"]);
-    assert!(!list_output.iter().any(|x| x == &~"foo"));
-    assert!(list_output.iter().any(|x| x == &~"bar"));
-    assert!(list_output.iter().any(|x| x == &~"quux"));
+    let list_output = command_line_test_output_with_env([~"list"], rust_path_to_use.clone());
+    assert!(list_output.iter().any(|x| x.starts_with("foo")));
+    assert!(list_output.iter().any(|x| x.starts_with("bar")));
+    assert!(list_output.iter().any(|x| x.starts_with("quux")));
+    command_line_test([~"uninstall", ~"foo"], &dir);
+    let list_output = command_line_test_output_with_env([~"list"], rust_path_to_use.clone());
+    assert!(!list_output.iter().any(|x| x.starts_with("foo")));
+    assert!(list_output.iter().any(|x| x.starts_with("bar")));
+    assert!(list_output.iter().any(|x| x.starts_with("quux")));
+}
+
+#[test]
+fn install_check_duplicates() {
+    // should check that we don't install two packages with the same full name *and* version
+    // ("Is already installed -- doing nothing")
+    // check invariant that there are no dups in the pkg database
+    let dir = mkdtemp(&os::tmpdir(), "install_remove").expect("install_remove");
+    let foo = PkgId::new("foo");
+    create_local_package_in(&foo, &dir);
+
+    command_line_test([~"install", ~"foo"], &dir);
+    command_line_test([~"install", ~"foo"], &dir);
+    let mut contents = ~[];
+    let check_dups = |p: &PkgId| {
+        if contents.contains(p) {
+            fail!("package database contains duplicate ID");
+        }
+        else {
+            contents.push(copy *p);
+        }
+        false
+    };
+    list_installed_packages(check_dups);
 }
 
 #[test]
diff --git a/src/librustpkg/testsuite/pass/src/install-paths/lib.rs b/src/librustpkg/testsuite/pass/src/install-paths/lib.rs
index baf90446f7a..2cc0056696f 100644
--- a/src/librustpkg/testsuite/pass/src/install-paths/lib.rs
+++ b/src/librustpkg/testsuite/pass/src/install-paths/lib.rs
@@ -8,4 +8,4 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-fn f() -> int { 42 }
+pub fn f() -> int { 42 }
diff --git a/src/librustpkg/testsuite/pass/src/install-paths/main.rs b/src/librustpkg/testsuite/pass/src/install-paths/main.rs
index 37e606dcb1a..431350c07b4 100644
--- a/src/librustpkg/testsuite/pass/src/install-paths/main.rs
+++ b/src/librustpkg/testsuite/pass/src/install-paths/main.rs
@@ -19,4 +19,10 @@ The test runner should check that, after `rustpkg install install-paths`
    * install-paths/build/install_pathsbench exists and is an executable
 */
 
-fn main() {}
+use lib::f;
+
+mod lib;
+
+fn main() {
+    f();
+}
diff --git a/src/librustpkg/usage.rs b/src/librustpkg/usage.rs
index fee52c3c11f..59e9e57d643 100644
--- a/src/librustpkg/usage.rs
+++ b/src/librustpkg/usage.rs
@@ -14,7 +14,7 @@ pub fn general() {
     io::println("Usage: rustpkg [options] <cmd> [args..]
 
 Where <cmd> is one of:
-    build, clean, do, info, install, prefer, test, uninstall, unprefer
+    build, clean, do, info, install, list, prefer, test, uninstall, unprefer
 
 Options:
 
@@ -55,6 +55,12 @@ Options:
     -j, --json      Output the result as JSON");
 }
 
+pub fn list() {
+    io::println("rustpkg list
+
+List all installed packages.");
+}
+
 pub fn install() {
     io::println("rustpkg [options..] install [url] [target]
 
diff --git a/src/librustpkg/util.rs b/src/librustpkg/util.rs
index da5c98680b9..1ee7caf6d24 100644
--- a/src/librustpkg/util.rs
+++ b/src/librustpkg/util.rs
@@ -28,8 +28,11 @@ use search::find_library_in_search_path;
 use path_util::target_library_in_workspace;
 pub use target::{OutputType, Main, Lib, Bench, Test};
 
+// It would be nice to have the list of commands in just one place -- for example,
+// you could update the match in rustpkg.rc but forget to update this list. I think
+// that should be fixed.
 static COMMANDS: &'static [&'static str] =
-    &["build", "clean", "do", "info", "install", "prefer", "test", "uninstall",
+    &["build", "clean", "do", "info", "install", "list", "prefer", "test", "uninstall",
       "unprefer"];
 
 
@@ -152,12 +155,6 @@ pub fn ready_crate(sess: session::Session,
     @fold.fold_crate(crate)
 }
 
-pub fn need_dir(s: &Path) {
-    if !os::path_is_dir(s) && !os::make_dir(s, 493_i32) {
-        fail!("can't create dir: %s", s.to_str());
-    }
-}
-
 // FIXME (#4432): Use workcache to only compile when needed
 pub fn compile_input(ctxt: &Ctx,
                      pkg_id: &PkgId,
diff --git a/src/librustpkg/version.rs b/src/librustpkg/version.rs
index 1ec15c107c7..28c3143d8de 100644
--- a/src/librustpkg/version.rs
+++ b/src/librustpkg/version.rs
@@ -18,13 +18,25 @@ use std::{char, os, result, run, str};
 use package_path::RemotePath;
 use extra::tempfile::mkdtemp;
 
-#[deriving(Eq)]
 pub enum Version {
     ExactRevision(~str), // Should look like a m.n.(...).x
     SemanticVersion(semver::Version),
     NoVersion // user didn't specify a version -- prints as 0.1
 }
 
+impl Eq for Version {
+    fn eq(&self, other: &Version) -> bool {
+        match (self, other) {
+            (&ExactRevision(ref s1), &ExactRevision(ref s2)) => *s1 == *s2,
+            (&SemanticVersion(ref v1), &SemanticVersion(ref v2)) => *v1 == *v2,
+            (&NoVersion, _) => true,
+            _ => false
+        }
+    }
+    fn ne(&self, other: &Version) -> bool {
+        !self.eq(other)
+    }
+}
 
 impl Ord for Version {
     fn lt(&self, other: &Version) -> bool {