Merge pull request #7749 from catamorphism/rustpkg-list-uninstall
rustpkg: Implement `uninstall` and `list` commands
This commit is contained in:
commit
a1f4843895
src/librustpkg
conditions.rsinstalled_packages.rspackage_id.rspath_util.rsrustpkg.rstests.rs
testsuite/pass/src/install-paths
usage.rsutil.rsversion.rs@ -32,3 +32,7 @@ condition! {
|
||||
condition! {
|
||||
bad_pkg_id: (super::Path, ~str) -> super::PkgId;
|
||||
}
|
||||
|
||||
condition! {
|
||||
no_rust_path: (~str) -> super::Path;
|
||||
}
|
||||
|
40
src/librustpkg/installed_packages.rs
Normal file
40
src/librustpkg/installed_packages.rs
Normal file
@ -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
|
||||
}
|
@ -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;
|
||||
|
@ -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()));
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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(),
|
||||
|
@ -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]
|
||||
|
@ -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 }
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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]
|
||||
|
||||
|
@ -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,
|
||||
|
@ -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 {
|
||||
|
Loading…
x
Reference in New Issue
Block a user