Skip to content

Commit

Permalink
Adds '--print-link-args' that outputs linker arguments that would be …
Browse files Browse the repository at this point in the history
…used
  • Loading branch information
James Miller committed Apr 29, 2013
1 parent 00ede34 commit e75203c
Show file tree
Hide file tree
Showing 3 changed files with 114 additions and 80 deletions.
176 changes: 97 additions & 79 deletions src/librustc/back/link.rs
Original file line number Diff line number Diff line change
Expand Up @@ -747,6 +747,71 @@ pub fn link_binary(sess: Session,
obj_filename: &Path,
out_filename: &Path,
lm: LinkMeta) {
// In the future, FreeBSD will use clang as default compiler.
// It would be flexible to use cc (system's default C compiler)
// instead of hard-coded gcc.
// For win32, there is no cc command,
// so we add a condition to make it use gcc.
let cc_prog: ~str = if sess.targ_cfg.os == session::os_android {
match &sess.opts.android_cross_path {
&Some(copy path) => {
fmt!("%s/bin/arm-linux-androideabi-gcc", path)
}
&None => {
sess.fatal(~"need Android NDK path for linking \
(--android-cross-path)")
}
}
} else if sess.targ_cfg.os == session::os_win32 { ~"gcc" }
else { ~"cc" };
// The invocations of cc share some flags across platforms


let output = if *sess.building_library {
let long_libname = output_dll_filename(sess.targ_cfg.os, lm);
debug!("link_meta.name: %s", lm.name);
debug!("long_libname: %s", long_libname);
debug!("out_filename: %s", out_filename.to_str());
debug!("dirname(out_filename): %s", out_filename.dir_path().to_str());

out_filename.dir_path().push(long_libname)
} else {
/*bad*/copy *out_filename
};

debug!("output: %s", output.to_str());
let mut cc_args = link_args(sess, obj_filename, out_filename, lm);
debug!("%s link args: %s", cc_prog, str::connect(cc_args, ~" "));
// We run 'cc' here
let prog = run::program_output(cc_prog, cc_args);
if 0 != prog.status {
sess.err(fmt!("linking with `%s` failed with code %d",
cc_prog, prog.status));
sess.note(fmt!("%s arguments: %s",
cc_prog, str::connect(cc_args, ~" ")));
sess.note(prog.err + prog.out);
sess.abort_if_errors();
}

// Clean up on Darwin
if sess.targ_cfg.os == session::os_macos {
run::run_program(~"dsymutil", ~[output.to_str()]);
}

// Remove the temporary object file if we aren't saving temps
if !sess.opts.save_temps {
if ! os::remove_file(obj_filename) {
sess.warn(fmt!("failed to delete object file `%s`",
obj_filename.to_str()));
}
}
}

pub fn link_args(sess: Session,
obj_filename: &Path,
out_filename: &Path,
lm:LinkMeta) -> ~[~str] {

// Converts a library file-stem into a cc -l argument
fn unlib(config: @session::config, stem: ~str) -> ~str {
if stem.starts_with("lib") &&
Expand All @@ -757,48 +822,23 @@ pub fn link_binary(sess: Session,
}
}


let output = if *sess.building_library {
let long_libname = output_dll_filename(sess.targ_cfg.os, lm);
debug!("link_meta.name: %s", lm.name);
debug!("long_libname: %s", long_libname);
debug!("out_filename: %s", out_filename.to_str());
debug!("dirname(out_filename): %s", out_filename.dir_path().to_str());

out_filename.dir_path().push(long_libname)
} else {
/*bad*/copy *out_filename
};

debug!("output: %s", output.to_str());

// The default library location, we need this to find the runtime.
// The location of crates will be determined as needed.
let stage: ~str = ~"-L" + sess.filesearch.get_target_lib_path().to_str();

// In the future, FreeBSD will use clang as default compiler.
// It would be flexible to use cc (system's default C compiler)
// instead of hard-coded gcc.
// For win32, there is no cc command,
// so we add a condition to make it use gcc.
let cc_prog: ~str = if sess.targ_cfg.os == session::os_android {
match &sess.opts.android_cross_path {
&Some(copy path) => {
fmt!("%s/bin/arm-linux-androideabi-gcc", path)
}
&None => {
sess.fatal(~"need Android NDK path for linking \
(--android-cross-path)")
}
}
} else if sess.targ_cfg.os == session::os_win32 { ~"gcc" }
else { ~"cc" };
// The invocations of cc share some flags across platforms
let mut args = vec::append(~[stage], sess.targ_cfg.target_strs.cc_args);

let mut cc_args =
vec::append(~[stage], sess.targ_cfg.target_strs.cc_args);
cc_args.push(~"-o");
cc_args.push(output.to_str());
cc_args.push(obj_filename.to_str());
args.push(~"-o");
args.push(output.to_str());
args.push(obj_filename.to_str());

let lib_cmd;
let os = sess.targ_cfg.os;
Expand All @@ -813,23 +853,23 @@ pub fn link_binary(sess: Session,
let cstore = sess.cstore;
for cstore::get_used_crate_files(cstore).each |cratepath| {
if cratepath.filetype() == Some(~".rlib") {
cc_args.push(cratepath.to_str());
args.push(cratepath.to_str());
loop;
}
let dir = cratepath.dirname();
if dir != ~"" { cc_args.push(~"-L" + dir); }
if dir != ~"" { args.push(~"-L" + dir); }
let libarg = unlib(sess.targ_cfg, cratepath.filestem().get());
cc_args.push(~"-l" + libarg);
args.push(~"-l" + libarg);
}

let ula = cstore::get_used_link_args(cstore);
for ula.each |arg| { cc_args.push(/*bad*/copy *arg); }
for ula.each |arg| { args.push(/*bad*/copy *arg); }

// Add all the link args for external crates.
do cstore::iter_crate_data(cstore) |crate_num, _| {
let link_args = csearch::get_link_args_for_crate(cstore, crate_num);
do vec::consume(link_args) |_, link_arg| {
cc_args.push(link_arg);
args.push(link_arg);
}
}

Expand All @@ -842,96 +882,74 @@ pub fn link_binary(sess: Session,
// forces to make sure that library can be found at runtime.

for sess.opts.addl_lib_search_paths.each |path| {
cc_args.push(~"-L" + path.to_str());
args.push(~"-L" + path.to_str());
}

// The names of the extern libraries
let used_libs = cstore::get_used_libraries(cstore);
for used_libs.each |l| { cc_args.push(~"-l" + *l); }
for used_libs.each |l| { args.push(~"-l" + *l); }

if *sess.building_library {
cc_args.push(lib_cmd);
args.push(lib_cmd);

// On mac we need to tell the linker to let this library
// be rpathed
if sess.targ_cfg.os == session::os_macos {
cc_args.push(~"-Wl,-install_name,@rpath/"
args.push(~"-Wl,-install_name,@rpath/"
+ output.filename().get());
}
}
// On linux librt and libdl are an indirect dependencies via rustrt,
// and binutils 2.22+ won't add them automatically
if sess.targ_cfg.os == session::os_linux {
cc_args.push_all(~[~"-lrt", ~"-ldl"]);
args.push_all(~[~"-lrt", ~"-ldl"]);

// LLVM implements the `frem` instruction as a call to `fmod`,
// which lives in libm. Similar to above, on some linuxes we
// have to be explicit about linking to it. See #2510
cc_args.push(~"-lm");
args.push(~"-lm");
}
else if sess.targ_cfg.os == session::os_android {
cc_args.push_all(~[~"-ldl", ~"-llog", ~"-lsupc++",
args.push_all(~[~"-ldl", ~"-llog", ~"-lsupc++",
~"-lgnustl_shared"]);
cc_args.push(~"-lm");
args.push(~"-lm");
}

if sess.targ_cfg.os == session::os_freebsd {
cc_args.push_all(~[~"-pthread", ~"-lrt",
~"-L/usr/local/lib", ~"-lexecinfo",
~"-L/usr/local/lib/gcc46",
~"-L/usr/local/lib/gcc44", ~"-lstdc++",
~"-Wl,-z,origin",
~"-Wl,-rpath,/usr/local/lib/gcc46",
~"-Wl,-rpath,/usr/local/lib/gcc44"]);
args.push_all(~[~"-pthread", ~"-lrt",
~"-L/usr/local/lib", ~"-lexecinfo",
~"-L/usr/local/lib/gcc46",
~"-L/usr/local/lib/gcc44", ~"-lstdc++",
~"-Wl,-z,origin",
~"-Wl,-rpath,/usr/local/lib/gcc46",
~"-Wl,-rpath,/usr/local/lib/gcc44"]);
}

// OS X 10.6 introduced 'compact unwind info', which is produced by the
// linker from the dwarf unwind info. Unfortunately, it does not seem to
// understand how to unwind our __morestack frame, so we have to turn it
// off. This has impacted some other projects like GHC.
if sess.targ_cfg.os == session::os_macos {
cc_args.push(~"-Wl,-no_compact_unwind");
args.push(~"-Wl,-no_compact_unwind");
}

// Stack growth requires statically linking a __morestack function
cc_args.push(~"-lmorestack");
args.push(~"-lmorestack");

// Always want the runtime linked in
cc_args.push(~"-lrustrt");
args.push(~"-lrustrt");

// FIXME (#2397): At some point we want to rpath our guesses as to where
// extern libraries might live, based on the addl_lib_search_paths
cc_args.push_all(rpath::get_rpath_flags(sess, &output));
args.push_all(rpath::get_rpath_flags(sess, &output));

// Finally add all the linker arguments provided on the command line
cc_args.push_all(sess.opts.linker_args);

debug!("%s link args: %s", cc_prog, str::connect(cc_args, ~" "));
// We run 'cc' here
let prog = run::program_output(cc_prog, cc_args);
if 0 != prog.status {
sess.err(fmt!("linking with `%s` failed with code %d",
cc_prog, prog.status));
sess.note(fmt!("%s arguments: %s",
cc_prog, str::connect(cc_args, ~" ")));
sess.note(prog.err + prog.out);
sess.abort_if_errors();
}
args.push_all(sess.opts.linker_args);

// Clean up on Darwin
if sess.targ_cfg.os == session::os_macos {
run::run_program(~"dsymutil", ~[output.to_str()]);
}

// Remove the temporary object file if we aren't saving temps
if !sess.opts.save_temps {
if ! os::remove_file(obj_filename) {
sess.warn(fmt!("failed to delete object file `%s`",
obj_filename.to_str()));
}
}
return args;
}

//
// Local Variables:
// mode: rust
Expand Down
13 changes: 12 additions & 1 deletion src/librustc/driver/driver.rs
Original file line number Diff line number Diff line change
Expand Up @@ -234,7 +234,6 @@ pub fn compile_rest(sess: Session,
let rp_set = time(time_passes, ~"region parameterization inference", ||
middle::region::determine_rp_in_crate(sess, ast_map, def_map, crate));


let outputs = outputs.get();

let (llmod, link_meta) = {
Expand Down Expand Up @@ -309,6 +308,11 @@ pub fn compile_rest(sess: Session,

};

if (sess.opts.output_info & session::out_link_args) > 0 {
io::println(str::connect(link::link_args(sess,
&outputs.obj_filename, &outputs.out_filename, link_meta), " "));
}

// NB: Android hack
if sess.targ_cfg.arch == abi::Arm &&
(sess.opts.output_type == link::output_type_object ||
Expand Down Expand Up @@ -659,6 +663,12 @@ pub fn build_session_options(binary: @~str,
let test = opt_present(matches, ~"test");
let android_cross_path = getopts::opt_maybe_str(
matches, ~"android-cross-path");

let mut output_info = 0;
if opt_present(matches, "print-link-args") {
output_info |= session::out_link_args;
}

let sopts = @session::options {
crate_type: crate_type,
is_static: static,
Expand All @@ -681,6 +691,7 @@ pub fn build_session_options(binary: @~str,
parse_only: parse_only,
no_trans: no_trans,
debugging_opts: debugging_opts,
output_info: output_info,
android_cross_path: android_cross_path
};
return sopts;
Expand Down
5 changes: 5 additions & 0 deletions src/librustc/driver/session.rs
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,9 @@ pub fn debugging_opts_map() -> ~[(~str, ~str, uint)] {
]
}

// Information output flags
pub static out_link_args : uint = 1 << 0;

#[deriving(Eq)]
pub enum OptLevel {
No, // -O0
Expand Down Expand Up @@ -136,6 +139,7 @@ pub struct options {
parse_only: bool,
no_trans: bool,
debugging_opts: uint,
output_info: uint,
android_cross_path: Option<~str>
}

Expand Down Expand Up @@ -310,6 +314,7 @@ pub fn basic_options() -> @options {
parse_only: false,
no_trans: false,
debugging_opts: 0u,
output_info: 0u,
android_cross_path: None
}
}
Expand Down

0 comments on commit e75203c

Please sign in to comment.