rustpkg: Add preliminary imperative API support

This commit is contained in:
Zack Corr 2013-01-23 11:19:13 +10:00 committed by Graydon Hoare
parent bd28fa4af5
commit 7079441843
5 changed files with 815 additions and 285 deletions

View File

@ -184,14 +184,14 @@ pub enum compile_upto {
cu_everything,
}
pub fn compile_upto(sess: Session, cfg: ast::crate_cfg,
input: input, upto: compile_upto,
outputs: Option<output_filenames>)
-> {crate: @ast::crate, tcx: Option<ty::ctxt>} {
// For continuing compilation after a parsed crate has been
// modified
pub fn compile_rest(sess: Session, cfg: ast::crate_cfg,
upto: compile_upto, outputs: Option<output_filenames>,
curr: Option<@ast::crate>)
-> {crate: @ast::crate, tcx: Option<ty::ctxt>} {
let time_passes = sess.time_passes();
let mut crate = time(time_passes, ~"parsing",
|| parse_input(sess, copy cfg, input) );
if upto == cu_parse { return {crate: crate, tcx: None}; }
let mut crate = curr.get();
*sess.building_library = session::building_library(
sess.opts.crate_type, crate, sess.opts.test);
@ -322,7 +322,6 @@ pub fn compile_upto(sess: Session, cfg: ast::crate_cfg,
};
time(time_passes, ~"LLVM passes", ||
link::write::run_passes(sess, llmod,
&outputs.obj_filename));
@ -342,9 +341,20 @@ pub fn compile_upto(sess: Session, cfg: ast::crate_cfg,
return {crate: crate, tcx: None};
}
pub fn compile_upto(sess: Session, +cfg: ast::crate_cfg,
input: input, upto: compile_upto,
outputs: Option<output_filenames>)
-> {crate: @ast::crate, tcx: Option<ty::ctxt>} {
let time_passes = sess.time_passes();
let mut crate = time(time_passes, ~"parsing",
|| parse_input(sess, copy cfg, input) );
if upto == cu_parse { return {crate: crate, tcx: None}; }
compile_rest(sess, cfg, upto, outputs, Some(crate))
}
pub fn compile_input(sess: Session, +cfg: ast::crate_cfg, input: input,
outdir: &Option<Path>, output: &Option<Path>) {
let upto = if sess.opts.parse_only { cu_parse }
else if sess.opts.no_trans { cu_no_trans }
else { cu_everything };

View File

@ -1,24 +1,130 @@
use core::*;
// 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.
use core::*;
use util::{compile_crate, note};
/// A crate is a unit of Rust code to be compiled into a binary or library
pub struct Crate {
file: ~str,
flags: ~[~str],
cfg: ~[~str]
cfgs: ~[~str]
}
pub struct Listener {
cmd: ~str,
cb: fn~()
}
pub fn run(listeners: ~[Listener]) {
io::println(src_dir().to_str());
io::println(work_dir().to_str());
let cmd = os::args()[1];
for listeners.each |listener| {
if listener.cmd == cmd {
(listener.cb)();
}
}
}
pub impl Crate {
fn flag(flag: ~str) -> Crate {
fn flag(flag: ~str) -> Crate {
Crate {
flags: vec::append(copy self.flags, ~[flag]),
.. copy self
}
}
fn flags(flags: ~[~str]) -> Crate {
Crate {
flags: vec::append(copy self.flags, flags),
.. copy self
}
}
fn cfg(cfg: ~str) -> Crate {
Crate {
cfgs: vec::append(copy self.cfgs, ~[cfg]),
.. copy self
}
}
fn cfgs(cfgs: ~[~str]) -> Crate {
Crate {
cfgs: vec::append(copy self.cfgs, cfgs),
.. copy self
}
}
}
pub fn build(_targets: ~[Crate]) {
// TODO: magic
/// Create a crate target from a source file
pub fn Crate(file: ~str) -> Crate {
Crate {
file: file,
flags: ~[],
cfgs: ~[]
}
}
/**
* Get the working directory of the package script.
* Assumes that the package script has been compiled
* in is the working directory.
*/
fn work_dir() -> Path {
os::self_exe_path().get()
}
/**
* Get the source directory of the package (i.e.
* where the crates are located). Assumes
* that the cwd is changed to it before
* running this executable.
*/
fn src_dir() -> Path {
os::getcwd()
}
pub fn args() -> ~[~str] {
let mut args = os::args();
args.shift();
args.shift();
args
}
/// Build a set of crates, should be called once
pub fn build(crates: ~[Crate]) -> bool {
let dir = src_dir();
let work_dir = work_dir();
let mut success = true;
for crates.each |&crate| {
let path = &dir.push_rel(&Path(crate.file)).normalize();
note(fmt!("compiling %s", path.to_str()));
success = compile_crate(path, &work_dir, crate.flags, crate.cfgs,
false, false);
if !success { break; }
}
os::set_exit_status(101);
success
}
pub mod util {
// TODO: utilities for working with things like autotools
}

View File

@ -31,7 +31,6 @@ use std::getopts;
use std::net::url;
use send_map::linear::LinearMap;
use rustc::driver::{driver, session};
use rustc::metadata::{filesearch};
use syntax::{ast, attr, codemap, diagnostic, parse, visit};
use semver::Version;
@ -46,19 +45,33 @@ struct PackageScript {
name: ~str,
vers: Version,
crates: ~[~str],
deps: ~[(~str, Option<~str>)]
deps: ~[(~str, Option<~str>)],
input: driver::input,
sess: session::Session,
cfg: ast::crate_cfg,
crate: @ast::crate,
custom: bool
}
impl PackageScript {
static fn parse(parent: Path) -> Result<PackageScript, ~str> {
static fn parse(parent: &Path) -> Result<PackageScript, ~str> {
let script = parent.push(~"package.rs");
if !os::path_exists(&script) {
return result::Err(~"no package.rs file");
}
let sess = parse::new_parse_sess(None);
let crate = parse::parse_crate_from_file(&script, ~[], sess);
let binary = os::args()[0];
let options: @session::options = @{
binary: binary,
crate_type: session::bin_crate,
.. *session::basic_options()
};
let input = driver::file_input(script);
let sess = driver::build_session(options, diagnostic::emit);
let cfg = driver::build_configuration(sess, binary, input);
let {crate, _} = driver::compile_upto(sess, cfg, input,
driver::cu_parse, None);
let mut id = None;
let mut vers = None;
let mut crates = ~[];
@ -165,8 +178,19 @@ impl PackageScript {
}
}
let mut custom = false;
// If we hit a function, we assume they want to use
// the build API.
for crate.node.module.items.each |i| {
match i.node {
ast::item_fn(_, _, _, _) => custom = true,
_ => {}
}
}
if id.is_none() || vers.is_none() {
return result::Err(~"package's pkg attr is missing required data (id, vers)");
return result::Err(~"pkg attr without (id, vers) values");
}
let id = id.get();
@ -184,12 +208,35 @@ impl PackageScript {
name: name,
vers: vers,
crates: crates,
deps: deps
deps: deps,
input: input,
sess: sess,
cfg: cfg,
crate: crate,
custom: custom
})
}
// Build the bootstrap and run a command
// TODO: Use JIT once it works, it should be faster
fn run(cmd: ~str) -> int {
let work_dir = self.work_dir();
let input = self.input;
let sess = self.sess;
let cfg = self.cfg;
let crate = util::ready_crate(sess, self.crate);
let outputs = driver::build_output_filenames(input, &Some(work_dir),
&None, sess);
let exe = work_dir.push(~"package" + util::exe_suffix());
driver::compile_rest(sess, cfg, driver::cu_parse,
Some(outputs), Some(crate));
run::run_program(exe.to_str(), ~[cmd])
}
fn hash() -> ~str {
fmt!("%s-%s-%s", self.name, util::hash(self.id + self.vers.to_str()), self.vers.to_str())
fmt!("%s-%s-%s", self.name, util::hash(self.id + self.vers.to_str()),
self.vers.to_str())
}
fn work_dir() -> Path {
@ -198,7 +245,7 @@ impl PackageScript {
}
struct Ctx {
cfg: ast::crate_cfg,
cfgs: ~[~str],
mut dep_cache: LinearMap<~str, bool>
}
@ -229,13 +276,17 @@ impl Ctx {
}
match cmd {
~"build" => self.build(),
~"clean" => self.clean(),
~"build" => {
self.build(&os::getcwd(), true, false, false);
}
~"clean" => {
self.clean();
}
~"install" => {
self.install(if args.len() >= 1 { Some(args[0]) }
else { None },
if args.len() >= 2 { Some(args[1]) }
else { None }, false)
else { None }, false);
}
~"prefer" => {
if args.len() < 1 {
@ -244,9 +295,11 @@ impl Ctx {
let (name, vers) = sep_name_vers(args[0]);
self.prefer(name.get(), vers)
self.prefer(name.get(), vers);
}
~"test" => {
self.test();
}
~"test" => self.test(),
~"uninstall" => {
if args.len() < 1 {
return usage::uninstall();
@ -254,7 +307,7 @@ impl Ctx {
let (name, vers) = sep_name_vers(args[0]);
self.uninstall(name.get(), vers)
self.uninstall(name.get(), vers);
}
~"unprefer" => {
if args.len() < 1 {
@ -263,28 +316,27 @@ impl Ctx {
let (name, vers) = sep_name_vers(args[0]);
self.unprefer(name.get(), vers)
self.unprefer(name.get(), vers);
}
_ => fail ~"reached an unhandled command"
};
}
}
fn build() -> bool {
let dir = os::getcwd();
fn build(dir: &Path, verbose: bool, opt: bool,
test: bool) -> Option<PackageScript> {
let cwd = &os::getcwd();
let script = match PackageScript::parse(dir) {
result::Ok(script) => script,
result::Err(err) => {
util::error(err);
return false;
return None;
}
};
let work_dir = script.work_dir();
let mut success = true;
util::need_dir(&work_dir);
util::note(fmt!("building %s v%s (%s)", script.name, script.vers.to_str(),
script.id));
if script.deps.len() >= 1 {
util::note(~"installing dependencies");
@ -302,15 +354,31 @@ impl Ctx {
util::error(fmt!("building %s v%s failed: a dep wasn't installed",
script.name, script.vers.to_str()));
return false;
return None;
}
util::note(~"installed dependencies");
}
// Build imperative crates
os::change_dir(dir);
if script.custom && script.run(~"build") != 0 {
util::error(fmt!("building %s v%s failed: custom build logic failed",
script.name, script.vers.to_str()));
return None;
}
os::change_dir(cwd);
for script.crates.each |&crate| {
success = self.compile(&work_dir, &dir.push_rel(&Path(crate)), ~[],
false, false);
let crate = &dir.push_rel(&Path(crate)).normalize();
util::note(fmt!("compiling %s", crate.to_str()));
success = self.compile(crate, &work_dir, ~[],
~[], opt, test);
if !success { break; }
}
@ -319,159 +387,23 @@ impl Ctx {
util::error(fmt!("building %s v%s failed: a crate failed to compile",
script.name, script.vers.to_str()));
return false;
return None;
}
util::note(fmt!("built %s v%s", script.name, script.vers.to_str()));
if verbose {
util::note(fmt!("built %s v%s", script.name, script.vers.to_str()));
}
true
Some(script)
}
fn compile(dir: &Path, crate: &Path, flags: ~[~str],
opt: bool, test: bool) -> bool {
util::note(~"compiling " + crate.to_str());
let lib_dir = dir.push(~"lib");
let bin_dir = dir.push(~"bin");
let test_dir = dir.push(~"test");
let binary = os::args()[0];
let options: @session::options = @{
binary: binary,
addl_lib_search_paths: ~[util::root().push(~"lib")],
crate_type: session::unknown_crate,
optimize: if opt { session::Aggressive } else { session::No },
test: test,
.. *session::basic_options()
};
let input = driver::file_input(*crate);
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() {
util::error(~"crate's link attr is missing required data (name, vers, uuid)");
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,
_ => {
util::warn(~"unknown crate_type, falling back to lib");
false
}
}
}
None => {
util::warn(~"missing crate_type attr, assuming lib");
false
}
};
if test {
util::need_dir(&test_dir);
outputs = driver::build_output_filenames(input, &Some(test_dir),
&None, sess)
}
else if is_bin {
util::need_dir(&bin_dir);
#[cfg(windows)]
fn suffix() -> ~str { ~".exe" }
#[cfg(target_os = "linux")]
#[cfg(target_os = "android")]
#[cfg(target_os = "freebsd")]
#[cfg(target_os = "macos")]
fn suffix() -> ~str { ~"" }
let path = bin_dir.push(fmt!("%s-%s-%s%s", name,
util::hash(name + uuid + vers),
vers, suffix()));
outputs = driver::build_output_filenames(input, &None, &Some(path), sess);
} else {
util::need_dir(&lib_dir);
outputs = driver::build_output_filenames(input, &Some(lib_dir),
&None, sess)
}
driver::compile_upto(sess, cfg, input, driver::cu_everything,
Some(outputs));
true
fn compile(crate: &Path, dir: &Path, flags: ~[~str],
cfgs: ~[~str], opt: bool, test: bool) -> bool {
util::compile_crate(crate, dir, flags, cfgs, opt, test)
}
fn clean() -> bool {
let script = match PackageScript::parse(os::getcwd()) {
let script = match PackageScript::parse(&os::getcwd()) {
result::Ok(script) => script,
result::Err(err) => {
util::error(err);
@ -505,7 +437,8 @@ impl Ctx {
dir = os::getcwd();
} else {
let url = url.get();
let hash = util::hash(if !target.is_none() { url + target.get() } else { url });
let hash = util::hash(if !target.is_none() { url + target.get() }
else { url });
if self.dep_cache.contains_key(&hash) {
util::warn(~"already installed dep this run");
@ -528,53 +461,13 @@ impl Ctx {
}
}
let script = match PackageScript::parse(dir) {
result::Ok(script) => script,
result::Err(err) => {
util::error(err);
let script = match self.build(&dir, false, true, false) {
Some(script) => script,
None => {
return false;
}
};
let work_dir = script.work_dir();
util::need_dir(&work_dir);
util::note(fmt!("installing %s v%s (%s)", script.name, script.vers.to_str(),
script.id));
if script.deps.len() >= 1 {
util::note(~"installing dependencies");
for script.deps.each |&dep| {
let (url, target) = dep;
success = self.install(Some(url), target, false);
if !success { break; }
}
if !success {
util::error(fmt!("installing %s v%s failed: a dep wasn't installed",
script.name, script.vers.to_str()));
return false;
}
util::note(~"installed dependencies");
}
for script.crates.each |&crate| {
success = self.compile(&work_dir, &dir.push_rel(&Path(crate)), ~[],
true, false);
if !success { break; }
}
if !success {
util::error(fmt!("installing %s v%s failed: a crate failed to compile",
script.name, script.vers.to_str()));
return false;
}
let from_bin_dir = work_dir.push(~"bin");
let from_lib_dir = work_dir.push(~"lib");
let to_bin_dir = util::root().push(~"bin");
@ -728,58 +621,14 @@ impl Ctx {
}
fn test() -> bool {
let dir = os::getcwd();
let script = match PackageScript::parse(dir) {
result::Ok(script) => script,
result::Err(err) => {
util::error(err);
return false;
let script = match self.build(&os::getcwd(), false, false, true) {
Some(script) => script,
None => {
return false;
}
};
let work_dir = script.work_dir();
let test_dir = work_dir.push(~"test");
let mut success = true;
util::need_dir(&work_dir);
util::note(fmt!("testing %s v%s (%s)", script.name, script.vers.to_str(),
script.id));
if script.deps.len() >= 1 {
util::note(~"installing dependencies");
for script.deps.each |&dep| {
let (url, target) = dep;
success = self.install(Some(url), target, true);
if !success { break; }
}
if !success {
util::error(fmt!("testing %s v%s failed: a dep wasn't installed",
script.name, script.vers.to_str()));
return false;
}
util::note(~"installed dependencies");
}
for script.crates.each |&crate| {
success = self.compile(&work_dir, &dir.push_rel(&Path(crate)), ~[],
false, true);
if !success { break; }
}
if !success {
util::error(fmt!("testing %s v%s failed: a crate failed to compile",
script.name, script.vers.to_str()));
return false;
}
for os::walk_dir(&test_dir) |test| {
util::note(fmt!("running %s", test.to_str()));
@ -892,7 +741,7 @@ pub fn main() {
};
let help = getopts::opt_present(matches, ~"h") ||
getopts::opt_present(matches, ~"help");
let cfg = vec::append(getopts::opt_strs(matches, ~"cfg"),
let cfgs = vec::append(getopts::opt_strs(matches, ~"cfg"),
getopts::opt_strs(matches, ~"c"));
let mut args = copy matches.free;
@ -919,14 +768,8 @@ pub fn main() {
};
}
let mut cfg_specs = ~[];
for cfg.each |s| {
cfg_specs.push(attr::mk_word_item(/*bad*/copy *s));
}
Ctx {
cfg: cfg_specs,
cfgs: cfgs,
mut dep_cache: LinearMap()
}.run(cmd, args);
}
@ -934,3 +777,5 @@ pub fn main() {
pub use Crate = api::Crate;
pub use build = api::build;
pub use util = api::util;
pub use Listener = api::Listener;
pub use run = api::run;

View File

@ -1,3 +1,13 @@
// 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.
use core::io;
pub fn general() {

View File

@ -1,8 +1,23 @@
// 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.
use core::*;
use send_map::linear::LinearMap;
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;
use semver::Version;
use std::{json, term, sort};
use api::Listener;
pub struct Package {
id: ~str,
@ -41,6 +56,401 @@ pub fn parse_name(id: ~str) -> result::Result<~str, ~str> {
result::Ok(parts.last())
}
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 {
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
}
}
fold::noop_fold_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
}
}
fn fold_item(ctx: @ReadyCtx, &&item: @ast::item,
fold: fold::ast_fold) -> Option<@ast::item> {
ctx.path.push(item.ident);
if attr::find_attrs_by_name(item.attrs, ~"pkg_do").is_not_empty() {
ctx.fns.push(ListenerFn {
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);
let pkg_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(),
};
{
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);
let 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")]);
let listener_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
};
let inner_ty = @{
id: ctx.sess.next_node_id(),
node: ast::ty_vec(vec_mt),
span: dummy_sp()
};
@{
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)
};
let inner_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()
};
@{
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 cmd_lit = no_span(ast::lit_str(@path_name_i(path,
ctx.sess.parse_sess.interner)));
let cmd_expr_inner = @{
id: ctx.sess.next_node_id(),
callee_id: ctx.sess.next_node_id(),
node: ast::expr_lit(@cmd_lit),
span: span
};
let cmd_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(~"cmd"),
expr: @cmd_expr,
});
let cb_path = path_node_global(path);
let cb_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);
@{
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 {
let call_expr = {
id: ctx.sess.next_node_id(),
callee_id: ctx.sess.next_node_id(),
node: ast::expr_call(@fn_path_expr, ~[], false),
span: span
};
let call_stmt = no_span(ast::stmt_semi(@call_expr, ctx.sess.next_node_id()));
let wrapper_decl = {
inputs: ~[],
output: @{
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
});
@{
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 {
let ret_ty = {
id: ctx.sess.next_node_id(),
node: ast::ty_nil,
span: dummy_sp()
};
let 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);
let listener_path_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);
let listener_call_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);
let rustpkg_run_path_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);
@{
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> {
match semver::parse(vers) {
Some(vers) => result::Ok(vers),
@ -417,6 +827,155 @@ pub fn remove_pkg(pkg: &Package) -> bool {
true
}
pub fn compile_input(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];
let options: @session::options = @{
binary: binary,
crate_type: session::unknown_crate,
optimize: if opt { session::Aggressive } else { session::No },
test: test,
.. *session::basic_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()));
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 { ~"" }
pub fn compile_crate(crate: &Path, dir: &Path, flags: ~[~str],
cfgs: ~[~str], opt: bool, test: bool) -> bool {
compile_input(driver::file_input(*crate), dir, flags, cfgs, opt, test)
}
pub fn compile_str(code: ~str, dir: &Path, flags: ~[~str],
cfgs: ~[~str], opt: bool, test: bool) -> bool {
compile_input(driver::str_input(code), dir, flags, cfgs, opt, test)
}
#[cfg(windows)]
pub fn link_exe(_src: &Path, _dest: &Path) -> bool{
/* FIXME: Investigate how to do this on win32