about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/librustc/back/link.rs220
-rw-r--r--src/librustc/driver/driver.rs15
-rw-r--r--src/librustc/driver/session.rs4
-rw-r--r--src/librustc/lib.rs1
-rw-r--r--src/librustc/metadata/common.rs2
-rw-r--r--src/librustc/metadata/csearch.rs8
-rw-r--r--src/librustc/metadata/cstore.rs2
-rw-r--r--src/librustc/metadata/decoder.rs26
-rw-r--r--src/librustc/metadata/encoder.rs19
-rw-r--r--src/librustc/middle/dependency_format.rs229
-rw-r--r--src/librustc/middle/trans/base.rs2
-rw-r--r--src/librustc/middle/ty.rs4
-rw-r--r--src/test/auxiliary/issue-12133-dylib2.rs (renamed from src/test/compile-fail/issue-12133-3.rs)5
-rw-r--r--src/test/run-make/mixing-formats/Makefile74
-rw-r--r--src/test/run-make/mixing-formats/bar1.rs11
-rw-r--r--src/test/run-make/mixing-formats/bar2.rs11
-rw-r--r--src/test/run-make/mixing-formats/baz.rs13
-rw-r--r--src/test/run-make/mixing-formats/baz2.rs15
-rw-r--r--src/test/run-make/mixing-formats/foo.rs9
-rw-r--r--src/test/run-make/mixing-libs/Makefile2
-rw-r--r--src/test/run-pass/issue-12133-1.rs (renamed from src/test/compile-fail/issue-12133-1.rs)2
-rw-r--r--src/test/run-pass/issue-12133-2.rs (renamed from src/test/compile-fail/issue-12133-2.rs)2
-rw-r--r--src/test/run-pass/issue-12133-3.rs17
23 files changed, 543 insertions, 150 deletions
diff --git a/src/librustc/back/link.rs b/src/librustc/back/link.rs
index 8d021a9a33e..ac6bb12e410 100644
--- a/src/librustc/back/link.rs
+++ b/src/librustc/back/link.rs
@@ -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 add_upstream_rust_crates(args: &mut Vec<~str>, sess: &Session,
         }
     }
 
-    // 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);
     }
 }
 
diff --git a/src/librustc/driver/driver.rs b/src/librustc/driver/driver.rs
index 332ecfb53db..247c41a6069 100644
--- a/src/librustc/driver/driver.rs
+++ b/src/librustc/driver/driver.rs
@@ -24,6 +24,7 @@ use metadata::cstore::CStore;
 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;
@@ -382,7 +383,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,
     }
 }
 
@@ -393,6 +394,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
@@ -400,11 +402,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
diff --git a/src/librustc/driver/session.rs b/src/librustc/driver/session.rs
index 58ccfeab73d..285a977ad6e 100644
--- a/src/librustc/driver/session.rs
+++ b/src/librustc/driver/session.rs
@@ -125,7 +125,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,
@@ -166,7 +166,7 @@ pub enum EntryFnType {
     EntryNone,
 }
 
-#[deriving(Eq, Ord, Clone, TotalOrd, TotalEq)]
+#[deriving(Eq, Ord, Clone, TotalOrd, TotalEq, Hash)]
 pub enum CrateType {
     CrateTypeExecutable,
     CrateTypeDylib,
diff --git a/src/librustc/lib.rs b/src/librustc/lib.rs
index a94715bdaa5..2dca3ff3c7a 100644
--- a/src/librustc/lib.rs
+++ b/src/librustc/lib.rs
@@ -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 {
diff --git a/src/librustc/metadata/common.rs b/src/librustc/metadata/common.rs
index 8f71ac4ebf7..839b0e08d37 100644
--- a/src/librustc/metadata/common.rs
+++ b/src/librustc/metadata/common.rs
@@ -203,6 +203,8 @@ pub static tag_macro_def: uint = 0x8d;
 
 pub static tag_crate_triple: uint = 0x66;
 
+pub static tag_dylib_dependency_formats: uint = 0x67;
+
 #[deriving(Clone, Show)]
 pub struct LinkMeta {
     pub crateid: CrateId,
diff --git a/src/librustc/metadata/csearch.rs b/src/librustc/metadata/csearch.rs
index 77b4871ea8b..ea764f132b7 100644
--- a/src/librustc/metadata/csearch.rs
+++ b/src/librustc/metadata/csearch.rs
@@ -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)
+}
diff --git a/src/librustc/metadata/cstore.rs b/src/librustc/metadata/cstore.rs
index 8e731a98dd7..390df9c0809 100644
--- a/src/librustc/metadata/cstore.rs
+++ b/src/librustc/metadata/cstore.rs
@@ -45,7 +45,7 @@ pub struct crate_metadata {
     pub span: Span,
 }
 
-#[deriving(Eq)]
+#[deriving(Show, Eq, Clone)]
 pub enum LinkagePreference {
     RequireDynamic,
     RequireStatic,
diff --git a/src/librustc/metadata/decoder.rs b/src/librustc/metadata/decoder.rs
index 93fc8b26e77..302bba2f56b 100644
--- a/src/librustc/metadata/decoder.rs
+++ b/src/librustc/metadata/decoder.rs
@@ -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;
+}
diff --git a/src/librustc/metadata/encoder.rs b/src/librustc/metadata/encoder.rs
index 214fa3ee04b..684745daa3c 100644
--- a/src/librustc/metadata/encoder.rs
+++ b/src/librustc/metadata/encoder.rs
@@ -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 @@ fn encode_metadata_inner(wr: &mut MemWriter, parms: EncodeParams, krate: &Crate)
     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
index 00000000000..7b5ef2bf2be
--- /dev/null
+++ b/src/librustc/middle/dependency_format.rs
@@ -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
+    }
+}
diff --git a/src/librustc/middle/trans/base.rs b/src/librustc/middle/trans/base.rs
index 949937c9037..c002c47d35d 100644
--- a/src/librustc/middle/trans/base.rs
+++ b/src/librustc/middle/trans/base.rs
@@ -2235,6 +2235,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,
@@ -2243,5 +2244,6 @@ pub fn trans_crate(krate: ast::Crate,
         metadata_module: metadata_module,
         metadata: metadata,
         reachable: reachable,
+        crate_formats: formats,
     })
 }
diff --git a/src/librustc/middle/ty.rs b/src/librustc/middle/ty.rs
index c114097fefa..89d93bb3111 100644
--- a/src/librustc/middle/ty.rs
+++ b/src/librustc/middle/ty.rs
@@ -15,6 +15,7 @@ use driver::session::Session;
 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/compile-fail/issue-12133-3.rs b/src/test/auxiliary/issue-12133-dylib2.rs
index 9b78130b703..a978eacbfd1 100644
--- a/src/test/compile-fail/issue-12133-3.rs
+++ b/src/test/auxiliary/issue-12133-dylib2.rs
@@ -8,13 +8,10 @@
 // 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
index 00000000000..e665cb2e535
--- /dev/null
+++ b/src/test/run-make/mixing-formats/Makefile
@@ -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
index 00000000000..4b4916fe96d
--- /dev/null
+++ b/src/test/run-make/mixing-formats/bar1.rs
@@ -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
index 00000000000..4b4916fe96d
--- /dev/null
+++ b/src/test/run-make/mixing-formats/bar2.rs
@@ -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
index 00000000000..3fb90f6a854
--- /dev/null
+++ b/src/test/run-make/mixing-formats/baz.rs
@@ -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
index 00000000000..4cfa65330bd
--- /dev/null
+++ b/src/test/run-make/mixing-formats/baz2.rs
@@ -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
index 00000000000..e6c76025738
--- /dev/null
+++ b/src/test/run-make/mixing-formats/foo.rs
@@ -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.
diff --git a/src/test/run-make/mixing-libs/Makefile b/src/test/run-make/mixing-libs/Makefile
index 533a6933a6d..4de0cb32762 100644
--- a/src/test/run-make/mixing-libs/Makefile
+++ b/src/test/run-make/mixing-libs/Makefile
@@ -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/compile-fail/issue-12133-1.rs b/src/test/run-pass/issue-12133-1.rs
index 63a0352e2ef..91003672f3a 100644
--- a/src/test/compile-fail/issue-12133-1.rs
+++ b/src/test/run-pass/issue-12133-1.rs
@@ -11,8 +11,6 @@
 // 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";
 
diff --git a/src/test/compile-fail/issue-12133-2.rs b/src/test/run-pass/issue-12133-2.rs
index 3f42d28bf27..877d4f706e9 100644
--- a/src/test/compile-fail/issue-12133-2.rs
+++ b/src/test/run-pass/issue-12133-2.rs
@@ -12,8 +12,6 @@
 // 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";
 
diff --git a/src/test/run-pass/issue-12133-3.rs b/src/test/run-pass/issue-12133-3.rs
new file mode 100644
index 00000000000..35f4d86efe0
--- /dev/null
+++ b/src/test/run-pass/issue-12133-3.rs
@@ -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() {}