]> git.lizzy.rs Git - rust.git/commitdiff
rustpkg: Implement `uninstall` and `list` commands
authorTim Chevalier <chevalier@alum.wellesley.edu>
Fri, 12 Jul 2013 01:20:31 +0000 (18:20 -0700)
committerTim Chevalier <chevalier@alum.wellesley.edu>
Sat, 13 Jul 2013 20:36:50 +0000 (13:36 -0700)
src/librustpkg/conditions.rs
src/librustpkg/installed_packages.rs [new file with mode: 0644]
src/librustpkg/package_id.rs
src/librustpkg/path_util.rs
src/librustpkg/rustpkg.rs
src/librustpkg/tests.rs
src/librustpkg/testsuite/pass/src/install-paths/lib.rs
src/librustpkg/testsuite/pass/src/install-paths/main.rs
src/librustpkg/usage.rs
src/librustpkg/util.rs
src/librustpkg/version.rs

index c44563d870358eadbbe7c10b56d61fa29ff3ffeb..4f192fd1d92c105385286e17e6567a432cef54ca 100644 (file)
@@ -32,3 +32,7 @@
 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 (file)
index 0000000..2716cc5
--- /dev/null
@@ -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;
+    let _ = do list_installed_packages() |installed| {
+        if installed == p {
+            is_installed = true;
+        }
+        false
+    };
+    is_installed
+}
\ No newline at end of file
index ebe2aa6f92a63b1f3b79e28a2c6f1acc68429a09..b11f9820960a96b9aadf465f0330cca6b12fe5b8 100644 (file)
@@ -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;
index c6f7735b204e47ac600817e0b7b2a3f8a6b31b5b..44bbe36feb87a9cd51e93223bb23d6f2ec04ed9b 100644 (file)
@@ -33,13 +33,18 @@ fn push_if_exists(vec: &mut ~[Path], p: &Path) {
 #[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()));
+    }
+
+}
index 4e4570961e752fd0edc642c3a6d93508fea71bff..5e9b9ffa788d71d44ae14fce999863dff7f2fbf4 100644 (file)
@@ -50,6 +50,7 @@
 mod conditions;
 mod context;
 mod crate;
+mod installed_packages;
 mod messages;
 mod package_id;
 mod package_path;
@@ -248,6 +249,14 @@ fn run(&self, cmd: &str, args: ~[~str]) {
                     }
                 }
             }
+            "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 @@ fn run(&self, cmd: &str, args: ~[~str]) {
                     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(),
index 251783577df8e8eb746a2452394d1406c5ab1fb4..c1bf72e8509205128937964ef7f30df16f0b4613 100644 (file)
 
 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]
index baf90446f7aaca6c3364f96306e94574ee76f602..2cc0056696f31e1c2cccd5bf24f23b6249f558db 100644 (file)
@@ -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 }
index 37e606dcb1ab3ee283c3c6a280cf8837c2bdc038..3be03b6278bc500f24415bc52a73432973917dba 100644 (file)
    * install-paths/build/install_pathsbench exists and is an executable
 */
 
-fn main() {}
+use lib::f;
+
+mod lib;
+
+fn main() {
+    let _ = f();
+}
index fee52c3c11f34215e668d3c28156ae00f89c0978..59e9e57d643f21d23dc712802ee03a571c105780 100644 (file)
@@ -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 @@ pub fn info() {
     -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]
 
index da5c98680b9199745444b797081c2497b8fd696f..1ee7caf6d24e0fd214eb9d885bc1705157b0dced 100644 (file)
 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,
index 1ec15c107c7804a18b79b6863d967c3ad36dbe42..28c3143d8de175d06a113f954cb61f55be1224db 100644 (file)
 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 {