Skip to content

Implement extern mod a = "b/c/d"; #8176

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
4 changes: 3 additions & 1 deletion mk/tests.mk
Original file line number Diff line number Diff line change
Expand Up @@ -348,7 +348,9 @@ $(3)/stage$(1)/test/rustpkgtest-$(2)$$(X_$(2)): \
$$(RUSTPKG_LIB) $$(RUSTPKG_INPUTS) \
$$(SREQ$(1)_T_$(2)_H_$(3)) \
$$(TLIB$(1)_T_$(2)_H_$(3))/$$(CFG_LIBSYNTAX_$(2)) \
$$(TLIB$(1)_T_$(2)_H_$(3))/$$(CFG_LIBRUSTC_$(2))
$$(TLIB$(1)_T_$(2)_H_$(3))/$$(CFG_LIBRUSTC_$(2)) \
$$(TBIN$(1)_T_$(2)_H_$(3))/rustpkg$$(X_$(2)) \
$$(TBIN$(1)_T_$(2)_H_$(3))/rustc$$(X_$(2))
@$$(call E, compile_and_link: $$@)
$$(STAGE$(1)_T_$(2)_H_$(3)) -o $$@ $$< --test

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