| use libc::{c_int, c_uint, c_char}; |
| use driver::session; |
| use session::session; |
| use lib::llvm::llvm; |
| use syntax::attr; |
| use middle::ty; |
| use metadata::{encoder, cstore}; |
| use middle::trans::common::crate_ctxt; |
| use metadata::common::link_meta; |
| use std::map::HashMap; |
| use std::sha1::sha1; |
| use syntax::ast; |
| use syntax::print::pprust; |
| use lib::llvm::{ModuleRef, mk_pass_manager, mk_target_data, True, False, |
| PassManagerRef, FileType}; |
| use metadata::filesearch; |
| use syntax::ast_map::{path, path_mod, path_name}; |
| use io::{Writer, WriterUtil}; |
| |
| enum output_type { |
| output_type_none, |
| output_type_bitcode, |
| output_type_assembly, |
| output_type_llvm_assembly, |
| output_type_object, |
| output_type_exe, |
| } |
| |
| impl output_type : cmp::Eq { |
| pure fn eq(other: &output_type) -> bool { |
| (self as uint) == ((*other) as uint) |
| } |
| pure fn ne(other: &output_type) -> bool { !self.eq(other) } |
| } |
| |
| fn llvm_err(sess: session, msg: ~str) -> ! unsafe { |
| let cstr = llvm::LLVMRustGetLastError(); |
| if cstr == ptr::null() { |
| sess.fatal(msg); |
| } else { sess.fatal(msg + ~": " + str::raw::from_c_str(cstr)); } |
| } |
| |
| fn WriteOutputFile(sess:session, |
| PM: lib::llvm::PassManagerRef, M: ModuleRef, |
| Triple: *c_char, |
| // FIXME: When #2334 is fixed, change |
| // c_uint to FileType |
| Output: *c_char, FileType: c_uint, |
| OptLevel: c_int, |
| EnableSegmentedStacks: bool) { |
| let result = llvm::LLVMRustWriteOutputFile( |
| PM, M, Triple, Output, FileType, OptLevel, EnableSegmentedStacks); |
| if (!result) { |
| llvm_err(sess, ~"Could not write output"); |
| } |
| } |
| |
| mod jit { |
| #[legacy_exports]; |
| #[nolink] |
| #[abi = "rust-intrinsic"] |
| extern mod rusti { |
| #[legacy_exports]; |
| fn morestack_addr() -> *(); |
| } |
| |
| struct Closure { |
| code: *(), |
| env: *(), |
| } |
| |
| fn exec(sess: session, |
| pm: PassManagerRef, |
| m: ModuleRef, |
| opt: c_int, |
| stacks: bool) unsafe { |
| let manager = llvm::LLVMRustPrepareJIT(rusti::morestack_addr()); |
| |
| // We need to tell JIT where to resolve all linked |
| // symbols from. The equivalent of -lstd, -lcore, etc. |
| // By default the JIT will resolve symbols from the std and |
| // core linked into rustc. We don't want that, |
| // incase the user wants to use an older std library. |
| |
| let cstore = sess.cstore; |
| for cstore::get_used_crate_files(cstore).each |cratepath| { |
| let path = cratepath.to_str(); |
| |
| debug!("linking: %s", path); |
| |
| let _: () = str::as_c_str( |
| path, |
| |buf_t| { |
| if !llvm::LLVMRustLoadCrate(manager, buf_t) { |
| llvm_err(sess, ~"Could not link"); |
| } |
| debug!("linked: %s", path); |
| }); |
| } |
| |
| // The execute function will return a void pointer |
| // to the _rust_main function. We can do closure |
| // magic here to turn it straight into a callable rust |
| // closure. It will also cleanup the memory manager |
| // for us. |
| |
| let entry = llvm::LLVMRustExecuteJIT(manager, |
| pm, m, opt, stacks); |
| |
| if ptr::is_null(entry) { |
| llvm_err(sess, ~"Could not JIT"); |
| } else { |
| let closure = Closure { |
| code: entry, |
| env: ptr::null() |
| }; |
| let func: fn(++argv: ~[~str]) = cast::transmute(move closure); |
| |
| func(~[sess.opts.binary]); |
| } |
| } |
| } |
| |
| mod write { |
| #[legacy_exports]; |
| fn is_object_or_assembly_or_exe(ot: output_type) -> bool { |
| if ot == output_type_assembly || ot == output_type_object || |
| ot == output_type_exe { |
| return true; |
| } |
| return false; |
| } |
| |
| fn run_passes(sess: session, llmod: ModuleRef, output: &Path) { |
| let opts = sess.opts; |
| if sess.time_llvm_passes() { llvm::LLVMRustEnableTimePasses(); } |
| let mut pm = mk_pass_manager(); |
| let td = mk_target_data( |
| sess.targ_cfg.target_strs.data_layout); |
| llvm::LLVMAddTargetData(td.lltd, pm.llpm); |
| // FIXME (#2812): run the linter here also, once there are llvm-c |
| // bindings for it. |
| |
| // Generate a pre-optimization intermediate file if -save-temps was |
| // specified. |
| |
| |
| if opts.save_temps { |
| match opts.output_type { |
| output_type_bitcode => { |
| if opts.optimize != session::No { |
| let filename = output.with_filetype("no-opt.bc"); |
| str::as_c_str(filename.to_str(), |buf| { |
| llvm::LLVMWriteBitcodeToFile(llmod, buf) |
| }); |
| } |
| } |
| _ => { |
| let filename = output.with_filetype("bc"); |
| str::as_c_str(filename.to_str(), |buf| { |
| llvm::LLVMWriteBitcodeToFile(llmod, buf) |
| }); |
| } |
| } |
| } |
| if !sess.no_verify() { llvm::LLVMAddVerifierPass(pm.llpm); } |
| // FIXME (#2396): This is mostly a copy of the bits of opt's -O2 that |
| // are available in the C api. |
| // Also: We might want to add optimization levels like -O1, -O2, |
| // -Os, etc |
| // Also: Should we expose and use the pass lists used by the opt |
| // tool? |
| |
| if opts.optimize != session::No { |
| let fpm = mk_pass_manager(); |
| llvm::LLVMAddTargetData(td.lltd, fpm.llpm); |
| |
| let FPMB = llvm::LLVMPassManagerBuilderCreate(); |
| llvm::LLVMPassManagerBuilderSetOptLevel(FPMB, 2u as c_uint); |
| llvm::LLVMPassManagerBuilderPopulateFunctionPassManager(FPMB, |
| fpm.llpm); |
| llvm::LLVMPassManagerBuilderDispose(FPMB); |
| |
| llvm::LLVMRunPassManager(fpm.llpm, llmod); |
| let mut threshold = 225; |
| if opts.optimize == session::Aggressive { threshold = 275; } |
| |
| let MPMB = llvm::LLVMPassManagerBuilderCreate(); |
| llvm::LLVMPassManagerBuilderSetOptLevel(MPMB, |
| opts.optimize as c_uint); |
| llvm::LLVMPassManagerBuilderSetSizeLevel(MPMB, False); |
| llvm::LLVMPassManagerBuilderSetDisableUnitAtATime(MPMB, False); |
| llvm::LLVMPassManagerBuilderSetDisableUnrollLoops(MPMB, False); |
| llvm::LLVMPassManagerBuilderSetDisableSimplifyLibCalls(MPMB, |
| False); |
| |
| if threshold != 0u { |
| llvm::LLVMPassManagerBuilderUseInlinerWithThreshold |
| (MPMB, threshold as c_uint); |
| } |
| llvm::LLVMPassManagerBuilderPopulateModulePassManager(MPMB, |
| pm.llpm); |
| |
| llvm::LLVMPassManagerBuilderDispose(MPMB); |
| } |
| if !sess.no_verify() { llvm::LLVMAddVerifierPass(pm.llpm); } |
| if is_object_or_assembly_or_exe(opts.output_type) || opts.jit { |
| let LLVMOptNone = 0 as c_int; // -O0 |
| let LLVMOptLess = 1 as c_int; // -O1 |
| let LLVMOptDefault = 2 as c_int; // -O2, -Os |
| let LLVMOptAggressive = 3 as c_int; // -O3 |
| |
| let mut CodeGenOptLevel = match opts.optimize { |
| session::No => LLVMOptNone, |
| session::Less => LLVMOptLess, |
| session::Default => LLVMOptDefault, |
| session::Aggressive => LLVMOptAggressive |
| }; |
| |
| if opts.jit { |
| // If we are using JIT, go ahead and create and |
| // execute the engine now. |
| // JIT execution takes ownership of the module, |
| // so don't dispose and return. |
| |
| jit::exec(sess, pm.llpm, llmod, CodeGenOptLevel, true); |
| |
| if sess.time_llvm_passes() { |
| llvm::LLVMRustPrintPassTimings(); |
| } |
| return; |
| } |
| |
| let mut FileType; |
| if opts.output_type == output_type_object || |
| opts.output_type == output_type_exe { |
| FileType = lib::llvm::ObjectFile; |
| } else { FileType = lib::llvm::AssemblyFile; } |
| // Write optimized bitcode if --save-temps was on. |
| |
| if opts.save_temps { |
| // Always output the bitcode file with --save-temps |
| |
| let filename = output.with_filetype("opt.bc"); |
| llvm::LLVMRunPassManager(pm.llpm, llmod); |
| str::as_c_str(filename.to_str(), |buf| { |
| llvm::LLVMWriteBitcodeToFile(llmod, buf) |
| }); |
| pm = mk_pass_manager(); |
| // Save the assembly file if -S is used |
| |
| if opts.output_type == output_type_assembly { |
| let _: () = str::as_c_str( |
| sess.targ_cfg.target_strs.target_triple, |
| |buf_t| { |
| str::as_c_str(output.to_str(), |buf_o| { |
| WriteOutputFile( |
| sess, |
| pm.llpm, |
| llmod, |
| buf_t, |
| buf_o, |
| lib::llvm::AssemblyFile as c_uint, |
| CodeGenOptLevel, |
| true) |
| }) |
| }); |
| } |
| |
| |
| // Save the object file for -c or --save-temps alone |
| // This .o is needed when an exe is built |
| if opts.output_type == output_type_object || |
| opts.output_type == output_type_exe { |
| let _: () = str::as_c_str( |
| sess.targ_cfg.target_strs.target_triple, |
| |buf_t| { |
| str::as_c_str(output.to_str(), |buf_o| { |
| WriteOutputFile( |
| sess, |
| pm.llpm, |
| llmod, |
| buf_t, |
| buf_o, |
| lib::llvm::ObjectFile as c_uint, |
| CodeGenOptLevel, |
| true) |
| }) |
| }); |
| } |
| } else { |
| // If we aren't saving temps then just output the file |
| // type corresponding to the '-c' or '-S' flag used |
| |
| let _: () = str::as_c_str( |
| sess.targ_cfg.target_strs.target_triple, |
| |buf_t| { |
| str::as_c_str(output.to_str(), |buf_o| { |
| WriteOutputFile( |
| sess, |
| pm.llpm, |
| llmod, |
| buf_t, |
| buf_o, |
| FileType as c_uint, |
| CodeGenOptLevel, |
| true) |
| }) |
| }); |
| } |
| // Clean up and return |
| |
| llvm::LLVMDisposeModule(llmod); |
| if sess.time_llvm_passes() { llvm::LLVMRustPrintPassTimings(); } |
| return; |
| } |
| |
| if opts.output_type == output_type_llvm_assembly { |
| // Given options "-S --emit-llvm": output LLVM assembly |
| str::as_c_str(output.to_str(), |buf_o| { |
| llvm::LLVMRustAddPrintModulePass(pm.llpm, llmod, buf_o)}); |
| } else { |
| // If only a bitcode file is asked for by using the '--emit-llvm' |
| // flag, then output it here |
| llvm::LLVMRunPassManager(pm.llpm, llmod); |
| str::as_c_str(output.to_str(), |
| |buf| llvm::LLVMWriteBitcodeToFile(llmod, buf) ); |
| } |
| |
| llvm::LLVMDisposeModule(llmod); |
| if sess.time_llvm_passes() { llvm::LLVMRustPrintPassTimings(); } |
| } |
| } |
| |
| |
| /* |
| * Name mangling and its relationship to metadata. This is complex. Read |
| * carefully. |
| * |
| * The semantic model of Rust linkage is, broadly, that "there's no global |
| * namespace" between crates. Our aim is to preserve the illusion of this |
| * model despite the fact that it's not *quite* possible to implement on |
| * modern linkers. We initially didn't use system linkers at all, but have |
| * been convinced of their utility. |
| * |
| * There are a few issues to handle: |
| * |
| * - Linkers operate on a flat namespace, so we have to flatten names. |
| * We do this using the C++ namespace-mangling technique. Foo::bar |
| * symbols and such. |
| * |
| * - Symbols with the same name but different types need to get different |
| * linkage-names. We do this by hashing a string-encoding of the type into |
| * a fixed-size (currently 16-byte hex) cryptographic hash function (CHF: |
| * we use SHA1) to "prevent collisions". This is not airtight but 16 hex |
| * digits on uniform probability means you're going to need 2**32 same-name |
| * symbols in the same process before you're even hitting birthday-paradox |
| * collision probability. |
| * |
| * - Symbols in different crates but with same names "within" the crate need |
| * to get different linkage-names. |
| * |
| * So here is what we do: |
| * |
| * - Separate the meta tags into two sets: exported and local. Only work with |
| * the exported ones when considering linkage. |
| * |
| * - Consider two exported tags as special (and mandatory): name and vers. |
| * Every crate gets them; if it doesn't name them explicitly we infer them |
| * as basename(crate) and "0.1", respectively. Call these CNAME, CVERS. |
| * |
| * - Define CMETA as all the non-name, non-vers exported meta tags in the |
| * crate (in sorted order). |
| * |
| * - Define CMH as hash(CMETA + hashes of dependent crates). |
| * |
| * - Compile our crate to lib CNAME-CMH-CVERS.so |
| * |
| * - Define STH(sym) as hash(CNAME, CMH, type_str(sym)) |
| * |
| * - Suffix a mangled sym with ::STH@CVERS, so that it is unique in the |
| * name, non-name metadata, and type sense, and versioned in the way |
| * system linkers understand. |
| * |
| */ |
| |
| fn build_link_meta(sess: session, c: ast::crate, output: &Path, |
| symbol_hasher: &hash::State) -> link_meta { |
| |
| type provided_metas = |
| {name: Option<~str>, |
| vers: Option<~str>, |
| cmh_items: ~[@ast::meta_item]}; |
| |
| fn provided_link_metas(sess: session, c: ast::crate) -> |
| provided_metas { |
| let mut name: Option<~str> = None; |
| let mut vers: Option<~str> = None; |
| let mut cmh_items: ~[@ast::meta_item] = ~[]; |
| let linkage_metas = attr::find_linkage_metas(c.node.attrs); |
| attr::require_unique_names(sess.diagnostic(), linkage_metas); |
| for linkage_metas.each |meta| { |
| if attr::get_meta_item_name(*meta) == ~"name" { |
| match attr::get_meta_item_value_str(*meta) { |
| Some(v) => { name = Some(v); } |
| None => cmh_items.push(*meta) |
| } |
| } else if attr::get_meta_item_name(*meta) == ~"vers" { |
| match attr::get_meta_item_value_str(*meta) { |
| Some(v) => { vers = Some(v); } |
| None => cmh_items.push(*meta) |
| } |
| } else { cmh_items.push(*meta); } |
| } |
| return {name: name, vers: vers, cmh_items: cmh_items}; |
| } |
| |
| // This calculates CMH as defined above |
| fn crate_meta_extras_hash(symbol_hasher: &hash::State, |
| _crate: ast::crate, |
| metas: provided_metas, |
| dep_hashes: ~[~str]) -> ~str { |
| fn len_and_str(s: ~str) -> ~str { |
| return fmt!("%u_%s", str::len(s), s); |
| } |
| |
| fn len_and_str_lit(l: ast::lit) -> ~str { |
| return len_and_str(pprust::lit_to_str(@l)); |
| } |
| |
| let cmh_items = attr::sort_meta_items(metas.cmh_items); |
| |
| symbol_hasher.reset(); |
| for cmh_items.each |m| { |
| match m.node { |
| ast::meta_name_value(key, value) => { |
| symbol_hasher.write_str(len_and_str(key)); |
| symbol_hasher.write_str(len_and_str_lit(value)); |
| } |
| ast::meta_word(name) => { |
| symbol_hasher.write_str(len_and_str(name)); |
| } |
| ast::meta_list(_, _) => { |
| // FIXME (#607): Implement this |
| fail ~"unimplemented meta_item variant"; |
| } |
| } |
| } |
| |
| for dep_hashes.each |dh| { |
| symbol_hasher.write_str(len_and_str(*dh)); |
| } |
| |
| return truncated_hash_result(symbol_hasher); |
| } |
| |
| fn warn_missing(sess: session, name: ~str, default: ~str) { |
| if !sess.building_library { return; } |
| sess.warn(fmt!("missing crate link meta `%s`, using `%s` as default", |
| name, default)); |
| } |
| |
| fn crate_meta_name(sess: session, _crate: ast::crate, |
| output: &Path, metas: provided_metas) -> ~str { |
| return match metas.name { |
| Some(v) => v, |
| None => { |
| let name = match output.filestem() { |
| None => sess.fatal(fmt!("output file name `%s` doesn't\ |
| appear to have a stem", |
| output.to_str())), |
| Some(s) => s |
| }; |
| warn_missing(sess, ~"name", name); |
| name |
| } |
| }; |
| } |
| |
| fn crate_meta_vers(sess: session, _crate: ast::crate, |
| metas: provided_metas) -> ~str { |
| return match metas.vers { |
| Some(v) => v, |
| None => { |
| let vers = ~"0.0"; |
| warn_missing(sess, ~"vers", vers); |
| vers |
| } |
| }; |
| } |
| |
| let provided_metas = provided_link_metas(sess, c); |
| let name = crate_meta_name(sess, c, output, provided_metas); |
| let vers = crate_meta_vers(sess, c, provided_metas); |
| let dep_hashes = cstore::get_dep_hashes(sess.cstore); |
| let extras_hash = |
| crate_meta_extras_hash(symbol_hasher, c, provided_metas, dep_hashes); |
| |
| return {name: name, vers: vers, extras_hash: extras_hash}; |
| } |
| |
| fn truncated_hash_result(symbol_hasher: &hash::State) -> ~str unsafe { |
| symbol_hasher.result_str() |
| } |
| |
| |
| // This calculates STH for a symbol, as defined above |
| fn symbol_hash(tcx: ty::ctxt, symbol_hasher: &hash::State, t: ty::t, |
| link_meta: link_meta) -> ~str { |
| // NB: do *not* use abbrevs here as we want the symbol names |
| // to be independent of one another in the crate. |
| |
| symbol_hasher.reset(); |
| symbol_hasher.write_str(link_meta.name); |
| symbol_hasher.write_str(~"-"); |
| symbol_hasher.write_str(link_meta.extras_hash); |
| symbol_hasher.write_str(~"-"); |
| symbol_hasher.write_str(encoder::encoded_ty(tcx, t)); |
| let hash = truncated_hash_result(symbol_hasher); |
| // Prefix with _ so that it never blends into adjacent digits |
| |
| return ~"_" + hash; |
| } |
| |
| fn get_symbol_hash(ccx: @crate_ctxt, t: ty::t) -> ~str { |
| match ccx.type_hashcodes.find(t) { |
| Some(h) => return h, |
| None => { |
| let hash = symbol_hash(ccx.tcx, ccx.symbol_hasher, t, ccx.link_meta); |
| ccx.type_hashcodes.insert(t, hash); |
| return hash; |
| } |
| } |
| } |
| |
| |
| // Name sanitation. LLVM will happily accept identifiers with weird names, but |
| // gas doesn't! |
| fn sanitize(s: ~str) -> ~str { |
| let mut result = ~""; |
| for str::chars_each(s) |c| { |
| match c { |
| '@' => result += ~"_sbox_", |
| '~' => result += ~"_ubox_", |
| '*' => result += ~"_ptr_", |
| '&' => result += ~"_ref_", |
| ',' => result += ~"_", |
| |
| '{' | '(' => result += ~"_of_", |
| 'a' .. 'z' |
| | 'A' .. 'Z' |
| | '0' .. '9' |
| | '_' => str::push_char(&mut result, c), |
| _ => { |
| if c > 'z' && char::is_XID_continue(c) { |
| str::push_char(&mut result, c); |
| } |
| } |
| } |
| } |
| |
| // Underscore-qualify anything that didn't start as an ident. |
| if result.len() > 0u && |
| result[0] != '_' as u8 && |
| ! char::is_XID_start(result[0] as char) { |
| return ~"_" + result; |
| } |
| |
| return result; |
| } |
| |
| fn mangle(sess: session, ss: path) -> ~str { |
| // Follow C++ namespace-mangling style |
| |
| let mut n = ~"_ZN"; // Begin name-sequence. |
| |
| for ss.each |s| { |
| match *s { path_name(s) | path_mod(s) => { |
| let sani = sanitize(sess.str_of(s)); |
| n += fmt!("%u%s", str::len(sani), sani); |
| } } |
| } |
| n += ~"E"; // End name-sequence. |
| n |
| } |
| |
| fn exported_name(sess: session, path: path, hash: ~str, vers: ~str) -> ~str { |
| return mangle(sess, |
| vec::append_one( |
| vec::append_one(path, path_name(sess.ident_of(hash))), |
| path_name(sess.ident_of(vers)))); |
| } |
| |
| fn mangle_exported_name(ccx: @crate_ctxt, path: path, t: ty::t) -> ~str { |
| let hash = get_symbol_hash(ccx, t); |
| return exported_name(ccx.sess, path, hash, ccx.link_meta.vers); |
| } |
| |
| fn mangle_internal_name_by_type_only(ccx: @crate_ctxt, |
| t: ty::t, name: ~str) -> |
| ~str { |
| let s = util::ppaux::ty_to_short_str(ccx.tcx, t); |
| let hash = get_symbol_hash(ccx, t); |
| return mangle(ccx.sess, |
| ~[path_name(ccx.sess.ident_of(name)), |
| path_name(ccx.sess.ident_of(s)), |
| path_name(ccx.sess.ident_of(hash))]); |
| } |
| |
| fn mangle_internal_name_by_path_and_seq(ccx: @crate_ctxt, path: path, |
| flav: ~str) -> ~str { |
| return mangle(ccx.sess, |
| vec::append_one(path, path_name(ccx.names(flav)))); |
| } |
| |
| fn mangle_internal_name_by_path(ccx: @crate_ctxt, path: path) -> ~str { |
| return mangle(ccx.sess, path); |
| } |
| |
| fn mangle_internal_name_by_seq(ccx: @crate_ctxt, flav: ~str) -> ~str { |
| return fmt!("%s_%u", flav, ccx.names(flav).repr); |
| } |
| |
| // If the user wants an exe generated we need to invoke |
| // cc to link the object file with some libs |
| fn link_binary(sess: session, |
| obj_filename: &Path, |
| out_filename: &Path, |
| lm: link_meta) { |
| // 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()) |
| } else { |
| stem |
| } |
| } |
| |
| let output = if sess.building_library { |
| let long_libname = |
| os::dll_filename(fmt!("%s-%s-%s", |
| lm.name, lm.extras_hash, lm.vers)); |
| 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 { |
| *out_filename |
| }; |
| |
| log(debug, ~"output: " + 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_win32 { ~"gcc" } 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 mut 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") { |
| 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(*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. |
| |
| let addl_paths = sess.opts.addl_lib_search_paths; |
| for addl_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()); |
| } |
| } |
| |
| // Always want the runtime linked in |
| cc_args.push(~"-lrustrt"); |
| |
| // 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"); |
| } |
| |
| 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"); |
| |
| // 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)); |
| |
| 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())); |
| } |
| } |
| } |
| // |
| // Local Variables: |
| // mode: rust |
| // fill-column: 78; |
| // indent-tabs-mode: nil |
| // c-basic-offset: 4 |
| // buffer-file-coding-system: utf-8-unix |
| // End: |
| // |