use session;
use session::config;
-use metadata::cstore;
-use metadata::csearch;
-use middle::ty;
+use middle::cstore::CrateStore;
+use middle::cstore::LinkagePreference::{self, RequireStatic, RequireDynamic};
use util::nodemap::FnvHashMap;
/// A list of dependencies for a certain crate type.
/// 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>>;
+pub type DependencyList = Vec<Linkage>;
/// 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().iter() {
- fmts.insert(ty, calculate_type(&tcx.sess, ty));
+#[derive(Copy, Clone, PartialEq, Debug)]
+pub enum Linkage {
+ NotLinked,
+ IncludedFromDylib,
+ Static,
+ Dynamic,
+}
+
+pub fn calculate(sess: &session::Session) {
+ let mut fmts = sess.dependency_formats.borrow_mut();
+ for &ty in sess.crate_types.borrow().iter() {
+ let linkage = calculate_type(sess, ty);
+ verify_ok(sess, &linkage);
+ fmts.insert(ty, linkage);
}
- tcx.sess.abort_if_errors();
+ sess.abort_if_errors();
}
fn calculate_type(sess: &session::Session,
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 }
+ for cnum in sess.cstore.crates() {
+ let src = sess.cstore.used_crate_source(cnum);
+ if src.rlib.is_some() { continue }
sess.err(&format!("dependency `{}` not found in rlib format",
- data.name));
- });
+ sess.cstore.crate_name(cnum)));
+ }
return Vec::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();
+ for cnum in sess.cstore.crates() {
+ let name = sess.cstore.crate_name(cnum);
+ let src = sess.cstore.used_crate_source(cnum);
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);
+ info!("adding dylib: {}", name);
+ add_library(sess, cnum, RequireDynamic, &mut formats);
+ let deps = sess.cstore.dylib_dependency_formats(cnum);
for &(depnum, style) in &deps {
- debug!("adding {:?}: {}", style,
- sess.cstore.get_crate_data(depnum).name.clone());
+ info!("adding {:?}: {}", style,
+ sess.cstore.crate_name(depnum));
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,
+ let last_crate = sess.cstore.crates().len() as ast::CrateNum;
+ let mut ret = (1..last_crate+1).map(|cnum| {
+ match formats.get(&cnum) {
+ Some(&RequireDynamic) => Linkage::Dynamic,
+ Some(&RequireStatic) => Linkage::IncludedFromDylib,
+ None => Linkage::NotLinked,
}
}).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) {
+ //
+ // If the crate hasn't been included yet and it's not actually required
+ // (e.g. it's an allocator) then we skip it here as well.
+ for cnum in sess.cstore.crates() {
+ let src = sess.cstore.used_crate_source(cnum);
+ if src.dylib.is_none() &&
+ !formats.contains_key(&cnum) &&
+ sess.cstore.is_explicitly_linked(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);
+ info!("adding staticlib: {}", sess.cstore.crate_name(cnum));
+ add_library(sess, cnum, RequireStatic, &mut formats);
+ ret[cnum as usize - 1] = Linkage::Static;
}
- });
+ }
+
+ // We've gotten this far because we're emitting some form of a final
+ // artifact which means that we're going to need an allocator of some form.
+ // No allocator may have been required or linked so far, so activate one
+ // here if one isn't set.
+ activate_allocator(sess, &mut ret);
// 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
// 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();
+ let cnum = (cnum + 1) as ast::CrateNum;
+ let src = sess.cstore.used_crate_source(cnum);
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);
+ Linkage::NotLinked |
+ Linkage::IncludedFromDylib => {}
+ Linkage::Static if src.rlib.is_some() => continue,
+ Linkage::Dynamic if src.dylib.is_some() => continue,
+ kind => {
+ let kind = match kind {
+ Linkage::Static => "rlib",
+ _ => "dylib",
+ };
+ let name = sess.cstore.crate_name(cnum);
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",
- }));
+ name, kind));
}
}
}
fn add_library(sess: &session::Session,
cnum: ast::CrateNum,
- link: cstore::LinkagePreference,
- m: &mut FnvHashMap<ast::CrateNum, cstore::LinkagePreference>) {
+ link: LinkagePreference,
+ m: &mut FnvHashMap<ast::CrateNum, LinkagePreference>) {
match m.get(&cnum) {
Some(&link2) => {
// If the linkages differ, then we'd have two copies of the library
//
// 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");
+ if link2 != link || link == RequireStatic {
+ sess.struct_err(&format!("cannot satisfy dependencies so `{}` only \
+ shows up once", sess.cstore.crate_name(cnum)))
+ .help("having upstream crates all available in one format \
+ will likely make this go away")
+ .emit();
}
}
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
+ let crates = sess.cstore.used_crates(RequireStatic);
+ if !crates.iter().by_ref().all(|&(_, ref p)| p.is_some()) {
+ return None
+ }
+
+ // All crates are available in an rlib format, so we're just going to link
+ // everything in explicitly so long as it's actually required.
+ let last_crate = sess.cstore.crates().len() as ast::CrateNum;
+ let mut ret = (1..last_crate+1).map(|cnum| {
+ if sess.cstore.is_explicitly_linked(cnum) {
+ Linkage::Static
+ } else {
+ Linkage::NotLinked
+ }
+ }).collect::<Vec<_>>();
+
+ // Our allocator may not have been activated as it's not flagged with
+ // explicitly_linked, so flag it here if necessary.
+ activate_allocator(sess, &mut ret);
+
+ Some(ret)
+}
+
+// Given a list of how to link upstream dependencies so far, ensure that an
+// allocator is activated. This will not do anything if one was transitively
+// included already (e.g. via a dylib or explicitly so).
+//
+// If an allocator was not found then we're guaranteed the metadata::creader
+// module has injected an allocator dependency (not listed as a required
+// dependency) in the session's `injected_allocator` field. If this field is not
+// set then this compilation doesn't actually need an allocator and we can also
+// skip this step entirely.
+fn activate_allocator(sess: &session::Session, list: &mut DependencyList) {
+ let mut allocator_found = false;
+ for (i, slot) in list.iter().enumerate() {
+ let cnum = (i + 1) as ast::CrateNum;
+ if !sess.cstore.is_allocator(cnum) {
+ continue
+ }
+ if let Linkage::NotLinked = *slot {
+ continue
+ }
+ allocator_found = true;
+ }
+ if !allocator_found {
+ if let Some(injected_allocator) = sess.injected_allocator.get() {
+ let idx = injected_allocator as usize - 1;
+ assert_eq!(list[idx], Linkage::NotLinked);
+ list[idx] = Linkage::Static;
+ }
+ }
+}
+
+// After the linkage for a crate has been determined we need to verify that
+// there's only going to be one allocator in the output.
+fn verify_ok(sess: &session::Session, list: &[Linkage]) {
+ if list.len() == 0 {
+ return
+ }
+ let mut allocator = None;
+ for (i, linkage) in list.iter().enumerate() {
+ let cnum = (i + 1) as ast::CrateNum;
+ if !sess.cstore.is_allocator(cnum) {
+ continue
+ }
+ if let Linkage::NotLinked = *linkage {
+ continue
+ }
+ if let Some(prev_alloc) = allocator {
+ let prev_name = sess.cstore.crate_name(prev_alloc);
+ let cur_name = sess.cstore.crate_name(cnum);
+ sess.err(&format!("cannot link together two \
+ allocators: {} and {}",
+ prev_name, cur_name));
+ }
+ allocator = Some(cnum);
}
}