1 // Copyright 2012-2013 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
13 use driver::session::Session;
14 use driver::{config, PpMode};
15 use driver::{PpmFlowGraph, PpmExpanded, PpmExpandedIdentified, PpmTyped};
16 use driver::{PpmIdentified};
18 use lib::llvm::{ContextRef, ModuleRef};
19 use metadata::common::LinkMeta;
20 use metadata::creader;
22 use middle::cfg::graphviz::LabelledCFG;
23 use middle::{trans, freevars, stability, kind, ty, typeck, reachable};
24 use middle::dependency_format;
26 use plugin::load::Plugins;
27 use plugin::registry::Registry;
30 use util::common::time;
32 use util::nodemap::{NodeSet};
36 use serialize::{json, Encodable};
40 use std::io::MemReader;
43 use syntax::attr::{AttrMetaMethods};
44 use syntax::crateid::CrateId;
46 use syntax::parse::token;
47 use syntax::print::{pp, pprust};
50 pub fn host_triple() -> &'static str {
51 // Get the host triple out of the build environment. This ensures that our
52 // idea of the host triple is the same as for the set of libraries we've
53 // actually built. We can't just take LLVM's host triple because they
54 // normalize all ix86 architectures to i386.
56 // Instead of grabbing the host triple (for the current host), we grab (at
57 // compile time) the target triple that this rustc is built with and
58 // calling that (at runtime) the host triple.
59 (option_env!("CFG_COMPILER_HOST_TRIPLE")).
60 expect("CFG_COMPILER_HOST_TRIPLE")
63 pub fn compile_input(sess: Session,
64 cfg: ast::CrateConfig,
66 outdir: &Option<Path>,
67 output: &Option<Path>) {
68 // We need nested scopes here, because the intermediate results can keep
69 // large chunks of memory alive and we want to free them as soon as
70 // possible to keep the peak memory usage low
71 let (outputs, trans, sess) = {
72 let (outputs, expanded_crate, ast_map) = {
73 let krate = phase_1_parse_input(&sess, cfg, input);
74 if stop_after_phase_1(&sess) { return; }
75 let outputs = build_output_filenames(input,
78 krate.attrs.as_slice(),
80 let id = link::find_crate_id(krate.attrs.as_slice(),
81 outputs.out_filestem.as_slice());
82 let (expanded_crate, ast_map) =
83 phase_2_configure_and_expand(&sess, krate, &id);
84 (outputs, expanded_crate, ast_map)
86 write_out_deps(&sess, input, &outputs, &expanded_crate);
88 if stop_after_phase_2(&sess) { return; }
90 let analysis = phase_3_run_analysis_passes(sess, &expanded_crate, ast_map);
91 phase_save_analysis(&analysis.ty_cx.sess, &expanded_crate, &analysis, outdir);
92 if stop_after_phase_3(&analysis.ty_cx.sess) { return; }
93 let (tcx, trans) = phase_4_translate_to_llvm(expanded_crate,
96 // Discard interned strings as they are no longer required.
97 token::get_ident_interner().clear();
99 (outputs, trans, tcx.sess)
101 phase_5_run_llvm_passes(&sess, &trans, &outputs);
102 if stop_after_phase_5(&sess) { return; }
103 phase_6_link_output(&sess, &trans, &outputs);
107 * The name used for source code that doesn't originate in a file
108 * (e.g. source from stdin or a string)
110 pub fn anon_src() -> String {
114 pub fn source_name(input: &Input) -> String {
116 // FIXME (#9639): This needs to handle non-utf8 paths
117 FileInput(ref ifile) => ifile.as_str().unwrap().to_string(),
118 StrInput(_) => anon_src()
123 /// Load source from file
125 /// The string is the source
130 fn filestem(&self) -> String {
132 FileInput(ref ifile) => ifile.filestem_str().unwrap().to_string(),
133 StrInput(_) => "rust_out".to_string(),
139 pub fn phase_1_parse_input(sess: &Session, cfg: ast::CrateConfig, input: &Input)
141 let krate = time(sess.time_passes(), "parsing", (), |_| {
143 FileInput(ref file) => {
144 parse::parse_crate_from_file(&(*file), cfg.clone(), &sess.parse_sess)
146 StrInput(ref src) => {
147 parse::parse_crate_from_source_str(anon_src().to_string(),
155 if sess.opts.debugging_opts & config::AST_JSON_NOEXPAND != 0 {
156 let mut stdout = io::BufferedWriter::new(io::stdout());
157 let mut json = json::PrettyEncoder::new(&mut stdout);
158 // unwrapping so IoError isn't ignored
159 krate.encode(&mut json).unwrap();
162 if sess.show_span() {
163 front::show_span::run(sess, &krate);
169 // For continuing compilation after a parsed crate has been
172 /// Run the "early phases" of the compiler: initial `cfg` processing,
173 /// syntax expansion, secondary `cfg` expansion, synthesis of a test
174 /// harness if one is to be provided and injection of a dependency on the
175 /// standard library and prelude.
176 pub fn phase_2_configure_and_expand(sess: &Session,
177 mut krate: ast::Crate,
179 -> (ast::Crate, syntax::ast_map::Map) {
180 let time_passes = sess.time_passes();
182 *sess.crate_types.borrow_mut() = collect_crate_types(sess, krate.attrs.as_slice());
184 time(time_passes, "gated feature checking", (), |_|
185 front::feature_gate::check_crate(sess, &krate));
187 krate = time(time_passes, "crate injection", krate, |krate|
188 front::std_inject::maybe_inject_crates_ref(sess, krate));
190 // strip before expansion to allow macros to depend on
191 // configuration variables e.g/ in
193 // #[macro_escape] #[cfg(foo)]
194 // mod bar { macro_rules! baz!(() => {{}}) }
196 // baz! should not use this definition unless foo is enabled.
198 krate = time(time_passes, "configuration 1", krate, |krate|
199 front::config::strip_unconfigured_items(krate));
201 let Plugins { macros, registrars }
202 = time(time_passes, "plugin loading", (), |_|
203 plugin::load::load_plugins(sess, &krate));
205 let mut registry = Registry::new(&krate);
207 time(time_passes, "plugin registration", (), |_| {
208 for ®istrar in registrars.iter() {
209 registrar(&mut registry);
213 let Registry { syntax_exts, .. } = registry;
215 krate = time(time_passes, "expansion", (krate, macros, syntax_exts),
216 |(krate, macros, syntax_exts)| {
217 // Windows dlls do not have rpaths, so they don't know how to find their
218 // dependencies. It's up to us to tell the system where to find all the
219 // dependent dlls. Note that this uses cfg!(windows) as opposed to
220 // targ_cfg because syntax extensions are always loaded for the host
221 // compiler, not for the target.
223 sess.host_filesearch().add_dylib_search_paths();
225 let cfg = syntax::ext::expand::ExpansionConfig {
226 deriving_hash_type_parameter: sess.features.default_type_params.get(),
227 crate_id: crate_id.clone(),
229 syntax::ext::expand::expand_crate(&sess.parse_sess,
237 // strip again, in case expansion added anything with a #[cfg].
238 krate = time(time_passes, "configuration 2", krate, |krate|
239 front::config::strip_unconfigured_items(krate));
241 krate = time(time_passes, "maybe building test harness", krate, |krate|
242 front::test::modify_for_testing(sess, krate));
244 krate = time(time_passes, "prelude injection", krate, |krate|
245 front::std_inject::maybe_inject_prelude(sess, krate));
247 let (krate, map) = time(time_passes, "assigning node ids and indexing ast", krate, |krate|
248 front::assign_node_ids_and_map::assign_node_ids_and_map(sess, krate));
250 if sess.opts.debugging_opts & config::AST_JSON != 0 {
251 let mut stdout = io::BufferedWriter::new(io::stdout());
252 let mut json = json::PrettyEncoder::new(&mut stdout);
253 // unwrapping so IoError isn't ignored
254 krate.encode(&mut json).unwrap();
260 pub struct CrateAnalysis {
261 pub exp_map2: middle::resolve::ExportMap2,
262 pub exported_items: middle::privacy::ExportedItems,
263 pub public_items: middle::privacy::PublicItems,
265 pub reachable: NodeSet,
268 /// Run the resolution, typechecking, region checking and other
269 /// miscellaneous analysis passes on the crate. Return various
270 /// structures carrying the results of the analysis.
271 pub fn phase_3_run_analysis_passes(sess: Session,
273 ast_map: syntax::ast_map::Map) -> CrateAnalysis {
275 let time_passes = sess.time_passes();
277 time(time_passes, "external crate/lib resolution", (), |_|
278 creader::read_crates(&sess, krate));
280 let lang_items = time(time_passes, "language item collection", (), |_|
281 middle::lang_items::collect_language_items(krate, &sess));
283 let middle::resolve::CrateMap {
286 trait_map: trait_map,
287 external_exports: external_exports,
288 last_private_map: last_private_map
290 time(time_passes, "resolution", (), |_|
291 middle::resolve::resolve_crate(&sess, &lang_items, krate));
293 // Discard MTWT tables that aren't required past resolution.
294 syntax::ext::mtwt::clear_tables();
296 let named_region_map = time(time_passes, "lifetime resolution", (),
297 |_| middle::resolve_lifetime::krate(&sess, krate));
299 time(time_passes, "looking for entry point", (),
300 |_| middle::entry::find_entry_point(&sess, krate, &ast_map));
302 sess.plugin_registrar_fn.set(
303 time(time_passes, "looking for plugin registrar", (), |_|
304 plugin::build::find_plugin_registrar(
305 sess.diagnostic(), krate)));
307 let freevars = time(time_passes, "freevar finding", (), |_|
308 freevars::annotate_freevars(&def_map, krate));
310 let region_map = time(time_passes, "region resolution", (), |_|
311 middle::region::resolve_crate(&sess, krate));
313 time(time_passes, "loop checking", (), |_|
314 middle::check_loop::check_crate(&sess, krate));
316 let stability_index = time(time_passes, "stability index", (), |_|
317 stability::Index::build(krate));
319 let ty_cx = ty::mk_ctxt(sess, def_map, named_region_map, ast_map,
320 freevars, region_map, lang_items, stability_index);
322 // passes are timed inside typeck
323 typeck::check_crate(&ty_cx, trait_map, krate);
325 time(time_passes, "check static items", (), |_|
326 middle::check_static::check_crate(&ty_cx, krate));
328 // These next two const passes can probably be merged
329 time(time_passes, "const marking", (), |_|
330 middle::const_eval::process_crate(krate, &ty_cx));
332 time(time_passes, "const checking", (), |_|
333 middle::check_const::check_crate(krate, &ty_cx));
335 let maps = (external_exports, last_private_map);
336 let (exported_items, public_items) =
337 time(time_passes, "privacy checking", maps, |(a, b)|
338 middle::privacy::check_crate(&ty_cx, &exp_map2, a, b, krate));
340 time(time_passes, "intrinsic checking", (), |_|
341 middle::intrinsicck::check_crate(&ty_cx, krate));
343 time(time_passes, "effect checking", (), |_|
344 middle::effect::check_crate(&ty_cx, krate));
346 time(time_passes, "match checking", (), |_|
347 middle::check_match::check_crate(&ty_cx, krate));
349 time(time_passes, "liveness checking", (), |_|
350 middle::liveness::check_crate(&ty_cx, krate));
352 time(time_passes, "borrow checking", (), |_|
353 middle::borrowck::check_crate(&ty_cx, krate));
355 time(time_passes, "kind checking", (), |_|
356 kind::check_crate(&ty_cx, krate));
359 time(time_passes, "reachability checking", (), |_|
360 reachable::find_reachable(&ty_cx, &exported_items));
362 time(time_passes, "death checking", (), |_| {
363 middle::dead::check_crate(&ty_cx,
369 time(time_passes, "lint checking", (), |_|
370 lint::check_crate(&ty_cx, &exported_items, krate));
375 exported_items: exported_items,
376 public_items: public_items,
377 reachable: reachable_map,
381 pub fn phase_save_analysis(sess: &Session,
383 analysis: &CrateAnalysis,
384 odir: &Option<Path>) {
385 if (sess.opts.debugging_opts & config::SAVE_ANALYSIS) == 0 {
388 time(sess.time_passes(), "save analysis", krate, |krate|
389 middle::save::process_crate(sess, krate, analysis, odir));
392 pub struct CrateTranslation {
393 pub context: ContextRef,
394 pub module: ModuleRef,
395 pub metadata_module: ModuleRef,
397 pub metadata: Vec<u8>,
398 pub reachable: Vec<String>,
399 pub crate_formats: dependency_format::Dependencies,
400 pub no_builtins: bool,
403 /// Run the translation phase to LLVM, after which the AST and analysis can
405 pub fn phase_4_translate_to_llvm(krate: ast::Crate,
406 analysis: CrateAnalysis,
407 outputs: &OutputFilenames) -> (ty::ctxt, CrateTranslation) {
408 let time_passes = analysis.ty_cx.sess.time_passes();
410 time(time_passes, "resolving dependency formats", (), |_|
411 dependency_format::calculate(&analysis.ty_cx));
413 // Option dance to work around the lack of stack once closures.
414 time(time_passes, "translation", (krate, analysis), |(krate, analysis)|
415 trans::base::trans_crate(krate, analysis, outputs))
418 /// Run LLVM itself, producing a bitcode file, assembly file or object file
419 /// as a side effect.
420 pub fn phase_5_run_llvm_passes(sess: &Session,
421 trans: &CrateTranslation,
422 outputs: &OutputFilenames) {
423 if sess.opts.cg.no_integrated_as {
424 let output_type = link::OutputTypeAssembly;
426 time(sess.time_passes(), "LLVM passes", (), |_|
427 link::write::run_passes(sess, trans, [output_type], outputs));
429 link::write::run_assembler(sess, outputs);
431 // Remove assembly source, unless --save-temps was specified
432 if !sess.opts.cg.save_temps {
433 fs::unlink(&outputs.temp_path(link::OutputTypeAssembly)).unwrap();
436 time(sess.time_passes(), "LLVM passes", (), |_|
437 link::write::run_passes(sess,
439 sess.opts.output_types.as_slice(),
444 /// Run the linker on any artifacts that resulted from the LLVM run.
445 /// This should produce either a finished executable or library.
446 pub fn phase_6_link_output(sess: &Session,
447 trans: &CrateTranslation,
448 outputs: &OutputFilenames) {
449 time(sess.time_passes(), "linking", (), |_|
450 link::link_binary(sess,
453 &trans.link.crateid));
456 pub fn stop_after_phase_3(sess: &Session) -> bool {
457 if sess.opts.no_trans {
458 debug!("invoked with --no-trans, returning early from compile_input");
464 pub fn stop_after_phase_1(sess: &Session) -> bool {
465 if sess.opts.parse_only {
466 debug!("invoked with --parse-only, returning early from compile_input");
469 if sess.show_span() {
472 return sess.opts.debugging_opts & config::AST_JSON_NOEXPAND != 0;
475 pub fn stop_after_phase_2(sess: &Session) -> bool {
476 if sess.opts.no_analysis {
477 debug!("invoked with --no-analysis, returning early from compile_input");
480 return sess.opts.debugging_opts & config::AST_JSON != 0;
483 pub fn stop_after_phase_5(sess: &Session) -> bool {
484 if !sess.opts.output_types.iter().any(|&i| i == link::OutputTypeExe) {
485 debug!("not building executable, returning early from compile_input");
491 fn write_out_deps(sess: &Session,
493 outputs: &OutputFilenames,
494 krate: &ast::Crate) {
495 let id = link::find_crate_id(krate.attrs.as_slice(),
496 outputs.out_filestem.as_slice());
498 let mut out_filenames = Vec::new();
499 for output_type in sess.opts.output_types.iter() {
500 let file = outputs.path(*output_type);
502 link::OutputTypeExe => {
503 for output in sess.crate_types.borrow().iter() {
504 let p = link::filename_for_input(sess, *output, &id, &file);
505 out_filenames.push(p);
508 _ => { out_filenames.push(file); }
512 // Write out dependency rules to the dep-info file if requested with
514 let deps_filename = match sess.opts.write_dependency_info {
515 // Use filename from --dep-file argument if given
516 (true, Some(ref filename)) => filename.clone(),
517 // Use default filename: crate source filename with extension replaced
519 (true, None) => match *input {
520 FileInput(..) => outputs.with_extension("d"),
522 sess.warn("can not write --dep-info without a filename \
523 when compiling stdin.");
531 // Build a list of files used to compile the output and
532 // write Makefile-compatible dependency rules
533 let files: Vec<String> = sess.codemap().files.borrow()
534 .iter().filter(|fmap| fmap.is_real_file())
535 .map(|fmap| fmap.name.to_string())
537 let mut file = try!(io::File::create(&deps_filename));
538 for path in out_filenames.iter() {
539 try!(write!(&mut file as &mut Writer,
540 "{}: {}\n\n", path.display(), files.connect(" ")));
548 sess.fatal(format!("error writing dependencies to `{}`: {}",
549 deps_filename.display(), e).as_slice());
554 struct IdentifiedAnnotation;
556 impl pprust::PpAnn for IdentifiedAnnotation {
558 s: &mut pprust::State,
559 node: pprust::AnnNode) -> io::IoResult<()> {
561 pprust::NodeExpr(_) => s.popen(),
566 s: &mut pprust::State,
567 node: pprust::AnnNode) -> io::IoResult<()> {
569 pprust::NodeItem(item) => {
570 try!(pp::space(&mut s.s));
571 s.synth_comment(item.id.to_str())
573 pprust::NodeBlock(blk) => {
574 try!(pp::space(&mut s.s));
575 s.synth_comment((format!("block {}", blk.id)).to_string())
577 pprust::NodeExpr(expr) => {
578 try!(pp::space(&mut s.s));
579 try!(s.synth_comment(expr.id.to_str()));
582 pprust::NodePat(pat) => {
583 try!(pp::space(&mut s.s));
584 s.synth_comment((format!("pat {}", pat.id)).to_string())
590 struct TypedAnnotation {
591 analysis: CrateAnalysis,
594 impl pprust::PpAnn for TypedAnnotation {
596 s: &mut pprust::State,
597 node: pprust::AnnNode) -> io::IoResult<()> {
599 pprust::NodeExpr(_) => s.popen(),
604 s: &mut pprust::State,
605 node: pprust::AnnNode) -> io::IoResult<()> {
606 let tcx = &self.analysis.ty_cx;
608 pprust::NodeExpr(expr) => {
609 try!(pp::space(&mut s.s));
610 try!(pp::word(&mut s.s, "as"));
611 try!(pp::space(&mut s.s));
612 try!(pp::word(&mut s.s,
615 ty::expr_ty(tcx, expr)).as_slice()));
623 pub fn pretty_print_input(sess: Session,
624 cfg: ast::CrateConfig,
627 ofile: Option<Path>) {
628 let krate = phase_1_parse_input(&sess, cfg, input);
629 let id = link::find_crate_id(krate.attrs.as_slice(),
630 input.filestem().as_slice());
632 let (krate, ast_map, is_expanded) = match ppm {
633 PpmExpanded | PpmExpandedIdentified | PpmTyped | PpmFlowGraph(_) => {
634 let (krate, ast_map) = phase_2_configure_and_expand(&sess,
637 (krate, Some(ast_map), true)
639 _ => (krate, None, false)
642 let src_name = source_name(input);
643 let src = Vec::from_slice(sess.codemap()
644 .get_filemap(src_name.as_slice())
647 let mut rdr = MemReader::new(src);
649 let out = match ofile {
650 None => box io::stdout() as Box<Writer>,
652 let r = io::File::create(&p);
654 Ok(w) => box w as Box<Writer>,
655 Err(e) => fail!("print-print failed to open {} due to {}",
661 PpmIdentified | PpmExpandedIdentified => {
662 pprust::print_crate(sess.codemap(),
665 src_name.to_string(),
668 &IdentifiedAnnotation,
672 let ast_map = ast_map.expect("--pretty=typed missing ast_map");
673 let analysis = phase_3_run_analysis_passes(sess, &krate, ast_map);
674 let annotation = TypedAnnotation {
677 pprust::print_crate(annotation.analysis.ty_cx.sess.codemap(),
678 annotation.analysis.ty_cx.sess.diagnostic(),
680 src_name.to_string(),
686 PpmFlowGraph(nodeid) => {
687 let ast_map = ast_map.expect("--pretty flowgraph missing ast_map");
688 let node = ast_map.find(nodeid).unwrap_or_else(|| {
689 sess.fatal(format!("--pretty flowgraph couldn't find id: {}",
692 let block = match node {
693 syntax::ast_map::NodeBlock(block) => block,
695 let message = format!("--pretty=flowgraph needs block, got {:?}",
698 // point to what was found, if there's an
700 match ast_map.opt_span(nodeid) {
701 Some(sp) => sess.span_fatal(sp, message.as_slice()),
702 None => sess.fatal(message.as_slice())
706 let analysis = phase_3_run_analysis_passes(sess, &krate, ast_map);
707 print_flowgraph(analysis, block, out)
710 pprust::print_crate(sess.codemap(),
713 src_name.to_string(),
723 fn print_flowgraph<W:io::Writer>(analysis: CrateAnalysis,
724 block: ast::P<ast::Block>,
725 mut out: W) -> io::IoResult<()> {
726 let ty_cx = &analysis.ty_cx;
727 let cfg = cfg::CFG::new(ty_cx, &*block);
728 let lcfg = LabelledCFG { ast_map: &ty_cx.map,
730 name: format!("block{}", block.id).to_string(), };
731 debug!("cfg: {:?}", cfg);
732 let r = dot::render(&lcfg, &mut out);
733 return expand_err_details(r);
735 fn expand_err_details(r: io::IoResult<()>) -> io::IoResult<()> {
737 let orig_detail = ioerr.detail.clone();
738 let m = "graphviz::render failed";
740 detail: Some(match orig_detail {
741 None => m.into_string(),
742 Some(d) => format!("{}: {}", m, d)
750 pub fn collect_crate_types(session: &Session,
751 attrs: &[ast::Attribute]) -> Vec<config::CrateType> {
752 // Unconditionally collect crate types from attributes to make them used
753 let attr_types: Vec<config::CrateType> = attrs.iter().filter_map(|a| {
754 if a.check_name("crate_type") {
755 match a.value_str() {
756 Some(ref n) if n.equiv(&("rlib")) => {
757 Some(config::CrateTypeRlib)
759 Some(ref n) if n.equiv(&("dylib")) => {
760 Some(config::CrateTypeDylib)
762 Some(ref n) if n.equiv(&("lib")) => {
763 Some(config::default_lib_output())
765 Some(ref n) if n.equiv(&("staticlib")) => {
766 Some(config::CrateTypeStaticlib)
768 Some(ref n) if n.equiv(&("bin")) => Some(config::CrateTypeExecutable),
770 session.add_lint(lint::builtin::unknown_crate_type,
773 "invalid `crate_type` \
778 session.add_lint(lint::builtin::unknown_crate_type,
781 "`crate_type` requires a \
791 // If we're generating a test executable, then ignore all other output
792 // styles at all other locations
793 if session.opts.test {
794 return vec!(config::CrateTypeExecutable)
797 // Only check command line flags if present. If no types are specified by
798 // command line, then reuse the empty `base` Vec to hold the types that
799 // will be found in crate attributes.
800 let mut base = session.opts.crate_types.clone();
802 base.extend(attr_types.move_iter());
804 base.push(link::default_output_for_target(session));
806 base.as_mut_slice().sort();
810 base.move_iter().filter(|crate_type| {
811 let res = !link::invalid_output_for_target(session, *crate_type);
814 session.warn(format!("dropping unsupported crate type `{}` \
816 *crate_type, session.targ_cfg.os).as_slice());
823 pub struct OutputFilenames {
824 pub out_directory: Path,
825 pub out_filestem: String,
826 pub single_output_file: Option<Path>,
829 impl OutputFilenames {
830 pub fn path(&self, flavor: link::OutputType) -> Path {
831 match self.single_output_file {
832 Some(ref path) => return path.clone(),
835 self.temp_path(flavor)
838 pub fn temp_path(&self, flavor: link::OutputType) -> Path {
839 let base = self.out_directory.join(self.out_filestem.as_slice());
841 link::OutputTypeBitcode => base.with_extension("bc"),
842 link::OutputTypeAssembly => base.with_extension("s"),
843 link::OutputTypeLlvmAssembly => base.with_extension("ll"),
844 link::OutputTypeObject => base.with_extension("o"),
845 link::OutputTypeExe => base,
849 pub fn with_extension(&self, extension: &str) -> Path {
850 let stem = self.out_filestem.as_slice();
851 self.out_directory.join(stem).with_extension(extension)
855 pub fn build_output_filenames(input: &Input,
857 ofile: &Option<Path>,
858 attrs: &[ast::Attribute],
863 // "-" as input file will cause the parser to read from stdin so we
864 // have to make up a name
865 // We want to toss everything after the final '.'
866 let dirpath = match *odir {
867 Some(ref d) => d.clone(),
868 None => Path::new(".")
871 let mut stem = input.filestem();
873 // If a crateid is present, we use it as the link name
874 let crateid = attr::find_crateid(attrs);
877 Some(crateid) => stem = crateid.name.to_string(),
880 out_directory: dirpath,
882 single_output_file: None,
886 Some(ref out_file) => {
887 let ofile = if sess.opts.output_types.len() > 1 {
888 sess.warn("ignoring specified output filename because multiple \
889 outputs were requested");
892 Some(out_file.clone())
895 sess.warn("ignoring --out-dir flag due to -o flag.");
898 out_directory: out_file.dir_path(),
899 out_filestem: out_file.filestem_str().unwrap().to_string(),
900 single_output_file: ofile,