@@ -884,10 +884,10 @@ fn link_binary_output(sess: &Session,
884
884
link_staticlib ( sess, & obj_filename, & out_filename) ;
885
885
}
886
886
session:: CrateTypeExecutable => {
887
- link_natively ( sess, trans , false , & obj_filename, & out_filename) ;
887
+ link_natively ( sess, false , & obj_filename, & out_filename) ;
888
888
}
889
889
session:: CrateTypeDylib => {
890
- link_natively ( sess, trans , true , & obj_filename, & out_filename) ;
890
+ link_natively ( sess, true , & obj_filename, & out_filename) ;
891
891
}
892
892
}
893
893
@@ -1037,13 +1037,13 @@ fn link_staticlib(sess: &Session, obj_filename: &Path, out_filename: &Path) {
1037
1037
//
1038
1038
// This will invoke the system linker/cc to create the resulting file. This
1039
1039
// links to all upstream files as well.
1040
- fn link_natively ( sess : & Session , trans : & CrateTranslation , dylib : bool ,
1041
- obj_filename : & Path , out_filename : & Path ) {
1040
+ fn link_natively ( sess : & Session , dylib : bool , obj_filename : & Path ,
1041
+ out_filename : & Path ) {
1042
1042
let tmpdir = TempDir :: new ( "rustc" ) . expect ( "needs a temp dir" ) ;
1043
1043
// The invocations of cc share some flags across platforms
1044
1044
let cc_prog = get_cc_prog ( sess) ;
1045
1045
let mut cc_args = sess. targ_cfg . target_strs . cc_args . clone ( ) ;
1046
- cc_args. push_all_move ( link_args ( sess, dylib, tmpdir. path ( ) , trans ,
1046
+ cc_args. push_all_move ( link_args ( sess, dylib, tmpdir. path ( ) ,
1047
1047
obj_filename, out_filename) ) ;
1048
1048
if ( sess. opts . debugging_opts & session:: PRINT_LINK_ARGS ) != 0 {
1049
1049
println ! ( "{} link args: '{}'" , cc_prog, cc_args. connect( "' '" ) ) ;
@@ -1092,7 +1092,6 @@ fn link_natively(sess: &Session, trans: &CrateTranslation, dylib: bool,
1092
1092
fn link_args ( sess : & Session ,
1093
1093
dylib : bool ,
1094
1094
tmpdir : & Path ,
1095
- trans : & CrateTranslation ,
1096
1095
obj_filename : & Path ,
1097
1096
out_filename : & Path ) -> Vec < ~str > {
1098
1097
@@ -1252,7 +1251,7 @@ fn link_args(sess: &Session,
1252
1251
// this kind of behavior is pretty platform specific and generally not
1253
1252
// recommended anyway, so I don't think we're shooting ourself in the foot
1254
1253
// much with that.
1255
- add_upstream_rust_crates ( & mut args, sess, dylib, tmpdir, trans ) ;
1254
+ add_upstream_rust_crates ( & mut args, sess, dylib, tmpdir) ;
1256
1255
add_local_native_libraries ( & mut args, sess) ;
1257
1256
add_upstream_native_libraries ( & mut args, sess) ;
1258
1257
@@ -1362,44 +1361,73 @@ fn add_local_native_libraries(args: &mut Vec<~str>, sess: &Session) {
1362
1361
// dependencies will be linked when producing the final output (instead of
1363
1362
// the intermediate rlib version)
1364
1363
fn add_upstream_rust_crates ( args : & mut Vec < ~str > , sess : & Session ,
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.
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.
1370
1374
//
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.
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
+ }
1374
1390
1375
- let data = if dylib {
1376
- trans. crate_formats . get ( & session:: CrateTypeDylib )
1377
- } else {
1378
- trans. crate_formats . get ( & session:: CrateTypeExecutable )
1379
- } ;
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
+ }
1380
1399
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 ( ) )
1397
- }
1398
- cstore:: RequireStatic => {
1399
- add_static_crate ( args, sess, tmpdir, cnum, src. rlib . unwrap ( ) )
1400
+ ( Some ( deps) , _, _, _) => add_dynamic_crates ( args, sess, deps) ,
1401
+
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) ) ;
1400
1410
}
1401
1411
}
1402
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) ;
1429
+ }
1430
+ }
1403
1431
}
1404
1432
1405
1433
// Converts a library file-stem into a cc -l argument
@@ -1411,64 +1439,82 @@ fn add_upstream_rust_crates(args: &mut Vec<~str>, sess: &Session,
1411
1439
}
1412
1440
}
1413
1441
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
+
1414
1456
// Adds the static "rlib" versions of all crates to the command line.
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 ( ) ;
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
+ }
1444
1488
}
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 ( ) ) ;
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 ) ;
1495
+ }
1496
+ } ) ;
1497
+ } else {
1498
+ args . push ( cratepath . as_str ( ) . unwrap ( ) . to_owned ( ) ) ;
1499
+ }
1456
1500
}
1457
1501
}
1458
1502
1459
1503
// Same thing as above, but for dynamic crates instead of static crates.
1460
- fn add_dynamic_crate ( args : & mut Vec < ~str > , sess : & Session ,
1461
- cratepath : Path ) {
1504
+ fn add_dynamic_crates ( args : & mut Vec < ~str > , sess : & Session ,
1505
+ crates : Vec < ( ast :: CrateNum , Path ) > ) {
1462
1506
// If we're performing LTO, then it should have been previously required
1463
1507
// that all upstream rust dependencies were available in an rlib format.
1464
1508
assert ! ( !sess. lto( ) ) ;
1465
1509
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) ;
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
+ }
1472
1518
}
1473
1519
}
1474
1520
0 commit comments