diff --git a/src/librustc_metadata/lib.rs b/src/librustc_metadata/lib.rs index ee99f7465b905..480c344f38123 100644 --- a/src/librustc_metadata/lib.rs +++ b/src/librustc_metadata/lib.rs @@ -13,6 +13,7 @@ html_root_url = "https://doc.rust-lang.org/nightly/")] #![feature(box_patterns)] +#![feature(drain_filter)] #![feature(libc)] #![feature(nll)] #![feature(proc_macro_internals)] diff --git a/src/librustc_metadata/native_libs.rs b/src/librustc_metadata/native_libs.rs index 6f85418b297ed..2c6ef4baa55e5 100644 --- a/src/librustc_metadata/native_libs.rs +++ b/src/librustc_metadata/native_libs.rs @@ -208,34 +208,31 @@ impl<'a, 'tcx> Collector<'a, 'tcx> { } // Update kind and, optionally, the name of all native libraries - // (there may be more than one) with the specified name. + // (there may be more than one) with the specified name. If any + // library is mentioned more than once, keep the latest mention + // of it, so that any possible dependent libraries appear before + // it. (This ensures that the linker is able to see symbols from + // all possible dependent libraries before linking in the library + // in question.) for &(ref name, ref new_name, kind) in &self.tcx.sess.opts.libs { - let mut found = false; - for lib in self.libs.iter_mut() { - let lib_name = match lib.name { - Some(n) => n, - None => continue, - }; - if lib_name == name as &str { - let mut changed = false; - if let Some(k) = kind { - lib.kind = k; - changed = true; - } - if let &Some(ref new_name) = new_name { - lib.name = Some(Symbol::intern(new_name)); - changed = true; - } - if !changed { - let msg = format!("redundant linker flag specified for \ - library `{}`", name); - self.tcx.sess.warn(&msg); + // If we've already added any native libraries with the same + // name, they will be pulled out into `existing`, so that we + // can move them to the end of the list below. + let mut existing = self.libs.drain_filter(|lib| { + if let Some(lib_name) = lib.name { + if lib_name == name as &str { + if let Some(k) = kind { + lib.kind = k; + } + if let &Some(ref new_name) = new_name { + lib.name = Some(Symbol::intern(new_name)); + } + return true; } - - found = true; } - } - if !found { + false + }).collect::>(); + if existing.is_empty() { // Add if not found let new_name = new_name.as_ref().map(|s| &**s); // &Option -> Option<&str> let lib = NativeLibrary { @@ -246,6 +243,10 @@ impl<'a, 'tcx> Collector<'a, 'tcx> { wasm_import_module: None, }; self.register_native_lib(None, lib); + } else { + // Move all existing libraries with the same name to the + // end of the command line. + self.libs.append(&mut existing); } } } diff --git a/src/test/run-make-fulldeps/redundant-libs/Makefile b/src/test/run-make-fulldeps/redundant-libs/Makefile new file mode 100644 index 0000000000000..9486e07d21bf7 --- /dev/null +++ b/src/test/run-make-fulldeps/redundant-libs/Makefile @@ -0,0 +1,27 @@ +-include ../tools.mk + +ifdef IS_WINDOWS +all: +else + +# rustc will remove one of the two redundant references to foo below. Depending +# on which one gets removed, we'll get a linker error on SOME platforms (like +# Linux). On these platforms, when a library is referenced, the linker will +# only pull in the symbols needed _at that point in time_. If a later library +# depends on additional symbols from the library, they will not have been pulled +# in, and you'll get undefined symbols errors. +# +# So in this example, we need to ensure that rustc keeps the _later_ reference +# to foo, and not the former one. +RUSTC_FLAGS = \ + -l static=bar \ + -l foo \ + -l static=baz \ + -l foo \ + -Z print-link-args + +all: $(call DYLIB,foo) $(call STATICLIB,bar) $(call STATICLIB,baz) + $(RUSTC) $(RUSTC_FLAGS) main.rs + $(call RUN,main) + +endif diff --git a/src/test/run-make-fulldeps/redundant-libs/bar.c b/src/test/run-make-fulldeps/redundant-libs/bar.c new file mode 100644 index 0000000000000..e42599986781f --- /dev/null +++ b/src/test/run-make-fulldeps/redundant-libs/bar.c @@ -0,0 +1 @@ +void bar() {} diff --git a/src/test/run-make-fulldeps/redundant-libs/baz.c b/src/test/run-make-fulldeps/redundant-libs/baz.c new file mode 100644 index 0000000000000..a4e2c2b717fdb --- /dev/null +++ b/src/test/run-make-fulldeps/redundant-libs/baz.c @@ -0,0 +1,7 @@ +extern void foo1(); +extern void foo2(); + +void baz() { + foo1(); + foo2(); +} diff --git a/src/test/run-make-fulldeps/redundant-libs/foo.c b/src/test/run-make-fulldeps/redundant-libs/foo.c new file mode 100644 index 0000000000000..339ee86c99eae --- /dev/null +++ b/src/test/run-make-fulldeps/redundant-libs/foo.c @@ -0,0 +1,2 @@ +void foo1() {} +void foo2() {} diff --git a/src/test/run-make-fulldeps/redundant-libs/main.rs b/src/test/run-make-fulldeps/redundant-libs/main.rs new file mode 100644 index 0000000000000..90d185ff51dbd --- /dev/null +++ b/src/test/run-make-fulldeps/redundant-libs/main.rs @@ -0,0 +1,11 @@ +extern "C" { + fn bar(); + fn baz(); +} + +fn main() { + unsafe { + bar(); + baz(); + } +}