diff --git a/src/librustc/back/link.rs b/src/librustc/back/link.rs index 8d021a9a33ebb..ac6bb12e410bb 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 > - { - 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 b579a9b9c64c1..f2e3c106bf6be 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; @@ -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, 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 diff --git a/src/librustc/driver/session.rs b/src/librustc/driver/session.rs index 950e6bd8ee833..4af50d1ae4611 100644 --- a/src/librustc/driver/session.rs +++ b/src/librustc/driver/session.rs @@ -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 , + pub crate_types: Vec, 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, diff --git a/src/librustc/lib.rs b/src/librustc/lib.rs index c9a40cffe587c..e165242397a0c 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 8f71ac4ebf7ef..839b0e08d3795 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 77b4871ea8b6f..ea764f132b72d 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 8e731a98dd7e7..390df9c080989 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 93fc8b26e7749..302bba2f56b6c 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 214fa3ee04bd9..684745daa3cfa 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::>(); + 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 0000000000000..7b5ef2bf2be62 --- /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 or the MIT license +// , 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>; + +/// 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; + +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::>(); + + // 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) { + 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 { + 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 daed00a32f21d..09d4f526af189 100644 --- a/src/librustc/middle/trans/base.rs +++ b/src/librustc/middle/trans/base.rs @@ -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, }) } diff --git a/src/librustc/middle/ty.rs b/src/librustc/middle/ty.rs index bb4ac63529f7d..d223170459d89 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, } 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 similarity index 80% rename from src/test/compile-fail/issue-12133-3.rs rename to src/test/auxiliary/issue-12133-dylib2.rs index 9b78130b7039b..a978eacbfd1df 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 0000000000000..e665cb2e5350d --- /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 0000000000000..4b4916fe96d63 --- /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 or the MIT license +// , 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 0000000000000..4b4916fe96d63 --- /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 or the MIT license +// , 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 0000000000000..3fb90f6a854f6 --- /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 or the MIT license +// , 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 0000000000000..4cfa65330bd19 --- /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 or the MIT license +// , 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 0000000000000..e6c760257380c --- /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 or the MIT license +// , 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 533a6933a6dbc..4de0cb3276290 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 similarity index 88% rename from src/test/compile-fail/issue-12133-1.rs rename to src/test/run-pass/issue-12133-1.rs index 63a0352e2ef3a..91003672f3a52 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 similarity index 88% rename from src/test/compile-fail/issue-12133-2.rs rename to src/test/run-pass/issue-12133-2.rs index 3f42d28bf27a5..877d4f706e964 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 0000000000000..35f4d86efe055 --- /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 or the MIT license +// , 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() {}