]> git.lizzy.rs Git - rust.git/commitdiff
rustc: Add some suppot for mixing rlibs and dylibs
authorAlex Crichton <alex@alexcrichton.com>
Fri, 2 May 2014 07:59:27 +0000 (00:59 -0700)
committerAlex Crichton <alex@alexcrichton.com>
Fri, 2 May 2014 18:39:18 +0000 (11:39 -0700)
Currently, rustc requires that a linkage be a product of 100% rlibs or 100%
dylibs. This is to satisfy the requirement that each object appear at most once
in the final output products. This is a bit limiting, and the upcoming libcore
library cannot exist as a dylib, so these rules must change.

The goal of this commit is to enable *some* use cases for mixing rlibs and
dylibs, primarily libcore's use case. It is not targeted at allowing an
exhaustive number of linkage flavors.

There is a new dependency_format module in rustc which calculates what format
each upstream library should be linked as in each output type of the current
unit of compilation. The module itself contains many gory details about what's
going on here.

cc #10729

26 files changed:
src/librustc/back/link.rs
src/librustc/driver/driver.rs
src/librustc/driver/session.rs
src/librustc/lib.rs
src/librustc/metadata/common.rs
src/librustc/metadata/csearch.rs
src/librustc/metadata/cstore.rs
src/librustc/metadata/decoder.rs
src/librustc/metadata/encoder.rs
src/librustc/middle/dependency_format.rs [new file with mode: 0644]
src/librustc/middle/trans/base.rs
src/librustc/middle/ty.rs
src/test/auxiliary/issue-12133-dylib2.rs [new file with mode: 0644]
src/test/compile-fail/issue-12133-1.rs [deleted file]
src/test/compile-fail/issue-12133-2.rs [deleted file]
src/test/compile-fail/issue-12133-3.rs [deleted file]
src/test/run-make/mixing-formats/Makefile [new file with mode: 0644]
src/test/run-make/mixing-formats/bar1.rs [new file with mode: 0644]
src/test/run-make/mixing-formats/bar2.rs [new file with mode: 0644]
src/test/run-make/mixing-formats/baz.rs [new file with mode: 0644]
src/test/run-make/mixing-formats/baz2.rs [new file with mode: 0644]
src/test/run-make/mixing-formats/foo.rs [new file with mode: 0644]
src/test/run-make/mixing-libs/Makefile
src/test/run-pass/issue-12133-1.rs [new file with mode: 0644]
src/test/run-pass/issue-12133-2.rs [new file with mode: 0644]
src/test/run-pass/issue-12133-3.rs [new file with mode: 0644]

index 8d021a9a33ebba779c5a4881eaab36f0b614189c..ac6bb12e410bb9ef1a890aab09564a7e2677eda2 100644 (file)
@@ -884,10 +884,10 @@ fn link_binary_output(sess: &Session,
             link_staticlib(sess, &obj_filename, &out_filename);
         }
         session::CrateTypeExecutable => {
-            link_natively(sess, false, &obj_filename, &out_filename);
+            link_natively(sess, trans, false, &obj_filename, &out_filename);
         }
         session::CrateTypeDylib => {
-            link_natively(sess, true, &obj_filename, &out_filename);
+            link_natively(sess, trans, true, &obj_filename, &out_filename);
         }
     }
 
@@ -1037,13 +1037,13 @@ fn link_staticlib(sess: &Session, obj_filename: &Path, out_filename: &Path) {
 //
 // This will invoke the system linker/cc to create the resulting file. This
 // links to all upstream files as well.
-fn link_natively(sess: &Session, dylib: bool, obj_filename: &Path,
-                 out_filename: &Path) {
+fn link_natively(sess: &Session, trans: &CrateTranslation, dylib: bool,
+                 obj_filename: &Path, out_filename: &Path) {
     let tmpdir = TempDir::new("rustc").expect("needs a temp dir");
     // The invocations of cc share some flags across platforms
     let cc_prog = get_cc_prog(sess);
     let mut cc_args = sess.targ_cfg.target_strs.cc_args.clone();
-    cc_args.push_all_move(link_args(sess, dylib, tmpdir.path(),
+    cc_args.push_all_move(link_args(sess, dylib, tmpdir.path(), trans,
                                     obj_filename, out_filename));
     if (sess.opts.debugging_opts & session::PRINT_LINK_ARGS) != 0 {
         println!("{} link args: '{}'", cc_prog, cc_args.connect("' '"));
@@ -1092,6 +1092,7 @@ fn link_natively(sess: &Session, dylib: bool, obj_filename: &Path,
 fn link_args(sess: &Session,
              dylib: bool,
              tmpdir: &Path,
+             trans: &CrateTranslation,
              obj_filename: &Path,
              out_filename: &Path) -> Vec<~str> {
 
@@ -1251,7 +1252,7 @@ fn link_args(sess: &Session,
     // this kind of behavior is pretty platform specific and generally not
     // recommended anyway, so I don't think we're shooting ourself in the foot
     // much with that.
-    add_upstream_rust_crates(&mut args, sess, dylib, tmpdir);
+    add_upstream_rust_crates(&mut args, sess, dylib, tmpdir, trans);
     add_local_native_libraries(&mut args, sess);
     add_upstream_native_libraries(&mut args, sess);
 
@@ -1361,73 +1362,44 @@ fn add_local_native_libraries(args: &mut Vec<~str>, sess: &Session) {
 // dependencies will be linked when producing the final output (instead of
 // the intermediate rlib version)
 fn add_upstream_rust_crates(args: &mut Vec<~str>, sess: &Session,
-                            dylib: bool, tmpdir: &Path) {
-
-    // As a limitation of the current implementation, we require that everything
-    // must be static or everything must be dynamic. The reasons for this are a
-    // little subtle, but as with staticlibs and rlibs, the goal is to prevent
-    // duplicate copies of the same library showing up. For example, a static
-    // immediate dependency might show up as an upstream dynamic dependency and
-    // we currently have no way of knowing that. We know that all dynamic
-    // libraries require dynamic dependencies (see above), so it's satisfactory
-    // to include either all static libraries or all dynamic libraries.
+                            dylib: bool, tmpdir: &Path,
+                            trans: &CrateTranslation) {
+    // All of the heavy lifting has previously been accomplished by the
+    // dependency_format module of the compiler. This is just crawling the
+    // output of that module, adding crates as necessary.
     //
-    // With this limitation, we expose a compiler default linkage type and an
-    // option to reverse that preference. The current behavior looks like:
-    //
-    // * If a dylib is being created, upstream dependencies must be dylibs
-    // * If nothing else is specified, static linking is preferred
-    // * If the -C prefer-dynamic flag is given, dynamic linking is preferred
-    // * If one form of linking fails, the second is also attempted
-    // * If both forms fail, then we emit an error message
-
-    let dynamic = get_deps(&sess.cstore, cstore::RequireDynamic);
-    let statik = get_deps(&sess.cstore, cstore::RequireStatic);
-    match (dynamic, statik, sess.opts.cg.prefer_dynamic, dylib) {
-        (_, Some(deps), false, false) => {
-            add_static_crates(args, sess, tmpdir, deps)
-        }
-
-        (None, Some(deps), true, false) => {
-            // If you opted in to dynamic linking and we decided to emit a
-            // static output, you should probably be notified of such an event!
-            sess.warn("dynamic linking was preferred, but dependencies \
-                       could not all be found in a dylib format.");
-            sess.warn("linking statically instead, using rlibs");
-            add_static_crates(args, sess, tmpdir, deps)
-        }
+    // Linking to a rlib involves just passing it to the linker (the linker
+    // will slurp up the object files inside), and linking to a dynamic library
+    // involves just passing the right -l flag.
 
-        (Some(deps), _, _, _) => add_dynamic_crates(args, sess, deps),
+    let data = if dylib {
+        trans.crate_formats.get(&session::CrateTypeDylib)
+    } else {
+        trans.crate_formats.get(&session::CrateTypeExecutable)
+    };
 
-        (None, _, _, true) => {
-            sess.err("dylib output requested, but some depenencies could not \
-                      be found in the dylib format");
-            let deps = sess.cstore.get_used_crates(cstore::RequireDynamic);
-            for (cnum, path) in deps.move_iter() {
-                if path.is_some() { continue }
-                let name = sess.cstore.get_crate_data(cnum).name.clone();
-                sess.note(format!("dylib not found: {}", name));
+    // Invoke get_used_crates to ensure that we get a topological sorting of
+    // crates.
+    let deps = sess.cstore.get_used_crates(cstore::RequireDynamic);
+
+    for &(cnum, _) in deps.iter() {
+        // We may not pass all crates through to the linker. Some crates may
+        // appear statically in an existing dylib, meaning we'll pick up all the
+        // symbols from the dylib.
+        let kind = match *data.get(cnum as uint - 1) {
+            Some(t) => t,
+            None => continue
+        };
+        let src = sess.cstore.get_used_crate_source(cnum).unwrap();
+        match kind {
+            cstore::RequireDynamic => {
+                add_dynamic_crate(args, sess, src.dylib.unwrap())
             }
-        }
-
-        (None, None, pref, false) => {
-            let (pref, name) = if pref {
-                sess.err("dynamic linking is preferred, but dependencies were \
-                          not found in either dylib or rlib format");
-                (cstore::RequireDynamic, "dylib")
-            } else {
-                sess.err("dependencies were not all found in either dylib or \
-                          rlib format");
-                (cstore::RequireStatic, "rlib")
-            };
-            sess.note(format!("dependencies not found in the `{}` format",
-                              name));
-            for (cnum, path) in sess.cstore.get_used_crates(pref).move_iter() {
-                if path.is_some() { continue }
-                let name = sess.cstore.get_crate_data(cnum).name.clone();
-                sess.note(name);
+            cstore::RequireStatic => {
+                add_static_crate(args, sess, tmpdir, cnum, src.rlib.unwrap())
             }
         }
+
     }
 
     // Converts a library file-stem into a cc -l argument
@@ -1439,82 +1411,64 @@ fn unlib(config: &session::Config, stem: &str) -> ~str {
         }
     }
 
-    // Attempts to find all dependencies with a certain linkage preference,
-    // returning `None` if not all libraries could be found with that
-    // preference.
-    fn get_deps(cstore: &cstore::CStore,  preference: cstore::LinkagePreference)
-            -> Option<Vec<(ast::CrateNum, Path)> >
-    {
-        let crates = cstore.get_used_crates(preference);
-        if crates.iter().all(|&(_, ref p)| p.is_some()) {
-            Some(crates.move_iter().map(|(a, b)| (a, b.unwrap())).collect())
-        } else {
-            None
-        }
-    }
-
     // Adds the static "rlib" versions of all crates to the command line.
-    fn add_static_crates(args: &mut Vec<~str>, sess: &Session, tmpdir: &Path,
-                         crates: Vec<(ast::CrateNum, Path)>) {
-        for (cnum, cratepath) in crates.move_iter() {
-            // When performing LTO on an executable output, all of the
-            // bytecode from the upstream libraries has already been
-            // included in our object file output. We need to modify all of
-            // the upstream archives to remove their corresponding object
-            // file to make sure we don't pull the same code in twice.
-            //
-            // We must continue to link to the upstream archives to be sure
-            // to pull in native static dependencies. As the final caveat,
-            // on linux it is apparently illegal to link to a blank archive,
-            // so if an archive no longer has any object files in it after
-            // we remove `lib.o`, then don't link against it at all.
-            //
-            // If we're not doing LTO, then our job is simply to just link
-            // against the archive.
-            if sess.lto() {
-                let name = sess.cstore.get_crate_data(cnum).name.clone();
-                time(sess.time_passes(), format!("altering {}.rlib", name),
-                     (), |()| {
-                    let dst = tmpdir.join(cratepath.filename().unwrap());
-                    match fs::copy(&cratepath, &dst) {
-                        Ok(..) => {}
-                        Err(e) => {
-                            sess.err(format!("failed to copy {} to {}: {}",
-                                             cratepath.display(),
-                                             dst.display(),
-                                             e));
-                            sess.abort_if_errors();
-                        }
-                    }
-                    let dst_str = dst.as_str().unwrap().to_owned();
-                    let mut archive = Archive::open(sess, dst);
-                    archive.remove_file(format!("{}.o", name));
-                    let files = archive.files();
-                    if files.iter().any(|s| s.ends_with(".o")) {
-                        args.push(dst_str);
+    fn add_static_crate(args: &mut Vec<~str>, sess: &Session, tmpdir: &Path,
+                        cnum: ast::CrateNum, cratepath: Path) {
+        // When performing LTO on an executable output, all of the
+        // bytecode from the upstream libraries has already been
+        // included in our object file output. We need to modify all of
+        // the upstream archives to remove their corresponding object
+        // file to make sure we don't pull the same code in twice.
+        //
+        // We must continue to link to the upstream archives to be sure
+        // to pull in native static dependencies. As the final caveat,
+        // on linux it is apparently illegal to link to a blank archive,
+        // so if an archive no longer has any object files in it after
+        // we remove `lib.o`, then don't link against it at all.
+        //
+        // If we're not doing LTO, then our job is simply to just link
+        // against the archive.
+        if sess.lto() {
+            let name = sess.cstore.get_crate_data(cnum).name.clone();
+            time(sess.time_passes(), format!("altering {}.rlib", name),
+                 (), |()| {
+                let dst = tmpdir.join(cratepath.filename().unwrap());
+                match fs::copy(&cratepath, &dst) {
+                    Ok(..) => {}
+                    Err(e) => {
+                        sess.err(format!("failed to copy {} to {}: {}",
+                                         cratepath.display(),
+                                         dst.display(),
+                                         e));
+                        sess.abort_if_errors();
                     }
-                });
-            } else {
-                args.push(cratepath.as_str().unwrap().to_owned());
-            }
+                }
+                let dst_str = dst.as_str().unwrap().to_owned();
+                let mut archive = Archive::open(sess, dst);
+                archive.remove_file(format!("{}.o", name));
+                let files = archive.files();
+                if files.iter().any(|s| s.ends_with(".o")) {
+                    args.push(dst_str);
+                }
+            });
+        } else {
+            args.push(cratepath.as_str().unwrap().to_owned());
         }
     }
 
     // Same thing as above, but for dynamic crates instead of static crates.
-    fn add_dynamic_crates(args: &mut Vec<~str>, sess: &Session,
-                          crates: Vec<(ast::CrateNum, Path)> ) {
+    fn add_dynamic_crate(args: &mut Vec<~str>, sess: &Session,
+                         cratepath: Path) {
         // If we're performing LTO, then it should have been previously required
         // that all upstream rust dependencies were available in an rlib format.
         assert!(!sess.lto());
 
-        for (_, cratepath) in crates.move_iter() {
-            // Just need to tell the linker about where the library lives and
-            // what its name is
-            let dir = cratepath.dirname_str().unwrap();
-            if !dir.is_empty() { args.push("-L" + dir); }
-            let libarg = unlib(&sess.targ_cfg, cratepath.filestem_str().unwrap());
-            args.push("-l" + libarg);
-        }
+        // Just need to tell the linker about where the library lives and
+        // what its name is
+        let dir = cratepath.dirname_str().unwrap();
+        if !dir.is_empty() { args.push("-L" + dir); }
+        let libarg = unlib(&sess.targ_cfg, cratepath.filestem_str().unwrap());
+        args.push("-l" + libarg);
     }
 }
 
index b579a9b9c64c12276548613c441a877a74bc2c72..f2e3c106bf6beb84a5fb71b2dfce9bbff6b2ba59 100644 (file)
@@ -24,6 +24,7 @@
 use metadata::creader::Loader;
 use metadata;
 use middle::{trans, freevars, kind, ty, typeck, lint, reachable};
+use middle::dependency_format;
 use middle;
 use util::common::time;
 use util::ppaux;
@@ -383,7 +384,7 @@ pub fn phase_3_run_analysis_passes(sess: Session,
         ty_cx: ty_cx,
         exported_items: exported_items,
         public_items: public_items,
-        reachable: reachable_map
+        reachable: reachable_map,
     }
 }
 
@@ -394,6 +395,7 @@ pub struct CrateTranslation {
     pub link: LinkMeta,
     pub metadata: Vec<u8>,
     pub reachable: Vec<~str>,
+    pub crate_formats: dependency_format::Dependencies,
 }
 
 /// Run the translation phase to LLVM, after which the AST and analysis can
@@ -401,11 +403,14 @@ pub struct CrateTranslation {
 pub fn phase_4_translate_to_llvm(krate: ast::Crate,
                                  analysis: CrateAnalysis,
                                  outputs: &OutputFilenames) -> (ty::ctxt, CrateTranslation) {
-    // Option dance to work around the lack of stack once closures.
     let time_passes = analysis.ty_cx.sess.time_passes();
-    let mut analysis = Some(analysis);
-    time(time_passes, "translation", krate, |krate|
-         trans::base::trans_crate(krate, analysis.take_unwrap(), outputs))
+
+    time(time_passes, "resolving dependency formats", (), |_|
+         dependency_format::calculate(&analysis.ty_cx));
+
+    // Option dance to work around the lack of stack once closures.
+    time(time_passes, "translation", (krate, analysis), |(krate, analysis)|
+         trans::base::trans_crate(krate, analysis, outputs))
 }
 
 /// Run LLVM itself, producing a bitcode file, assembly file or object file
index 950e6bd8ee8333621f3437cbff875083ac8a7f1c..4af50d1ae46113dd9b8c809b1cdbefad3d627881 100644 (file)
@@ -126,7 +126,7 @@ pub enum DebugInfoLevel {
 pub struct Options {
     // The crate config requested for the session, which may be combined
     // with additional crate configurations during the compile process
-    pub crate_types: Vec<CrateType> ,
+    pub crate_types: Vec<CrateType>,
 
     pub gc: bool,
     pub optimize: OptLevel,
@@ -167,7 +167,7 @@ pub enum EntryFnType {
     EntryNone,
 }
 
-#[deriving(Eq, Ord, Clone, TotalOrd, TotalEq)]
+#[deriving(Eq, Ord, Clone, TotalOrd, TotalEq, Hash)]
 pub enum CrateType {
     CrateTypeExecutable,
     CrateTypeDylib,
index c9a40cffe587c8d7be409297f08c3ed4bc7af5e9..e165242397a0c7b276ff744cdff2b38f4a238cc7 100644 (file)
@@ -93,6 +93,7 @@ pub mod middle {
     pub mod cfg;
     pub mod dead;
     pub mod expr_use_visitor;
+    pub mod dependency_format;
 }
 
 pub mod front {
index 8f71ac4ebf7ef537127919c50b6a82b136406d32..839b0e08d37952a8e192d96d6bbc06fc5713411f 100644 (file)
@@ -203,6 +203,8 @@ pub fn from_uint(value : uint) -> Option<astencode_tag> {
 
 pub static tag_crate_triple: uint = 0x66;
 
+pub static tag_dylib_dependency_formats: uint = 0x67;
+
 #[deriving(Clone, Show)]
 pub struct LinkMeta {
     pub crateid: CrateId,
index 77b4871ea8b6f1c1991fe57171134196e72fbf07..ea764f132b72dd8c02b03127e870a062476a4c70 100644 (file)
@@ -284,3 +284,11 @@ pub fn get_tuple_struct_definition_if_ctor(cstore: &cstore::CStore,
     let cdata = cstore.get_crate_data(def_id.krate);
     decoder::get_tuple_struct_definition_if_ctor(&*cdata, def_id.node)
 }
+
+pub fn get_dylib_dependency_formats(cstore: &cstore::CStore,
+                                    cnum: ast::CrateNum)
+    -> Vec<(ast::CrateNum, cstore::LinkagePreference)>
+{
+    let cdata = cstore.get_crate_data(cnum);
+    decoder::get_dylib_dependency_formats(&*cdata)
+}
index 8e731a98dd7e70abf92b82e62ca490ae6abd1212..390df9c0809897f4d9df3c126814053b9d22d3c6 100644 (file)
@@ -45,7 +45,7 @@ pub struct crate_metadata {
     pub span: Span,
 }
 
-#[deriving(Eq)]
+#[deriving(Show, Eq, Clone)]
 pub enum LinkagePreference {
     RequireDynamic,
     RequireStatic,
index 93fc8b26e7749b869b0e4f0c26c23fc95fffd3c2..302bba2f56b6c3eac2bec66725084c5c8791a42a 100644 (file)
@@ -1273,3 +1273,29 @@ pub fn get_exported_macros(data: &[u8]) -> Vec<~str> {
     });
     result
 }
+
+pub fn get_dylib_dependency_formats(cdata: Cmd)
+    -> Vec<(ast::CrateNum, cstore::LinkagePreference)>
+{
+    let formats = reader::get_doc(reader::Doc(cdata.data()),
+                                  tag_dylib_dependency_formats);
+    let mut result = Vec::new();
+
+    debug!("found dylib deps: {}", formats.as_str_slice());
+    for spec in formats.as_str_slice().split(',') {
+        if spec.len() == 0 { continue }
+        let cnum = spec.split(':').nth(0).unwrap();
+        let link = spec.split(':').nth(1).unwrap();
+        let cnum = from_str(cnum).unwrap();
+        let cnum = match cdata.cnum_map.find(&cnum) {
+            Some(&n) => n,
+            None => fail!("didn't find a crate in the cnum_map")
+        };
+        result.push((cnum, if link == "d" {
+            cstore::RequireDynamic
+        } else {
+            cstore::RequireStatic
+        }));
+    }
+    return result;
+}
index 214fa3ee04bd942324bd30da74b99d754e815b98..684745daa3cfae95137c304a59a43cd143ec23b1 100644 (file)
@@ -14,6 +14,7 @@
 #![allow(non_camel_case_types)]
 
 use back::svh::Svh;
+use driver::session;
 use metadata::common::*;
 use metadata::cstore;
 use metadata::decoder;
@@ -1696,6 +1697,23 @@ fn encode_crate_triple(ebml_w: &mut Encoder, triple: &str) {
     ebml_w.end_tag();
 }
 
+fn encode_dylib_dependency_formats(ebml_w: &mut Encoder, ecx: &EncodeContext) {
+    ebml_w.start_tag(tag_dylib_dependency_formats);
+    match ecx.tcx.dependency_formats.borrow().find(&session::CrateTypeDylib) {
+        Some(arr) => {
+            let s = arr.iter().enumerate().filter_map(|(i, slot)| {
+                slot.map(|kind| format!("{}:{}", i + 1, match kind {
+                    cstore::RequireDynamic => "d",
+                    cstore::RequireStatic => "s",
+                }))
+            }).collect::<Vec<~str>>();
+            ebml_w.writer.write(s.connect(",").as_bytes());
+        }
+        None => {}
+    }
+    ebml_w.end_tag();
+}
+
 // NB: Increment this as you change the metadata encoding version.
 pub static metadata_encoding_version : &'static [u8] =
     &[0x72, //'r' as u8,
@@ -1767,6 +1785,7 @@ struct Stats {
     encode_crate_id(&mut ebml_w, &ecx.link_meta.crateid);
     encode_crate_triple(&mut ebml_w, tcx.sess.targ_cfg.target_strs.target_triple);
     encode_hash(&mut ebml_w, &ecx.link_meta.crate_hash);
+    encode_dylib_dependency_formats(&mut ebml_w, &ecx);
 
     let mut i = ebml_w.writer.tell().unwrap();
     let crate_attrs = synthesize_crate_attrs(&ecx, krate);
diff --git a/src/librustc/middle/dependency_format.rs b/src/librustc/middle/dependency_format.rs
new file mode 100644 (file)
index 0000000..7b5ef2b
--- /dev/null
@@ -0,0 +1,229 @@
+// Copyright 2014 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 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+//! Resolution of mixing rlibs and dylibs
+//!
+//! When producing a final artifact, such as a dynamic library, the compiler has
+//! a choice between linking an rlib or linking a dylib of all upstream
+//! dependencies. The linking phase must guarantee, however, that a library only
+//! show up once in the object file. For example, it is illegal for library A to
+//! be statically linked to B and C in separate dylibs, and then link B and C
+//! into a crate D (because library A appears twice).
+//!
+//! The job of this module is to calculate what format each upstream crate
+//! should be used when linking each output type requested in this session. This
+//! generally follows this set of rules:
+//!
+//!     1. Each library must appear exactly once in the output.
+//!     2. Each rlib contains only one library (it's just an object file)
+//!     3. Each dylib can contain more than one library (due to static linking),
+//!        and can also bring in many dynamic dependencies.
+//!
+//! With these constraints in mind, it's generally a very difficult problem to
+//! find a solution that's not "all rlibs" or "all dylibs". I have suspicions
+//! that NP-ness may come into the picture here...
+//!
+//! The current selection algorithm below looks mostly similar to:
+//!
+//!     1. If static linking is required, then require all upstream dependencies
+//!        to be available as rlibs. If not, generate an error.
+//!     2. If static linking is requested (generating an executable), then
+//!        attempt to use all upstream dependencies as rlibs. If any are not
+//!        found, bail out and continue to step 3.
+//!     3. Static linking has failed, at least one library must be dynamically
+//!        linked. Apply a heuristic by greedily maximizing the number of
+//!        dynamically linked libraries.
+//!     4. Each upstream dependency available as a dynamic library is
+//!        registered. The dependencies all propagate, adding to a map. It is
+//!        possible for a dylib to add a static library as a dependency, but it
+//!        is illegal for two dylibs to add the same static library as a
+//!        dependency. The same dylib can be added twice. Additionally, it is
+//!        illegal to add a static dependency when it was previously found as a
+//!        dylib (and vice versa)
+//!     5. After all dynamic dependencies have been traversed, re-traverse the
+//!        remaining dependencies and add them statically (if they haven't been
+//!        added already).
+//!
+//! While not perfect, this algorithm should help support use-cases such as leaf
+//! dependencies being static while the larger tree of inner dependencies are
+//! all dynamic. This isn't currently very well battle tested, so it will likely
+//! fall short in some use cases.
+//!
+//! Currently, there is no way to specify the preference of linkage with a
+//! particular library (other than a global dynamic/static switch).
+//! Additionally, the algorithm is geared towards finding *any* solution rather
+//! than finding a number of solutions (there are normally quite a few).
+
+use collections::HashMap;
+use syntax::ast;
+
+use driver::session;
+use metadata::cstore;
+use metadata::csearch;
+use middle::ty;
+
+/// A list of dependencies for a certain crate type.
+///
+/// The length of this vector is the same as the number of external crates used.
+/// The value is None if the crate does not need to be linked (it was found
+/// statically in another dylib), or Some(kind) if it needs to be linked as
+/// `kind` (either static or dynamic).
+pub type DependencyList = Vec<Option<cstore::LinkagePreference>>;
+
+/// A mapping of all required dependencies for a particular flavor of output.
+///
+/// This is local to the tcx, and is generally relevant to one session.
+pub type Dependencies = HashMap<session::CrateType, DependencyList>;
+
+pub fn calculate(tcx: &ty::ctxt) {
+    let mut fmts = tcx.dependency_formats.borrow_mut();
+    for &ty in tcx.sess.crate_types.borrow().iter() {
+        fmts.insert(ty, calculate_type(&tcx.sess, ty));
+    }
+    tcx.sess.abort_if_errors();
+}
+
+fn calculate_type(sess: &session::Session,
+                  ty: session::CrateType) -> DependencyList {
+    match ty {
+        // If the global prefer_dynamic switch is turned off, first attempt
+        // static linkage (this can fail).
+        session::CrateTypeExecutable if !sess.opts.cg.prefer_dynamic => {
+            match attempt_static(sess) {
+                Some(v) => return v,
+                None => {}
+            }
+        }
+
+        // No linkage happens with rlibs, we just needed the metadata (which we
+        // got long ago), so don't bother with anything.
+        session::CrateTypeRlib => return Vec::new(),
+
+        // Staticlibs must have all static dependencies. If any fail to be
+        // found, we generate some nice pretty errors.
+        session::CrateTypeStaticlib => {
+            match attempt_static(sess) {
+                Some(v) => return v,
+                None => {}
+            }
+            sess.cstore.iter_crate_data(|cnum, data| {
+                let src = sess.cstore.get_used_crate_source(cnum).unwrap();
+                if src.rlib.is_some() { return }
+                sess.err(format!("dependency `{}` not found in rlib format",
+                                 data.name));
+            });
+            return Vec::new();
+        }
+
+        // Everything else falls through below
+        session::CrateTypeExecutable | session::CrateTypeDylib => {},
+    }
+
+    let mut formats = HashMap::new();
+
+    // Sweep all crates for found dylibs. Add all dylibs, as well as their
+    // dependencies, ensuring there are no conflicts. The only valid case for a
+    // dependency to be relied upon twice is for both cases to rely on a dylib.
+    sess.cstore.iter_crate_data(|cnum, data| {
+        let src = sess.cstore.get_used_crate_source(cnum).unwrap();
+        if src.dylib.is_some() {
+            add_library(sess, cnum, cstore::RequireDynamic, &mut formats);
+            debug!("adding dylib: {}", data.name);
+            let deps = csearch::get_dylib_dependency_formats(&sess.cstore, cnum);
+            for &(depnum, style) in deps.iter() {
+                add_library(sess, depnum, style, &mut formats);
+                debug!("adding {}: {}", style,
+                       sess.cstore.get_crate_data(depnum).name.clone());
+            }
+        }
+    });
+
+    // Collect what we've got so far in the return vector.
+    let mut ret = range(1, sess.cstore.next_crate_num()).map(|i| {
+        match formats.find(&i).map(|v| *v) {
+            v @ Some(cstore::RequireDynamic) => v,
+            _ => None,
+        }
+    }).collect::<Vec<_>>();
+
+    // Run through the dependency list again, and add any missing libraries as
+    // static libraries.
+    sess.cstore.iter_crate_data(|cnum, data| {
+        let src = sess.cstore.get_used_crate_source(cnum).unwrap();
+        if src.dylib.is_none() && !formats.contains_key(&cnum) {
+            assert!(src.rlib.is_some());
+            add_library(sess, cnum, cstore::RequireStatic, &mut formats);
+            *ret.get_mut(cnum as uint - 1) = Some(cstore::RequireStatic);
+            debug!("adding staticlib: {}", data.name);
+        }
+    });
+
+    // When dylib B links to dylib A, then when using B we must also link to A.
+    // It could be the case, however, that the rlib for A is present (hence we
+    // found metadata), but the dylib for A has since been removed.
+    //
+    // For situations like this, we perform one last pass over the dependencies,
+    // making sure that everything is available in the requested format.
+    for (cnum, kind) in ret.iter().enumerate() {
+        let cnum = cnum as ast::CrateNum;
+        let src = sess.cstore.get_used_crate_source(cnum + 1).unwrap();
+        match *kind {
+            None => continue,
+            Some(cstore::RequireStatic) if src.rlib.is_some() => continue,
+            Some(cstore::RequireDynamic) if src.dylib.is_some() => continue,
+            Some(kind) => {
+                let data = sess.cstore.get_crate_data(cnum + 1);
+                sess.err(format!("crate `{}` required to be available in {}, \
+                                  but it was not available in this form",
+                                 data.name,
+                                 match kind {
+                                     cstore::RequireStatic => "rlib",
+                                     cstore::RequireDynamic => "dylib",
+                                 }));
+            }
+        }
+    }
+
+    return ret;
+}
+
+fn add_library(sess: &session::Session,
+               cnum: ast::CrateNum,
+               link: cstore::LinkagePreference,
+               m: &mut HashMap<ast::CrateNum, cstore::LinkagePreference>) {
+    match m.find(&cnum) {
+        Some(&link2) => {
+            // If the linkages differ, then we'd have two copies of the library
+            // if we continued linking. If the linkages are both static, then we
+            // would also have two copies of the library (static from two
+            // different locations).
+            //
+            // This error is probably a little obscure, but I imagine that it
+            // can be refined over time.
+            if link2 != link || link == cstore::RequireStatic {
+                let data = sess.cstore.get_crate_data(cnum);
+                sess.err(format!("cannot satisfy dependencies so `{}` only \
+                                  shows up once", data.name));
+                sess.note("having upstream crates all available in one format \
+                           will likely make this go away");
+            }
+        }
+        None => { m.insert(cnum, link); }
+    }
+}
+
+fn attempt_static(sess: &session::Session) -> Option<DependencyList> {
+    let crates = sess.cstore.get_used_crates(cstore::RequireStatic);
+    if crates.iter().all(|&(_, ref p)| p.is_some()) {
+        Some(crates.move_iter().map(|_| Some(cstore::RequireStatic)).collect())
+    } else {
+        None
+    }
+}
index daed00a32f21dee248872d0da25e32e410eee766..09d4f526af18911bd4f62a6df091e7a853611139 100644 (file)
@@ -2194,6 +2194,7 @@ pub fn trans_crate(krate: ast::Crate,
     reachable.push("rust_eh_personality_catch".to_owned()); // referenced from rt/rust_try.ll
 
     let metadata_module = ccx.metadata_llmod;
+    let formats = ccx.tcx.dependency_formats.borrow().clone();
 
     (ccx.tcx, CrateTranslation {
         context: llcx,
@@ -2202,5 +2203,6 @@ pub fn trans_crate(krate: ast::Crate,
         metadata_module: metadata_module,
         metadata: metadata,
         reachable: reachable,
+        crate_formats: formats,
     })
 }
index bb4ac63529f7d11f15a625088055f0f7bce4cfff..d223170459d890bc2978c30792462b1fc808af88 100644 (file)
@@ -15,6 +15,7 @@
 use metadata::csearch;
 use mc = middle::mem_categorization;
 use middle::const_eval;
+use middle::dependency_format;
 use middle::lang_items::{ExchangeHeapLangItem, OpaqueStructLangItem};
 use middle::lang_items::{TyDescStructLangItem, TyVisitorTraitLangItem};
 use middle::freevars;
@@ -349,6 +350,8 @@ pub struct ctxt {
 
     pub method_map: typeck::MethodMap,
     pub vtable_map: typeck::vtable_map,
+
+    pub dependency_formats: RefCell<dependency_format::Dependencies>,
 }
 
 pub enum tbox_flag {
@@ -1123,6 +1126,7 @@ pub fn mk_ctxt(s: Session,
         extern_const_variants: RefCell::new(DefIdMap::new()),
         method_map: RefCell::new(FnvHashMap::new()),
         vtable_map: RefCell::new(FnvHashMap::new()),
+        dependency_formats: RefCell::new(HashMap::new()),
     }
 }
 
diff --git a/src/test/auxiliary/issue-12133-dylib2.rs b/src/test/auxiliary/issue-12133-dylib2.rs
new file mode 100644 (file)
index 0000000..a978eac
--- /dev/null
@@ -0,0 +1,17 @@
+// Copyright 2014 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 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// no-prefer-dynamic
+
+#![crate_type = "dylib"]
+
+extern crate a = "issue-12133-rlib";
+extern crate b = "issue-12133-dylib";
+
diff --git a/src/test/compile-fail/issue-12133-1.rs b/src/test/compile-fail/issue-12133-1.rs
deleted file mode 100644 (file)
index 63a0352..0000000
+++ /dev/null
@@ -1,19 +0,0 @@
-// Copyright 2014 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 <LICENSE-APACHE or
-// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
-// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
-// option. This file may not be copied, modified, or distributed
-// except according to those terms.
-
-// aux-build:issue-12133-rlib.rs
-// aux-build:issue-12133-dylib.rs
-
-// error-pattern: dynamic linking is preferred, but dependencies were not found
-
-extern crate a = "issue-12133-rlib";
-extern crate b = "issue-12133-dylib";
-
-fn main() {}
diff --git a/src/test/compile-fail/issue-12133-2.rs b/src/test/compile-fail/issue-12133-2.rs
deleted file mode 100644 (file)
index 3f42d28..0000000
+++ /dev/null
@@ -1,20 +0,0 @@
-// Copyright 2014 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 <LICENSE-APACHE or
-// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
-// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
-// option. This file may not be copied, modified, or distributed
-// except according to those terms.
-
-// aux-build:issue-12133-rlib.rs
-// aux-build:issue-12133-dylib.rs
-// no-prefer-dynamic
-
-// error-pattern: dependencies were not all found in either dylib or rlib format
-
-extern crate a = "issue-12133-rlib";
-extern crate b = "issue-12133-dylib";
-
-fn main() {}
diff --git a/src/test/compile-fail/issue-12133-3.rs b/src/test/compile-fail/issue-12133-3.rs
deleted file mode 100644 (file)
index 9b78130..0000000
+++ /dev/null
@@ -1,20 +0,0 @@
-// Copyright 2014 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 <LICENSE-APACHE or
-// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
-// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
-// option. This file may not be copied, modified, or distributed
-// except according to those terms.
-
-// aux-build:issue-12133-rlib.rs
-// aux-build:issue-12133-dylib.rs
-// no-prefer-dynamic
-
-// error-pattern: dylib output requested, but some depenencies could not
-
-#![crate_type = "dylib"]
-
-extern crate a = "issue-12133-rlib";
-extern crate b = "issue-12133-dylib";
diff --git a/src/test/run-make/mixing-formats/Makefile b/src/test/run-make/mixing-formats/Makefile
new file mode 100644 (file)
index 0000000..e665cb2
--- /dev/null
@@ -0,0 +1,74 @@
+-include ../tools.mk
+
+# Testing various mixings of rlibs and dylibs. Makes sure that it's possible to
+# link an rlib to a dylib. The dependency tree among the file looks like:
+#
+#                              foo
+#                            /     \
+#                          bar1   bar2
+#                         /    \ /
+#                       baz    baz2
+#
+# This is generally testing the permutations of the foo/bar1/bar2 layer against
+# the baz/baz2 layer
+
+all:
+       # Building just baz
+       $(RUSTC) --crate-type=rlib  foo.rs
+       $(RUSTC) --crate-type=dylib bar1.rs
+       $(RUSTC) --crate-type=dylib,rlib baz.rs
+       $(RUSTC) --crate-type=bin baz.rs
+       rm $(TMPDIR)/*
+       $(RUSTC) --crate-type=dylib foo.rs
+       $(RUSTC) --crate-type=rlib  bar1.rs
+       $(RUSTC) --crate-type=dylib,rlib baz.rs
+       $(RUSTC) --crate-type=bin baz.rs
+       rm $(TMPDIR)/*
+       # Building baz2
+       $(RUSTC) --crate-type=rlib  foo.rs
+       $(RUSTC) --crate-type=dylib bar1.rs
+       $(RUSTC) --crate-type=dylib bar2.rs
+       $(RUSTC) --crate-type=dylib baz2.rs && exit 1 || exit 0
+       $(RUSTC) --crate-type=bin baz2.rs && exit 1 || exit 0
+       rm $(TMPDIR)/*
+       $(RUSTC) --crate-type=rlib  foo.rs
+       $(RUSTC) --crate-type=rlib  bar1.rs
+       $(RUSTC) --crate-type=dylib bar2.rs
+       $(RUSTC) --crate-type=dylib,rlib baz2.rs
+       $(RUSTC) --crate-type=bin baz2.rs
+       rm $(TMPDIR)/*
+       $(RUSTC) --crate-type=rlib  foo.rs
+       $(RUSTC) --crate-type=dylib bar1.rs
+       $(RUSTC) --crate-type=rlib  bar2.rs
+       $(RUSTC) --crate-type=dylib,rlib baz2.rs
+       $(RUSTC) --crate-type=bin baz2.rs
+       rm $(TMPDIR)/*
+       $(RUSTC) --crate-type=rlib  foo.rs
+       $(RUSTC) --crate-type=rlib  bar1.rs
+       $(RUSTC) --crate-type=rlib  bar2.rs
+       $(RUSTC) --crate-type=dylib,rlib baz2.rs
+       $(RUSTC) --crate-type=bin baz2.rs
+       rm $(TMPDIR)/*
+       $(RUSTC) --crate-type=dylib foo.rs
+       $(RUSTC) --crate-type=rlib  bar1.rs
+       $(RUSTC) --crate-type=rlib  bar2.rs
+       $(RUSTC) --crate-type=dylib,rlib baz2.rs
+       $(RUSTC) --crate-type=bin baz2.rs
+       rm $(TMPDIR)/*
+       $(RUSTC) --crate-type=dylib foo.rs
+       $(RUSTC) --crate-type=dylib bar1.rs
+       $(RUSTC) --crate-type=rlib  bar2.rs
+       $(RUSTC) --crate-type=dylib,rlib baz2.rs
+       $(RUSTC) --crate-type=bin baz2.rs
+       rm $(TMPDIR)/*
+       $(RUSTC) --crate-type=dylib foo.rs
+       $(RUSTC) --crate-type=rlib  bar1.rs
+       $(RUSTC) --crate-type=dylib bar2.rs
+       $(RUSTC) --crate-type=dylib,rlib baz2.rs
+       $(RUSTC) --crate-type=bin baz2.rs
+       rm $(TMPDIR)/*
+       $(RUSTC) --crate-type=dylib foo.rs
+       $(RUSTC) --crate-type=dylib bar1.rs
+       $(RUSTC) --crate-type=dylib bar2.rs
+       $(RUSTC) --crate-type=dylib,rlib baz2.rs
+       $(RUSTC) --crate-type=bin baz2.rs
diff --git a/src/test/run-make/mixing-formats/bar1.rs b/src/test/run-make/mixing-formats/bar1.rs
new file mode 100644 (file)
index 0000000..4b4916f
--- /dev/null
@@ -0,0 +1,11 @@
+// Copyright 2014 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 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+extern crate foo;
diff --git a/src/test/run-make/mixing-formats/bar2.rs b/src/test/run-make/mixing-formats/bar2.rs
new file mode 100644 (file)
index 0000000..4b4916f
--- /dev/null
@@ -0,0 +1,11 @@
+// Copyright 2014 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 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+extern crate foo;
diff --git a/src/test/run-make/mixing-formats/baz.rs b/src/test/run-make/mixing-formats/baz.rs
new file mode 100644 (file)
index 0000000..3fb90f6
--- /dev/null
@@ -0,0 +1,13 @@
+// Copyright 2014 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 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+extern crate bar1;
+
+fn main() {}
diff --git a/src/test/run-make/mixing-formats/baz2.rs b/src/test/run-make/mixing-formats/baz2.rs
new file mode 100644 (file)
index 0000000..4cfa653
--- /dev/null
@@ -0,0 +1,15 @@
+// Copyright 2014 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 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+extern crate bar1;
+extern crate bar2;
+
+fn main() {}
+
diff --git a/src/test/run-make/mixing-formats/foo.rs b/src/test/run-make/mixing-formats/foo.rs
new file mode 100644 (file)
index 0000000..e6c7602
--- /dev/null
@@ -0,0 +1,9 @@
+// Copyright 2014 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 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
index 533a6933a6dbc247de548d4c919f005923072dee..4de0cb3276290b11ed7cc018bbf203df2ecef7f3 100644 (file)
@@ -2,7 +2,7 @@
 
 all:
        $(RUSTC) rlib.rs
-       $(RUSTC) dylib.rs && exit 1 || exit 0
+       $(RUSTC) dylib.rs
        $(RUSTC) rlib.rs --crate-type=dylib
        $(RUSTC) dylib.rs
        rm $(call DYLIB,rlib-*)
diff --git a/src/test/run-pass/issue-12133-1.rs b/src/test/run-pass/issue-12133-1.rs
new file mode 100644 (file)
index 0000000..9100367
--- /dev/null
@@ -0,0 +1,17 @@
+// Copyright 2014 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 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// aux-build:issue-12133-rlib.rs
+// aux-build:issue-12133-dylib.rs
+
+extern crate a = "issue-12133-rlib";
+extern crate b = "issue-12133-dylib";
+
+fn main() {}
diff --git a/src/test/run-pass/issue-12133-2.rs b/src/test/run-pass/issue-12133-2.rs
new file mode 100644 (file)
index 0000000..877d4f7
--- /dev/null
@@ -0,0 +1,18 @@
+// Copyright 2014 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 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// aux-build:issue-12133-rlib.rs
+// aux-build:issue-12133-dylib.rs
+// no-prefer-dynamic
+
+extern crate a = "issue-12133-rlib";
+extern crate b = "issue-12133-dylib";
+
+fn main() {}
diff --git a/src/test/run-pass/issue-12133-3.rs b/src/test/run-pass/issue-12133-3.rs
new file mode 100644 (file)
index 0000000..35f4d86
--- /dev/null
@@ -0,0 +1,17 @@
+// Copyright 2014 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 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// aux-build:issue-12133-rlib.rs
+// aux-build:issue-12133-dylib.rs
+// aux-build:issue-12133-dylib2.rs
+
+extern crate other = "issue-12133-dylib2";
+
+fn main() {}