Skip to content

Commit a1f4843

Browse files
committed
Merge pull request #7749 from catamorphism/rustpkg-list-uninstall
rustpkg: Implement `uninstall` and `list` commands
2 parents 8d0feb5 + 563172a commit a1f4843

File tree

11 files changed

+226
-34
lines changed

11 files changed

+226
-34
lines changed

src/librustpkg/conditions.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,3 +32,7 @@ condition! {
3232
condition! {
3333
bad_pkg_id: (super::Path, ~str) -> super::PkgId;
3434
}
35+
36+
condition! {
37+
no_rust_path: (~str) -> super::Path;
38+
}

src/librustpkg/installed_packages.rs

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
// Listing installed packages
12+
13+
use path_util::*;
14+
use std::os;
15+
16+
pub fn list_installed_packages(f: &fn(&PkgId) -> bool) -> bool {
17+
let workspaces = rust_path();
18+
for workspaces.iter().advance |p| {
19+
let binfiles = os::list_dir(&p.push("bin"));
20+
for binfiles.iter().advance() |exec| {
21+
f(&PkgId::new(*exec));
22+
}
23+
let libfiles = os::list_dir(&p.push("lib"));
24+
for libfiles.iter().advance() |lib| {
25+
f(&PkgId::new(*lib));
26+
}
27+
}
28+
true
29+
}
30+
31+
pub fn package_is_installed(p: &PkgId) -> bool {
32+
let mut is_installed = false;
33+
do list_installed_packages() |installed| {
34+
if installed == p {
35+
is_installed = true;
36+
}
37+
false
38+
};
39+
is_installed
40+
}

src/librustpkg/package_id.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,15 @@ pub struct PkgId {
3030
version: Version
3131
}
3232

33+
impl Eq for PkgId {
34+
fn eq(&self, p: &PkgId) -> bool {
35+
*p.local_path == *self.local_path && p.version == self.version
36+
}
37+
fn ne(&self, p: &PkgId) -> bool {
38+
!(self.eq(p))
39+
}
40+
}
41+
3342
impl PkgId {
3443
pub fn new(s: &str) -> PkgId {
3544
use conditions::bad_pkg_id::cond;

src/librustpkg/path_util.rs

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,13 +33,18 @@ static PATH_ENTRY_SEPARATOR: &'static str = ";";
3333
#[cfg(not(windows))]
3434
static PATH_ENTRY_SEPARATOR: &'static str = ":";
3535

36+
/// Returns RUST_PATH as a string, without default paths added
37+
pub fn get_rust_path() -> Option<~str> {
38+
os::getenv("RUST_PATH")
39+
}
40+
3641
/// Returns the value of RUST_PATH, as a list
3742
/// of Paths. Includes default entries for, if they exist:
3843
/// $HOME/.rust
3944
/// DIR/.rust for any DIR that's the current working directory
4045
/// or an ancestor of it
4146
pub fn rust_path() -> ~[Path] {
42-
let mut env_rust_path: ~[Path] = match os::getenv("RUST_PATH") {
47+
let mut env_rust_path: ~[Path] = match get_rust_path() {
4348
Some(env_path) => {
4449
let env_path_components: ~[&str] =
4550
env_path.split_str_iter(PATH_ENTRY_SEPARATOR).collect();
@@ -378,3 +383,23 @@ pub fn mk_output_path(what: OutputType, where: Target,
378383
debug!("mk_output_path: returning %s", output_path.to_str());
379384
output_path
380385
}
386+
387+
/// Removes files for the package `pkgid`, assuming it's installed in workspace `workspace`
388+
pub fn uninstall_package_from(workspace: &Path, pkgid: &PkgId) {
389+
let mut did_something = false;
390+
let installed_bin = target_executable_in_workspace(pkgid, workspace);
391+
if os::path_exists(&installed_bin) {
392+
os::remove_file(&installed_bin);
393+
did_something = true;
394+
}
395+
let installed_lib = target_library_in_workspace(pkgid, workspace);
396+
if os::path_exists(&installed_lib) {
397+
os::remove_file(&installed_lib);
398+
did_something = true;
399+
}
400+
if !did_something {
401+
warn(fmt!("Warning: there don't seem to be any files for %s installed in %s",
402+
pkgid.to_str(), workspace.to_str()));
403+
}
404+
405+
}

src/librustpkg/rustpkg.rs

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ pub mod api;
5050
mod conditions;
5151
mod context;
5252
mod crate;
53+
mod installed_packages;
5354
mod messages;
5455
mod package_id;
5556
mod package_path;
@@ -248,6 +249,14 @@ impl CtxMethods for Ctx {
248249
}
249250
}
250251
}
252+
"list" => {
253+
io::println("Installed packages:");
254+
for installed_packages::list_installed_packages |pkg_id| {
255+
io::println(fmt!("%s-%s",
256+
pkg_id.local_path.to_str(),
257+
pkg_id.version.to_str()));
258+
}
259+
}
251260
"prefer" => {
252261
if args.len() < 1 {
253262
return usage::uninstall();
@@ -263,11 +272,24 @@ impl CtxMethods for Ctx {
263272
return usage::uninstall();
264273
}
265274

266-
self.uninstall(args[0], None);
275+
let pkgid = PkgId::new(args[0]);
276+
if !installed_packages::package_is_installed(&pkgid) {
277+
warn(fmt!("Package %s doesn't seem to be installed! Doing nothing.", args[0]));
278+
return;
279+
}
280+
else {
281+
let rp = rust_path();
282+
assert!(!rp.is_empty());
283+
for each_pkg_parent_workspace(&pkgid) |workspace| {
284+
path_util::uninstall_package_from(workspace, &pkgid);
285+
note(fmt!("Uninstalled package %s (was installed in %s)",
286+
pkgid.to_str(), workspace.to_str()));
287+
}
288+
}
267289
}
268290
"unprefer" => {
269291
if args.len() < 1 {
270-
return usage::uninstall();
292+
return usage::unprefer();
271293
}
272294

273295
self.unprefer(args[0], None);
@@ -447,6 +469,7 @@ pub fn main() {
447469
~"do" => usage::do_cmd(),
448470
~"info" => usage::info(),
449471
~"install" => usage::install(),
472+
~"list" => usage::list(),
450473
~"prefer" => usage::prefer(),
451474
~"test" => usage::test(),
452475
~"uninstall" => usage::uninstall(),

src/librustpkg/tests.rs

Lines changed: 90 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,10 @@
1212

1313
use context::Ctx;
1414
use std::hashmap::HashMap;
15-
use std::{io, libc, os, result, run, str, vec};
15+
use std::{io, libc, os, result, run, str};
1616
use extra::tempfile::mkdtemp;
1717
use std::run::ProcessOutput;
18+
use installed_packages::list_installed_packages;
1819
use package_path::*;
1920
use package_id::{PkgId};
2021
use package_source::*;
@@ -128,20 +129,27 @@ fn test_sysroot() -> Path {
128129
self_path.pop()
129130
}
130131

132+
fn command_line_test(args: &[~str], cwd: &Path) -> ProcessOutput {
133+
command_line_test_with_env(args, cwd, None)
134+
}
135+
131136
/// Runs `rustpkg` (based on the directory that this executable was
132137
/// invoked from) with the given arguments, in the given working directory.
133138
/// Returns the process's output.
134-
fn command_line_test(args: &[~str], cwd: &Path) -> ProcessOutput {
139+
fn command_line_test_with_env(args: &[~str], cwd: &Path, env: Option<~[(~str, ~str)]>)
140+
-> ProcessOutput {
135141
let cmd = test_sysroot().push("bin").push("rustpkg").to_str();
136142
let cwd = normalize(RemotePath(copy *cwd));
137143
debug!("About to run command: %? %? in %s", cmd, args, cwd.to_str());
138144
assert!(os::path_is_dir(&*cwd));
139-
let mut prog = run::Process::new(cmd, args, run::ProcessOptions { env: None,
140-
dir: Some(&*cwd),
141-
in_fd: None,
142-
out_fd: None,
143-
err_fd: None
144-
});
145+
let cwd = cwd.clone();
146+
let mut prog = run::Process::new(cmd, args, run::ProcessOptions {
147+
env: env.map(|v| v.slice(0, v.len())),
148+
dir: Some(&cwd),
149+
in_fd: None,
150+
out_fd: None,
151+
err_fd: None
152+
});
145153
let output = prog.finish_with_output();
146154
debug!("Output from command %s with args %? was %s {%s}[%?]",
147155
cmd, args, str::from_bytes(output.output),
@@ -252,6 +260,16 @@ fn command_line_test_output(args: &[~str]) -> ~[~str] {
252260
result
253261
}
254262

263+
fn command_line_test_output_with_env(args: &[~str], env: ~[(~str, ~str)]) -> ~[~str] {
264+
let mut result = ~[];
265+
let p_output = command_line_test_with_env(args, &os::getcwd(), Some(env));
266+
let test_output = str::from_bytes(p_output.output);
267+
for test_output.split_iter('\n').advance |s| {
268+
result.push(s.to_owned());
269+
}
270+
result
271+
}
272+
255273
// assumes short_name and local_path are one and the same -- I should fix
256274
fn lib_output_file_name(workspace: &Path, parent: &str, short_name: &str) -> Path {
257275
debug!("lib_output_file_name: given %s and parent %s and short name %s",
@@ -476,8 +494,9 @@ fn test_package_version() {
476494
push("test_pkg_version")));
477495
}
478496
479-
// FIXME #7006: Fails on linux/mac for some reason
480-
#[test] #[ignore]
497+
// FIXME #7006: Fails on linux for some reason
498+
#[test]
499+
#[ignore]
481500
fn test_package_request_version() {
482501
let temp_pkg_id = PkgId::new("github.com/catamorphism/test_pkg_version#0.3");
483502
let temp = mk_empty_workspace(&LocalPath(Path("test_pkg_version")), &ExactRevision(~"0.3"));
@@ -613,7 +632,33 @@ fn rust_path_parse() {
613632
}
614633
615634
#[test]
616-
#[ignore(reason = "Package database not yet implemented")]
635+
fn test_list() {
636+
let foo = PkgId::new("foo");
637+
let dir = mkdtemp(&os::tmpdir(), "test_list").expect("test_list failed");
638+
create_local_package_in(&foo, &dir);
639+
let bar = PkgId::new("bar");
640+
create_local_package_in(&bar, &dir);
641+
let quux = PkgId::new("quux");
642+
create_local_package_in(&quux, &dir);
643+
644+
command_line_test([~"install", ~"foo"], &dir);
645+
let env_arg = ~[(~"RUST_PATH", dir.to_str())];
646+
let list_output = command_line_test_output_with_env([~"list"], env_arg.clone());
647+
assert!(list_output.iter().any(|x| x.starts_with("foo-")));
648+
649+
command_line_test([~"install", ~"bar"], &dir);
650+
let list_output = command_line_test_output_with_env([~"list"], env_arg.clone());
651+
assert!(list_output.iter().any(|x| x.starts_with("foo-")));
652+
assert!(list_output.iter().any(|x| x.starts_with("bar-")));
653+
654+
command_line_test([~"install", ~"quux"], &dir);
655+
let list_output = command_line_test_output_with_env([~"list"], env_arg);
656+
assert!(list_output.iter().any(|x| x.starts_with("foo-")));
657+
assert!(list_output.iter().any(|x| x.starts_with("bar-")));
658+
assert!(list_output.iter().any(|x| x.starts_with("quux-")));
659+
}
660+
661+
#[test]
617662
fn install_remove() {
618663
let foo = PkgId::new("foo");
619664
let bar = PkgId::new("bar");
@@ -622,18 +667,43 @@ fn install_remove() {
622667
create_local_package_in(&foo, &dir);
623668
create_local_package_in(&bar, &dir);
624669
create_local_package_in(&quux, &dir);
670+
let rust_path_to_use = ~[(~"RUST_PATH", dir.to_str())];
625671
command_line_test([~"install", ~"foo"], &dir);
626672
command_line_test([~"install", ~"bar"], &dir);
627673
command_line_test([~"install", ~"quux"], &dir);
628-
let list_output = command_line_test_output([~"list"]);
629-
assert!(list_output.iter().any(|x| x == &~"foo"));
630-
assert!(list_output.iter().any(|x| x == &~"bar"));
631-
assert!(list_output.iter().any(|x| x == &~"quux"));
632-
command_line_test([~"remove", ~"foo"], &dir);
633-
let list_output = command_line_test_output([~"list"]);
634-
assert!(!list_output.iter().any(|x| x == &~"foo"));
635-
assert!(list_output.iter().any(|x| x == &~"bar"));
636-
assert!(list_output.iter().any(|x| x == &~"quux"));
674+
let list_output = command_line_test_output_with_env([~"list"], rust_path_to_use.clone());
675+
assert!(list_output.iter().any(|x| x.starts_with("foo")));
676+
assert!(list_output.iter().any(|x| x.starts_with("bar")));
677+
assert!(list_output.iter().any(|x| x.starts_with("quux")));
678+
command_line_test([~"uninstall", ~"foo"], &dir);
679+
let list_output = command_line_test_output_with_env([~"list"], rust_path_to_use.clone());
680+
assert!(!list_output.iter().any(|x| x.starts_with("foo")));
681+
assert!(list_output.iter().any(|x| x.starts_with("bar")));
682+
assert!(list_output.iter().any(|x| x.starts_with("quux")));
683+
}
684+
685+
#[test]
686+
fn install_check_duplicates() {
687+
// should check that we don't install two packages with the same full name *and* version
688+
// ("Is already installed -- doing nothing")
689+
// check invariant that there are no dups in the pkg database
690+
let dir = mkdtemp(&os::tmpdir(), "install_remove").expect("install_remove");
691+
let foo = PkgId::new("foo");
692+
create_local_package_in(&foo, &dir);
693+
694+
command_line_test([~"install", ~"foo"], &dir);
695+
command_line_test([~"install", ~"foo"], &dir);
696+
let mut contents = ~[];
697+
let check_dups = |p: &PkgId| {
698+
if contents.contains(p) {
699+
fail!("package database contains duplicate ID");
700+
}
701+
else {
702+
contents.push(copy *p);
703+
}
704+
false
705+
};
706+
list_installed_packages(check_dups);
637707
}
638708
639709
#[test]

src/librustpkg/testsuite/pass/src/install-paths/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,4 +8,4 @@
88
// option. This file may not be copied, modified, or distributed
99
// except according to those terms.
1010

11-
fn f() -> int { 42 }
11+
pub fn f() -> int { 42 }

src/librustpkg/testsuite/pass/src/install-paths/main.rs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,4 +19,10 @@ The test runner should check that, after `rustpkg install install-paths`
1919
* install-paths/build/install_pathsbench exists and is an executable
2020
*/
2121

22-
fn main() {}
22+
use lib::f;
23+
24+
mod lib;
25+
26+
fn main() {
27+
f();
28+
}

src/librustpkg/usage.rs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ pub fn general() {
1414
io::println("Usage: rustpkg [options] <cmd> [args..]
1515
1616
Where <cmd> is one of:
17-
build, clean, do, info, install, prefer, test, uninstall, unprefer
17+
build, clean, do, info, install, list, prefer, test, uninstall, unprefer
1818
1919
Options:
2020
@@ -55,6 +55,12 @@ Options:
5555
-j, --json Output the result as JSON");
5656
}
5757

58+
pub fn list() {
59+
io::println("rustpkg list
60+
61+
List all installed packages.");
62+
}
63+
5864
pub fn install() {
5965
io::println("rustpkg [options..] install [url] [target]
6066

src/librustpkg/util.rs

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,11 @@ use search::find_library_in_search_path;
2828
use path_util::target_library_in_workspace;
2929
pub use target::{OutputType, Main, Lib, Bench, Test};
3030

31+
// It would be nice to have the list of commands in just one place -- for example,
32+
// you could update the match in rustpkg.rc but forget to update this list. I think
33+
// that should be fixed.
3134
static COMMANDS: &'static [&'static str] =
32-
&["build", "clean", "do", "info", "install", "prefer", "test", "uninstall",
35+
&["build", "clean", "do", "info", "install", "list", "prefer", "test", "uninstall",
3336
"unprefer"];
3437

3538

@@ -152,12 +155,6 @@ pub fn ready_crate(sess: session::Session,
152155
@fold.fold_crate(crate)
153156
}
154157

155-
pub fn need_dir(s: &Path) {
156-
if !os::path_is_dir(s) && !os::make_dir(s, 493_i32) {
157-
fail!("can't create dir: %s", s.to_str());
158-
}
159-
}
160-
161158
// FIXME (#4432): Use workcache to only compile when needed
162159
pub fn compile_input(ctxt: &Ctx,
163160
pkg_id: &PkgId,

0 commit comments

Comments
 (0)