about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2013-04-30 18:36:45 -0700
committerbors <bors@rust-lang.org>2013-04-30 18:36:45 -0700
commitcb527bff09e057ff6eeefe893c44e19b9d29a920 (patch)
treeb71d2187c10ccd2e14be0f19a48936b00a35ee64
parent7a857673ff76c966ceb061e3794b119e2e498c40 (diff)
parent2deefbe847e68c8fa749ae59d7e84e1a80eba452 (diff)
downloadrust-cb527bff09e057ff6eeefe893c44e19b9d29a920.tar.gz
rust-cb527bff09e057ff6eeefe893c44e19b9d29a920.zip
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.
-rw-r--r--src/librustc/back/link.rs175
-rw-r--r--src/librustc/driver/driver.rs123
-rw-r--r--src/librustc/driver/session.rs4
3 files changed, 171 insertions, 131 deletions
diff --git a/src/librustc/back/link.rs b/src/librustc/back/link.rs
index 8c442f2d5c9..adaffe5873d 100644
--- a/src/librustc/back/link.rs
+++ b/src/librustc/back/link.rs
@@ -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") &&
@@ -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;
@@ -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);
         }
     }
 
@@ -842,20 +882,20 @@ 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());
         }
     }
@@ -863,27 +903,27 @@ pub fn link_binary(sess: Session,
     // 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
@@ -891,44 +931,25 @@ pub fn link_binary(sess: Session,
     // 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));
 
-    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();
-    }
+    // Finally add all the linker arguments provided on the command line
+    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
diff --git a/src/librustc/driver/driver.rs b/src/librustc/driver/driver.rs
index 2e64c0c45bf..8c053c265fa 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>", ~"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"),
  ]
 }
 
diff --git a/src/librustc/driver/session.rs b/src/librustc/driver/session.rs
index 55c81e6d17b..ff623049f75 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<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: ~"",