1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241
// 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 syntax::ast; use session; use session::config; use metadata::cstore; use metadata::csearch; use middle::ty; use util::nodemap::FnvHashMap; /// 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 = FnvHashMap<config::CrateType, DependencyList>; pub fn calculate(tcx: &ty::ctxt) { let mut fmts = tcx.dependency_formats.borrow_mut(); for &ty in &*tcx.sess.crate_types.borrow() { fmts.insert(ty, calculate_type(&tcx.sess, ty)); } tcx.sess.abort_if_errors(); } fn calculate_type(sess: &session::Session, ty: config::CrateType) -> DependencyList { match ty { // If the global prefer_dynamic switch is turned off, first attempt // static linkage (this can fail). config::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. config::CrateTypeRlib => return Vec::new(), // Staticlibs must have all static dependencies. If any fail to be // found, we generate some nice pretty errors. config::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(); } // Generating a dylib without `-C prefer-dynamic` means that we're going // to try to eagerly statically link all dependencies. This is normally // done for end-product dylibs, not intermediate products. config::CrateTypeDylib if !sess.opts.cg.prefer_dynamic => { match attempt_static(sess) { Some(v) => return v, None => {} } } // Everything else falls through below config::CrateTypeExecutable | config::CrateTypeDylib => {}, } let mut formats = FnvHashMap(); // 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() { debug!("adding dylib: {}", data.name); add_library(sess, cnum, cstore::RequireDynamic, &mut formats); let deps = csearch::get_dylib_dependency_formats(&sess.cstore, cnum); for &(depnum, style) in &deps { debug!("adding {:?}: {}", style, sess.cstore.get_crate_data(depnum).name.clone()); add_library(sess, depnum, style, &mut formats); } } }); // Collect what we've got so far in the return vector. let mut ret = (1..sess.cstore.next_crate_num()).map(|i| { match formats.get(&i).cloned() { 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()); debug!("adding staticlib: {}", data.name); add_library(sess, cnum, cstore::RequireStatic, &mut formats); ret[cnum as usize - 1] = Some(cstore::RequireStatic); } }); // 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 FnvHashMap<ast::CrateNum, cstore::LinkagePreference>) { match m.get(&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.help("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().by_ref().all(|&(_, ref p)| p.is_some()) { Some(crates.into_iter().map(|_| Some(cstore::RequireStatic)).collect()) } else { None } }