rust/src/librustpkg/util.rs

1080 lines
30 KiB
Rust
Raw Normal View History

// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
2013-01-16 05:59:37 -06:00
use core::*;
use send_map::linear::LinearMap;
2013-01-16 05:59:37 -06:00
use rustc::metadata::filesearch;
use rustc::driver::{driver, session};
use syntax::ast_util::*;
use syntax::{ast, attr, codemap, diagnostic, fold, parse, visit};
use codemap::span;
2013-01-16 05:59:37 -06:00
use semver::Version;
2013-01-22 21:29:47 -06:00
use std::{json, term, sort, getopts};
use getopts::groups::getopts;
2013-01-23 00:38:05 -06:00
use Listener;
pub struct Package {
id: ~str,
vers: Version,
bins: ~[~str],
libs: ~[~str],
}
2013-01-16 05:59:37 -06:00
pub fn root() -> Path {
match filesearch::get_rustpkg_root() {
result::Ok(path) => path,
result::Err(err) => fail err
}
}
2013-01-15 07:57:03 -06:00
pub fn is_cmd(cmd: ~str) -> bool {
let cmds = &[~"build", ~"clean", ~"do", ~"install", ~"prefer",
~"test", ~"uninstall", ~"unprefer"];
2013-01-15 07:57:03 -06:00
vec::contains(cmds, &cmd)
}
2013-01-16 05:59:37 -06:00
pub fn parse_name(id: ~str) -> result::Result<~str, ~str> {
2013-01-16 05:59:37 -06:00
let parts = str::split_char(id, '.');
for parts.each |&part| {
for str::chars(part).each |&char| {
if char::is_whitespace(char) {
2013-01-23 00:38:05 -06:00
return result::Err(
~"could not parse id: contains whitespace");
2013-01-16 05:59:37 -06:00
} else if char::is_uppercase(char) {
2013-01-23 00:38:05 -06:00
return result::Err(
~"could not parse id: should be all lowercase");
2013-01-16 05:59:37 -06:00
}
}
}
result::Ok(parts.last())
2013-01-16 05:59:37 -06:00
}
fn mk_rustpkg_use(ctx: @ReadyCtx) -> @ast::view_item {
let vers = ast::lit_str(@~"0.6");
let vers = no_span(vers);
let mi = ast::meta_name_value(~"vers", vers);
let mi = no_span(mi);
let vi = ast::view_item_use(ctx.sess.ident_of(~"rustpkg"),
~[@mi],
ctx.sess.next_node_id());
@ast::view_item {
node: vi,
attrs: ~[],
vis: ast::private,
span: dummy_sp()
}
}
struct ListenerFn {
cmds: ~[~str],
span: codemap::span,
path: ~[ast::ident]
}
struct ReadyCtx {
sess: session::Session,
crate: @ast::crate,
mut path: ~[ast::ident],
mut fns: ~[ListenerFn]
}
fn fold_mod(ctx: @ReadyCtx, m: ast::_mod,
fold: fold::ast_fold) -> ast::_mod {
fn strip_main(item: @ast::item) -> @ast::item {
@ast::item {
attrs: do item.attrs.filtered |attr| {
attr::get_attr_name(*attr) != ~"main"
},
.. copy *item
}
}
2013-01-23 00:38:05 -06:00
fold::noop_fold_mod(ast::_mod {
view_items: vec::append_one(m.view_items, mk_rustpkg_use(ctx)),
items: do vec::map(m.items) |item| {
strip_main(*item)
}
}, fold)
}
fn fold_crate(ctx: @ReadyCtx, crate: ast::crate_,
fold: fold::ast_fold) -> ast::crate_ {
let folded = fold::noop_fold_crate(crate, fold);
ast::crate_ {
module: add_pkg_module(ctx, /*bad*/copy folded.module),
.. folded
}
}
2013-01-23 00:38:05 -06:00
fn fold_item(ctx: @ReadyCtx, item: @ast::item,
fold: fold::ast_fold) -> Option<@ast::item> {
ctx.path.push(item.ident);
let attrs = attr::find_attrs_by_name(item.attrs, ~"pkg_do");
if attrs.len() > 0 {
let mut cmds = ~[];
for attrs.each |attr| {
match attr.node.value.node {
ast::meta_list(_, mis) => {
for mis.each |mi| {
match mi.node {
ast::meta_word(cmd) => cmds.push(cmd),
_ => {}
};
}
}
_ => cmds.push(~"build")
};
}
ctx.fns.push(ListenerFn {
cmds: cmds,
span: item.span,
path: /*bad*/copy ctx.path
});
}
let res = fold::noop_fold_item(item, fold);
ctx.path.pop();
res
}
fn mk_rustpkg_import(ctx: @ReadyCtx) -> @ast::view_item {
let path = @ast::path {
span: dummy_sp(),
global: false,
idents: ~[ctx.sess.ident_of(~"rustpkg")],
rp: None,
types: ~[]
};
let vp = @no_span(ast::view_path_simple(ctx.sess.ident_of(~"rustpkg"),
path, ast::type_value_ns,
ctx.sess.next_node_id()));
@ast::view_item {
node: ast::view_item_import(~[vp]),
attrs: ~[],
vis: ast::private,
span: dummy_sp()
}
}
fn add_pkg_module(ctx: @ReadyCtx, m: ast::_mod) -> ast::_mod {
let listeners = mk_listeners(ctx);
let main = mk_main(ctx);
2013-01-23 00:38:05 -06:00
let pkg_mod = @ast::_mod {
view_items: ~[mk_rustpkg_import(ctx)],
items: ~[main, listeners]
};
let resolve_unexported_attr =
attr::mk_attr(attr::mk_word_item(~"!resolve_unexported"));
let item_ = ast::item_mod(*pkg_mod);
let item = @ast::item {
ident: ctx.sess.ident_of(~"__pkg"),
attrs: ~[resolve_unexported_attr],
id: ctx.sess.next_node_id(),
node: item_,
vis: ast::public,
span: dummy_sp(),
};
2013-01-23 00:38:05 -06:00
ast::_mod {
items: vec::append_one(/*bad*/copy m.items, item),
.. m
}
}
fn no_span<T: Copy>(t: T) -> ast::spanned<T> {
ast::spanned {
node: t,
span: dummy_sp()
}
}
fn path_node(ids: ~[ast::ident]) -> @ast::path {
@ast::path {
span: dummy_sp(),
global: false,
idents: ids,
rp: None,
types: ~[]
}
}
fn path_node_global(ids: ~[ast::ident]) -> @ast::path {
@ast::path {
span: dummy_sp(),
global: true,
idents: ids,
rp: None,
types: ~[]
}
}
fn mk_listeners(ctx: @ReadyCtx) -> @ast::item {
let ret_ty = mk_listener_vec_ty(ctx);
2013-01-23 00:38:05 -06:00
let decl = ast::fn_decl {
inputs: ~[],
output: ret_ty,
cf: ast::return_val
};
let listeners = mk_listener_vec(ctx);
let body_ = default_block(~[], option::Some(listeners),
ctx.sess.next_node_id());
let body = no_span(body_);
let item_ = ast::item_fn(decl, ast::impure_fn, ~[], body);
@ast::item {
ident: ctx.sess.ident_of(~"listeners"),
attrs: ~[],
id: ctx.sess.next_node_id(),
node: item_,
vis: ast::public,
span: dummy_sp(),
}
}
fn mk_path(ctx: @ReadyCtx, path: ~[ast::ident]) -> @ast::path {
path_node(~[ctx.sess.ident_of(~"rustpkg")] + path)
}
fn mk_listener_vec_ty(ctx: @ReadyCtx) -> @ast::Ty {
let listener_ty_path = mk_path(ctx, ~[ctx.sess.ident_of(~"Listener")]);
2013-01-23 00:38:05 -06:00
let listener_ty = ast::Ty {
id: ctx.sess.next_node_id(),
node: ast::ty_path(listener_ty_path,
ctx.sess.next_node_id()),
span: dummy_sp()
};
let vec_mt = ast::mt {
ty: @listener_ty,
mutbl: ast::m_imm
};
2013-01-23 00:38:05 -06:00
let inner_ty = @ast::Ty {
id: ctx.sess.next_node_id(),
node: ast::ty_vec(vec_mt),
span: dummy_sp()
};
2013-01-23 00:38:05 -06:00
@ast::Ty {
id: ctx.sess.next_node_id(),
node: ast::ty_uniq(ast::mt {
ty: inner_ty,
mutbl: ast::m_imm
}),
span: dummy_sp()
}
}
fn mk_listener_vec(ctx: @ReadyCtx) -> @ast::expr {
let fns = ctx.fns;
let descs = do fns.map |listener| {
mk_listener_rec(ctx, *listener)
};
2013-01-23 00:38:05 -06:00
let inner_expr = @ast::expr {
id: ctx.sess.next_node_id(),
callee_id: ctx.sess.next_node_id(),
node: ast::expr_vec(descs, ast::m_imm),
span: dummy_sp()
};
2013-01-23 00:38:05 -06:00
@ast::expr {
id: ctx.sess.next_node_id(),
callee_id: ctx.sess.next_node_id(),
node: ast::expr_vstore(inner_expr, ast::expr_vstore_uniq),
span: dummy_sp()
}
}
fn mk_listener_rec(ctx: @ReadyCtx, listener: ListenerFn) -> @ast::expr {
let span = listener.span;
let path = /*bad*/copy listener.path;
let descs = do listener.cmds.map |&cmd| {
2013-01-23 00:38:05 -06:00
let inner = @ast::expr {
id: ctx.sess.next_node_id(),
callee_id: ctx.sess.next_node_id(),
node: ast::expr_lit(@no_span(ast::lit_str(@cmd))),
span: span
};
2013-01-23 00:38:05 -06:00
@ast::expr {
id: ctx.sess.next_node_id(),
callee_id: ctx.sess.next_node_id(),
node: ast::expr_vstore(inner, ast::expr_vstore_uniq),
span: dummy_sp()
}
};
2013-01-23 00:38:05 -06:00
let cmd_expr_inner = @ast::expr {
id: ctx.sess.next_node_id(),
callee_id: ctx.sess.next_node_id(),
node: ast::expr_vec(descs, ast::m_imm),
span: dummy_sp()
};
2013-01-23 00:38:05 -06:00
let cmd_expr = ast::expr {
id: ctx.sess.next_node_id(),
callee_id: ctx.sess.next_node_id(),
node: ast::expr_vstore(cmd_expr_inner, ast::expr_vstore_uniq),
span: dummy_sp()
};
let cmd_field = no_span(ast::field_ {
mutbl: ast::m_imm,
ident: ctx.sess.ident_of(~"cmds"),
expr: @cmd_expr,
});
let cb_path = path_node_global(path);
2013-01-23 00:38:05 -06:00
let cb_expr = ast::expr {
id: ctx.sess.next_node_id(),
callee_id: ctx.sess.next_node_id(),
node: ast::expr_path(cb_path),
span: span
};
let cb_wrapper_expr = mk_fn_wrapper(ctx, cb_expr, span);
let cb_field = no_span(ast::field_ {
mutbl: ast::m_imm,
ident: ctx.sess.ident_of(~"cb"),
expr: cb_wrapper_expr
});
let listener_path = mk_path(ctx, ~[ctx.sess.ident_of(~"Listener")]);
let listener_rec_ = ast::expr_struct(listener_path,
~[cmd_field, cb_field],
option::None);
2013-01-23 00:38:05 -06:00
@ast::expr {
id: ctx.sess.next_node_id(),
callee_id: ctx.sess.next_node_id(),
node: listener_rec_,
span: span
}
}
fn mk_fn_wrapper(ctx: @ReadyCtx, fn_path_expr: ast::expr,
span: span) -> @ast::expr {
2013-01-23 00:38:05 -06:00
let call_expr = ast::expr {
id: ctx.sess.next_node_id(),
callee_id: ctx.sess.next_node_id(),
node: ast::expr_call(@fn_path_expr, ~[], false),
span: span
};
2013-01-23 00:38:05 -06:00
let call_stmt = no_span(ast::stmt_semi(@call_expr,
ctx.sess.next_node_id()));
let wrapper_decl = ast::fn_decl {
inputs: ~[],
2013-01-23 00:38:05 -06:00
output: @ast::Ty {
id: ctx.sess.next_node_id(),
node: ast::ty_nil, span: span
},
cf: ast::return_val
};
let wrapper_body = no_span(ast::blk_ {
view_items: ~[],
stmts: ~[@call_stmt],
expr: option::None,
id: ctx.sess.next_node_id(),
rules: ast::default_blk
});
2013-01-23 00:38:05 -06:00
@ast::expr {
id: ctx.sess.next_node_id(),
callee_id: ctx.sess.next_node_id(),
node: ast::expr_fn(ast::ProtoBare, wrapper_decl,
wrapper_body, @~[]),
span: span
}
}
fn mk_main(ctx: @ReadyCtx) -> @ast::item {
2013-01-23 00:38:05 -06:00
let ret_ty = ast::Ty {
id: ctx.sess.next_node_id(),
node: ast::ty_nil,
span: dummy_sp()
};
2013-01-23 00:38:05 -06:00
let decl = ast::fn_decl {
inputs: ~[],
output: @ret_ty,
cf: ast::return_val
};
let run_call_expr = mk_run_call(ctx);
let body_ = default_block(~[], option::Some(run_call_expr),
ctx.sess.next_node_id());
let body = ast::spanned {
node: body_,
span: dummy_sp()
};
let item_ = ast::item_fn(decl, ast::impure_fn, ~[], body);
@ast::item {
ident: ctx.sess.ident_of(~"main"),
attrs: ~[attr::mk_attr(attr::mk_word_item(~"main"))],
id: ctx.sess.next_node_id(),
node: item_,
vis: ast::public,
span: dummy_sp(),
}
}
fn mk_run_call(ctx: @ReadyCtx) -> @ast::expr {
let listener_path = path_node(~[ctx.sess.ident_of(~"listeners")]);
let listener_path_expr_ = ast::expr_path(listener_path);
2013-01-23 00:38:05 -06:00
let listener_path_expr = ast::expr {
id: ctx.sess.next_node_id(),
callee_id: ctx.sess.next_node_id(),
node: listener_path_expr_,
span: dummy_sp()
};
let listener_call_expr_ = ast::expr_call(@listener_path_expr, ~[], false);
2013-01-23 00:38:05 -06:00
let listener_call_expr = ast::expr {
id: ctx.sess.next_node_id(),
callee_id: ctx.sess.next_node_id(),
node: listener_call_expr_,
span: dummy_sp()
};
let rustpkg_run_path = mk_path(ctx, ~[ctx.sess.ident_of(~"run")]);
let rustpkg_run_path_expr_ = ast::expr_path(rustpkg_run_path);
2013-01-23 00:38:05 -06:00
let rustpkg_run_path_expr = ast::expr {
id: ctx.sess.next_node_id(),
callee_id: ctx.sess.next_node_id(),
node: rustpkg_run_path_expr_,
span: dummy_sp()
};
let rustpkg_run_call_expr_ = ast::expr_call(@rustpkg_run_path_expr,
~[@listener_call_expr],
false);
2013-01-23 00:38:05 -06:00
@ast::expr {
id: ctx.sess.next_node_id(),
callee_id: ctx.sess.next_node_id(),
node: rustpkg_run_call_expr_,
span: dummy_sp()
}
}
/// Generate/filter main function, add the list of commands, etc.
pub fn ready_crate(sess: session::Session,
crate: @ast::crate) -> @ast::crate {
let ctx = @ReadyCtx {
sess: sess,
crate: crate,
mut path: ~[],
mut fns: ~[]
};
let precursor = @fold::AstFoldFns {
fold_crate: fold::wrap(|a, b| fold_crate(ctx, a, b)),
fold_item: |a, b| fold_item(ctx, a, b),
fold_mod: |a, b| fold_mod(ctx, a, b),
.. *fold::default_ast_fold()
};
let fold = fold::make_fold(precursor);
@fold.fold_crate(*crate)
}
pub fn parse_vers(vers: ~str) -> result::Result<Version, ~str> {
2013-01-16 05:59:37 -06:00
match semver::parse(vers) {
Some(vers) => result::Ok(vers),
None => result::Err(~"could not parse version: invalid")
2013-01-16 05:59:37 -06:00
}
}
pub fn need_dir(s: &Path) {
if !os::path_is_dir(s) && !os::make_dir(s, 493_i32) {
fail fmt!("can't create dir: %s", s.to_str());
}
}
pub fn note(msg: ~str) {
let out = io::stdout();
if term::color_supported() {
term::fg(out, term::color_green);
out.write_str(~"note: ");
term::reset(out);
out.write_line(msg);
} else { out.write_line(~"note: " + msg); }
}
pub 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); }
}
pub 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); }
}
pub fn hash(data: ~str) -> ~str {
let hasher = hash::default_state();
hasher.write_str(data);
hasher.result_str()
}
pub fn temp_change_dir<T>(dir: &Path, cb: fn() -> T) {
let cwd = os::getcwd();
os::change_dir(dir);
cb();
os::change_dir(&cwd);
}
pub fn touch(path: &Path) {
match io::mk_file_writer(path, ~[io::Create]) {
result::Ok(writer) => writer.write_line(~""),
_ => {}
}
}
pub fn remove_dir_r(path: &Path) {
for os::walk_dir(path) |&file| {
let mut cdir = file;
loop {
if os::path_is_dir(&cdir) {
os::remove_dir(&cdir);
} else {
os::remove_file(&cdir);
}
cdir = cdir.dir_path();
if cdir == *path { break; }
}
}
os::remove_dir(path);
}
pub fn wait_for_lock(path: &Path) {
if os::path_exists(path) {
warn(fmt!("the database appears locked, please wait (or rm %s)",
path.to_str()));
loop {
if !os::path_exists(path) { break; }
}
}
}
fn _add_pkg(packages: ~[json::Json], pkg: &Package) -> ~[json::Json] {
for packages.each |&package| {
match package {
json::Object(map) => {
let mut has_id = false;
match map.get(&~"id") {
json::String(str) => {
if pkg.id == str {
has_id = true;
}
}
_ => {}
}
match map.get(&~"vers") {
json::String(str) => {
2013-01-23 00:38:05 -06:00
if has_id && pkg.vers.to_str() == str {
return packages;
}
}
_ => {}
}
}
_ => {}
}
}
let mut map = ~LinearMap();
map.insert(~"id", json::String(pkg.id));
map.insert(~"vers", json::String(pkg.vers.to_str()));
map.insert(~"bins", json::List(do pkg.bins.map |&bin| {
json::String(bin)
}));
map.insert(~"libs", json::List(do pkg.libs.map |&lib| {
json::String(lib)
}));
vec::append(packages, ~[json::Object(map)])
}
fn _rm_pkg(packages: ~[json::Json], pkg: &Package) -> ~[json::Json] {
do packages.filter_map |&package| {
match package {
json::Object(map) => {
let mut has_id = false;
match map.get(&~"id") {
json::String(str) => {
if pkg.id == str {
has_id = true;
}
}
_ => {}
}
match map.get(&~"vers") {
json::String(str) => {
2013-01-23 00:38:05 -06:00
if has_id && pkg.vers.to_str() == str { None }
else { Some(package) }
}
_ => { Some(package) }
}
}
_ => { Some(package) }
}
}
}
pub fn load_pkgs() -> result::Result<~[json::Json], ~str> {
let root = root();
let db = root.push(~"db.json");
let db_lock = root.push(~"db.json.lck");
wait_for_lock(&db_lock);
touch(&db_lock);
let packages = if os::path_exists(&db) {
match io::read_whole_file_str(&db) {
result::Ok(str) => {
match json::from_str(str) {
result::Ok(json) => {
match json {
json::List(list) => list,
_ => {
os::remove_file(&db_lock);
2013-01-23 00:38:05 -06:00
return result::Err(
~"package db's json is not a list");
}
}
}
result::Err(err) => {
os::remove_file(&db_lock);
2013-01-23 00:38:05 -06:00
return result::Err(
fmt!("failed to parse package db: %s",
err.to_str()));
}
}
}
result::Err(err) => {
os::remove_file(&db_lock);
2013-01-23 00:38:05 -06:00
return result::Err(fmt!("failed to read package db: %s",
err));
}
}
} else { ~[] };
os::remove_file(&db_lock);
result::Ok(packages)
}
2013-01-23 00:38:05 -06:00
pub fn get_pkg(id: ~str,
vers: Option<~str>) -> result::Result<Package, ~str> {
let name = match parse_name(id) {
result::Ok(name) => name,
result::Err(err) => return result::Err(err)
};
let packages = match load_pkgs() {
result::Ok(packages) => packages,
result::Err(err) => return result::Err(err)
};
let mut sel = None;
let mut possibs = ~[];
let mut err = None;
for packages.each |&package| {
match package {
json::Object(map) => {
let pid = match map.get(&~"id") {
json::String(str) => str,
_ => loop
};
let pname = match parse_name(pid) {
result::Ok(pname) => pname,
result::Err(perr) => {
err = Some(perr);
break;
}
};
let pvers = match map.get(&~"vers") {
json::String(str) => str,
_ => loop
};
if pid == id || pname == name {
let bins = match map.get(&~"bins") {
json::List(list) => {
do list.map |&bin| {
match bin {
json::String(str) => str,
_ => ~""
}
}
}
_ => ~[]
};
let libs = match map.get(&~"libs") {
json::List(list) => {
do list.map |&lib| {
match lib {
json::String(str) => str,
_ => ~""
}
}
}
_ => ~[]
};
let package = Package {
id: pid,
vers: match parse_vers(pvers) {
result::Ok(vers) => vers,
result::Err(verr) => {
err = Some(verr);
break;
}
},
bins: bins,
libs: libs
};
if !vers.is_none() && vers.get() == pvers {
sel = Some(package);
}
else {
possibs.push(package);
}
}
}
_ => {}
}
}
if !err.is_none() {
return result::Err(err.get());
}
if !sel.is_none() {
return result::Ok(sel.get());
}
if !vers.is_none() || possibs.len() < 1 {
return result::Err(~"package not found");
}
result::Ok(sort::merge_sort(possibs, |v1, v2| {
v1.vers <= v2.vers
}).last())
}
pub fn add_pkg(pkg: &Package) -> bool {
let root = root();
let db = root.push(~"db.json");
let db_lock = root.push(~"db.json.lck");
let packages = match load_pkgs() {
result::Ok(packages) => packages,
result::Err(err) => {
error(err);
return false;
}
};
wait_for_lock(&db_lock);
touch(&db_lock);
os::remove_file(&db);
match io::mk_file_writer(&db, ~[io::Create]) {
result::Ok(writer) => {
2013-01-23 00:38:05 -06:00
writer.write_line(json::to_pretty_str(&json::List(
_add_pkg(packages, pkg))));
}
result::Err(err) => {
error(fmt!("failed to dump package db: %s", err));
os::remove_file(&db_lock);
return false;
}
}
os::remove_file(&db_lock);
true
}
pub fn remove_pkg(pkg: &Package) -> bool {
let root = root();
let db = root.push(~"db.json");
let db_lock = root.push(~"db.json.lck");
let packages = match load_pkgs() {
result::Ok(packages) => packages,
result::Err(err) => {
error(err);
return false;
}
};
wait_for_lock(&db_lock);
touch(&db_lock);
os::remove_file(&db);
match io::mk_file_writer(&db, ~[io::Create]) {
result::Ok(writer) => {
2013-01-23 00:38:05 -06:00
writer.write_line(json::to_pretty_str(&json::List(
_rm_pkg(packages, pkg))));
}
result::Err(err) => {
error(fmt!("failed to dump package db: %s", err));
os::remove_file(&db_lock);
return false;
}
}
os::remove_file(&db_lock);
true
}
pub fn compile_input(sysroot: Option<Path>, input: driver::input, dir: &Path,
flags: ~[~str], cfgs: ~[~str], opt: bool, test: bool) -> bool {
let lib_dir = dir.push(~"lib");
let bin_dir = dir.push(~"bin");
let test_dir = dir.push(~"test");
let binary = os::args()[0];
2013-01-22 21:29:47 -06:00
let matches = getopts(flags, driver::optgroups()).get();
2013-01-23 00:38:05 -06:00
let options = @{
crate_type: session::unknown_crate,
optimize: if opt { session::Aggressive } else { session::No },
test: test,
maybe_sysroot: sysroot,
.. *driver::build_session_options(binary, &matches, diagnostic::emit)
};
let mut crate_cfg = options.cfg;
2013-01-22 21:29:47 -06:00
2013-01-23 00:38:05 -06:00
for cfgs.each |&cfg| {
crate_cfg.push(attr::mk_word_item(cfg));
}
2013-01-22 21:29:47 -06:00
2013-01-23 00:38:05 -06:00
let options = @{
cfg: vec::append(options.cfg, crate_cfg),
.. *options
};
let sess = driver::build_session(options, diagnostic::emit);
let cfg = driver::build_configuration(sess, binary, input);
let mut outputs = driver::build_output_filenames(input, &None, &None,
sess);
let {crate, _} = driver::compile_upto(sess, cfg, input, driver::cu_parse,
Some(outputs));
let mut name = None;
let mut vers = None;
let mut uuid = None;
let mut crate_type = None;
fn load_link_attr(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| {
match a.node {
ast::meta_name_value(v, ast::spanned {node: ast::lit_str(s),
span: _}) => {
match v {
~"name" => name = Some(*s),
~"vers" => vers = Some(*s),
~"uuid" => uuid = Some(*s),
_ => { }
}
}
_ => {}
}
}
(name, vers, uuid)
}
for crate.node.attrs.each |a| {
match a.node.value.node {
ast::meta_name_value(v, ast::spanned {node: ast::lit_str(s),
span: _}) => {
match v {
~"crate_type" => crate_type = Some(*s),
_ => {}
}
}
ast::meta_list(v, mis) => {
match v {
~"link" => {
let (n, v, u) = load_link_attr(mis);
name = n;
vers = v;
uuid = u;
}
_ => {}
}
}
_ => {}
}
}
if name.is_none() || vers.is_none() || uuid.is_none() {
error(~"link attr without (name, vers, uuid) values");
return false;
}
let name = name.get();
let vers = vers.get();
let uuid = uuid.get();
let is_bin = match crate_type {
Some(crate_type) => {
match crate_type {
~"bin" => true,
~"lib" => false,
_ => {
warn(~"unknown crate_type, falling back to lib");
false
}
}
}
None => {
warn(~"missing crate_type attr, assuming lib");
false
}
};
if test {
need_dir(&test_dir);
outputs = driver::build_output_filenames(input, &Some(test_dir),
&None, sess)
}
else if is_bin {
need_dir(&bin_dir);
let path = bin_dir.push(fmt!("%s-%s-%s%s", name,
hash(name + uuid + vers),
vers, exe_suffix()));
2013-01-23 00:38:05 -06:00
outputs = driver::build_output_filenames(input, &None, &Some(path),
sess);
} else {
need_dir(&lib_dir);
outputs = driver::build_output_filenames(input, &Some(lib_dir),
&None, sess)
}
driver::compile_rest(sess, cfg, driver::cu_everything,
Some(outputs), Some(crate));
true
}
#[cfg(windows)]
pub fn exe_suffix() -> ~str { ~".exe" }
#[cfg(target_os = "linux")]
#[cfg(target_os = "android")]
#[cfg(target_os = "freebsd")]
#[cfg(target_os = "macos")]
pub fn exe_suffix() -> ~str { ~"" }
2013-01-23 00:38:05 -06:00
// FIXME: Use workcache to only compile when needed
pub fn compile_crate(sysroot: Option<Path>, crate: &Path, dir: &Path,
flags: ~[~str], cfgs: ~[~str], opt: bool,
test: bool) -> bool {
compile_input(sysroot, driver::file_input(*crate), dir, flags, cfgs,
opt, test)
}
2013-01-23 00:38:05 -06:00
pub fn compile_str(sysroot: Option<Path>, code: ~str, dir: &Path,
flags: ~[~str], cfgs: ~[~str], opt: bool,
test: bool) -> bool {
compile_input(sysroot, driver::str_input(code), dir, flags, cfgs,
opt, test)
}
#[cfg(windows)]
2013-01-23 00:38:05 -06:00
pub fn link_exe(_src: &Path, _dest: &Path) -> bool {
/* FIXME: Investigate how to do this on win32
Node wraps symlinks by having a .bat,
but that won't work with minGW. */
false
}
#[cfg(target_os = "linux")]
#[cfg(target_os = "android")]
#[cfg(target_os = "freebsd")]
#[cfg(target_os = "macos")]
pub fn link_exe(src: &Path, dest: &Path) -> bool unsafe {
do str::as_c_str(src.to_str()) |src_buf| {
do str::as_c_str(dest.to_str()) |dest_buf| {
libc::link(src_buf, dest_buf) == 0 as libc::c_int &&
libc::chmod(dest_buf, 755) == 0 as libc::c_int
}
}
}
2013-01-16 05:59:37 -06:00
#[test]
fn test_is_cmd() {
assert is_cmd(~"build");
assert is_cmd(~"clean");
assert is_cmd(~"do");
2013-01-16 05:59:37 -06:00
assert is_cmd(~"install");
assert is_cmd(~"prefer");
assert is_cmd(~"test");
assert is_cmd(~"uninstall");
assert is_cmd(~"unprefer");
}
#[test]
fn test_parse_name() {
assert parse_name(~"org.mozilla.servo").get() == ~"servo";
assert parse_name(~"org. mozilla.servo 2131").is_err();
2013-01-16 05:59:37 -06:00
}