From cc3c8bbfaf5af19caf3deb131a995a65ca4674f9 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Tue, 1 Jul 2014 08:37:54 -0700 Subject: [PATCH] rustc: Add a flag for specifying dependencies This comit implements a new flag, --extern, which is used to specify where a crate is located. The purpose of this flag is to bypass the normal crate loading/matching of the compiler to point it directly at the right file. This flag takes the form `--extern foo=bar` where `foo` is the name of a crate and `bar` is the location at which to find the crate. Multiple `--extern` directives are allowed with the same crate name to specify the rlib/dylib pair for a crate. It is invalid to specify more than one rlib or more than one dylib, and it's required that the crates are valid rust crates. I have also added some extensive documentation to metadata::loader about how crate loading should work. RFC: 0035-remove-crate-id --- src/librustc/back/link.rs | 5 +- src/librustc/back/svh.rs | 8 +- src/librustc/driver/config.rs | 26 +- src/librustc/metadata/creader.rs | 32 +- src/librustc/metadata/loader.rs | 284 +++++++++++++++++- src/librustc/middle/trans/base.rs | 2 +- .../extern-flag-disambiguates/Makefile | 24 ++ .../run-make/extern-flag-disambiguates/a.rs | 6 + .../run-make/extern-flag-disambiguates/b.rs | 9 + .../run-make/extern-flag-disambiguates/c.rs | 9 + .../run-make/extern-flag-disambiguates/d.rs | 11 + src/test/run-make/extern-flag-fun/Makefile | 16 + src/test/run-make/extern-flag-fun/bar.rs | 0 src/test/run-make/extern-flag-fun/foo.rs | 3 + .../metadata-flag-frobs-symbols/Makefile | 10 + .../metadata-flag-frobs-symbols/bar.rs | 8 + .../metadata-flag-frobs-symbols/foo.rs | 6 + 17 files changed, 444 insertions(+), 15 deletions(-) create mode 100644 src/test/run-make/extern-flag-disambiguates/Makefile create mode 100644 src/test/run-make/extern-flag-disambiguates/a.rs create mode 100644 src/test/run-make/extern-flag-disambiguates/b.rs create mode 100644 src/test/run-make/extern-flag-disambiguates/c.rs create mode 100644 src/test/run-make/extern-flag-disambiguates/d.rs create mode 100644 src/test/run-make/extern-flag-fun/Makefile create mode 100644 src/test/run-make/extern-flag-fun/bar.rs create mode 100644 src/test/run-make/extern-flag-fun/foo.rs create mode 100644 src/test/run-make/metadata-flag-frobs-symbols/Makefile create mode 100644 src/test/run-make/metadata-flag-frobs-symbols/bar.rs create mode 100644 src/test/run-make/metadata-flag-frobs-symbols/foo.rs diff --git a/src/librustc/back/link.rs b/src/librustc/back/link.rs index 1ad8b0b3698f..497fcab44e1b 100644 --- a/src/librustc/back/link.rs +++ b/src/librustc/back/link.rs @@ -588,10 +588,11 @@ pub fn find_crate_name(sess: Option<&Session>, }), None) } -pub fn build_link_meta(krate: &ast::Crate, name: String) -> LinkMeta { +pub fn build_link_meta(sess: &Session, krate: &ast::Crate, + name: String) -> LinkMeta { let r = LinkMeta { crate_name: name, - crate_hash: Svh::calculate(krate), + crate_hash: Svh::calculate(sess, krate), }; info!("{}", r); return r; diff --git a/src/librustc/back/svh.rs b/src/librustc/back/svh.rs index c487f674c2d5..24b9ff970aa2 100644 --- a/src/librustc/back/svh.rs +++ b/src/librustc/back/svh.rs @@ -53,6 +53,8 @@ use std::iter::range_step; use syntax::ast; use syntax::visit; +use driver::session::Session; + #[deriving(Clone, PartialEq)] pub struct Svh { hash: String, @@ -68,7 +70,7 @@ impl Svh { self.hash.as_slice() } - pub fn calculate(krate: &ast::Crate) -> Svh { + pub fn calculate(sess: &Session, krate: &ast::Crate) -> Svh { // FIXME (#14132): This is better than it used to be, but it still not // ideal. We now attempt to hash only the relevant portions of the // Crate AST as well as the top-level crate attributes. (However, @@ -80,6 +82,10 @@ impl Svh { // avoid collisions. let mut state = SipState::new(); + for data in sess.opts.cg.metadata.iter() { + data.hash(&mut state); + } + { let mut visit = svh_visitor::make(&mut state); visit::walk_crate(&mut visit, krate, ()); diff --git a/src/librustc/driver/config.rs b/src/librustc/driver/config.rs index c7f8b4a62ee4..728757358649 100644 --- a/src/librustc/driver/config.rs +++ b/src/librustc/driver/config.rs @@ -30,7 +30,7 @@ use syntax::diagnostic::{ColorConfig, Auto, Always, Never}; use syntax::parse; use syntax::parse::token::InternedString; -use std::collections::HashSet; +use std::collections::{HashSet, HashMap}; use getopts::{optopt, optmulti, optflag, optflagopt}; use getopts; use lib::llvm::llvm; @@ -95,6 +95,7 @@ pub struct Options { pub print_metas: (bool, bool), pub cg: CodegenOptions, pub color: ColorConfig, + pub externs: HashMap>, } /// Some reasonable defaults @@ -120,6 +121,7 @@ pub fn basic_options() -> Options { print_metas: (false, false), cg: basic_codegen_options(), color: Auto, + externs: HashMap::new(), } } @@ -551,7 +553,9 @@ pub fn optgroups() -> Vec { optopt("", "color", "Configure coloring of output: auto = colorize, if output goes to a tty (default); always = always colorize output; - never = never colorize output", "auto|always|never") + never = never colorize output", "auto|always|never"), + optmulti("", "extern", "Specify where an external rust library is located", + "PATH"), ) } @@ -730,6 +734,21 @@ pub fn build_session_options(matches: &getopts::Matches) -> Options { } }; + let mut externs = HashMap::new(); + for arg in matches.opt_strs("extern").iter() { + let mut parts = arg.as_slice().splitn('=', 1); + let name = match parts.next() { + Some(s) => s, + None => early_error("--extern value must not be empty"), + }; + let location = match parts.next() { + Some(s) => s, + None => early_error("--extern value must be of the format `foo=bar`"), + }; + let locs = externs.find_or_insert(name.to_string(), Vec::new()); + locs.push(location.to_string()); + } + Options { crate_types: crate_types, gc: gc, @@ -750,7 +769,8 @@ pub fn build_session_options(matches: &getopts::Matches) -> Options { write_dependency_info: write_dependency_info, print_metas: print_metas, cg: cg, - color: color + color: color, + externs: externs, } } diff --git a/src/librustc/metadata/creader.rs b/src/librustc/metadata/creader.rs index 62178e9cd286..7d3d67df6d15 100644 --- a/src/librustc/metadata/creader.rs +++ b/src/librustc/metadata/creader.rs @@ -280,12 +280,32 @@ fn existing_match(e: &Env, name: &str, hash: Option<&Svh>) -> Option { let mut ret = None; e.sess.cstore.iter_crate_data(|cnum, data| { - if data.name().as_slice() == name { - let other_hash = data.hash(); - match hash { - Some(hash) if *hash != other_hash => {} - Some(..) | None => { ret = Some(cnum); } + if data.name().as_slice() != name { return } + + match hash { + Some(hash) if *hash == data.hash() => { ret = Some(cnum); return } + Some(..) => return, + None => {} + } + + // When the hash is None we're dealing with a top-level dependency in + // which case we may have a specification on the command line for this + // library. Even though an upstream library may have loaded something of + // the same name, we have to make sure it was loaded from the exact same + // location as well. + let source = e.sess.cstore.get_used_crate_source(cnum).unwrap(); + let dylib = source.dylib.as_ref().map(|p| p.as_vec()); + let rlib = source.rlib.as_ref().map(|p| p.as_vec()); + match e.sess.opts.externs.find_equiv(&name) { + Some(locs) => { + let found = locs.iter().any(|l| { + Some(l.as_bytes()) == dylib || Some(l.as_bytes()) == rlib + }); + if found { + ret = Some(cnum); + } } + None => ret = Some(cnum), } }); return ret; @@ -361,6 +381,7 @@ fn resolve_crate<'a>(e: &mut Env, root: root, rejected_via_hash: vec!(), rejected_via_triple: vec!(), + should_match_name: true, }; let library = load_ctxt.load_library_crate(); register_crate(e, root, ident, name, span, library) @@ -422,6 +443,7 @@ impl<'a> PluginMetadataReader<'a> { root: &None, rejected_via_hash: vec!(), rejected_via_triple: vec!(), + should_match_name: true, }; let library = match load_ctxt.maybe_load_library_crate() { Some (l) => l, diff --git a/src/librustc/metadata/loader.rs b/src/librustc/metadata/loader.rs index 8b5b450eb467..1b2349a271ed 100644 --- a/src/librustc/metadata/loader.rs +++ b/src/librustc/metadata/loader.rs @@ -9,6 +9,208 @@ // except according to those terms. //! Finds crate binaries and loads their metadata +//! +//! Might I be the first to welcome you to a world of platform differences, +//! version requirements, dependency graphs, conficting desires, and fun! This +//! is the major guts (along with metadata::creader) of the compiler for loading +//! crates and resolving dependencies. Let's take a tour! +//! +//! # The problem +//! +//! Each invocation of the compiler is immediately concerned with one primary +//! problem, to connect a set of crates to resolved crates on the filesystem. +//! Concretely speaking, the compiler follows roughly these steps to get here: +//! +//! 1. Discover a set of `extern crate` statements. +//! 2. Transform these directives into crate names. If the directive does not +//! have an explicit name, then the identifier is the name. +//! 3. For each of these crate names, find a corresponding crate on the +//! filesystem. +//! +//! Sounds easy, right? Let's walk into some of the nuances. +//! +//! ## Transitive Dependencies +//! +//! Let's say we've got three crates: A, B, and C. A depends on B, and B depends +//! on C. When we're compiling A, we primarily need to find and locate B, but we +//! also end up needing to find and locate C as well. +//! +//! The reason for this is that any of B's types could be composed of C's types, +//! any function in B could return a type from C, etc. To be able to guarantee +//! that we can always typecheck/translate any function, we have to have +//! complete knowledge of the whole ecosystem, not just our immediate +//! dependencies. +//! +//! So now as part of the "find a corresponding crate on the filesystem" step +//! above, this involves also finding all crates for *all upstream +//! dependencies*. This includes all dependencies transitively. +//! +//! ## Rlibs and Dylibs +//! +//! The compiler has two forms of intermediate dependencies. These are dubbed +//! rlibs and dylibs for the static and dynamic variants, respectively. An rlib +//! is a rustc-defined file format (currently just an ar archive) while a dylib +//! is a platform-defined dynamic library. Each library has a metadata somewhere +//! inside of it. +//! +//! When translating a crate name to a crate on the filesystem, we all of a +//! sudden need to take into account both rlibs and dylibs! Linkage later on may +//! use either one of these files, as each has their pros/cons. The job of crate +//! loading is to discover what's possible by finding all candidates. +//! +//! Most parts of this loading systems keep the dylib/rlib as just separate +//! variables. +//! +//! ## Where to look? +//! +//! We can't exactly scan your whole hard drive when looking for dependencies, +//! so we need to places to look. Currently the compiler will implicitly add the +//! target lib search path ($prefix/lib/rustlib/$target/lib) to any compilation, +//! and otherwise all -L flags are added to the search paths. +//! +//! ## What criterion to select on? +//! +//! This a pretty tricky area of loading crates. Given a file, how do we know +//! whether it's the right crate? Currently, the rules look along these lines: +//! +//! 1. Does the filename match an rlib/dylib pattern? That is to say, does the +//! filename have the right prefix/suffix? +//! 2. Does the filename have the right prefix for the crate name being queried? +//! This is filtering for files like `libfoo*.rlib` and such. +//! 3. Is the file an actual rust library? This is done by loading the metadata +//! from the library and making sure it's actually there. +//! 4. Does the name in the metadata agree with the name of the library? +//! 5. Does the target in the metadata agree with the current target? +//! 6. Does the SVH match? (more on this later) +//! +//! If the file answeres `yes` to all these questions, then the file is +//! considered as being *candidate* for being accepted. It is illegal to have +//! more than two candidates as the compiler has no method by which to resolve +//! this conflict. Additionally, rlib/dylib candidates are considered +//! separately. +//! +//! After all this has happened, we have 1 or two files as candidates. These +//! represent the rlib/dylib file found for a library, and they're returned as +//! being found. +//! +//! ### What about versions? +//! +//! A lot of effort has been put forth to remove versioning from the compiler. +//! There have been forays in the past to have versioning baked in, but it was +//! largely always deemed insufficient to the point that it was recognized that +//! it's probably something the compiler shouldn't do anyway due to its +//! complicated nature and the state of the half-baked solutions. +//! +//! With a departure from versioning, the primary criterion for loading crates +//! is just the name of a crate. If we stopped here, it would imply that you +//! could never link two crates of the same name from different sources +//! together, which is clearly a bad state to be in. +//! +//! To resolve this problem, we come to the next section! +//! +//! # Expert Mode +//! +//! A number of flags have been added to the compiler to solve the "version +//! problem" in the previous section, as well as generally enabling more +//! powerful usage of the crate loading system of the compiler. The goal of +//! these flags and options are to enable third-party tools to drive the +//! compiler with prior knowledge about how the world should look. +//! +//! ## The `--extern` flag +//! +//! The compiler accepts a flag of this form a number of times: +//! +//! ```notrust +//! --extern crate-name=path/to/the/crate.rlib +//! ``` +//! +//! This flag is basically the following letter to the compiler: +//! +//! > Dear rustc, +//! > +//! > When you are attempting to load the immediate dependency `crate-name`, I +//! > would like you too assume that the library is located at +//! > `path/to/the/crate.rlib`, and look nowhere else. Also, please do not +//! > assume that the path I specified has the name `crate-name`. +//! +//! This flag basically overrides most matching logic except for validating that +//! the file is indeed a rust library. The same `crate-name` can be specified +//! twice to specify the rlib/dylib pair. +//! +//! ## Enabling "multiple versions" +//! +//! This basically boils down to the ability to specify arbitrary packages to +//! the compiler. For example, if crate A wanted to use Bv1 and Bv2, then it +//! would look something like: +//! +//! ```ignore +//! extern crate b1; +//! extern crate b2; +//! +//! fn main() {} +//! ``` +//! +//! and the compiler would be invoked as: +//! +//! ```notrust +//! rustc a.rs --extern b1=path/to/libb1.rlib --extern b2=path/to/libb2.rlib +//! ``` +//! +//! In this scenario there are two crates named `b` and the compiler must be +//! manually driven to be informed where each crate is. +//! +//! ## Frobbing symbols +//! +//! One of the immediate problems with linking the same library together twice +//! in the same problem is dealing with duplicate symbols. The primary way to +//! deal with this in rustc is to add hashes to the end of each symbol. +//! +//! In order to force hashes to change between versions of a library, if +//! desired, the compiler exposes an option `-C metadata=foo`, which is used to +//! initially seed each symbol hash. The string `foo` is prepended to each +//! string-to-hash to ensure that symbols change over time. +//! +//! ## Loading transitive dependencies +//! +//! Dealing with same-named-but-distinct crates is not just a local problem, but +//! one that also needs to be dealt with for transitive dependences. Note that +//! in the letter above `--extern` flags only apply to the *local* set of +//! dependencies, not the upstream transitive dependencies. Consider this +//! dependency graph: +//! +//! ```notrust +//! A.1 A.2 +//! | | +//! | | +//! B C +//! \ / +//! \ / +//! D +//! ``` +//! +//! In this scenario, when we compile `D`, we need to be able to distinctly +//! resolve `A.1` and `A.2`, but an `--extern` flag cannot apply to these +//! transitive dependencies. +//! +//! Note that the key idea here is that `B` and `C` are both *already compiled*. +//! That is, they have already resolved their dependencies. Due to unrelated +//! technical reasons, when a library is compiled, it is only compatible with +//! the *exact same* version of the upstream libraries it was compiled against. +//! We use the "Strict Version Hash" to identify the exact copy of an upstream +//! library. +//! +//! With this knowledge, we know that `B` and `C` will depend on `A` with +//! different SVH values, so we crawl the normal `-L` paths looking for +//! `liba*.rlib` and filter based on the contained SVH. +//! +//! In the end, this ends up not needing `--extern` to specify upstream +//! transitive dependencies. +//! +//! # Wrapping up +//! +//! That's the general overview of loading crates in the compiler, but it's by +//! no means all of the necessary details. Take a look at the rest of +//! metadata::loader or metadata::creader for all the juicy details! use back::archive::{ArchiveRO, METADATA_FILENAME}; use back::svh::Svh; @@ -67,6 +269,7 @@ pub struct Context<'a> { pub root: &'a Option, pub rejected_via_hash: Vec, pub rejected_via_triple: Vec, + pub should_match_name: bool, } pub struct Library { @@ -164,6 +367,17 @@ impl<'a> Context<'a> { } fn find_library_crate(&mut self) -> Option { + // If an SVH is specified, then this is a transitive dependency that + // must be loaded via -L plus some filtering. + if self.hash.is_none() { + self.should_match_name = false; + match self.find_commandline_library() { + Some(l) => return Some(l), + None => {} + } + self.should_match_name = true; + } + let dypair = self.dylibname(); // want: crate_name.dir_part() + prefix + crate_name.file_part + "-" @@ -348,9 +562,11 @@ impl<'a> Context<'a> { } fn crate_matches(&mut self, crate_data: &[u8], libpath: &Path) -> bool { - match decoder::maybe_get_crate_name(crate_data) { - Some(ref name) if self.crate_name == name.as_slice() => {} - _ => { info!("Rejecting via crate name"); return false } + if self.should_match_name { + match decoder::maybe_get_crate_name(crate_data) { + Some(ref name) if self.crate_name == name.as_slice() => {} + _ => { info!("Rejecting via crate name"); return false } + } } let hash = match decoder::maybe_get_crate_hash(crate_data) { Some(hash) => hash, None => { @@ -403,6 +619,68 @@ impl<'a> Context<'a> { } } + fn find_commandline_library(&mut self) -> Option { + let locs = match self.sess.opts.externs.find_equiv(&self.crate_name) { + Some(s) => s, + None => return None, + }; + + // First, filter out all libraries that look suspicious. We only accept + // files which actually exist that have the correct naming scheme for + // rlibs/dylibs. + let sess = self.sess; + let dylibname = self.dylibname(); + let mut locs = locs.iter().map(|l| Path::new(l.as_slice())).filter(|loc| { + if !loc.exists() { + sess.err(format!("extern location does not exist: {}", + loc.display()).as_slice()); + return false; + } + let file = loc.filename_str().unwrap(); + if file.starts_with("lib") && file.ends_with(".rlib") { + return true + } else { + match dylibname { + Some((prefix, suffix)) => { + if file.starts_with(prefix) && file.ends_with(suffix) { + return true + } + } + None => {} + } + } + sess.err(format!("extern location is of an unknown type: {}", + loc.display()).as_slice()); + false + }); + + // Now that we have an itertor of good candidates, make sure there's at + // most one rlib and at most one dylib. + let mut rlibs = HashSet::new(); + let mut dylibs = HashSet::new(); + for loc in locs { + if loc.filename_str().unwrap().ends_with(".rlib") { + rlibs.insert(loc.clone()); + } else { + dylibs.insert(loc.clone()); + } + } + + // Extract the rlib/dylib pair. + let mut metadata = None; + let rlib = self.extract_one(rlibs, "rlib", &mut metadata); + let dylib = self.extract_one(dylibs, "dylib", &mut metadata); + + if rlib.is_none() && dylib.is_none() { return None } + match metadata { + Some(metadata) => Some(Library { + dylib: dylib, + rlib: rlib, + metadata: metadata, + }), + None => None, + } + } } pub fn note_crate_name(diag: &SpanHandler, name: &str) { diff --git a/src/librustc/middle/trans/base.rs b/src/librustc/middle/trans/base.rs index 2f9a5f4a4fcd..d371a2589ffd 100644 --- a/src/librustc/middle/trans/base.rs +++ b/src/librustc/middle/trans/base.rs @@ -2309,7 +2309,7 @@ pub fn trans_crate(krate: ast::Crate, } } - let link_meta = link::build_link_meta(&krate, name); + let link_meta = link::build_link_meta(&tcx.sess, &krate, name); // Append ".rs" to crate name as LLVM module identifier. // diff --git a/src/test/run-make/extern-flag-disambiguates/Makefile b/src/test/run-make/extern-flag-disambiguates/Makefile new file mode 100644 index 000000000000..9b86bf975495 --- /dev/null +++ b/src/test/run-make/extern-flag-disambiguates/Makefile @@ -0,0 +1,24 @@ +-include ../tools.mk + +# Attempt to build this dependency tree: +# +# A.1 A.2 +# |\ | +# | \ | +# B \ C +# \ | / +# \|/ +# D +# +# Note that A.1 and A.2 are crates with the same name. + +all: + $(RUSTC) -C metadata=1 -C extra-filename=-1 a.rs + $(RUSTC) -C metadata=2 -C extra-filename=-2 a.rs + $(RUSTC) b.rs --extern a=$(TMPDIR)/liba-1.rlib + $(RUSTC) c.rs --extern a=$(TMPDIR)/liba-2.rlib + $(RUSTC) --cfg before d.rs --extern a=$(TMPDIR)/liba-1.rlib + $(call RUN,d) + $(RUSTC) --cfg after d.rs --extern a=$(TMPDIR)/liba-1.rlib + $(call RUN,d) + diff --git a/src/test/run-make/extern-flag-disambiguates/a.rs b/src/test/run-make/extern-flag-disambiguates/a.rs new file mode 100644 index 000000000000..db1a0e0433ec --- /dev/null +++ b/src/test/run-make/extern-flag-disambiguates/a.rs @@ -0,0 +1,6 @@ +#![crate_name = "a"] +#![crate_type = "rlib"] + +static FOO: uint = 3; + +pub fn token() -> &'static uint { &FOO } diff --git a/src/test/run-make/extern-flag-disambiguates/b.rs b/src/test/run-make/extern-flag-disambiguates/b.rs new file mode 100644 index 000000000000..f4fb09631a9c --- /dev/null +++ b/src/test/run-make/extern-flag-disambiguates/b.rs @@ -0,0 +1,9 @@ +#![crate_name = "b"] +#![crate_type = "rlib"] + +extern crate a; + +static FOO: uint = 3; + +pub fn token() -> &'static uint { &FOO } +pub fn a_token() -> &'static uint { a::token() } diff --git a/src/test/run-make/extern-flag-disambiguates/c.rs b/src/test/run-make/extern-flag-disambiguates/c.rs new file mode 100644 index 000000000000..e017d747e6fe --- /dev/null +++ b/src/test/run-make/extern-flag-disambiguates/c.rs @@ -0,0 +1,9 @@ +#![crate_name = "c"] +#![crate_type = "rlib"] + +extern crate a; + +static FOO: uint = 3; + +pub fn token() -> &'static uint { &FOO } +pub fn a_token() -> &'static uint { a::token() } diff --git a/src/test/run-make/extern-flag-disambiguates/d.rs b/src/test/run-make/extern-flag-disambiguates/d.rs new file mode 100644 index 000000000000..e3c968edb6a9 --- /dev/null +++ b/src/test/run-make/extern-flag-disambiguates/d.rs @@ -0,0 +1,11 @@ +#[cfg(before)] extern crate a; +extern crate b; +extern crate c; +#[cfg(after)] extern crate a; + +fn t(a: &'static uint) -> uint { a as *const _ as uint } + +fn main() { + assert!(t(a::token()) == t(b::a_token())); + assert!(t(a::token()) != t(c::a_token())); +} diff --git a/src/test/run-make/extern-flag-fun/Makefile b/src/test/run-make/extern-flag-fun/Makefile new file mode 100644 index 000000000000..ca5aa052a7b3 --- /dev/null +++ b/src/test/run-make/extern-flag-fun/Makefile @@ -0,0 +1,16 @@ +-include ../tools.mk + +all: + $(RUSTC) bar.rs --crate-type=rlib + $(RUSTC) bar.rs --crate-type=rlib -C extra-filename=-a + $(RUSTC) foo.rs --extern hello && exit 1 || exit 0 + $(RUSTC) foo.rs --extern bar=no-exist && exit 1 || exit 0 + $(RUSTC) foo.rs --extern bar=foo.rs && exit 1 || exit 0 + $(RUSTC) foo.rs \ + --extern bar=$(TMPDIR)/libbar.rlib \ + --extern bar=$(TMPDIR)/libbar-a.rlib \ + && exit 1 || exit 0 + $(RUSTC) foo.rs \ + --extern bar=$(TMPDIR)/libbar.rlib \ + --extern bar=$(TMPDIR)/libbar.rlib + $(RUSTC) foo.rs --extern bar=$(TMPDIR)/libbar.rlib diff --git a/src/test/run-make/extern-flag-fun/bar.rs b/src/test/run-make/extern-flag-fun/bar.rs new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/src/test/run-make/extern-flag-fun/foo.rs b/src/test/run-make/extern-flag-fun/foo.rs new file mode 100644 index 000000000000..0edda7d7b884 --- /dev/null +++ b/src/test/run-make/extern-flag-fun/foo.rs @@ -0,0 +1,3 @@ +extern crate bar; + +fn main() {} diff --git a/src/test/run-make/metadata-flag-frobs-symbols/Makefile b/src/test/run-make/metadata-flag-frobs-symbols/Makefile new file mode 100644 index 000000000000..09e6ae0bbf7c --- /dev/null +++ b/src/test/run-make/metadata-flag-frobs-symbols/Makefile @@ -0,0 +1,10 @@ +-include ../tools.mk + +all: + $(RUSTC) foo.rs -C metadata=a -C extra-filename=-a + $(RUSTC) foo.rs -C metadata=b -C extra-filename=-b + $(RUSTC) bar.rs \ + --extern foo1=$(TMPDIR)/libfoo-a.rlib \ + --extern foo2=$(TMPDIR)/libfoo-b.rlib \ + -Z print-link-args + $(call RUN,bar) diff --git a/src/test/run-make/metadata-flag-frobs-symbols/bar.rs b/src/test/run-make/metadata-flag-frobs-symbols/bar.rs new file mode 100644 index 000000000000..1e6957a3694b --- /dev/null +++ b/src/test/run-make/metadata-flag-frobs-symbols/bar.rs @@ -0,0 +1,8 @@ +extern crate foo1; +extern crate foo2; + +fn main() { + let a = foo1::foo(); + let b = foo2::foo(); + assert!(a as *const _ != b as *const _); +} diff --git a/src/test/run-make/metadata-flag-frobs-symbols/foo.rs b/src/test/run-make/metadata-flag-frobs-symbols/foo.rs new file mode 100644 index 000000000000..1974f4bc5628 --- /dev/null +++ b/src/test/run-make/metadata-flag-frobs-symbols/foo.rs @@ -0,0 +1,6 @@ +#![crate_name = "foo"] +#![crate_type = "rlib"] + +static FOO: uint = 3; + +pub fn foo() -> &'static uint { &FOO }