diff --git a/src/librustpkg/conditions.rs b/src/librustpkg/conditions.rs index 5b19a3bd66042..680e0924d798c 100644 --- a/src/librustpkg/conditions.rs +++ b/src/librustpkg/conditions.rs @@ -28,3 +28,7 @@ condition! { condition! { missing_pkg_files: (super::PkgId) -> (); } + +condition! { + bad_pkg_id: (super::Path, ~str) -> ::util::PkgId; +} diff --git a/src/librustpkg/path_util.rs b/src/librustpkg/path_util.rs index 477a7af45504b..bbd8d09235484 100644 --- a/src/librustpkg/path_util.rs +++ b/src/librustpkg/path_util.rs @@ -10,12 +10,10 @@ // rustpkg utilities having to do with paths and directories -use util::PkgId; +pub use util::{PkgId, RemotePath, LocalPath}; use core::libc::consts::os::posix88::{S_IRUSR, S_IWUSR, S_IXUSR}; use core::os::mkdir_recursive; - -#[deriving(Eq)] -pub enum OutputType { Main, Lib, Bench, Test } +pub use util::{normalize, OutputType, Main, Lib, Bench, Test}; /// Returns the value of RUST_PATH, as a list /// of Paths. In general this should be read from the @@ -31,24 +29,6 @@ pub static u_rwx: i32 = (S_IRUSR | S_IWUSR | S_IXUSR) as i32; /// succeeded. pub fn make_dir_rwx(p: &Path) -> bool { os::make_dir(p, u_rwx) } -/// 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 - } - } - } -} - // n.b. So far this only handles local workspaces // n.b. The next three functions ignore the package version right // now. Should fix that. @@ -56,7 +36,7 @@ pub fn normalize(p: ~Path) -> ~Path { /// True if there's a directory in 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()); + let pkgpath = workspace.push("src").push(pkgid.local_path.to_str()); os::path_is_dir(&pkgpath) } @@ -64,34 +44,58 @@ pub fn workspace_contains_package_id(pkgid: &PkgId, workspace: &Path) -> bool { /// 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()) + result.push(pkgid.local_path.to_str()) } /// Figure out what the executable name for in 's build /// directory is, and if the file exists, return it. pub fn built_executable_in_workspace(pkgid: &PkgId, workspace: &Path) -> Option { let mut result = workspace.push("build"); - result = result.push_rel(&pkgid.path); // should use a target-specific subdirectory - result = mk_output_path(Main, fmt!("%s-%s", pkgid.path.to_str(), pkgid.version.to_str()), - result); + result = mk_output_path(Main, pkgid, &result); debug!("built_executable_in_workspace: checking whether %s exists", result.to_str()); if os::path_exists(&result) { Some(result) } else { + // This is not an error, but it's worth logging it + error!(fmt!("built_executable_in_workspace: %s does not exist", result.to_str())); None } } -/// Figure out what the library name for in 's build +/// Figure out what the test name for in 's build /// directory is, and if the file exists, return it. -pub fn built_library_in_workspace(pkgid: &PkgId, workspace: &Path) -> Option { +pub fn built_test_in_workspace(pkgid: &PkgId, workspace: &Path) -> Option { + output_in_workspace(pkgid, workspace, Test) +} + +/// Figure out what the test name for in 's build +/// directory is, and if the file exists, return it. +pub fn built_bench_in_workspace(pkgid: &PkgId, workspace: &Path) -> Option { + output_in_workspace(pkgid, workspace, Bench) +} + +fn output_in_workspace(pkgid: &PkgId, workspace: &Path, what: OutputType) -> Option { let mut result = workspace.push("build"); - result = result.push_rel(&pkgid.path); // should use a target-specific subdirectory - result = mk_output_path(Lib, pkgid.path.to_str(), result); + result = mk_output_path(what, pkgid, &result); + debug!("output_in_workspace: checking whether %s exists", + result.to_str()); + if os::path_exists(&result) { + Some(result) + } + else { + error!(fmt!("output_in_workspace: %s does not exist", result.to_str())); + None + } +} + +/// Figure out what the library name for in 's build +/// directory is, and if the file exists, return it. +pub fn built_library_in_workspace(pkgid: &PkgId, workspace: &Path) -> Option { + let result = mk_output_path(Lib, pkgid, &workspace.push("build")); debug!("built_library_in_workspace: checking whether %s exists", result.to_str()); @@ -100,8 +104,7 @@ pub fn built_library_in_workspace(pkgid: &PkgId, workspace: &Path) -> Option Path { /// Returns the test executable that would be installed for /// in +/// note that we *don't* install test executables, so this is just for unit testing pub fn target_test_in_workspace(pkgid: &PkgId, workspace: &Path) -> Path { target_file_in_workspace(pkgid, workspace, Test) } /// Returns the bench executable that would be installed for /// in +/// note that we *don't* install bench executables, so this is just for unit testing pub fn target_bench_in_workspace(pkgid: &PkgId, workspace: &Path) -> Path { target_file_in_workspace(pkgid, workspace, Bench) } @@ -187,18 +192,14 @@ fn target_file_in_workspace(pkgid: &PkgId, workspace: &Path, what: OutputType) -> Path { use conditions::bad_path::cond; - let (subdir, create_dir) = match what { - Main => ("bin", true), Lib => ("lib", true), Test | Bench => ("build", false) + let subdir = match what { + Lib => "lib", Main | Test | Bench => "bin" }; let result = workspace.push(subdir); - if create_dir { - if !os::path_exists(&result) && !mkdir_recursive(&result, u_rwx) { - cond.raise((copy result, - fmt!("I couldn't create the %s dir", subdir))); - } + if !os::path_exists(&result) && !mkdir_recursive(&result, u_rwx) { + cond.raise((copy result, fmt!("I couldn't create the %s dir", subdir))); } - mk_output_path(what, pkgid.path.to_str(), result) - + mk_output_path(what, pkgid, &result) } /// Return the directory for 's build artifacts in . @@ -209,7 +210,7 @@ pub fn build_pkg_id_in_workspace(pkgid: &PkgId, workspace: &Path) -> Path { let mut result = workspace.push("build"); // n.b. Should actually use a target-specific // subdirectory of build/ - result = result.push(normalize(~copy pkgid.path).to_str()); + result = result.push_rel(&*pkgid.local_path); if os::path_exists(&result) || os::mkdir_recursive(&result, u_rwx) { result } @@ -220,15 +221,26 @@ pub fn build_pkg_id_in_workspace(pkgid: &PkgId, workspace: &Path) -> Path { /// 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, +pub fn mk_output_path(what: OutputType, pkg_id: &PkgId, workspace: &Path) -> Path { + let short_name_with_version = pkg_id.short_name_with_version(); + // Not local_path.dir_path()! For package foo/bar/blat/, we want + // the executable blat-0.5 to live under blat/ + let dir = workspace.push_rel(&*pkg_id.local_path); + debug!("mk_output_path: short_name = %s, path = %s", + if what == Lib { copy short_name_with_version } else { copy pkg_id.short_name }, + dir.to_str()); + let output_path = match what { + // this code is duplicated from elsewhere; fix this + Lib => dir.push(os::dll_filename(short_name_with_version)), + // executable names *aren't* versioned + _ => dir.push(fmt!("%s%s%s", copy pkg_id.short_name, match what { Test => "test", Bench => "bench", _ => "" } os::EXE_SUFFIX)) - } + }; + debug!("mk_output_path: returning %s", output_path.to_str()); + output_path } diff --git a/src/librustpkg/rustpkg.rc b/src/librustpkg/rustpkg.rc index 884f0a73589dc..a69613776efdc 100644 --- a/src/librustpkg/rustpkg.rc +++ b/src/librustpkg/rustpkg.rc @@ -30,12 +30,10 @@ use rustc::metadata::filesearch; use std::{getopts}; use syntax::{ast, diagnostic}; use util::*; -use path_util::normalize; -use path_util::{build_pkg_id_in_workspace, pkgid_src_in_workspace}; +use path_util::{build_pkg_id_in_workspace, pkgid_src_in_workspace, u_rwx}; use path_util::{built_executable_in_workspace, built_library_in_workspace}; use path_util::{target_executable_in_workspace, target_library_in_workspace}; use workspace::pkg_parent_workspaces; -use rustc::driver::session::{lib_crate, bin_crate, crate_type}; use context::Ctx; mod conditions; @@ -159,27 +157,6 @@ impl<'self> PkgScript<'self> { impl Ctx { fn run(&self, cmd: ~str, args: ~[~str]) { - let root = util::root(); - - util::need_dir(&root); - util::need_dir(&root.push(~"work")); - util::need_dir(&root.push(~"lib")); - util::need_dir(&root.push(~"bin")); - util::need_dir(&root.push(~"tmp")); - - fn sep_name_vers(in: ~str) -> (Option<~str>, Option<~str>) { - let mut name = None; - let mut vers = None; - - for str::each_split_char(in, '@') |s| { - if name.is_none() { name = Some(s.to_owned()); } - else if vers.is_none() { vers = Some(s.to_owned()); } - else { break; } - } - - (name, vers) - } - match cmd { ~"build" => { if args.len() < 1 { @@ -229,9 +206,7 @@ impl Ctx { return usage::uninstall(); } - let (name, vers) = sep_name_vers(copy args[0]); - - self.prefer(name.get(), vers); + self.prefer(args[0], None); } ~"test" => { self.test(); @@ -241,20 +216,16 @@ impl Ctx { return usage::uninstall(); } - let (name, vers) = sep_name_vers(copy args[0]); - - self.uninstall(name.get(), vers); + self.uninstall(args[0], None); } ~"unprefer" => { if args.len() < 1 { return usage::uninstall(); } - let (name, vers) = sep_name_vers(copy args[0]); - - self.unprefer(name.get(), vers); + self.unprefer(args[0], None); } - _ => fail!("reached an unhandled command") + _ => fail!(fmt!("I don't know the command `%s`", cmd)) } } @@ -269,7 +240,7 @@ impl Ctx { debug!("Destination dir = %s", build_dir.to_str()); // Create the package source - let mut src = PkgSrc::new(&workspace.push("src"), &build_dir, pkgid); + let mut src = PkgSrc::new(workspace, &build_dir, pkgid); debug!("Package src = %?", src); // Is there custom build logic? If so, use it @@ -307,7 +278,6 @@ impl Ctx { // Build it! src.build(&build_dir, cfgs, self.sysroot_opt); } - } fn clean(&self, workspace: &Path, id: &PkgId) { @@ -319,7 +289,7 @@ impl Ctx { 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); + os::remove_dir_recursive(&dir); util::note(fmt!("Removed directory %s", dir.to_str())); } @@ -337,6 +307,8 @@ impl Ctx { // Should use RUST_PATH in the future. // Also should use workcache to not build if not necessary. self.build(workspace, id); + debug!("install: workspace = %s, id = %s", workspace.to_str(), + id.to_str()); // Now copy stuff into the install dirs let maybe_executable = built_executable_in_workspace(id, workspace); @@ -344,104 +316,29 @@ impl Ctx { let target_exec = target_executable_in_workspace(id, workspace); let target_lib = target_library_in_workspace(id, workspace); + debug!("target_exec = %s target_lib = %s \ + maybe_executable = %? maybe_library = %?", + target_exec.to_str(), target_lib.to_str(), + maybe_executable, maybe_library); + for maybe_executable.each |exec| { debug!("Copying: %s -> %s", exec.to_str(), target_exec.to_str()); - if !os::copy_file(exec, &target_exec) { + if !(os::mkdir_recursive(&target_exec.dir_path(), u_rwx) && + os::copy_file(exec, &target_exec)) { cond.raise((copy *exec, copy target_exec)); } } for maybe_library.each |lib| { debug!("Copying: %s -> %s", lib.to_str(), target_lib.to_str()); - if !os::copy_file(lib, &target_lib) { + if !(os::mkdir_recursive(&target_lib.dir_path(), u_rwx) && + os::copy_file(lib, &target_lib)) { cond.raise((copy *lib, copy target_lib)); } } } - fn fetch(&self, _dir: &Path, _url: ~str, _target: Option<~str>) { - // stub - fail!("fetch not yet implemented"); - } - - 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"); - - if run::program_output(~"curl", ~[~"-f", ~"-s", - ~"-o", tar.to_str(), - url]).status != 0 { - util::error(~"fetching failed: downloading using curl failed"); - - fail!(); - } - - if run::program_output(~"tar", ~[~"-x", ~"--strip-components=1", - ~"-C", dir.to_str(), ~"-f", - tar.to_str()]).status != 0 { - util::error(~"fetching failed: extracting using tar failed" + - ~"(is it a valid tar archive?)"); - - fail!(); - } - } - - fn fetch_git(&self, dir: &Path, url: ~str, mut target: Option<~str>) { - util::note(fmt!("fetching from %s using git", url)); - - // Git can't clone into a non-empty directory - util::remove_dir_r(dir); - - if run::program_output(~"git", ~[~"clone", url, - dir.to_str()]).status != 0 { - util::error(~"fetching failed: can't clone repository"); - fail!(); - } - - if !target.is_none() { - let mut success = true; - - do util::temp_change_dir(dir) { - success = run::program_output(~"git", - ~[~"checkout", - target.swap_unwrap()]).status != 0 - } - - if !success { - util::error(~"fetching failed: can't checkout target"); - fail!(); - } - } - } - - 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); - fail!(); // Condition? - } - }; - let name = package.id.path.to_str(); // ??? - - util::note(fmt!("preferring %s v%s", name, package.id.version.to_str())); - - 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()); - - util::link_exe(&path, &out); - util::note(fmt!("linked %s", out.to_str())); - } - - util::note(fmt!("preferred %s v%s", name, package.id.version.to_str())); + fn prefer(&self, _id: &str, _vers: Option<~str>) { + fail!(~"prefer not yet implemented"); } fn test(&self) { @@ -449,15 +346,16 @@ impl Ctx { fail!("test not yet implemented"); } - fn uninstall(&self, _id: ~str, _vers: Option<~str>) { + fn uninstall(&self, _id: &str, _vers: Option<~str>) { fail!("uninstall not yet implemented"); } - fn unprefer(&self, _id: ~str, _vers: Option<~str>) { + fn unprefer(&self, _id: &str, _vers: Option<~str>) { fail!("unprefer not yet implemented"); } } + pub fn main() { io::println("WARNING: The Rust package manager is experimental and may be unstable"); @@ -518,32 +416,6 @@ pub struct Crate { cfgs: ~[~str] } -pub struct Listener { - cmds: ~[~str], - cb: ~fn() -} - -pub fn run(listeners: ~[Listener]) { - let rcmd = copy os::args()[2]; - let mut found = false; - - for listeners.each |listener| { - for listener.cmds.each |&cmd| { - if cmd == rcmd { - (listener.cb)(); - - found = true; - - break; - } - } - } - - if !found { - os::set_exit_status(42); - } -} - pub impl Crate { fn new(p: &Path) -> Crate { @@ -602,10 +474,6 @@ pub fn src_dir() -> Path { os::getcwd() } -condition! { - bad_pkg_id: (super::Path, ~str) -> ::util::PkgId; -} - // An enumeration of the unpacked source of a package workspace. // This contains a list of files found in the source workspace. pub struct PkgSrc { @@ -641,17 +509,19 @@ impl PkgSrc { fn check_dir(&self) -> Path { use conditions::nonexistent_package::cond; - debug!("Pushing onto root: %s | %s", self.id.path.to_str(), + debug!("Pushing onto root: %s | %s", self.id.to_str(), self.root.to_str()); - let dir = self.root.push_rel(&self.id.path).normalize(); + let mut dir = self.root.push("src"); + dir = dir.push(self.id.to_str()); // ?? Should this use the version number? debug!("Checking dir: %s", dir.to_str()); - // tjc: Rather than erroring out, need to try downloading the - // contents of the path to a local directory (#5679) if !os::path_exists(&dir) { - cond.raise((copy self.id, ~"missing package dir")); + if !self.fetch_git() { + cond.raise((copy self.id, ~"supplied path for package dir does not \ + exist, and couldn't interpret it as a URL fragment")); + } } if !os::path_is_dir(&dir) { @@ -662,6 +532,28 @@ impl PkgSrc { dir } + /// Try interpreting self's package id as a remote package, and try + /// fetching it and caching it in a local directory. If that didn't + /// work, return false. + /// (right now we only support git) + fn fetch_git(&self) -> bool { + + let mut local = self.root.push("src"); + local = local.push(self.id.to_str()); + // Git can't clone into a non-empty directory + os::remove_dir_recursive(&local); + + let url = fmt!("https://%s", self.id.remote_path.to_str()); + util::note(fmt!("git clone %s %s", url, local.to_str())); + + if run::program_output(~"git", ~[~"clone", copy url, local.to_str()]).status != 0 { + util::note(fmt!("fetching %s failed: can't clone repository", url)); + return false; + } + true + } + + // 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 { @@ -680,7 +572,7 @@ impl PkgSrc { /// Requires that dashes in p have already been normalized to /// underscores fn stem_matches(&self, p: &Path) -> bool { - let self_id = normalize(~copy self.id.path).filestem(); + let self_id = self.id.local_path.filestem(); if self_id == p.filestem() { return true; } @@ -715,7 +607,7 @@ impl PkgSrc { let dir = self.check_dir(); let prefix = dir.components.len(); - debug!("Matching against %?", self.id.path.filestem()); + debug!("Matching against %?", self.id.local_path.filestem()); for os::walk_dir(&dir) |pth| { match pth.filename() { Some(~"lib.rs") => push_crate(&mut self.libs, @@ -752,8 +644,7 @@ impl PkgSrc { src_dir: &Path, crates: &[Crate], cfgs: &[~str], - test: bool, crate_type: crate_type) { - + what: OutputType) { for crates.each |&crate| { let path = &src_dir.push_rel(&crate.file).normalize(); util::note(fmt!("build_crates: compiling %s", path.to_str())); @@ -763,7 +654,7 @@ impl PkgSrc { dst_dir, crate.flags, crate.cfgs + cfgs, - false, test, crate_type); + false, what); if !result { build_err::cond.raise(fmt!("build failure on %s", path.to_str())); @@ -776,12 +667,13 @@ impl PkgSrc { fn build(&self, dst_dir: &Path, cfgs: ~[~str], maybe_sysroot: Option<@Path>) { let dir = self.check_dir(); debug!("Building libs"); - self.build_crates(maybe_sysroot, dst_dir, &dir, self.libs, cfgs, false, lib_crate); + self.build_crates(maybe_sysroot, dst_dir, &dir, self.libs, cfgs, Lib); debug!("Building mains"); - self.build_crates(maybe_sysroot, dst_dir, &dir, self.mains, cfgs, false, bin_crate); + self.build_crates(maybe_sysroot, dst_dir, &dir, self.mains, cfgs, Main); debug!("Building tests"); - self.build_crates(maybe_sysroot, dst_dir, &dir, self.tests, cfgs, true, bin_crate); + self.build_crates(maybe_sysroot, dst_dir, &dir, self.tests, cfgs, Test); debug!("Building benches"); - self.build_crates(maybe_sysroot, dst_dir, &dir, self.benchs, cfgs, true, bin_crate); + self.build_crates(maybe_sysroot, dst_dir, &dir, self.benchs, cfgs, Bench); } } + diff --git a/src/librustpkg/tests.rs b/src/librustpkg/tests.rs index e661e758d41a5..487e8d0da36e2 100644 --- a/src/librustpkg/tests.rs +++ b/src/librustpkg/tests.rs @@ -17,8 +17,8 @@ use std::tempfile::mkdtemp; use util::{PkgId, default_version}; use path_util::{target_executable_in_workspace, target_library_in_workspace, target_test_in_workspace, target_bench_in_workspace, - make_dir_rwx, u_rwx}; -use core::os::mkdir_recursive; + make_dir_rwx, u_rwx, RemotePath, LocalPath, normalize, + built_bench_in_workspace, built_test_in_workspace}; fn fake_ctxt(sysroot_opt: Option<@Path>) -> Ctx { Ctx { @@ -29,15 +29,22 @@ fn fake_ctxt(sysroot_opt: Option<@Path>) -> Ctx { } fn fake_pkg() -> PkgId { + let sn = ~"bogus"; + let remote = RemotePath(Path(sn)); PkgId { - path: Path(~"bogus"), + local_path: normalize(copy remote), + remote_path: remote, + short_name: sn, version: default_version() } } fn remote_pkg() -> PkgId { + let remote = RemotePath(Path(~"github.com/catamorphism/test-pkg")); PkgId { - path: Path(~"github.com/catamorphism/test-pkg"), + local_path: normalize(copy remote), + remote_path: remote, + short_name: ~"test_pkg", version: default_version() } } @@ -49,10 +56,11 @@ fn writeFile(file_path: &Path, contents: ~str) { out.write_line(contents); } -fn mk_temp_workspace(short_name: &Path) -> Path { +fn mk_temp_workspace(short_name: &LocalPath) -> Path { let workspace = mkdtemp(&os::tmpdir(), "test").expect("couldn't create temp dir"); - let package_dir = workspace.push(~"src").push_rel(short_name); - assert!(mkdir_recursive(&package_dir, u_rwx)); + // include version number in directory name + let package_dir = workspace.push(~"src").push(fmt!("%s-0.1", short_name.to_str())); + assert!(os::mkdir_recursive(&package_dir, u_rwx)); // Create main, lib, test, and bench files writeFile(&package_dir.push(~"main.rs"), ~"fn main() { let _x = (); }"); @@ -104,7 +112,7 @@ fn test_install_valid() { debug!("sysroot = %s", sysroot.to_str()); let ctxt = fake_ctxt(Some(@sysroot)); let temp_pkg_id = fake_pkg(); - let temp_workspace = mk_temp_workspace(&temp_pkg_id.path); + let temp_workspace = mk_temp_workspace(&temp_pkg_id.local_path); // should have test, bench, lib, and main ctxt.install(&temp_workspace, &temp_pkg_id); // Check that all files exist @@ -146,27 +154,79 @@ fn test_install_invalid() { } #[test] -#[ignore(reason = "install from URL-fragment not yet implemented")] fn test_install_url() { + let workspace = mkdtemp(&os::tmpdir(), "test").expect("couldn't create temp dir"); let sysroot = test_sysroot(); debug!("sysroot = %s", sysroot.to_str()); let ctxt = fake_ctxt(Some(@sysroot)); let temp_pkg_id = remote_pkg(); - let temp_workspace = mk_temp_workspace(&temp_pkg_id.path); // should have test, bench, lib, and main - ctxt.install(&temp_workspace, &temp_pkg_id); + ctxt.install(&workspace, &temp_pkg_id); // Check that all files exist - let exec = target_executable_in_workspace(&temp_pkg_id, &temp_workspace); + let exec = target_executable_in_workspace(&temp_pkg_id, &workspace); debug!("exec = %s", exec.to_str()); assert!(os::path_exists(&exec)); assert!(is_rwx(&exec)); - let lib = target_library_in_workspace(&temp_pkg_id, &temp_workspace); + let lib = target_library_in_workspace(&temp_pkg_id, &workspace); debug!("lib = %s", lib.to_str()); assert!(os::path_exists(&lib)); assert!(is_rwx(&lib)); + let built_test = built_test_in_workspace(&temp_pkg_id, &workspace).expect(~"test_install_url"); + assert!(os::path_exists(&built_test)); + let built_bench = built_bench_in_workspace(&temp_pkg_id, + &workspace).expect(~"test_install_url"); + assert!(os::path_exists(&built_bench)); // And that the test and bench executables aren't installed - assert!(!os::path_exists(&target_test_in_workspace(&temp_pkg_id, &temp_workspace))); - let bench = target_bench_in_workspace(&temp_pkg_id, &temp_workspace); + let test = target_test_in_workspace(&temp_pkg_id, &workspace); + assert!(!os::path_exists(&test)); + debug!("test = %s", test.to_str()); + let bench = target_bench_in_workspace(&temp_pkg_id, &workspace); debug!("bench = %s", bench.to_str()); assert!(!os::path_exists(&bench)); } + +#[test] +fn test_package_ids_must_be_relative_path_like() { + use conditions::bad_pkg_id::cond; + + /* + Okay: + - One identifier, with no slashes + - Several slash-delimited things, with no / at the root + + Not okay: + - Empty string + - Absolute path (as per os::is_absolute) + + */ + + let default_version_str = "0.1"; + let addversion = |s| { + fmt!("%s-%s", s, default_version_str) + }; + + let whatever = PkgId::new("foo"); + + assert!(addversion("foo") == whatever.to_str()); + assert!(addversion("github.com/mozilla/rust") == + PkgId::new("github.com/mozilla/rust").to_str()); + + do cond.trap(|(p, e)| { + assert!("" == p.to_str()); + assert!("0-length pkgid" == e); + copy whatever + }).in { + let x = PkgId::new(""); + assert!(addversion("foo") == x.to_str()); + } + + do cond.trap(|(p, e)| { + assert!(p.to_str() == os::make_absolute(&Path("foo/bar/quux")).to_str()); + assert!("absolute pkgid" == e); + copy whatever + }).in { + let z = PkgId::new(os::make_absolute(&Path("foo/bar/quux")).to_str()); + assert!(addversion("foo") == z.to_str()); + } + +} diff --git a/src/librustpkg/util.rs b/src/librustpkg/util.rs index e9c4b7fde5548..4a9c276948aa0 100644 --- a/src/librustpkg/util.rs +++ b/src/librustpkg/util.rs @@ -13,20 +13,18 @@ use core::cmp::Ord; use core::hash::Streaming; use core::rt::io::Writer; use rustc::driver::{driver, session}; -use rustc::driver::session::{lib_crate, unknown_crate}; use rustc::metadata::filesearch; use std::getopts::groups::getopts; use std::semver; -use std::{json, term, getopts}; +use std::{term, getopts}; use syntax::ast_util::*; 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}; +use syntax::ast::{meta_name_value, meta_list}; use syntax::attr::{mk_attr}; use rustc::back::link::output_type_exe; -use rustc::driver::session::{lib_crate, unknown_crate, crate_type}; +use rustc::driver::session::{lib_crate, bin_crate}; static Commands: &'static [&'static str] = &["build", "clean", "do", "info", "install", "prefer", "test", "uninstall", @@ -83,20 +81,34 @@ impl ToStr for Version { } } +#[deriving(Eq)] +pub enum OutputType { Main, Lib, Bench, Test } + /// Placeholder pub 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. +/// 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, + /// Remote path: for example, github.com/mozilla/quux-whatever + remote_path: RemotePath, + /// Local path: for example, /home/quux/github.com/mozilla/quux_whatever + /// Note that '-' normalizes to '_' when mapping a remote path + /// onto a local path + /// Also, this will change when we implement #6407, though we'll still + /// need to keep track of separate local and remote paths + local_path: LocalPath, + /// Short name. This is the local path's filestem, but we store it + /// redundantly so as to not call get() everywhere (filestem() returns an + /// option) + short_name: ~str, version: Version } pub impl PkgId { fn new(s: &str) -> PkgId { - use bad_pkg_id::cond; + use conditions::bad_pkg_id::cond; let p = Path(s); if p.is_absolute { @@ -105,31 +117,32 @@ pub impl PkgId { if p.components.len() < 1 { return cond.raise((p, ~"0-length pkgid")); } + let remote_path = RemotePath(p); + let local_path = normalize(copy remote_path); + let short_name = (copy local_path).filestem().expect(fmt!("Strange path! %s", s)); PkgId { - path: p, + local_path: local_path, + remote_path: remote_path, + short_name: short_name, version: default_version() } } fn hash(&self) -> ~str { - fmt!("%s-%s-%s", self.path.to_str(), - hash(self.path.to_str() + self.version.to_str()), + fmt!("%s-%s-%s", self.remote_path.to_str(), + hash(self.remote_path.to_str() + self.version.to_str()), self.version.to_str()) } + fn short_name_with_version(&self) -> ~str { + fmt!("%s-%s", self.short_name, 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-%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(), ".", "-")) + fmt!("%s-%s", self.local_path.to_str(), self.version.to_str()) } } @@ -156,26 +169,6 @@ pub fn is_cmd(cmd: &str) -> bool { Commands.any(|&c| c == cmd) } -pub fn parse_name(id: ~str) -> result::Result<~str, ~str> { - let mut last_part = None; - - for str::each_split_char(id, '.') |part| { - for str::each_char(part) |char| { - if char::is_whitespace(char) { - return result::Err( - ~"could not parse id: contains whitespace"); - } else if char::is_uppercase(char) { - return result::Err( - ~"could not parse id: should be all lowercase"); - } - } - last_part = Some(part.to_owned()); - } - if last_part.is_none() { return result::Err(~"could not parse id: is empty"); } - - result::Ok(last_part.unwrap()) -} - struct ListenerFn { cmds: ~[~str], span: codemap::span, @@ -248,52 +241,6 @@ fn fold_item(ctx: @mut ReadyCtx, res } -fn add_pkg_module(ctx: @mut ReadyCtx, m: ast::_mod) -> ast::_mod { - let listeners = mk_listener_vec(ctx); - let ext_cx = ctx.ext_cx; - let item = quote_item! ( - mod __pkg { - extern mod rustpkg (vers="0.7-pre"); - static listeners : &[rustpkg::Listener] = $listeners; - #[main] - fn main() { - rustpkg::run(listeners); - } - } - ); - ast::_mod { - items: vec::append_one(/*bad*/copy m.items, item.get()), - .. m - } -} - -fn mk_listener_vec(ctx: @mut ReadyCtx) -> @ast::expr { - let descs = do ctx.fns.map |listener| { - mk_listener_rec(ctx, listener) - }; - let ext_cx = ctx.ext_cx; - build::mk_slice_vec_e(ext_cx, dummy_sp(), descs) -} - -fn mk_listener_rec(ctx: @mut ReadyCtx, listener: &ListenerFn) -> @ast::expr { - let span = listener.span; - let cmds = do listener.cmds.map |&cmd| { - let ext_cx = ctx.ext_cx; - build::mk_base_str(ext_cx, span, cmd) - }; - - let ext_cx = ctx.ext_cx; - let cmds_expr = build::mk_slice_vec_e(ext_cx, span, cmds); - let cb_expr = build::mk_path(ext_cx, span, copy listener.path); - - quote_expr!( - Listener { - cmds: $cmds_expr, - cb: $cb_expr - } - ) -} - /// Generate/filter main function, add the list of commands, etc. pub fn ready_crate(sess: session::Session, crate: @ast::crate) -> @ast::crate { @@ -375,67 +322,6 @@ pub fn hash(data: ~str) -> ~str { hasher.result_str() } -pub fn temp_change_dir(dir: &Path, cb: &fn() -> T) { - let cwd = os::getcwd(); - - os::change_dir(dir); - cb(); - os::change_dir(&cwd); -} - -pub fn touch(path: &Path) { - match io::mk_file_writer(path, ~[io::Create]) { - result::Ok(writer) => writer.write_line(~""), - _ => {} - } -} - -pub fn remove_dir_r(path: &Path) { - for os::walk_dir(path) |&file| { - let mut cdir = file; - - loop { - if os::path_is_dir(&cdir) { - os::remove_dir(&cdir); - } else { - os::remove_file(&cdir); - } - - cdir = cdir.dir_path(); - - if cdir == *path { break; } - } - } - - os::remove_dir(path); -} - -pub fn wait_for_lock(path: &Path) { - if os::path_exists(path) { - warn(fmt!("the database appears locked, please wait (or rm %s)", - path.to_str())); - - loop { - if !os::path_exists(path) { break; } - } - } -} - -pub fn load_pkgs() -> result::Result<~[json::Json], ~str> { - fail!("load_pkg not implemented"); -} - -pub fn get_pkg(_id: ~str, - _vers: Option<~str>) -> result::Result { - fail!("get_pkg not implemented"); -} - -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 -} - // FIXME (#4432): Use workcache to only compile when needed pub fn compile_input(sysroot: Option<@Path>, pkg_id: &PkgId, @@ -444,31 +330,24 @@ pub fn compile_input(sysroot: Option<@Path>, flags: &[~str], cfgs: &[~str], opt: bool, - test: bool, - crate_type: session::crate_type) -> bool { - - // 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()); + what: OutputType) -> bool { assert!(in_file.components.len() > 1); let input = driver::file_input(copy *in_file); - debug!("compile_input: %s / %?", in_file.to_str(), crate_type); + debug!("compile_input: %s / %?", in_file.to_str(), what); // tjc: by default, use the package ID name as the link name // not sure if we should support anything else - let binary = @copy os::args()[0]; - let building_library = match crate_type { - lib_crate | unknown_crate => true, - _ => false - }; + let binary = @(copy os::args()[0]); + let building_library = what == Lib; let out_file = if building_library { - out_dir.push(os::dll_filename(short_name)) + out_dir.push(os::dll_filename(pkg_id.short_name)) } else { - out_dir.push(short_name + if test { ~"test" } else { ~"" } - + os::EXE_SUFFIX) + out_dir.push(pkg_id.short_name + match what { + Test => ~"test", Bench => ~"bench", Main | Lib => ~"" + } + os::EXE_SUFFIX) }; debug!("compiling %s into %s", @@ -478,18 +357,24 @@ pub fn compile_input(sysroot: Option<@Path>, debug!("cfgs: %s", str::connect(cfgs, ~" ")); debug!("compile_input's sysroot = %?", sysroot); + let crate_type = match what { + Lib => lib_crate, + Test | Bench | Main => bin_crate + }; let matches = getopts(~[~"-Z", ~"time-passes"] - + if building_library { ~[~"--lib"] } - else if test { ~[~"--test"] } - // bench? - else { ~[] } + + match what { + Lib => ~[~"--lib"], + // --test compiles both #[test] and #[bench] fns + Test | Bench => ~[~"--test"], + Main => ~[] + } + flags + cfgs.flat_map(|&c| { ~[~"--cfg", c] }), driver::optgroups()).get(); let mut options = session::options { crate_type: crate_type, optimize: if opt { session::Aggressive } else { session::No }, - test: test, + test: what == Test || what == Bench, maybe_sysroot: sysroot, addl_lib_search_paths: ~[copy *out_dir], // output_type should be conditional @@ -540,26 +425,27 @@ pub fn compile_crate_from_input(input: &driver::input, debug!("Calling compile_upto, outputs = %?", outputs); let (crate, _) = driver::compile_upto(sess, copy cfg, input, driver::cu_parse, Some(outputs)); + let mut crate = crate; debug!("About to inject link_meta info..."); // 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; debug!("How many attrs? %?", attr::find_linkage_metas(crate.node.attrs).len()); if attr::find_linkage_metas(crate.node.attrs).is_empty() { - crate_to_use = add_attrs(copy *crate, - ~[mk_attr(@dummy_spanned(meta_list(@~"link", - // change PkgId to have a 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())))])))]); + crate = @codemap::respan(crate.span, ast::crate_ { + attrs: ~[mk_attr(@dummy_spanned( + meta_list(@~"link", + ~[@dummy_spanned(meta_name_value(@~"name", + mk_string_lit(@(copy pkg_id.short_name)))), + @dummy_spanned(meta_name_value(@~"vers", + mk_string_lit(@(copy pkg_id.version.to_str()))))])))], + ..copy crate.node}); } - driver::compile_rest(sess, cfg, what, Some(outputs), Some(crate_to_use)); - crate_to_use + driver::compile_rest(sess, cfg, what, Some(outputs), Some(crate)); + crate } } } @@ -573,30 +459,41 @@ pub fn exe_suffix() -> ~str { ~".exe" } #[cfg(target_os = "macos")] pub fn exe_suffix() -> ~str { ~"" } - -/// Returns a copy of crate `c` with attributes `attrs` added to its -/// attributes -fn add_attrs(mut c: ast::crate, new_attrs: ~[attribute]) -> @ast::crate { - c.node.attrs += new_attrs; - @c -} - // Called by build_crates // FIXME (#4432): Use workcache to only compile when needed pub fn compile_crate(sysroot: Option<@Path>, pkg_id: &PkgId, crate: &Path, dir: &Path, flags: &[~str], cfgs: &[~str], opt: bool, - test: bool, crate_type: crate_type) -> bool { + what: OutputType) -> bool { debug!("compile_crate: crate=%s, dir=%s", crate.to_str(), dir.to_str()); debug!("compile_crate: short_name = %s, flags =...", pkg_id.to_str()); for flags.each |&fl| { debug!("+++ %s", fl); } - compile_input(sysroot, pkg_id, - crate, dir, flags, cfgs, opt, test, crate_type) + compile_input(sysroot, pkg_id, crate, dir, flags, cfgs, opt, what) +} + +// normalize should be the only way to construct a LocalPath +// (though this isn't enforced) +/// 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_: RemotePath) -> LocalPath { + let RemotePath(p) = p_; + match p.filestem() { + None => LocalPath(p), + Some(st) => { + let replaced = str::replace(st, "-", "_"); + if replaced != st { + LocalPath(p.with_filestem(replaced)) + } + else { + LocalPath(p) + } + } + } } - #[cfg(windows)] pub fn link_exe(_src: &Path, _dest: &Path) -> bool { /* FIXME (#1768): Investigate how to do this on win32 @@ -628,9 +525,13 @@ pub fn mk_string_lit(s: @~str) -> ast::lit { } } +/// Wrappers to prevent local and remote paths from getting confused +pub struct RemotePath (Path); +pub struct LocalPath (Path); + #[cfg(test)] mod test { - use super::{is_cmd, parse_name}; + use super::is_cmd; #[test] fn test_is_cmd() { @@ -645,9 +546,4 @@ mod test { assert!(is_cmd(~"unprefer")); } - #[test] - fn test_parse_name() { - assert!(parse_name(~"org.mozilla.servo").get() == ~"servo"); - assert!(parse_name(~"org. mozilla.servo 2131").is_err()); - } } diff --git a/src/librustpkg/workspace.rs b/src/librustpkg/workspace.rs index 8941dbfd20eab..cb9f735bce8d4 100644 --- a/src/librustpkg/workspace.rs +++ b/src/librustpkg/workspace.rs @@ -21,9 +21,10 @@ pub fn pkg_parent_workspaces(pkgid: &PkgId, action: &fn(&Path) -> bool) -> bool workspace_contains_package_id(pkgid, ws)); if workspaces.is_empty() { // tjc: make this a condition - fail!("Package %s not found in any of the following workspaces: %s", - pkgid.path.to_str(), - rust_path().to_str()); + fail!("Package %s not found in any of \ + the following workspaces: %s", + pkgid.remote_path.to_str(), + rust_path().to_str()); } for workspaces.each |ws| { if action(ws) { diff --git a/src/libuv b/src/libuv index 97ac7c087a0ca..218ab86721eef 160000 --- a/src/libuv +++ b/src/libuv @@ -1 +1 @@ -Subproject commit 97ac7c087a0caf6b0f611b80e14f7fe3cb18bb27 +Subproject commit 218ab86721eefd7b7e97fa6d9f95a80a1fa8686c