diff --git a/src/librustc/back/link.rs b/src/librustc/back/link.rs index 8c442f2d5c9f..adaffe5873da 100644 --- a/src/librustc/back/link.rs +++ b/src/librustc/back/link.rs @@ -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 diff --git a/src/librustc/driver/driver.rs b/src/librustc/driver/driver.rs index 2e64c0c45bff..8c053c265fa2 100644 --- a/src/librustc/driver/driver.rs +++ b/src/librustc/driver/driver.rs @@ -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"), - optopt(~"", ~"opt-level", - ~"Optimize with possible levels 0-3", ~"LEVEL"), - optopt( ~"", ~"out-dir", - ~"Write output to compiler-chosen filename - in ", ~"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"), + optopt("", "opt-level", + "Optimize with possible levels 0-3", "LEVEL"), + optopt( "", "out-dir", + "Write output to compiler-chosen filename + in ", "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"), ] } diff --git a/src/librustc/driver/session.rs b/src/librustc/driver/session.rs index 55c81e6d17b2..ff623049f758 100644 --- a/src/librustc/driver/session.rs +++ b/src/librustc/driver/session.rs @@ -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, 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: ~"",