diff --git a/doc/rust.md b/doc/rust.md index 2f5c310ec83..d285253ffe2 100644 --- a/doc/rust.md +++ b/doc/rust.md @@ -744,7 +744,7 @@ There are several kinds of view item: ##### Extern mod declarations ~~~~~~~~ {.ebnf .gram} -extern_mod_decl : "extern" "mod" ident [ '(' link_attrs ')' ] ? ; +extern_mod_decl : "extern" "mod" ident [ '(' link_attrs ')' ] ? [ '=' string_lit ] ? ; link_attrs : link_attr [ ',' link_attrs ] + ; link_attr : ident '=' literal ; ~~~~~~~~ @@ -755,13 +755,25 @@ as the `ident` provided in the `extern_mod_decl`. The external crate is resolved to a specific `soname` at compile time, and a runtime linkage requirement to that `soname` is passed to the linker for -loading at runtime. The `soname` is resolved at compile time by scanning the -compiler's library path and matching the `link_attrs` provided in the -`use_decl` against any `#link` attributes that were declared on the external -crate when it was compiled. If no `link_attrs` are provided, a default `name` -attribute is assumed, equal to the `ident` given in the `use_decl`. +loading at runtime. +The `soname` is resolved at compile time by scanning the compiler's library path +and matching the `link_attrs` provided in the `use_decl` against any `#link` attributes that +were declared on the external crate when it was compiled. +If no `link_attrs` are provided, +a default `name` attribute is assumed, +equal to the `ident` given in the `use_decl`. -Three examples of `extern mod` declarations: +Optionally, an identifier in an `extern mod` declaration may be followed by an equals sign, +then a string literal denoting a relative path on the filesystem. +This path should exist in one of the directories in the Rust path, +which by default contains the `.rust` subdirectory of the current directory and each of its parents, +as well as any directories in the colon-separated (or semicolon-separated on Windows) +list of paths that is the `RUST_PATH` environment variable. +The meaning of `extern mod a = "b/c/d";`, supposing that `/a` is in the RUST_PATH, +is that the name `a` should be taken as a reference to the crate whose absolute location is +`/a/b/c/d`. + +Four examples of `extern mod` declarations: ~~~~~~~~{.xfail-test} extern mod pcre (uuid = "54aba0f8-a7b1-4beb-92f1-4cf625264841"); @@ -769,6 +781,8 @@ extern mod pcre (uuid = "54aba0f8-a7b1-4beb-92f1-4cf625264841"); extern mod extra; // equivalent to: extern mod extra ( name = "extra" ); extern mod rustextra (name = "extra"); // linking to 'extra' under another name + +extern mod complicated_mod = "some-file/in/the-rust/path"; ~~~~~~~~ ##### Use declarations diff --git a/mk/tests.mk b/mk/tests.mk index 8e921d300b5..349ffc63d97 100644 --- a/mk/tests.mk +++ b/mk/tests.mk @@ -348,7 +348,9 @@ $(3)/stage$(1)/test/rustpkgtest-$(2)$$(X_$(2)): \ $$(RUSTPKG_LIB) $$(RUSTPKG_INPUTS) \ $$(SREQ$(1)_T_$(2)_H_$(3)) \ $$(TLIB$(1)_T_$(2)_H_$(3))/$$(CFG_LIBSYNTAX_$(2)) \ - $$(TLIB$(1)_T_$(2)_H_$(3))/$$(CFG_LIBRUSTC_$(2)) + $$(TLIB$(1)_T_$(2)_H_$(3))/$$(CFG_LIBRUSTC_$(2)) \ + $$(TBIN$(1)_T_$(2)_H_$(3))/rustpkg$$(X_$(2)) \ + $$(TBIN$(1)_T_$(2)_H_$(3))/rustc$$(X_$(2)) @$$(call E, compile_and_link: $$@) $$(STAGE$(1)_T_$(2)_H_$(3)) -o $$@ $$< --test diff --git a/src/librustc/back/link.rs b/src/librustc/back/link.rs index 637ea159d79..bd95f1b735b 100644 --- a/src/librustc/back/link.rs +++ b/src/librustc/back/link.rs @@ -16,7 +16,7 @@ use lib::llvm::llvm; use lib::llvm::ModuleRef; use lib; use metadata::common::LinkMeta; -use metadata::{encoder, csearch, cstore}; +use metadata::{encoder, csearch, cstore, filesearch}; use middle::trans::context::CrateContext; use middle::trans::common::gensym_name; use middle::ty; @@ -497,6 +497,7 @@ pub fn build_link_meta(sess: Session, struct ProvidedMetas { name: Option<@str>, vers: Option<@str>, + pkg_id: Option<@str>, cmh_items: ~[@ast::MetaItem] } @@ -504,6 +505,7 @@ pub fn build_link_meta(sess: Session, ProvidedMetas { let mut name = None; let mut vers = None; + let mut pkg_id = None; let mut cmh_items = ~[]; let linkage_metas = attr::find_linkage_metas(c.attrs); attr::require_unique_names(sess.diagnostic(), linkage_metas); @@ -511,6 +513,7 @@ pub fn build_link_meta(sess: Session, match meta.name_str_pair() { Some((n, value)) if "name" == n => name = Some(value), Some((n, value)) if "vers" == n => vers = Some(value), + Some((n, value)) if "package_id" == n => pkg_id = Some(value), _ => cmh_items.push(*meta) } } @@ -518,6 +521,7 @@ pub fn build_link_meta(sess: Session, ProvidedMetas { name: name, vers: vers, + pkg_id: pkg_id, cmh_items: cmh_items } } @@ -525,7 +529,8 @@ pub fn build_link_meta(sess: Session, // This calculates CMH as defined above fn crate_meta_extras_hash(symbol_hasher: &mut hash::State, cmh_items: ~[@ast::MetaItem], - dep_hashes: ~[@str]) -> @str { + dep_hashes: ~[@str], + pkg_id: Option<@str>) -> @str { fn len_and_str(s: &str) -> ~str { fmt!("%u_%s", s.len(), s) } @@ -563,7 +568,10 @@ pub fn build_link_meta(sess: Session, write_string(symbol_hasher, len_and_str(*dh)); } - // tjc: allocation is unfortunate; need to change std::hash + for p in pkg_id.iter() { + write_string(symbol_hasher, len_and_str(*p)); + } + return truncated_hash_result(symbol_hasher).to_managed(); } @@ -605,6 +613,7 @@ pub fn build_link_meta(sess: Session, let ProvidedMetas { name: opt_name, vers: opt_vers, + pkg_id: opt_pkg_id, cmh_items: cmh_items } = provided_link_metas(sess, c); let name = crate_meta_name(sess, output, opt_name); @@ -612,11 +621,12 @@ pub fn build_link_meta(sess: Session, let dep_hashes = cstore::get_dep_hashes(sess.cstore); let extras_hash = crate_meta_extras_hash(symbol_hasher, cmh_items, - dep_hashes); + dep_hashes, opt_pkg_id); LinkMeta { name: name, vers: vers, + package_id: opt_pkg_id, extras_hash: extras_hash } } @@ -939,6 +949,11 @@ pub fn link_args(sess: Session, args.push(~"-L" + path.to_str()); } + let rustpath = filesearch::rust_path(); + for path in rustpath.iter() { + args.push(~"-L" + path.to_str()); + } + // The names of the extern libraries let used_libs = cstore::get_used_libraries(cstore); for l in used_libs.iter() { args.push(~"-l" + *l); } diff --git a/src/librustc/back/rpath.rs b/src/librustc/back/rpath.rs index 6aac627729c..5dc92dbc5e6 100644 --- a/src/librustc/back/rpath.rs +++ b/src/librustc/back/rpath.rs @@ -14,10 +14,7 @@ use metadata::cstore; use metadata::filesearch; use std::hashmap::HashSet; -use std::num; -use std::os; -use std::util; -use std::vec; +use std::{num, os, path, uint, util, vec}; fn not_win32(os: session::os) -> bool { os != session::os_win32 @@ -122,42 +119,7 @@ pub fn get_rpath_relative_to_output(os: session::os, session::os_win32 => util::unreachable() }; - Path(prefix).push_rel(&get_relative_to(&os::make_absolute(output), - &os::make_absolute(lib))) -} - -// Find the relative path from one file to another -pub fn get_relative_to(abs1: &Path, abs2: &Path) -> Path { - assert!(abs1.is_absolute); - assert!(abs2.is_absolute); - let abs1 = abs1.normalize(); - let abs2 = abs2.normalize(); - debug!("finding relative path from %s to %s", - abs1.to_str(), abs2.to_str()); - let split1: &[~str] = abs1.components; - let split2: &[~str] = abs2.components; - let len1 = split1.len(); - let len2 = split2.len(); - assert!(len1 > 0); - assert!(len2 > 0); - - let max_common_path = num::min(len1, len2) - 1; - let mut start_idx = 0; - while start_idx < max_common_path - && split1[start_idx] == split2[start_idx] { - start_idx += 1; - } - - let mut path = ~[]; - for _ in range(start_idx, len1 - 1) { path.push(~".."); }; - - path.push_all(split2.slice(start_idx, len2 - 1)); - - return if !path.is_empty() { - Path("").push_many(path) - } else { - Path(".") - } + Path(prefix).push_rel(&os::make_absolute(output).get_relative_to(&os::make_absolute(lib))) } fn get_absolute_rpaths(libs: &[Path]) -> ~[Path] { @@ -208,8 +170,7 @@ mod test { #[cfg(test)] #[cfg(test)] use back::rpath::{get_absolute_rpath, get_install_prefix_rpath}; - use back::rpath::{get_relative_to, get_rpath_relative_to_output}; - use back::rpath::{minimize_rpaths, rpaths_to_flags}; + use back::rpath::{minimize_rpaths, rpaths_to_flags, get_rpath_relative_to_output}; use driver::session; #[test] @@ -253,78 +214,9 @@ mod test { assert_eq!(res, ~[Path("1a"), Path("2"), Path("4a"), Path("3")]); } - #[test] - fn test_relative_to1() { - let p1 = Path("/usr/bin/rustc"); - let p2 = Path("/usr/lib/mylib"); - let res = get_relative_to(&p1, &p2); - assert_eq!(res, Path("../lib")); - } - - #[test] - fn test_relative_to2() { - let p1 = Path("/usr/bin/rustc"); - let p2 = Path("/usr/bin/../lib/mylib"); - let res = get_relative_to(&p1, &p2); - assert_eq!(res, Path("../lib")); - } - - #[test] - fn test_relative_to3() { - let p1 = Path("/usr/bin/whatever/rustc"); - let p2 = Path("/usr/lib/whatever/mylib"); - let res = get_relative_to(&p1, &p2); - assert_eq!(res, Path("../../lib/whatever")); - } - - #[test] - fn test_relative_to4() { - let p1 = Path("/usr/bin/whatever/../rustc"); - let p2 = Path("/usr/lib/whatever/mylib"); - let res = get_relative_to(&p1, &p2); - assert_eq!(res, Path("../lib/whatever")); - } - - #[test] - fn test_relative_to5() { - let p1 = Path("/usr/bin/whatever/../rustc"); - let p2 = Path("/usr/lib/whatever/../mylib"); - let res = get_relative_to(&p1, &p2); - assert_eq!(res, Path("../lib")); - } - - #[test] - fn test_relative_to6() { - let p1 = Path("/1"); - let p2 = Path("/2/3"); - let res = get_relative_to(&p1, &p2); - assert_eq!(res, Path("2")); - } - - #[test] - fn test_relative_to7() { - let p1 = Path("/1/2"); - let p2 = Path("/3"); - let res = get_relative_to(&p1, &p2); - assert_eq!(res, Path("..")); - } - - #[test] - fn test_relative_to8() { - let p1 = Path("/home/brian/Dev/rust/build/").push_rel( - &Path("stage2/lib/rustc/i686-unknown-linux-gnu/lib/librustc.so")); - let p2 = Path("/home/brian/Dev/rust/build/stage2/bin/..").push_rel( - &Path("lib/rustc/i686-unknown-linux-gnu/lib/libstd.so")); - let res = get_relative_to(&p1, &p2); - debug!("test_relative_tu8: %s vs. %s", - res.to_str(), - Path(".").to_str()); - assert_eq!(res, Path(".")); - } - #[test] #[cfg(target_os = "linux")] - #[cfg(target_os = "andorid")] + #[cfg(target_os = "android")] fn test_rpath_relative() { let o = session::os_linux; let res = get_rpath_relative_to_output(o, @@ -344,7 +236,6 @@ mod test { #[test] #[cfg(target_os = "macos")] fn test_rpath_relative() { - // this is why refinements would be nice let o = session::os_macos; let res = get_rpath_relative_to_output(o, &Path("bin/rustc"), diff --git a/src/librustc/front/std_inject.rs b/src/librustc/front/std_inject.rs index 3a1129b1dd9..2a61ea28e0c 100644 --- a/src/librustc/front/std_inject.rs +++ b/src/librustc/front/std_inject.rs @@ -47,7 +47,7 @@ fn inject_libstd_ref(sess: Session, crate: &ast::Crate) -> @ast::Crate { let n1 = sess.next_node_id(); let vi1 = ast::view_item { node: ast::view_item_extern_mod( - sess.ident_of("std"), ~[], n1), + sess.ident_of("std"), None, ~[], n1), attrs: ~[ attr::mk_attr( attr::mk_name_value_item_str(@"vers", STD_VERSION.to_managed())) diff --git a/src/librustc/front/test.rs b/src/librustc/front/test.rs index d2d2a8b4be9..c6b1bdbe51b 100644 --- a/src/librustc/front/test.rs +++ b/src/librustc/front/test.rs @@ -282,7 +282,7 @@ fn mk_std(cx: &TestCtxt) -> ast::view_item { cx.sess.next_node_id()))]) } else { let mi = attr::mk_name_value_item_str(@"vers", @"0.8-pre"); - ast::view_item_extern_mod(id_extra, ~[mi], cx.sess.next_node_id()) + ast::view_item_extern_mod(id_extra, None, ~[mi], cx.sess.next_node_id()) }; ast::view_item { node: vi, diff --git a/src/librustc/metadata/common.rs b/src/librustc/metadata/common.rs index 1c5d202d4d9..f2d8b68faa6 100644 --- a/src/librustc/metadata/common.rs +++ b/src/librustc/metadata/common.rs @@ -185,5 +185,7 @@ pub static tag_item_impl_vtables: uint = 0x82; pub struct LinkMeta { name: @str, vers: @str, + // Optional package ID + package_id: Option<@str>, // non-None if this was a URL-like package ID extras_hash: @str } diff --git a/src/librustc/metadata/creader.rs b/src/librustc/metadata/creader.rs index d8f14228824..0a9e8490f22 100644 --- a/src/librustc/metadata/creader.rs +++ b/src/librustc/metadata/creader.rs @@ -18,6 +18,7 @@ use metadata::loader; use std::hashmap::HashMap; use syntax::ast; +use std::vec; use syntax::attr; use syntax::attr::AttrMetaMethods; use syntax::codemap::{span, dummy_sp}; @@ -137,18 +138,33 @@ fn visit_crate(e: &Env, c: &ast::Crate) { fn visit_view_item(e: @mut Env, i: &ast::view_item) { match i.node { - ast::view_item_extern_mod(ident, ref meta_items, id) => { - debug!("resolving extern mod stmt. ident: %?, meta: %?", - ident, *meta_items); - let cnum = resolve_crate(e, - ident, - (*meta_items).clone(), - @"", - i.span); - cstore::add_extern_mod_stmt_cnum(e.cstore, id, cnum); + ast::view_item_extern_mod(ident, path_opt, ref meta_items, id) => { + let ident = token::ident_to_str(&ident); + let meta_items = match path_opt { + None => meta_items.clone(), + Some(p) => { + let p_path = Path(p); + match p_path.filestem() { + Some(s) => + vec::append( + ~[attr::mk_name_value_item_str(@"package_id", p), + attr::mk_name_value_item_str(@"name", s.to_managed())], + *meta_items), + None => e.diag.span_bug(i.span, "Bad package path in `extern mod` item") + } + } + }; + debug!("resolving extern mod stmt. ident: %?, meta: %?", + ident, meta_items); + let cnum = resolve_crate(e, + ident, + meta_items, + @"", + i.span); + cstore::add_extern_mod_stmt_cnum(e.cstore, id, cnum); } _ => () - } + } } fn visit_item(e: &Env, i: @ast::item) { @@ -233,12 +249,12 @@ fn existing_match(e: &Env, metas: &[@ast::MetaItem], hash: &str) } fn resolve_crate(e: @mut Env, - ident: ast::ident, + ident: @str, metas: ~[@ast::MetaItem], hash: @str, span: span) -> ast::CrateNum { - let metas = metas_with_ident(token::ident_to_str(&ident), metas); + let metas = metas_with_ident(ident, metas); match existing_match(e, metas, hash) { None => { @@ -279,7 +295,7 @@ fn resolve_crate(e: @mut Env, match attr::last_meta_item_value_str_by_name(load_ctxt.metas, "name") { Some(v) => v, - None => token::ident_to_str(&ident), + None => ident }; let cmeta = @cstore::crate_metadata { name: cname, @@ -308,7 +324,6 @@ fn resolve_crate_deps(e: @mut Env, cdata: @~[u8]) -> cstore::cnum_map { let r = decoder::get_crate_deps(cdata); for dep in r.iter() { let extrn_cnum = dep.cnum; - let cname = dep.name; let cname_str = token::ident_to_str(&dep.name); let cmetas = metas_with(dep.vers, @"vers", ~[]); debug!("resolving dep crate %s ver: %s hash: %s", @@ -327,7 +342,7 @@ fn resolve_crate_deps(e: @mut Env, cdata: @~[u8]) -> cstore::cnum_map { // FIXME (#2404): Need better error reporting than just a bogus // span. let fake_span = dummy_sp(); - let local_cnum = resolve_crate(e, cname, cmetas, dep.hash, + let local_cnum = resolve_crate(e, cname_str, cmetas, dep.hash, fake_span); cnum_map.insert(extrn_cnum, local_cnum); } diff --git a/src/librustc/metadata/filesearch.rs b/src/librustc/metadata/filesearch.rs index e18879464e8..2b44d793a9d 100644 --- a/src/librustc/metadata/filesearch.rs +++ b/src/librustc/metadata/filesearch.rs @@ -11,12 +11,15 @@ use std::option; use std::os; -use std::result; +use std::{result, str}; +use std::hashmap::HashSet; // A module for searching for libraries // FIXME (#2658): I'm not happy how this module turned out. Should // probably just be folded into cstore. +/// Functions with type `pick` take a parent directory as well as +/// a file found in that directory. pub type pick<'self, T> = &'self fn(path: &Path) -> Option; pub fn pick_file(file: Path, path: &Path) -> Option { @@ -46,28 +49,33 @@ pub fn mk_filesearch(maybe_sysroot: &Option<@Path>, impl FileSearch for FileSearchImpl { fn sysroot(&self) -> @Path { self.sysroot } fn for_each_lib_search_path(&self, f: &fn(&Path) -> bool) -> bool { + let mut visited_dirs = HashSet::new(); + debug!("filesearch: searching additional lib search paths [%?]", self.addl_lib_search_paths.len()); - // a little weird - self.addl_lib_search_paths.iter().advance(|path| f(path)); + for path in self.addl_lib_search_paths.iter() { + f(path); + visited_dirs.insert(path.to_str()); + } debug!("filesearch: searching target lib path"); - if !f(&make_target_lib_path(self.sysroot, - self.target_triple)) { - return false; - } - debug!("filesearch: searching rustpkg lib path nearest"); - if match get_rustpkg_lib_path_nearest() { - result::Ok(ref p) => f(p), - result::Err(_) => true - } { - return true; + let tlib_path = make_target_lib_path(self.sysroot, + self.target_triple); + if !visited_dirs.contains(&tlib_path.to_str()) { + if !f(&tlib_path) { + return false; } - debug!("filesearch: searching rustpkg lib path"); - match get_rustpkg_lib_path() { - result::Ok(ref p) => f(p), - result::Err(_) => true - } + } + visited_dirs.insert(tlib_path.to_str()); + // Try RUST_PATH + let rustpath = rust_path(); + for path in rustpath.iter() { + if !visited_dirs.contains(&path.push("lib").to_str()) { + f(&path.push("lib")); + visited_dirs.insert(path.push("lib").to_str()); + } + } + true } fn get_target_lib_path(&self) -> Path { make_target_lib_path(self.sysroot, self.target_triple) @@ -94,12 +102,15 @@ pub fn search(filesearch: @FileSearch, pick: pick) -> Option { for path in r.iter() { debug!("testing %s", path.to_str()); let maybe_picked = pick(path); - if maybe_picked.is_some() { - debug!("picked %s", path.to_str()); - rslt = maybe_picked; - break; - } else { - debug!("rejected %s", path.to_str()); + match maybe_picked { + Some(_) => { + debug!("picked %s", path.to_str()); + rslt = maybe_picked; + break; + } + None => { + debug!("rejected %s", path.to_str()); + } } } rslt.is_none() @@ -132,55 +143,59 @@ fn get_sysroot(maybe_sysroot: &Option<@Path>) -> @Path { } } -pub fn get_rustpkg_sysroot() -> Result { - result::Ok(get_or_default_sysroot().push_many([libdir(), ~"rustpkg"])) +#[cfg(windows)] +static PATH_ENTRY_SEPARATOR: &'static str = ";"; +#[cfg(not(windows))] +static PATH_ENTRY_SEPARATOR: &'static str = ":"; + +/// Returns RUST_PATH as a string, without default paths added +pub fn get_rust_path() -> Option<~str> { + os::getenv("RUST_PATH") } -pub fn get_rustpkg_root() -> Result { - match os::getenv("RUSTPKG_ROOT") { - Some(ref _p) => result::Ok(Path((*_p))), - None => match os::homedir() { - Some(ref _q) => result::Ok((*_q).push(".rustpkg")), - None => result::Err(~"no RUSTPKG_ROOT or home directory") +/// Returns the value of RUST_PATH, as a list +/// of Paths. Includes default entries for, if they exist: +/// $HOME/.rust +/// DIR/.rust for any DIR that's the current working directory +/// or an ancestor of it +pub fn rust_path() -> ~[Path] { + let mut env_rust_path: ~[Path] = match get_rust_path() { + Some(env_path) => { + let env_path_components: ~[&str] = + env_path.split_str_iter(PATH_ENTRY_SEPARATOR).collect(); + env_path_components.map(|&s| Path(s)) + } + None => ~[] + }; + let cwd = os::getcwd(); + // now add in default entries + let cwd_dot_rust = cwd.push(".rust"); + if !env_rust_path.contains(&cwd_dot_rust) { + env_rust_path.push(cwd_dot_rust); + } + if !env_rust_path.contains(&cwd) { + env_rust_path.push(cwd.clone()); + } + do cwd.each_parent() |p| { + if !env_rust_path.contains(&p.push(".rust")) { + push_if_exists(&mut env_rust_path, p); } } -} - -pub fn get_rustpkg_root_nearest() -> Result { - do get_rustpkg_root().chain |p| { - let cwd = os::getcwd(); - let cwd_rustpkg = cwd.push(".rustpkg"); - let rustpkg_is_non_root_file = - !os::path_is_dir(&cwd_rustpkg) && cwd_rustpkg != p; - let mut par_rustpkg = cwd.pop().push(".rustpkg"); - let mut rslt = result::Ok(cwd_rustpkg); - - if rustpkg_is_non_root_file { - while par_rustpkg != p { - if os::path_is_dir(&par_rustpkg) { - rslt = result::Ok(par_rustpkg); - break; - } - if par_rustpkg.components.len() == 1 { - // We just checked /.rustpkg, stop now. - break; - } - par_rustpkg = par_rustpkg.pop().pop().push(".rustpkg"); - } + let h = os::homedir(); + for h in h.iter() { + if !env_rust_path.contains(&h.push(".rust")) { + push_if_exists(&mut env_rust_path, h); } - rslt } + env_rust_path } -fn get_rustpkg_lib_path() -> Result { - do get_rustpkg_root().chain |p| { - result::Ok(p.push(libdir())) - } -} -fn get_rustpkg_lib_path_nearest() -> Result { - do get_rustpkg_root_nearest().chain |p| { - result::Ok(p.push(libdir())) +/// Adds p/.rust into vec, only if it exists +fn push_if_exists(vec: &mut ~[Path], p: &Path) { + let maybe_dir = p.push(".rust"); + if os::path_exists(&maybe_dir) { + vec.push(maybe_dir); } } diff --git a/src/librustc/metadata/loader.rs b/src/librustc/metadata/loader.rs index 9330cfc5c88..5f145d87ca8 100644 --- a/src/librustc/metadata/loader.rs +++ b/src/librustc/metadata/loader.rs @@ -18,7 +18,6 @@ use metadata::filesearch::FileSearch; use metadata::filesearch; use syntax::codemap::span; use syntax::diagnostic::span_handler; -use syntax::parse::token; use syntax::parse::token::ident_interner; use syntax::print::pprust; use syntax::{ast, attr}; @@ -46,7 +45,7 @@ pub struct Context { diag: @span_handler, filesearch: @FileSearch, span: span, - ident: ast::ident, + ident: @str, metas: ~[@ast::MetaItem], hash: @str, os: os, @@ -60,7 +59,7 @@ pub fn load_library_crate(cx: &Context) -> (~str, @~[u8]) { None => { cx.diag.span_fatal(cx.span, fmt!("can't find crate for `%s`", - token::ident_to_str(&cx.ident))); + cx.ident)); } } } @@ -89,37 +88,38 @@ fn find_library_crate_aux( filesearch: @filesearch::FileSearch ) -> Option<(~str, @~[u8])> { let crate_name = crate_name_from_metas(cx.metas); - let prefix = prefix + crate_name + "-"; - + // want: crate_name.dir_part() + prefix + crate_name.file_part + "-" + let prefix = fmt!("%s%s-", prefix, crate_name); let mut matches = ~[]; filesearch::search(filesearch, |path| -> Option<()> { - debug!("inspecting file %s", path.to_str()); - match path.filename() { - Some(ref f) if f.starts_with(prefix) && f.ends_with(suffix) => { - debug!("%s is a candidate", path.to_str()); - match get_metadata_section(cx.os, path) { - Some(cvec) => - if !crate_matches(cvec, cx.metas, cx.hash) { - debug!("skipping %s, metadata doesn't match", - path.to_str()); - None - } else { - debug!("found %s with matching metadata", path.to_str()); - matches.push((path.to_str(), cvec)); - None - }, - _ => { - debug!("could not load metadata for %s", path.to_str()); - None - } - } - } - _ => { - debug!("skipping %s, doesn't look like %s*%s", path.to_str(), - prefix, suffix); - None - } - }}); + let path_str = path.filename(); + match path_str { + None => None, + Some(path_str) => + if path_str.starts_with(prefix) && path_str.ends_with(suffix) { + debug!("%s is a candidate", path.to_str()); + match get_metadata_section(cx.os, path) { + Some(cvec) => + if !crate_matches(cvec, cx.metas, cx.hash) { + debug!("skipping %s, metadata doesn't match", + path.to_str()); + None + } else { + debug!("found %s with matching metadata", path.to_str()); + matches.push((path.to_str(), cvec)); + None + }, + _ => { + debug!("could not load metadata for %s", path.to_str()); + None + } + } + } + else { + None + } + } + }); match matches.len() { 0 => None, @@ -137,8 +137,8 @@ fn find_library_crate_aux( } cx.diag.handler().abort_if_errors(); None - } } + } } pub fn crate_name_from_metas(metas: &[@ast::MetaItem]) -> @str { @@ -151,6 +151,16 @@ pub fn crate_name_from_metas(metas: &[@ast::MetaItem]) -> @str { fail!("expected to find the crate name") } +pub fn package_id_from_metas(metas: &[@ast::MetaItem]) -> Option<@str> { + for m in metas.iter() { + match m.name_str_pair() { + Some((name, s)) if "package_id" == name => { return Some(s); } + _ => {} + } + } + None +} + pub fn note_linkage_attrs(intr: @ident_interner, diag: @span_handler, attrs: ~[ast::Attribute]) { @@ -175,6 +185,8 @@ fn crate_matches(crate_data: @~[u8], pub fn metadata_matches(extern_metas: &[@ast::MetaItem], local_metas: &[@ast::MetaItem]) -> bool { +// extern_metas: metas we read from the crate +// local_metas: metas we're looking for debug!("matching %u metadata requirements against %u items", local_metas.len(), extern_metas.len()); diff --git a/src/librustc/middle/resolve.rs b/src/librustc/middle/resolve.rs index 43953a50e15..7d892d97676 100644 --- a/src/librustc/middle/resolve.rs +++ b/src/librustc/middle/resolve.rs @@ -1487,9 +1487,10 @@ impl Resolver { } } - view_item_extern_mod(name, _, node_id) => { + view_item_extern_mod(name, _, _, node_id) => { + // n.b. we don't need to look at the path option here, because cstore already did match find_extern_mod_stmt_cnum(self.session.cstore, - node_id) { + node_id) { Some(crate_id) => { let def_id = def_id { crate: crate_id, node: 0 }; let parent_link = ModuleParentLink diff --git a/src/librustpkg/api.rs b/src/librustpkg/api.rs index bcda135cbb6..2da66baa412 100644 --- a/src/librustpkg/api.rs +++ b/src/librustpkg/api.rs @@ -23,33 +23,30 @@ fn default_ctxt(p: @Path) -> Ctx { Ctx { sysroot_opt: Some(p), json: false, dep_cache: @mut HashMap::new() } } -pub fn build_lib(sysroot: @Path, root: Path, dest: Path, name: ~str, version: Version, +pub fn build_lib(sysroot: @Path, root: Path, name: ~str, version: Version, lib: Path) { let pkg_src = PkgSrc { root: root, - dst_dir: dest.clone(), - id: PkgId{ version: version, ..PkgId::new(name, &dest.pop())}, + id: PkgId{ version: version, ..PkgId::new(name)}, libs: ~[mk_crate(lib)], mains: ~[], tests: ~[], benchs: ~[] }; - pkg_src.build(&default_ctxt(sysroot), pkg_src.dst_dir, ~[]); + pkg_src.build(&default_ctxt(sysroot), ~[]); } -pub fn build_exe(sysroot: @Path, root: Path, dest: Path, name: ~str, version: Version, - main: Path) { +pub fn build_exe(sysroot: @Path, root: Path, name: ~str, version: Version, main: Path) { let pkg_src = PkgSrc { root: root, - dst_dir: dest.clone(), - id: PkgId{ version: version, ..PkgId::new(name, &dest.pop())}, + id: PkgId{ version: version, ..PkgId::new(name)}, libs: ~[], mains: ~[mk_crate(main)], tests: ~[], benchs: ~[] }; - pkg_src.build(&default_ctxt(sysroot), pkg_src.dst_dir, ~[]); + pkg_src.build(&default_ctxt(sysroot), ~[]); } @@ -62,12 +59,9 @@ pub fn install_lib(sysroot: @Path, debug!("sysroot = %s", sysroot.to_str()); debug!("workspace = %s", workspace.to_str()); // make a PkgSrc - let pkg_id = PkgId{ version: version, ..PkgId::new(name, &workspace)}; - let build_dir = workspace.push("build"); - let dst_dir = build_dir.push_rel(&*pkg_id.local_path); + let pkg_id = PkgId{ version: version, ..PkgId::new(name)}; let pkg_src = PkgSrc { root: workspace.clone(), - dst_dir: dst_dir.clone(), id: pkg_id.clone(), libs: ~[mk_crate(lib_path)], mains: ~[], @@ -75,13 +69,13 @@ pub fn install_lib(sysroot: @Path, benchs: ~[] }; let cx = default_ctxt(sysroot); - pkg_src.build(&cx, dst_dir, ~[]); + pkg_src.build(&cx, ~[]); cx.install_no_build(&workspace, &pkg_id); } pub fn install_exe(sysroot: @Path, workspace: Path, name: ~str, version: Version) { default_ctxt(sysroot).install(&workspace, &PkgId{ version: version, - ..PkgId::new(name, &workspace)}); + ..PkgId::new(name)}); } diff --git a/src/librustpkg/context.rs b/src/librustpkg/context.rs index 230a0f915ac..f051be25f26 100644 --- a/src/librustpkg/context.rs +++ b/src/librustpkg/context.rs @@ -12,6 +12,7 @@ use std::hashmap::HashMap; +use std::os; pub struct Ctx { // Sysroot -- if this is None, uses rustc filesearch's @@ -23,3 +24,26 @@ pub struct Ctx { // though I'm not sure why the value is a bool dep_cache: @mut HashMap<~str, bool>, } + +impl Ctx { + /// Debugging + pub fn sysroot_opt_str(&self) -> ~str { + match self.sysroot_opt { + None => ~"[none]", + Some(p) => p.to_str() + } + } +} + +/// We assume that if ../../rustc exists, then we're running +/// rustpkg from a Rust target directory. This is part of a +/// kludgy hack used to adjust the sysroot. +pub fn in_target(sysroot_opt: Option<@Path>) -> bool { + match sysroot_opt { + None => false, + Some(p) => { + debug!("Checking whether %s is in target", p.to_str()); + os::path_is_dir(&p.pop().pop().push("rustc")) + } + } +} diff --git a/src/librustpkg/installed_packages.rs b/src/librustpkg/installed_packages.rs index cec64f36947..7c3cde65517 100644 --- a/src/librustpkg/installed_packages.rs +++ b/src/librustpkg/installed_packages.rs @@ -10,6 +10,7 @@ // Listing installed packages +use rustc::metadata::filesearch::rust_path; use path_util::*; use std::os; @@ -20,21 +21,46 @@ pub fn list_installed_packages(f: &fn(&PkgId) -> bool) -> bool { for exec in binfiles.iter() { let exec_path = Path(*exec).filestem(); do exec_path.iter().advance |s| { - f(&PkgId::new(*s, p)) + f(&PkgId::new(*s)) }; } let libfiles = os::list_dir(&p.push("lib")); for lib in libfiles.iter() { - debug!("Full name: %s", *lib); - let lib_path = Path(*lib).filestem(); - do lib_path.iter().advance |s| { - f(&PkgId::new(*s, p)) - }; - } + let lib = Path(*lib); + debug!("Full name: %s", lib.to_str()); + match has_library(&lib) { + Some(basename) => { + debug!("parent = %s, child = %s", + p.push("lib").to_str(), lib.to_str()); + let rel_p = p.push("lib/").get_relative_to(&lib); + debug!("Rel: %s", rel_p.to_str()); + let rel_path = rel_p.push(basename).to_str(); + debug!("Rel name: %s", rel_path); + f(&PkgId::new(rel_path)); + } + None => () + } + }; } true } +pub fn has_library(p: &Path) -> Option<~str> { + let files = os::list_dir(p); + for q in files.iter() { + let as_path = Path(*q); + if as_path.filetype() == Some(os::consts::DLL_SUFFIX.to_owned()) { + let stuff : ~str = as_path.filestem().expect("has_library: weird path"); + let mut stuff2 = stuff.split_str_iter(&"-"); + let stuff3: ~[&str] = stuff2.collect(); + // argh + let chars_to_drop = os::consts::DLL_PREFIX.len(); + return Some(stuff3[0].slice(chars_to_drop, stuff3[0].len()).to_owned()); + } + } + None +} + pub fn package_is_installed(p: &PkgId) -> bool { let mut is_installed = false; do list_installed_packages() |installed| { @@ -44,4 +70,4 @@ pub fn package_is_installed(p: &PkgId) -> bool { false }; is_installed -} \ No newline at end of file +} diff --git a/src/librustpkg/package_id.rs b/src/librustpkg/package_id.rs index e1cf5b1fd35..e3b3252587a 100644 --- a/src/librustpkg/package_id.rs +++ b/src/librustpkg/package_id.rs @@ -8,33 +8,37 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -pub use package_path::{RemotePath, LocalPath, normalize, hash}; use version::{try_getting_version, try_getting_local_version, Version, NoVersion, split_version}; +use std::rt::io::Writer; +use std::hash::Streaming; +use std::hash; /// Path-fragment identifier of a package such as /// 'github.com/graydon/test'; path must be a relative /// path with >=1 component. #[deriving(Clone)] pub struct PkgId { - /// 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 + /// This is a path, on the local filesystem, referring to where the + /// files for this package live. For example: + /// github.com/mozilla/quux-whatever (it's assumed that if we're + /// working with a package ID of this form, rustpkg has already cloned + /// the sources into a local directory in the RUST_PATH). + path: Path, + /// Short name. This is the path's filestem, but we store it /// redundantly so as to not call get() everywhere (filestem() returns an /// option) + /// The short name does not need to be a valid Rust identifier. + /// Users can write: `extern mod foo = "...";` to get around the issue + /// of package IDs whose short names aren't valid Rust identifiers. short_name: ~str, + /// The requested package version. version: Version } impl Eq for PkgId { fn eq(&self, p: &PkgId) -> bool { - *p.local_path == *self.local_path && p.version == self.version + p.path == self.path && p.version == self.version } fn ne(&self, p: &PkgId) -> bool { !(self.eq(p)) @@ -42,10 +46,7 @@ impl Eq for PkgId { } impl PkgId { - // The PkgId constructor takes a Path argument so as - // to be able to infer the version if the path refers - // to a local git repository - pub fn new(s: &str, work_dir: &Path) -> PkgId { + pub fn new(s: &str) -> PkgId { use conditions::bad_pkg_id::cond; let mut given_version = None; @@ -63,51 +64,64 @@ impl PkgId { } }; - let p = Path(s); - if p.is_absolute { - return cond.raise((p, ~"absolute pkgid")); + let path = Path(s); + if path.is_absolute { + return cond.raise((path, ~"absolute pkgid")); } - if p.components.len() < 1 { - return cond.raise((p, ~"0-length pkgid")); + if path.components.len() < 1 { + return cond.raise((path, ~"0-length pkgid")); } - let remote_path = RemotePath(p); - let local_path = normalize(remote_path.clone()); - let short_name = local_path.clone().filestem().expect(fmt!("Strange path! %s", s)); + let short_name = path.clone().filestem().expect(fmt!("Strange path! %s", s)); let version = match given_version { Some(v) => v, - None => match try_getting_local_version(&work_dir.push_rel(&*local_path)) { + None => match try_getting_local_version(&path) { Some(v) => v, - None => match try_getting_version(&remote_path) { + None => match try_getting_version(&path) { Some(v) => v, None => NoVersion } } }; - debug!("local_path = %s, remote_path = %s", local_path.to_str(), remote_path.to_str()); + debug!("path = %s", path.to_str()); PkgId { - local_path: local_path, - remote_path: remote_path, + path: path, short_name: short_name, version: version } } pub fn hash(&self) -> ~str { - fmt!("%s-%s-%s", self.remote_path.to_str(), - hash(self.remote_path.to_str() + self.version.to_str()), + fmt!("%s-%s-%s", self.path.to_str(), + hash(self.path.to_str() + self.version.to_str()), self.version.to_str()) } pub fn short_name_with_version(&self) -> ~str { fmt!("%s%s", self.short_name, self.version.to_str()) } + + /// True if the ID has multiple components + pub fn is_complex(&self) -> bool { + self.short_name != self.path.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.local_path.to_str(), self.version.to_str()) + fmt!("%s-%s", self.path.to_str(), self.version.to_str()) } } + + +pub fn write(writer: &mut W, string: &str) { + writer.write(string.as_bytes()); +} + +pub fn hash(data: ~str) -> ~str { + let hasher = &mut hash::default_state(); + write(hasher, data); + hasher.result_str() +} diff --git a/src/librustpkg/package_path.rs b/src/librustpkg/package_path.rs deleted file mode 100644 index 4ba9c8066e4..00000000000 --- a/src/librustpkg/package_path.rs +++ /dev/null @@ -1,68 +0,0 @@ -// 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 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -// rustpkg utilities having to do with local and remote paths - -use std::clone::Clone; -use std::hash::Streaming; -use std::hash; -use std::option::Some; -use std::path::Path; -use std::rt::io::Writer; - -/// Wrappers to prevent local and remote paths from getting confused -/// (These will go away after #6407) -pub struct RemotePath (Path); - -impl Clone for RemotePath { - fn clone(&self) -> RemotePath { - RemotePath((**self).clone()) - } -} - -pub struct LocalPath (Path); - -impl Clone for LocalPath { - fn clone(&self) -> LocalPath { - LocalPath((**self).clone()) - } -} - - -// 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 = st.replace("-", "_"); - if replaced != st { - LocalPath(p.with_filestem(replaced)) - } - else { - LocalPath(p) - } - } - } -} - -pub fn write(writer: &mut W, string: &str) { - writer.write(string.as_bytes()); -} - -pub fn hash(data: ~str) -> ~str { - let hasher = &mut hash::default_state(); - write(hasher, data); - hasher.result_str() -} diff --git a/src/librustpkg/package_source.rs b/src/librustpkg/package_source.rs index bbe35ee5004..9833e18e016 100644 --- a/src/librustpkg/package_source.rs +++ b/src/librustpkg/package_source.rs @@ -11,7 +11,7 @@ use target::*; use package_id::PkgId; use std::path::Path; -use std::{os, str}; +use std::os; use context::*; use crate::Crate; use messages::*; @@ -23,7 +23,6 @@ use util::compile_crate; // This contains a list of files found in the source workspace. pub struct PkgSrc { root: Path, // root of where the package source code lives - dst_dir: Path, // directory where we will put the compiled output id: PkgId, libs: ~[Crate], mains: ~[Crate], @@ -37,11 +36,9 @@ condition! { impl PkgSrc { - pub fn new(src_dir: &Path, dst_dir: &Path, - id: &PkgId) -> PkgSrc { + pub fn new(src_dir: &Path, id: &PkgId) -> PkgSrc { PkgSrc { root: (*src_dir).clone(), - dst_dir: (*dst_dir).clone(), id: (*id).clone(), libs: ~[], mains: ~[], @@ -54,8 +51,7 @@ impl PkgSrc { fn check_dir(&self) -> Path { use conditions::nonexistent_package::cond; - debug!("Pushing onto root: %s | %s", self.id.remote_path.to_str(), - self.root.to_str()); + debug!("Pushing onto root: %s | %s", self.id.path.to_str(), self.root.to_str()); let dir; let dirs = pkgid_src_in_workspace(&self.id, &self.root); debug!("Checking dirs: %?", dirs); @@ -89,18 +85,18 @@ impl PkgSrc { os::remove_dir_recursive(&local); debug!("Checking whether %s exists locally. Cwd = %s, does it? %?", - self.id.local_path.to_str(), + self.id.path.to_str(), os::getcwd().to_str(), - os::path_exists(&*self.id.local_path)); + os::path_exists(&self.id.path)); - if os::path_exists(&*self.id.local_path) { + if os::path_exists(&self.id.path) { debug!("%s exists locally! Cloning it into %s", - self.id.local_path.to_str(), local.to_str()); - git_clone(&*self.id.local_path, &local, &self.id.version); + self.id.path.to_str(), local.to_str()); + git_clone(&self.id.path, &local, &self.id.version); return Some(local); } - let url = fmt!("https://%s", self.id.remote_path.to_str()); + let url = fmt!("https://%s", self.id.path.to_str()); note(fmt!("Fetching package: git clone %s %s [version=%s]", url, local.to_str(), self.id.version.to_str())); if git_clone_general(url, &local, &self.id.version) { @@ -125,25 +121,8 @@ impl PkgSrc { } /// True if the given path's stem is self's pkg ID's stem - /// or if the pkg ID's stem is 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.local_path.filestem(); - if self_id == p.filestem() { - return true; - } - else { - for pth in self_id.iter() { - 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 - } { return true; } - } - } - false + p.filestem().map_default(false, |p| { p == &self.id.short_name }) } fn push_crate(cs: &mut ~[Crate], prefix: uint, p: &Path) { @@ -164,7 +143,7 @@ impl PkgSrc { let dir = self.check_dir(); debug!("Called check_dir, I'm in %s", dir.to_str()); let prefix = dir.components.len(); - debug!("Matching against %?", self.id.local_path.filestem()); + debug!("Matching against %?", self.id.short_name); do os::walk_dir(&dir) |pth| { match pth.filename() { Some(~"lib.rs") => PkgSrc::push_crate(&mut self.libs, @@ -202,7 +181,6 @@ impl PkgSrc { fn build_crates(&self, ctx: &Ctx, - dst_dir: &Path, src_dir: &Path, crates: &[Crate], cfgs: &[~str], @@ -210,12 +188,13 @@ impl PkgSrc { for crate in crates.iter() { let path = &src_dir.push_rel(&crate.file).normalize(); note(fmt!("build_crates: compiling %s", path.to_str())); - note(fmt!("build_crates: destination dir is %s", dst_dir.to_str())); + note(fmt!("build_crates: using as workspace %s", self.root.to_str())); let result = compile_crate(ctx, &self.id, path, - dst_dir, + // compile_crate wants the workspace + &self.root, crate.flags, crate.cfgs + cfgs, false, @@ -229,15 +208,15 @@ impl PkgSrc { } } - pub fn build(&self, ctx: &Ctx, dst_dir: Path, cfgs: ~[~str]) { + pub fn build(&self, ctx: &Ctx, cfgs: ~[~str]) { let dir = self.check_dir(); debug!("Building libs in %s", dir.to_str()); - self.build_crates(ctx, &dst_dir, &dir, self.libs, cfgs, Lib); + self.build_crates(ctx, &dir, self.libs, cfgs, Lib); debug!("Building mains"); - self.build_crates(ctx, &dst_dir, &dir, self.mains, cfgs, Main); + self.build_crates(ctx, &dir, self.mains, cfgs, Main); debug!("Building tests"); - self.build_crates(ctx, &dst_dir, &dir, self.tests, cfgs, Test); + self.build_crates(ctx, &dir, self.tests, cfgs, Test); debug!("Building benches"); - self.build_crates(ctx, &dst_dir, &dir, self.benchs, cfgs, Bench); + self.build_crates(ctx, &dir, self.benchs, cfgs, Bench); } } diff --git a/src/librustpkg/path_util.rs b/src/librustpkg/path_util.rs index fdfa29b2f83..bbe84b2ecac 100644 --- a/src/librustpkg/path_util.rs +++ b/src/librustpkg/path_util.rs @@ -10,65 +10,17 @@ // rustpkg utilities having to do with paths and directories -pub use package_path::{RemotePath, LocalPath, normalize}; pub use package_id::PkgId; pub use target::{OutputType, Main, Lib, Test, Bench, Target, Build, Install}; pub use version::{Version, NoVersion, split_version_general}; +pub use rustc::metadata::filesearch::rust_path; + use std::libc::consts::os::posix88::{S_IRUSR, S_IWUSR, S_IXUSR}; use std::os::mkdir_recursive; use std::os; -use std::iterator::IteratorUtil; use messages::*; use package_id::*; -fn push_if_exists(vec: &mut ~[Path], p: &Path) { - let maybe_dir = p.push(".rust"); - if os::path_exists(&maybe_dir) { - vec.push(maybe_dir); - } -} - -#[cfg(windows)] -static PATH_ENTRY_SEPARATOR: &'static str = ";"; -#[cfg(not(windows))] -static PATH_ENTRY_SEPARATOR: &'static str = ":"; - -/// Returns RUST_PATH as a string, without default paths added -pub fn get_rust_path() -> Option<~str> { - os::getenv("RUST_PATH") -} - -/// Returns the value of RUST_PATH, as a list -/// of Paths. Includes default entries for, if they exist: -/// $HOME/.rust -/// DIR/.rust for any DIR that's the current working directory -/// or an ancestor of it -pub fn rust_path() -> ~[Path] { - let mut env_rust_path: ~[Path] = match get_rust_path() { - Some(env_path) => { - let env_path_components: ~[&str] = - env_path.split_str_iter(PATH_ENTRY_SEPARATOR).collect(); - env_path_components.map(|&s| Path(s)) - } - None => ~[] - }; - debug!("RUST_PATH entries from environment: %?", env_rust_path); - let cwd = os::getcwd(); - // now add in default entries - env_rust_path.push(cwd.clone()); - do cwd.each_parent() |p| { push_if_exists(&mut env_rust_path, p) }; - let h = os::homedir(); - // Avoid adding duplicates - // could still add dups if someone puts one of these in the RUST_PATH - // manually, though... - for hdir in h.iter() { - if !(cwd.is_ancestor_of(hdir) || hdir.is_ancestor_of(&cwd)) { - push_if_exists(&mut env_rust_path, hdir); - } - } - env_rust_path -} - pub fn default_workspace() -> Path { let p = rust_path(); if p.is_empty() { @@ -99,39 +51,39 @@ pub fn make_dir_rwx(p: &Path) -> bool { os::make_dir(p, U_RWX) } /// pkgid's short name pub fn workspace_contains_package_id(pkgid: &PkgId, workspace: &Path) -> bool { let src_dir = workspace.push("src"); - let dirs = os::list_dir(&src_dir); - for p in dirs.iter() { - let p = Path((*p).clone()); + let mut found = false; + do os::walk_dir(&src_dir) |p| { debug!("=> p = %s", p.to_str()); - if !os::path_is_dir(&src_dir.push_rel(&p)) { - loop; - } - debug!("p = %s, remote_path = %s", p.to_str(), pkgid.remote_path.to_str()); + if os::path_is_dir(p) { + debug!("p = %s, path = %s [%s]", p.to_str(), pkgid.path.to_str(), + src_dir.push_rel(&pkgid.path).to_str()); - if p == *pkgid.remote_path { - return true; - } - else { - let pf = p.filename(); - for pf in pf.iter() { - let f_ = (*pf).clone(); - let g = f_.to_str(); - match split_version_general(g, '-') { - Some((ref might_match, ref vers)) => { - debug!("might_match = %s, vers = %s", *might_match, + if *p == src_dir.push_rel(&pkgid.path) { + found = true; + } + else { + let pf = p.filename(); + for pf in pf.iter() { + let f_ = (*pf).clone(); + let g = f_.to_str(); + match split_version_general(g, '-') { + Some((ref might_match, ref vers)) => { + debug!("might_match = %s, vers = %s", *might_match, vers.to_str()); - if *might_match == pkgid.short_name - && (*vers == pkgid.version || pkgid.version == NoVersion) - { - return true; + if *might_match == pkgid.short_name + && (*vers == pkgid.version || pkgid.version == NoVersion) + { + found = true; + } } - } - None => () + None => () + } } } } - } - false + true + }; + found } /// Returns a list of possible directories @@ -141,9 +93,9 @@ pub fn workspace_contains_package_id(pkgid: &PkgId, workspace: &Path) -> bool { pub fn pkgid_src_in_workspace(pkgid: &PkgId, workspace: &Path) -> ~[Path] { let mut results = ~[]; let result = workspace.push("src").push(fmt!("%s-%s", - pkgid.local_path.to_str(), pkgid.version.to_str())); + pkgid.path.to_str(), pkgid.version.to_str())); results.push(result); - results.push(workspace.push("src").push_rel(&*pkgid.remote_path)); + results.push(workspace.push("src").push_rel(&pkgid.path)); results } @@ -163,7 +115,7 @@ pub fn first_pkgid_src_in_workspace(pkgid: &PkgId, workspace: &Path) -> Option

Option { let mut result = workspace.push("build"); // should use a target-specific subdirectory - result = mk_output_path(Main, Build, pkgid, &result); + result = mk_output_path(Main, Build, pkgid, result); debug!("built_executable_in_workspace: checking whether %s exists", result.to_str()); if os::path_exists(&result) { @@ -191,7 +143,7 @@ pub fn built_bench_in_workspace(pkgid: &PkgId, workspace: &Path) -> Option fn output_in_workspace(pkgid: &PkgId, workspace: &Path, what: OutputType) -> Option { let mut result = workspace.push("build"); // should use a target-specific subdirectory - result = mk_output_path(what, Build, pkgid, &result); + result = mk_output_path(what, Build, pkgid, result); debug!("output_in_workspace: checking whether %s exists", result.to_str()); if os::path_exists(&result) { @@ -206,14 +158,12 @@ fn output_in_workspace(pkgid: &PkgId, workspace: &Path, what: OutputType) -> Opt /// 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 { - library_in_workspace(&pkgid.local_path, pkgid.short_name, - Build, workspace, "build") + library_in_workspace(&pkgid.path, pkgid.short_name, Build, workspace, "build") } /// Does the actual searching stuff pub fn installed_library_in_workspace(short_name: &str, workspace: &Path) -> Option { - library_in_workspace(&normalize(RemotePath(Path(short_name))), - short_name, Install, workspace, "lib") + library_in_workspace(&Path(short_name), short_name, Install, workspace, "lib") } @@ -221,7 +171,7 @@ pub fn installed_library_in_workspace(short_name: &str, workspace: &Path) -> Opt /// don't know the entire package ID. /// `workspace` is used to figure out the directory to search. /// `short_name` is taken as the link name of the library. -pub fn library_in_workspace(path: &LocalPath, short_name: &str, where: Target, +pub fn library_in_workspace(path: &Path, short_name: &str, where: Target, workspace: &Path, prefix: &str) -> Option { debug!("library_in_workspace: checking whether a library named %s exists", short_name); @@ -233,7 +183,7 @@ pub fn library_in_workspace(path: &LocalPath, short_name: &str, where: Target, prefix = %s", short_name, where, workspace.to_str(), prefix); let dir_to_search = match where { - Build => workspace.push(prefix).push_rel(&**path), + Build => workspace.push(prefix).push_rel(path), Install => workspace.push(prefix) }; debug!("Listing directory %s", dir_to_search.to_str()); @@ -349,7 +299,7 @@ fn target_file_in_workspace(pkgid: &PkgId, workspace: &Path, // Artifacts in the build directory live in a package-ID-specific subdirectory, // but installed ones don't. let result = match where { - Build => workspace.push(subdir).push_rel(&*pkgid.local_path), + Build => workspace.push(subdir).push_rel(&pkgid.path), _ => workspace.push(subdir) }; if !os::path_exists(&result) && !mkdir_recursive(&result, U_RWX) { @@ -357,7 +307,7 @@ fn target_file_in_workspace(pkgid: &PkgId, workspace: &Path, create the %s dir (pkgid=%s, workspace=%s, what=%?, where=%?", subdir, pkgid.to_str(), workspace.to_str(), what, where))); } - mk_output_path(what, where, pkgid, &result) + mk_output_path(what, where, pkgid, result) } /// Return the directory for 's build artifacts in . @@ -368,7 +318,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_rel(&*pkgid.local_path); + result = result.push_rel(&pkgid.path); if os::path_exists(&result) || os::mkdir_recursive(&result, U_RWX) { result } @@ -380,19 +330,16 @@ 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, where: Target, - pkg_id: &PkgId, workspace: &Path) -> Path { + pkg_id: &PkgId, workspace: Path) -> Path { let short_name_with_version = fmt!("%s-%s", pkg_id.short_name, pkg_id.version.to_str()); // Not local_path.dir_path()! For package foo/bar/blat/, we want // the executable blat-0.5 to live under blat/ let dir = match where { // If we're installing, it just goes under ... - Install => { - // bad copy, but I just couldn't make the borrow checker happy - (*workspace).clone() - } + Install => workspace, // and if we're just building, it goes in a package-specific subdir - Build => workspace.push_rel(&*pkg_id.local_path) + Build => workspace.push_rel(&pkg_id.path) }; debug!("[%?:%?] mk_output_path: short_name = %s, path = %s", what, where, if what == Lib { short_name_with_version.clone() } else { pkg_id.short_name.clone() }, diff --git a/src/librustpkg/rustpkg.rs b/src/librustpkg/rustpkg.rs index fa03a5bbfc2..26dab4120fd 100644 --- a/src/librustpkg/rustpkg.rs +++ b/src/librustpkg/rustpkg.rs @@ -33,12 +33,13 @@ use std::hashmap::HashMap; use rustc::driver::{driver, session}; use rustc::metadata::filesearch; +use rustc::metadata::filesearch::rust_path; use extra::{getopts}; use syntax::{ast, diagnostic}; use util::*; use messages::*; use path_util::{build_pkg_id_in_workspace, first_pkgid_src_in_workspace}; -use path_util::{U_RWX, rust_path, in_rust_path}; +use path_util::{U_RWX, in_rust_path}; use path_util::{built_executable_in_workspace, built_library_in_workspace, default_workspace}; use path_util::{target_executable_in_workspace, target_library_in_workspace}; use source_control::is_git_dir; @@ -54,7 +55,6 @@ mod crate; mod installed_packages; mod messages; mod package_id; -mod package_path; mod package_source; mod path_util; mod search; @@ -138,35 +138,28 @@ impl<'self> PkgScript<'self> { let crate = util::ready_crate(sess, self.crate); debug!("Building output filenames with script name %s", driver::source_name(&self.input)); - match filesearch::get_rustpkg_sysroot() { - Ok(r) => { - let root = r.pop().pop().pop().pop(); // :-\ - debug!("Root is %s, calling compile_rest", root.to_str()); - let exe = self.build_dir.push(~"pkg" + util::exe_suffix()); - util::compile_crate_from_input(&self.input, - &self.build_dir, - sess, - crate); - debug!("Running program: %s %s %s %s", exe.to_str(), - sysroot.to_str(), root.to_str(), "install"); - // FIXME #7401 should support commands besides `install` - let status = run::process_status(exe.to_str(), [sysroot.to_str(), ~"install"]); - if status != 0 { - return (~[], status); - } - else { - debug!("Running program (configs): %s %s %s", - exe.to_str(), root.to_str(), "configs"); - let output = run::process_output(exe.to_str(), [root.to_str(), ~"configs"]); - // Run the configs() function to get the configs - let cfgs = str::from_bytes_slice(output.output).word_iter() - .transform(|w| w.to_owned()).collect(); - (cfgs, output.status) - } - } - Err(e) => { - fail!("Running package script, couldn't find rustpkg sysroot (%s)", e) - } + let root = filesearch::get_or_default_sysroot().pop().pop(); // :-\ + debug!("Root is %s, calling compile_rest", root.to_str()); + let exe = self.build_dir.push(~"pkg" + util::exe_suffix()); + util::compile_crate_from_input(&self.input, + &self.build_dir, + sess, + crate); + debug!("Running program: %s %s %s %s", exe.to_str(), + sysroot.to_str(), root.to_str(), "install"); + // FIXME #7401 should support commands besides `install` + let status = run::process_status(exe.to_str(), [sysroot.to_str(), ~"install"]); + if status != 0 { + return (~[], status); + } + else { + debug!("Running program (configs): %s %s %s", + exe.to_str(), root.to_str(), "configs"); + let output = run::process_output(exe.to_str(), [root.to_str(), ~"configs"]); + // Run the configs() function to get the configs + let cfgs = str::from_bytes_slice(output.output).word_iter() + .transform(|w| w.to_owned()).collect(); + (cfgs, output.status) } } @@ -205,7 +198,7 @@ impl CtxMethods for Ctx { else { // The package id is presumed to be the first command-line // argument - let pkgid = PkgId::new(args[0].clone(), &os::getcwd()); + let pkgid = PkgId::new(args[0].clone()); do each_pkg_parent_workspace(&pkgid) |workspace| { debug!("found pkg %s in workspace %s, trying to build", pkgid.to_str(), workspace.to_str()); @@ -228,7 +221,7 @@ impl CtxMethods for Ctx { else { // The package id is presumed to be the first command-line // argument - let pkgid = PkgId::new(args[0].clone(), &os::getcwd()); + let pkgid = PkgId::new(args[0].clone()); let cwd = os::getcwd(); self.clean(&cwd, &pkgid); // tjc: should use workspace, not cwd } @@ -254,13 +247,12 @@ impl CtxMethods for Ctx { else { // The package id is presumed to be the first command-line // argument - let pkgid = PkgId::new(args[0], &os::getcwd()); + let pkgid = PkgId::new(args[0]); let workspaces = pkg_parent_workspaces(&pkgid); if workspaces.is_empty() { let rp = rust_path(); assert!(!rp.is_empty()); - let src = PkgSrc::new(&rp[0], &build_pkg_id_in_workspace(&pkgid, &rp[0]), - &pkgid); + let src = PkgSrc::new(&rp[0], &pkgid); src.fetch_git(); self.install(&rp[0], &pkgid); } @@ -275,7 +267,7 @@ impl CtxMethods for Ctx { "list" => { io::println("Installed packages:"); do installed_packages::list_installed_packages |pkg_id| { - println(pkg_id.local_path.to_str()); + println(pkg_id.path.to_str()); true }; } @@ -294,7 +286,7 @@ impl CtxMethods for Ctx { return usage::uninstall(); } - let pkgid = PkgId::new(args[0], &os::getcwd()); // ?? + let pkgid = PkgId::new(args[0]); if !installed_packages::package_is_installed(&pkgid) { warn(fmt!("Package %s doesn't seem to be installed! Doing nothing.", args[0])); return; @@ -329,20 +321,18 @@ impl CtxMethods for Ctx { fn build(&self, workspace: &Path, pkgid: &PkgId) { debug!("build: workspace = %s (in Rust path? %? is git dir? %? \ pkgid = %s", workspace.to_str(), - in_rust_path(workspace), is_git_dir(&workspace.push_rel(&*pkgid.local_path)), + in_rust_path(workspace), is_git_dir(&workspace.push_rel(&pkgid.path)), pkgid.to_str()); let src_dir = first_pkgid_src_in_workspace(pkgid, workspace); - let build_dir = build_pkg_id_in_workspace(pkgid, workspace); - debug!("Destination dir = %s", build_dir.to_str()); // If workspace isn't in the RUST_PATH, and it's a git repo, // then clone it into the first entry in RUST_PATH, and repeat debug!("%? %? %s", in_rust_path(workspace), - is_git_dir(&workspace.push_rel(&*pkgid.local_path)), + is_git_dir(&workspace.push_rel(&pkgid.path)), workspace.to_str()); - if !in_rust_path(workspace) && is_git_dir(&workspace.push_rel(&*pkgid.local_path)) { - let out_dir = default_workspace().push("src").push_rel(&*pkgid.local_path); - source_control::git_clone(&workspace.push_rel(&*pkgid.local_path), + if !in_rust_path(workspace) && is_git_dir(&workspace.push_rel(&pkgid.path)) { + let out_dir = default_workspace().push("src").push_rel(&pkgid.path); + source_control::git_clone(&workspace.push_rel(&pkgid.path), &out_dir, &pkgid.version); let default_ws = default_workspace(); debug!("Calling build recursively with %? and %?", default_ws.to_str(), @@ -351,7 +341,7 @@ impl CtxMethods for Ctx { } // Create the package source - let mut src = PkgSrc::new(workspace, &build_dir, pkgid); + let mut src = PkgSrc::new(workspace, pkgid); debug!("Package src = %?", src); // Is there custom build logic? If so, use it @@ -385,7 +375,7 @@ impl CtxMethods for Ctx { // Find crates inside the workspace src.find_crates(); // Build it! - src.build(self, build_dir, cfgs); + src.build(self, cfgs); } } @@ -444,6 +434,7 @@ impl CtxMethods for Ctx { for lib in maybe_library.iter() { let target_lib = target_lib.clone().expect(fmt!("I built %s but apparently \ didn't install it!", lib.to_str())); + let target_lib = target_lib.pop().push(lib.filename().expect("weird target lib")); debug!("Copying: %s -> %s", lib.to_str(), target_lib.to_str()); if !(os::mkdir_recursive(&target_lib.dir_path(), U_RWX) && os::copy_file(lib, &target_lib)) { @@ -518,9 +509,7 @@ pub fn main() { }; } - let sroot = match filesearch::get_rustpkg_sysroot() { - Ok(r) => Some(@r.pop().pop()), Err(_) => None - }; + let sroot = Some(@filesearch::get_or_default_sysroot()); debug!("Using sysroot: %?", sroot); Ctx { sysroot_opt: sroot, // Currently, only tests override this diff --git a/src/librustpkg/source_control.rs b/src/librustpkg/source_control.rs index e3b796a03bb..caa004a53b2 100644 --- a/src/librustpkg/source_control.rs +++ b/src/librustpkg/source_control.rs @@ -10,7 +10,7 @@ // Utils for working with version control repositories. Just git right now. -use std::{os, run, str}; +use std::{io, os, run, str}; use std::run::{ProcessOutput, ProcessOptions, Process}; use version::*; @@ -19,14 +19,37 @@ pub fn git_clone(source: &Path, target: &Path, v: &Version) { assert!(os::path_is_dir(source)); assert!(is_git_dir(source)); if !os::path_exists(target) { - debug!("Running: git clone %s %s", source.to_str(), - target.to_str()); - assert!(git_clone_general(source.to_str(), target, v)); + debug!("Running: git clone %s %s", source.to_str(), target.to_str()); + let outp = run::process_output("git", [~"clone", source.to_str(), target.to_str()]); + if outp.status != 0 { + io::println(str::from_bytes_owned(outp.output.clone())); + io::println(str::from_bytes_owned(outp.error)); + fail!("Couldn't `git clone` %s", source.to_str()); + } + else { + match v { + &ExactRevision(ref s) => { + debug!("`Running: git --work-tree=%s --git-dir=%s checkout %s", + *s, target.to_str(), target.push(".git").to_str()); + let outp = run::process_output("git", + [fmt!("--work-tree=%s", target.to_str()), + fmt!("--git-dir=%s", target.push(".git").to_str()), + ~"checkout", fmt!("%s", *s)]); + if outp.status != 0 { + io::println(str::from_bytes_owned(outp.output.clone())); + io::println(str::from_bytes_owned(outp.error)); + fail!("Couldn't `git checkout %s` in %s", + *s, target.to_str()); + } + } + _ => () + } + } } else { - // Pull changes - // Note that this ignores tags, which is probably wrong. There are no tests for - // it, though. + // Check that no version was specified. There's no reason to not handle the + // case where a version was requested, but I haven't implemented it. + assert!(*v == NoVersion); debug!("Running: git --work-tree=%s --git-dir=%s pull --no-edit %s", target.to_str(), target.push(".git").to_str(), source.to_str()); let outp = run::process_output("git", [fmt!("--work-tree=%s", target.to_str()), diff --git a/src/librustpkg/tests.rs b/src/librustpkg/tests.rs index 9fea8662129..121dcf40150 100644 --- a/src/librustpkg/tests.rs +++ b/src/librustpkg/tests.rs @@ -12,11 +12,10 @@ use context::Ctx; use std::hashmap::HashMap; -use std::{io, libc, os, result, run, str}; +use std::{io, libc, os, run, str}; use extra::tempfile::mkdtemp; use std::run::ProcessOutput; use installed_packages::list_installed_packages; -use package_path::*; use package_id::{PkgId}; use version::{ExactRevision, NoVersion, Version, Tagged}; use path_util::{target_executable_in_workspace, target_library_in_workspace, @@ -24,7 +23,9 @@ use path_util::{target_executable_in_workspace, target_library_in_workspace, make_dir_rwx, U_RWX, library_in_workspace, built_bench_in_workspace, built_test_in_workspace, built_library_in_workspace, built_executable_in_workspace, - installed_library_in_workspace, rust_path}; + installed_library_in_workspace}; +use rustc::metadata::filesearch::rust_path; +use rustc::driver::driver::host_triple; use target::*; /// Returns the last-modified date as an Option @@ -42,31 +43,25 @@ fn fake_ctxt(sysroot_opt: Option<@Path>) -> Ctx { fn fake_pkg() -> PkgId { let sn = ~"bogus"; - let remote = RemotePath(Path(sn)); PkgId { - local_path: normalize(remote.clone()), - remote_path: remote, + path: Path(sn), short_name: sn, version: NoVersion } } fn git_repo_pkg() -> PkgId { - let remote = RemotePath(Path("mockgithub.com/catamorphism/test-pkg")); PkgId { - local_path: normalize(remote.clone()), - remote_path: remote, - short_name: ~"test_pkg", + path: Path("mockgithub.com/catamorphism/test-pkg"), + short_name: ~"test-pkg", version: NoVersion } } fn git_repo_pkg_with_tag(a_tag: ~str) -> PkgId { - let remote = RemotePath(Path("mockgithub.com/catamorphism/test-pkg")); PkgId { - local_path: normalize(remote.clone()), - remote_path: remote, - short_name: ~"test_pkg", + path: Path("mockgithub.com/catamorphism/test-pkg"), + short_name: ~"test-pkg", version: Tagged(a_tag) } } @@ -76,13 +71,13 @@ fn writeFile(file_path: &Path, contents: &str) { out.write_line(contents); } -fn mk_empty_workspace(short_name: &LocalPath, version: &Version) -> Path { +fn mk_empty_workspace(short_name: &Path, version: &Version) -> Path { let workspace_dir = mkdtemp(&os::tmpdir(), "test").expect("couldn't create temp dir"); mk_workspace(&workspace_dir, short_name, version); workspace_dir } -fn mk_workspace(workspace: &Path, short_name: &LocalPath, version: &Version) -> Path { +fn mk_workspace(workspace: &Path, short_name: &Path, version: &Version) -> Path { // include version number in directory name let package_dir = workspace.push("src").push(fmt!("%s-%s", short_name.to_str(), version.to_str())); @@ -90,7 +85,7 @@ fn mk_workspace(workspace: &Path, short_name: &LocalPath, version: &Version) -> package_dir } -fn mk_temp_workspace(short_name: &LocalPath, version: &Version) -> Path { +fn mk_temp_workspace(short_name: &Path, version: &Version) -> Path { let package_dir = mk_empty_workspace(short_name, version).push("src").push(fmt!("%s-%s", short_name.to_str(), @@ -116,6 +111,22 @@ fn mk_temp_workspace(short_name: &LocalPath, version: &Version) -> Path { package_dir } +fn run_git(args: &[~str], env: Option<~[(~str, ~str)]>, cwd: &Path, err_msg: &str) { + let cwd = (*cwd).clone(); + let mut prog = run::Process::new("git", args, run::ProcessOptions { + env: env.map(|v| v.slice(0, v.len())), + dir: Some(&cwd), + in_fd: None, + out_fd: None, + err_fd: None + }); + let rslt = prog.finish_with_output(); + if rslt.status != 0 { + fail!("%s [git returned %?, output = %s, error = %s]", err_msg, + rslt.status, str::from_bytes(rslt.output), str::from_bytes(rslt.error)); + } +} + /// Should create an empty git repo in p, relative to the tmp dir, and return the new /// absolute path fn init_git_repo(p: &Path) -> Path { @@ -125,37 +136,14 @@ fn init_git_repo(p: &Path) -> Path { let work_dir_for_opts = work_dir.clone(); assert!(os::mkdir_recursive(&work_dir, U_RWX)); debug!("Running: git init in %s", work_dir.to_str()); - let opts = run::ProcessOptions { - env: None, - dir: Some(&work_dir_for_opts), - in_fd: None, - out_fd: None, - err_fd: None - }; - let mut prog = run::Process::new("git", [~"init"], opts); - let mut output = prog.finish_with_output(); - if output.status == 0 { - // Add stuff to the dir so that git tag succeeds - writeFile(&work_dir.push("README"), ""); - prog = run::Process::new("git", [~"add", ~"README"], opts); - output = prog.finish_with_output(); - if output.status == 0 { - prog = run::Process::new("git", [~"commit", ~"-m", ~"whatever"], opts); - output = prog.finish_with_output(); - if output.status == 0 { - tmp - } - else { - fail!("Couldn't commit in %s", work_dir.to_str()); - } - } - else { - fail!("Couldn't add in %s", work_dir.to_str()); - } - } - else { - fail!("Couldn't initialize git repository in %s", work_dir.to_str()) - } + let ws = work_dir.to_str(); + run_git([~"init"], None, &work_dir_for_opts, + fmt!("Couldn't initialize git repository in %s", ws)); + // Add stuff to the dir so that git tag succeeds + writeFile(&work_dir.push("README"), ""); + run_git([~"add", ~"README"], None, &work_dir_for_opts, fmt!("Couldn't add in %s", ws)); + git_commit(&work_dir_for_opts, ~"whatever"); + tmp } fn add_all_and_commit(repo: &Path) { @@ -164,51 +152,20 @@ fn add_all_and_commit(repo: &Path) { } fn git_commit(repo: &Path, msg: ~str) { - let mut prog = run::Process::new("git", [~"commit", ~"-m", msg], - run::ProcessOptions { env: None, - dir: Some(repo), - in_fd: None, - out_fd: None, - err_fd: None - }); - let output = prog.finish_with_output(); - if output.status != 0 { - fail!("Couldn't commit in %s: output was %s", repo.to_str(), - str::from_bytes(output.output + output.error)) - } - + run_git([~"commit", ~"--author=tester ", ~"-m", msg], + None, repo, fmt!("Couldn't commit in %s", repo.to_str())); } fn git_add_all(repo: &Path) { - let mut prog = run::Process::new("git", [~"add", ~"-A"], - run::ProcessOptions { env: None, - dir: Some(repo), - in_fd: None, - out_fd: None, - err_fd: None - }); - let output = prog.finish_with_output(); - if output.status != 0 { - fail!("Couldn't add all files in %s: output was %s", - repo.to_str(), str::from_bytes(output.output + output.error)) - } + run_git([~"add", ~"-A"], None, repo, fmt!("Couldn't add all files in %s", repo.to_str())); } fn add_git_tag(repo: &Path, tag: ~str) { assert!(repo.is_absolute()); git_add_all(repo); git_commit(repo, ~"whatever"); - let mut prog = run::Process::new("git", [~"tag", tag.clone()], - run::ProcessOptions { env: None, - dir: Some(repo), - in_fd: None, - out_fd: None, - err_fd: None - }); - let output = prog.finish_with_output(); - if output.status != 0 { - fail!("Couldn't add git tag %s in %s", tag, repo.to_str()) - } + run_git([~"tag", tag.clone()], None, repo, + fmt!("Couldn't add git tag %s in %s", tag, repo.to_str())); } fn is_rwx(p: &Path) -> bool { @@ -231,6 +188,25 @@ fn test_sysroot() -> Path { self_path.pop() } +// Returns the path to rustpkg +fn rustpkg_exec() -> Path { + // Ugh + let first_try = test_sysroot().push("lib").push("rustc") + .push(host_triple()).push("bin").push("rustpkg"); + if is_executable(&first_try) { + first_try + } + else { + let second_try = test_sysroot().push("bin").push("rustpkg"); + if is_executable(&second_try) { + second_try + } + else { + fail!("in rustpkg test, can't find an installed rustpkg"); + } + } +} + fn command_line_test(args: &[~str], cwd: &Path) -> ProcessOutput { command_line_test_with_env(args, cwd, None) } @@ -240,8 +216,9 @@ fn command_line_test(args: &[~str], cwd: &Path) -> ProcessOutput { /// Returns the process's output. fn command_line_test_with_env(args: &[~str], cwd: &Path, env: Option<~[(~str, ~str)]>) -> ProcessOutput { - let cmd = test_sysroot().push("bin").push("rustpkg").to_str(); - debug!("About to run command: %? %? in %s", cmd, args, cwd.to_str()); + let cmd = rustpkg_exec().to_str(); + debug!("cd %s; %s %s", + cwd.to_str(), cmd, args.connect(" ")); assert!(os::path_is_dir(&*cwd)); let cwd = (*cwd).clone(); let mut prog = run::Process::new(cmd, args, run::ProcessOptions { @@ -263,14 +240,15 @@ So tests that use this need to check the existence of a file to make sure the command succeeded */ if output.status != 0 { - fail!("Command %s %? failed with exit code %?", - cmd, args, output.status); + fail!("Command %s %? failed with exit code %?; its output was {{{ %s }}}", + cmd, args, output.status, + str::from_bytes(output.output) + str::from_bytes(output.error)); } output } fn create_local_package(pkgid: &PkgId) -> Path { - let parent_dir = mk_temp_workspace(&pkgid.local_path, &pkgid.version); + let parent_dir = mk_temp_workspace(&pkgid.path, &pkgid.version); debug!("Created empty package dir for %s, returning %s", pkgid.to_str(), parent_dir.to_str()); parent_dir.pop().pop() } @@ -327,26 +305,26 @@ fn create_local_package_with_custom_build_hook(pkgid: &PkgId, } -fn assert_lib_exists(repo: &Path, short_name: &str, v: Version) { +fn assert_lib_exists(repo: &Path, short_name: &str, _v: Version) { // ??? version? debug!("assert_lib_exists: repo = %s, short_name = %s", repo.to_str(), short_name); - let lib = target_library_in_workspace(&(PkgId { - version: v, ..PkgId::new(short_name, repo)} - ), repo); - debug!("assert_lib_exists: checking whether %s exists", lib.to_str()); - assert!(os::path_exists(&lib)); - assert!(is_rwx(&lib)); + let lib = installed_library_in_workspace(short_name, repo); + debug!("assert_lib_exists: checking whether %? exists", lib); + assert!(lib.is_some()); + let libname = lib.get_ref(); + assert!(os::path_exists(libname)); + assert!(is_rwx(libname)); } fn assert_executable_exists(repo: &Path, short_name: &str) { debug!("assert_executable_exists: repo = %s, short_name = %s", repo.to_str(), short_name); - let exec = target_executable_in_workspace(&PkgId::new(short_name, repo), repo); + let exec = target_executable_in_workspace(&PkgId::new(short_name), repo); assert!(os::path_exists(&exec)); assert!(is_rwx(&exec)); } fn assert_built_executable_exists(repo: &Path, short_name: &str) { debug!("assert_built_executable_exists: repo = %s, short_name = %s", repo.to_str(), short_name); - let exec = built_executable_in_workspace(&PkgId::new(short_name, repo), + let exec = built_executable_in_workspace(&PkgId::new(short_name), repo).expect("assert_built_executable_exists failed"); assert!(os::path_exists(&exec)); assert!(is_rwx(&exec)); @@ -372,11 +350,11 @@ fn command_line_test_output_with_env(args: &[~str], env: ~[(~str, ~str)]) -> ~[~ result } -// assumes short_name and local_path are one and the same -- I should fix +// assumes short_name and path are one and the same -- I should fix fn lib_output_file_name(workspace: &Path, parent: &str, short_name: &str) -> Path { debug!("lib_output_file_name: given %s and parent %s and short name %s", workspace.to_str(), parent, short_name); - library_in_workspace(&normalize(RemotePath(Path(short_name))), + library_in_workspace(&Path(short_name), short_name, Build, workspace, @@ -451,7 +429,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.local_path, &NoVersion).pop().pop(); + let temp_workspace = mk_temp_workspace(&temp_pkg_id.path, &NoVersion).pop().pop(); debug!("temp_workspace = %s", temp_workspace.to_str()); // should have test, bench, lib, and main ctxt.install(&temp_workspace, &temp_pkg_id); @@ -504,7 +482,7 @@ fn test_install_git() { let sysroot = test_sysroot(); debug!("sysroot = %s", sysroot.to_str()); let temp_pkg_id = git_repo_pkg(); - let repo = init_git_repo(&Path(temp_pkg_id.local_path.to_str())); + let repo = init_git_repo(&temp_pkg_id.path); let repo_subdir = repo.push("mockgithub.com").push("catamorphism").push("test_pkg"); writeFile(&repo_subdir.push("main.rs"), "fn main() { let _x = (); }"); @@ -517,9 +495,9 @@ fn test_install_git() { add_git_tag(&repo_subdir, ~"0.1"); // this has the effect of committing the files debug!("test_install_git: calling rustpkg install %s in %s", - temp_pkg_id.local_path.to_str(), repo.to_str()); + temp_pkg_id.path.to_str(), repo.to_str()); // should have test, bench, lib, and main - command_line_test([~"install", temp_pkg_id.local_path.to_str()], &repo); + command_line_test([~"install", temp_pkg_id.path.to_str()], &repo); // Check that all files exist debug!("Checking for files in %s", repo.to_str()); let exec = target_executable_in_workspace(&temp_pkg_id, &repo); @@ -563,18 +541,18 @@ fn test_package_ids_must_be_relative_path_like() { */ - let whatever = PkgId::new("foo", &os::getcwd()); + let whatever = PkgId::new("foo"); assert_eq!(~"foo-0.1", whatever.to_str()); - assert!("github.com/catamorphism/test_pkg-0.1" == - PkgId::new("github.com/catamorphism/test-pkg", &os::getcwd()).to_str()); + assert!("github.com/catamorphism/test-pkg-0.1" == + PkgId::new("github.com/catamorphism/test-pkg").to_str()); do cond.trap(|(p, e)| { assert!("" == p.to_str()); assert!("0-length pkgid" == e); whatever.clone() }).inside { - let x = PkgId::new("", &os::getcwd()); + let x = PkgId::new(""); assert_eq!(~"foo-0.1", x.to_str()); } @@ -583,8 +561,7 @@ fn test_package_ids_must_be_relative_path_like() { assert!("absolute pkgid" == e); whatever.clone() }).inside { - let z = PkgId::new(os::make_absolute(&Path("foo/bar/quux")).to_str(), - &os::getcwd()); + let z = PkgId::new(os::make_absolute(&Path("foo/bar/quux")).to_str()); assert_eq!(~"foo-0.1", z.to_str()); } @@ -607,7 +584,7 @@ fn test_package_version() { "#[bench] pub fn f() { (); }"); add_git_tag(&repo_subdir, ~"0.4"); - let temp_pkg_id = PkgId::new("mockgithub.com/catamorphism/test_pkg_version", &repo); + let temp_pkg_id = PkgId::new("mockgithub.com/catamorphism/test_pkg_version"); match temp_pkg_id.version { ExactRevision(~"0.4") => (), _ => fail!(fmt!("test_package_version: package version was %?, expected Some(0.4)", @@ -656,7 +633,7 @@ fn test_package_request_version() { } None => false }); - let temp_pkg_id = PkgId::new("mockgithub.com/catamorphism/test_pkg_version#0.3", &repo); + let temp_pkg_id = PkgId::new("mockgithub.com/catamorphism/test_pkg_version#0.3"); assert!(target_executable_in_workspace(&temp_pkg_id, &repo.push(".rust")) == repo.push(".rust").push("bin").push("test_pkg_version")); @@ -696,12 +673,12 @@ fn rustpkg_library_target() { add_git_tag(&package_dir, ~"1.0"); command_line_test([~"install", ~"foo"], &foo_repo); - assert_lib_exists(&foo_repo, "foo", ExactRevision(~"1.0")); + assert_lib_exists(&foo_repo.push(".rust"), "foo", ExactRevision(~"1.0")); } #[test] fn rustpkg_local_pkg() { - let dir = create_local_package(&PkgId::new("foo", &os::getcwd())); + let dir = create_local_package(&PkgId::new("foo")); command_line_test([~"install", ~"foo"], &dir); assert_executable_exists(&dir, "foo"); } @@ -711,7 +688,7 @@ fn rustpkg_local_pkg() { #[test] #[ignore] fn package_script_with_default_build() { - let dir = create_local_package(&PkgId::new("fancy-lib", &os::getcwd())); + let dir = create_local_package(&PkgId::new("fancy-lib")); debug!("dir = %s", dir.to_str()); let source = test_sysroot().pop().pop().pop().push("src").push("librustpkg"). push("testsuite").push("pass").push("src").push("fancy-lib").push("pkg.rs"); @@ -763,7 +740,7 @@ fn rustpkg_clean_no_arg() { command_line_test([~"build"], &package_dir); assert_built_executable_exists(&tmp, "foo"); command_line_test([~"clean"], &package_dir); - assert!(!built_executable_in_workspace(&PkgId::new("foo", &package_dir), + assert!(!built_executable_in_workspace(&PkgId::new("foo"), &tmp).map_default(false, |m| { os::path_exists(m) })); } @@ -771,14 +748,13 @@ fn rustpkg_clean_no_arg() { #[ignore (reason = "Specifying env doesn't work -- see #8028")] fn rust_path_test() { let dir_for_path = mkdtemp(&os::tmpdir(), "more_rust").expect("rust_path_test failed"); - let dir = mk_workspace(&dir_for_path, &normalize(RemotePath(Path("foo"))), &NoVersion); + let dir = mk_workspace(&dir_for_path, &Path("foo"), &NoVersion); debug!("dir = %s", dir.to_str()); writeFile(&dir.push("main.rs"), "fn main() { let _x = (); }"); let cwd = os::getcwd(); debug!("cwd = %s", cwd.to_str()); - debug!("Running command: cd %s; RUST_LOG=rustpkg RUST_PATH=%s rustpkg install foo", - cwd.to_str(), dir_for_path.to_str()); + // use command_line_test_with_env let mut prog = run::Process::new("rustpkg", [~"install", ~"foo"], run::ProcessOptions { env: Some(&[(~"RUST_LOG", @@ -830,39 +806,38 @@ fn rust_path_parse() { #[test] fn test_list() { let dir = mkdtemp(&os::tmpdir(), "test_list").expect("test_list failed"); - let foo = PkgId::new("foo", &dir); + let foo = PkgId::new("foo"); create_local_package_in(&foo, &dir); - let bar = PkgId::new("bar", &dir); + let bar = PkgId::new("bar"); create_local_package_in(&bar, &dir); - let quux = PkgId::new("quux", &dir); + let quux = PkgId::new("quux"); create_local_package_in(&quux, &dir); -// NOTE Not really great output, though... -// NOTE do any tests need to be unignored? +// list doesn't output very much right now... command_line_test([~"install", ~"foo"], &dir); let env_arg = ~[(~"RUST_PATH", dir.to_str())]; debug!("RUST_PATH = %s", dir.to_str()); let list_output = command_line_test_output_with_env([~"list"], env_arg.clone()); - assert!(list_output.iter().any(|x| x.starts_with("libfoo_"))); + assert!(list_output.iter().any(|x| x.starts_with("foo"))); command_line_test([~"install", ~"bar"], &dir); let list_output = command_line_test_output_with_env([~"list"], env_arg.clone()); - assert!(list_output.iter().any(|x| x.starts_with("libfoo_"))); - assert!(list_output.iter().any(|x| x.starts_with("libbar_"))); + assert!(list_output.iter().any(|x| x.starts_with("foo"))); + assert!(list_output.iter().any(|x| x.starts_with("bar"))); command_line_test([~"install", ~"quux"], &dir); let list_output = command_line_test_output_with_env([~"list"], env_arg); - assert!(list_output.iter().any(|x| x.starts_with("libfoo_"))); - assert!(list_output.iter().any(|x| x.starts_with("libbar_"))); - assert!(list_output.iter().any(|x| x.starts_with("libquux_"))); + assert!(list_output.iter().any(|x| x.starts_with("foo"))); + assert!(list_output.iter().any(|x| x.starts_with("bar"))); + assert!(list_output.iter().any(|x| x.starts_with("quux"))); } #[test] fn install_remove() { let dir = mkdtemp(&os::tmpdir(), "install_remove").expect("install_remove"); - let foo = PkgId::new("foo", &dir); - let bar = PkgId::new("bar", &dir); - let quux = PkgId::new("quux", &dir); + let foo = PkgId::new("foo"); + let bar = PkgId::new("bar"); + let quux = PkgId::new("quux"); create_local_package_in(&foo, &dir); create_local_package_in(&bar, &dir); create_local_package_in(&quux, &dir); @@ -887,7 +862,7 @@ fn install_check_duplicates() { // ("Is already installed -- doing nothing") // check invariant that there are no dups in the pkg database let dir = mkdtemp(&os::tmpdir(), "install_remove").expect("install_remove"); - let foo = PkgId::new("foo", &dir); + let foo = PkgId::new("foo"); create_local_package_in(&foo, &dir); command_line_test([~"install", ~"foo"], &dir); @@ -895,7 +870,7 @@ fn install_check_duplicates() { let mut contents = ~[]; let check_dups = |p: &PkgId| { if contents.contains(p) { - fail!("package %s appears in `list` output more than once", p.local_path.to_str()); + fail!("package %s appears in `list` output more than once", p.path.to_str()); } else { contents.push((*p).clone()); @@ -908,7 +883,7 @@ fn install_check_duplicates() { #[test] #[ignore(reason = "Workcache not yet implemented -- see #7075")] fn no_rebuilding() { - let p_id = PkgId::new("foo", &os::getcwd()); + let p_id = PkgId::new("foo"); let workspace = create_local_package(&p_id); command_line_test([~"build", ~"foo"], &workspace); let date = datestamp(&built_library_in_workspace(&p_id, @@ -922,8 +897,8 @@ fn no_rebuilding() { #[test] #[ignore(reason = "Workcache not yet implemented -- see #7075")] fn no_rebuilding_dep() { - let p_id = PkgId::new("foo", &os::getcwd()); - let dep_id = PkgId::new("bar", &os::getcwd()); + let p_id = PkgId::new("foo"); + let dep_id = PkgId::new("bar"); let workspace = create_local_package_with_dep(&p_id, &dep_id); command_line_test([~"build", ~"foo"], &workspace); let bar_date = datestamp(&lib_output_file_name(&workspace, @@ -935,8 +910,8 @@ fn no_rebuilding_dep() { #[test] fn do_rebuild_dep_dates_change() { - let p_id = PkgId::new("foo", &os::getcwd()); - let dep_id = PkgId::new("bar", &os::getcwd()); + let p_id = PkgId::new("foo"); + let dep_id = PkgId::new("bar"); let workspace = create_local_package_with_dep(&p_id, &dep_id); command_line_test([~"build", ~"foo"], &workspace); let bar_date = datestamp(&lib_output_file_name(&workspace, "build", "bar")); @@ -948,8 +923,8 @@ fn do_rebuild_dep_dates_change() { #[test] fn do_rebuild_dep_only_contents_change() { - let p_id = PkgId::new("foo", &os::getcwd()); - let dep_id = PkgId::new("bar", &os::getcwd()); + let p_id = PkgId::new("foo"); + let dep_id = PkgId::new("bar"); let workspace = create_local_package_with_dep(&p_id, &dep_id); command_line_test([~"build", ~"foo"], &workspace); let bar_date = datestamp(&lib_output_file_name(&workspace, "build", "bar")); @@ -962,8 +937,8 @@ fn do_rebuild_dep_only_contents_change() { #[test] fn test_versions() { - let workspace = create_local_package(&PkgId::new("foo#0.1", &os::getcwd())); - create_local_package(&PkgId::new("foo#0.2", &os::getcwd())); + let workspace = create_local_package(&PkgId::new("foo#0.1")); + create_local_package(&PkgId::new("foo#0.2")); command_line_test([~"install", ~"foo#0.1"], &workspace); let output = command_line_test_output([~"list"]); // make sure output includes versions @@ -973,7 +948,7 @@ fn test_versions() { #[test] #[ignore(reason = "do not yet implemented")] fn test_build_hooks() { - let workspace = create_local_package_with_custom_build_hook(&PkgId::new("foo", &os::getcwd()), + let workspace = create_local_package_with_custom_build_hook(&PkgId::new("foo"), "frob"); command_line_test([~"do", ~"foo", ~"frob"], &workspace); } @@ -983,7 +958,7 @@ fn test_build_hooks() { #[ignore(reason = "info not yet implemented")] fn test_info() { let expected_info = ~"package foo"; // fill in - let workspace = create_local_package(&PkgId::new("foo", &os::getcwd())); + let workspace = create_local_package(&PkgId::new("foo")); let output = command_line_test([~"info", ~"foo"], &workspace); assert_eq!(str::from_bytes(output.output), expected_info); } @@ -992,7 +967,7 @@ fn test_info() { #[ignore(reason = "test not yet implemented")] fn test_rustpkg_test() { let expected_results = ~"1 out of 1 tests passed"; // fill in - let workspace = create_local_package_with_test(&PkgId::new("foo", &os::getcwd())); + let workspace = create_local_package_with_test(&PkgId::new("foo")); let output = command_line_test([~"test", ~"foo"], &workspace); assert_eq!(str::from_bytes(output.output), expected_results); } @@ -1000,7 +975,7 @@ fn test_rustpkg_test() { #[test] #[ignore(reason = "test not yet implemented")] fn test_uninstall() { - let workspace = create_local_package(&PkgId::new("foo", &os::getcwd())); + let workspace = create_local_package(&PkgId::new("foo")); let _output = command_line_test([~"info", ~"foo"], &workspace); command_line_test([~"uninstall", ~"foo"], &workspace); let output = command_line_test([~"list"], &workspace); @@ -1010,8 +985,8 @@ fn test_uninstall() { #[test] fn test_non_numeric_tag() { let temp_pkg_id = git_repo_pkg(); - let repo = init_git_repo(&Path(temp_pkg_id.local_path.to_str())); - let repo_subdir = repo.push("mockgithub.com").push("catamorphism").push("test_pkg"); + let repo = init_git_repo(&temp_pkg_id.path); + let repo_subdir = repo.push("mockgithub.com").push("catamorphism").push("test-pkg"); writeFile(&repo_subdir.push("foo"), "foo"); writeFile(&repo_subdir.push("lib.rs"), "pub fn f() { let _x = (); }"); @@ -1021,13 +996,70 @@ fn test_non_numeric_tag() { writeFile(&repo_subdir.push("not_on_testbranch_only"), "bye bye"); add_all_and_commit(&repo_subdir); - - command_line_test([~"install", fmt!("%s#testbranch", temp_pkg_id.remote_path.to_str())], - &repo); + command_line_test([~"install", fmt!("%s#testbranch", temp_pkg_id.path.to_str())], &repo); let file1 = repo.push_many(["mockgithub.com", "catamorphism", - "test_pkg", "testbranch_only"]); - let file2 = repo.push_many(["mockgithub.com", "catamorphism", "test_pkg", + "test-pkg", "testbranch_only"]); + let file2 = repo.push_many(["mockgithub.com", "catamorphism", "test-pkg", "master_only"]); assert!(os::path_exists(&file1)); assert!(!os::path_exists(&file2)); } + +#[test] +fn test_extern_mod() { + let dir = mkdtemp(&os::tmpdir(), "test_extern_mod").expect("test_extern_mod"); + let main_file = dir.push("main.rs"); + let lib_depend_dir = mkdtemp(&os::tmpdir(), "foo").expect("test_extern_mod"); + let aux_dir = lib_depend_dir.push_many(["src", "mockgithub.com", "catamorphism", "test_pkg"]); + assert!(os::mkdir_recursive(&aux_dir, U_RWX)); + let aux_pkg_file = aux_dir.push("lib.rs"); + + writeFile(&aux_pkg_file, "pub mod bar { pub fn assert_true() { assert!(true); } }\n"); + assert!(os::path_exists(&aux_pkg_file)); + + writeFile(&main_file, + "extern mod test = \"mockgithub.com/catamorphism/test_pkg\";\nuse test::bar;\ + fn main() { bar::assert_true(); }\n"); + + command_line_test([~"install", ~"mockgithub.com/catamorphism/test_pkg"], &lib_depend_dir); + + let exec_file = dir.push("out"); + // Be sure to extend the existing environment + let env = Some([(~"RUST_PATH", lib_depend_dir.to_str())] + os::env()); + let rustpkg_exec = rustpkg_exec(); + let rustc = rustpkg_exec.with_filename("rustc"); + debug!("RUST_PATH=%s %s %s \n --sysroot %s -o %s", + lib_depend_dir.to_str(), + rustc.to_str(), + main_file.to_str(), + test_sysroot().to_str(), + exec_file.to_str()); + + let mut prog = run::Process::new(rustc.to_str(), [main_file.to_str(), + ~"--sysroot", test_sysroot().to_str(), + ~"-o", exec_file.to_str()], + run::ProcessOptions { + env: env.map(|v| v.slice(0, v.len())), + dir: Some(&dir), + in_fd: None, + out_fd: None, + err_fd: None + }); + let outp = prog.finish_with_output(); + if outp.status != 0 { + fail!("output was %s, error was %s", + str::from_bytes(outp.output), + str::from_bytes(outp.error)); + } + assert!(os::path_exists(&exec_file) && is_executable(&exec_file)); +} + +/// Returns true if p exists and is executable +fn is_executable(p: &Path) -> bool { + use std::libc::consts::os::posix88::{S_IXUSR}; + + match p.get_mode() { + None => false, + Some(mode) => mode & S_IXUSR as uint == S_IXUSR as uint + } +} diff --git a/src/librustpkg/util.rs b/src/librustpkg/util.rs index 81b47d6a16c..ed21f8e0872 100644 --- a/src/librustpkg/util.rs +++ b/src/librustpkg/util.rs @@ -8,9 +8,8 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use std::{os, result}; +use std::os; use rustc::driver::{driver, session}; -use rustc::metadata::filesearch; use extra::getopts::groups::getopts; use syntax::ast_util::*; use syntax::codemap::{dummy_sp, spanned}; @@ -19,10 +18,10 @@ use syntax::{ast, attr, codemap, diagnostic, fold}; use syntax::attr::AttrMetaMethods; use rustc::back::link::output_type_exe; use rustc::driver::session::{lib_crate, bin_crate}; -use context::Ctx; +use context::{Ctx, in_target}; use package_id::PkgId; use search::find_library_in_search_path; -use path_util::target_library_in_workspace; +use path_util::{target_library_in_workspace, U_RWX}; pub use target::{OutputType, Main, Lib, Bench, Test}; // It would be nice to have the list of commands in just one place -- for example, @@ -47,13 +46,6 @@ impl ToStr for Pkg { } } -pub fn root() -> Path { - match filesearch::get_rustpkg_root() { - result::Ok(path) => path, - result::Err(err) => fail!(err) - } -} - pub fn is_cmd(cmd: &str) -> bool { COMMANDS.iter().any(|&c| c == cmd) } @@ -162,25 +154,25 @@ pub fn ready_crate(sess: session::Session, pub fn compile_input(ctxt: &Ctx, pkg_id: &PkgId, in_file: &Path, - out_dir: &Path, + workspace: &Path, flags: &[~str], cfgs: &[~str], opt: bool, what: OutputType) -> bool { - let workspace = out_dir.pop().pop(); - assert!(in_file.components.len() > 1); let input = driver::file_input((*in_file).clone()); 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 out_dir = workspace.push("build").push_rel(&pkg_id.path); + let binary = os::args()[0].to_managed(); debug!("flags: %s", flags.connect(" ")); debug!("cfgs: %s", cfgs.connect(" ")); - debug!("compile_input's sysroot = %?", ctxt.sysroot_opt); + debug!("out_dir = %s", out_dir.to_str()); let crate_type = match what { Lib => lib_crate, @@ -196,12 +188,22 @@ pub fn compile_input(ctxt: &Ctx, + flags + cfgs.flat_map(|c| { ~[~"--cfg", (*c).clone()] }), driver::optgroups()).unwrap(); + // Hack so that rustpkg can run either out of a rustc target dir, + // or the host dir + let sysroot_to_use = if !in_target(ctxt.sysroot_opt) { + ctxt.sysroot_opt + } + else { + ctxt.sysroot_opt.map(|p| { @p.pop().pop().pop() }) + }; + debug!("compile_input's sysroot = %?", ctxt.sysroot_opt_str()); + debug!("sysroot_to_use = %?", sysroot_to_use); let options = @session::options { crate_type: crate_type, optimize: if opt { session::Aggressive } else { session::No }, test: what == Test || what == Bench, - maybe_sysroot: ctxt.sysroot_opt, - addl_lib_search_paths: @mut (~[(*out_dir).clone()]), + maybe_sysroot: sysroot_to_use, + addl_lib_search_paths: @mut (~[out_dir.clone()]), // output_type should be conditional output_type: output_type_exe, // Use this to get a library? That's weird .. (*driver::build_session_options(binary, &matches, diagnostic::emit)).clone() @@ -211,7 +213,12 @@ pub fn compile_input(ctxt: &Ctx, // Make sure all the library directories actually exist, since the linker will complain // otherwise for p in addl_lib_search_paths.iter() { - assert!(os::path_is_dir(p)); + if os::path_exists(p) { + assert!(os::path_is_dir(p)); + } + else { + assert!(os::mkdir_recursive(p, U_RWX)); + } } let sess = driver::build_session(options, diagnostic::emit); @@ -224,35 +231,44 @@ pub fn compile_input(ctxt: &Ctx, // Not really right. Should search other workspaces too, and the installed // database (which doesn't exist yet) - find_and_install_dependencies(ctxt, sess, &workspace, crate, + find_and_install_dependencies(ctxt, sess, workspace, crate, |p| { debug!("a dependency: %s", p.to_str()); // Pass the directory containing a dependency // as an additional lib search path - addl_lib_search_paths.push(p); + if !addl_lib_search_paths.contains(&p) { + // Might be inefficient, but this set probably + // won't get too large -- tjc + addl_lib_search_paths.push(p); + } }); // Inject the link attributes so we get the right package name and version if attr::find_linkage_metas(crate.attrs).is_empty() { - let short_name_to_use = match what { - Test => fmt!("%stest", pkg_id.short_name), - Bench => fmt!("%sbench", pkg_id.short_name), - _ => pkg_id.short_name.clone() + let name_to_use = match what { + Test => fmt!("%stest", pkg_id.short_name).to_managed(), + Bench => fmt!("%sbench", pkg_id.short_name).to_managed(), + _ => pkg_id.short_name.to_managed() }; - debug!("Injecting link name: %s", short_name_to_use); + debug!("Injecting link name: %s", name_to_use); let link_options = - ~[attr::mk_name_value_item_str(@"name", short_name_to_use.to_managed()), - attr::mk_name_value_item_str(@"vers", pkg_id.version.to_str().to_managed())]; + ~[attr::mk_name_value_item_str(@"name", name_to_use), + attr::mk_name_value_item_str(@"vers", pkg_id.version.to_str().to_managed())] + + if pkg_id.is_complex() { + ~[attr::mk_name_value_item_str(@"package_id", + pkg_id.path.to_str().to_managed())] + } else { ~[] }; + debug!("link options: %?", link_options); crate = @ast::Crate { attrs: ~[attr::mk_attr(attr::mk_list_item(@"link", link_options))], .. (*crate).clone() - }; + } } - debug!("calling compile_crate_from_input, out_dir = %s, + debug!("calling compile_crate_from_input, workspace = %s, building_library = %?", out_dir.to_str(), sess.building_library); - compile_crate_from_input(&input, out_dir, sess, crate); + compile_crate_from_input(&input, &out_dir, sess, crate); true } @@ -262,17 +278,22 @@ pub fn compile_input(ctxt: &Ctx, // call compile_upto and return the crate // also, too many arguments pub fn compile_crate_from_input(input: &driver::input, - build_dir: &Path, + // should be of the form /build/ + out_dir: &Path, sess: session::Session, crate: @ast::Crate) { debug!("Calling build_output_filenames with %s, building library? %?", - build_dir.to_str(), sess.building_library); + out_dir.to_str(), sess.building_library); // bad copy - let outputs = driver::build_output_filenames(input, &Some((*build_dir).clone()), &None, + debug!("out_dir = %s", out_dir.to_str()); + let outputs = driver::build_output_filenames(input, &Some(out_dir.clone()), &None, crate.attrs, sess); - debug!("Outputs are %? and output type = %?", outputs, sess.opts.output_type); + debug!("Outputs are out_filename: %s and obj_filename: %s and output type = %?", + outputs.out_filename.to_str(), + outputs.obj_filename.to_str(), + sess.opts.output_type); debug!("additional libraries:"); for lib in sess.opts.addl_lib_search_paths.iter() { debug!("an additional library: %s", lib.to_str()); @@ -298,15 +319,15 @@ pub fn exe_suffix() -> ~str { ~"" } // Called by build_crates // FIXME (#4432): Use workcache to only compile when needed pub fn compile_crate(ctxt: &Ctx, pkg_id: &PkgId, - crate: &Path, dir: &Path, + crate: &Path, workspace: &Path, flags: &[~str], cfgs: &[~str], opt: bool, what: OutputType) -> bool { - debug!("compile_crate: crate=%s, dir=%s", crate.to_str(), dir.to_str()); + debug!("compile_crate: crate=%s, workspace=%s", crate.to_str(), workspace.to_str()); debug!("compile_crate: short_name = %s, flags =...", pkg_id.to_str()); for fl in flags.iter() { debug!("+++ %s", *fl); } - compile_input(ctxt, pkg_id, crate, dir, flags, cfgs, opt, what) + compile_input(ctxt, pkg_id, crate, workspace, flags, cfgs, opt, what) } @@ -327,19 +348,20 @@ pub fn find_and_install_dependencies(ctxt: &Ctx, debug!("A view item!"); match vi.node { // ignore metadata, I guess - ast::view_item_extern_mod(lib_ident, _, _) => { + ast::view_item_extern_mod(lib_ident, path_opt, _, _) => { match my_ctxt.sysroot_opt { - Some(ref x) => debug!("sysroot: %s", x.to_str()), + Some(ref x) => debug!("*** sysroot: %s", x.to_str()), None => debug!("No sysroot given") }; - let lib_name = sess.str_of(lib_ident); + let lib_name = match path_opt { // ??? + Some(p) => p, None => sess.str_of(lib_ident) }; match find_library_in_search_path(my_ctxt.sysroot_opt, lib_name) { Some(installed_path) => { debug!("It exists: %s", installed_path.to_str()); } None => { // Try to install it - let pkg_id = PkgId::new(lib_name, &os::getcwd()); + let pkg_id = PkgId::new(lib_name); my_ctxt.install(&my_workspace, &pkg_id); // Also, add an additional search path debug!("let installed_path...") diff --git a/src/librustpkg/version.rs b/src/librustpkg/version.rs index 88391850891..ab4f47ba69a 100644 --- a/src/librustpkg/version.rs +++ b/src/librustpkg/version.rs @@ -15,8 +15,8 @@ extern mod std; use extra::semver; use std::{char, os, result, run, str}; -use package_path::RemotePath; use extra::tempfile::mkdtemp; +use path_util::rust_path; #[deriving(Clone)] pub enum Version { @@ -92,19 +92,22 @@ pub fn parse_vers(vers: ~str) -> result::Result { } } -/// If `local_path` is a git repo, and the most recent tag in that repo denotes a version, -/// return it; otherwise, `None` +/// If `local_path` is a git repo in the RUST_PATH, and the most recent tag +/// in that repo denotes a version, return it; otherwise, `None` pub fn try_getting_local_version(local_path: &Path) -> Option { - debug!("in try_getting_local_version"); - let outp = run::process_output("git", + let rustpath = rust_path(); + for rp in rustpath.iter() { + let local_path = rp.push_rel(local_path); + debug!("in try_getting_local_version"); + let outp = run::process_output("git", [fmt!("--git-dir=%s", local_path.push(".git").to_str()), ~"tag", ~"-l"]); - debug!("git --git-dir=%s tag -l ~~~> %?", local_path.push(".git").to_str(), outp.status); + debug!("git --git-dir=%s tag -l ~~~> %?", local_path.push(".git").to_str(), outp.status); - if outp.status != 0 { - return None; - } + if outp.status != 0 { + loop; + } let mut output = None; let output_text = str::from_bytes(outp.output); @@ -112,14 +115,19 @@ pub fn try_getting_local_version(local_path: &Path) -> Option { if !l.is_whitespace() { output = Some(l); } + match output.chain(try_parsing_version) { + Some(v) => return Some(v), + None => () + } } - output.chain(try_parsing_version) + } + None } /// If `remote_path` refers to a git repo that can be downloaded, /// and the most recent tag in that repo denotes a version, return it; /// otherwise, `None` -pub fn try_getting_version(remote_path: &RemotePath) -> Option { +pub fn try_getting_version(remote_path: &Path) -> Option { debug!("try_getting_version: %s", remote_path.to_str()); if is_url_like(remote_path) { debug!("Trying to fetch its sources.."); @@ -190,7 +198,7 @@ fn try_parsing_version(s: &str) -> Option { } /// Just an approximation -fn is_url_like(p: &RemotePath) -> bool { +fn is_url_like(p: &Path) -> bool { let str = p.to_str(); str.split_iter('/').len_() > 2 } diff --git a/src/librustpkg/workspace.rs b/src/librustpkg/workspace.rs index d877b4ff489..3e0e08dfe2d 100644 --- a/src/librustpkg/workspace.rs +++ b/src/librustpkg/workspace.rs @@ -12,9 +12,11 @@ use std::os; use std::path::Path; -use path_util::{rust_path, workspace_contains_package_id}; +use path_util::workspace_contains_package_id; use package_id::PkgId; +use rustc::metadata::filesearch::rust_path; + pub fn each_pkg_parent_workspace(pkgid: &PkgId, action: &fn(&Path) -> bool) -> bool { // Using the RUST_PATH, find workspaces that contain // this package ID @@ -23,7 +25,7 @@ pub fn each_pkg_parent_workspace(pkgid: &PkgId, action: &fn(&Path) -> bool) -> b // tjc: make this a condition fail!("Package %s not found in any of \ the following workspaces: %s", - pkgid.remote_path.to_str(), + pkgid.path.to_str(), rust_path().to_str()); } for ws in workspaces.iter() { @@ -58,5 +60,5 @@ pub fn cwd_to_workspace() -> (Path, PkgId) { let ws = cwd.pop().pop(); let cwd_ = cwd.clone(); let pkgid = cwd_.components.last().to_str(); - (ws, PkgId::new(pkgid, &cwd)) + (ws, PkgId::new(pkgid)) } diff --git a/src/libstd/path.rs b/src/libstd/path.rs index 76001ae4188..3e1cb50ce4e 100644 --- a/src/libstd/path.rs +++ b/src/libstd/path.rs @@ -19,13 +19,14 @@ Cross-platform file path handling use clone::Clone; use container::Container; use cmp::Eq; -use iterator::{Iterator, IteratorUtil}; +use iterator::{Iterator, IteratorUtil, range}; use libc; +use num; use option::{None, Option, Some}; use str::{OwnedStr, Str, StrSlice, StrVector}; use to_str::ToStr; use ascii::{AsciiCast, AsciiStr}; -use vec::{OwnedVector, ImmutableVector}; +use vec::{OwnedVector, ImmutableVector, OwnedCopyableVector}; #[cfg(windows)] pub use Path = self::WindowsPath; @@ -124,6 +125,43 @@ pub trait GenericPath { /// True if `self` is an ancestor of `other`. See `test_is_ancestor_of` for examples fn is_ancestor_of(&self, (&Self)) -> bool; + + /// Find the relative path from one file to another + fn get_relative_to(&self, abs2: (&Self)) -> Self { + assert!(self.is_absolute()); + assert!(abs2.is_absolute()); + let abs1 = self.normalize(); + let abs2 = abs2.normalize(); + + let split1: &[~str] = abs1.components(); + let split2: &[~str] = abs2.components(); + let len1 = split1.len(); + let len2 = split2.len(); + assert!(len1 > 0); + assert!(len2 > 0); + + let max_common_path = num::min(len1, len2) - 1; + let mut start_idx = 0; + while start_idx < max_common_path + && split1[start_idx] == split2[start_idx] { + start_idx += 1; + } + + let mut path: ~[~str] = ~[]; + for _ in range(start_idx, len1 - 1) { path.push(~".."); }; + + path.push_all(split2.slice(start_idx, len2 - 1)); + + let mut result: Self = GenericPath::from_str("."); + if !path.is_empty() { + // Without this type hint, the typechecker doesn't seem to like it + let p: Self = GenericPath::from_str(""); + result = p.push_many(path); + }; + result + } + + fn components(self) -> ~[~str]; } #[cfg(target_os = "linux")] @@ -703,6 +741,7 @@ impl GenericPath for PosixPath { self.is_ancestor_of(&other.pop())) } + fn components(self) -> ~[~str] { self.components } } @@ -985,6 +1024,8 @@ impl GenericPath for WindowsPath { (!other.components.is_empty() && !(self.components.is_empty() && !self.is_absolute) && self.is_ancestor_of(&other.pop())) } + + fn components(self) -> ~[~str] { self.components } } pub fn normalize(components: &[~str]) -> ~[~str] { @@ -1341,4 +1382,124 @@ mod tests { } + #[test] + fn test_relative_to1() { + let p1 = PosixPath("/usr/bin/rustc"); + let p2 = PosixPath("/usr/lib/mylib"); + let res = p1.get_relative_to(&p2); + assert_eq!(res, PosixPath("../lib")); + + let p1 = WindowsPath("C:\\usr\\bin\\rustc"); + let p2 = WindowsPath("C:\\usr\\lib\\mylib"); + let res = p1.get_relative_to(&p2); + assert_eq!(res, WindowsPath("..\\lib")); + + } + + #[test] + fn test_relative_to2() { + let p1 = PosixPath("/usr/bin/rustc"); + let p2 = PosixPath("/usr/bin/../lib/mylib"); + let res = p1.get_relative_to(&p2); + assert_eq!(res, PosixPath("../lib")); + + let p1 = WindowsPath("C:\\usr\\bin\\rustc"); + let p2 = WindowsPath("C:\\usr\\bin\\..\\lib\\mylib"); + let res = p1.get_relative_to(&p2); + assert_eq!(res, WindowsPath("..\\lib")); + } + + #[test] + fn test_relative_to3() { + let p1 = PosixPath("/usr/bin/whatever/rustc"); + let p2 = PosixPath("/usr/lib/whatever/mylib"); + let res = p1.get_relative_to(&p2); + assert_eq!(res, PosixPath("../../lib/whatever")); + + let p1 = WindowsPath("C:\\usr\\bin\\whatever\\rustc"); + let p2 = WindowsPath("C:\\usr\\lib\\whatever\\mylib"); + let res = p1.get_relative_to(&p2); + assert_eq!(res, WindowsPath("..\\..\\lib\\whatever")); + + } + + #[test] + fn test_relative_to4() { + let p1 = PosixPath("/usr/bin/whatever/../rustc"); + let p2 = PosixPath("/usr/lib/whatever/mylib"); + let res = p1.get_relative_to(&p2); + assert_eq!(res, PosixPath("../lib/whatever")); + + let p1 = WindowsPath("C:\\usr\\bin\\whatever\\..\\rustc"); + let p2 = WindowsPath("C:\\usr\\lib\\whatever\\mylib"); + let res = p1.get_relative_to(&p2); + assert_eq!(res, WindowsPath("..\\lib\\whatever")); + + } + + #[test] + fn test_relative_to5() { + let p1 = PosixPath("/usr/bin/whatever/../rustc"); + let p2 = PosixPath("/usr/lib/whatever/../mylib"); + let res = p1.get_relative_to(&p2); + assert_eq!(res, PosixPath("../lib")); + + let p1 = WindowsPath("C:\\usr\\bin/whatever\\..\\rustc"); + let p2 = WindowsPath("C:\\usr\\lib\\whatever\\..\\mylib"); + let res = p1.get_relative_to(&p2); + assert_eq!(res, WindowsPath("..\\lib")); + } + + #[test] + fn test_relative_to6() { + let p1 = PosixPath("/1"); + let p2 = PosixPath("/2/3"); + let res = p1.get_relative_to(&p2); + assert_eq!(res, PosixPath("2")); + + let p1 = WindowsPath("C:\\1"); + let p2 = WindowsPath("C:\\2\\3"); + let res = p1.get_relative_to(&p2); + assert_eq!(res, WindowsPath("2")); + + } + + #[test] + fn test_relative_to7() { + let p1 = PosixPath("/1/2"); + let p2 = PosixPath("/3"); + let res = p1.get_relative_to(&p2); + assert_eq!(res, PosixPath("..")); + + let p1 = WindowsPath("C:\\1\\2"); + let p2 = WindowsPath("C:\\3"); + let res = p1.get_relative_to(&p2); + assert_eq!(res, WindowsPath("..")); + + } + + #[test] + fn test_relative_to8() { + let p1 = PosixPath("/home/brian/Dev/rust/build/").push_rel( + &PosixPath("stage2/lib/rustc/i686-unknown-linux-gnu/lib/librustc.so")); + let p2 = PosixPath("/home/brian/Dev/rust/build/stage2/bin/..").push_rel( + &PosixPath("lib/rustc/i686-unknown-linux-gnu/lib/libstd.so")); + let res = p1.get_relative_to(&p2); + debug!("test_relative_to8: %s vs. %s", + res.to_str(), + PosixPath(".").to_str()); + assert_eq!(res, PosixPath(".")); + + let p1 = WindowsPath("C:\\home\\brian\\Dev\\rust\\build\\").push_rel( + &WindowsPath("stage2\\lib\\rustc\\i686-unknown-linux-gnu\\lib\\librustc.so")); + let p2 = WindowsPath("\\home\\brian\\Dev\\rust\\build\\stage2\\bin\\..").push_rel( + &WindowsPath("lib\\rustc\\i686-unknown-linux-gnu\\lib\\libstd.so")); + let res = p1.get_relative_to(&p2); + debug!("test_relative_to8: %s vs. %s", + res.to_str(), + WindowsPath(".").to_str()); + assert_eq!(res, WindowsPath(".")); + + } + } diff --git a/src/libsyntax/ast.rs b/src/libsyntax/ast.rs index 435be3c71af..17247222c3f 100644 --- a/src/libsyntax/ast.rs +++ b/src/libsyntax/ast.rs @@ -951,7 +951,11 @@ pub struct view_item { #[deriving(Clone, Eq, Encodable, Decodable, IterBytes)] pub enum view_item_ { - view_item_extern_mod(ident, ~[@MetaItem], NodeId), + // ident: name used to refer to this crate in the code + // optional @str: if present, this is a location (containing + // arbitrary characters) from which to fetch the crate sources + // For example, extern mod whatever = "github.com/mozilla/rust" + view_item_extern_mod(ident, Option<@str>, ~[@MetaItem], NodeId), view_item_use(~[@view_path]), } diff --git a/src/libsyntax/ast_util.rs b/src/libsyntax/ast_util.rs index ba167fe6714..9a8a3bc25d8 100644 --- a/src/libsyntax/ast_util.rs +++ b/src/libsyntax/ast_util.rs @@ -419,7 +419,7 @@ impl Visitor<()> for IdVisitor { fn visit_view_item(@mut self, view_item: &view_item, env: ()) { match view_item.node { - view_item_extern_mod(_, _, node_id) => { + view_item_extern_mod(_, _, _, node_id) => { (self.visit_callback)(node_id) } view_item_use(ref view_paths) => { diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index 7d6dce22fb7..ddb0e3bfa68 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -4181,8 +4181,16 @@ impl Parser { self.this_token_to_str())); } - let (sort, ident) = match *self.token { - token::IDENT(*) => (ast::named, self.parse_ident()), + let (sort, maybe_path, ident) = match *self.token { + token::IDENT(*) => { + let the_ident = self.parse_ident(); + let path = if *self.token == token::EQ { + self.bump(); + Some(self.parse_str()) + } + else { None }; + (ast::named, path, the_ident) + } _ => { if must_be_named_mod { self.span_fatal(*self.span, @@ -4191,7 +4199,7 @@ impl Parser { self.this_token_to_str())); } - (ast::anonymous, + (ast::anonymous, None, special_idents::clownshoes_foreign_mod) } }; @@ -4230,7 +4238,7 @@ impl Parser { let metadata = self.parse_optional_meta(); self.expect(&token::SEMI); iovi_view_item(ast::view_item { - node: view_item_extern_mod(ident, metadata, self.get_id()), + node: view_item_extern_mod(ident, maybe_path, metadata, self.get_id()), attrs: attrs, vis: visibility, span: mk_sp(lo, self.last_span.hi) @@ -4812,8 +4820,13 @@ impl Parser { } else if self.eat_keyword(keywords::Extern) { self.expect_keyword(keywords::Mod); let ident = self.parse_ident(); + let path = if *self.token == token::EQ { + self.bump(); + Some(self.parse_str()) + } + else { None }; let metadata = self.parse_optional_meta(); - view_item_extern_mod(ident, metadata, self.get_id()) + view_item_extern_mod(ident, path, metadata, self.get_id()) } else { self.bug("expected view item"); }; diff --git a/src/libsyntax/print/pprust.rs b/src/libsyntax/print/pprust.rs index f517179f603..ffe9575a864 100644 --- a/src/libsyntax/print/pprust.rs +++ b/src/libsyntax/print/pprust.rs @@ -1856,9 +1856,13 @@ pub fn print_view_item(s: @ps, item: &ast::view_item) { print_outer_attributes(s, item.attrs); print_visibility(s, item.vis); match item.node { - ast::view_item_extern_mod(id, ref mta, _) => { + ast::view_item_extern_mod(id, ref optional_path, ref mta, _) => { head(s, "extern mod"); print_ident(s, id); + for p in optional_path.iter() { + word(s.s, "="); + print_string(s, *p); + } if !mta.is_empty() { popen(s); commasep(s, consistent, *mta, |p, &i| print_meta_item(p, i)); diff --git a/src/test/run-pass/extern-mod-url.rs b/src/test/run-pass/extern-mod-url.rs deleted file mode 100644 index 363c54f6812..00000000000 --- a/src/test/run-pass/extern-mod-url.rs +++ /dev/null @@ -1,16 +0,0 @@ -// 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 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -// Just a test that new-style extern mods parse - -// xfail-test FIXME #6407 -extern mod test = "github.com/catamorphism/test-pkg"; - -fn main() {}