auto merge of #13892 : alexcrichton/rust/mixing-rlib-dylib-deps, r=brson
Currently, rustc requires that a linkage be a product of 100% rlibs or 100% dylibs. This is to satisfy the requirement that each object appear at most once in the final output products. This is a bit limiting, and the upcoming libcore library cannot exist as a dylib, so these rules must change. The goal of this commit is to enable *some* use cases for mixing rlibs and dylibs, primarily libcore's use case. It is not targeted at allowing an exhaustive number of linkage flavors. There is a new dependency_format module in rustc which calculates what format each upstream library should be linked as in each output type of the current unit of compilation. The module itself contains many gory details about what's going on here. cc #10729
This commit is contained in:
commit
24f6f26e63
|
@ -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
|
||||
// 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.
|
||||
|
||||
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)
|
||||
}
|
||||
let data = if dylib {
|
||||
trans.crate_formats.get(&session::CrateTypeDylib)
|
||||
} else {
|
||||
trans.crate_formats.get(&session::CrateTypeExecutable)
|
||||
};
|
||||
|
||||
(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)
|
||||
}
|
||||
// Invoke get_used_crates to ensure that we get a topological sorting of
|
||||
// crates.
|
||||
let deps = sess.cstore.get_used_crates(cstore::RequireDynamic);
|
||||
|
||||
(Some(deps), _, _, _) => add_dynamic_crates(args, sess, deps),
|
||||
|
||||
(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));
|
||||
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())
|
||||
}
|
||||
cstore::RequireStatic => {
|
||||
add_static_crate(args, sess, tmpdir, cnum, src.rlib.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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 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();
|
||||
}
|
||||
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();
|
||||
}
|
||||
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());
|
||||
}
|
||||
}
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -45,7 +45,7 @@ pub struct crate_metadata {
|
|||
pub span: Span,
|
||||
}
|
||||
|
||||
#[deriving(Eq)]
|
||||
#[deriving(Show, Eq, Clone)]
|
||||
pub enum LinkagePreference {
|
||||
RequireDynamic,
|
||||
RequireStatic,
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
|
@ -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,
|
||||
})
|
||||
}
|
||||
|
|
|
@ -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()),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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";
|
||||
|
|
@ -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
|
|
@ -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;
|
|
@ -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;
|
|
@ -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() {}
|
|
@ -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() {}
|
||||
|
|
@ -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.
|
|
@ -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-*)
|
||||
|
|
|
@ -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";
|
||||
|
|
@ -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";
|
||||
|
|
@ -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() {}
|
Loading…
Reference in New Issue