2011-11-30 18:57:46 -05:00
|
|
|
// cargo.rs - Rust package manager
|
|
|
|
|
2012-05-13 17:12:56 -07:00
|
|
|
import syntax::{ast, codemap};
|
|
|
|
import syntax::parse;
|
2012-05-29 20:38:46 -07:00
|
|
|
import rustc::metadata::filesearch::{get_cargo_root, get_cargo_root_nearest,
|
|
|
|
get_cargo_sysroot, libdir};
|
2012-05-13 17:12:56 -07:00
|
|
|
import syntax::diagnostic;
|
2011-11-30 18:57:46 -05:00
|
|
|
|
2012-02-25 16:39:32 -08:00
|
|
|
import result::{ok, err};
|
2012-01-11 15:15:54 +01:00
|
|
|
import io::writer_util;
|
2011-12-16 20:33:39 -05:00
|
|
|
import result;
|
2012-06-02 16:25:56 +10:00
|
|
|
import std::{map, json, tempfile, term, sort, getopts};
|
|
|
|
import map::hashmap;
|
2011-12-13 16:25:51 -08:00
|
|
|
import str;
|
|
|
|
import vec;
|
2012-02-07 17:15:39 +09:00
|
|
|
import getopts::{optflag, optopt, opt_present};
|
2011-11-30 18:57:46 -05:00
|
|
|
|
2011-12-16 20:33:39 -05:00
|
|
|
type package = {
|
2012-06-06 18:55:44 +10:00
|
|
|
mut name: str,
|
|
|
|
mut uuid: str,
|
|
|
|
mut url: str,
|
|
|
|
mut method: str,
|
|
|
|
mut description: str,
|
|
|
|
mut ref: option<str>,
|
|
|
|
mut tags: [str]
|
2011-12-16 20:33:39 -05:00
|
|
|
};
|
|
|
|
|
|
|
|
type source = {
|
2012-06-06 18:55:44 +10:00
|
|
|
mut name: str,
|
|
|
|
mut url: str,
|
|
|
|
mut method: str,
|
|
|
|
mut key: option<str>,
|
|
|
|
mut keyfp: option<str>,
|
2012-03-26 18:35:18 -07:00
|
|
|
mut packages: [package]
|
2011-12-16 20:33:39 -05:00
|
|
|
};
|
|
|
|
|
2011-12-08 23:50:25 -05:00
|
|
|
type cargo = {
|
2011-12-20 19:59:37 -05:00
|
|
|
pgp: bool,
|
2011-12-08 23:50:25 -05:00
|
|
|
root: str,
|
2012-06-02 16:25:56 +10:00
|
|
|
installdir: str,
|
2011-12-08 23:50:25 -05:00
|
|
|
bindir: str,
|
|
|
|
libdir: str,
|
|
|
|
workdir: str,
|
2011-12-16 20:33:39 -05:00
|
|
|
sourcedir: str,
|
2012-01-18 22:16:14 -05:00
|
|
|
sources: map::hashmap<str, source>,
|
2012-02-05 18:30:03 +09:00
|
|
|
opts: options
|
2011-12-08 23:50:25 -05:00
|
|
|
};
|
|
|
|
|
2011-11-30 18:57:46 -05:00
|
|
|
type pkg = {
|
2012-06-06 18:55:44 +10:00
|
|
|
mut name: str,
|
|
|
|
mut vers: str,
|
|
|
|
mut uuid: str,
|
|
|
|
mut desc: option<str>,
|
|
|
|
mut sigs: option<str>,
|
|
|
|
mut crate_type: option<str>
|
2011-11-30 18:57:46 -05:00
|
|
|
};
|
|
|
|
|
2012-02-05 18:30:03 +09:00
|
|
|
type options = {
|
|
|
|
test: bool,
|
2012-02-07 17:15:39 +09:00
|
|
|
mode: mode,
|
2012-02-05 18:30:03 +09:00
|
|
|
free: [str],
|
2012-05-26 08:35:54 -07:00
|
|
|
help: bool,
|
2012-02-05 18:30:03 +09:00
|
|
|
};
|
|
|
|
|
2012-02-07 17:15:39 +09:00
|
|
|
enum mode { system_mode, user_mode, local_mode }
|
|
|
|
|
2012-02-05 18:30:03 +09:00
|
|
|
fn opts() -> [getopts::opt] {
|
2012-06-02 16:25:56 +10:00
|
|
|
[optflag("g"), optflag("G"), optflag("test"),
|
2012-05-26 08:35:54 -07:00
|
|
|
optflag("h"), optflag("help")]
|
2012-02-05 18:30:03 +09:00
|
|
|
}
|
|
|
|
|
2011-12-16 20:33:39 -05:00
|
|
|
fn info(msg: str) {
|
2012-06-02 16:25:56 +10:00
|
|
|
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 20:33:39 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
fn warn(msg: str) {
|
2012-06-02 16:25:56 +10:00
|
|
|
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 20:33:39 -05:00
|
|
|
}
|
|
|
|
|
2011-12-16 22:08:25 -05:00
|
|
|
fn error(msg: str) {
|
2012-06-02 16:25:56 +10:00
|
|
|
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-02 23:55:32 -07:00
|
|
|
for vec::eachi(parts) { |i, part|
|
2012-06-06 18:55:44 +10:00
|
|
|
fn is_hex_digit(ch: char) -> bool {
|
|
|
|
('0' <= ch && ch <= '9') ||
|
|
|
|
('a' <= ch && ch <= 'f') ||
|
|
|
|
('A' <= ch && ch <= 'F')
|
|
|
|
}
|
2012-06-02 23:55:32 -07:00
|
|
|
|
|
|
|
if !part.all(is_hex_digit) {
|
|
|
|
ret false;
|
|
|
|
}
|
|
|
|
|
2012-06-02 16:25:56 +10:00
|
|
|
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-02 23:55:32 -07:00
|
|
|
ret false;
|
2012-06-02 16:25:56 +10:00
|
|
|
}
|
|
|
|
|
2012-06-02 23:30:11 -07:00
|
|
|
#[test]
|
|
|
|
fn test_is_uuid() {
|
2012-06-02 23:55:32 -07:00
|
|
|
assert is_uuid("aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaafAF09");
|
2012-06-02 23:30:11 -07:00
|
|
|
assert !is_uuid("aaaaaaaa-aaaa-aaaa-aaaaa-aaaaaaaaaaaa");
|
|
|
|
assert !is_uuid("");
|
2012-06-02 23:55:32 -07: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-02 23:30:11 -07:00
|
|
|
}
|
|
|
|
|
2012-06-06 18:55:44 +10: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.xz") ||
|
|
|
|
str::ends_with(p, ".tar.bz2")
|
|
|
|
}
|
2012-06-02 16:25:56 +10:00
|
|
|
|
2012-06-06 18:55:44 +10:00
|
|
|
fn is_archive_path(u: str) -> bool {
|
|
|
|
has_archive_extension(u) && os::path_exists(u)
|
2012-06-02 16:25:56 +10:00
|
|
|
}
|
|
|
|
|
2012-06-06 18:55:44 +10:00
|
|
|
fn is_archive_url(u: str) -> bool {
|
|
|
|
// FIXME: this requires the protocol bit - if we had proper url parsing,
|
2012-06-02 16:25:56 +10:00
|
|
|
// we wouldn't need it
|
|
|
|
|
2012-06-06 18:55:44 +10:00
|
|
|
alt str::find_str(u, "://") {
|
|
|
|
option::some(i) { has_archive_extension(u) }
|
|
|
|
_ { false }
|
2012-06-02 16:25:56 +10:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
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")
|
|
|
|
}
|
2011-12-16 22:08:25 -05:00
|
|
|
}
|
|
|
|
|
2012-06-06 18:55:44 +10:00
|
|
|
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"
|
|
|
|
}
|
|
|
|
|
2012-01-31 17:05:20 -08:00
|
|
|
fn load_link(mis: [@ast::meta_item]) -> (option<str>,
|
|
|
|
option<str>,
|
|
|
|
option<str>) {
|
2012-03-22 08:39:41 -07:00
|
|
|
let mut name = none;
|
|
|
|
let mut vers = none;
|
|
|
|
let mut uuid = none;
|
2012-04-06 20:01:43 +02:00
|
|
|
for mis.each {|a|
|
2011-11-30 18:57:46 -05: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); }
|
|
|
|
_ { }
|
|
|
|
}
|
|
|
|
}
|
2012-01-30 21:00:57 -08:00
|
|
|
_ { fail "load_link: meta items must be name-values"; }
|
2011-11-30 18:57:46 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
(name, vers, uuid)
|
|
|
|
}
|
|
|
|
|
2012-01-31 17:05:20 -08:00
|
|
|
fn load_pkg(filename: str) -> option<pkg> {
|
2012-01-13 17:33:16 -08:00
|
|
|
let cm = codemap::new_codemap();
|
2012-01-24 21:42:54 -08:00
|
|
|
let handler = diagnostic::mk_handler(none);
|
2012-01-13 17:33:16 -08:00
|
|
|
let sess = @{
|
|
|
|
cm: cm,
|
2012-03-26 18:35:18 -07:00
|
|
|
mut next_id: 1,
|
2012-01-24 21:42:54 -08:00
|
|
|
span_diagnostic: diagnostic::mk_span_handler(handler, cm),
|
2012-03-26 18:35:18 -07:00
|
|
|
mut chpos: 0u,
|
|
|
|
mut byte_pos: 0u
|
2012-01-13 17:33:16 -08:00
|
|
|
};
|
2012-04-17 23:34:48 -07:00
|
|
|
let c = parse::parse_crate_from_crate_file(filename, [], sess);
|
2011-11-30 18:57:46 -05:00
|
|
|
|
2012-03-22 08:39:41 -07: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 18:57:46 -05:00
|
|
|
|
2012-04-06 20:01:43 +02:00
|
|
|
for c.node.attrs.each {|a|
|
2011-11-30 18:57:46 -05: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); }
|
2011-12-08 23:41:29 -05:00
|
|
|
"crate_type" { crate_type = some(v); }
|
2011-11-30 18:57:46 -05:00
|
|
|
_ { }
|
|
|
|
}
|
|
|
|
}
|
|
|
|
ast::meta_list(v, mis) {
|
|
|
|
if v == "link" {
|
|
|
|
let (n, v, u) = load_link(mis);
|
|
|
|
name = n;
|
|
|
|
vers = v;
|
|
|
|
uuid = u;
|
|
|
|
}
|
|
|
|
}
|
2012-01-30 21:00:57 -08:00
|
|
|
_ { fail "load_pkg: pkg attributes may not contain meta_words"; }
|
2011-11-30 18:57:46 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
alt (name, vers, uuid) {
|
|
|
|
(some(name0), some(vers0), some(uuid0)) {
|
|
|
|
some({
|
2012-06-06 18:55:44 +10:00
|
|
|
mut name: name0,
|
|
|
|
mut vers: vers0,
|
|
|
|
mut uuid: uuid0,
|
|
|
|
mut desc: desc,
|
|
|
|
mut sigs: sigs,
|
|
|
|
mut crate_type: crate_type})
|
2011-11-30 18:57:46 -05:00
|
|
|
}
|
|
|
|
_ { ret none; }
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn print(s: str) {
|
|
|
|
io::stdout().write_line(s);
|
|
|
|
}
|
|
|
|
|
|
|
|
fn rest(s: str, start: uint) -> str {
|
2012-02-23 01:44:04 -08:00
|
|
|
if (start >= str::len(s)) {
|
2011-11-30 18:57:46 -05:00
|
|
|
""
|
|
|
|
} else {
|
2012-02-23 01:44:04 -08:00
|
|
|
str::slice(s, start, str::len(s))
|
2011-11-30 18:57:46 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-12-08 23:41:29 -05:00
|
|
|
fn need_dir(s: str) {
|
2012-03-12 20:04:27 -07:00
|
|
|
if os::path_is_dir(s) { ret; }
|
2012-05-26 08:35:54 -07:00
|
|
|
if !os::make_dir(s, 493_i32 /* oct: 755 */) {
|
2011-12-08 23:41:29 -05:00
|
|
|
fail #fmt["can't make_dir %s", s];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-06-06 18:55:44 +10:00
|
|
|
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 20:33:39 -05:00
|
|
|
fn parse_source(name: str, j: json::json) -> source {
|
2012-06-06 18:55:44 +10:00
|
|
|
if !valid_pkg_name(name) {
|
|
|
|
fail #fmt("'%s' is an invalid source name", name);
|
|
|
|
}
|
|
|
|
|
2011-12-16 20:33:39 -05:00
|
|
|
alt j {
|
|
|
|
json::dict(_j) {
|
2012-06-06 18:55:44 +10:00
|
|
|
let mut url = alt _j.find("url") {
|
2011-12-16 20:33:39 -05:00
|
|
|
some(json::string(u)) {
|
2011-12-20 19:59:37 -05:00
|
|
|
u
|
2011-12-16 20:33:39 -05:00
|
|
|
}
|
2012-06-02 16:25:56 +10:00
|
|
|
_ { fail "needed 'url' field in source"; }
|
2011-12-16 20:33:39 -05:00
|
|
|
};
|
2012-06-06 18:55:44 +10:00
|
|
|
let method = alt _j.find("method") {
|
2011-12-20 19:59:37 -05:00
|
|
|
some(json::string(u)) {
|
2012-06-06 18:55:44 +10:00
|
|
|
u
|
2011-12-20 19:59:37 -05:00
|
|
|
}
|
2012-06-06 18:55:44 +10:00
|
|
|
_ { assume_source_method(url) }
|
2011-12-20 19:59:37 -05:00
|
|
|
};
|
|
|
|
let key = alt _j.find("key") {
|
|
|
|
some(json::string(u)) {
|
|
|
|
some(u)
|
|
|
|
}
|
|
|
|
_ { none }
|
|
|
|
};
|
|
|
|
let keyfp = alt _j.find("keyfp") {
|
|
|
|
some(json::string(u)) {
|
|
|
|
some(u)
|
|
|
|
}
|
|
|
|
_ { none }
|
|
|
|
};
|
2012-06-06 18:55:44 +10:00
|
|
|
if method == "file" {
|
|
|
|
url = os::make_absolute(url);
|
|
|
|
}
|
|
|
|
ret { mut name: name,
|
|
|
|
mut url: url,
|
|
|
|
mut method: method,
|
|
|
|
mut key: key,
|
|
|
|
mut keyfp: keyfp,
|
2012-03-26 18:35:18 -07:00
|
|
|
mut packages: [] };
|
2011-12-16 20:33:39 -05:00
|
|
|
}
|
2012-06-02 16:25:56 +10:00
|
|
|
_ { fail "needed dict value in source"; }
|
2011-12-16 20:33:39 -05:00
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
fn try_parse_sources(filename: str, sources: map::hashmap<str, source>) {
|
2012-03-12 20:04:27 -07:00
|
|
|
if !os::path_exists(filename) { ret; }
|
2011-12-16 20:33:39 -05:00
|
|
|
let c = io::read_whole_file_str(filename);
|
2012-02-25 16:39:32 -08:00
|
|
|
alt json::from_str(result::get(c)) {
|
|
|
|
ok(json::dict(j)) {
|
2012-04-23 13:42:15 +02:00
|
|
|
for j.each { |k, v|
|
2011-12-16 20:33:39 -05:00
|
|
|
sources.insert(k, parse_source(k, v));
|
2011-12-22 14:42:52 -08:00
|
|
|
#debug("source: %s", k);
|
2011-12-16 20:33:39 -05:00
|
|
|
}
|
|
|
|
}
|
2012-02-25 16:39:32 -08:00
|
|
|
ok(_) { fail "malformed sources.json"; }
|
|
|
|
err(e) { fail #fmt("%s:%u:%u: %s", filename, e.line, e.col, e.msg); }
|
2011-12-16 20:33:39 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-12-16 22:27:04 -05:00
|
|
|
fn load_one_source_package(&src: source, p: map::hashmap<str, json::json>) {
|
2011-12-16 20:33:39 -05:00
|
|
|
let name = alt p.find("name") {
|
2012-06-06 18:55:44 +10: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;
|
|
|
|
}
|
|
|
|
_n
|
|
|
|
}
|
2011-12-16 20:33:39 -05:00
|
|
|
_ {
|
2012-06-02 16:25:56 +10:00
|
|
|
warn("malformed source json: " + src.name + " (missing name)");
|
2011-12-16 20:33:39 -05:00
|
|
|
ret;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
let uuid = alt p.find("uuid") {
|
2012-06-06 18:55:44 +10:00
|
|
|
some(json::string(_n)) {
|
|
|
|
if !is_uuid(_n) {
|
|
|
|
warn("malformed source json: " + src.name + ", '" + _n + "'"+
|
|
|
|
" is an invalid uuid");
|
|
|
|
ret;
|
|
|
|
}
|
|
|
|
_n
|
|
|
|
}
|
2011-12-16 20:33:39 -05:00
|
|
|
_ {
|
2012-06-02 16:25:56 +10:00
|
|
|
warn("malformed source json: " + src.name + " (missing uuid)");
|
2011-12-16 20:33:39 -05:00
|
|
|
ret;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
let url = alt p.find("url") {
|
|
|
|
some(json::string(_n)) { _n }
|
|
|
|
_ {
|
2012-06-02 16:25:56 +10:00
|
|
|
warn("malformed source json: " + src.name + " (missing url)");
|
2011-12-16 20:33:39 -05:00
|
|
|
ret;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2011-12-16 22:08:25 -05:00
|
|
|
let method = alt p.find("method") {
|
|
|
|
some(json::string(_n)) { _n }
|
|
|
|
_ {
|
2012-06-02 16:25:56 +10:00
|
|
|
warn("malformed source json: " + src.name + " (missing method)");
|
2011-12-16 22:08:25 -05:00
|
|
|
ret;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2011-12-20 23:41:22 -05:00
|
|
|
let ref = alt p.find("ref") {
|
|
|
|
some(json::string(_n)) { some(_n) }
|
|
|
|
_ { none }
|
|
|
|
};
|
|
|
|
|
2012-03-22 08:39:41 -07:00
|
|
|
let mut tags = [];
|
2011-12-20 20:41:23 -05:00
|
|
|
alt p.find("tags") {
|
|
|
|
some(json::list(js)) {
|
2012-04-06 20:01:43 +02:00
|
|
|
for js.each {|j|
|
2011-12-20 20:41:23 -05:00
|
|
|
alt j {
|
|
|
|
json::string(_j) { vec::grow(tags, 1u, _j); }
|
|
|
|
_ { }
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
_ { }
|
|
|
|
}
|
2012-01-23 21:42:29 -06:00
|
|
|
|
|
|
|
let description = alt p.find("description") {
|
|
|
|
some(json::string(_n)) { _n }
|
|
|
|
_ {
|
2012-06-02 16:25:56 +10:00
|
|
|
warn("malformed source json: " + src.name
|
2012-01-23 21:14:48 -08:00
|
|
|
+ " (missing description)");
|
2012-01-23 21:42:29 -06:00
|
|
|
ret;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2012-06-06 18:55:44 +10:00
|
|
|
let newpkg = {
|
|
|
|
mut name: name,
|
|
|
|
mut uuid: uuid,
|
|
|
|
mut url: url,
|
|
|
|
mut method: method,
|
|
|
|
mut description: description,
|
|
|
|
mut ref: ref,
|
|
|
|
mut tags: tags
|
|
|
|
};
|
|
|
|
|
|
|
|
for src.packages.each { |pkg|
|
|
|
|
if pkg.uuid == uuid {
|
|
|
|
pkg.name = newpkg.name;
|
|
|
|
pkg.uuid = newpkg.uuid;
|
|
|
|
pkg.url = newpkg.url;
|
|
|
|
pkg.method = newpkg.method;
|
|
|
|
pkg.description = newpkg.description;
|
|
|
|
pkg.ref = newpkg.ref;
|
|
|
|
pkg.tags = newpkg.tags;
|
|
|
|
log(debug, " updated package: " + src.name + "/" + name);
|
|
|
|
ret;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
vec::grow(src.packages, 1u, newpkg);
|
2012-06-02 16:25:56 +10:00
|
|
|
log(debug, " loaded package: " + src.name + "/" + name);
|
2011-12-16 20:33:39 -05:00
|
|
|
}
|
|
|
|
|
2012-06-06 18:55:44 +10: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:%u:%u: %s", src.name, e.line, e.col, e.msg));
|
|
|
|
}
|
|
|
|
};
|
|
|
|
}
|
2011-12-16 22:08:25 -05:00
|
|
|
fn load_source_packages(&c: cargo, &src: source) {
|
2012-06-02 16:25:56 +10:00
|
|
|
log(debug, "loading source: " + src.name);
|
2012-03-12 20:04:27 -07:00
|
|
|
let dir = path::connect(c.sourcedir, src.name);
|
|
|
|
let pkgfile = path::connect(dir, "packages.json");
|
|
|
|
if !os::path_exists(pkgfile) { ret; }
|
2011-12-16 20:33:39 -05:00
|
|
|
let pkgstr = io::read_whole_file_str(pkgfile);
|
2012-02-25 16:39:32 -08:00
|
|
|
alt json::from_str(result::get(pkgstr)) {
|
|
|
|
ok(json::list(js)) {
|
2012-04-06 20:01:43 +02:00
|
|
|
for js.each {|_j|
|
2011-12-16 20:33:39 -05:00
|
|
|
alt _j {
|
|
|
|
json::dict(_p) {
|
2011-12-16 22:27:04 -05:00
|
|
|
load_one_source_package(src, _p);
|
2011-12-16 20:33:39 -05:00
|
|
|
}
|
|
|
|
_ {
|
2012-06-02 16:25:56 +10:00
|
|
|
warn("malformed source json: " + src.name +
|
2011-12-16 23:15:13 -05:00
|
|
|
" (non-dict pkg)");
|
2011-12-16 20:33:39 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2012-02-25 16:39:32 -08:00
|
|
|
ok(_) {
|
2012-06-06 18:55:44 +10:00
|
|
|
warn("malformed packages.json: " + src.name +
|
2012-02-25 16:39:32 -08:00
|
|
|
"(packages is not a list)");
|
|
|
|
}
|
|
|
|
err(e) {
|
|
|
|
warn(#fmt("%s:%u:%u: %s", src.name, e.line, e.col, e.msg));
|
2011-12-16 20:33:39 -05:00
|
|
|
}
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2012-02-05 18:30:03 +09: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");
|
2012-05-26 08:35:54 -07:00
|
|
|
let G = opt_present(match, "G");
|
|
|
|
let g = opt_present(match, "g");
|
|
|
|
let help = opt_present(match, "h") || opt_present(match, "help");
|
2012-06-02 16:25:56 +10:00
|
|
|
let len = vec::len(match.free);
|
2012-05-26 08:35:54 -07:00
|
|
|
|
2012-06-02 16:25:56 +10:00
|
|
|
let is_install = len > 1u && match.free[1] == "install";
|
|
|
|
let is_uninstall = len > 1u && match.free[1] == "uninstall";
|
2012-02-20 17:08:58 +09:00
|
|
|
|
|
|
|
if G && g { fail "-G and -g both provided"; }
|
|
|
|
|
2012-06-02 16:25:56 +10:00
|
|
|
if !is_install && !is_uninstall && (g || G) {
|
|
|
|
fail "-g and -G are only valid for `install` and `uninstall|rm`";
|
2012-05-26 08:35:54 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
let mode =
|
2012-06-02 16:25:56 +10:00
|
|
|
if (!is_install && !is_uninstall) || g { user_mode }
|
|
|
|
else if G { system_mode }
|
|
|
|
else { local_mode };
|
2012-02-09 13:42:59 -08:00
|
|
|
|
2012-05-26 08:35:54 -07:00
|
|
|
{test: test, mode: mode, free: match.free, help: help}
|
2012-02-05 18:30:03 +09:00
|
|
|
}
|
|
|
|
|
|
|
|
fn configure(opts: options) -> cargo {
|
2012-06-02 16:25:56 +10:00
|
|
|
let home = alt get_cargo_root() {
|
|
|
|
ok(_home) { _home }
|
|
|
|
err(_err) { result::get(get_cargo_sysroot()) }
|
|
|
|
};
|
2012-05-26 08:35:54 -07:00
|
|
|
|
2012-02-07 17:15:39 +09:00
|
|
|
let get_cargo_dir = alt opts.mode {
|
|
|
|
system_mode { get_cargo_sysroot }
|
|
|
|
user_mode { get_cargo_root }
|
|
|
|
local_mode { get_cargo_root_nearest }
|
2012-02-05 18:30:03 +09:00
|
|
|
};
|
|
|
|
|
2012-05-26 08:35:54 -07:00
|
|
|
let p = result::get(get_cargo_dir());
|
2011-12-08 23:41:29 -05:00
|
|
|
|
2012-03-14 12:07:23 -07:00
|
|
|
let sources = map::str_hash::<source>();
|
2012-06-02 16:25:56 +10:00
|
|
|
try_parse_sources(path::connect(home, "sources.json"), sources);
|
|
|
|
try_parse_sources(path::connect(home, "local-sources.json"), sources);
|
2012-05-26 08:35:54 -07:00
|
|
|
|
2012-03-22 08:39:41 -07:00
|
|
|
let mut c = {
|
2011-12-20 19:59:37 -05:00
|
|
|
pgp: pgp::supported(),
|
2012-06-02 16:25:56 +10:00
|
|
|
root: home,
|
|
|
|
installdir: p,
|
2012-03-12 20:04:27 -07:00
|
|
|
bindir: path::connect(p, "bin"),
|
|
|
|
libdir: path::connect(p, "lib"),
|
2012-06-02 16:25:56 +10:00
|
|
|
workdir: path::connect(home, "work"),
|
|
|
|
sourcedir: path::connect(home, "sources"),
|
2012-01-18 22:16:14 -05:00
|
|
|
sources: sources,
|
2012-02-05 18:30:03 +09:00
|
|
|
opts: opts
|
2011-12-08 23:50:25 -05:00
|
|
|
};
|
|
|
|
|
|
|
|
need_dir(c.root);
|
2011-12-16 20:33:39 -05:00
|
|
|
need_dir(c.sourcedir);
|
2011-12-08 23:50:25 -05:00
|
|
|
need_dir(c.workdir);
|
2012-06-02 16:25:56 +10:00
|
|
|
need_dir(c.installdir);
|
2011-12-08 23:50:25 -05:00
|
|
|
need_dir(c.libdir);
|
|
|
|
need_dir(c.bindir);
|
2011-12-08 23:41:29 -05:00
|
|
|
|
2012-04-23 13:42:15 +02:00
|
|
|
for sources.each_key { |k|
|
2012-03-22 08:39:41 -07:00
|
|
|
let mut s = sources.get(k);
|
2011-12-16 22:08:25 -05:00
|
|
|
load_source_packages(c, s);
|
|
|
|
sources.insert(k, s);
|
2012-04-23 13:42:15 +02:00
|
|
|
}
|
2011-12-16 20:33:39 -05:00
|
|
|
|
2011-12-20 19:59:37 -05:00
|
|
|
if c.pgp {
|
|
|
|
pgp::init(c.root);
|
2012-02-14 10:06:20 +09:00
|
|
|
} else {
|
2012-06-02 16:25:56 +10:00
|
|
|
warn("command `gpg` was not found");
|
|
|
|
warn("you have to install gpg from source " +
|
2012-02-14 10:06:20 +09:00
|
|
|
" or package manager to get it to work correctly");
|
2011-12-20 19:59:37 -05:00
|
|
|
}
|
|
|
|
|
2011-12-08 23:50:25 -05:00
|
|
|
c
|
2011-12-08 23:41:29 -05:00
|
|
|
}
|
|
|
|
|
2012-06-06 18:55:44 +10:00
|
|
|
fn for_each_package(&c: cargo, b: fn(source, package)) {
|
2012-04-23 13:42:15 +02:00
|
|
|
for c.sources.each_value {|v|
|
2012-04-24 15:18:24 -07:00
|
|
|
// FIXME (#2280): this temporary shouldn't be
|
|
|
|
// necessary, but seems to be, for borrowing.
|
|
|
|
let pks = copy v.packages;
|
|
|
|
for vec::each(pks) {|p|
|
2011-12-16 22:08:25 -05:00
|
|
|
b(v, p);
|
2011-12-16 20:33:39 -05:00
|
|
|
}
|
2012-04-23 13:42:15 +02:00
|
|
|
}
|
2011-12-16 20:33:39 -05:00
|
|
|
}
|
|
|
|
|
2012-03-15 14:03:30 -07:00
|
|
|
// Runs all programs in directory <buildpath>
|
|
|
|
fn run_programs(buildpath: str) {
|
2012-04-01 11:39:17 -04:00
|
|
|
let newv = os::list_dir_path(buildpath);
|
2012-04-06 20:01:43 +02:00
|
|
|
for newv.each {|ct|
|
2012-01-21 14:59:10 -05:00
|
|
|
run::run_program(ct, []);
|
2012-01-18 22:24:07 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-03-15 14:03:30 -07: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 22:16:14 -05:00
|
|
|
need_dir(buildpath);
|
2012-03-15 14:03:30 -07:00
|
|
|
#debug("%s: %s -> %s", what, cf, buildpath);
|
2012-02-14 09:33:59 +09:00
|
|
|
let p = run::program_output(rustc_sysroot(),
|
2012-03-15 14:03:30 -07:00
|
|
|
["--out-dir", buildpath, cf] + extra_flags);
|
2012-01-18 22:16:14 -05:00
|
|
|
if p.status != 0 {
|
|
|
|
error(#fmt["rustc failed: %d\n%s\n%s", p.status, p.err, p.out]);
|
2012-03-15 14:03:30 -07:00
|
|
|
ret none;
|
2012-01-18 22:16:14 -05:00
|
|
|
}
|
2012-03-15 14:03:30 -07:00
|
|
|
some(buildpath)
|
|
|
|
}
|
|
|
|
|
|
|
|
fn test_one_crate(_c: cargo, path: str, cf: str) {
|
2012-06-02 16:25:56 +10:00
|
|
|
let buildpath = alt run_in_buildpath("testing", path, "/test", cf,
|
2012-03-15 14:03:30 -07:00
|
|
|
[ "--test"]) {
|
|
|
|
none { ret; }
|
|
|
|
some(bp) { bp }
|
|
|
|
};
|
|
|
|
run_programs(buildpath);
|
|
|
|
}
|
|
|
|
|
2012-06-06 18:55:44 +10:00
|
|
|
fn install_one_crate(&c: cargo, path: str, cf: str) {
|
2012-06-02 16:25:56 +10:00
|
|
|
let buildpath = alt run_in_buildpath("installing", path,
|
2012-03-15 14:03:30 -07:00
|
|
|
"/build", cf, []) {
|
|
|
|
none { ret; }
|
|
|
|
some(bp) { bp }
|
|
|
|
};
|
2012-04-01 11:39:17 -04:00
|
|
|
let newv = os::list_dir_path(buildpath);
|
2012-03-12 20:04:27 -07:00
|
|
|
let exec_suffix = os::exe_suffix();
|
2012-04-06 20:01:43 +02:00
|
|
|
for newv.each {|ct|
|
2011-12-16 17:08:24 +08:00
|
|
|
if (exec_suffix != "" && str::ends_with(ct, exec_suffix)) ||
|
2012-03-12 20:04:27 -07:00
|
|
|
(exec_suffix == "" && !str::starts_with(path::basename(ct),
|
2012-01-21 14:59:10 -05:00
|
|
|
"lib")) {
|
2011-12-22 14:42:52 -08:00
|
|
|
#debug(" bin: %s", ct);
|
2012-05-26 08:35:54 -07:00
|
|
|
install_to_dir(ct, c.bindir);
|
2012-02-20 17:33:50 +09:00
|
|
|
if c.opts.mode == system_mode {
|
2012-06-02 16:25:56 +10:00
|
|
|
// 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`
|
2012-02-20 17:33:50 +09:00
|
|
|
}
|
2011-12-08 23:41:29 -05:00
|
|
|
} else {
|
2011-12-22 14:42:52 -08:00
|
|
|
#debug(" lib: %s", ct);
|
2012-05-28 17:54:47 -07:00
|
|
|
install_to_dir(ct, c.libdir);
|
2012-02-20 17:33:50 +09:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-12-08 23:41:29 -05:00
|
|
|
|
2012-02-14 09:33:59 +09:00
|
|
|
fn rustc_sysroot() -> str {
|
2012-03-12 20:04:27 -07:00
|
|
|
alt os::self_exe_path() {
|
2012-02-14 09:33:59 +09:00
|
|
|
some(_path) {
|
|
|
|
let path = [_path, "..", "bin", "rustc"];
|
|
|
|
check vec::is_not_empty(path);
|
2012-03-12 20:04:27 -07:00
|
|
|
let rustc = path::normalize(path::connect_many(path));
|
2012-02-14 09:33:59 +09:00
|
|
|
#debug(" rustc: %s", rustc);
|
|
|
|
rustc
|
|
|
|
}
|
|
|
|
none { "rustc" }
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-06-06 18:55:44 +10:00
|
|
|
fn install_source(&c: cargo, path: str) {
|
2011-12-22 14:42:52 -08:00
|
|
|
#debug("source: %s", path);
|
2012-03-12 20:04:27 -07:00
|
|
|
os::change_dir(path);
|
2011-12-01 22:37:56 -05:00
|
|
|
|
2012-04-21 15:46:05 -07:00
|
|
|
let mut cratefiles = [];
|
|
|
|
for os::walk_dir(".") {|p|
|
|
|
|
if str::ends_with(p, ".rc") {
|
|
|
|
cratefiles += [p];
|
|
|
|
}
|
|
|
|
}
|
2011-12-01 22:37:56 -05:00
|
|
|
|
2011-12-08 23:41:29 -05:00
|
|
|
if vec::is_empty(cratefiles) {
|
2012-06-02 16:25:56 +10:00
|
|
|
fail "this doesn't look like a rust package (no .rc files)";
|
2011-12-01 22:37:56 -05:00
|
|
|
}
|
|
|
|
|
2012-04-06 20:01:43 +02:00
|
|
|
for cratefiles.each {|cf|
|
2011-12-08 23:41:29 -05:00
|
|
|
let p = load_pkg(cf);
|
|
|
|
alt p {
|
2012-01-18 22:37:22 -08:00
|
|
|
none { cont; }
|
2012-03-15 14:03:30 -07:00
|
|
|
some(_) {
|
2012-02-05 18:30:03 +09:00
|
|
|
if c.opts.test {
|
2012-03-15 14:03:30 -07:00
|
|
|
test_one_crate(c, path, cf);
|
2012-01-18 22:16:14 -05:00
|
|
|
}
|
2012-03-15 14:03:30 -07:00
|
|
|
install_one_crate(c, path, cf);
|
2011-12-08 23:41:29 -05:00
|
|
|
}
|
|
|
|
}
|
2011-12-01 22:37:56 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-06-06 18:55:44 +10:00
|
|
|
fn install_git(&c: cargo, wd: str, url: str, ref: option<str>) {
|
|
|
|
run::program_output("git", ["clone", url, wd]);
|
2011-12-20 23:41:22 -05:00
|
|
|
if option::is_some::<str>(ref) {
|
|
|
|
let r = option::get::<str>(ref);
|
2012-03-12 20:04:27 -07:00
|
|
|
os::change_dir(wd);
|
2011-12-20 23:41:22 -05:00
|
|
|
run::run_program("git", ["checkout", r]);
|
|
|
|
}
|
|
|
|
|
2011-12-09 00:39:41 -05:00
|
|
|
install_source(c, wd);
|
2011-12-09 00:34:06 -05:00
|
|
|
}
|
|
|
|
|
2012-06-06 18:55:44 +10:00
|
|
|
fn install_curl(&c: cargo, wd: str, url: str) {
|
2012-03-12 20:04:27 -07:00
|
|
|
let tarpath = path::connect(wd, "pkg.tar");
|
2011-12-16 22:08:25 -05:00
|
|
|
let p = run::program_output("curl", ["-f", "-s", "-o",
|
|
|
|
tarpath, url]);
|
|
|
|
if p.status != 0 {
|
2012-06-02 16:25:56 +10:00
|
|
|
fail #fmt["fetch of %s failed: %s", url, p.err];
|
2011-12-16 22:08:25 -05:00
|
|
|
}
|
2011-12-09 00:39:41 -05:00
|
|
|
run::run_program("tar", ["-x", "--strip-components=1",
|
2011-12-16 22:08:25 -05:00
|
|
|
"-C", wd, "-f", tarpath]);
|
2011-12-16 22:27:04 -05:00
|
|
|
install_source(c, wd);
|
2011-12-16 22:08:25 -05:00
|
|
|
}
|
|
|
|
|
2012-06-06 18:55:44 +10:00
|
|
|
fn install_file(&c: cargo, wd: str, path: str) {
|
|
|
|
run::program_output("tar", ["-x", "--strip-components=1",
|
2011-12-16 22:08:25 -05:00
|
|
|
"-C", wd, "-f", path]);
|
2011-12-09 00:39:41 -05:00
|
|
|
install_source(c, wd);
|
2011-11-30 18:57:46 -05:00
|
|
|
}
|
|
|
|
|
2012-06-06 18:55:44 +10: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); }
|
|
|
|
_ {}
|
2011-12-16 22:08:25 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-06-06 18:55:44 +10:00
|
|
|
fn cargo_suggestion(&c: cargo, fallback: fn())
|
2012-01-25 00:08:58 +08:00
|
|
|
{
|
|
|
|
if c.sources.size() == 0u {
|
2012-06-02 16:25:56 +10:00
|
|
|
error("no sources defined - you may wish to run " +
|
2012-06-06 18:55:44 +10:00
|
|
|
"`cargo init`");
|
2012-01-25 00:08:58 +08:00
|
|
|
ret;
|
|
|
|
}
|
|
|
|
fallback();
|
|
|
|
}
|
|
|
|
|
2012-06-06 18:55:44 +10:00
|
|
|
fn install_uuid(&c: cargo, wd: str, uuid: str) {
|
2012-03-22 08:39:41 -07:00
|
|
|
let mut ps = [];
|
2011-12-16 22:08:25 -05:00
|
|
|
for_each_package(c, { |s, p|
|
2011-12-16 20:33:39 -05:00
|
|
|
if p.uuid == uuid {
|
2012-06-06 18:55:44 +10:00
|
|
|
vec::grow(ps, 1u, (s.name, copy p));
|
2011-12-16 20:33:39 -05:00
|
|
|
}
|
|
|
|
});
|
2011-12-16 22:08:25 -05:00
|
|
|
if vec::len(ps) == 1u {
|
2012-06-06 18:55:44 +10:00
|
|
|
let (sname, p) = copy ps[0];
|
|
|
|
install_package(c, sname, wd, p);
|
2011-12-16 22:08:25 -05:00
|
|
|
ret;
|
|
|
|
} else if vec::len(ps) == 0u {
|
2012-06-06 18:55:44 +10:00
|
|
|
cargo_suggestion(c, { ||
|
2012-06-02 18:11:31 +10:00
|
|
|
error("can't find package: " + uuid);
|
|
|
|
});
|
2011-12-16 22:08:25 -05:00
|
|
|
ret;
|
|
|
|
}
|
2012-06-02 16:25:56 +10:00
|
|
|
error("found multiple packages:");
|
2012-04-06 20:01:43 +02:00
|
|
|
for ps.each {|elt|
|
2012-06-06 18:55:44 +10:00
|
|
|
let (sname,p) = copy elt;
|
2012-05-24 14:49:39 -07:00
|
|
|
info(" " + sname + "/" + p.uuid + " (" + p.name + ")");
|
2011-12-16 20:33:39 -05:00
|
|
|
}
|
2011-12-15 20:27:55 -05:00
|
|
|
}
|
|
|
|
|
2012-06-06 18:55:44 +10:00
|
|
|
fn install_named(&c: cargo, wd: str, name: str) {
|
2012-03-22 08:39:41 -07:00
|
|
|
let mut ps = [];
|
2011-12-16 22:08:25 -05:00
|
|
|
for_each_package(c, { |s, p|
|
2011-12-16 20:33:39 -05:00
|
|
|
if p.name == name {
|
2012-06-06 18:55:44 +10:00
|
|
|
vec::grow(ps, 1u, (s.name, copy p));
|
2011-12-16 20:33:39 -05:00
|
|
|
}
|
|
|
|
});
|
2011-12-16 22:08:25 -05:00
|
|
|
if vec::len(ps) == 1u {
|
2012-06-06 18:55:44 +10:00
|
|
|
let (sname, p) = copy ps[0];
|
|
|
|
install_package(c, sname, wd, p);
|
2011-12-16 22:08:25 -05:00
|
|
|
ret;
|
|
|
|
} else if vec::len(ps) == 0u {
|
2012-06-06 18:55:44 +10:00
|
|
|
cargo_suggestion(c, { ||
|
2012-06-02 18:11:31 +10:00
|
|
|
error("can't find package: " + name);
|
|
|
|
});
|
2011-12-16 22:08:25 -05:00
|
|
|
ret;
|
|
|
|
}
|
2012-06-02 16:25:56 +10:00
|
|
|
error("found multiple packages:");
|
2012-04-06 20:01:43 +02:00
|
|
|
for ps.each {|elt|
|
2012-06-06 18:55:44 +10:00
|
|
|
let (sname,p) = copy elt;
|
2012-05-24 14:49:39 -07:00
|
|
|
info(" " + sname + "/" + p.uuid + " (" + p.name + ")");
|
2011-12-16 22:08:25 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-06-06 18:55:44 +10:00
|
|
|
fn install_uuid_specific(&c: cargo, wd: str, src: str, uuid: str) {
|
2011-12-16 22:08:25 -05:00
|
|
|
alt c.sources.find(src) {
|
2012-05-18 10:40:54 -07:00
|
|
|
some(s) {
|
|
|
|
let packages = copy s.packages;
|
|
|
|
if vec::any(packages, { |p|
|
|
|
|
if p.uuid == uuid {
|
2012-06-06 18:55:44 +10:00
|
|
|
install_package(c, src, wd, p);
|
2012-05-18 10:40:54 -07:00
|
|
|
true
|
|
|
|
} else { false }
|
|
|
|
}) { ret; }
|
|
|
|
}
|
|
|
|
_ { }
|
2011-12-16 20:33:39 -05:00
|
|
|
}
|
2012-06-02 18:11:31 +10:00
|
|
|
error("can't find package: " + src + "/" + uuid);
|
2011-12-16 22:08:25 -05:00
|
|
|
}
|
|
|
|
|
2012-06-06 18:55:44 +10:00
|
|
|
fn install_named_specific(&c: cargo, wd: str, src: str, name: str) {
|
2011-12-16 22:08:25 -05:00
|
|
|
alt c.sources.find(src) {
|
|
|
|
some(s) {
|
2012-05-18 10:40:54 -07:00
|
|
|
let packages = copy s.packages;
|
|
|
|
if vec::any(packages, { |p|
|
2011-12-16 22:08:25 -05:00
|
|
|
if p.name == name {
|
2012-06-06 18:55:44 +10:00
|
|
|
install_package(c, src, wd, p);
|
2012-03-26 13:16:29 +02:00
|
|
|
true
|
|
|
|
} else { false }
|
2011-12-16 22:08:25 -05:00
|
|
|
}) { ret; }
|
|
|
|
}
|
|
|
|
_ { }
|
|
|
|
}
|
2012-06-02 18:11:31 +10:00
|
|
|
error("can't find package: " + src + "/" + name);
|
2011-12-15 20:27:55 -05:00
|
|
|
}
|
|
|
|
|
2012-06-06 18:55:44 +10:00
|
|
|
fn cmd_uninstall(&c: cargo) {
|
2012-02-05 18:30:03 +09:00
|
|
|
if vec::len(c.opts.free) < 3u {
|
2011-11-30 18:57:46 -05:00
|
|
|
cmd_usage();
|
|
|
|
ret;
|
|
|
|
}
|
|
|
|
|
2012-06-02 18:46:08 +10:00
|
|
|
let lib = c.libdir;
|
|
|
|
let bin = c.bindir;
|
2012-06-02 16:25:56 +10:00
|
|
|
let target = c.opts.free[2u];
|
|
|
|
|
|
|
|
// FIXME: needs stronger pattern matching
|
2012-06-02 18:46:08 +10:00
|
|
|
// FIXME: needs to uninstall from a specified location in a cache instead
|
|
|
|
// of looking for it (binaries can be uninstalled by name only)
|
2012-06-02 16:25:56 +10:00
|
|
|
if is_uuid(target) {
|
2012-06-02 18:46:08 +10:00
|
|
|
for os::list_dir(lib).each { |file|
|
2012-06-02 16:25:56 +10:00
|
|
|
alt str::find_str(file, "-" + target + "-") {
|
|
|
|
some(idx) {
|
2012-06-02 18:46:08 +10:00
|
|
|
let full = path::normalize(path::connect(lib, file));
|
2012-06-02 16:25:56 +10:00
|
|
|
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 {
|
2012-06-02 18:46:08 +10:00
|
|
|
for os::list_dir(lib).each { |file|
|
2012-06-02 16:25:56 +10:00
|
|
|
alt str::find_str(file, "lib" + target + "-") {
|
|
|
|
some(idx) {
|
2012-06-02 18:46:08 +10:00
|
|
|
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));
|
2012-06-02 16:25:56 +10:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-06-06 18:55:44 +10:00
|
|
|
fn cmd_install(&c: cargo) unsafe {
|
2012-05-26 08:35:54 -07:00
|
|
|
let wd_base = c.workdir + path::path_sep();
|
|
|
|
let wd = alt tempfile::mkdtemp(wd_base, "") {
|
2011-12-09 00:39:41 -05:00
|
|
|
some(_wd) { _wd }
|
2012-05-26 08:35:54 -07:00
|
|
|
none { fail #fmt("needed temp dir: %s", wd_base); }
|
2011-12-09 00:39:41 -05:00
|
|
|
};
|
|
|
|
|
2012-06-02 16:25:56 +10:00
|
|
|
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);
|
2011-12-16 22:08:25 -05:00
|
|
|
}
|
2012-06-02 16:25:56 +10:00
|
|
|
|
|
|
|
install_source(c, wd);
|
|
|
|
ret;
|
|
|
|
}
|
|
|
|
|
2012-06-06 18:55:44 +10:00
|
|
|
sync(c);
|
|
|
|
|
2012-06-02 16:25:56 +10:00
|
|
|
let target = c.opts.free[2];
|
|
|
|
|
2012-06-06 18:55:44 +10:00
|
|
|
if is_archive_path(target) {
|
|
|
|
install_file(c, wd, target);
|
|
|
|
ret;
|
2012-06-02 16:25:56 +10:00
|
|
|
} else if is_git_url(target) {
|
2012-05-28 18:58:01 -07:00
|
|
|
let ref = if c.opts.free.len() >= 4u {
|
|
|
|
some(c.opts.free[3u])
|
|
|
|
} else {
|
|
|
|
none
|
|
|
|
};
|
|
|
|
install_git(c, wd, target, ref)
|
2012-06-06 18:55:44 +10:00
|
|
|
} else if !valid_pkg_name(target) && has_archive_extension(target) {
|
|
|
|
install_curl(c, wd, target);
|
2012-06-02 16:25:56 +10:00
|
|
|
ret;
|
2011-12-15 20:27:55 -05:00
|
|
|
} else {
|
2012-06-02 16:25:56 +10:00
|
|
|
let mut ps = copy target;
|
|
|
|
|
|
|
|
alt str::find_char(ps, '/') {
|
2012-02-11 03:20:45 -08:00
|
|
|
option::some(idx) {
|
2012-06-02 16:25:56 +10:00
|
|
|
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 03:20:45 -08:00
|
|
|
}
|
|
|
|
option::none {
|
2012-06-02 16:25:56 +10:00
|
|
|
if is_uuid(ps) {
|
|
|
|
install_uuid(c, wd, ps);
|
|
|
|
} else {
|
|
|
|
install_named(c, wd, ps);
|
|
|
|
}
|
2012-02-11 03:20:45 -08:00
|
|
|
}
|
2011-12-16 22:08:25 -05:00
|
|
|
}
|
2011-12-01 22:37:56 -05:00
|
|
|
}
|
2011-11-30 18:57:46 -05:00
|
|
|
}
|
|
|
|
|
2012-06-06 18:55:44 +10:00
|
|
|
fn sync(&c: cargo) {
|
|
|
|
for c.sources.each_key { |k|
|
|
|
|
let mut s = c.sources.get(k);
|
|
|
|
|
|
|
|
sync_one(c, s);
|
|
|
|
// FIXME: mutability hack
|
|
|
|
c.sources.insert(k, s);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
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");
|
2012-03-12 20:04:27 -07:00
|
|
|
let pkgfile = path::connect(dir, "packages.json.new");
|
|
|
|
let destpkgfile = path::connect(dir, "packages.json");
|
|
|
|
let keyfile = path::connect(dir, "key.gpg");
|
2012-06-06 18:55:44 +10:00
|
|
|
let srcsigfile = path::connect(dir, "source.json.sig");
|
|
|
|
let sigfile = path::connect(dir, "packages.json.sig");
|
2011-12-16 20:33:39 -05:00
|
|
|
let url = src.url;
|
2012-06-06 18:55:44 +10:00
|
|
|
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;
|
2011-12-16 20:33:39 -05:00
|
|
|
}
|
2012-06-06 18:55:44 +10:00
|
|
|
|
|
|
|
if os::copy_file(path::connect(url, "source.json"), srcfile) {
|
|
|
|
has_src_file = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
os::copy_file(path::connect(url, "source.json.sig"), srcsigfile);
|
|
|
|
os::copy_file(path::connect(url, "packages.json.sig"), sigfile);
|
|
|
|
|
|
|
|
alt src.key {
|
2011-12-20 19:59:37 -05:00
|
|
|
some(u) {
|
2012-06-06 18:55:44 +10:00
|
|
|
let p = run::program_output("curl", ["-f", "-s", "-o", keyfile,
|
|
|
|
u]);
|
2011-12-20 19:59:37 -05:00
|
|
|
if p.status != 0 {
|
2012-06-06 18:55:44 +10:00
|
|
|
error(#fmt["fetch for source %s (key %s) failed", name, u]);
|
|
|
|
ret false;
|
2011-12-20 19:59:37 -05:00
|
|
|
}
|
2012-06-06 18:55:44 +10:00
|
|
|
pgp::add(c.root, keyfile);
|
2011-12-20 19:59:37 -05:00
|
|
|
}
|
|
|
|
_ { }
|
|
|
|
}
|
2012-06-06 18:55:44 +10:00
|
|
|
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);
|
|
|
|
|
2011-12-20 19:59:37 -05:00
|
|
|
alt src.key {
|
|
|
|
some(u) {
|
2011-12-20 21:52:50 -05:00
|
|
|
let p = run::program_output("curl", ["-f", "-s", "-o", keyfile,
|
|
|
|
u]);
|
2011-12-20 19:59:37 -05:00
|
|
|
if p.status != 0 {
|
2012-06-06 18:55:44 +10:00
|
|
|
error(#fmt["fetch for source %s (key %s) failed", name, u]);
|
|
|
|
rollback(name, dir, false);
|
|
|
|
ret false;
|
2011-12-20 19:59:37 -05:00
|
|
|
}
|
|
|
|
pgp::add(c.root, keyfile);
|
|
|
|
}
|
|
|
|
_ { }
|
|
|
|
}
|
2012-06-06 18:55:44 +10:00
|
|
|
alt (src.key, src.keyfp) {
|
|
|
|
(some(_), some(f)) {
|
2011-12-20 19:59:37 -05:00
|
|
|
let r = pgp::verify(c.root, pkgfile, sigfile, f);
|
2012-06-06 18:55:44 +10:00
|
|
|
|
2011-12-20 19:59:37 -05:00
|
|
|
if !r {
|
2012-06-06 18:55:44 +10:00
|
|
|
error(#fmt["signature verification failed for source %s",
|
2011-12-20 21:52:50 -05:00
|
|
|
name]);
|
2012-06-06 18:55:44 +10:00
|
|
|
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;
|
|
|
|
}
|
2011-12-20 19:59:37 -05:00
|
|
|
}
|
|
|
|
}
|
2012-06-06 18:55:44 +10:00
|
|
|
_ {}
|
2011-12-20 19:59:37 -05:00
|
|
|
}
|
2012-06-06 18:55:44 +10:00
|
|
|
|
|
|
|
os::remove_file(keyfile);
|
|
|
|
|
|
|
|
info(#fmt["synced source: %s", name]);
|
|
|
|
|
|
|
|
ret true;
|
2011-12-16 20:33:39 -05:00
|
|
|
}
|
|
|
|
|
2012-06-06 18:55:44 +10:00
|
|
|
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 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;
|
2012-06-02 16:25:56 +10:00
|
|
|
}
|
2012-06-06 18:55:44 +10:00
|
|
|
|
|
|
|
let e = pgp::verify(c.root, srcfile, srcsigfile, f);
|
|
|
|
|
|
|
|
if !e {
|
|
|
|
error("signature verification failed for " +
|
|
|
|
"source " + name);
|
|
|
|
ret false;
|
2012-06-02 16:25:56 +10:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2012-06-06 18:55:44 +10:00
|
|
|
_ {}
|
|
|
|
}
|
|
|
|
|
|
|
|
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 20:33:39 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-06-06 18:55:44 +10:00
|
|
|
fn cmd_init(&c: cargo) {
|
2011-12-20 19:59:37 -05:00
|
|
|
let srcurl = "http://www.rust-lang.org/cargo/sources.json";
|
|
|
|
let sigurl = "http://www.rust-lang.org/cargo/sources.json.sig";
|
|
|
|
|
2012-03-12 20:04:27 -07:00
|
|
|
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");
|
2011-12-20 19:59:37 -05:00
|
|
|
|
|
|
|
let p = run::program_output("curl", ["-f", "-s", "-o", srcfile, srcurl]);
|
|
|
|
if p.status != 0 {
|
|
|
|
warn(#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 {
|
|
|
|
warn(#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 {
|
2012-06-06 18:55:44 +10:00
|
|
|
error(#fmt["signature verification failed for '%s'", srcfile]);
|
|
|
|
ret;
|
2011-12-20 19:59:37 -05:00
|
|
|
}
|
2012-06-06 18:55:44 +10:00
|
|
|
|
2012-04-02 22:53:40 -07:00
|
|
|
copy_warn(srcfile, destsrcfile);
|
2012-06-06 18:55:44 +10:00
|
|
|
os::remove_file(srcfile);
|
|
|
|
os::remove_file(sigfile);
|
2012-02-07 17:15:39 +09:00
|
|
|
|
2012-06-02 16:25:56 +10:00
|
|
|
info(#fmt["initialized .cargo in %s", c.root]);
|
2011-12-20 19:59:37 -05:00
|
|
|
}
|
|
|
|
|
2011-12-20 20:41:23 -05:00
|
|
|
fn print_pkg(s: source, p: package) {
|
2012-03-22 08:39:41 -07:00
|
|
|
let mut m = s.name + "/" + p.name + " (" + p.uuid + ")";
|
2011-12-20 20:41:23 -05:00
|
|
|
if vec::len(p.tags) > 0u {
|
|
|
|
m = m + " [" + str::connect(p.tags, ", ") + "]";
|
|
|
|
}
|
|
|
|
info(m);
|
2012-01-23 20:23:31 -06:00
|
|
|
if p.description != "" {
|
2012-01-23 21:50:32 -06:00
|
|
|
print(" >> " + p.description + "\n")
|
2012-01-23 20:23:31 -06:00
|
|
|
}
|
2011-12-20 20:41:23 -05:00
|
|
|
}
|
2012-06-02 16:25:56 +10:00
|
|
|
|
|
|
|
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 { ", " });
|
2011-12-20 20:41:23 -05:00
|
|
|
}
|
2012-06-02 16:25:56 +10:00
|
|
|
|
|
|
|
writer.write_line(list);
|
2011-12-20 20:41:23 -05:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2012-06-06 18:55:44 +10:00
|
|
|
fn cmd_list(&c: cargo) {
|
|
|
|
sync(c);
|
|
|
|
|
2012-06-02 16:25:56 +10:00
|
|
|
if vec::len(c.opts.free) >= 3u {
|
|
|
|
vec::iter_between(c.opts.free, 2u, vec::len(c.opts.free)) { |name|
|
2012-06-06 18:55:44 +10:00
|
|
|
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));
|
|
|
|
}
|
2012-06-02 16:25:56 +10:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
for c.sources.each_value { |v|
|
|
|
|
print_source(v);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-06-06 18:55:44 +10:00
|
|
|
fn cmd_search(&c: cargo) {
|
2012-02-05 18:30:03 +09:00
|
|
|
if vec::len(c.opts.free) < 3u {
|
2011-12-20 20:41:23 -05:00
|
|
|
cmd_usage();
|
|
|
|
ret;
|
|
|
|
}
|
2012-06-06 18:55:44 +10:00
|
|
|
|
|
|
|
sync(c);
|
|
|
|
|
2012-03-22 08:39:41 -07:00
|
|
|
let mut n = 0;
|
2012-02-05 18:30:03 +09:00
|
|
|
let name = c.opts.free[2];
|
|
|
|
let tags = vec::slice(c.opts.free, 3u, vec::len(c.opts.free));
|
2011-12-20 20:41:23 -05:00
|
|
|
for_each_package(c, { |s, p|
|
|
|
|
if (str::contains(p.name, name) || name == "*") &&
|
2012-02-11 17:54:49 -08:00
|
|
|
vec::all(tags, { |t| vec::contains(p.tags, t) }) {
|
2011-12-20 20:41:23 -05:00
|
|
|
print_pkg(s, p);
|
|
|
|
n += 1;
|
|
|
|
}
|
|
|
|
});
|
2012-06-02 16:25:56 +10:00
|
|
|
info(#fmt["found %d packages", n]);
|
2011-12-20 20:41:23 -05:00
|
|
|
}
|
|
|
|
|
2012-05-26 08:35:54 -07:00
|
|
|
fn install_to_dir(srcfile: str, destdir: str) {
|
|
|
|
let newfile = path::connect(destdir, path::basename(srcfile));
|
|
|
|
|
2012-06-02 16:25:56 +10:00
|
|
|
let status = run::run_program("cp", [srcfile, newfile]);
|
|
|
|
if status == 0 {
|
|
|
|
info(#fmt["installed: '%s'", newfile]);
|
|
|
|
} else {
|
|
|
|
error(#fmt["could not install: '%s'", newfile]);
|
|
|
|
}
|
2012-05-26 08:35:54 -07:00
|
|
|
}
|
|
|
|
|
2012-06-06 18:55:44 +10:00
|
|
|
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) {
|
|
|
|
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 v.key {
|
|
|
|
some(key) {
|
|
|
|
chash.insert("key", json::string(key));
|
|
|
|
}
|
|
|
|
_ {}
|
|
|
|
}
|
|
|
|
alt 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));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-05-26 08:35:54 -07:00
|
|
|
fn copy_warn(srcfile: str, destfile: str) {
|
2012-06-06 18:55:44 +10:00
|
|
|
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", v.name, v.url, 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, {
|
|
|
|
mut name: name,
|
|
|
|
mut url: url,
|
|
|
|
mut method: assume_source_method(url),
|
|
|
|
mut key: none,
|
|
|
|
mut keyfp: none,
|
|
|
|
mut packages: []
|
|
|
|
});
|
|
|
|
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;
|
|
|
|
|
|
|
|
source.url = if source.method == "file" {
|
|
|
|
os::make_absolute(url)
|
|
|
|
} else {
|
|
|
|
url
|
|
|
|
};
|
|
|
|
|
|
|
|
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" }
|
|
|
|
};
|
|
|
|
|
|
|
|
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-02 22:53:40 -07:00
|
|
|
}
|
|
|
|
|
2011-11-30 18:57:46 -05:00
|
|
|
fn cmd_usage() {
|
2012-06-02 16:25:56 +10:00
|
|
|
print("Usage: cargo <verb> [options] [args..]\n" +
|
2012-05-26 08:35:54 -07:00
|
|
|
" e.g.: cargo [init | sync]\n" +
|
2012-06-02 16:25:56 +10:00
|
|
|
" e.g.: cargo install [-g | -G] <package>
|
2012-05-26 08:35:54 -07:00
|
|
|
|
2012-06-02 16:25:56 +10:00
|
|
|
General:
|
|
|
|
init Reinitialize cargo in ~/.cargo
|
|
|
|
usage Display this message
|
2012-05-26 08:35:54 -07:00
|
|
|
|
|
|
|
Querying:
|
2012-06-06 18:55:44 +10:00
|
|
|
list [sources..] List the packages in sources
|
2012-06-02 16:25:56 +10:00
|
|
|
search <name | '*'> [tags...] Search packages
|
|
|
|
|
2012-06-06 18:55:44 +10:00
|
|
|
Sources:
|
|
|
|
sources List sources
|
|
|
|
sources add <name> <url> Add a source
|
|
|
|
sources remove <name> Remove a source
|
|
|
|
sources rename <name> <new> Rename a source
|
|
|
|
sources set-url <name> <url> Change the source URL
|
|
|
|
sources set-method <name> <method> Change the method (guesses from
|
|
|
|
the URL by default) can be ;git',
|
|
|
|
'file' or 'curl'
|
|
|
|
sources clear Remove all sources
|
|
|
|
|
2012-06-02 16:25:56 +10:00
|
|
|
Packages:
|
|
|
|
install [options] Install a package from source
|
|
|
|
code in the current directory
|
|
|
|
install [options] [source/]<name> Install a package by name
|
|
|
|
install [options] [source/]<uuid> Install a package by uuid
|
|
|
|
install [options] <url> Install a package via curl (HTTP,
|
2012-06-02 16:48:52 +10:00
|
|
|
FTP, etc.) from an
|
2012-06-02 18:11:31 +10:00
|
|
|
.tar[.gz|bz2|xz] file
|
2012-06-06 18:55:44 +10:00
|
|
|
install [options] <url> [ref] Install a package via read-only
|
|
|
|
git
|
2012-06-02 16:25:56 +10:00
|
|
|
install [options] <file> Install a package directly from an
|
2012-06-02 18:11:31 +10:00
|
|
|
.tar[.gz|bz2|xz] file
|
2012-06-06 18:55:44 +10:00
|
|
|
uninstall [options] [source/]<name> Remove a package by [meta]name
|
|
|
|
uninstall [options] [source/]<uuid> Remove a package by [meta]uuid
|
2012-05-26 08:35:54 -07:00
|
|
|
|
|
|
|
Package installation options:
|
2012-06-06 18:55:44 +10:00
|
|
|
--test Run crate tests before installing
|
2012-06-02 16:25:56 +10:00
|
|
|
|
|
|
|
Package [un]installation options:
|
|
|
|
-g Work at the user level (~/.cargo/bin/ instead of
|
|
|
|
locally in ./.cargo/bin/ by default)
|
|
|
|
-G Work at the system level (/usr/local/lib/cargo/bin/)
|
2012-05-26 08:35:54 -07:00
|
|
|
|
|
|
|
Other:
|
|
|
|
-h, --help Display this message
|
2012-02-07 17:15:39 +09:00
|
|
|
");
|
2011-11-30 18:57:46 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
fn main(argv: [str]) {
|
2012-02-05 18:30:03 +09:00
|
|
|
let o = build_cargo_options(argv);
|
|
|
|
|
2012-05-26 08:35:54 -07:00
|
|
|
if vec::len(o.free) < 2u || o.help {
|
2011-11-30 18:57:46 -05:00
|
|
|
cmd_usage();
|
|
|
|
ret;
|
|
|
|
}
|
2012-02-05 18:30:03 +09:00
|
|
|
|
2012-06-02 16:25:56 +10:00
|
|
|
let mut c = configure(o);
|
|
|
|
let home = c.root;
|
2012-06-06 18:55:44 +10:00
|
|
|
let first_time = os::path_exists(path::connect(home, "sources.json"));
|
2012-06-02 16:25:56 +10:00
|
|
|
|
2012-06-06 18:55:44 +10:00
|
|
|
if !first_time && o.free[1] != "init" {
|
2012-06-02 16:25:56 +10:00
|
|
|
cmd_init(c);
|
|
|
|
|
|
|
|
// FIXME: shouldn't need to reconfigure
|
|
|
|
c = configure(o);
|
|
|
|
}
|
2012-02-05 18:30:03 +09:00
|
|
|
|
|
|
|
alt o.free[1] {
|
2011-12-20 21:52:50 -05:00
|
|
|
"init" { cmd_init(c); }
|
2012-02-05 18:30:03 +09:00
|
|
|
"install" { cmd_install(c); }
|
2012-06-02 16:25:56 +10:00
|
|
|
"uninstall" { cmd_uninstall(c); }
|
2012-02-05 18:30:03 +09:00
|
|
|
"list" { cmd_list(c); }
|
|
|
|
"search" { cmd_search(c); }
|
2012-06-06 18:55:44 +10:00
|
|
|
"sources" { cmd_sources(c); }
|
2011-11-30 18:57:46 -05:00
|
|
|
_ { cmd_usage(); }
|
|
|
|
}
|
2012-06-06 18:55:44 +10:00
|
|
|
|
|
|
|
dump_cache(c);
|
|
|
|
dump_sources(c);
|
|
|
|
}
|