auto merge of #6105 : Aatch/rust/linker-improv, r=pcwalton

Adds two extra flags: `--linker` which takes extra flags to pass to the linker, can be used multiple times and `--print-link-args` which prints out linker arguments. Currently `--print-link-args` needs execution to get past translation to get the `LinkMeta` data.

I haven't done tests or updated any extra documentation yet, so this pull request is currently here for review.
This commit is contained in:
bors 2013-04-30 18:36:45 -07:00
commit cb527bff09
3 changed files with 229 additions and 189 deletions

View file

@ -747,34 +747,6 @@ pub fn link_binary(sess: Session,
obj_filename: &Path,
out_filename: &Path,
lm: LinkMeta) {
// Converts a library file-stem into a cc -l argument
fn unlib(config: @session::config, stem: ~str) -> ~str {
if stem.starts_with("lib") &&
config.os != session::os_win32 {
stem.slice(3, stem.len()).to_owned()
} else {
stem
}
}
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.
@ -794,116 +766,21 @@ pub fn link_binary(sess: Session,
else { ~"cc" };
// The invocations of cc share some flags across platforms
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());
let lib_cmd;
let os = sess.targ_cfg.os;
if os == session::os_macos {
lib_cmd = ~"-dynamiclib";
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 {
lib_cmd = ~"-shared";
}
// # Crate linking
let cstore = sess.cstore;
for cstore::get_used_crate_files(cstore).each |cratepath| {
if cratepath.filetype() == Some(~".rlib") {
cc_args.push(cratepath.to_str());
loop;
}
let dir = cratepath.dirname();
if dir != ~"" { cc_args.push(~"-L" + dir); }
let libarg = unlib(sess.targ_cfg, cratepath.filestem().get());
cc_args.push(~"-l" + libarg);
}
let ula = cstore::get_used_link_args(cstore);
for ula.each |arg| { cc_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);
}
}
// # Extern library linking
// User-supplied library search paths (-L on the cammand line) These are
// the same paths used to find Rust crates, so some of them may have been
// added already by the previous crate linking code. This only allows them
// to be found at compile time so it is still entirely up to outside
// 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());
}
// The names of the extern libraries
let used_libs = cstore::get_used_libraries(cstore);
for used_libs.each |l| { cc_args.push(~"-l" + *l); }
if *sess.building_library {
cc_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/"
+ 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"]);
// 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");
}
else if sess.targ_cfg.os == session::os_android {
cc_args.push_all(~[~"-ldl", ~"-llog", ~"-lsupc++",
~"-lgnustl_shared"]);
cc_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"]);
}
// 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");
}
// Stack growth requires statically linking a __morestack function
cc_args.push(~"-lmorestack");
// Always want the runtime linked in
cc_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));
/*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);
@ -929,6 +806,150 @@ pub fn link_binary(sess: Session,
}
}
}
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") &&
config.os != session::os_win32 {
stem.slice(3, stem.len()).to_owned()
} else {
stem
}
}
let output = if *sess.building_library {
let long_libname = output_dll_filename(sess.targ_cfg.os, lm);
out_filename.dir_path().push(long_libname)
} else {
/*bad*/copy *out_filename
};
// 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();
let mut args = vec::append(~[stage], sess.targ_cfg.target_strs.cc_args);
args.push(~"-o");
args.push(output.to_str());
args.push(obj_filename.to_str());
let lib_cmd;
let os = sess.targ_cfg.os;
if os == session::os_macos {
lib_cmd = ~"-dynamiclib";
} else {
lib_cmd = ~"-shared";
}
// # Crate linking
let cstore = sess.cstore;
for cstore::get_used_crate_files(cstore).each |cratepath| {
if cratepath.filetype() == Some(~".rlib") {
args.push(cratepath.to_str());
loop;
}
let dir = cratepath.dirname();
if dir != ~"" { args.push(~"-L" + dir); }
let libarg = unlib(sess.targ_cfg, cratepath.filestem().get());
args.push(~"-l" + libarg);
}
let ula = cstore::get_used_link_args(cstore);
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| {
args.push(link_arg);
}
}
// # Extern library linking
// User-supplied library search paths (-L on the cammand line) These are
// the same paths used to find Rust crates, so some of them may have been
// added already by the previous crate linking code. This only allows them
// to be found at compile time so it is still entirely up to outside
// forces to make sure that library can be found at runtime.
for sess.opts.addl_lib_search_paths.each |path| {
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| { args.push(~"-l" + *l); }
if *sess.building_library {
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 {
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 {
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
args.push(~"-lm");
}
else if sess.targ_cfg.os == session::os_android {
args.push_all(~[~"-ldl", ~"-llog", ~"-lsupc++",
~"-lgnustl_shared"]);
args.push(~"-lm");
}
if sess.targ_cfg.os == session::os_freebsd {
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 {
args.push(~"-Wl,-no_compact_unwind");
}
// Stack growth requires statically linking a __morestack function
args.push(~"-lmorestack");
// Always want the runtime linked in
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
args.push_all(rpath::get_rpath_flags(sess, &output));
// Finally add all the linker arguments provided on the command line
args.push_all(sess.opts.linker_args);
return args;
}
//
// Local Variables:
// mode: rust

View file

@ -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) = {
@ -309,6 +308,11 @@ pub fn compile_rest(sess: Session,
};
if (sess.opts.debugging_opts & session::print_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 ||
@ -645,13 +649,21 @@ pub fn build_session_options(binary: @~str,
Some(s) => s
};
let addl_lib_search_paths =
getopts::opt_strs(matches, ~"L")
.map(|s| Path(*s));
let addl_lib_search_paths = getopts::opt_strs(matches, ~"L").map(|s| Path(*s));
let linker_args = getopts::opt_strs(matches, ~"link-args").flat_map( |a| {
let mut args = ~[];
for str::each_split_char(*a, ',') |arg| {
args.push(str::from_slice(arg));
}
args
});
let cfg = parse_cfgspecs(getopts::opt_strs(matches, ~"cfg"), demitter);
let test = opt_present(matches, ~"test");
let android_cross_path = getopts::opt_maybe_str(
matches, ~"android-cross-path");
let sopts = @session::options {
crate_type: crate_type,
is_static: static,
@ -664,6 +676,7 @@ pub fn build_session_options(binary: @~str,
jit: jit,
output_type: output_type,
addl_lib_search_paths: addl_lib_search_paths,
linker_args: linker_args,
maybe_sysroot: sysroot_opt,
target_triple: target,
target_feature: target_feature,
@ -737,62 +750,64 @@ pub fn parse_pretty(sess: Session, name: &str) -> pp_mode {
// rustc command line options
pub fn optgroups() -> ~[getopts::groups::OptGroup] {
~[
optflag(~"", ~"bin", ~"Compile an executable crate (default)"),
optflag(~"c", ~"", ~"Compile and assemble, but do not link"),
optmulti(~"", ~"cfg", ~"Configure the compilation
environment", ~"SPEC"),
optflag(~"", ~"emit-llvm",
~"Produce an LLVM bitcode file"),
optflag(~"h", ~"help",~"Display this message"),
optmulti(~"L", ~"", ~"Add a directory to the library search path",
~"PATH"),
optflag(~"", ~"lib", ~"Compile a library crate"),
optflag(~"", ~"ls", ~"List the symbols defined by a library crate"),
optflag(~"", ~"no-trans",
~"Run all passes except translation; no output"),
optflag(~"O", ~"", ~"Equivalent to --opt-level=2"),
optopt(~"o", ~"", ~"Write output to <filename>", ~"FILENAME"),
optopt(~"", ~"opt-level",
~"Optimize with possible levels 0-3", ~"LEVEL"),
optopt( ~"", ~"out-dir",
~"Write output to compiler-chosen filename
in <dir>", ~"DIR"),
optflag(~"", ~"parse-only",
~"Parse only; do not compile, assemble, or link"),
optflagopt(~"", ~"pretty",
~"Pretty-print the input instead of compiling;
optflag("", "bin", "Compile an executable crate (default)"),
optflag("c", "", "Compile and assemble, but do not link"),
optmulti("", "cfg", "Configure the compilation
environment", "SPEC"),
optflag("", "emit-llvm",
"Produce an LLVM bitcode file"),
optflag("h", "help","Display this message"),
optmulti("L", "", "Add a directory to the library search path",
"PATH"),
optflag("", "lib", "Compile a library crate"),
optmulti("", "link-args", "FLAGS is a comma-separated list of flags
passed to the linker", "FLAGS"),
optflag("", "ls", "List the symbols defined by a library crate"),
optflag("", "no-trans",
"Run all passes except translation; no output"),
optflag("O", "", "Equivalent to --opt-level=2"),
optopt("o", "", "Write output to <filename>", "FILENAME"),
optopt("", "opt-level",
"Optimize with possible levels 0-3", "LEVEL"),
optopt( "", "out-dir",
"Write output to compiler-chosen filename
in <dir>", "DIR"),
optflag("", "parse-only",
"Parse only; do not compile, assemble, or link"),
optflagopt("", "pretty",
"Pretty-print the input instead of compiling;
valid types are: normal (un-annotated source),
expanded (crates expanded),
typed (crates expanded, with type annotations),
or identified (fully parenthesized,
AST nodes and blocks with IDs)", ~"TYPE"),
optflag(~"S", ~"", ~"Compile only; do not assemble or link"),
optflag(~"", ~"save-temps",
~"Write intermediate files (.bc, .opt.bc, .o)
AST nodes and blocks with IDs)", "TYPE"),
optflag("S", "", "Compile only; do not assemble or link"),
optflag("", "save-temps",
"Write intermediate files (.bc, .opt.bc, .o)
in addition to normal output"),
optopt(~"", ~"sysroot",
~"Override the system root", ~"PATH"),
optflag(~"", ~"test", ~"Build a test harness"),
optopt(~"", ~"target",
~"Target triple cpu-manufacturer-kernel[-os]
optopt("", "sysroot",
"Override the system root", "PATH"),
optflag("", "test", "Build a test harness"),
optopt("", "target",
"Target triple cpu-manufacturer-kernel[-os]
to compile for (see chapter 3.4 of http://www.sourceware.org/autobook/
for detail)", ~"TRIPLE"),
optopt(~"", ~"target-feature",
~"Target specific attributes (llc -mattr=help
for detail)", ~"FEATURE"),
optopt(~"", ~"android-cross-path",
~"The path to the Android NDK", "PATH"),
optmulti(~"W", ~"warn",
~"Set lint warnings", ~"OPT"),
optmulti(~"A", ~"allow",
~"Set lint allowed", ~"OPT"),
optmulti(~"D", ~"deny",
~"Set lint denied", ~"OPT"),
optmulti(~"F", ~"forbid",
~"Set lint forbidden", ~"OPT"),
optmulti(~"Z", ~"", ~"Set internal debugging options", "FLAG"),
optflag( ~"v", ~"version",
~"Print version info and exit"),
for detail)", "TRIPLE"),
optopt("", "target-feature",
"Target specific attributes (llc -mattr=help
for detail)", "FEATURE"),
optopt("", "android-cross-path",
"The path to the Android NDK", "PATH"),
optmulti("W", "warn",
"Set lint warnings", "OPT"),
optmulti("A", "allow",
"Set lint allowed", "OPT"),
optmulti("D", "deny",
"Set lint denied", "OPT"),
optmulti("F", "forbid",
"Set lint forbidden", "OPT"),
optmulti("Z", "", "Set internal debugging options", "FLAG"),
optflag( "v", "version",
"Print version info and exit"),
]
}

View file

@ -63,6 +63,7 @@ pub static jit: uint = 1 << 19;
pub static debug_info: uint = 1 << 20;
pub static extra_debug_info: uint = 1 << 21;
pub static static: uint = 1 << 22;
pub static print_link_args: uint = 1 << 23;
pub fn debugging_opts_map() -> ~[(~str, ~str, uint)] {
~[(~"verbose", ~"in general, enable more debug printouts", verbose),
@ -90,6 +91,7 @@ pub fn debugging_opts_map() -> ~[(~str, ~str, uint)] {
(~"no-opt", ~"do not optimize, even if -O is passed", no_opt),
(~"no-monomorphic-collapse", ~"do not collapse template instantiations",
no_monomorphic_collapse),
(~"print-link-args", ~"Print the arguments passed to the linker", print_link_args),
(~"gc", ~"Garbage collect shared data (experimental)", gc),
(~"jit", ~"Execute using JIT (experimental)", jit),
(~"extra-debug-info", ~"Extra debugging info (experimental)",
@ -122,6 +124,7 @@ pub struct options {
jit: bool,
output_type: back::link::output_type,
addl_lib_search_paths: ~[Path],
linker_args: ~[~str],
maybe_sysroot: Option<Path>,
target_triple: ~str,
target_feature: ~str,
@ -299,6 +302,7 @@ pub fn basic_options() -> @options {
jit: false,
output_type: link::output_type_exe,
addl_lib_search_paths: ~[],
linker_args:~[],
maybe_sysroot: None,
target_triple: host_triple(),
target_feature: ~"",