rust/src/cargo/cargo.rs

1922 lines
52 KiB
Rust
Raw Normal View History

2011-11-30 17:57:46 -06:00
// cargo.rs - Rust package manager
import syntax::{ast, codemap, parse, visit, attr};
import syntax::diagnostic::span_handler;
import codemap::span;
import rustc::metadata::filesearch::{get_cargo_root, get_cargo_root_nearest,
get_cargo_sysroot, libdir};
import syntax::diagnostic;
2011-11-30 17:57:46 -06:00
import result::{ok, err};
import io::writer_util;
import std::{map, json, tempfile, term, sort, getopts};
import map::hashmap;
import json::to_str;
import getopts::{optflag, optopt, opt_present};
2011-11-30 17:57:46 -06:00
2011-12-16 19:33:39 -06:00
type package = {
name: str,
uuid: str,
url: str,
method: str,
description: str,
ref: option<str>,
tags: [str],
versions: [(str, str)]
};
type local_package = {
name: str,
metaname: str,
version: str,
files: [str]
2011-12-16 19:33:39 -06:00
};
type source = @{
name: str,
mut url: str,
mut method: str,
mut key: option<str>,
mut keyfp: option<str>,
mut packages: [mut package]
2011-12-16 19:33:39 -06:00
};
2011-12-08 22:50:25 -06:00
type cargo = {
pgp: bool,
2011-12-08 22:50:25 -06:00
root: str,
installdir: str,
2011-12-08 22:50:25 -06:00
bindir: str,
libdir: str,
workdir: str,
2011-12-16 19:33:39 -06:00
sourcedir: str,
2012-01-18 21:16:14 -06:00
sources: map::hashmap<str, source>,
mut current_install: str,
dep_cache: map::hashmap<str, bool>,
opts: options
2011-12-08 22:50:25 -06:00
};
type crate = {
name: str,
vers: str,
uuid: str,
desc: option<str>,
sigs: option<str>,
crate_type: option<str>,
deps: [str]
2011-11-30 17:57:46 -06:00
};
type options = {
test: bool,
mode: mode,
free: [str],
help: bool,
};
enum mode { system_mode, user_mode, local_mode }
fn opts() -> [getopts::opt] {
[optflag("g"), optflag("G"), optflag("test"),
optflag("h"), optflag("help")]
}
2011-12-16 19:33:39 -06:00
fn info(msg: str) {
let out = io::stdout();
if term::color_supported() {
term::fg(out, term::color_green);
out.write_str("info: ");
term::reset(out);
out.write_line(msg);
} else { out.write_line("info: " + msg); }
2011-12-16 19:33:39 -06:00
}
fn warn(msg: str) {
let out = io::stdout();
if term::color_supported() {
term::fg(out, term::color_yellow);
out.write_str("warning: ");
term::reset(out);
out.write_line(msg);
}else { out.write_line("warning: " + msg); }
2011-12-16 19:33:39 -06:00
}
fn error(msg: str) {
let out = io::stdout();
if term::color_supported() {
term::fg(out, term::color_red);
out.write_str("error: ");
term::reset(out);
out.write_line(msg);
}
else { out.write_line("error: " + msg); }
}
fn is_uuid(id: str) -> bool {
let parts = str::split_str(id, "-");
if vec::len(parts) == 5u {
let mut correct = 0u;
2012-06-03 01:55:32 -05:00
for vec::eachi(parts) { |i, part|
fn is_hex_digit(ch: char) -> bool {
('0' <= ch && ch <= '9') ||
('a' <= ch && ch <= 'f') ||
('A' <= ch && ch <= 'F')
}
2012-06-03 01:55:32 -05:00
if !part.all(is_hex_digit) {
ret false;
}
alt i {
0u {
if str::len(part) == 8u {
correct += 1u;
}
}
1u | 2u | 3u {
if str::len(part) == 4u {
correct += 1u;
}
}
4u {
if str::len(part) == 12u {
correct += 1u;
}
}
_ { }
}
}
if correct >= 5u {
ret true;
}
}
2012-06-03 01:55:32 -05:00
ret false;
}
2012-06-03 01:30:11 -05:00
#[test]
fn test_is_uuid() {
2012-06-03 01:55:32 -05:00
assert is_uuid("aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaafAF09");
2012-06-03 01:30:11 -05:00
assert !is_uuid("aaaaaaaa-aaaa-aaaa-aaaaa-aaaaaaaaaaaa");
assert !is_uuid("");
2012-06-03 01:55:32 -05:00
assert !is_uuid("aaaaaaaa-aaa -aaaa-aaaa-aaaaaaaaaaaa");
assert !is_uuid("aaaaaaaa-aaa!-aaaa-aaaa-aaaaaaaaaaaa");
assert !is_uuid("aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa-a");
assert !is_uuid("aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaป");
2012-06-03 01:30:11 -05:00
}
// FIXME: implement url/URL parsing so we don't have to resort to weak checks
fn has_archive_extension(p: str) -> bool {
str::ends_with(p, ".tar") ||
str::ends_with(p, ".tar.gz") ||
str::ends_with(p, ".tar.bz2") ||
str::ends_with(p, ".tar.Z") ||
str::ends_with(p, ".tar.lz") ||
str::ends_with(p, ".tar.xz") ||
str::ends_with(p, ".tgz") ||
str::ends_with(p, ".tbz") ||
str::ends_with(p, ".tbz2") ||
str::ends_with(p, ".tb2") ||
str::ends_with(p, ".taz") ||
str::ends_with(p, ".tlz") ||
str::ends_with(p, ".txz")
}
fn is_archive_path(u: str) -> bool {
has_archive_extension(u) && os::path_exists(u)
}
fn is_archive_url(u: str) -> bool {
// FIXME: this requires the protocol bit - if we had proper url parsing,
// we wouldn't need it
alt str::find_str(u, "://") {
option::some(i) { has_archive_extension(u) }
_ { false }
}
}
fn is_git_url(url: str) -> bool {
if str::ends_with(url, "/") { str::ends_with(url, ".git/") }
else {
str::starts_with(url, "git://") || str::ends_with(url, ".git")
}
}
fn assume_source_method(url: str) -> str {
if is_git_url(url) {
ret "git";
}
if str::starts_with(url, "file://") || os::path_exists(url) {
ret "file";
}
"curl"
}
fn load_link(mis: [@ast::meta_item]) -> (option<str>,
option<str>,
option<str>) {
let mut name = none;
let mut vers = none;
let mut uuid = none;
for mis.each {|a|
2011-11-30 17:57:46 -06:00
alt a.node {
ast::meta_name_value(v, {node: ast::lit_str(s), span: _}) {
alt v {
"name" { name = some(s); }
"vers" { vers = some(s); }
"uuid" { uuid = some(s); }
_ { }
}
}
_ { fail "load_link: meta items must be name-values"; }
2011-11-30 17:57:46 -06:00
}
}
(name, vers, uuid)
}
fn load_crate(filename: str) -> option<crate> {
2012-01-13 19:33:16 -06:00
let cm = codemap::new_codemap();
let handler = diagnostic::mk_handler(none);
2012-01-13 19:33:16 -06:00
let sess = @{
cm: cm,
2012-03-26 20:35:18 -05:00
mut next_id: 1,
span_diagnostic: diagnostic::mk_span_handler(handler, cm),
2012-03-26 20:35:18 -05:00
mut chpos: 0u,
mut byte_pos: 0u
2012-01-13 19:33:16 -06:00
};
let c = parse::parse_crate_from_crate_file(filename, [], sess);
2011-11-30 17:57:46 -06:00
let mut name = none;
let mut vers = none;
let mut uuid = none;
let mut desc = none;
let mut sigs = none;
let mut crate_type = none;
2011-11-30 17:57:46 -06:00
for c.node.attrs.each {|a|
2011-11-30 17:57:46 -06:00
alt a.node.value.node {
ast::meta_name_value(v, {node: ast::lit_str(s), span: _}) {
alt v {
"desc" { desc = some(v); }
"sigs" { sigs = some(v); }
"crate_type" { crate_type = some(v); }
2011-11-30 17:57:46 -06:00
_ { }
}
}
ast::meta_list(v, mis) {
if v == "link" {
let (n, v, u) = load_link(mis);
name = n;
vers = v;
uuid = u;
}
}
_ {
fail "crate attributes may not contain " +
"meta_words";
}
2011-11-30 17:57:46 -06:00
}
}
type env = @{
mut deps: [str]
};
fn goto_view_item(e: env, i: @ast::view_item) {
alt i.node {
ast::view_item_use(ident, metas, id) {
let name_items = attr::find_meta_items_by_name(metas, "name");
let m = if name_items.is_empty() {
metas + [attr::mk_name_value_item_str("name", ident)]
} else {
metas
};
let mut attr_name = ident;
let mut attr_vers = "";
let mut attr_from = "";
for m.each { |item|
alt attr::get_meta_item_value_str(item) {
some(value) {
let name = attr::get_meta_item_name(item);
alt name {
"vers" { attr_vers = value; }
"from" { attr_from = value; }
_ {}
}
}
none {}
}
}
let query = if !str::is_empty(attr_from) {
attr_from
} else {
if !str::is_empty(attr_vers) {
attr_name + "@" + attr_vers
} else { attr_name }
};
alt attr_name {
"std" | "core" { }
_ { e.deps += [query]; }
}
}
_ { }
}
}
fn goto_item(_e: env, _i: @ast::item) {
}
let e = @{
mut deps: []
};
let v = visit::mk_simple_visitor(@{
visit_view_item: bind goto_view_item(e, _),
visit_item: bind goto_item(e, _),
with *visit::default_simple_visitor()
});
visit::visit_crate(*c, (), v);
let deps = copy e.deps;
2011-11-30 17:57:46 -06:00
alt (name, vers, uuid) {
(some(name0), some(vers0), some(uuid0)) {
some({
name: name0,
vers: vers0,
uuid: uuid0,
desc: desc,
sigs: sigs,
crate_type: crate_type,
deps: deps })
2011-11-30 17:57:46 -06:00
}
_ { ret none; }
}
}
fn print(s: str) {
io::stdout().write_line(s);
}
fn rest(s: str, start: uint) -> str {
2012-02-23 03:44:04 -06:00
if (start >= str::len(s)) {
2011-11-30 17:57:46 -06:00
""
} else {
2012-02-23 03:44:04 -06:00
str::slice(s, start, str::len(s))
2011-11-30 17:57:46 -06:00
}
}
fn need_dir(s: str) {
if os::path_is_dir(s) { ret; }
if !os::make_dir(s, 493_i32 /* oct: 755 */) {
fail #fmt["can't make_dir %s", s];
}
}
fn valid_pkg_name(s: str) -> bool {
fn is_valid_digit(c: char) -> bool {
('0' <= c && c <= '9') ||
('a' <= c && c <= 'z') ||
('A' <= c && c <= 'Z') ||
c == '-' ||
c == '_'
}
s.all(is_valid_digit)
}
2011-12-16 19:33:39 -06:00
fn parse_source(name: str, j: json::json) -> source {
if !valid_pkg_name(name) {
fail #fmt("'%s' is an invalid source name", name);
}
2011-12-16 19:33:39 -06:00
alt j {
2012-06-11 10:19:49 -05:00
json::dict(j) {
let mut url = alt j.find("url") {
2011-12-16 19:33:39 -06:00
some(json::string(u)) {
u
2011-12-16 19:33:39 -06:00
}
_ { fail "needed 'url' field in source"; }
2011-12-16 19:33:39 -06:00
};
2012-06-11 10:19:49 -05:00
let method = alt j.find("method") {
some(json::string(u)) {
u
}
_ { assume_source_method(url) }
};
2012-06-11 10:19:49 -05:00
let key = alt j.find("key") {
some(json::string(u)) {
some(u)
}
_ { none }
};
2012-06-11 10:19:49 -05:00
let keyfp = alt j.find("keyfp") {
some(json::string(u)) {
some(u)
}
_ { none }
};
if method == "file" {
url = os::make_absolute(url);
}
ret @{
name: name,
mut url: url,
mut method: method,
mut key: key,
mut keyfp: keyfp,
mut packages: [mut] };
2011-12-16 19:33:39 -06:00
}
_ { fail "needed dict value in source"; }
2011-12-16 19:33:39 -06:00
};
}
fn try_parse_sources(filename: str, sources: map::hashmap<str, source>) {
if !os::path_exists(filename) { ret; }
2011-12-16 19:33:39 -06:00
let c = io::read_whole_file_str(filename);
alt json::from_str(result::get(c)) {
ok(json::dict(j)) {
for j.each { |k, v|
2011-12-16 19:33:39 -06:00
sources.insert(k, parse_source(k, v));
#debug("source: %s", k);
2011-12-16 19:33:39 -06:00
}
}
ok(_) { fail "malformed sources.json"; }
err(e) { fail #fmt("%s:%s", filename, e.to_str()); }
2011-12-16 19:33:39 -06:00
}
}
fn load_one_source_package(src: source, p: map::hashmap<str, json::json>) {
2011-12-16 19:33:39 -06:00
let name = alt p.find("name") {
2012-06-11 10:19:49 -05:00
some(json::string(n)) {
if !valid_pkg_name(n) {
warn("malformed source json: " + src.name + ", '" + n + "'"+
" is an invalid name (alphanumeric, underscores and" +
" dashes only)");
ret;
}
2012-06-11 10:19:49 -05:00
n
}
2011-12-16 19:33:39 -06:00
_ {
warn("malformed source json: " + src.name + " (missing name)");
2011-12-16 19:33:39 -06:00
ret;
}
};
let uuid = alt p.find("uuid") {
2012-06-11 10:19:49 -05:00
some(json::string(n)) {
if !is_uuid(n) {
warn("malformed source json: " + src.name + ", '" + n + "'"+
" is an invalid uuid");
ret;
}
2012-06-11 10:19:49 -05:00
n
}
2011-12-16 19:33:39 -06:00
_ {
warn("malformed source json: " + src.name + " (missing uuid)");
2011-12-16 19:33:39 -06:00
ret;
}
};
let url = alt p.find("url") {
2012-06-11 10:19:49 -05:00
some(json::string(n)) { n }
2011-12-16 19:33:39 -06:00
_ {
warn("malformed source json: " + src.name + " (missing url)");
2011-12-16 19:33:39 -06:00
ret;
}
};
let method = alt p.find("method") {
2012-06-11 10:19:49 -05:00
some(json::string(n)) { n }
_ {
warn("malformed source json: " + src.name + " (missing method)");
ret;
}
};
let ref = alt p.find("ref") {
2012-06-11 10:19:49 -05:00
some(json::string(n)) { some(n) }
_ { none }
};
let mut tags = [];
alt p.find("tags") {
some(json::list(js)) {
for js.each {|j|
alt j {
2012-06-11 10:19:49 -05:00
json::string(j) { vec::grow(tags, 1u, j); }
_ { }
}
}
}
_ { }
}
let description = alt p.find("description") {
2012-06-11 10:19:49 -05:00
some(json::string(n)) { n }
_ {
warn("malformed source json: " + src.name
2012-01-23 23:14:48 -06:00
+ " (missing description)");
ret;
}
};
let newpkg = {
name: name,
uuid: uuid,
url: url,
method: method,
description: description,
ref: ref,
tags: tags,
versions: []
};
alt src.packages.position({ |pkg| pkg.uuid == uuid }) {
some(idx) {
src.packages[idx] = newpkg;
log(debug, " updated package: " + src.name + "/" + name);
}
none {
vec::grow(src.packages, 1u, newpkg);
}
}
log(debug, " loaded package: " + src.name + "/" + name);
2011-12-16 19:33:39 -06:00
}
fn load_source_info(c: cargo, src: source) {
let dir = path::connect(c.sourcedir, src.name);
let srcfile = path::connect(dir, "source.json");
if !os::path_exists(srcfile) { ret; }
let srcstr = io::read_whole_file_str(srcfile);
alt json::from_str(result::get(srcstr)) {
ok(json::dict(_s)) {
let o = parse_source(src.name, json::dict(_s));
src.key = o.key;
src.keyfp = o.keyfp;
}
ok(_) {
warn("malformed source.json: " + src.name +
"(source info is not a dict)");
}
err(e) {
warn(#fmt("%s:%s", src.name, e.to_str()));
}
};
}
fn load_source_packages(c: cargo, src: source) {
log(debug, "loading source: " + src.name);
let dir = path::connect(c.sourcedir, src.name);
let pkgfile = path::connect(dir, "packages.json");
if !os::path_exists(pkgfile) { ret; }
2011-12-16 19:33:39 -06:00
let pkgstr = io::read_whole_file_str(pkgfile);
alt json::from_str(result::get(pkgstr)) {
ok(json::list(js)) {
2012-06-11 10:19:49 -05:00
for js.each {|j|
alt j {
json::dict(p) {
load_one_source_package(src, p);
2011-12-16 19:33:39 -06:00
}
_ {
warn("malformed source json: " + src.name +
2011-12-16 22:15:13 -06:00
" (non-dict pkg)");
2011-12-16 19:33:39 -06:00
}
}
}
}
ok(_) {
warn("malformed packages.json: " + src.name +
"(packages is not a list)");
}
err(e) {
warn(#fmt("%s:%s", src.name, e.to_str()));
2011-12-16 19:33:39 -06:00
}
};
}
fn build_cargo_options(argv: [str]) -> options {
let match = alt getopts::getopts(argv, opts()) {
result::ok(m) { m }
result::err(f) {
fail #fmt["%s", getopts::fail_str(f)];
}
};
let test = opt_present(match, "test");
let G = opt_present(match, "G");
let g = opt_present(match, "g");
let help = opt_present(match, "h") || opt_present(match, "help");
let len = vec::len(match.free);
let is_install = len > 1u && match.free[1] == "install";
let is_uninstall = len > 1u && match.free[1] == "uninstall";
if G && g { fail "-G and -g both provided"; }
if !is_install && !is_uninstall && (g || G) {
fail "-g and -G are only valid for `install` and `uninstall|rm`";
}
let mode =
if (!is_install && !is_uninstall) || g { user_mode }
else if G { system_mode }
else { local_mode };
{test: test, mode: mode, free: match.free, help: help}
}
fn configure(opts: options) -> cargo {
let home = alt get_cargo_root() {
ok(_home) { _home }
err(_err) { result::get(get_cargo_sysroot()) }
};
let get_cargo_dir = alt opts.mode {
system_mode { get_cargo_sysroot }
user_mode { get_cargo_root }
local_mode { get_cargo_root_nearest }
};
let p = result::get(get_cargo_dir());
let sources = map::str_hash::<source>();
try_parse_sources(path::connect(home, "sources.json"), sources);
try_parse_sources(path::connect(home, "local-sources.json"), sources);
let dep_cache = map::str_hash::<bool>();
let mut c = {
pgp: pgp::supported(),
root: home,
installdir: p,
bindir: path::connect(p, "bin"),
libdir: path::connect(p, "lib"),
workdir: path::connect(p, "work"),
sourcedir: path::connect(home, "sources"),
2012-01-18 21:16:14 -06:00
sources: sources,
mut current_install: "",
dep_cache: dep_cache,
opts: opts
2011-12-08 22:50:25 -06:00
};
need_dir(c.root);
2011-12-16 19:33:39 -06:00
need_dir(c.sourcedir);
2011-12-08 22:50:25 -06:00
need_dir(c.workdir);
need_dir(c.installdir);
2011-12-08 22:50:25 -06:00
need_dir(c.libdir);
need_dir(c.bindir);
for sources.each_key { |k|
let mut s = sources.get(k);
load_source_packages(c, s);
sources.insert(k, s);
}
2011-12-16 19:33:39 -06:00
if c.pgp {
pgp::init(c.root);
} else {
warn("command `gpg` was not found");
warn("you have to install gpg from source " +
" or package manager to get it to work correctly");
}
2011-12-08 22:50:25 -06:00
c
}
fn for_each_package(c: cargo, b: fn(source, package)) {
for c.sources.each_value {|v|
// FIXME (#2280): this temporary shouldn't be
// necessary, but seems to be, for borrowing.
let pks = copy v.packages;
for vec::each(pks) {|p|
b(v, p);
2011-12-16 19:33:39 -06:00
}
}
2011-12-16 19:33:39 -06:00
}
2012-03-15 16:03:30 -05:00
// Runs all programs in directory <buildpath>
fn run_programs(buildpath: str) {
let newv = os::list_dir_path(buildpath);
for newv.each {|ct|
run::run_program(ct, []);
2012-01-18 21:24:07 -06:00
}
}
2012-03-15 16:03:30 -05:00
// Runs rustc in <path + subdir> with the given flags
// and returns <path + subdir>
fn run_in_buildpath(what: str, path: str, subdir: str, cf: str,
extra_flags: [str]) -> option<str> {
let buildpath = path::connect(path, subdir);
2012-01-18 21:16:14 -06:00
need_dir(buildpath);
2012-03-15 16:03:30 -05:00
#debug("%s: %s -> %s", what, cf, buildpath);
let p = run::program_output(rustc_sysroot(),
2012-03-15 16:03:30 -05:00
["--out-dir", buildpath, cf] + extra_flags);
2012-01-18 21:16:14 -06:00
if p.status != 0 {
error(#fmt["rustc failed: %d\n%s\n%s", p.status, p.err, p.out]);
2012-03-15 16:03:30 -05:00
ret none;
2012-01-18 21:16:14 -06:00
}
2012-03-15 16:03:30 -05:00
some(buildpath)
}
fn test_one_crate(_c: cargo, path: str, cf: str) {
let buildpath = alt run_in_buildpath("testing", path, "/test", cf,
2012-03-15 16:03:30 -05:00
[ "--test"]) {
none { ret; }
some(bp) { bp }
};
run_programs(buildpath);
}
fn install_one_crate(c: cargo, path: str, cf: str) {
let buildpath = alt run_in_buildpath("installing", path,
2012-03-15 16:03:30 -05:00
"/build", cf, []) {
none { ret; }
some(bp) { bp }
};
let newv = os::list_dir_path(buildpath);
let exec_suffix = os::exe_suffix();
for newv.each {|ct|
2011-12-16 03:08:24 -06:00
if (exec_suffix != "" && str::ends_with(ct, exec_suffix)) ||
(exec_suffix == "" && !str::starts_with(path::basename(ct),
"lib")) {
#debug(" bin: %s", ct);
install_to_dir(ct, c.bindir);
if c.opts.mode == system_mode {
// TODO: Put this file in PATH / symlink it so it can be
// used as a generic executable
// `cargo install -G rustray` and `rustray file.obj`
}
} else {
#debug(" lib: %s", ct);
install_to_dir(ct, c.libdir);
}
}
}
fn rustc_sysroot() -> str {
alt os::self_exe_path() {
2012-06-11 10:19:49 -05:00
some(path) {
let path = [path, "..", "bin", "rustc"];
check vec::is_not_empty(path);
let rustc = path::normalize(path::connect_many(path));
#debug(" rustc: %s", rustc);
rustc
}
none { "rustc" }
}
}
fn install_source(c: cargo, path: str) {
#debug("source: %s", path);
os::change_dir(path);
let mut cratefiles = [];
for os::walk_dir(".") {|p|
if str::ends_with(p, ".rc") {
cratefiles += [p];
}
}
if vec::is_empty(cratefiles) {
fail "this doesn't look like a rust package (no .rc files)";
}
for cratefiles.each {|cf|
alt load_crate(cf) {
none { cont; }
some(crate) {
for crate.deps.each { |query|
// TODO: handle cyclic dependencies
let wd_base = c.workdir + path::path_sep();
let wd = alt tempfile::mkdtemp(wd_base, "") {
some(_wd) { _wd }
none { fail #fmt("needed temp dir: %s", wd_base); }
};
install_query(c, wd, query);
}
os::change_dir(path);
if c.opts.test {
2012-03-15 16:03:30 -05:00
test_one_crate(c, path, cf);
2012-01-18 21:16:14 -06:00
}
2012-03-15 16:03:30 -05:00
install_one_crate(c, path, cf);
}
}
}
}
fn install_git(c: cargo, wd: str, url: str, ref: option<str>) {
run::program_output("git", ["clone", url, wd]);
if option::is_some::<str>(ref) {
let r = option::get::<str>(ref);
os::change_dir(wd);
run::run_program("git", ["checkout", r]);
}
2011-12-08 23:39:41 -06:00
install_source(c, wd);
2011-12-08 23:34:06 -06:00
}
fn install_curl(c: cargo, wd: str, url: str) {
let tarpath = path::connect(wd, "pkg.tar");
let p = run::program_output("curl", ["-f", "-s", "-o",
tarpath, url]);
if p.status != 0 {
fail #fmt["fetch of %s failed: %s", url, p.err];
}
2011-12-08 23:39:41 -06:00
run::run_program("tar", ["-x", "--strip-components=1",
"-C", wd, "-f", tarpath]);
2011-12-16 21:27:04 -06:00
install_source(c, wd);
}
fn install_file(c: cargo, wd: str, path: str) {
run::program_output("tar", ["-x", "--strip-components=1",
"-C", wd, "-f", path]);
2011-12-08 23:39:41 -06:00
install_source(c, wd);
2011-11-30 17:57:46 -06:00
}
fn install_package(c: cargo, src: str, wd: str, pkg: package) {
let url = copy pkg.url;
let method = alt pkg.method {
"git" { "git" }
"file" { "file" }
_ { "curl" }
};
info(#fmt["installing %s/%s via %s...", src, pkg.name, method]);
alt method {
"git" { install_git(c, wd, url, copy pkg.ref); }
"file" { install_file(c, wd, url); }
"curl" { install_curl(c, wd, copy url); }
_ {}
}
}
fn cargo_suggestion(c: cargo, fallback: fn())
{
if c.sources.size() == 0u {
error("no sources defined - you may wish to run " +
"`cargo init`");
ret;
}
fallback();
}
fn install_uuid(c: cargo, wd: str, uuid: str) {
let mut ps = [];
for_each_package(c, { |s, p|
2011-12-16 19:33:39 -06:00
if p.uuid == uuid {
vec::grow(ps, 1u, (s.name, copy p));
2011-12-16 19:33:39 -06:00
}
});
if vec::len(ps) == 1u {
let (sname, p) = copy ps[0];
install_package(c, sname, wd, p);
ret;
} else if vec::len(ps) == 0u {
cargo_suggestion(c, { ||
error("can't find package: " + uuid);
});
ret;
}
error("found multiple packages:");
for ps.each {|elt|
let (sname,p) = copy elt;
info(" " + sname + "/" + p.uuid + " (" + p.name + ")");
2011-12-16 19:33:39 -06:00
}
}
fn install_named(c: cargo, wd: str, name: str) {
let mut ps = [];
for_each_package(c, { |s, p|
2011-12-16 19:33:39 -06:00
if p.name == name {
vec::grow(ps, 1u, (s.name, copy p));
2011-12-16 19:33:39 -06:00
}
});
if vec::len(ps) == 1u {
let (sname, p) = copy ps[0];
install_package(c, sname, wd, p);
ret;
} else if vec::len(ps) == 0u {
cargo_suggestion(c, { ||
error("can't find package: " + name);
});
ret;
}
error("found multiple packages:");
for ps.each {|elt|
let (sname,p) = copy elt;
info(" " + sname + "/" + p.uuid + " (" + p.name + ")");
}
}
fn install_uuid_specific(c: cargo, wd: str, src: str, uuid: str) {
alt c.sources.find(src) {
some(s) {
let packages = copy s.packages;
if vec::any(packages, { |p|
if p.uuid == uuid {
install_package(c, src, wd, p);
true
} else { false }
}) { ret; }
}
_ { }
2011-12-16 19:33:39 -06:00
}
error("can't find package: " + src + "/" + uuid);
}
fn install_named_specific(c: cargo, wd: str, src: str, name: str) {
alt c.sources.find(src) {
some(s) {
let packages = copy s.packages;
if vec::any(packages, { |p|
if p.name == name {
install_package(c, src, wd, p);
true
} else { false }
}) { ret; }
}
_ { }
}
error("can't find package: " + src + "/" + name);
}
fn cmd_uninstall(c: cargo) {
if vec::len(c.opts.free) < 3u {
2011-11-30 17:57:46 -06:00
cmd_usage();
ret;
}
let lib = c.libdir;
let bin = c.bindir;
let target = c.opts.free[2u];
// FIXME: needs stronger pattern matching
// FIXME: needs to uninstall from a specified location in a cache instead
// of looking for it (binaries can be uninstalled by name only)
if is_uuid(target) {
for os::list_dir(lib).each { |file|
alt str::find_str(file, "-" + target + "-") {
some(idx) {
let full = path::normalize(path::connect(lib, file));
if os::remove_file(full) {
info("uninstalled: '" + full + "'");
} else {
error("could not uninstall: '" + full + "'");
}
ret;
}
none { cont; }
}
}
error("can't find package with uuid: " + target);
} else {
for os::list_dir(lib).each { |file|
alt str::find_str(file, "lib" + target + "-") {
some(idx) {
let full = path::normalize(path::connect(lib,
file));
if os::remove_file(full) {
info("uninstalled: '" + full + "'");
} else {
error("could not uninstall: '" + full + "'");
}
ret;
}
none { cont; }
}
}
for os::list_dir(bin).each { |file|
alt str::find_str(file, target) {
some(idx) {
let full = path::normalize(path::connect(bin, file));
if os::remove_file(full) {
info("uninstalled: '" + full + "'");
} else {
error("could not uninstall: '" + full + "'");
}
ret;
}
none { cont; }
}
}
error("can't find package with name: " + target);
}
}
fn install_query(c: cargo, wd: str, target: str) {
alt c.dep_cache.find(target) {
some(_inst) {
if _inst {
ret;
}
}
none {}
}
c.dep_cache.insert(target, true);
if is_archive_path(target) {
install_file(c, wd, target);
ret;
} else if is_git_url(target) {
2012-05-28 20:58:01 -05:00
let ref = if c.opts.free.len() >= 4u {
some(c.opts.free[3u])
} else {
none
};
install_git(c, wd, target, ref);
} else if !valid_pkg_name(target) && has_archive_extension(target) {
install_curl(c, wd, target);
ret;
} else {
let mut ps = copy target;
alt str::find_char(ps, '/') {
2012-02-11 05:20:45 -06:00
option::some(idx) {
let source = str::slice(ps, 0u, idx);
ps = str::slice(ps, idx + 1u, str::len(ps));
if is_uuid(ps) {
install_uuid_specific(c, wd, source, ps);
} else {
install_named_specific(c, wd, source, ps);
}
2012-02-11 05:20:45 -06:00
}
option::none {
if is_uuid(ps) {
install_uuid(c, wd, ps);
} else {
install_named(c, wd, ps);
}
2012-02-11 05:20:45 -06:00
}
}
}
// FIXME: This whole dep_cache and current_install
// thing is a bit of a hack. It should be cleaned up in the future.
if target == c.current_install {
for c.dep_cache.each { |k, _v|
c.dep_cache.remove(k);
}
c.current_install = "";
}
}
fn cmd_install(c: cargo) unsafe {
let wd_base = c.workdir + path::path_sep();
let wd = alt tempfile::mkdtemp(wd_base, "") {
some(_wd) { _wd }
none { fail #fmt("needed temp dir: %s", wd_base); }
};
if vec::len(c.opts.free) == 2u {
let cwd = os::getcwd();
let status = run::run_program("cp", ["-R", cwd, wd]);
if status != 0 {
fail #fmt("could not copy directory: %s", cwd);
}
install_source(c, wd);
ret;
}
sync(c);
let query = c.opts.free[2];
c.current_install = copy query;
install_query(c, wd, copy query);
}
fn sync(c: cargo) {
for c.sources.each_key { |k|
let mut s = c.sources.get(k);
sync_one(c, s);
c.sources.insert(k, s);
}
2011-11-30 17:57:46 -06:00
}
fn sync_one_file(c: cargo, dir: str, src: source) -> bool {
let name = src.name;
let srcfile = path::connect(dir, "source.json.new");
let destsrcfile = path::connect(dir, "source.json");
let pkgfile = path::connect(dir, "packages.json.new");
let destpkgfile = path::connect(dir, "packages.json");
let keyfile = path::connect(dir, "key.gpg");
let srcsigfile = path::connect(dir, "source.json.sig");
let sigfile = path::connect(dir, "packages.json.sig");
2011-12-16 19:33:39 -06:00
let url = src.url;
let mut has_src_file = false;
if !os::copy_file(path::connect(url, "packages.json"), pkgfile) {
error(#fmt["fetch for source %s (url %s) failed", name, url]);
ret false;
}
if os::copy_file(path::connect(url, "source.json"), srcfile) {
has_src_file = false;
2011-12-16 19:33:39 -06:00
}
os::copy_file(path::connect(url, "source.json.sig"), srcsigfile);
os::copy_file(path::connect(url, "packages.json.sig"), sigfile);
alt copy src.key {
some(u) {
let p = run::program_output("curl", ["-f", "-s", "-o", keyfile,
u]);
if p.status != 0 {
error(#fmt["fetch for source %s (key %s) failed", name, u]);
ret false;
}
pgp::add(c.root, keyfile);
}
_ { }
}
alt (src.key, src.keyfp) {
(some(_), some(f)) {
let r = pgp::verify(c.root, pkgfile, sigfile, f);
if !r {
error(#fmt["signature verification failed for source %s",
name]);
ret false;
}
if has_src_file {
let e = pgp::verify(c.root, srcfile, srcsigfile, f);
if !e {
error(#fmt["signature verification failed for source %s",
name]);
ret false;
}
}
}
_ {}
}
copy_warn(pkgfile, destpkgfile);
if has_src_file {
copy_warn(srcfile, destsrcfile);
}
os::remove_file(keyfile);
os::remove_file(srcfile);
os::remove_file(srcsigfile);
os::remove_file(pkgfile);
os::remove_file(sigfile);
info(#fmt["synced source: %s", name]);
ret true;
}
fn sync_one_git(c: cargo, dir: str, src: source) -> bool {
let name = src.name;
let srcfile = path::connect(dir, "source.json");
let pkgfile = path::connect(dir, "packages.json");
let keyfile = path::connect(dir, "key.gpg");
let srcsigfile = path::connect(dir, "source.json.sig");
let sigfile = path::connect(dir, "packages.json.sig");
let url = src.url;
fn rollback(name: str, dir: str, insecure: bool) {
fn msg(name: str, insecure: bool) {
error(#fmt["could not rollback source: %s", name]);
if insecure {
warn("a past security check failed on source " +
name + " and rolling back the source failed -"
+ " this source may be compromised");
}
}
if !os::change_dir(dir) {
msg(name, insecure);
}
else {
let p = run::program_output("git", ["reset", "--hard",
"HEAD@{1}"]);
if p.status != 0 {
msg(name, insecure);
}
}
}
if !os::path_exists(path::connect(dir, ".git")) {
let p = run::program_output("git", ["clone", url, dir]);
if p.status != 0 {
error(#fmt["fetch for source %s (url %s) failed", name, url]);
ret false;
}
}
else {
if !os::change_dir(dir) {
error(#fmt["fetch for source %s (url %s) failed", name, url]);
ret false;
}
let p = run::program_output("git", ["pull"]);
if p.status != 0 {
error(#fmt["fetch for source %s (url %s) failed", name, url]);
ret false;
}
}
let has_src_file = os::path_exists(srcfile);
alt copy src.key {
some(u) {
2011-12-20 20:52:50 -06:00
let p = run::program_output("curl", ["-f", "-s", "-o", keyfile,
u]);
if p.status != 0 {
error(#fmt["fetch for source %s (key %s) failed", name, u]);
rollback(name, dir, false);
ret false;
}
pgp::add(c.root, keyfile);
}
_ { }
}
alt (src.key, src.keyfp) {
(some(_), some(f)) {
let r = pgp::verify(c.root, pkgfile, sigfile, f);
if !r {
error(#fmt["signature verification failed for source %s",
2011-12-20 20:52:50 -06:00
name]);
rollback(name, dir, false);
ret false;
}
if has_src_file {
let e = pgp::verify(c.root, srcfile, srcsigfile, f);
if !e {
error(#fmt["signature verification failed for source %s",
name]);
rollback(name, dir, false);
ret false;
}
}
}
_ {}
}
os::remove_file(keyfile);
info(#fmt["synced source: %s", name]);
ret true;
}
fn sync_one_curl(c: cargo, dir: str, src: source) -> bool {
let name = src.name;
let srcfile = path::connect(dir, "source.json.new");
let destsrcfile = path::connect(dir, "source.json");
let pkgfile = path::connect(dir, "packages.json.new");
let destpkgfile = path::connect(dir, "packages.json");
let keyfile = path::connect(dir, "key.gpg");
let srcsigfile = path::connect(dir, "source.json.sig");
let sigfile = path::connect(dir, "packages.json.sig");
let mut url = src.url;
let smart = !str::ends_with(src.url, "packages.json");
let mut has_src_file = false;
if smart {
url += "/packages.json";
}
let p = run::program_output("curl", ["-f", "-s", "-o", pkgfile, url]);
if p.status != 0 {
error(#fmt["fetch for source %s (url %s) failed", name, url]);
ret false;
}
if smart {
url = src.url + "/source.json";
let p = run::program_output("curl", ["-f", "-s", "-o", srcfile, url]);
if p.status == 0 {
has_src_file = true;
}
}
alt copy src.key {
some(u) {
let p = run::program_output("curl", ["-f", "-s", "-o", keyfile,
u]);
if p.status != 0 {
error(#fmt["fetch for source %s (key %s) failed", name, u]);
ret false;
}
pgp::add(c.root, keyfile);
}
_ { }
}
alt (src.key, src.keyfp) {
(some(_), some(f)) {
if smart {
url = src.url + "/packages.json.sig";
}
else {
url = src.url + ".sig";
}
let mut p = run::program_output("curl", ["-f", "-s", "-o",
sigfile, url]);
if p.status != 0 {
error(#fmt["fetch for source %s (sig %s) failed", name, url]);
ret false;
}
let r = pgp::verify(c.root, pkgfile, sigfile, f);
if !r {
error(#fmt["signature verification failed for source %s",
name]);
ret false;
}
if smart && has_src_file {
url = src.url + "/source.json.sig";
p = run::program_output("curl", ["-f", "-s", "-o", srcsigfile,
url]);
if p.status != 0 {
error(#fmt["fetch for source %s (sig %s) failed",
name, url]);
ret false;
}
let e = pgp::verify(c.root, srcfile, srcsigfile, f);
if !e {
error("signature verification failed for " +
"source " + name);
ret false;
}
}
}
_ {}
}
copy_warn(pkgfile, destpkgfile);
if smart && has_src_file {
copy_warn(srcfile, destsrcfile);
}
os::remove_file(keyfile);
os::remove_file(srcfile);
os::remove_file(srcsigfile);
os::remove_file(pkgfile);
os::remove_file(sigfile);
info(#fmt["synced source: %s", name]);
ret true;
}
fn sync_one(c: cargo, src: source) {
let name = src.name;
let dir = path::connect(c.sourcedir, name);
info(#fmt["syncing source: %s...", name]);
need_dir(dir);
let result = alt src.method {
"git" { sync_one_git(c, dir, src) }
"file" { sync_one_file(c, dir, src) }
_ { sync_one_curl(c, dir, src) }
};
if result {
load_source_info(c, src);
load_source_packages(c, src);
2011-12-16 19:33:39 -06:00
}
}
fn cmd_init(c: cargo) {
let srcurl = "http://www.rust-lang.org/cargo/sources.json";
let sigurl = "http://www.rust-lang.org/cargo/sources.json.sig";
let srcfile = path::connect(c.root, "sources.json.new");
let sigfile = path::connect(c.root, "sources.json.sig");
let destsrcfile = path::connect(c.root, "sources.json");
let p = run::program_output("curl", ["-f", "-s", "-o", srcfile, srcurl]);
if p.status != 0 {
error(#fmt["fetch of sources.json failed: %s", p.out]);
ret;
}
let p = run::program_output("curl", ["-f", "-s", "-o", sigfile, sigurl]);
if p.status != 0 {
error(#fmt["fetch of sources.json.sig failed: %s", p.out]);
ret;
}
let r = pgp::verify(c.root, srcfile, sigfile, pgp::signing_key_fp());
if !r {
error(#fmt["signature verification failed for '%s'", srcfile]);
ret;
}
2012-04-03 00:53:40 -05:00
copy_warn(srcfile, destsrcfile);
os::remove_file(srcfile);
os::remove_file(sigfile);
info(#fmt["initialized .cargo in %s", c.root]);
}
fn print_pkg(s: source, p: package) {
let mut m = s.name + "/" + p.name + " (" + p.uuid + ")";
if vec::len(p.tags) > 0u {
m = m + " [" + str::connect(p.tags, ", ") + "]";
}
info(m);
if p.description != "" {
print(" >> " + p.description + "\n")
}
}
fn print_source(s: source) {
info(s.name + " (" + s.url + ")");
let pks = sort::merge_sort({ |a, b|
a < b
}, copy s.packages);
let l = vec::len(pks);
print(io::with_str_writer() { |writer|
let mut list = " >> ";
vec::iteri(pks) { |i, pk|
if str::len(list) > 78u {
writer.write_line(list);
list = " >> ";
}
list += pk.name + (if l - 1u == i { "" } else { ", " });
}
writer.write_line(list);
});
}
fn cmd_list(c: cargo) {
sync(c);
if vec::len(c.opts.free) >= 3u {
vec::iter_between(c.opts.free, 2u, vec::len(c.opts.free)) { |name|
if !valid_pkg_name(name) {
error(#fmt("'%s' is an invalid source name", name));
} else {
alt c.sources.find(name) {
some(source) {
print_source(source);
}
none {
error(#fmt("no such source: %s", name));
}
}
}
}
} else {
for c.sources.each_value { |v|
print_source(v);
}
}
}
fn cmd_search(c: cargo) {
if vec::len(c.opts.free) < 3u {
cmd_usage();
ret;
}
sync(c);
let mut n = 0;
let name = c.opts.free[2];
let tags = vec::slice(c.opts.free, 3u, vec::len(c.opts.free));
for_each_package(c, { |s, p|
if (str::contains(p.name, name) || name == "*") &&
vec::all(tags, { |t| vec::contains(p.tags, t) }) {
print_pkg(s, p);
n += 1;
}
});
info(#fmt["found %d packages", n]);
}
fn install_to_dir(srcfile: str, destdir: str) {
let newfile = path::connect(destdir, path::basename(srcfile));
let status = run::run_program("cp", [srcfile, newfile]);
if status == 0 {
info(#fmt["installed: '%s'", newfile]);
} else {
error(#fmt["could not install: '%s'", newfile]);
}
}
fn dump_cache(c: cargo) {
need_dir(c.root);
let out = path::connect(c.root, "cache.json");
let _root = json::dict(map::str_hash());
if os::path_exists(out) {
copy_warn(out, path::connect(c.root, "cache.json.old"));
}
}
fn dump_sources(c: cargo) {
if c.sources.size() < 1u {
ret;
}
need_dir(c.root);
let out = path::connect(c.root, "sources.json");
if os::path_exists(out) {
copy_warn(out, path::connect(c.root, "sources.json.old"));
}
alt io::buffered_file_writer(out) {
result::ok(writer) {
let hash = map::str_hash();
let root = json::dict(hash);
for c.sources.each { |k, v|
let chash = map::str_hash();
let child = json::dict(chash);
chash.insert("url", json::string(v.url));
chash.insert("method", json::string(v.method));
alt copy v.key {
some(key) {
chash.insert("key", json::string(key));
}
_ {}
}
alt copy v.keyfp {
some(keyfp) {
chash.insert("keyfp", json::string(keyfp));
}
_ {}
}
hash.insert(k, child);
}
writer.write_str(json::to_str(root));
}
result::err(e) {
error(#fmt("could not dump sources: %s", e));
}
}
}
fn copy_warn(srcfile: str, destfile: str) {
if !os::copy_file(srcfile, destfile) {
warn(#fmt["copying %s to %s failed", srcfile, destfile]);
}
}
fn cmd_sources(c: cargo) {
if vec::len(c.opts.free) < 3u {
for c.sources.each_value { |v|
info(#fmt("%s (%s) via %s",
copy v.name, copy v.url, copy v.method));
}
ret;
}
let action = c.opts.free[2u];
alt action {
"clear" {
for c.sources.each_key { |k|
c.sources.remove(k);
}
info("cleared sources");
}
"add" {
if vec::len(c.opts.free) < 5u {
cmd_usage();
ret;
}
let name = c.opts.free[3u];
let url = c.opts.free[4u];
if !valid_pkg_name(name) {
error(#fmt("'%s' is an invalid source name", name));
ret;
}
alt c.sources.find(name) {
some(source) {
error(#fmt("source already exists: %s", name));
}
none {
c.sources.insert(name, @{
name: name,
mut url: url,
mut method: assume_source_method(url),
mut key: none,
mut keyfp: none,
mut packages: [mut]
});
info(#fmt("added source: %s", name));
}
}
}
"remove" {
if vec::len(c.opts.free) < 4u {
cmd_usage();
ret;
}
let name = c.opts.free[3u];
if !valid_pkg_name(name) {
error(#fmt("'%s' is an invalid source name", name));
ret;
}
alt c.sources.find(name) {
some(source) {
c.sources.remove(name);
info(#fmt("removed source: %s", name));
}
none {
error(#fmt("no such source: %s", name));
}
}
}
"set-url" {
if vec::len(c.opts.free) < 5u {
cmd_usage();
ret;
}
let name = c.opts.free[3u];
let url = c.opts.free[4u];
if !valid_pkg_name(name) {
error(#fmt("'%s' is an invalid source name", name));
ret;
}
alt c.sources.find(name) {
some(source) {
let old = copy source.url;
let method = assume_source_method(url);
source.url = url;
source.method = method;
c.sources.insert(name, source);
info(#fmt("changed source url: '%s' to '%s'", old, url));
}
none {
error(#fmt("no such source: %s", name));
}
}
}
"set-method" {
if vec::len(c.opts.free) < 5u {
cmd_usage();
ret;
}
let name = c.opts.free[3u];
let method = c.opts.free[4u];
if !valid_pkg_name(name) {
error(#fmt("'%s' is an invalid source name", name));
ret;
}
alt c.sources.find(name) {
some(source) {
let old = copy source.method;
source.method = alt method {
"git" { "git" }
"file" { "file" }
_ { "curl" }
};
c.sources.insert(name, source);
info(#fmt("changed source method: '%s' to '%s'", old,
method));
}
none {
error(#fmt("no such source: %s", name));
}
}
}
"rename" {
if vec::len(c.opts.free) < 5u {
cmd_usage();
ret;
}
let name = c.opts.free[3u];
let newn = c.opts.free[4u];
if !valid_pkg_name(name) {
error(#fmt("'%s' is an invalid source name", name));
ret;
}
if !valid_pkg_name(newn) {
error(#fmt("'%s' is an invalid source name", newn));
ret;
}
alt c.sources.find(name) {
some(source) {
c.sources.remove(name);
c.sources.insert(newn, source);
info(#fmt("renamed source: %s to %s", name, newn));
}
none {
error(#fmt("no such source: %s", name));
}
}
}
_ { cmd_usage(); }
}
2012-04-03 00:53:40 -05:00
}
2011-11-30 17:57:46 -06:00
fn cmd_usage() {
print("Usage: cargo <cmd> [options] [args..]
e.g. cargo install <name>
Where <cmd> is one of:
init, install, list, search, sources,
uninstall, usage
Options:
-h, --help Display this message
<cmd> -h, <cmd> --help Display help for <cmd>
");
}
fn cmd_usage_init() {
print("cargo init
Re-initialize cargo in ~/.cargo. Clears all sources and then adds the
default sources from <www.rust-lang.org/sources.json>.");
}
fn cmd_usage_install() {
print("cargo install
cargo install [source/]<name>[@version]
cargo install [source/]<uuid>[@version]
cargo install <git url> [ref]
cargo install <tarball url>
cargo install <tarball file>
Options:
--test Run crate tests before installing
-g Install to the user level (~/.cargo/bin/ instead of
locally in ./.cargo/bin/ by default)
-G Install to the system level (/usr/local/lib/cargo/bin/)
Install a crate. If no arguments are supplied, it installs from
the current working directory. If a source is provided, only install
from that source, otherwise it installs from any source.");
}
fn cmd_usage_uninstall() {
print("cargo uninstall [source/]<name>[@version]
cargo uninstall [source/]<uuid>[@version]
cargo uninstall <meta-name>[@version]
cargo uninstall <meta-uuid>[@version]
Options:
-g Remove from the user level (~/.cargo/bin/ instead of
locally in ./.cargo/bin/ by default)
-G Remove from the system level (/usr/local/lib/cargo/bin/)
Remove a crate. If a source is provided, only remove
from that source, otherwise it removes from any source.
If a crate was installed directly (git, tarball, etc.), you can remove
it by metadata.");
}
fn cmd_usage_list() {
print("cargo list [sources..]
If no arguments are provided, list all sources and their packages.
If source names are provided, list those sources and their packages.
");
2011-11-30 17:57:46 -06:00
}
fn cmd_usage_search() {
print("cargo search <query | '*'> [tags..]
Search packages.");
}
fn cmd_usage_sources() {
print("cargo sources
cargo sources add <name> <url>
cargo sources remove <name>
cargo sources rename <name> <new>
cargo sources set-url <name> <url>
cargo sources set-method <name> <method>
If no arguments are supplied, list all sources (but not their packages).
Commands:
add Add a source. The source method will be guessed
from the URL.
remove Remove a source.
rename Rename a source.
set-url Change the URL for a source.
set-method Change the method for a source.");
}
2011-11-30 17:57:46 -06:00
fn main(argv: [str]) {
let o = build_cargo_options(argv);
if vec::len(o.free) < 2u {
cmd_usage();
ret;
}
if o.help {
alt o.free[1] {
"init" { cmd_usage_init(); }
"install" { cmd_usage_install(); }
"uninstall" { cmd_usage_uninstall(); }
"list" { cmd_usage_list(); }
"search" { cmd_usage_search(); }
"sources" { cmd_usage_sources(); }
_ { cmd_usage(); }
}
ret;
}
if o.free[1] == "usage" {
2011-11-30 17:57:46 -06:00
cmd_usage();
ret;
}
let mut c = configure(o);
let home = c.root;
let first_time = os::path_exists(path::connect(home, "sources.json"));
if !first_time && o.free[1] != "init" {
cmd_init(c);
// FIXME: shouldn't need to reconfigure
c = configure(o);
}
alt o.free[1] {
2011-12-20 20:52:50 -06:00
"init" { cmd_init(c); }
"install" { cmd_install(c); }
"uninstall" { cmd_uninstall(c); }
"list" { cmd_list(c); }
"search" { cmd_search(c); }
"sources" { cmd_sources(c); }
2011-11-30 17:57:46 -06:00
_ { cmd_usage(); }
}
dump_cache(c);
dump_sources(c);
}