about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
authorZack Corr <zack@z0w0.me>2013-01-23 11:19:13 +1000
committerGraydon Hoare <graydon@mozilla.com>2013-02-15 18:04:10 -0800
commit707944184304acbd81c026156a04b98570c6ef9c (patch)
treef3cd82efda760e524f64b6b6a88a16fab988982a /src
parentbd28fa4af5b5200b274b8e965a422174fb7ad354 (diff)
rustpkg: Add preliminary imperative API support
Diffstat (limited to 'src')
-rw-r--r--src/librustc/driver/driver.rs28
-rw-r--r--src/librustpkg/api.rs116
-rw-r--r--src/librustpkg/rustpkg.rc379
-rw-r--r--src/librustpkg/usage.rs10
-rw-r--r--src/librustpkg/util.rs559
5 files changed, 811 insertions, 281 deletions
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<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 };
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 @@
+// 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
+        }
+    }
+}
+
+/// Create a crate target from a source file
+pub fn Crate(file: ~str) -> Crate {
+    Crate {
+        file: file,
+        flags: ~[],
+        cfgs: ~[]
+    }
 }
 
-pub fn build(_targets: ~[Crate]) {
-    // TODO: magic
+/** 
+ * 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<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,176 +354,56 @@ 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");
         }
 
-        for script.crates.each |&crate| {
-            success = self.compile(&work_dir, &dir.push_rel(&Path(crate)), ~[],
-                                   false, false);
-
-            if !success { break; }
-        }
+        // Build imperative crates
+        os::change_dir(dir);
 
-        if !success {
-            util::error(fmt!("building %s v%s failed: a crate failed to compile",
+        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 false;
+            return None;
         }
 
-        util::note(fmt!("built %s v%s", script.name, script.vers.to_str()));
-
-        true
-    }
-
-    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)
-        }
+        os::change_dir(cwd);
 
-        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);
+        for script.crates.each |&crate| {
+            let crate = &dir.push_rel(&Path(crate)).normalize();
 
-                            name = n;
-                            vers = v;
-                            uuid = u;
-                        }
-                        _ => {}
-                    }
-                }
-                _ => {}
-            }
-        }
+            util::note(fmt!("compiling %s", crate.to_str()));
 
-        if name.is_none() || vers.is_none() || uuid.is_none() {
-            util::error(~"crate's link attr is missing required data (name, vers, uuid)");
+            success = self.compile(crate, &work_dir, ~[],
+                                   ~[], opt, test);
 
-            return false;
+            if !success { break; }
         }
 
-        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);
+        if !success {
+            util::error(fmt!("building %s v%s failed: a crate failed to compile",
+                             script.name, script.vers.to_str()));
 
-            outputs = driver::build_output_filenames(input, &Some(test_dir),
-                                                     &None, sess)
+            return None;
         }
-        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)
+        if verbose {
+            util::note(fmt!("built %s v%s", script.name, script.vers.to_str()));
         }
 
-        driver::compile_upto(sess, cfg, input, driver::cu_everything,
-                             Some(outputs));
+        Some(script)
+    }
 
-        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 <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() {
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 <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