Skip to content

Commit

Permalink
std/rustc/rustpkg/syntax: Support the extern mod = ... form
Browse files Browse the repository at this point in the history
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
  • Loading branch information
catamorphism committed Aug 9, 2013
1 parent e751c90 commit 96fd606
Show file tree
Hide file tree
Showing 29 changed files with 821 additions and 604 deletions.
30 changes: 22 additions & 8 deletions doc/rust.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 ;
~~~~~~~~
Expand All @@ -755,20 +755,34 @@ 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");
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
Expand Down
2 changes: 1 addition & 1 deletion mk/tests.mk
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
23 changes: 19 additions & 4 deletions src/librustc/back/link.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -497,35 +497,40 @@ pub fn build_link_meta(sess: Session,
struct ProvidedMetas {
name: Option<@str>,
vers: Option<@str>,
pkg_id: Option<@str>,
cmh_items: ~[@ast::MetaItem]
}

fn provided_link_metas(sess: Session, c: &ast::Crate) ->
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);
for meta in linkage_metas.iter() {
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)
}
}

ProvidedMetas {
name: name,
vers: vers,
pkg_id: pkg_id,
cmh_items: cmh_items
}
}

// 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)
}
Expand Down Expand Up @@ -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();
}

Expand Down Expand Up @@ -605,18 +613,20 @@ 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);
let vers = crate_meta_vers(sess, opt_vers);
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
}
}
Expand Down Expand Up @@ -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); }
Expand Down
117 changes: 4 additions & 113 deletions src/librustc/back/rpath.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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] {
Expand Down Expand Up @@ -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]
Expand Down Expand Up @@ -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,
Expand All @@ -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"),
Expand Down
2 changes: 1 addition & 1 deletion src/librustc/front/std_inject.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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()))
Expand Down
2 changes: 1 addition & 1 deletion src/librustc/front/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
2 changes: 2 additions & 0 deletions src/librustc/metadata/common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Loading

0 comments on commit 96fd606

Please sign in to comment.