From c3825c835197a97ff4f254802efde80335d3833b Mon Sep 17 00:00:00 2001 From: Steven Fackler Date: Wed, 7 Aug 2013 00:50:23 -0400 Subject: env! syntax extension changes env! aborts compilation of the specified environment variable is not defined and takes an optional second argument containing a custom error message. option_env! creates an Option<&'static str> containing the value of the environment variable. There are no run-pass tests that check the behavior when the environment variable is defined since the test framework doesn't support setting environment variables at compile time as opposed to runtime. However, both env! and option_env! are used inside of rustc itself, which should act as a sufficient test. Close #2248 --- src/libsyntax/ext/asm.rs | 4 ++-- src/libsyntax/ext/base.rs | 14 ++++++++------ src/libsyntax/ext/env.rs | 32 +++++++++++++++++++++++++------- src/libsyntax/ext/fmt.rs | 4 ++-- 4 files changed, 37 insertions(+), 17 deletions(-) (limited to 'src/libsyntax') diff --git a/src/libsyntax/ext/asm.rs b/src/libsyntax/ext/asm.rs index ee0ec664e1b..b5d97427baf 100644 --- a/src/libsyntax/ext/asm.rs +++ b/src/libsyntax/ext/asm.rs @@ -1,4 +1,4 @@ -// Copyright 2012 The Rust Project Developers. See the COPYRIGHT +// Copyright 2012-2013 The Rust Project Developers. See the COPYRIGHT // file at the top-level directory of this distribution and at // http://rust-lang.org/COPYRIGHT. // @@ -59,7 +59,7 @@ pub fn expand_asm(cx: @ExtCtxt, sp: span, tts: &[ast::token_tree]) match state { Asm => { asm = expr_to_str(cx, p.parse_expr(), - ~"inline assembly must be a string literal."); + "inline assembly must be a string literal."); } Outputs => { while *p.token != token::EOF && diff --git a/src/libsyntax/ext/base.rs b/src/libsyntax/ext/base.rs index efaf6b8e001..1ba32442d5e 100644 --- a/src/libsyntax/ext/base.rs +++ b/src/libsyntax/ext/base.rs @@ -146,7 +146,9 @@ pub fn syntax_expander_table() -> SyntaxEnv { intern(&"auto_decode"), @SE(ItemDecorator(ext::auto_encode::expand_auto_decode))); syntax_expanders.insert(intern(&"env"), - builtin_normal_tt(ext::env::expand_syntax_ext)); + builtin_normal_tt(ext::env::expand_env)); + syntax_expanders.insert(intern(&"option_env"), + builtin_normal_tt(ext::env::expand_option_env)); syntax_expanders.insert(intern("bytes"), builtin_normal_tt(ext::bytes::expand_syntax_ext)); syntax_expanders.insert(intern("concat_idents"), @@ -311,7 +313,7 @@ impl ExtCtxt { } } -pub fn expr_to_str(cx: @ExtCtxt, expr: @ast::expr, err_msg: ~str) -> @str { +pub fn expr_to_str(cx: @ExtCtxt, expr: @ast::expr, err_msg: &str) -> @str { match expr.node { ast::expr_lit(l) => match l.node { ast::lit_str(s) => s, @@ -536,8 +538,8 @@ mod test { a.insert (@"abc",@15); let m = MapChain::new(~a); m.insert (@"def",@16); - // FIXME: #4492 (ICE) assert_eq!(m.find(&@"abc"),Some(@15)); - // .... assert_eq!(m.find(&@"def"),Some(@16)); + assert_eq!(m.find(&@"abc"),Some(@15)); + assert_eq!(m.find(&@"def"),Some(@16)); assert_eq!(*(m.find(&@"abc").unwrap()),15); assert_eq!(*(m.find(&@"def").unwrap()),16); let n = m.push_frame(); @@ -549,8 +551,8 @@ mod test { assert_eq!(*(n.find(&@"abc").unwrap()),15); assert_eq!(*(n.find(&@"def").unwrap()),17); // ... but m still has the old ones - // FIXME: #4492: assert_eq!(m.find(&@"abc"),Some(@15)); - // FIXME: #4492: assert_eq!(m.find(&@"def"),Some(@16)); + assert_eq!(m.find(&@"abc"),Some(@15)); + assert_eq!(m.find(&@"def"),Some(@16)); assert_eq!(*(m.find(&@"abc").unwrap()),15); assert_eq!(*(m.find(&@"def").unwrap()),16); } diff --git a/src/libsyntax/ext/env.rs b/src/libsyntax/ext/env.rs index a6cb6155878..c9e01b0f0d5 100644 --- a/src/libsyntax/ext/env.rs +++ b/src/libsyntax/ext/env.rs @@ -1,4 +1,4 @@ -// Copyright 2012 The Rust Project Developers. See the COPYRIGHT +// Copyright 2012-2013 The Rust Project Developers. See the COPYRIGHT // file at the top-level directory of this distribution and at // http://rust-lang.org/COPYRIGHT. // @@ -22,17 +22,35 @@ use ext::build::AstBuilder; use std::os; -pub fn expand_syntax_ext(cx: @ExtCtxt, sp: span, tts: &[ast::token_tree]) +pub fn expand_option_env(ext_cx: @ExtCtxt, sp: span, tts: &[ast::token_tree]) -> base::MacResult { + let var = get_single_str_from_tts(ext_cx, sp, tts, "option_env!"); - let var = get_single_str_from_tts(cx, sp, tts, "env!"); + let e = match os::getenv(var) { + None => quote_expr!(::std::option::None), + Some(s) => quote_expr!(::std::option::Some($s)) + }; + MRExpr(e) +} - // FIXME (#2248): if this was more thorough it would manufacture an - // Option rather than just an maybe-empty string. +pub fn expand_env(ext_cx: @ExtCtxt, sp: span, tts: &[ast::token_tree]) + -> base::MacResult { + let exprs = get_exprs_from_tts(ext_cx, sp, tts); + + if exprs.len() == 0 { + ext_cx.span_fatal(sp, "env! takes 1 or 2 arguments"); + } + + let var = expr_to_str(ext_cx, exprs[0], "expected string literal"); + let msg = match exprs.len() { + 1 => fmt!("Environment variable %s not defined", var).to_managed(), + 2 => expr_to_str(ext_cx, exprs[1], "expected string literal"), + _ => ext_cx.span_fatal(sp, "env! takes 1 or 2 arguments") + }; let e = match os::getenv(var) { - None => cx.expr_str(sp, @""), - Some(s) => cx.expr_str(sp, s.to_managed()) + None => ext_cx.span_fatal(sp, msg), + Some(s) => ext_cx.expr_str(sp, s.to_managed()) }; MRExpr(e) } diff --git a/src/libsyntax/ext/fmt.rs b/src/libsyntax/ext/fmt.rs index 2dbf6887a21..008545c9729 100644 --- a/src/libsyntax/ext/fmt.rs +++ b/src/libsyntax/ext/fmt.rs @@ -1,4 +1,4 @@ -// Copyright 2012 The Rust Project Developers. See the COPYRIGHT +// Copyright 2012-2013 The Rust Project Developers. See the COPYRIGHT // file at the top-level directory of this distribution and at // http://rust-lang.org/COPYRIGHT. // @@ -32,7 +32,7 @@ pub fn expand_syntax_ext(cx: @ExtCtxt, sp: span, tts: &[ast::token_tree]) } let fmt = expr_to_str(cx, args[0], - ~"first argument to fmt! must be a string literal."); + "first argument to fmt! must be a string literal."); let fmtspan = args[0].span; debug!("Format string: %s", fmt); fn parse_fmt_err_(cx: @ExtCtxt, sp: span, msg: &str) -> ! { -- cgit 1.4.1-3-g733a5 From 96fd606dddba6bd4773c41be66c44fc076a96ff8 Mon Sep 17 00:00:00 2001 From: Tim Chevalier Date: Wed, 31 Jul 2013 13:47:32 -0700 Subject: std/rustc/rustpkg/syntax: Support the `extern mod = ...` form This commit allows you to write: extern mod x = "a/b/c"; which means rustc will search in the RUST_PATH for a package with ID a/b/c, and bind it to the name `x` if it's found. Incidentally, move get_relative_to from back::rpath into std::path --- doc/rust.md | 30 +++- mk/tests.mk | 2 +- src/librustc/back/link.rs | 23 ++- src/librustc/back/rpath.rs | 117 +-------------- src/librustc/front/std_inject.rs | 2 +- src/librustc/front/test.rs | 2 +- src/librustc/metadata/common.rs | 2 + src/librustc/metadata/creader.rs | 45 ++++-- src/librustc/metadata/filesearch.rs | 143 ++++++++++-------- src/librustc/metadata/loader.rs | 78 ++++++---- src/librustc/middle/resolve.rs | 5 +- src/librustpkg/api.rs | 24 ++- src/librustpkg/context.rs | 24 +++ src/librustpkg/installed_packages.rs | 42 +++++- src/librustpkg/package_id.rs | 9 +- src/librustpkg/package_source.rs | 21 ++- src/librustpkg/path_util.rs | 103 ++++--------- src/librustpkg/rustpkg.rs | 76 ++++------ src/librustpkg/source_control.rs | 37 ++++- src/librustpkg/tests.rs | 283 ++++++++++++++++++++--------------- src/librustpkg/util.rs | 106 +++++++------ src/librustpkg/version.rs | 27 ++-- src/librustpkg/workspace.rs | 6 +- src/libstd/path.rs | 165 +++++++++++++++++++- src/libsyntax/ast.rs | 6 +- src/libsyntax/ast_util.rs | 2 +- src/libsyntax/parse/parser.rs | 23 ++- src/libsyntax/print/pprust.rs | 6 +- src/test/run-pass/extern-mod-url.rs | 16 -- 29 files changed, 821 insertions(+), 604 deletions(-) delete mode 100644 src/test/run-pass/extern-mod-url.rs (limited to 'src/libsyntax') 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`. - -Three examples of `extern mod` declarations: +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`. + +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 45a6e1447fc..536a153b222 100644 --- a/mk/tests.mk +++ b/mk/tests.mk @@ -348,7 +348,7 @@ $(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)) @$$(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 571721ed40c..23129547562 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] { @@ -199,8 +161,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] @@ -244,78 +205,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, @@ -335,7 +227,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 2422be3960b..2311ef06961 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); } -} - -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"); - } + 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); } - rslt } -} - -fn get_rustpkg_lib_path() -> Result { - do get_rustpkg_root().chain |p| { - result::Ok(p.push(libdir())) + 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); + } } + env_rust_path } -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 da0ba1558c9..6ffe7c17ff6 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..233e975cbb7 100644 --- a/src/librustpkg/package_id.rs +++ b/src/librustpkg/package_id.rs @@ -45,7 +45,7 @@ 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; @@ -76,7 +76,7 @@ impl PkgId { 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(&*local_path) { Some(v) => v, None => match try_getting_version(&remote_path) { Some(v) => v, @@ -103,6 +103,11 @@ impl PkgId { 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.local_path.to_str() + } } impl ToStr for PkgId { diff --git a/src/librustpkg/package_source.rs b/src/librustpkg/package_source.rs index bbe35ee5004..b14f95cfae5 100644 --- a/src/librustpkg/package_source.rs +++ b/src/librustpkg/package_source.rs @@ -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: ~[], @@ -202,7 +199,6 @@ impl PkgSrc { fn build_crates(&self, ctx: &Ctx, - dst_dir: &Path, src_dir: &Path, crates: &[Crate], cfgs: &[~str], @@ -210,12 +206,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 +226,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 1c54b630421..3eff260e79a 100644 --- a/src/librustpkg/path_util.rs +++ b/src/librustpkg/path_util.rs @@ -14,61 +14,14 @@ 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 +52,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 diff --git a/src/librustpkg/rustpkg.rs b/src/librustpkg/rustpkg.rs index fa03a5bbfc2..bac31478bde 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; @@ -138,35 +139,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 +199,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 +222,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 +248,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); } @@ -294,7 +287,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; @@ -332,8 +325,6 @@ impl CtxMethods for Ctx { in_rust_path(workspace), is_git_dir(&workspace.push_rel(&*pkgid.local_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 @@ -351,7 +342,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 +376,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 +435,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 +510,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..98fd843925d 100644 --- a/src/librustpkg/tests.rs +++ b/src/librustpkg/tests.rs @@ -24,7 +24,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 @@ -116,6 +118,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 +143,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 +159,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 +195,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 +223,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,8 +247,9 @@ 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 } @@ -329,24 +314,24 @@ fn create_local_package_with_custom_build_hook(pkgid: &PkgId, fn assert_lib_exists(repo: &Path, short_name: &str, v: 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)); @@ -563,18 +548,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()); + 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 +568,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 +591,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 +640,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 +680,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 +695,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 +747,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) })); } @@ -777,8 +761,7 @@ fn rust_path_test() { 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 +813,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 +869,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); @@ -908,7 +890,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 +904,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 +917,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 +930,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 +944,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 +955,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 +965,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 +974,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 +982,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); @@ -1031,3 +1013,62 @@ fn test_non_numeric_tag() { 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..a1b318fcc95 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.local_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.local_path.to_str()).to_managed(), + Bench => fmt!("%sbench", pkg_id.local_path.to_str()).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.local_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..b5203ef0756 100644 --- a/src/librustpkg/version.rs +++ b/src/librustpkg/version.rs @@ -17,6 +17,7 @@ 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 +93,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,8 +116,13 @@ 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, diff --git a/src/librustpkg/workspace.rs b/src/librustpkg/workspace.rs index d877b4ff489..2efe274a4a2 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 @@ -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 4902c4587ac..5240edf00f1 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -4169,8 +4169,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, @@ -4179,7 +4187,7 @@ impl Parser { self.this_token_to_str())); } - (ast::anonymous, + (ast::anonymous, None, special_idents::clownshoes_foreign_mod) } }; @@ -4218,7 +4226,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) @@ -4800,8 +4808,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() {} -- cgit 1.4.1-3-g733a5