about summary refs log tree commit diff
diff options
context:
space:
mode:
authorTim Chevalier <chevalier@alum.wellesley.edu>2013-04-22 17:54:54 -0700
committerTim Chevalier <chevalier@alum.wellesley.edu>2013-04-24 17:51:30 -0700
commit6a3e26aa4062019629e144196a7852ef039f8b0a (patch)
tree47fdbee5268417865977956dd690b9418aa61c7a
parentc2af1de95b65a0cc2225565f47aa9c08fd072371 (diff)
rustpkg: Make path searching work as described in the rustpkg doc
rustpkg now searches for package directories in ./src rather than
in . . I also added a rudimentary RUST_PATH that's currently
hard-wired to the current directory. rustpkg now uses src/, lib/,
and build/ directories as described in the manual.

Most of the existing test scenarios build now; the README file
(in a separate commit) explains which ones.
-rw-r--r--src/librustpkg/path_util.rs168
-rw-r--r--src/librustpkg/rustpkg.rc158
-rw-r--r--src/librustpkg/util.rs15
3 files changed, 190 insertions, 151 deletions
diff --git a/src/librustpkg/path_util.rs b/src/librustpkg/path_util.rs
index cd9b44c278e..a8931e52747 100644
--- a/src/librustpkg/path_util.rs
+++ b/src/librustpkg/path_util.rs
@@ -15,39 +15,43 @@ use core::{os, str};
 use core::option::*;
 use util::PkgId;
 
-/// Returns the output directory to use.
-/// Right now is always the default, should
-/// support changing it.
-pub fn dest_dir(pkgid: PkgId) -> Path {
-    default_dest_dir(&pkgid.path)
+#[deriving(Eq)]
+pub enum OutputType { Main, Lib, Bench, Test }
+
+/// Returns the value of RUST_PATH, as a list
+/// of Paths. In general this should be read from the
+/// environment; for now, it's hard-wired to just be "."
+pub fn rust_path() -> ~[Path] {
+    ~[Path(".")]
 }
 
-/// Returns the default output directory for compilation.
-/// Creates that directory if it doesn't exist.
-pub fn default_dest_dir(pkg_dir: &Path) -> Path {
+/// Creates a directory that is readable, writeable,
+/// and executable by the user. Returns true iff creation
+/// succeeded.
+pub fn make_dir_rwx(p: &Path) -> bool {
     use core::libc::consts::os::posix88::{S_IRUSR, S_IWUSR, S_IXUSR};
-    use conditions::bad_path::cond;
 
-    // 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 {
-            rslt
-        }
-        else {
-            cond.raise((rslt, ~"Path names a file that isn't a directory"))
-        }
+    os::make_dir(p, (S_IRUSR | S_IWUSR | S_IXUSR) as i32)
+}
+
+/// Creates a directory that is readable, writeable,
+/// and executable by the user. Returns true iff creation
+/// succeeded. Also creates all intermediate subdirectories
+/// if they don't already exist.
+pub fn mkdir_recursive(p: &Path) -> bool {
+    if os::path_is_dir(p) {
+        return true;
+    }
+    let parent = p.dir_path();
+    debug!("mkdir_recursive: parent = %s",
+           parent.to_str());
+    if parent.to_str() == ~"."
+        || parent.to_str() == ~"/" { // !!!
+        // No parent directories to create
+        os::path_is_dir(&parent) && make_dir_rwx(p)
     }
     else {
-        // Create it
-        if os::make_dir(&rslt, (S_IRUSR | S_IWUSR | S_IXUSR) as i32) {
-            rslt
-        }
-        else {
-            cond.raise((rslt, ~"Could not create directory"))
-        }
+        mkdir_recursive(&parent) && make_dir_rwx(p)
     }
 }
 
@@ -69,34 +73,94 @@ pub fn normalize(p: ~Path) -> ~Path {
     }
 }
 
+// n.b. So far this only handles local workspaces
+// n.b. The next three functions ignore the package version right
+// now. Should fix that.
+
+/// True if there's a directory in <workspace> with
+/// pkgid's short name
+pub fn workspace_contains_package_id(pkgid: PkgId, workspace: &Path) -> bool {
+    let pkgpath = workspace.push("src").push(pkgid.path.to_str());
+    os::path_is_dir(&pkgpath)
+}
+
+/// Return the directory for <pkgid>'s source files in <workspace>.
+/// Doesn't check that it exists.
+pub fn pkgid_src_in_workspace(pkgid: PkgId, workspace: &Path) -> Path {
+    let result = workspace.push("src");
+    result.push(pkgid.path.to_str())
+}
+
+/// Returns the executable that would be installed for <pkgid>
+/// in <workspace>
+pub fn target_executable_in_workspace(pkgid: PkgId, workspace: &Path) -> Path {
+    let result = workspace.push("bin");
+    // should use a target-specific subdirectory
+    mk_output_path(Main, pkgid.path.to_str(), result)
+}
+
+
+/// Returns the executable that would be installed for <pkgid>
+/// in <workspace>
+pub fn target_library_in_workspace(pkgid: PkgId, workspace: &Path) -> Path {
+    let result = workspace.push("lib");
+    mk_output_path(Lib, pkgid.path.to_str(), result)
+}
+
+/// Returns the test executable that would be installed for <pkgid>
+/// in <workspace>
+pub fn target_test_in_workspace(pkgid: PkgId, workspace: &Path) -> Path {
+    let result = workspace.push("build");
+    mk_output_path(Test, pkgid.path.to_str(), result)
+}
+
+/// Returns the bench executable that would be installed for <pkgid>
+/// in <workspace>
+pub fn target_bench_in_workspace(pkgid: PkgId, workspace: &Path) -> Path {
+    let result = workspace.push("build");
+    mk_output_path(Bench, pkgid.path.to_str(), result)
+}
+
+/// Return the directory for <pkgid>'s build artifacts in <workspace>.
+/// Creates it if it doesn't exist.
+pub fn build_pkg_id_in_workspace(pkgid: PkgId, workspace: &Path) -> Path {
+    use conditions::bad_path::cond;
+
+    let mut result = workspace.push("build");
+    // n.b. Should actually use a target-specific
+    // subdirectory of build/
+    result = result.push(normalize(~pkgid.path).to_str());
+    if os::path_exists(&result) || mkdir_recursive(&result) {
+        result
+    }
+    else {
+        cond.raise((result, fmt!("Could not create directory for package %s", pkgid.to_str())))
+    }
+}
+
+/// Return the output file for a given directory name,
+/// given whether we're building a library and whether we're building tests
+pub fn mk_output_path(what: OutputType, short_name: ~str, dir: Path) -> Path {
+    match what {
+        Lib => dir.push(os::dll_filename(short_name)),
+        _ => dir.push(fmt!("%s%s%s", short_name,
+                           if what == Test { ~"test" } else { ~"" },
+                           os::EXE_SUFFIX))
+    }
+}
+
 #[cfg(test)]
 mod test {
-    use core::{os, rand};
-    use core::path::Path;
-    use path_util::*;
-    use core::rand::RngUtil;
-
-    // Helper function to create a directory name that doesn't exist
-    pub fn mk_nonexistent(tmpdir: &Path, suffix: &str) -> Path {
-        let r = rand::rng();
-        for 1000.times {
-            let p = tmpdir.push(r.gen_str(16) + suffix);
-            if !os::path_exists(&p) {
-                return p;
-            }
-        }
-        fail!(~"Couldn't compute a non-existent path name; this is worrisome")
-    }
+    use core::os;
 
     #[test]
-    fn default_dir_ok() {
-        let the_path = os::tmpdir();
-        let substitute_path = Path("xyzzy");
-        assert!(default_dest_dir(&the_path) == the_path.push(~"build"));
-        let nonexistent_path = mk_nonexistent(&the_path, "quux");
-        let bogus = do ::conditions::bad_path::cond.trap(|_| {
-            substitute_path
-        }).in { default_dest_dir(&nonexistent_path) };
-        assert!(bogus == substitute_path);
+    fn recursive_mkdir_ok() {
+        let root = os::tmpdir();
+        let path = "xy/z/zy";
+        let nested = root.push(path);
+        assert!(super::mkdir_recursive(&nested));
+        assert!(os::path_is_dir(&root.push("xy")));
+        assert!(os::path_is_dir(&root.push("xy/z")));
+        assert!(os::path_is_dir(&nested));
     }
 }
diff --git a/src/librustpkg/rustpkg.rc b/src/librustpkg/rustpkg.rc
index 705bcc82427..8381e6ad816 100644
--- a/src/librustpkg/rustpkg.rc
+++ b/src/librustpkg/rustpkg.rc
@@ -36,7 +36,8 @@ use rustc::metadata::filesearch;
 use std::{getopts};
 use syntax::{ast, diagnostic};
 use util::*;
-use path_util::{dest_dir, normalize};
+use path_util::{normalize, workspace_contains_package_id};
+use path_util::{build_pkg_id_in_workspace, pkgid_src_in_workspace, rust_path};
 use rustc::driver::session::{lib_crate, bin_crate, crate_type};
 
 mod conditions;
@@ -70,7 +71,7 @@ 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 {
+    fn parse(script: Path, workspace: &Path, id: PkgId) -> PkgScript {
         // Get the executable name that was invoked
         let binary = os::args()[0];
         // Build the rustc session data structures to pass
@@ -85,7 +86,7 @@ impl PkgScript {
         let cfg = driver::build_configuration(sess, @binary, &input);
         let (crate, _) = driver::compile_upto(sess, cfg, &input,
                                               driver::cu_parse, None);
-        let work_dir = dest_dir(id);
+        let work_dir = build_pkg_id_in_workspace(id, workspace);
 
         debug!("Returning package script with id %?", id);
 
@@ -193,54 +194,60 @@ impl Ctx {
                 // 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 = %s", cwd.to_str());
-
-                // Create the package source
-                let mut src = PkgSrc::new(&cwd, &dst_dir, &pkgid);
-                debug!("Package src = %?", src);
-
-                // 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) => {
-                        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"))
+                // Using the RUST_PATH, find workspaces that contain
+                // this package ID
+                let workspaces = rust_path().filtered(|ws|
+                    workspace_contains_package_id(pkgid, ws));
+                if workspaces.is_empty() {
+                    fail!(fmt!("Package %s not found in any of \
+                        the following workspaces: %s",
+                               pkgid.path.to_str(),
+                               rust_path().to_str()));
+                }
+                for workspaces.each |workspace| {
+                    let src_dir   = pkgid_src_in_workspace(pkgid, workspace);
+                    let build_dir = build_pkg_id_in_workspace(pkgid, workspace);
+                    debug!("Destination dir = %s", build_dir.to_str());
+
+                    // Create the package source
+                    let mut src = PkgSrc::new(&workspace.push("src"), &build_dir, &pkgid);
+                    debug!("Package src = %?", src);
+
+                    // Is there custom build logic? If so, use it
+                    let pkg_src_dir = src_dir;
+                    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) => {
+                            let pscript = PkgScript::parse(package_script_path,
+                                                           workspace,
+                                                           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"))
+                            }
+                            custom = true;
+                            // otherwise, the package script succeeded
+                            cfgs
                         }
-                        custom = true;
-                        // otherwise, the package script succeeded
-                        cfgs
-                    }
-                    None => {
-                        debug!("No package script, continuing");
-                        ~[]
+                        None => {
+                            debug!("No package script, continuing");
+                            ~[]
+                        }
+                    };
+
+                    // 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(&build_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" => {
@@ -250,8 +257,8 @@ impl Ctx {
                 // The package id is presumed to be the first command-line
                 // argument
                 let pkgid = PkgId::new(args[0]);
-
-                self.clean(pkgid);
+                let cwd = os::getcwd();
+                self.clean(&cwd, pkgid); // tjc: should use workspace, not cwd
             }
             ~"do" => {
                 if args.len() < 2 {
@@ -304,57 +311,16 @@ impl Ctx {
     }
 
     fn do_cmd(&self, cmd: ~str, pkgname: ~str)  {
-        match cmd {
-            ~"build" | ~"test" => {
-                util::error(~"that command cannot be manually called");
-                fail!(~"do_cmd");
-            }
-            _ => {}
-        }
-
-        let cwd = &os::getcwd();
-        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");
-                    fail!(~"do_cmd");
-                }
-            }
-            None => {
-                util::error(fmt!("invoked `do`, but there is no package script in %s",
-                                 cwd.to_str()));
-                fail!(~"do_cmd");
-            }
-        }
-    }
-
-    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)  {
-        // What's the difference between build and compile?
-        fail!(~"compile not yet implemented");
+        // stub
+        fail!("`do` not yet implemented");
     }
 
-    fn clean(&self, id: PkgId)  {
+    fn clean(&self, workspace: &Path, 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);
+        let dir = build_pkg_id_in_workspace(id, workspace);
         util::note(fmt!("Cleaning package %s (removing directory %s)",
                         id.to_str(), dir.to_str()));
         if os::path_exists(&dir) {
diff --git a/src/librustpkg/util.rs b/src/librustpkg/util.rs
index 2ab1ea1e7d2..36d409adcd2 100644
--- a/src/librustpkg/util.rs
+++ b/src/librustpkg/util.rs
@@ -116,7 +116,14 @@ pub impl PkgId {
 impl ToStr for PkgId {
     fn to_str(&self) -> ~str {
         // should probably use the filestem and not the whole path
-        fmt!("%s-%s", self.path.to_str(), self.version.to_str())
+        fmt!("%s-%s", self.path.to_str(),
+             // Replace dots with -s in the version
+             // this is because otherwise rustc will think
+             // that foo-0.1 has .1 as its extension
+             // (Temporary hack until I figure out how to
+             // get rustc to not name the object file
+             // foo-0.o if I pass in foo-0.1 to build_output_filenames)
+             str::replace(self.version.to_str(), ".", "-"))
     }
 }
 
@@ -438,7 +445,9 @@ pub fn compile_input(sysroot: Option<Path>,
                      test: bool,
                      crate_type: session::crate_type) -> bool {
 
-    let short_name = pkg_id.to_str();
+    // Want just the directory component here
+    let pkg_filename = pkg_id.path.filename().expect(~"Weird pkg id");
+    let short_name = fmt!("%s-%s", pkg_filename, pkg_id.version.to_str());
 
     assert!(in_file.components.len() > 1);
     let input = driver::file_input(copy *in_file);
@@ -515,7 +524,7 @@ pub fn compile_crate_from_input(input: driver::input,
                                 out_file: Path,
                                 binary: ~str,
                                 what: driver::compile_upto) -> @ast::crate {
-    debug!("Calling build_output_filenames with %?", build_dir_opt);
+    debug!("Calling build_output_filenames with %? and %s", build_dir_opt, out_file.to_str());
     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);