diff --git a/src/librustpkg/conditions.rs b/src/librustpkg/conditions.rs index c44563d870358..4f192fd1d92c1 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 0000000000000..980b00d3864d5 --- /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 or the MIT license +// , 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 ebe2aa6f92a63..b11f9820960a9 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 c6f7735b204e4..44bbe36feb87a 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 4e4570961e752..5e9b9ffa788d7 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 251783577df8e..c1bf72e850920 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 baf90446f7aac..2cc0056696f31 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 37e606dcb1ab3..431350c07b487 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 fee52c3c11f34..59e9e57d643f2 100644 --- a/src/librustpkg/usage.rs +++ b/src/librustpkg/usage.rs @@ -14,7 +14,7 @@ pub fn general() { io::println("Usage: rustpkg [options] [args..] Where 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 da5c98680b919..1ee7caf6d24e0 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 1ec15c107c780..28c3143d8de17 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 {