2011-11-30 17:57:46 -06:00
|
|
|
// cargo.rs - Rust package manager
|
|
|
|
|
|
|
|
import rustc::syntax::{ast, codemap, visit};
|
|
|
|
import rustc::syntax::parse::parser;
|
|
|
|
|
2011-12-01 21:37:56 -06:00
|
|
|
import std::fs;
|
2011-12-08 22:41:29 -06:00
|
|
|
import std::generic_os;
|
2011-11-30 17:57:46 -06:00
|
|
|
import std::io;
|
2011-12-15 19:27:55 -06:00
|
|
|
import std::json;
|
2011-12-13 18:25:51 -06:00
|
|
|
import option;
|
|
|
|
import option::{none, some};
|
2011-12-08 22:41:29 -06:00
|
|
|
import std::os;
|
2011-11-30 17:57:46 -06:00
|
|
|
import std::run;
|
2011-12-13 18:25:51 -06:00
|
|
|
import str;
|
2011-11-30 17:57:46 -06:00
|
|
|
import std::tempfile;
|
2011-12-13 18:25:51 -06:00
|
|
|
import vec;
|
2011-11-30 17:57:46 -06:00
|
|
|
|
2011-12-08 22:50:25 -06:00
|
|
|
type cargo = {
|
|
|
|
root: str,
|
|
|
|
bindir: str,
|
|
|
|
libdir: str,
|
|
|
|
workdir: str,
|
|
|
|
fetchdir: str
|
|
|
|
};
|
|
|
|
|
2011-11-30 17:57:46 -06:00
|
|
|
type pkg = {
|
|
|
|
name: str,
|
|
|
|
vers: str,
|
|
|
|
uuid: str,
|
|
|
|
desc: option::t<str>,
|
2011-12-08 22:41:29 -06:00
|
|
|
sigs: option::t<str>,
|
|
|
|
crate_type: option::t<str>
|
2011-11-30 17:57:46 -06:00
|
|
|
};
|
|
|
|
|
|
|
|
fn load_link(mis: [@ast::meta_item]) -> (option::t<str>,
|
|
|
|
option::t<str>,
|
|
|
|
option::t<str>) {
|
|
|
|
let name = none;
|
|
|
|
let vers = none;
|
|
|
|
let uuid = none;
|
|
|
|
for a: @ast::meta_item in mis {
|
|
|
|
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); }
|
|
|
|
_ { }
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
(name, vers, uuid)
|
|
|
|
}
|
|
|
|
|
|
|
|
fn load_pkg(filename: str) -> option::t<pkg> {
|
|
|
|
let sess = @{cm: codemap::new_codemap(), mutable next_id: 0};
|
|
|
|
let c = parser::parse_crate_from_crate_file(filename, [], sess);
|
|
|
|
|
|
|
|
let name = none;
|
|
|
|
let vers = none;
|
|
|
|
let uuid = none;
|
|
|
|
let desc = none;
|
|
|
|
let sigs = none;
|
2011-12-08 22:41:29 -06:00
|
|
|
let crate_type = none;
|
2011-11-30 17:57:46 -06:00
|
|
|
|
|
|
|
for a in c.node.attrs {
|
|
|
|
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 22:41:29 -06:00
|
|
|
"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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
alt (name, vers, uuid) {
|
|
|
|
(some(name0), some(vers0), some(uuid0)) {
|
|
|
|
some({
|
|
|
|
name: name0,
|
|
|
|
vers: vers0,
|
|
|
|
uuid: uuid0,
|
|
|
|
desc: desc,
|
2011-12-08 22:41:29 -06:00
|
|
|
sigs: sigs,
|
|
|
|
crate_type: crate_type})
|
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 {
|
|
|
|
if (start >= str::char_len(s)) {
|
|
|
|
""
|
|
|
|
} else {
|
|
|
|
str::char_slice(s, start, str::char_len(s))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-12-08 22:41:29 -06:00
|
|
|
fn need_dir(s: str) {
|
|
|
|
if fs::file_is_dir(s) { ret; }
|
|
|
|
if !fs::make_dir(s, 0x1c0i32) {
|
|
|
|
fail #fmt["can't make_dir %s", s];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-12-08 22:50:25 -06:00
|
|
|
fn configure() -> cargo {
|
2011-12-08 22:41:29 -06:00
|
|
|
let p = alt generic_os::getenv("CARGO_ROOT") {
|
|
|
|
some(_p) { _p }
|
|
|
|
none. {
|
|
|
|
alt generic_os::getenv("HOME") {
|
2011-12-08 22:56:19 -06:00
|
|
|
some(_q) { fs::connect(_q, ".cargo") }
|
2011-12-08 22:41:29 -06:00
|
|
|
none. { fail "no CARGO_ROOT or HOME"; }
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
log #fmt["p: %s", p];
|
|
|
|
|
2011-12-08 22:50:25 -06:00
|
|
|
let c = {
|
|
|
|
root: p,
|
|
|
|
bindir: fs::connect(p, "bin"),
|
|
|
|
libdir: fs::connect(p, "lib"),
|
|
|
|
workdir: fs::connect(p, "work"),
|
|
|
|
fetchdir: fs::connect(p, "fetch")
|
|
|
|
};
|
|
|
|
|
|
|
|
need_dir(c.root);
|
|
|
|
need_dir(c.fetchdir);
|
|
|
|
need_dir(c.workdir);
|
|
|
|
need_dir(c.libdir);
|
|
|
|
need_dir(c.bindir);
|
2011-12-08 22:41:29 -06:00
|
|
|
|
2011-12-08 22:50:25 -06:00
|
|
|
c
|
2011-12-08 22:41:29 -06:00
|
|
|
}
|
|
|
|
|
2011-12-15 06:46:27 -06:00
|
|
|
fn install_one_crate(c: cargo, _path: str, cf: str, _p: pkg) {
|
2011-12-08 22:41:29 -06:00
|
|
|
let name = fs::basename(cf);
|
|
|
|
let ri = str::index(name, '.' as u8);
|
|
|
|
if ri != -1 {
|
|
|
|
name = str::slice(name, 0u, ri as uint);
|
|
|
|
}
|
|
|
|
log #fmt["Installing: %s", name];
|
2011-12-16 03:08:24 -06:00
|
|
|
let old = vec::map({|x| str::slice(x, 2u, str::byte_len(x))},
|
|
|
|
fs::list_dir("."));
|
2011-12-15 09:11:01 -06:00
|
|
|
run::run_program("rustc", [name + ".rc"]);
|
2011-12-16 03:08:24 -06:00
|
|
|
let new = vec::map({|x| str::slice(x, 2u, str::byte_len(x))},
|
|
|
|
fs::list_dir("."));
|
|
|
|
let created =
|
|
|
|
vec::filter::<str>({ |n| !vec::member::<str>(n, old) }, new);
|
|
|
|
let exec_suffix = os::exec_suffix();
|
2011-12-08 22:50:25 -06:00
|
|
|
for ct: str in created {
|
2011-12-16 03:08:24 -06:00
|
|
|
if (exec_suffix != "" && str::ends_with(ct, exec_suffix)) ||
|
|
|
|
(exec_suffix == "" && !str::starts_with(ct, "lib")) {
|
2011-12-08 22:50:25 -06:00
|
|
|
log #fmt[" bin: %s", ct];
|
2011-12-08 22:41:29 -06:00
|
|
|
// FIXME: need libstd fs::copy or something
|
2011-12-08 22:50:25 -06:00
|
|
|
run::run_program("cp", [ct, c.bindir]);
|
2011-12-08 22:41:29 -06:00
|
|
|
} else {
|
2011-12-08 22:50:25 -06:00
|
|
|
log #fmt[" lib: %s", ct];
|
|
|
|
run::run_program("cp", [ct, c.libdir]);
|
2011-12-08 22:41:29 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-12-08 22:50:25 -06:00
|
|
|
fn install_source(c: cargo, path: str) {
|
2011-12-01 21:37:56 -06:00
|
|
|
log #fmt["source: %s", path];
|
|
|
|
fs::change_dir(path);
|
|
|
|
let contents = fs::list_dir(".");
|
|
|
|
|
|
|
|
log #fmt["contents: %s", str::connect(contents, ", ")];
|
|
|
|
|
2011-12-16 03:08:24 -06:00
|
|
|
let cratefiles =
|
|
|
|
vec::filter::<str>({ |n| str::ends_with(n, ".rc") }, contents);
|
2011-12-01 21:37:56 -06:00
|
|
|
|
2011-12-08 22:41:29 -06:00
|
|
|
if vec::is_empty(cratefiles) {
|
|
|
|
fail "This doesn't look like a rust package (no .rc files).";
|
2011-12-01 21:37:56 -06:00
|
|
|
}
|
|
|
|
|
2011-12-08 22:41:29 -06:00
|
|
|
for cf: str in cratefiles {
|
|
|
|
let p = load_pkg(cf);
|
|
|
|
alt p {
|
|
|
|
none. { cont; }
|
|
|
|
some(_p) {
|
2011-12-08 22:50:25 -06:00
|
|
|
install_one_crate(c, path, cf, _p);
|
2011-12-08 22:41:29 -06:00
|
|
|
}
|
|
|
|
}
|
2011-12-01 21:37:56 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-12-08 23:39:41 -06:00
|
|
|
fn install_git(c: cargo, wd: str, _path: str) {
|
|
|
|
run::run_program("git", ["clone", _path, wd]);
|
|
|
|
install_source(c, wd);
|
2011-12-08 23:34:06 -06:00
|
|
|
}
|
|
|
|
|
2011-12-08 23:39:41 -06:00
|
|
|
fn install_file(c: cargo, wd: str, _path: str) {
|
|
|
|
run::run_program("tar", ["-x", "--strip-components=1",
|
|
|
|
"-C", wd, "-f", _path]);
|
|
|
|
install_source(c, wd);
|
2011-11-30 17:57:46 -06:00
|
|
|
}
|
|
|
|
|
2011-12-15 19:27:55 -06:00
|
|
|
fn install_resolved(c: cargo, wd: str, key: str) {
|
|
|
|
fs::remove_dir(wd);
|
|
|
|
let u = "https://rust-package-index.appspot.com/pkg/" + key;
|
|
|
|
let p = run::program_output("curl", [u]);
|
|
|
|
if p.status != 0 {
|
|
|
|
fail #fmt["Fetch of %s failed: %s", u, p.err];
|
|
|
|
}
|
|
|
|
let j = json::from_str(p.out);
|
|
|
|
alt j {
|
|
|
|
some (json::dict(_j)) {
|
|
|
|
alt _j.find("install") {
|
|
|
|
some (json::string(g)) {
|
|
|
|
log #fmt["Resolved: %s -> %s", key, g];
|
|
|
|
cmd_install(c, ["cargo", "install", g]);
|
|
|
|
}
|
|
|
|
_ { fail #fmt["Bogus install: '%s'", p.out]; }
|
|
|
|
}
|
|
|
|
}
|
|
|
|
_ { fail #fmt["Bad json: '%s'", p.out]; }
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn install_uuid(c: cargo, wd: str, uuid: str) {
|
2011-12-15 20:22:42 -06:00
|
|
|
install_resolved(c, wd, "by-uuid/" + uuid);
|
2011-12-15 19:27:55 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
fn install_named(c: cargo, wd: str, name: str) {
|
2011-12-15 20:22:42 -06:00
|
|
|
install_resolved(c, wd, "by-name/" + name);
|
2011-12-15 19:27:55 -06:00
|
|
|
}
|
|
|
|
|
2011-12-08 22:50:25 -06:00
|
|
|
fn cmd_install(c: cargo, argv: [str]) {
|
2011-11-30 17:57:46 -06:00
|
|
|
// cargo install <pkg>
|
|
|
|
if vec::len(argv) < 3u {
|
|
|
|
cmd_usage();
|
|
|
|
ret;
|
|
|
|
}
|
|
|
|
|
2011-12-08 23:39:41 -06:00
|
|
|
let wd = alt tempfile::mkdtemp(c.workdir + fs::path_sep(), "") {
|
|
|
|
some(_wd) { _wd }
|
|
|
|
none. { fail "needed temp dir"; }
|
|
|
|
};
|
|
|
|
|
2011-12-08 23:34:06 -06:00
|
|
|
if str::starts_with(argv[2], "git:") {
|
2011-12-08 23:39:41 -06:00
|
|
|
install_git(c, wd, argv[2]);
|
|
|
|
} else if str::starts_with(argv[2], "github:") {
|
|
|
|
let path = rest(argv[2], 7u);
|
|
|
|
install_git(c, wd, "git://github.com/" + path);
|
|
|
|
} else if str::starts_with(argv[2], "file:") {
|
2011-11-30 17:57:46 -06:00
|
|
|
let path = rest(argv[2], 5u);
|
2011-12-08 23:39:41 -06:00
|
|
|
install_file(c, wd, path);
|
2011-12-15 19:27:55 -06:00
|
|
|
} else if str::starts_with(argv[2], "uuid:") {
|
|
|
|
let uuid = rest(argv[2], 5u);
|
|
|
|
install_uuid(c, wd, uuid);
|
|
|
|
} else {
|
|
|
|
install_named(c, wd, argv[2]);
|
2011-12-01 21:37:56 -06:00
|
|
|
}
|
2011-11-30 17:57:46 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
fn cmd_usage() {
|
|
|
|
print("Usage: cargo <verb> [args...]");
|
|
|
|
}
|
|
|
|
|
|
|
|
fn main(argv: [str]) {
|
|
|
|
if vec::len(argv) < 2u {
|
|
|
|
cmd_usage();
|
|
|
|
ret;
|
|
|
|
}
|
2011-12-08 22:50:25 -06:00
|
|
|
let c = configure();
|
2011-11-30 17:57:46 -06:00
|
|
|
alt argv[1] {
|
2011-12-08 22:50:25 -06:00
|
|
|
"install" { cmd_install(c, argv); }
|
2011-11-30 17:57:46 -06:00
|
|
|
"usage" { cmd_usage(); }
|
|
|
|
_ { cmd_usage(); }
|
|
|
|
}
|
|
|
|
}
|