diff --git a/src/librustc/driver/driver.rs b/src/librustc/driver/driver.rs index f1712d05562..5fc284bc9fc 100644 --- a/src/librustc/driver/driver.rs +++ b/src/librustc/driver/driver.rs @@ -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) - -> {crate: @ast::crate, tcx: Option} { +// 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, + curr: Option<@ast::crate>) + -> {crate: @ast::crate, tcx: Option} { 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) + -> {crate: @ast::crate, tcx: Option} { + 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, output: &Option) { - let upto = if sess.opts.parse_only { cu_parse } else if sess.opts.no_trans { cu_no_trans } else { cu_everything }; diff --git a/src/librustpkg/api.rs b/src/librustpkg/api.rs index 6dd238b3d5c..d431db27e68 100644 --- a/src/librustpkg/api.rs +++ b/src/librustpkg/api.rs @@ -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 or the MIT license +// , 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 } diff --git a/src/librustpkg/rustpkg.rc b/src/librustpkg/rustpkg.rc index aec3f21eb6f..9d29bf76d6c 100644 --- a/src/librustpkg/rustpkg.rc +++ b/src/librustpkg/rustpkg.rc @@ -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 { + static fn parse(parent: &Path) -> Result { 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 { + 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; diff --git a/src/librustpkg/usage.rs b/src/librustpkg/usage.rs index 28694948511..cad5aa650db 100644 --- a/src/librustpkg/usage.rs +++ b/src/librustpkg/usage.rs @@ -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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + use core::io; pub fn general() { diff --git a/src/librustpkg/util.rs b/src/librustpkg/util.rs index 16ded3c640b..b7d6ad17f05 100644 --- a/src/librustpkg/util.rs +++ b/src/librustpkg/util.rs @@ -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 or the MIT license +// , 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: T) -> ast::spanned { + 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 { 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