Skip to content

Commit 24f6f26

Browse files
committed
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
2 parents b299231 + a82f921 commit 24f6f26

File tree

23 files changed

+543
-150
lines changed

23 files changed

+543
-150
lines changed

src/librustc/back/link.rs

+87-133
Original file line numberDiff line numberDiff line change
@@ -884,10 +884,10 @@ fn link_binary_output(sess: &Session,
884884
link_staticlib(sess, &obj_filename, &out_filename);
885885
}
886886
session::CrateTypeExecutable => {
887-
link_natively(sess, false, &obj_filename, &out_filename);
887+
link_natively(sess, trans, false, &obj_filename, &out_filename);
888888
}
889889
session::CrateTypeDylib => {
890-
link_natively(sess, true, &obj_filename, &out_filename);
890+
link_natively(sess, trans, true, &obj_filename, &out_filename);
891891
}
892892
}
893893

@@ -1037,13 +1037,13 @@ fn link_staticlib(sess: &Session, obj_filename: &Path, out_filename: &Path) {
10371037
//
10381038
// This will invoke the system linker/cc to create the resulting file. This
10391039
// links to all upstream files as well.
1040-
fn link_natively(sess: &Session, dylib: bool, obj_filename: &Path,
1041-
out_filename: &Path) {
1040+
fn link_natively(sess: &Session, trans: &CrateTranslation, dylib: bool,
1041+
obj_filename: &Path, out_filename: &Path) {
10421042
let tmpdir = TempDir::new("rustc").expect("needs a temp dir");
10431043
// The invocations of cc share some flags across platforms
10441044
let cc_prog = get_cc_prog(sess);
10451045
let mut cc_args = sess.targ_cfg.target_strs.cc_args.clone();
1046-
cc_args.push_all_move(link_args(sess, dylib, tmpdir.path(),
1046+
cc_args.push_all_move(link_args(sess, dylib, tmpdir.path(), trans,
10471047
obj_filename, out_filename));
10481048
if (sess.opts.debugging_opts & session::PRINT_LINK_ARGS) != 0 {
10491049
println!("{} link args: '{}'", cc_prog, cc_args.connect("' '"));
@@ -1092,6 +1092,7 @@ fn link_natively(sess: &Session, dylib: bool, obj_filename: &Path,
10921092
fn link_args(sess: &Session,
10931093
dylib: bool,
10941094
tmpdir: &Path,
1095+
trans: &CrateTranslation,
10951096
obj_filename: &Path,
10961097
out_filename: &Path) -> Vec<~str> {
10971098

@@ -1251,7 +1252,7 @@ fn link_args(sess: &Session,
12511252
// this kind of behavior is pretty platform specific and generally not
12521253
// recommended anyway, so I don't think we're shooting ourself in the foot
12531254
// much with that.
1254-
add_upstream_rust_crates(&mut args, sess, dylib, tmpdir);
1255+
add_upstream_rust_crates(&mut args, sess, dylib, tmpdir, trans);
12551256
add_local_native_libraries(&mut args, sess);
12561257
add_upstream_native_libraries(&mut args, sess);
12571258

@@ -1361,73 +1362,44 @@ fn add_local_native_libraries(args: &mut Vec<~str>, sess: &Session) {
13611362
// dependencies will be linked when producing the final output (instead of
13621363
// the intermediate rlib version)
13631364
fn add_upstream_rust_crates(args: &mut Vec<~str>, sess: &Session,
1364-
dylib: bool, tmpdir: &Path) {
1365-
1366-
// As a limitation of the current implementation, we require that everything
1367-
// must be static or everything must be dynamic. The reasons for this are a
1368-
// little subtle, but as with staticlibs and rlibs, the goal is to prevent
1369-
// duplicate copies of the same library showing up. For example, a static
1370-
// immediate dependency might show up as an upstream dynamic dependency and
1371-
// we currently have no way of knowing that. We know that all dynamic
1372-
// libraries require dynamic dependencies (see above), so it's satisfactory
1373-
// to include either all static libraries or all dynamic libraries.
1365+
dylib: bool, tmpdir: &Path,
1366+
trans: &CrateTranslation) {
1367+
// All of the heavy lifting has previously been accomplished by the
1368+
// dependency_format module of the compiler. This is just crawling the
1369+
// output of that module, adding crates as necessary.
13741370
//
1375-
// With this limitation, we expose a compiler default linkage type and an
1376-
// option to reverse that preference. The current behavior looks like:
1377-
//
1378-
// * If a dylib is being created, upstream dependencies must be dylibs
1379-
// * If nothing else is specified, static linking is preferred
1380-
// * If the -C prefer-dynamic flag is given, dynamic linking is preferred
1381-
// * If one form of linking fails, the second is also attempted
1382-
// * If both forms fail, then we emit an error message
1383-
1384-
let dynamic = get_deps(&sess.cstore, cstore::RequireDynamic);
1385-
let statik = get_deps(&sess.cstore, cstore::RequireStatic);
1386-
match (dynamic, statik, sess.opts.cg.prefer_dynamic, dylib) {
1387-
(_, Some(deps), false, false) => {
1388-
add_static_crates(args, sess, tmpdir, deps)
1389-
}
1390-
1391-
(None, Some(deps), true, false) => {
1392-
// If you opted in to dynamic linking and we decided to emit a
1393-
// static output, you should probably be notified of such an event!
1394-
sess.warn("dynamic linking was preferred, but dependencies \
1395-
could not all be found in a dylib format.");
1396-
sess.warn("linking statically instead, using rlibs");
1397-
add_static_crates(args, sess, tmpdir, deps)
1398-
}
1371+
// Linking to a rlib involves just passing it to the linker (the linker
1372+
// will slurp up the object files inside), and linking to a dynamic library
1373+
// involves just passing the right -l flag.
13991374

1400-
(Some(deps), _, _, _) => add_dynamic_crates(args, sess, deps),
1375+
let data = if dylib {
1376+
trans.crate_formats.get(&session::CrateTypeDylib)
1377+
} else {
1378+
trans.crate_formats.get(&session::CrateTypeExecutable)
1379+
};
14011380

1402-
(None, _, _, true) => {
1403-
sess.err("dylib output requested, but some depenencies could not \
1404-
be found in the dylib format");
1405-
let deps = sess.cstore.get_used_crates(cstore::RequireDynamic);
1406-
for (cnum, path) in deps.move_iter() {
1407-
if path.is_some() { continue }
1408-
let name = sess.cstore.get_crate_data(cnum).name.clone();
1409-
sess.note(format!("dylib not found: {}", name));
1381+
// Invoke get_used_crates to ensure that we get a topological sorting of
1382+
// crates.
1383+
let deps = sess.cstore.get_used_crates(cstore::RequireDynamic);
1384+
1385+
for &(cnum, _) in deps.iter() {
1386+
// We may not pass all crates through to the linker. Some crates may
1387+
// appear statically in an existing dylib, meaning we'll pick up all the
1388+
// symbols from the dylib.
1389+
let kind = match *data.get(cnum as uint - 1) {
1390+
Some(t) => t,
1391+
None => continue
1392+
};
1393+
let src = sess.cstore.get_used_crate_source(cnum).unwrap();
1394+
match kind {
1395+
cstore::RequireDynamic => {
1396+
add_dynamic_crate(args, sess, src.dylib.unwrap())
14101397
}
1411-
}
1412-
1413-
(None, None, pref, false) => {
1414-
let (pref, name) = if pref {
1415-
sess.err("dynamic linking is preferred, but dependencies were \
1416-
not found in either dylib or rlib format");
1417-
(cstore::RequireDynamic, "dylib")
1418-
} else {
1419-
sess.err("dependencies were not all found in either dylib or \
1420-
rlib format");
1421-
(cstore::RequireStatic, "rlib")
1422-
};
1423-
sess.note(format!("dependencies not found in the `{}` format",
1424-
name));
1425-
for (cnum, path) in sess.cstore.get_used_crates(pref).move_iter() {
1426-
if path.is_some() { continue }
1427-
let name = sess.cstore.get_crate_data(cnum).name.clone();
1428-
sess.note(name);
1398+
cstore::RequireStatic => {
1399+
add_static_crate(args, sess, tmpdir, cnum, src.rlib.unwrap())
14291400
}
14301401
}
1402+
14311403
}
14321404

14331405
// 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,
14391411
}
14401412
}
14411413

1442-
// Attempts to find all dependencies with a certain linkage preference,
1443-
// returning `None` if not all libraries could be found with that
1444-
// preference.
1445-
fn get_deps(cstore: &cstore::CStore, preference: cstore::LinkagePreference)
1446-
-> Option<Vec<(ast::CrateNum, Path)> >
1447-
{
1448-
let crates = cstore.get_used_crates(preference);
1449-
if crates.iter().all(|&(_, ref p)| p.is_some()) {
1450-
Some(crates.move_iter().map(|(a, b)| (a, b.unwrap())).collect())
1451-
} else {
1452-
None
1453-
}
1454-
}
1455-
14561414
// Adds the static "rlib" versions of all crates to the command line.
1457-
fn add_static_crates(args: &mut Vec<~str>, sess: &Session, tmpdir: &Path,
1458-
crates: Vec<(ast::CrateNum, Path)>) {
1459-
for (cnum, cratepath) in crates.move_iter() {
1460-
// When performing LTO on an executable output, all of the
1461-
// bytecode from the upstream libraries has already been
1462-
// included in our object file output. We need to modify all of
1463-
// the upstream archives to remove their corresponding object
1464-
// file to make sure we don't pull the same code in twice.
1465-
//
1466-
// We must continue to link to the upstream archives to be sure
1467-
// to pull in native static dependencies. As the final caveat,
1468-
// on linux it is apparently illegal to link to a blank archive,
1469-
// so if an archive no longer has any object files in it after
1470-
// we remove `lib.o`, then don't link against it at all.
1471-
//
1472-
// If we're not doing LTO, then our job is simply to just link
1473-
// against the archive.
1474-
if sess.lto() {
1475-
let name = sess.cstore.get_crate_data(cnum).name.clone();
1476-
time(sess.time_passes(), format!("altering {}.rlib", name),
1477-
(), |()| {
1478-
let dst = tmpdir.join(cratepath.filename().unwrap());
1479-
match fs::copy(&cratepath, &dst) {
1480-
Ok(..) => {}
1481-
Err(e) => {
1482-
sess.err(format!("failed to copy {} to {}: {}",
1483-
cratepath.display(),
1484-
dst.display(),
1485-
e));
1486-
sess.abort_if_errors();
1487-
}
1488-
}
1489-
let dst_str = dst.as_str().unwrap().to_owned();
1490-
let mut archive = Archive::open(sess, dst);
1491-
archive.remove_file(format!("{}.o", name));
1492-
let files = archive.files();
1493-
if files.iter().any(|s| s.ends_with(".o")) {
1494-
args.push(dst_str);
1415+
fn add_static_crate(args: &mut Vec<~str>, sess: &Session, tmpdir: &Path,
1416+
cnum: ast::CrateNum, cratepath: Path) {
1417+
// When performing LTO on an executable output, all of the
1418+
// bytecode from the upstream libraries has already been
1419+
// included in our object file output. We need to modify all of
1420+
// the upstream archives to remove their corresponding object
1421+
// file to make sure we don't pull the same code in twice.
1422+
//
1423+
// We must continue to link to the upstream archives to be sure
1424+
// to pull in native static dependencies. As the final caveat,
1425+
// on linux it is apparently illegal to link to a blank archive,
1426+
// so if an archive no longer has any object files in it after
1427+
// we remove `lib.o`, then don't link against it at all.
1428+
//
1429+
// If we're not doing LTO, then our job is simply to just link
1430+
// against the archive.
1431+
if sess.lto() {
1432+
let name = sess.cstore.get_crate_data(cnum).name.clone();
1433+
time(sess.time_passes(), format!("altering {}.rlib", name),
1434+
(), |()| {
1435+
let dst = tmpdir.join(cratepath.filename().unwrap());
1436+
match fs::copy(&cratepath, &dst) {
1437+
Ok(..) => {}
1438+
Err(e) => {
1439+
sess.err(format!("failed to copy {} to {}: {}",
1440+
cratepath.display(),
1441+
dst.display(),
1442+
e));
1443+
sess.abort_if_errors();
14951444
}
1496-
});
1497-
} else {
1498-
args.push(cratepath.as_str().unwrap().to_owned());
1499-
}
1445+
}
1446+
let dst_str = dst.as_str().unwrap().to_owned();
1447+
let mut archive = Archive::open(sess, dst);
1448+
archive.remove_file(format!("{}.o", name));
1449+
let files = archive.files();
1450+
if files.iter().any(|s| s.ends_with(".o")) {
1451+
args.push(dst_str);
1452+
}
1453+
});
1454+
} else {
1455+
args.push(cratepath.as_str().unwrap().to_owned());
15001456
}
15011457
}
15021458

15031459
// Same thing as above, but for dynamic crates instead of static crates.
1504-
fn add_dynamic_crates(args: &mut Vec<~str>, sess: &Session,
1505-
crates: Vec<(ast::CrateNum, Path)> ) {
1460+
fn add_dynamic_crate(args: &mut Vec<~str>, sess: &Session,
1461+
cratepath: Path) {
15061462
// If we're performing LTO, then it should have been previously required
15071463
// that all upstream rust dependencies were available in an rlib format.
15081464
assert!(!sess.lto());
15091465

1510-
for (_, cratepath) in crates.move_iter() {
1511-
// Just need to tell the linker about where the library lives and
1512-
// what its name is
1513-
let dir = cratepath.dirname_str().unwrap();
1514-
if !dir.is_empty() { args.push("-L" + dir); }
1515-
let libarg = unlib(&sess.targ_cfg, cratepath.filestem_str().unwrap());
1516-
args.push("-l" + libarg);
1517-
}
1466+
// Just need to tell the linker about where the library lives and
1467+
// what its name is
1468+
let dir = cratepath.dirname_str().unwrap();
1469+
if !dir.is_empty() { args.push("-L" + dir); }
1470+
let libarg = unlib(&sess.targ_cfg, cratepath.filestem_str().unwrap());
1471+
args.push("-l" + libarg);
15181472
}
15191473
}
15201474

src/librustc/driver/driver.rs

+10-5
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ use metadata::cstore::CStore;
2424
use metadata::creader::Loader;
2525
use metadata;
2626
use middle::{trans, freevars, kind, ty, typeck, lint, reachable};
27+
use middle::dependency_format;
2728
use middle;
2829
use util::common::time;
2930
use util::ppaux;
@@ -382,7 +383,7 @@ pub fn phase_3_run_analysis_passes(sess: Session,
382383
ty_cx: ty_cx,
383384
exported_items: exported_items,
384385
public_items: public_items,
385-
reachable: reachable_map
386+
reachable: reachable_map,
386387
}
387388
}
388389

@@ -393,18 +394,22 @@ pub struct CrateTranslation {
393394
pub link: LinkMeta,
394395
pub metadata: Vec<u8>,
395396
pub reachable: Vec<~str>,
397+
pub crate_formats: dependency_format::Dependencies,
396398
}
397399

398400
/// Run the translation phase to LLVM, after which the AST and analysis can
399401
/// be discarded.
400402
pub fn phase_4_translate_to_llvm(krate: ast::Crate,
401403
analysis: CrateAnalysis,
402404
outputs: &OutputFilenames) -> (ty::ctxt, CrateTranslation) {
403-
// Option dance to work around the lack of stack once closures.
404405
let time_passes = analysis.ty_cx.sess.time_passes();
405-
let mut analysis = Some(analysis);
406-
time(time_passes, "translation", krate, |krate|
407-
trans::base::trans_crate(krate, analysis.take_unwrap(), outputs))
406+
407+
time(time_passes, "resolving dependency formats", (), |_|
408+
dependency_format::calculate(&analysis.ty_cx));
409+
410+
// Option dance to work around the lack of stack once closures.
411+
time(time_passes, "translation", (krate, analysis), |(krate, analysis)|
412+
trans::base::trans_crate(krate, analysis, outputs))
408413
}
409414

410415
/// Run LLVM itself, producing a bitcode file, assembly file or object file

src/librustc/driver/session.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,7 @@ pub enum DebugInfoLevel {
125125
pub struct Options {
126126
// The crate config requested for the session, which may be combined
127127
// with additional crate configurations during the compile process
128-
pub crate_types: Vec<CrateType> ,
128+
pub crate_types: Vec<CrateType>,
129129

130130
pub gc: bool,
131131
pub optimize: OptLevel,
@@ -166,7 +166,7 @@ pub enum EntryFnType {
166166
EntryNone,
167167
}
168168

169-
#[deriving(Eq, Ord, Clone, TotalOrd, TotalEq)]
169+
#[deriving(Eq, Ord, Clone, TotalOrd, TotalEq, Hash)]
170170
pub enum CrateType {
171171
CrateTypeExecutable,
172172
CrateTypeDylib,

src/librustc/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,7 @@ pub mod middle {
9393
pub mod cfg;
9494
pub mod dead;
9595
pub mod expr_use_visitor;
96+
pub mod dependency_format;
9697
}
9798

9899
pub mod front {

src/librustc/metadata/common.rs

+2
Original file line numberDiff line numberDiff line change
@@ -203,6 +203,8 @@ pub static tag_macro_def: uint = 0x8d;
203203

204204
pub static tag_crate_triple: uint = 0x66;
205205

206+
pub static tag_dylib_dependency_formats: uint = 0x67;
207+
206208
#[deriving(Clone, Show)]
207209
pub struct LinkMeta {
208210
pub crateid: CrateId,

src/librustc/metadata/csearch.rs

+8
Original file line numberDiff line numberDiff line change
@@ -284,3 +284,11 @@ pub fn get_tuple_struct_definition_if_ctor(cstore: &cstore::CStore,
284284
let cdata = cstore.get_crate_data(def_id.krate);
285285
decoder::get_tuple_struct_definition_if_ctor(&*cdata, def_id.node)
286286
}
287+
288+
pub fn get_dylib_dependency_formats(cstore: &cstore::CStore,
289+
cnum: ast::CrateNum)
290+
-> Vec<(ast::CrateNum, cstore::LinkagePreference)>
291+
{
292+
let cdata = cstore.get_crate_data(cnum);
293+
decoder::get_dylib_dependency_formats(&*cdata)
294+
}

src/librustc/metadata/cstore.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ pub struct crate_metadata {
4545
pub span: Span,
4646
}
4747

48-
#[deriving(Eq)]
48+
#[deriving(Show, Eq, Clone)]
4949
pub enum LinkagePreference {
5050
RequireDynamic,
5151
RequireStatic,

0 commit comments

Comments
 (0)