about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2013-04-18 16:06:52 -0700
committerbors <bors@rust-lang.org>2013-04-18 16:06:52 -0700
commit50cd218c1e74dac5113e82bd3e03008d746a26df (patch)
treec734545721ab7107849355a360bb11edfd6163d7
parentb19c644d543b3638c4d420e923446606ad8ac2e2 (diff)
parent1aebf30f72c16628ce3b5404329e65a0d4ad4e05 (diff)
downloadrust-50cd218c1e74dac5113e82bd3e03008d746a26df.tar.gz
rust-50cd218c1e74dac5113e82bd3e03008d746a26df.zip
auto merge of #5920 : catamorphism/rust/rustpkg, r=catamorphism
r? @graydon
-rw-r--r--src/librustpkg/path_util.rs21
-rw-r--r--src/librustpkg/rustpkg.rc304
-rw-r--r--src/librustpkg/testsuite/fail/no-inferred-crates/zzyzx.rs22
-rw-r--r--src/librustpkg/testsuite/pass/commands.txt35
-rw-r--r--src/librustpkg/testsuite/pass/deeply/nested/path/foo/main.rs18
-rw-r--r--src/librustpkg/testsuite/pass/external-crate/main.rs21
-rw-r--r--src/librustpkg/testsuite/pass/fancy-lib/bar.rs13
-rw-r--r--src/librustpkg/testsuite/pass/fancy-lib/fancy-lib.rs24
-rw-r--r--src/librustpkg/testsuite/pass/fancy-lib/foo.rs12
-rw-r--r--src/librustpkg/testsuite/pass/fancy-lib/pkg.rs23
-rw-r--r--src/librustpkg/testsuite/pass/hello-world/main.rs25
-rw-r--r--src/librustpkg/testsuite/pass/install-paths/bench.rs17
-rw-r--r--src/librustpkg/testsuite/pass/install-paths/lib.rs11
-rw-r--r--src/librustpkg/testsuite/pass/install-paths/main.rs22
-rw-r--r--src/librustpkg/testsuite/pass/install-paths/test.rs14
-rw-r--r--src/librustpkg/testsuite/pass/simple-lib/bar.rs13
-rw-r--r--src/librustpkg/testsuite/pass/simple-lib/foo.rs12
-rw-r--r--src/librustpkg/testsuite/pass/simple-lib/lib.rs21
-rw-r--r--src/librustpkg/testsuite/pass/simple-lib/simple-lib.rc21
-rw-r--r--src/librustpkg/util.rs115
20 files changed, 526 insertions, 238 deletions
diff --git a/src/librustpkg/path_util.rs b/src/librustpkg/path_util.rs
index c821281184d..140d9ced580 100644
--- a/src/librustpkg/path_util.rs
+++ b/src/librustpkg/path_util.rs
@@ -11,7 +11,8 @@
 // rustpkg utilities having to do with paths and directories
 
 use core::path::*;
-use core::os;
+use core::{os, str};
+use core::option::*;
 use util::PkgId;
 
 /// Returns the output directory to use.
@@ -50,6 +51,24 @@ pub fn default_dest_dir(pkg_dir: &Path) -> Path {
     }
 }
 
+/// Replace all occurrences of '-' in the stem part of path with '_'
+/// This is because we treat rust-foo-bar-quux and rust_foo_bar_quux
+/// as the same name
+pub fn normalize(p: ~Path) -> ~Path {
+    match p.filestem() {
+        None => p,
+        Some(st) => {
+            let replaced = str::replace(st, "-", "_");
+            if replaced != st {
+                ~p.with_filestem(replaced)
+            }
+            else {
+                p
+            }
+        }
+    }
+}
+
 #[cfg(test)]
 mod test {
     use core::{os, rand};
diff --git a/src/librustpkg/rustpkg.rc b/src/librustpkg/rustpkg.rc
index f8805142769..206404ae204 100644
--- a/src/librustpkg/rustpkg.rc
+++ b/src/librustpkg/rustpkg.rc
@@ -33,11 +33,11 @@ use core::hashmap::HashMap;
 use core::io::WriterUtil;
 use rustc::driver::{driver, session};
 use rustc::metadata::filesearch;
-use std::net::url;
 use std::{getopts};
 use syntax::{ast, diagnostic};
-use util::{ExitCode, Pkg, PkgId};
-use path_util::dest_dir;
+use util::*;
+use path_util::{dest_dir, normalize};
+use rustc::driver::session::{lib_crate, bin_crate, unknown_crate, crate_type};
 
 mod conditions;
 mod usage;
@@ -117,9 +117,12 @@ impl PkgScript {
             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());
+                util::compile_crate_from_input(self.input, self.id,
+                                               Some(self.build_dir),
+                                               sess, Some(crate),
+                                               exe, os::args()[0],
+                                               driver::cu_everything);
                 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 {
@@ -199,15 +202,15 @@ impl Ctx {
                 // relative to the CWD. In the future, we should search
                 // paths
                 let cwd = os::getcwd().normalize();
-                debug!("Current working directory = %?", cwd);
+                debug!("Current working directory = %s", cwd.to_str());
 
-                // Find crates inside the workspace
+                // Create the package source
                 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);
+                let mut custom = false;;
                 debug!("Package source directory = %s", pkg_src_dir.to_str());
                 let cfgs = match src.package_script_option(&pkg_src_dir) {
                     Some(package_script_path) => {
@@ -221,6 +224,7 @@ impl Ctx {
                         if hook_result != 0 {
                             fail!(fmt!("Error running custom build command"))
                         }
+                        custom = true;
                         // otherwise, the package script succeeded
                         cfgs
                     }
@@ -229,19 +233,32 @@ impl Ctx {
                         ~[]
                     }
                 };
-                src.build(&dst_dir, cfgs);
+
+                // If there was a package script, it should have finished
+                // the build already. Otherwise...
+                if !custom {
+                    // Find crates inside the workspace
+                    src.find_crates();
+                    // Build it!
+                    src.build(&dst_dir, cfgs);
+                }
             }
             ~"clean" => {
-                self.clean();
+                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]);
+
+                self.clean(pkgid);
             }
             ~"do" => {
                 if args.len() < 2 {
                     return usage::do_cmd();
                 }
 
-                if !self.do_cmd(args[0], args[1]) {
-                    fail!(~"a command failed!");
-                }
+                self.do_cmd(args[0], args[1]);
             }
             ~"info" => {
                 self.info();
@@ -286,12 +303,11 @@ impl Ctx {
         }
     }
 
-    fn do_cmd(&self, cmd: ~str, pkgname: ~str) -> bool {
+    fn do_cmd(&self, cmd: ~str, pkgname: ~str)  {
         match cmd {
             ~"build" | ~"test" => {
                 util::error(~"that command cannot be manually called");
-
-                return false;
+                fail!(~"do_cmd");
             }
             _ => {}
         }
@@ -307,16 +323,15 @@ impl Ctx {
             Some(script_path) => {
                 let script = PkgScript::parse(script_path, pkgid);
                 let (_, status) = script.run_custom(cmd); // Ignore cfgs?
-                if status == 42 { // ???
+                if status == 42 {
                     util::error(~"no fns are listening for that cmd");
-                    return false;
+                    fail!(~"do_cmd");
                 }
-                status == 0
             }
             None => {
                 util::error(fmt!("invoked `do`, but there is no package script in %s",
                                  cwd.to_str()));
-                false
+                fail!(~"do_cmd");
             }
         }
     }
@@ -329,128 +344,44 @@ impl Ctx {
     }
 
     fn compile(&self, _crate: &Path, _dir: &Path, _flags: ~[~str],
-               _cfgs: ~[~str], _opt: bool, _test: bool) -> bool {
+               _cfgs: ~[~str], _opt: bool, _test: bool)  {
         // What's the difference between build and compile?
         fail!(~"compile not yet implemented");
     }
 
-    fn clean(&self) -> bool {
-        // stub
-        fail!();
+    fn clean(&self, id: PkgId)  {
+        // Could also support a custom build hook in the pkg
+        // script for cleaning files rustpkg doesn't know about.
+        // Do something reasonable for now
+
+        let dir = dest_dir(id);
+        util::note(fmt!("Cleaning package %s (removing directory %s)",
+                        id.to_str(), dir.to_str()));
+        if os::path_exists(&dir) {
+            util::remove_dir_r(&dir);
+            util::note(fmt!("Removed directory %s", dir.to_str()));
+        }
+
+        util::note(fmt!("Cleaned package %s", id.to_str()));
     }
 
     fn info(&self) {
         // stub
-        fail!();
+        fail!(~"info not yet implemented");
     }
 
-    fn install(&self, url: Option<~str>,
-               target: Option<~str>, cache: bool) -> bool {
-       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 });
-
-                if self.dep_cache.contains_key(&hash) {
-                    util::warn(~"already installed dep this run");
-                    return true;
-                }
-
-                self.dep_cache.insert(hash, true);
-
-                let dir = util::root().push(~"tmp").push(hash);
-
-                if cache && os::path_exists(&dir) {
-                    return true;
-                }
-
-                if !self.fetch(&dir, url, target) {
-                    return false;
-                }
-                dir
-            }
-        };
-
-        let script = match self.build(&dir, false, true, false) {
-            Some(script) => script,
-            None => {
-                return false;
-            }
-        };
-        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");
-        let to_lib_dir = util::root().push(~"lib");
-        let mut bins = ~[];
-        let mut libs = ~[];
-
-        for os::walk_dir(&from_bin_dir) |bin| {
-            let to = to_bin_dir.push_rel(&bin.file_path());
-
-            os::copy_file(bin, &to);
-            bins.push(to.to_str());
-        }
-
-        for os::walk_dir(&from_lib_dir) |lib| {
-            let to = to_lib_dir.push_rel(&lib.file_path());
-
-            os::copy_file(lib, &to);
-            libs.push(to.to_str());
-        }
-
-        let package = Pkg {
-            id: script.id,
-            bins: bins,
-            libs: libs
-        };
-
-        util::note(fmt!("installed %s", script.id.to_str()));
-        util::add_pkg(&package);
-
-        true
+    fn install(&self, _url: Option<~str>,
+               _target: Option<~str>, _cache: bool)  {
+        // stub
+        fail!(~"install not yet implemented");
     }
 
-    fn fetch(&self, dir: &Path, url: ~str, target: Option<~str>) -> bool {
-        let url = if str::find_str(url, "://").is_none() {
-            ~"http://" + url }
-        else { url };
-        let url = match url::from_str(url) {
-            result::Ok(url) => url,
-            result::Err(err) => {
-                util::error(fmt!("failed parsing %s", err.to_lower()));
-
-                return false;
-            }
-        };
-        let str = url.to_str();
-
-        match Path(url.path).filetype() {
-            Some(ext) => {
-                if ext == ~".git" {
-                    return self.fetch_git(dir, str, target);
-                }
-            }
-            None => {}
-        }
-
-        match url.scheme {
-            ~"git" => self.fetch_git(dir, str, target),
-            ~"http" | ~"ftp" | ~"file" => self.fetch_curl(dir, str),
-            _ => {
-                util::warn(~"unknown url scheme to fetch, using curl");
-                self.fetch_curl(dir, str)
-            }
-        }
+    fn fetch(&self, _dir: &Path, _url: ~str, _target: Option<~str>)  {
+        // stub
+        fail!(~"fetch not yet implemented");
     }
 
-    fn fetch_curl(&self, dir: &Path, url: ~str) -> bool {
+    fn fetch_curl(&self, dir: &Path, url: ~str)  {
         util::note(fmt!("fetching from %s using curl", url));
 
         let tar = dir.dir_path().push(&dir.file_path().to_str() + ~".tar");
@@ -460,7 +391,7 @@ impl Ctx {
                                           url]).status != 0 {
             util::error(~"fetching failed: downloading using curl failed");
 
-            return false;
+            fail!();
         }
 
         if run::program_output(~"tar", ~[~"-x", ~"--strip-components=1",
@@ -469,13 +400,11 @@ impl Ctx {
             util::error(~"fetching failed: extracting using tar failed" +
                         ~"(is it a valid tar archive?)");
 
-            return false;
+           fail!();
         }
-
-        true
     }
 
-    fn fetch_git(&self, dir: &Path, url: ~str, target: Option<~str>) -> bool {
+    fn fetch_git(&self, dir: &Path, url: ~str, target: Option<~str>)  {
         util::note(fmt!("fetching from %s using git", url));
 
         // Git can't clone into a non-empty directory
@@ -484,8 +413,7 @@ impl Ctx {
         if run::program_output(~"git", ~[~"clone", url,
                                          dir.to_str()]).status != 0 {
             util::error(~"fetching failed: can't clone repository");
-
-            return false;
+            fail!();
         }
 
         if !target.is_none() {
@@ -499,21 +427,17 @@ impl Ctx {
 
             if !success {
                 util::error(~"fetching failed: can't checkout target");
-
-                return false;
+                fail!();
             }
         }
-
-        true
     }
 
-    fn prefer(&self, id: ~str, vers: Option<~str>) -> bool {
+    fn prefer(&self, id: ~str, vers: Option<~str>)  {
         let package = match util::get_pkg(id, vers) {
             result::Ok(package) => package,
             result::Err(err) => {
                 util::error(err);
-
-                return false;
+                fail!(); // Condition?
             }
         };
         let name = package.id.path.to_str(); // ???
@@ -536,29 +460,18 @@ impl Ctx {
         }
 
         util::note(fmt!("preferred %s v%s", name, package.id.version.to_str()));
-
-        true
     }
 
-    fn test(&self) -> bool {
-        let script = match self.build(&os::getcwd(), false, false, true) {
-            Some(script) => script,
-            None => {
-                return false;
-            }
-        };
-
-        // To do
-        util::note(fmt!("Would test %s, but this is a dry run",
-                        script.id.to_str()));
-        false
+    fn test(&self)  {
+        // stub
+        fail!(~"test not yet implemented");
     }
 
-    fn uninstall(&self, _id: ~str, _vers: Option<~str>) -> bool {
+    fn uninstall(&self, _id: ~str, _vers: Option<~str>)  {
         fail!(~"uninstall not yet implemented");
     }
 
-    fn unprefer(&self, _id: ~str, _vers: Option<~str>) -> bool {
+    fn unprefer(&self, _id: ~str, _vers: Option<~str>)  {
         fail!(~"unprefer not yet implemented");
     }
 }
@@ -728,7 +641,6 @@ condition! {
 
 impl PkgSrc {
 
-
     fn new(src_dir: &Path, dst_dir: &Path,
                   id: &PkgId) -> PkgSrc {
         PkgSrc {
@@ -765,12 +677,6 @@ impl PkgSrc {
         dir
     }
 
-
-    fn has_pkg_file(&self) -> bool {
-        let dir = self.check_dir();
-        dir.push("pkg.rs").exists()
-    }
-
     // 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> {
@@ -786,14 +692,16 @@ impl PkgSrc {
     /// 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
+    /// Requires that dashes in p have already been normalized to
+    /// underscores
     fn stem_matches(&self, p: &Path) -> bool {
-        let self_id = self.id.path.filestem();
+        let self_id = normalize(~self.id.path).filestem();
         if self_id == p.filestem() {
             return true;
         }
         else {
             for self_id.each |pth| {
-                if pth.starts_with("rust-")
+                if pth.starts_with("rust_") // because p is already normalized
                     && match p.filestem() {
                            Some(s) => str::eq_slice(s, pth.slice(5, pth.len())),
                            None => false
@@ -814,17 +722,14 @@ impl PkgSrc {
         cs.push(Crate::new(&sub));
     }
 
+    /// Infers crates to build. Called only in the case where there
+    /// is no custom build logic
     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());
+        debug!("Matching against %?", self.id.path.filestem());
         for os::walk_dir(&dir) |pth| {
             match pth.filename() {
                 Some(~"lib.rs") => push_crate(&mut self.libs,
@@ -835,34 +740,19 @@ impl PkgSrc {
                                                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;
-                        }
-                    }
-                }
+                _ => ()
             }
         }
+
+        if self.libs.is_empty() && self.mains.is_empty()
+            && self.tests.is_empty() && self.benchs.is_empty() {
+
+            util::note(~"Couldn't infer any crates to build.\n\
+                         Try naming a crate `main.rs`, `lib.rs`, \
+                         `test.rs`, or `bench.rs`.");
+            fail!(~"Failed to infer crates to build");
+        }
+
         debug!("found %u libs, %u mains, %u tests, %u benchs",
                self.libs.len(),
                self.mains.len(),
@@ -870,22 +760,22 @@ impl PkgSrc {
                self.benchs.len())
     }
 
-    fn build_crates(dst_dir: &Path,
+    fn build_crates(&self, dst_dir: &Path,
                            src_dir: &Path,
                            crates: &[Crate],
                            cfgs: ~[~str],
-                           test: bool) {
+                           test: bool, crate_type: crate_type) {
 
         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,
+            let result = util::compile_crate(None, self.id, path,
                                      dst_dir,
                                      crate.flags,
                                      crate.cfgs + cfgs,
-                                     false, test);
+                                     false, test, crate_type);
             if !result {
                 build_err::cond.raise(fmt!("build failure on %s",
                                            path.to_str()));
@@ -898,12 +788,12 @@ impl PkgSrc {
     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);
+        self.build_crates(dst_dir, &dir, self.libs, cfgs, false, lib_crate);
         debug!("Building mains");
-        PkgSrc::build_crates(dst_dir, &dir, self.mains, cfgs, false);
+        self.build_crates(dst_dir, &dir, self.mains, cfgs, false, bin_crate);
         debug!("Building tests");
-        PkgSrc::build_crates(dst_dir, &dir, self.tests, cfgs, true);
+        self.build_crates(dst_dir, &dir, self.tests, cfgs, true, bin_crate);
         debug!("Building benches");
-        PkgSrc::build_crates(dst_dir, &dir, self.benchs, cfgs, true);
+        self.build_crates(dst_dir, &dir, self.benchs, cfgs, true, bin_crate);
     }
 }
diff --git a/src/librustpkg/testsuite/fail/no-inferred-crates/zzyzx.rs b/src/librustpkg/testsuite/fail/no-inferred-crates/zzyzx.rs
new file mode 100644
index 00000000000..c9c1f00fb08
--- /dev/null
+++ b/src/librustpkg/testsuite/fail/no-inferred-crates/zzyzx.rs
@@ -0,0 +1,22 @@
+// Copyright 2013 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.
+
+/*
+The test runner should check that, after `rustpkg build hello-world`:
+  * testsuite/hello-world/build/ exists
+  * testsuite/hello-world/build/ contains an executable named hello-world
+  * testsuite/hello-world/build/ does not contain a library
+*/
+
+use core::io;
+
+fn main() {
+    io::println(~"Hello world!");
+}
diff --git a/src/librustpkg/testsuite/pass/commands.txt b/src/librustpkg/testsuite/pass/commands.txt
new file mode 100644
index 00000000000..e1a1b2462b2
--- /dev/null
+++ b/src/librustpkg/testsuite/pass/commands.txt
@@ -0,0 +1,35 @@
+Commands that should succeed:
+
+1. rustpkg install github.com/mozilla-servo/rust-http-client
+
+2. Create a git repo containing a package "foo", add a tag called "1.0" -- `rustpkg install foo` should install a library called "libfoo-....-1.0..."
+
+3. rustpkg install foo, if ./.rust/foo exists and is a valid package directory
+
+4. RUST_PATH=/home/rust rustpkg install foo installs an executable in /home/rust/foo if ./foo exists and is a valid package directory
+
+5. RUST_PATH=/home/rust:/home/more_rust rustpkg install foo succeeds if /home/more_rust/foo exists and is a valid package directory
+
+6. rustpkg install foo; rustpkg install bar; rustpkg install quux; rustpkg list should show foo, bar, and quux
+  6a. then, rustpkg remove foo; rustpkg list should show bar and quux, but not foo
+
+7. Execute `rustpkg build foo`. Check the datestamp on build/foo. Execute the same command again. Make sure the datestamp hasn't changed.
+
+8. Execute `rustpkg build foo` where foo has a dependency on bar, which hasn't been built before. Check the datestamps on build/foo and build/bar and make sure bar's datestamp is earlier than foo's.
+
+9. Execute `rustpkg build foo` where foo has a dependency on bar, which hasn't been built before. Then, change one of the source files in bar. Execute `rustpkg build foo` again. Make sure, based on datestamps, that foo was really rebuilt.
+
+10. Repeat test 9 in the case where the contents of the source file in bar change but its datestamp doesn't change.
+
+11. If the current directory contains package directories for foo-0.1 and foo.0.2, `rustpkg install foo#0.1` installs foo#0.1 and doesn't install foo#0.2.
+
+12. `rustpkg do fancy-pkg frob` succeeds if `fancy-pkg` has a package script that defines a custom build hook named `frob`.
+
+13. `rustpkg info foo` prints out something about foo, if foo is installed.
+
+14. (Not sure what prefer and unprefer do)
+
+15. `rustpkg test foo` runs tests and prints their output, if foo contains #[test]s.
+
+16. If foo is installed, `rustpkg uninstall foo; rustpkg list` doesn't include foo in the list
+
diff --git a/src/librustpkg/testsuite/pass/deeply/nested/path/foo/main.rs b/src/librustpkg/testsuite/pass/deeply/nested/path/foo/main.rs
new file mode 100644
index 00000000000..41041ccb509
--- /dev/null
+++ b/src/librustpkg/testsuite/pass/deeply/nested/path/foo/main.rs
@@ -0,0 +1,18 @@
+// Copyright 2013 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.
+
+/*
+The test runner should check that, after `rustpkg install deeply/nested/path/foo`:
+  with RUST_PATH undefined in the environment:
+   * ./deeply/nested/path/foo exists and is an executable
+*/
+
+fn main() {}
+
diff --git a/src/librustpkg/testsuite/pass/external-crate/main.rs b/src/librustpkg/testsuite/pass/external-crate/main.rs
new file mode 100644
index 00000000000..d094bcd6bba
--- /dev/null
+++ b/src/librustpkg/testsuite/pass/external-crate/main.rs
@@ -0,0 +1,21 @@
+// Copyright 2013 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.
+
+/*
+The test runner should check that, after `rustpkg install external crate`
+  with RUST_PATH undefined in the environment
+  and with `rustpkg install deeply/nested/path/foo` already
+     executed:
+   * ./.rust/external_crate exists and is an executable
+*/
+
+extern mod foo; // refers to deeply/nested/path/foo
+
+fn main() {}
diff --git a/src/librustpkg/testsuite/pass/fancy-lib/bar.rs b/src/librustpkg/testsuite/pass/fancy-lib/bar.rs
new file mode 100644
index 00000000000..ffbc6e2a7f9
--- /dev/null
+++ b/src/librustpkg/testsuite/pass/fancy-lib/bar.rs
@@ -0,0 +1,13 @@
+// Copyright 2013 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.
+
+pub fn assert_true() {
+    assert!(true);
+}
diff --git a/src/librustpkg/testsuite/pass/fancy-lib/fancy-lib.rs b/src/librustpkg/testsuite/pass/fancy-lib/fancy-lib.rs
new file mode 100644
index 00000000000..55261a82098
--- /dev/null
+++ b/src/librustpkg/testsuite/pass/fancy-lib/fancy-lib.rs
@@ -0,0 +1,24 @@
+// Copyright 2013 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.
+
+/*
+The test runner should check that, after `rustpkg build fancy-lib`:
+  * testsuite/fancy-lib/build/ exists
+  * testsuite/fancy-lib/build/ contains a file called generated.rs
+  * testsuite/fancy-lib/build/ contains a library named libfancy_lib
+  * testsuite/fancy-lib/build/ does not contain an executable
+  *
+*/
+
+extern mod std;
+
+pub mod foo;
+pub mod bar;
+#[path = "build/generated.rs"] pub mod generated;
diff --git a/src/librustpkg/testsuite/pass/fancy-lib/foo.rs b/src/librustpkg/testsuite/pass/fancy-lib/foo.rs
new file mode 100644
index 00000000000..542a6af402d
--- /dev/null
+++ b/src/librustpkg/testsuite/pass/fancy-lib/foo.rs
@@ -0,0 +1,12 @@
+// Copyright 2013 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.
+
+pub fn do_nothing() {
+}
\ No newline at end of file
diff --git a/src/librustpkg/testsuite/pass/fancy-lib/pkg.rs b/src/librustpkg/testsuite/pass/fancy-lib/pkg.rs
new file mode 100644
index 00000000000..be3c68d731b
--- /dev/null
+++ b/src/librustpkg/testsuite/pass/fancy-lib/pkg.rs
@@ -0,0 +1,23 @@
+// Copyright 2013 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::run;
+
+pub fn main() {
+    let cwd = os::getcwd();
+    debug!("cwd = %s", cwd.to_str());
+    let file = io::file_writer(&Path(~"fancy-lib/build/generated.rs"),
+                               [io::Create]).get();
+    file.write_str("pub fn wheeeee() { for [1, 2, 3].each() |_| { assert!(true); } }");
+
+    // now compile the crate itself
+    run::run_program("rustc", ~[~"fancy-lib/fancy-lib.rs", ~"--lib",
+                                ~"-o", ~"fancy-lib/build/fancy_lib"]);
+}
\ No newline at end of file
diff --git a/src/librustpkg/testsuite/pass/hello-world/main.rs b/src/librustpkg/testsuite/pass/hello-world/main.rs
new file mode 100644
index 00000000000..2ef387d9620
--- /dev/null
+++ b/src/librustpkg/testsuite/pass/hello-world/main.rs
@@ -0,0 +1,25 @@
+// Copyright 2013 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.
+
+/*
+The test runner should check that, after `rustpkg build hello-world`:
+  * testsuite/pass/hello-world/build/ exists
+  * testsuite/pass/hello-world/build/ contains an executable named hello-world
+  * testsuite/pass/hello-world/build/ does not contain a library
+
+ It should also check that after `rustpkg clean hello-world`:
+  * testsuite/pass/hello-world/build is empty
+*/
+
+use core::io;
+
+fn main() {
+    io::println(~"Hello world!");
+}
diff --git a/src/librustpkg/testsuite/pass/install-paths/bench.rs b/src/librustpkg/testsuite/pass/install-paths/bench.rs
new file mode 100644
index 00000000000..e1641ccf074
--- /dev/null
+++ b/src/librustpkg/testsuite/pass/install-paths/bench.rs
@@ -0,0 +1,17 @@
+// Copyright 2013 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.
+
+#[bench]
+fn g() {
+    let mut x = 0;
+    while(x < 1000) {
+        x += 1;
+    }
+}
\ No newline at end of file
diff --git a/src/librustpkg/testsuite/pass/install-paths/lib.rs b/src/librustpkg/testsuite/pass/install-paths/lib.rs
new file mode 100644
index 00000000000..baf90446f7a
--- /dev/null
+++ b/src/librustpkg/testsuite/pass/install-paths/lib.rs
@@ -0,0 +1,11 @@
+// Copyright 2013 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.
+
+fn f() -> int { 42 }
diff --git a/src/librustpkg/testsuite/pass/install-paths/main.rs b/src/librustpkg/testsuite/pass/install-paths/main.rs
new file mode 100644
index 00000000000..37e606dcb1a
--- /dev/null
+++ b/src/librustpkg/testsuite/pass/install-paths/main.rs
@@ -0,0 +1,22 @@
+// Copyright 2013 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.
+
+/*
+The test runner should check that, after `rustpkg install install-paths`
+  with RUST_PATH undefined in the environment:
+   * ./.rust/install_paths exists and is an executable
+   * ./.rust/libinstall_paths exists and is a library
+   * ./.rust/install_pathstest does not exist
+   * ./.rust/install_pathsbench does not exist
+   * install-paths/build/install_pathstest exists and is an executable
+   * install-paths/build/install_pathsbench exists and is an executable
+*/
+
+fn main() {}
diff --git a/src/librustpkg/testsuite/pass/install-paths/test.rs b/src/librustpkg/testsuite/pass/install-paths/test.rs
new file mode 100644
index 00000000000..acfae9e04fb
--- /dev/null
+++ b/src/librustpkg/testsuite/pass/install-paths/test.rs
@@ -0,0 +1,14 @@
+// Copyright 2013 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.
+
+#[test]
+fn test_two_plus_two() {
+    assert!(2 + 2 == 4);
+}
diff --git a/src/librustpkg/testsuite/pass/simple-lib/bar.rs b/src/librustpkg/testsuite/pass/simple-lib/bar.rs
new file mode 100644
index 00000000000..ffbc6e2a7f9
--- /dev/null
+++ b/src/librustpkg/testsuite/pass/simple-lib/bar.rs
@@ -0,0 +1,13 @@
+// Copyright 2013 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.
+
+pub fn assert_true() {
+    assert!(true);
+}
diff --git a/src/librustpkg/testsuite/pass/simple-lib/foo.rs b/src/librustpkg/testsuite/pass/simple-lib/foo.rs
new file mode 100644
index 00000000000..542a6af402d
--- /dev/null
+++ b/src/librustpkg/testsuite/pass/simple-lib/foo.rs
@@ -0,0 +1,12 @@
+// Copyright 2013 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.
+
+pub fn do_nothing() {
+}
\ No newline at end of file
diff --git a/src/librustpkg/testsuite/pass/simple-lib/lib.rs b/src/librustpkg/testsuite/pass/simple-lib/lib.rs
new file mode 100644
index 00000000000..1cdca6cdd5d
--- /dev/null
+++ b/src/librustpkg/testsuite/pass/simple-lib/lib.rs
@@ -0,0 +1,21 @@
+// Copyright 2013 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.
+
+/*
+The test runner should check that, after `rustpkg build simple-lib`:
+  * testsuite/simple-lib/build/ exists
+  * testsuite/simple-lib/build/ contains a library named libsimple_lib
+  * testsuite/simple-lib/build/ does not contain an executable
+*/
+
+extern mod std;
+
+pub mod foo;
+pub mod bar;
diff --git a/src/librustpkg/testsuite/pass/simple-lib/simple-lib.rc b/src/librustpkg/testsuite/pass/simple-lib/simple-lib.rc
new file mode 100644
index 00000000000..1cdca6cdd5d
--- /dev/null
+++ b/src/librustpkg/testsuite/pass/simple-lib/simple-lib.rc
@@ -0,0 +1,21 @@
+// Copyright 2013 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.
+
+/*
+The test runner should check that, after `rustpkg build simple-lib`:
+  * testsuite/simple-lib/build/ exists
+  * testsuite/simple-lib/build/ contains a library named libsimple_lib
+  * testsuite/simple-lib/build/ does not contain an executable
+*/
+
+extern mod std;
+
+pub mod foo;
+pub mod bar;
diff --git a/src/librustpkg/util.rs b/src/librustpkg/util.rs
index 19938e8c5f1..bb162974e03 100644
--- a/src/librustpkg/util.rs
+++ b/src/librustpkg/util.rs
@@ -12,16 +12,20 @@ use core::*;
 use core::cmp::Ord;
 use core::hash::Streaming;
 use rustc::driver::{driver, session};
+use rustc::driver::session::{lib_crate, bin_crate, unknown_crate};
 use rustc::metadata::filesearch;
 use std::getopts::groups::getopts;
 use std::semver;
 use std::{json, term, getopts};
 use syntax::ast_util::*;
-use syntax::codemap::{dummy_sp};
+use syntax::codemap::{dummy_sp, spanned, dummy_spanned};
 use syntax::ext::base::{mk_ctxt, ext_ctxt};
 use syntax::ext::build;
 use syntax::{ast, attr, codemap, diagnostic, fold};
+use syntax::ast::{meta_name_value, meta_list, attribute, crate_};
+use syntax::attr::{mk_attr};
 use rustc::back::link::output_type_exe;
+use rustc::driver::session::{lib_crate, bin_crate, unknown_crate, crate_type};
 
 pub type ExitCode = int; // For now
 
@@ -112,7 +116,7 @@ pub impl PkgId {
 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())
+        fmt!("%s-%s", self.path.to_str(), self.version.to_str())
     }
 }
 
@@ -425,44 +429,51 @@ pub fn add_pkg(pkg: &Pkg) -> bool {
 
 // FIXME (#4432): Use workcache to only compile when needed
 pub fn compile_input(sysroot: Option<Path>,
+                     pkg_id: PkgId,
                      in_file: &Path,
                      out_dir: &Path,
                      flags: ~[~str],
                      cfgs: ~[~str],
                      opt: bool,
-                     test: bool) -> bool {
+                     test: bool,
+                     crate_type: session::crate_type) -> bool {
+
+    let short_name = pkg_id.to_str();
 
     assert!(in_file.components.len() > 1);
     let input = driver::file_input(copy *in_file);
-    debug!("compile_input: %s", in_file.to_str());
+    debug!("compile_input: %s / %?", in_file.to_str(), crate_type);
     // 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());
 
-// 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;
+    let binary = os::args()[0];
+    let building_library = match crate_type {
+        lib_crate | unknown_crate => true,
+        _ => false
+    };
+
+    let out_file = if building_library {
+        out_dir.push(os::dll_filename(short_name))
+    }
+    else {
+        out_dir.push(short_name + if test { ~"test" } else { ~"" }
+                     + os::EXE_SUFFIX)
+    };
 
     debug!("compiling %s into %s",
            in_file.to_str(),
            out_file.to_str());
-
-    let binary = os::args()[0];
-
     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 { ~[] }
+                          + if building_library { ~[~"--lib"] }
+                            else { ~[] }
                           + flags
                           + cfgs.flat_map(|&c| { ~[~"--cfg", c] }),
                           driver::optgroups()).get();
     let options = @session::options {
-        crate_type: if building_library { session::lib_crate }
-                    else { session::bin_crate },
+        crate_type: crate_type,
         optimize: if opt { session::Aggressive } else { session::No },
         test: test,
         maybe_sysroot: sysroot,
@@ -485,7 +496,9 @@ pub fn compile_input(sysroot: Option<Path>,
 
     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);
+    let _ = compile_crate_from_input(input, pkg_id, Some(*out_dir), sess, None,
+                                     out_file, binary,
+                                     driver::cu_everything);
     true
 }
 
@@ -493,24 +506,46 @@ pub fn compile_input(sysroot: Option<Path>,
 // 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 {
+// also, too many arguments
+pub fn compile_crate_from_input(input: driver::input,
+                                pkg_id: PkgId,
+                                build_dir_opt: Option<Path>,
+                                sess: session::Session,
+                                crate_opt: Option<@ast::crate>,
+                                out_file: Path,
+                                binary: ~str,
+                                what: driver::compile_upto) -> @ast::crate {
     debug!("Calling build_output_filenames with %?", build_dir_opt);
-    let outputs = driver::build_output_filenames(input, &build_dir_opt, &None, sess);
+    let outputs = driver::build_output_filenames(input, &build_dir_opt, &Some(out_file), 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);
+            assert!(what == driver::cu_everything);
             driver::compile_rest(sess, cfg, driver::cu_everything, Some(outputs), Some(c));
             c
         }
         None => {
             debug!("Calling compile_upto, outputs = %?", outputs);
-            let (crate, _) = driver::compile_upto(sess, cfg, input, driver::cu_parse,
-                                                  Some(outputs));
-            crate
+            let (crate, _) = driver::compile_upto(sess, cfg, input,
+                                                  driver::cu_parse, Some(outputs));
+
+            // Inject the inferred link_meta info if it's not already there
+            // (assumes that name and vers are the only linkage metas)
+            let mut crate_to_use = crate;
+            if attr::find_linkage_metas(crate.node.attrs).is_empty() {
+                crate_to_use = add_attrs(*crate, ~[mk_attr(@dummy_spanned(meta_list(@~"link",
+                                                  // change PkgId to have a <shortname> field?
+                    ~[@dummy_spanned(meta_name_value(@~"name",
+                                                    mk_string_lit(@pkg_id.path.filestem().get()))),
+                      @dummy_spanned(meta_name_value(@~"vers",
+                                                    mk_string_lit(@pkg_id.version.to_str())))])))]);
+            }
+
+
+            driver::compile_rest(sess, cfg, what, Some(outputs), Some(crate_to_use));
+            crate_to_use
         }
     }
 }
@@ -525,17 +560,30 @@ pub fn exe_suffix() -> ~str { ~".exe" }
 pub fn exe_suffix() -> ~str { ~"" }
 
 
+/// Returns a copy of crate `c` with attributes `attrs` added to its
+/// attributes
+fn add_attrs(c: ast::crate, new_attrs: ~[attribute]) -> @ast::crate {
+    @spanned {
+        node: crate_ {
+            attrs: c.node.attrs + new_attrs, ..c.node
+        },
+        span: c.span
+    }
+}
+
 // Called by build_crates
 // FIXME (#4432): Use workcache to only compile when needed
-pub fn compile_crate(sysroot: Option<Path>, crate: &Path, dir: &Path,
+pub fn compile_crate(sysroot: Option<Path>, pkg_id: PkgId,
+                     crate: &Path, dir: &Path,
                      flags: ~[~str], cfgs: ~[~str], opt: bool,
-                     test: bool) -> bool {
+                     test: bool, crate_type: crate_type) -> bool {
     debug!("compile_crate: crate=%s, dir=%s", crate.to_str(), dir.to_str());
-    debug!("compile_crate: flags =...");
+    debug!("compile_crate: short_name = %s, flags =...", pkg_id.to_str());
     for flags.each |&fl| {
         debug!("+++ %s", fl);
     }
-    compile_input(sysroot, crate, dir, flags, cfgs, opt, test)
+    compile_input(sysroot, pkg_id,
+                  crate, dir, flags, cfgs, opt, test, crate_type)
 }
 
 
@@ -563,6 +611,13 @@ pub fn link_exe(src: &Path, dest: &Path) -> bool {
     }
 }
 
+pub fn mk_string_lit(s: @~str) -> ast::lit {
+    spanned {
+        node: ast::lit_str(s),
+        span: dummy_sp()
+    }
+}
+
 #[cfg(test)]
 mod test {
     use super::{is_cmd, parse_name};