Skip to content

rustpkg: Implement uninstall and list commands #7749

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 2 commits into from
Jul 14, 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
4 changes: 4 additions & 0 deletions src/librustpkg/conditions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,3 +32,7 @@ condition! {
condition! {
bad_pkg_id: (super::Path, ~str) -> super::PkgId;
}

condition! {
no_rust_path: (~str) -> super::Path;
}
40 changes: 40 additions & 0 deletions src/librustpkg/installed_packages.rs
Original file line number Diff line number Diff line change
@@ -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
}
9 changes: 9 additions & 0 deletions src/librustpkg/package_id.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
27 changes: 26 additions & 1 deletion src/librustpkg/path_util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down Expand Up @@ -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()));
}

}
27 changes: 25 additions & 2 deletions src/librustpkg/rustpkg.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ pub mod api;
mod conditions;
mod context;
mod crate;
mod installed_packages;
mod messages;
mod package_id;
mod package_path;
Expand Down Expand Up @@ -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();
Expand All @@ -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);
Expand Down Expand Up @@ -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(),
Expand Down
110 changes: 90 additions & 20 deletions src/librustpkg/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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::*;
Expand Down Expand Up @@ -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),
Expand Down Expand Up @@ -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",
Expand Down Expand Up @@ -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"));
Expand Down Expand Up @@ -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");
Expand All @@ -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]
Expand Down
2 changes: 1 addition & 1 deletion src/librustpkg/testsuite/pass/src/install-paths/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 }
8 changes: 7 additions & 1 deletion src/librustpkg/testsuite/pass/src/install-paths/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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();
}
8 changes: 7 additions & 1 deletion src/librustpkg/usage.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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:

Expand Down Expand Up @@ -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]

Expand Down
11 changes: 4 additions & 7 deletions src/librustpkg/util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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"];


Expand Down Expand Up @@ -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,
Expand Down
Loading