about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/librustpkg/README.txt4
-rw-r--r--src/librustpkg/rustpkg.rc995
-rw-r--r--src/librustpkg/util.rs589
3 files changed, 636 insertions, 952 deletions
diff --git a/src/librustpkg/README.txt b/src/librustpkg/README.txt
new file mode 100644
index 00000000000..eacf07e01ea
--- /dev/null
+++ b/src/librustpkg/README.txt
@@ -0,0 +1,4 @@
+Right now (2013-04-11), only one package works, the branch of rust-sdl at:
+https://github.com/catamorphism/rust-sdl/tree/new-rustpkg
+
+and only one command works, "build".
diff --git a/src/librustpkg/rustpkg.rc b/src/librustpkg/rustpkg.rc
index c0d77f60d69..83f5c78af43 100644
--- a/src/librustpkg/rustpkg.rc
+++ b/src/librustpkg/rustpkg.rc
@@ -27,42 +27,51 @@ extern mod rustc(vers = "0.7-pre");
 extern mod syntax(vers = "0.7-pre");
 
 use core::*;
+pub use core::path::Path;
 use core::container::Map;
 use core::hashmap::HashMap;
 use core::io::WriterUtil;
 use rustc::driver::{driver, session};
 use rustc::metadata::filesearch;
 use std::net::url;
-use std::{json, semver, getopts};
-use syntax::codemap::spanned;
+use std::{getopts};
 use syntax::{ast, diagnostic};
-use util::Package;
+use util::{ExitCode, Pkg, PkgId};
 
 mod usage;
 mod util;
 
-struct PackageScript {
-    id: ~str,
-    name: ~str,
-    vers: semver::Version,
-    crates: ~[~str],
-    deps: ~[(~str, Option<~str>)],
+/// A PkgScript represents user-supplied custom logic for
+/// special build hooks. This only exists for packages with
+/// an explicit package script.
+struct PkgScript {
+    /// Uniquely identifies this package
+    id: PkgId,
+    // Used to have this field:    deps: ~[(~str, Option<~str>)]
+    // but I think it shouldn't be stored here
+    /// The contents of the package script: either a file path,
+    /// or a string containing the text of the input
     input: driver::input,
+    /// The session to use *only* for compiling the custom
+    /// build script
     sess: session::Session,
+    /// The config for compiling the custom build script
     cfg: ast::crate_cfg,
+    /// The crate for the custom build script
     crate: @ast::crate,
-    custom: bool
+    /// Directory in which to store build output
+    build_dir: Path
 }
 
-impl PackageScript {
-    fn parse(parent: &Path) -> Result<PackageScript, ~str> {
-        let script = parent.push(~"pkg.rs");
-
-        if !os::path_exists(&script) {
-            return result::Err(~"no pkg.rs file");
-        }
-
+impl PkgScript {
+    /// Given the path name for a package script
+    /// and a package ID, parse the package script into
+    /// a PkgScript that we can then execute
+    fn parse(script: Path, id: PkgId) -> PkgScript {
+        // Get the executable name that was invoked
         let binary = os::args()[0];
+        // Build the rustc session data structures to pass
+        // to the compiler
         let options = @session::options {
             binary: binary,
             crate_type: session::bin_crate,
@@ -73,190 +82,121 @@ impl PackageScript {
         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 = ~[];
-        let mut deps = ~[];
-
-        fn load_pkg_attr(mis: ~[@ast::meta_item]) -> (Option<~str>,
-                                                      Option<~str>) {
-            let mut id = None;
-            let mut vers = None;
-
-            for mis.each |a| {
-                match a.node {
-                    ast::meta_name_value(v, spanned {
-                                                node: ast::lit_str(s),
-                                                span: _}) => {
-                        match *v {
-                            ~"id" => id = Some(*s),
-                            ~"vers" => vers = Some(*s),
-                            _ => ()
-                        }
-                    }
-                    _ => {}
-                }
-            }
-
-            (id, vers)
-        }
-
-        fn load_pkg_dep_attr(mis: ~[@ast::meta_item]) -> (Option<~str>,
-                                                          Option<~str>) {
-            let mut url = None;
-            let mut target = None;
-
-            for mis.each |a| {
-                match a.node {
-                    ast::meta_name_value(v, spanned {
-                                                node: ast::lit_str(s),
-                                                span: _}) => {
-                        match *v {
-                            ~"url" => url = Some(*s),
-                            ~"target" => target = Some(*s),
-                            _ => ()
-                        }
-                    }
-                    _ => {}
-                }
-            }
-
-            (url, target)
-        }
-
-        fn load_pkg_crate_attr(mis: ~[@ast::meta_item]) -> Option<~str> {
-            let mut file = None;
-
-            for mis.each |a| {
-                match a.node {
-                    ast::meta_name_value(v, spanned {
-                                                node: ast::lit_str(s),
-                                                span: _}) => {
-                        match *v {
-                            ~"file" => file = Some(*s),
-                            _ => ()
-                        }
-                    }
-                    _ => {}
-                }
-            }
-
-            file
-        }
-
-        for crate.node.attrs.each |a| {
-            match a.node.value.node {
-                ast::meta_list(v, mis) => {
-                    match *v {
-                        ~"pkg" => {
-                            let (i, v) = load_pkg_attr(mis);
-
-                            id = i;
-                            vers = v;
-                        }
-                        ~"pkg_dep" => {
-                            let (u, t) = load_pkg_dep_attr(mis);
-
-                            if u.is_none() {
-                                fail!(~"pkg_dep attr without a url value");
-                            }
-
-                            deps.push((u.get(), t));
-                        }
-                        ~"pkg_crate" => {
-                            let f = load_pkg_crate_attr(mis);
-
-                            if f.is_none() {
-                                fail!(~"pkg_file attr without a file value");
-                            }
-
-                            crates.push(f.get());
-                        }
-                        _ => {}
-                    }
-                }
-                _ => {}
-            }
-        }
-
-        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;
-
-                    break;
-                }
-                _ => {}
-            }
-        }
-
-        if id.is_none() || vers.is_none() {
-            return result::Err(~"pkg attr without (id, vers) values");
-        }
+        let work_dir = dest_dir(id);
 
-        let id = id.get();
-        let name = match util::parse_name(id) {
-            result::Ok(name) => name,
-            result::Err(err) => return result::Err(err)
-        };
-        let vers = match util::parse_vers(vers.get()) {
-            result::Ok(vers) => vers,
-            result::Err(err) => return result::Err(err)
-        };
+        debug!("Returning package script with id %?", id);
 
-        result::Ok(PackageScript {
+        PkgScript {
             id: id,
-            name: name,
-            vers: vers,
-            crates: crates,
-            deps: deps,
             input: input,
             sess: sess,
             cfg: cfg,
             crate: crate,
-            custom: custom
-        })
+            build_dir: work_dir
+        }
     }
 
-    // Build the bootstrap and run a command
+    /// Run the contents of this package script, where <what>
+    /// is the command to pass to it (e.g., "build", "clean", "install")
+    /// Returns a pair of an exit code and list of configs (obtained by
+    /// calling the package script's configs() function if it exists
     // FIXME (#4432): Use workcache to only compile the script when changed
-    fn run(&self, cmd: ~str, test: bool) -> int {
-        let work_dir = self.work_dir();
-        let input = self.input;
+    fn run_custom(&self, what: ~str) -> (~[~str], ExitCode) {
+        debug!("run_custom: %s", what);
         let sess = self.sess;
-        let cfg = self.cfg;
+
+        debug!("Working directory = %s", self.build_dir.to_str());
+        // Collect together any user-defined commands in the package script
         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(~"pkg" + util::exe_suffix());
-        let root = filesearch::get_rustpkg_sysroot().get().pop().pop();
-
-        driver::compile_rest(sess, cfg, driver::cu_parse,
-                             Some(outputs), Some(crate));
-        run::run_program(exe.to_str(), ~[root.to_str(), cmd, test.to_str()])
+        debug!("Building output filenames with script name %s",
+               driver::source_name(self.input));
+        match filesearch::get_rustpkg_sysroot() {
+            Ok(r) => {
+                let root = r.pop().pop().pop().pop(); // :-\
+                debug!("Root is %s, calling compile_rest", root.to_str());
+                util::compile_crate_from_input(self.input, Some(self.build_dir),
+                                               sess, Some(crate), os::args()[0]);
+                let exe = self.build_dir.push(~"pkg" + util::exe_suffix());
+                debug!("Running program: %s %s %s", exe.to_str(), root.to_str(), what);
+                let status = run::run_program(exe.to_str(), ~[root.to_str(), what]);
+                if status != 0 {
+                    return (~[], status);
+                }
+                else {
+                    debug!("Running program (configs): %s %s %s",
+                           exe.to_str(), root.to_str(), ~"configs");
+                    let output = run::program_output(exe.to_str(), ~[root.to_str(), ~"configs"]);
+                    // Run the configs() function to get the configs
+                    let mut cfgs = ~[];
+                    for str::each_word(output.out) |w| {
+                        cfgs.push(w.to_owned());
+                    }
+                    (cfgs, output.status)
+                }
+            }
+            Err(e) => {
+                fail!(fmt!("Running package script, couldn't find rustpkg sysroot (%s)",
+                           e))
+            }
+        }
     }
 
     fn hash(&self) -> ~str {
-        fmt!("%s-%s-%s", self.name, util::hash(self.id + self.vers.to_str()),
-                         self.vers.to_str())
+        self.id.hash()
     }
 
-    fn work_dir(&self) -> Path {
-        util::root().push(~"work").push(self.hash())
-    }
 }
 
 struct Ctx {
-    cfgs: ~[~str],
+    // I'm not sure what this is for
     json: bool,
-    dep_cache: @mut HashMap<~str, bool>
+    // Cache of hashes of things already installed
+    // though I'm not sure why the value is a bool
+    dep_cache: @mut HashMap<~str, bool>,
+}
+
+
+/// Returns the output directory to use.
+/// Right now is always the default, should
+/// support changing it.
+fn dest_dir(pkgid: PkgId) -> Path {
+    default_dest_dir(&pkgid.path).expect(
+        ~"couldn't make default dir?!")
+}
+
+/// Returns the default output directory for compilation.
+/// Creates that directory if it doesn't exist.
+fn default_dest_dir(pkg_dir: &Path) -> Option<Path> {
+    use core::libc::consts::os::posix88::{S_IRUSR, S_IWUSR, S_IXUSR};
+
+    // For now: assumes that pkg_dir exists and is relative
+    // to the CWD. Change this later when we do path searching.
+    let rslt = pkg_dir.push("build");
+    let is_dir = os::path_is_dir(&rslt);
+    if os::path_exists(&rslt) {
+        if is_dir {
+            Some(rslt)
+        }
+        else {
+            util::error(fmt!("%s is not a directory", rslt.to_str()));
+            None
+        }
+    }
+    else {
+        // Create it
+        if os::make_dir(&rslt, (S_IRUSR | S_IWUSR | S_IXUSR) as i32) {
+            Some(rslt)
+        }
+        else {
+            util::error(fmt!("Could not create directory %s",
+                             rslt.to_str()));
+            None // ??? should probably use conditions
+        }
+    }
 }
 
 impl Ctx {
+
     fn run(&self, cmd: ~str, args: ~[~str]) {
         let root = util::root();
 
@@ -281,17 +221,64 @@ impl Ctx {
 
         match cmd {
             ~"build" => {
-                self.build(&os::getcwd(), true, false, false);
+                if args.len() < 1 {
+                    return usage::build();
+                }
+                // The package id is presumed to be the first command-line
+                // argument
+                let pkgid = PkgId::new(args[0]);
+                // Should allow the build directory to be configured.
+                // Right now it's always the "build" subdirectory in
+                // the package directory
+                let dst_dir = dest_dir(pkgid);
+                debug!("Destination dir = %s", dst_dir.to_str());
+                // Right now, we assume the pkgid path is a valid dir
+                // relative to the CWD. In the future, we should search
+                // paths
+                let cwd = os::getcwd().normalize();
+                debug!("Current working directory = %?", cwd);
+
+                // Find crates inside the workspace
+                let mut src = PkgSrc::new(&cwd, &dst_dir, &pkgid);
+                debug!("Package src = %?", src);
+                src.find_crates();
+
+                // Is there custom build logic? If so, use it
+                let pkg_src_dir = cwd.push_rel(&pkgid.path);
+                debug!("Package source directory = %s", pkg_src_dir.to_str());
+                let cfgs = match src.package_script_option(&pkg_src_dir) {
+                    Some(package_script_path) => {
+                        let pscript = PkgScript::parse(package_script_path,
+                                                       pkgid);
+                        // Limited right now -- we're only running the post_build
+                        // hook and probably fail otherwise
+                        // also post_build should be called pre_build
+                        let (cfgs, hook_result) = pscript.run_custom(~"post_build");
+                        debug!("Command return code = %?", hook_result);
+                        if hook_result != 0 {
+                            fail!(fmt!("Error running custom build command"))
+                        }
+                        // otherwise, the package script succeeded
+                        cfgs
+                    }
+                    None => {
+                        debug!("No package script, continuing");
+                        ~[]
+                    }
+                };
+                src.build(&dst_dir, cfgs);
             }
             ~"clean" => {
                 self.clean();
             }
             ~"do" => {
-                if args.len() < 1 {
+                if args.len() < 2 {
                     return usage::do_cmd();
                 }
 
-                self.do_cmd(args[0]);
+                if !self.do_cmd(args[0], args[1]) {
+                    fail!(~"a command failed!");
+                }
             }
             ~"info" => {
                 self.info();
@@ -336,7 +323,7 @@ impl Ctx {
         }
     }
 
-    fn do_cmd(&self, cmd: ~str) -> bool {
+    fn do_cmd(&self, cmd: ~str, pkgname: ~str) -> bool {
         match cmd {
             ~"build" | ~"test" => {
                 util::error(~"that command cannot be manually called");
@@ -347,230 +334,85 @@ impl Ctx {
         }
 
         let cwd = &os::getcwd();
-        let script = match PackageScript::parse(cwd) {
-            result::Ok(script) => script,
-            result::Err(err) => {
-                util::error(err);
-
-                return false;
-            }
-        };
-        let status = script.run(cmd, false);
-
-        if status == 42 {
-            util::error(~"no fns are listening for that cmd");
-
-            return false;
-        }
-
-        status == 0
-    }
-
-    fn build(&self, 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 None;
-            }
-        };
-        let work_dir = script.work_dir();
-        let mut success = true;
-
-        util::need_dir(&work_dir);
-
-        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!("building %s v%s failed: a dep wasn't installed",
-                         script.name, script.vers.to_str()));
-
-                return None;
+        let pkgid = PkgId::new(pkgname);
+        // Always use the "build" subdirectory of the package dir,
+        // but we should allow this to be configured
+        let dst_dir = dest_dir(pkgid);
+
+        let mut src = PkgSrc::new(cwd, &dst_dir, &pkgid);
+        match src.package_script_option(cwd) {
+            Some(script_path) => {
+                let script = PkgScript::parse(script_path, pkgid);
+                let (_, status) = script.run_custom(cmd); // Ignore cfgs?
+                if status == 42 { // ???
+                    util::error(~"no fns are listening for that cmd");
+                    return false;
+                }
+                status == 0
             }
-
-            util::note(~"installed dependencies");
-        }
-
-        // Build imperative crates
-        os::change_dir(dir);
-
-        if script.custom {
-            let status = script.run(~"build", test);
-
-            if status != 0 && status != 42 {
-                util::error(
-                    fmt!("building %s v%s failed: custom logic failed (%d)",
-                         script.name, script.vers.to_str(), status));
-
-                return None;
+            None => {
+                util::error(fmt!("invoked `do`, but there is no package script in %s",
+                                 cwd.to_str()));
+                false
             }
         }
+    }
 
-        os::change_dir(cwd);
-
-        for script.crates.each |&crate| {
-            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; }
-        }
-
-        if !success {
-            util::error(
-                fmt!("building %s v%s failed: a crate failed to compile",
-                     script.name, script.vers.to_str()));
-
-            return None;
-        }
-
-        if verbose {
-            util::note(fmt!("built %s v%s", script.name,
-                            script.vers.to_str()));
-        }
-
-        Some(script)
+    fn build(&self, _dir: &Path, _verbose: bool, _opt: bool,
+             _test: bool) -> Option<PkgScript> {
+ // either not needed anymore,
+ // or needed only when we don't have a package script. Not sure which one.
+        fail!();
     }
 
-    fn compile(&self, crate: &Path, dir: &Path, flags: ~[~str],
-               cfgs: ~[~str], opt: bool, test: bool) -> bool {
-        util::compile_crate(None, crate, dir, flags, cfgs, opt, test)
+    fn compile(&self, _crate: &Path, _dir: &Path, _flags: ~[~str],
+               _cfgs: ~[~str], _opt: bool, _test: bool) -> bool {
+        // What's the difference between build and compile?
+        fail!(~"compile not yet implemented");
     }
 
     fn clean(&self) -> bool {
-        let script = match PackageScript::parse(&os::getcwd()) {
-            result::Ok(script) => script,
-            result::Err(err) => {
-                util::error(err);
-
-                return false;
-            }
-        };
-        let dir = script.work_dir();
-
-        util::note(fmt!("cleaning %s v%s (%s)", script.name,
-                        script.vers.to_str(), script.id));
-
-        if os::path_exists(&dir) {
-            util::remove_dir_r(&dir);
-            util::note(fmt!("removed %s", dir.to_str()));
-        }
-
-        util::note(fmt!("cleaned %s v%s", script.name,
-                                          script.vers.to_str()));
-
-        true
+        // stub
+        fail!();
     }
 
     fn info(&self) {
-        if self.json {
-            match PackageScript::parse(&os::getcwd()) {
-                result::Ok(script) => {
-                    let mut map = ~HashMap::new();
-
-                    map.insert(~"id", json::String(script.id));
-                    map.insert(~"name", json::String(script.name));
-                    map.insert(~"vers", json::String(script.vers.to_str()));
-                    map.insert(~"deps", json::List(do script.deps.map |&dep| {
-                        let (url, target) = dep;
-                        let mut inner = ~HashMap::new();
-
-                        inner.insert(~"url", json::String(url));
-
-                        if !target.is_none() {
-                            inner.insert(~"target",
-                                         json::String(target.get()));
-                        }
-
-                        json::Object(inner)
-                    }));
-
-                    io::println(json::to_pretty_str(&json::Object(map)));
-                }
-                result::Err(_) => io::println(~"{}")
-            }
-        } else {
-            let script = match PackageScript::parse(&os::getcwd()) {
-                result::Ok(script) => script,
-                result::Err(err) => {
-                    util::error(err);
-
-                    return;
-                }
-            };
-
-            util::note(fmt!("id: %s", script.id));
-            util::note(fmt!("name: %s", script.name));
-            util::note(fmt!("vers: %s", script.vers.to_str()));
-            util::note(fmt!("deps: %s",
-                            if script.deps.len() > 0 {
-                                ~""
-                            } else {
-                                ~"none"
-                            }));
-
-            for script.deps.each |&dep| {
-                let (url, target) = dep;
-
-                util::note(fmt!("  <%s> (%s)", url, match target {
-                    Some(target) => target,
-                    None => ~""
-                }));
-            }
-        }
+        // stub
+        fail!();
     }
 
     fn install(&self, url: Option<~str>,
                target: Option<~str>, cache: bool) -> bool {
-        let mut success;
-        let mut dir;
-
-        if url.is_none() {
-            util::note(~"installing from the cwd");
-
-            dir = os::getcwd();
-        } else {
-            let url = url.get();
-            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");
-
-                return true;
+       let dir = match url {
+            None => {
+                util::note(~"installing from the cwd");
+                os::getcwd()
             }
+            Some(url) => {
+                let hash = util::hash(if !target.is_none() {
+                    url + target.get()
+                }
+                else { url });
 
-            self.dep_cache.insert(hash, true);
+                if self.dep_cache.contains_key(&hash) {
+                    util::warn(~"already installed dep this run");
+                    return true;
+                }
 
-            dir = util::root().push(~"tmp").push(hash);
+                self.dep_cache.insert(hash, true);
 
-            if cache && os::path_exists(&dir) {
-                return true;
-            }
+                let dir = util::root().push(~"tmp").push(hash);
 
-            success = self.fetch(&dir, url, target);
+                if cache && os::path_exists(&dir) {
+                    return true;
+                }
 
-            if !success {
-                return false;
+                if !self.fetch(&dir, url, target) {
+                    return false;
+                }
+                dir
             }
-        }
+        };
 
         let script = match self.build(&dir, false, true, false) {
             Some(script) => script,
@@ -578,7 +420,7 @@ impl Ctx {
                 return false;
             }
         };
-        let work_dir = script.work_dir();
+        let work_dir = script.build_dir;
         let from_bin_dir = work_dir.push(~"bin");
         let from_lib_dir = work_dir.push(~"lib");
         let to_bin_dir = util::root().push(~"bin");
@@ -600,15 +442,13 @@ impl Ctx {
             libs.push(to.to_str());
         }
 
-        let package = Package {
+        let package = Pkg {
             id: script.id,
-            vers: script.vers,
             bins: bins,
             libs: libs
         };
 
-        util::note(fmt!("installed %s v%s", script.name,
-                                            script.vers.to_str()));
+        util::note(fmt!("installed %s", script.id.to_str()));
         util::add_pkg(&package);
 
         true
@@ -713,17 +553,9 @@ impl Ctx {
                 return false;
             }
         };
-        let name = match util::parse_name(package.id) {
-            result::Ok(name) => name,
-            result::Err(err) => {
-                util::error(err);
-
-                return false;
-            }
-        };
+        let name = package.id.path.to_str(); // ???
 
-        util::note(fmt!("preferring %s v%s (%s)", name, package.vers.to_str(),
-                                                  package.id));
+        util::note(fmt!("preferring %s v%s", name, package.id.version.to_str()));
 
         let bin_dir = util::root().push(~"bin");
 
@@ -740,7 +572,7 @@ impl Ctx {
             util::note(fmt!("linked %s", out.to_str()));
         }
 
-        util::note(fmt!("preferred %s v%s", name, package.vers.to_str()));
+        util::note(fmt!("preferred %s v%s", name, package.id.version.to_str()));
 
         true
     }
@@ -752,126 +584,24 @@ impl Ctx {
                 return false;
             }
         };
-        let work_dir = script.work_dir();
-        let test_dir = work_dir.push(~"test");
-
-        for os::walk_dir(&test_dir) |test| {
-            util::note(fmt!("running %s", test.to_str()));
-
-            let status = run::run_program(test.to_str(), ~[]);
-
-            if status != 0 {
-                os::set_exit_status(status);
-            }
-        }
-
-        // Run custom test listener
-        if script.custom {
-            let status = script.run(~"test", false);
-
-            if status != 0 && status != 42 {
-                util::error(
-                    fmt!("testing %s v%s failed: custom logic failed (%d)",
-                         script.name, script.vers.to_str(), status));
 
-                os::set_exit_status(status);
-            }
-        }
-
-        util::note(fmt!("tested %s v%s", script.name, script.vers.to_str()));
-
-        true
+        // To do
+        util::note(fmt!("Would test %s, but this is a dry run",
+                        script.id.to_str()));
+        false
     }
 
-    fn uninstall(&self, id: ~str, vers: Option<~str>) -> bool {
-        let package = match util::get_pkg(id, vers) {
-            result::Ok(package) => package,
-            result::Err(err) => {
-                util::error(err);
-
-                return false;
-            }
-        };
-        let name = match util::parse_name(package.id) {
-            result::Ok(name) => name,
-            result::Err(err) => {
-                util::error(err);
-
-                return false;
-            }
-        };
-
-        util::note(fmt!("uninstalling %s v%s (%s)", name,
-                        package.vers.to_str(), package.id));
-
-        for vec::append(package.bins, package.libs).each |&file| {
-            let path = Path(file);
-
-            if os::path_exists(&path) {
-                if os::remove_file(&path) {
-                    util::note(fmt!("removed %s", path.to_str()));
-                } else {
-                    util::error(fmt!("could not remove %s", path.to_str()));
-                }
-            }
-        }
-
-        util::note(fmt!("uninstalled %s v%s", name, package.vers.to_str()));
-        util::remove_pkg(&package);
-
-        true
+    fn uninstall(&self, _id: ~str, _vers: Option<~str>) -> bool {
+        fail!(~"uninstall not yet implemented");
     }
 
-    fn unprefer(&self, id: ~str, vers: Option<~str>) -> bool {
-        let package = match util::get_pkg(id, vers) {
-            result::Ok(package) => package,
-            result::Err(err) => {
-                util::error(err);
-
-                return false;
-            }
-        };
-        let name = match util::parse_name(package.id) {
-            result::Ok(name) => name,
-            result::Err(err) => {
-                util::error(err);
-
-                return false;
-            }
-        };
-
-        util::note(fmt!("unpreferring %s v%s (%s)", name,
-                        package.vers.to_str(), package.id));
-
-        let bin_dir = util::root().push(~"bin");
-
-        for package.bins.each |&bin| {
-            let path = Path(bin);
-            let mut name = None;
-            for str::each_split_char(path.file_path().to_str(), '-') |s| {
-                name = Some(s.to_owned());
-                break;
-            }
-            let out = bin_dir.push(name.unwrap());
-
-            if os::path_exists(&out) {
-                if os::remove_file(&out) {
-                    util::note(fmt!("unlinked %s", out.to_str()));
-                } else {
-                    util::error(fmt!("could not unlink %s", out.to_str()));
-                }
-            }
-        }
-
-        util::note(fmt!("unpreferred %s v%s", name, package.vers.to_str()));
-
-        true
+    fn unprefer(&self, _id: ~str, _vers: Option<~str>) -> bool {
+        fail!(~"unprefer not yet implemented");
     }
 }
 
 pub fn main() {
-    io::println("WARNING: The Rust package manager is experimental and may");
-    io::println("be unstable.");
+    io::println("WARNING: The Rust package manager is experimental and may be unstable");
 
     let args = os::args();
     let opts = ~[getopts::optflag(~"h"), getopts::optflag(~"help"),
@@ -889,8 +619,6 @@ pub fn main() {
                getopts::opt_present(matches, ~"help");
     let json = getopts::opt_present(matches, ~"j") ||
                getopts::opt_present(matches, ~"json");
-    let cfgs = vec::append(getopts::opt_strs(matches, ~"cfg"),
-                           getopts::opt_strs(matches, ~"c"));
     let mut args = copy matches.free;
 
     args.shift();
@@ -919,7 +647,6 @@ pub fn main() {
     }
 
     Ctx {
-        cfgs: cfgs,
         json: json,
         dep_cache: @mut HashMap::new()
     }.run(cmd, args);
@@ -927,7 +654,7 @@ pub fn main() {
 
 /// A crate is a unit of Rust code to be compiled into a binary or library
 pub struct Crate {
-    file: ~str,
+    file: Path,
     flags: ~[~str],
     cfgs: ~[~str]
 }
@@ -959,28 +686,37 @@ pub fn run(listeners: ~[Listener]) {
 }
 
 pub impl Crate {
-    pub fn flag(&self, flag: ~str) -> Crate {
+
+    fn new(p: &Path) -> Crate {
+        Crate {
+            file: copy *p,
+            flags: ~[],
+            cfgs: ~[]
+        }
+    }
+
+    fn flag(&self, flag: ~str) -> Crate {
         Crate {
             flags: vec::append(copy self.flags, ~[flag]),
             .. copy *self
         }
     }
 
-    pub fn flags(&self, flags: ~[~str]) -> Crate {
+    fn flags(&self, flags: ~[~str]) -> Crate {
         Crate {
             flags: vec::append(copy self.flags, flags),
             .. copy *self
         }
     }
 
-    pub fn cfg(&self, cfg: ~str) -> Crate {
+    fn cfg(&self, cfg: ~str) -> Crate {
         Crate {
             cfgs: vec::append(copy self.cfgs, ~[cfg]),
             .. copy *self
         }
     }
 
-    pub fn cfgs(&self, cfgs: ~[~str]) -> Crate {
+    fn cfgs(&self, cfgs: ~[~str]) -> Crate {
         Crate {
             cfgs: vec::append(copy self.cfgs, cfgs),
             .. copy *self
@@ -988,15 +724,6 @@ pub impl Crate {
     }
 }
 
-/// 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
@@ -1016,30 +743,208 @@ pub fn src_dir() -> Path {
     os::getcwd()
 }
 
-/// Build a set of crates, should be called once
-pub fn build(crates: ~[Crate]) -> bool {
-    let args = os::args();
-    let dir = src_dir();
-    let work_dir = work_dir();
-    let mut success = true;
-    let sysroot = Path(args[1]);
-    let test = args[3] == ~"true";
+condition! {
+    bad_pkg_id: (super::Path, ~str) -> ::util::PkgId;
+}
 
-    for crates.each |&crate| {
-        let path = &dir.push_rel(&Path(crate.file)).normalize();
+// An enumeration of the unpacked source of a package workspace.
+// This contains a list of files found in the source workspace.
+pub struct PkgSrc {
+    root: Path, // root of where the package source code lives
+    dst_dir: Path, // directory where we will put the compiled output
+    id: PkgId,
+    libs: ~[Crate],
+    mains: ~[Crate],
+    tests: ~[Crate],
+    benchs: ~[Crate],
+}
 
-        util::note(fmt!("compiling %s", path.to_str()));
+condition! {
+    bad_path: (super::Path, ~str) -> super::Path;
+}
 
-        success = util::compile_crate(Some(sysroot), path, &work_dir,
-                                      crate.flags, crate.cfgs,
-                                      false, test);
+condition! {
+    build_err: (~str) -> ();
+}
 
-        if !success { break; }
+impl PkgSrc {
+
+
+    fn new(src_dir: &Path, dst_dir: &Path,
+                  id: &PkgId) -> PkgSrc {
+        PkgSrc {
+            root: copy *src_dir,
+            dst_dir: copy *dst_dir,
+            id: copy *id,
+            libs: ~[],
+            mains: ~[],
+            tests: ~[],
+            benchs: ~[]
+        }
     }
 
-    if !success {
-        os::set_exit_status(101);
+
+    fn check_dir(&self) -> Path {
+        use bad_path::cond;
+
+        debug!("Pushing onto root: %s | %s", self.id.path.to_str(),
+               self.root.to_str());
+
+        let dir = self.root.push_rel(&self.id.path).normalize();
+
+        debug!("Checking dir: %s", dir.to_str());
+
+        if !os::path_exists(&dir) {
+            return cond.raise((dir, ~"missing package dir"));
+        }
+
+        if !os::path_is_dir(&dir) {
+            return cond.raise((dir, ~"supplied path for package dir is a \
+                                      non-directory"));
+        }
+
+        dir
+    }
+
+
+    fn has_pkg_file(&self) -> bool {
+        let dir = self.check_dir();
+        dir.push("pkg.rs").exists()
     }
 
-    success
+    // If a file named "pkg.rs" in the current directory exists,
+    // return the path for it. Otherwise, None
+    fn package_script_option(&self, cwd: &Path) -> Option<Path> {
+        let maybe_path = cwd.push("pkg.rs");
+        if os::path_exists(&maybe_path) {
+            Some(maybe_path)
+        }
+        else {
+            None
+        }
+    }
+
+    /// True if the given path's stem is self's pkg ID's stem
+    /// or if the pkg ID's stem is <rust-foo> and the given path's
+    /// stem is foo
+    fn stem_matches(&self, p: &Path) -> bool {
+        let self_id = self.id.path.filestem();
+        if self_id == p.filestem() {
+            return true;
+        }
+        else {
+            for self_id.each |pth| {
+                if pth.starts_with("rust-")
+                    && match p.filestem() {
+                           Some(s) => str::eq_slice(s, pth.slice(5, pth.len())),
+                           None => false
+                       } { return true; }
+            }
+        }
+        false
+    }
+
+    fn push_crate(cs: &mut ~[Crate], prefix: uint, p: &Path) {
+        assert!(p.components.len() > prefix);
+        let mut sub = Path("");
+        for vec::slice(p.components, prefix,
+                       p.components.len()).each |c| {
+            sub = sub.push(*c);
+        }
+        debug!("found crate %s", sub.to_str());
+        cs.push(Crate::new(&sub));
+    }
+
+    fn find_crates(&mut self) {
+        use PkgSrc::push_crate;
+
+        let dir = self.check_dir();
+        let prefix = dir.components.len();
+        // This is ugly, but can go away once we get rid
+        // of .rc files
+        let mut saw_rs = false;
+        let mut saw_rc = false;
+        debug!("Matching against %?",
+               self.id.path.filestem());
+        for os::walk_dir(&dir) |pth| {
+            match pth.filename() {
+                Some(~"lib.rs") => push_crate(&mut self.libs,
+                                              prefix, pth),
+                Some(~"main.rs") => push_crate(&mut self.mains,
+                                               prefix, pth),
+                Some(~"test.rs") => push_crate(&mut self.tests,
+                                               prefix, pth),
+                Some(~"bench.rs") => push_crate(&mut self.benchs,
+                                                prefix, pth),
+                _ => {
+                    // If the file stem is the same as the
+                    // package ID, with an .rs or .rc extension,
+                    // consider it to be a crate
+                    let ext = pth.filetype();
+                    let matches = |p: &Path| {
+                        self.stem_matches(p) && (ext == Some(~".rc")
+                                                  || ext == Some(~".rs"))
+                    };
+                    debug!("Checking %? which %s and ext = %? %? %?", pth.filestem(),
+                           if matches(pth) { "matches" } else { "does not match" },
+                           ext, saw_rs, saw_rc);
+                    if matches(pth) &&
+                        // Avoid pushing foo.rc *and* foo.rs
+                         !((ext == Some(~".rc") && saw_rs) ||
+                           (ext == Some(~".rs") && saw_rc)) {
+                        push_crate(&mut self.libs, // ????
+                                   prefix, pth);
+                        if ext == Some(~".rc") {
+                            saw_rc = true;
+                        }
+                        else if ext == Some(~".rs") {
+                            saw_rs = true;
+                        }
+                    }
+                }
+            }
+        }
+        debug!("found %u libs, %u mains, %u tests, %u benchs",
+               self.libs.len(),
+               self.mains.len(),
+               self.tests.len(),
+               self.benchs.len())
+    }
+
+    fn build_crates(dst_dir: &Path,
+                           src_dir: &Path,
+                           crates: &[Crate],
+                           cfgs: ~[~str],
+                           test: bool) {
+
+        for crates.each |&crate| {
+            let path = &src_dir.push_rel(&crate.file).normalize();
+            util::note(fmt!("build_crates: compiling %s", path.to_str()));
+            util::note(fmt!("build_crates: destination dir is %s", dst_dir.to_str()));
+
+            let result = util::compile_crate(None, path,
+                                     dst_dir,
+                                     crate.flags,
+                                     crate.cfgs + cfgs,
+                                     false, test);
+            if !result {
+                build_err::cond.raise(fmt!("build failure on %s",
+                                           path.to_str()));
+            }
+            debug!("Result of compiling %s was %?",
+                   path.to_str(), result);
+        }
+    }
+
+    fn build(&self, dst_dir: &Path, cfgs: ~[~str]) {
+        let dir = self.check_dir();
+        debug!("Building libs");
+        PkgSrc::build_crates(dst_dir, &dir, self.libs, cfgs, false);
+        debug!("Building mains");
+        PkgSrc::build_crates(dst_dir, &dir, self.mains, cfgs, false);
+        debug!("Building tests");
+        PkgSrc::build_crates(dst_dir, &dir, self.tests, cfgs, true);
+        debug!("Building benches");
+        PkgSrc::build_crates(dst_dir, &dir, self.benchs, cfgs, true);
+    }
 }
diff --git a/src/librustpkg/util.rs b/src/librustpkg/util.rs
index 0d858afbb84..f7916329e48 100644
--- a/src/librustpkg/util.rs
+++ b/src/librustpkg/util.rs
@@ -9,26 +9,125 @@
 // except according to those terms.
 
 use core::*;
+use core::cmp::Ord;
 use core::hash::Streaming;
-use core::hashmap::HashMap;
 use rustc::driver::{driver, session};
 use rustc::metadata::filesearch;
 use std::getopts::groups::getopts;
 use std::semver;
-use std::{json, term, sort, getopts};
+use std::{json, term, getopts};
 use syntax::ast_util::*;
-use syntax::codemap::{dummy_sp, spanned};
+use syntax::codemap::{dummy_sp};
 use syntax::ext::base::{mk_ctxt, ext_ctxt};
 use syntax::ext::build;
 use syntax::{ast, attr, codemap, diagnostic, fold};
+use rustc::back::link::output_type_exe;
 
-pub struct Package {
-    id: ~str,
-    vers: semver::Version,
+pub type ExitCode = int; // For now
+
+/// A version is either an exact revision,
+/// or a semantic version
+pub enum Version {
+    ExactRevision(float),
+    SemVersion(semver::Version)
+}
+
+impl Ord for Version {
+    fn lt(&self, other: &Version) -> bool {
+        match (self, other) {
+            (&ExactRevision(f1), &ExactRevision(f2)) => f1 < f2,
+            (&SemVersion(v1), &SemVersion(v2)) => v1 < v2,
+            _ => false // incomparable, really
+        }
+    }
+    fn le(&self, other: &Version) -> bool {
+        match (self, other) {
+            (&ExactRevision(f1), &ExactRevision(f2)) => f1 <= f2,
+            (&SemVersion(v1), &SemVersion(v2)) => v1 <= v2,
+            _ => false // incomparable, really
+        }
+    }
+    fn ge(&self, other: &Version) -> bool {
+        match (self, other) {
+            (&ExactRevision(f1), &ExactRevision(f2)) => f1 > f2,
+            (&SemVersion(v1), &SemVersion(v2)) => v1 > v2,
+            _ => false // incomparable, really
+        }
+    }
+    fn gt(&self, other: &Version) -> bool {
+        match (self, other) {
+            (&ExactRevision(f1), &ExactRevision(f2)) => f1 >= f2,
+            (&SemVersion(v1), &SemVersion(v2)) => v1 >= v2,
+            _ => false // incomparable, really
+        }
+    }
+
+}
+
+impl ToStr for Version {
+    fn to_str(&self) -> ~str {
+        match *self {
+            ExactRevision(n) => n.to_str(),
+            SemVersion(v) => v.to_str()
+        }
+    }
+}
+
+/// Placeholder
+fn default_version() -> Version { ExactRevision(0.1) }
+
+// Path-fragment identifier of a package such as
+// 'github.com/graydon/test'; path must be a relative
+// path with >=1 component.
+pub struct PkgId {
+    path: Path,
+    version: Version
+}
+
+pub impl PkgId {
+    fn new(s: &str) -> PkgId {
+        use bad_pkg_id::cond;
+
+        let p = Path(s);
+        if p.is_absolute {
+            return cond.raise((p, ~"absolute pkgid"));
+        }
+        if p.components.len() < 1 {
+            return cond.raise((p, ~"0-length pkgid"));
+        }
+        PkgId {
+            path: p,
+            version: default_version()
+        }
+    }
+
+    fn hash(&self) -> ~str {
+        fmt!("%s-%s-%s", self.path.to_str(),
+             hash(self.path.to_str() + self.version.to_str()),
+             self.version.to_str())
+    }
+
+}
+
+impl ToStr for PkgId {
+    fn to_str(&self) -> ~str {
+        // should probably use the filestem and not the whole path
+        fmt!("%s-v%s", self.path.to_str(), self.version.to_str())
+    }
+}
+
+pub struct Pkg {
+    id: PkgId,
     bins: ~[~str],
     libs: ~[~str],
 }
 
+impl ToStr for Pkg {
+    fn to_str(&self) -> ~str {
+        self.id.to_str()
+    }
+}
+
 pub fn root() -> Path {
     match filesearch::get_rustpkg_root() {
         result::Ok(path) => path,
@@ -309,306 +408,65 @@ pub fn wait_for_lock(path: &Path) {
     }
 }
 
-fn _add_pkg(packages: ~[json::Json], pkg: &Package) -> ~[json::Json] {
-    for packages.each |&package| {
-        match &package {
-            &json::Object(ref map) => {
-                let mut has_id = false;
-
-                match map.get(&~"id") {
-                    &json::String(ref str) => {
-                        if pkg.id == *str {
-                            has_id = true;
-                        }
-                    }
-                    _ => {}
-                }
-
-                match map.get(&~"vers") {
-                    &json::String(ref str) => {
-                        if has_id && pkg.vers.to_str() == *str {
-                            return copy packages;
-                        }
-                    }
-                    _ => {}
-                }
-            }
-            _ => {}
-        }
-    }
-
-    let mut map = ~HashMap::new();
-
-    map.insert(~"id", json::String(pkg.id));
-    map.insert(~"vers", json::String(pkg.vers.to_str()));
-    map.insert(~"bins", json::List(do pkg.bins.map |&bin| {
-        json::String(bin)
-    }));
-    map.insert(~"libs", json::List(do pkg.libs.map |&lib| {
-        json::String(lib)
-    }));
-
-    vec::append(packages, ~[json::Object(map)])
-}
-
-fn _rm_pkg(packages: ~[json::Json], pkg: &Package) -> ~[json::Json] {
-    do packages.filter_mapped |&package| {
-        match &package {
-            &json::Object(ref map) => {
-                let mut has_id = false;
-
-                match map.get(&~"id") {
-                    &json::String(str) => {
-                        if pkg.id == str {
-                            has_id = true;
-                        }
-                    }
-                    _ => {}
-                }
-
-                match map.get(&~"vers") {
-                    &json::String(ref str) => {
-                        if has_id && pkg.vers.to_str() == *str {
-                            None
-                        } else {
-                            Some(copy package)
-                        }
-                    }
-                    _ => { Some(copy package) }
-                }
-            }
-            _ => { Some(copy package) }
-        }
-    }
-}
-
 pub fn load_pkgs() -> result::Result<~[json::Json], ~str> {
-    let root = root();
-    let db = root.push(~"db.json");
-    let db_lock = root.push(~"db.json.lck");
-
-    wait_for_lock(&db_lock);
-    touch(&db_lock);
-
-    let packages = if os::path_exists(&db) {
-        match io::read_whole_file_str(&db) {
-            result::Ok(str) => {
-                match json::from_str(str) {
-                    result::Ok(json) => {
-                        match json {
-                            json::List(list) => list,
-                            _ => {
-                                os::remove_file(&db_lock);
-
-                                return result::Err(
-                                    ~"package db's json is not a list");
-                            }
-                        }
-                    }
-                    result::Err(err) => {
-                        os::remove_file(&db_lock);
-
-                        return result::Err(
-                            fmt!("failed to parse package db: %s",
-                            err.to_str()));
-                    }
-                }
-            }
-            result::Err(err) => {
-                os::remove_file(&db_lock);
-
-                return result::Err(fmt!("failed to read package db: %s",
-                                        err));
-            }
-        }
-    } else { ~[] };
-
-    os::remove_file(&db_lock);
-
-    result::Ok(packages)
+    fail!(~"load_pkg not implemented");
 }
 
-pub fn get_pkg(id: ~str,
-               vers: Option<~str>) -> result::Result<Package, ~str> {
-    let name = match parse_name(id) {
-        result::Ok(name) => name,
-        result::Err(err) => return result::Err(err)
-    };
-    let packages = match load_pkgs() {
-        result::Ok(packages) => packages,
-        result::Err(err) => return result::Err(err)
-    };
-    let mut sel = None;
-    let mut possibs = ~[];
-    let mut err = None;
-
-    for packages.each |&package| {
-        match package {
-            json::Object(map) => {
-                let pid = match map.get(&~"id") {
-                    &json::String(str) => str,
-                    _ => loop
-                };
-                let pname = match parse_name(pid) {
-                    result::Ok(pname) => pname,
-                    result::Err(perr) => {
-                        err = Some(perr);
-
-                        break;
-                    }
-                };
-                let pvers = match map.get(&~"vers") {
-                    &json::String(str) => str,
-                    _ => loop
-                };
-                if pid == id || pname == name {
-                    let bins = match map.get(&~"bins") {
-                        &json::List(ref list) => {
-                            do list.map |&bin| {
-                                match bin {
-                                    json::String(str) => str,
-                                    _ => ~""
-                                }
-                            }
-                        }
-                        _ => ~[]
-                    };
-                    let libs = match map.get(&~"libs") {
-                        &json::List(ref list) => {
-                            do list.map |&lib| {
-                                match lib {
-                                    json::String(str) => str,
-                                    _ => ~""
-                                }
-                            }
-                        }
-                        _ => ~[]
-                    };
-                    let package = Package {
-                        id: pid,
-                        vers: match parse_vers(pvers) {
-                            result::Ok(vers) => vers,
-                            result::Err(verr) => {
-                                err = Some(verr);
-
-                                break;
-                            }
-                        },
-                        bins: bins,
-                        libs: libs
-                    };
-
-                    if !vers.is_none() && vers.get() == pvers {
-                        sel = Some(package);
-                    }
-                    else {
-                        possibs.push(package);
-                    }
-                }
-            }
-            _ => {}
-        }
-    }
-
-    if !err.is_none() {
-        return result::Err(err.get());
-    }
-    if !sel.is_none() {
-        return result::Ok(sel.get());
-    }
-    if !vers.is_none() || possibs.len() < 1 {
-        return result::Err(~"package not found");
-    }
-
-    let possibs = sort::merge_sort(possibs, |v1, v2| {
-        v1.vers <= v2.vers
-    });
-
-    result::Ok(copy *possibs.last())
+pub fn get_pkg(_id: ~str,
+               _vers: Option<~str>) -> result::Result<Pkg, ~str> {
+    fail!(~"get_pkg not implemented");
 }
 
-pub fn add_pkg(pkg: &Package) -> bool {
-    let root = root();
-    let db = root.push(~"db.json");
-    let db_lock = root.push(~"db.json.lck");
-    let packages = match load_pkgs() {
-        result::Ok(packages) => packages,
-        result::Err(err) => {
-            error(err);
-
-            return false;
-        }
-    };
-
-    wait_for_lock(&db_lock);
-    touch(&db_lock);
-    os::remove_file(&db);
-
-    match io::mk_file_writer(&db, ~[io::Create]) {
-        result::Ok(writer) => {
-            writer.write_line(json::to_pretty_str(&json::List(
-                _add_pkg(packages, pkg))));
-        }
-        result::Err(err) => {
-            error(fmt!("failed to dump package db: %s", err));
-            os::remove_file(&db_lock);
-
-            return false;
-        }
-    }
-
-    os::remove_file(&db_lock);
-
-    true
+pub fn add_pkg(pkg: &Pkg) -> bool {
+    note(fmt!("Would be adding package, but add_pkg is not yet implemented %s",
+         pkg.to_str()));
+    false
 }
 
-pub fn remove_pkg(pkg: &Package) -> bool {
-    let root = root();
-    let db = root.push(~"db.json");
-    let db_lock = root.push(~"db.json.lck");
-    let packages = match load_pkgs() {
-        result::Ok(packages) => packages,
-        result::Err(err) => {
-            error(err);
-
-            return false;
-        }
-    };
-
-    wait_for_lock(&db_lock);
-    touch(&db_lock);
-    os::remove_file(&db);
+// FIXME (#4432): Use workcache to only compile when needed
+pub fn compile_input(sysroot: Option<Path>,
+                     in_file: &Path,
+                     out_dir: &Path,
+                     flags: ~[~str],
+                     cfgs: ~[~str],
+                     opt: bool,
+                     test: bool) -> bool {
 
-    match io::mk_file_writer(&db, ~[io::Create]) {
-        result::Ok(writer) => {
-            writer.write_line(json::to_pretty_str(&json::List(
-                _rm_pkg(packages, pkg))));
-        }
-        result::Err(err) => {
-            error(fmt!("failed to dump package db: %s", err));
-            os::remove_file(&db_lock);
+    assert!(in_file.components.len() > 1);
+    let input = driver::file_input(copy *in_file);
+    debug!("compile_input: %s", in_file.to_str());
+    // tjc: by default, use the package ID name as the link name
+    // not sure if we should support anything else
+    let short_name = in_file.filestem().expect("Can't compile a directory!");
+    debug!("short_name = %s", short_name.to_str());
 
-            return false;
-        }
-    }
-
-    os::remove_file(&db_lock);
+// Right now we're always assuming that we're building a library.
+// What we should do is parse the crate and infer whether it's a library
+// from the absence or presence of a main fn
+    let out_file = out_dir.push(os::dll_filename(short_name));
+    let building_library = true;
 
-    true
-}
+    debug!("compiling %s into %s",
+           in_file.to_str(),
+           out_file.to_str());
 
-pub fn compile_input(sysroot: Option<Path>, input: driver::input, dir: &Path,
-               flags: ~[~str], cfgs: ~[~str], opt: bool, test: bool) -> bool {
-    let lib_dir = dir.push(~"lib");
-    let bin_dir = dir.push(~"bin");
-    let test_dir = dir.push(~"test");
     let binary = os::args()[0];
-    let matches = getopts(flags, driver::optgroups()).get();
+
+    debug!("flags: %s", str::connect(flags, ~" "));
+    debug!("cfgs: %s", str::connect(cfgs, ~" "));
+// Again, we assume we're building a library
+    let matches = getopts(~[~"-Z", ~"time-passes"]
+                          + if building_library { ~[~"--lib"] } else { ~[] }
+                          + flags
+                          + cfgs.flat_map(|&c| { ~[~"--cfg", c] }),
+                          driver::optgroups()).get();
     let options = @session::options {
-        crate_type: session::unknown_crate,
+        crate_type: if building_library { session::lib_crate }
+                    else { session::bin_crate },
         optimize: if opt { session::Aggressive } else { session::No },
         test: test,
         maybe_sysroot: sysroot,
+        addl_lib_search_paths: ~[copy *out_dir],
         .. *driver::build_session_options(binary, &matches, diagnostic::emit)
     };
     let mut crate_cfg = options.cfg;
@@ -619,124 +477,42 @@ pub fn compile_input(sysroot: Option<Path>, input: driver::input, dir: &Path,
 
     let options = @session::options {
         cfg: vec::append(options.cfg, crate_cfg),
+        // output_type should be conditional
+        output_type: output_type_exe, // Use this to get a library? That's weird
         .. *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, 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, 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");
+    debug!("calling compile_crate_from_input, out_dir = %s,
+           building_library = %?", out_dir.to_str(), sess.building_library);
+    compile_crate_from_input(input, Some(*out_dir), sess, None, binary);
+    true
+}
 
-                    false
-                }
-            }
+// Should use workcache to avoid recompiling when not necessary
+// Should also rename this to something better
+// If crate_opt is present, then finish compilation. If it's None, then
+// call compile_upto and return the crate
+pub fn compile_crate_from_input(input: driver::input, build_dir_opt: Option<Path>,
+    sess: session::Session, crate_opt: Option<@ast::crate>,
+                                binary: ~str) -> @ast::crate {
+    debug!("Calling build_output_filenames with %?", build_dir_opt);
+    let outputs = driver::build_output_filenames(input, &build_dir_opt, &None, sess);
+    debug!("Outputs are %? and output type = %?", outputs, sess.opts.output_type);
+    let cfg = driver::build_configuration(sess, binary, input);
+    match crate_opt {
+        Some(c) => {
+            debug!("Calling compile_rest, outputs = %?", outputs);
+            driver::compile_rest(sess, cfg, driver::cu_everything, Some(outputs), Some(c));
+            c
         }
         None => {
-            warn(~"missing crate_type attr, assuming lib");
-
-            false
+            debug!("Calling compile_upto, outputs = %?", outputs);
+            let (crate, _) = driver::compile_upto(sess, cfg, input, driver::cu_parse,
+                                                  Some(outputs));
+            crate
         }
-    };
-
-    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)]
@@ -749,20 +525,19 @@ pub fn exe_suffix() -> ~str { ~".exe" }
 pub fn exe_suffix() -> ~str { ~"" }
 
 
+// Called by build_crates
 // FIXME (#4432): Use workcache to only compile when needed
 pub fn compile_crate(sysroot: Option<Path>, crate: &Path, dir: &Path,
                      flags: ~[~str], cfgs: ~[~str], opt: bool,
                      test: bool) -> bool {
-    compile_input(sysroot, driver::file_input(*crate), dir, flags, cfgs,
-                  opt, test)
+    debug!("compile_crate: crate=%s, dir=%s", crate.to_str(), dir.to_str());
+    debug!("compile_crate: flags =...");
+    for flags.each |&fl| {
+        debug!("+++ %s", fl);
+    }
+    compile_input(sysroot, crate, dir, flags, cfgs, opt, test)
 }
 
-pub fn compile_str(sysroot: Option<Path>, code: ~str, dir: &Path,
-                   flags: ~[~str], cfgs: ~[~str], opt: bool,
-                   test: bool) -> bool {
-    compile_input(sysroot, driver::str_input(code), dir, flags, cfgs,
-                  opt, test)
-}
 
 #[cfg(windows)]
 pub fn link_exe(_src: &Path, _dest: &Path) -> bool {