]> git.lizzy.rs Git - rust.git/blob - src/librustc/driver/driver.rs
bc0d8e2f75c1579c4ec6e7f24b2da07920f6ffad
[rust.git] / src / librustc / driver / driver.rs
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.
4 //
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.
10
11
12 use back::link;
13 use driver::session::Session;
14 use driver::{config, PpMode};
15 use driver::{PpmFlowGraph, PpmExpanded, PpmExpandedIdentified, PpmTyped};
16 use driver::{PpmIdentified};
17 use front;
18 use lib::llvm::{ContextRef, ModuleRef};
19 use metadata::common::LinkMeta;
20 use metadata::creader;
21 use metadata::creader::Loader;
22 use middle::cfg;
23 use middle::cfg::graphviz::LabelledCFG;
24 use middle::{trans, freevars, kind, ty, typeck, lint, reachable};
25 use middle::dependency_format;
26 use middle;
27 use util::common::time;
28 use util::ppaux;
29 use util::nodemap::{NodeSet};
30
31 use dot = graphviz;
32
33 use serialize::{json, Encodable};
34
35 use std::io;
36 use std::io::fs;
37 use std::io::MemReader;
38 use syntax::ast;
39 use syntax::attr;
40 use syntax::attr::{AttrMetaMethods};
41 use syntax::crateid::CrateId;
42 use syntax::ext::base::CrateLoader;
43 use syntax::parse;
44 use syntax::parse::token;
45 use syntax::print::{pp, pprust};
46 use syntax;
47
48 pub fn host_triple() -> &'static str {
49     // Get the host triple out of the build environment. This ensures that our
50     // idea of the host triple is the same as for the set of libraries we've
51     // actually built.  We can't just take LLVM's host triple because they
52     // normalize all ix86 architectures to i386.
53     //
54     // Instead of grabbing the host triple (for the current host), we grab (at
55     // compile time) the target triple that this rustc is built with and
56     // calling that (at runtime) the host triple.
57     (option_env!("CFG_COMPILER_HOST_TRIPLE")).
58         expect("CFG_COMPILER_HOST_TRIPLE")
59 }
60
61 pub fn compile_input(sess: Session,
62                      cfg: ast::CrateConfig,
63                      input: &Input,
64                      outdir: &Option<Path>,
65                      output: &Option<Path>) {
66     // We need nested scopes here, because the intermediate results can keep
67     // large chunks of memory alive and we want to free them as soon as
68     // possible to keep the peak memory usage low
69     let (outputs, trans, sess) = {
70         let (outputs, expanded_crate, ast_map) = {
71             let krate = phase_1_parse_input(&sess, cfg, input);
72             if stop_after_phase_1(&sess) { return; }
73             let outputs = build_output_filenames(input,
74                                                  outdir,
75                                                  output,
76                                                  krate.attrs.as_slice(),
77                                                  &sess);
78             let loader = &mut Loader::new(&sess);
79             let id = link::find_crate_id(krate.attrs.as_slice(),
80                                          outputs.out_filestem.as_slice());
81             let (expanded_crate, ast_map) =
82                 phase_2_configure_and_expand(&sess, loader, krate, &id);
83             (outputs, expanded_crate, ast_map)
84         };
85         write_out_deps(&sess, input, &outputs, &expanded_crate);
86
87         if stop_after_phase_2(&sess) { return; }
88
89         let analysis = phase_3_run_analysis_passes(sess, &expanded_crate, ast_map);
90         if stop_after_phase_3(&analysis.ty_cx.sess) { return; }
91         let (tcx, trans) = phase_4_translate_to_llvm(expanded_crate,
92                                                      analysis, &outputs);
93
94         // Discard interned strings as they are no longer required.
95         token::get_ident_interner().clear();
96
97         (outputs, trans, tcx.sess)
98     };
99     phase_5_run_llvm_passes(&sess, &trans, &outputs);
100     if stop_after_phase_5(&sess) { return; }
101     phase_6_link_output(&sess, &trans, &outputs);
102 }
103
104 /**
105  * The name used for source code that doesn't originate in a file
106  * (e.g. source from stdin or a string)
107  */
108 pub fn anon_src() -> String {
109     "<anon>".to_string()
110 }
111
112 pub fn source_name(input: &Input) -> String {
113     match *input {
114         // FIXME (#9639): This needs to handle non-utf8 paths
115         FileInput(ref ifile) => ifile.as_str().unwrap().to_string(),
116         StrInput(_) => anon_src()
117     }
118 }
119
120 pub enum Input {
121     /// Load source from file
122     FileInput(Path),
123     /// The string is the source
124     StrInput(String)
125 }
126
127 impl Input {
128     fn filestem(&self) -> String {
129         match *self {
130             FileInput(ref ifile) => ifile.filestem_str().unwrap().to_string(),
131             StrInput(_) => "rust_out".to_string(),
132         }
133     }
134 }
135
136
137 pub fn phase_1_parse_input(sess: &Session, cfg: ast::CrateConfig, input: &Input)
138     -> ast::Crate {
139     let krate = time(sess.time_passes(), "parsing", (), |_| {
140         match *input {
141             FileInput(ref file) => {
142                 parse::parse_crate_from_file(&(*file), cfg.clone(), &sess.parse_sess)
143             }
144             StrInput(ref src) => {
145                 parse::parse_crate_from_source_str(anon_src().to_string(),
146                                                    src.to_string(),
147                                                    cfg.clone(),
148                                                    &sess.parse_sess)
149             }
150         }
151     });
152
153     if sess.opts.debugging_opts & config::AST_JSON_NOEXPAND != 0 {
154         let mut stdout = io::BufferedWriter::new(io::stdout());
155         let mut json = json::PrettyEncoder::new(&mut stdout);
156         // unwrapping so IoError isn't ignored
157         krate.encode(&mut json).unwrap();
158     }
159
160     if sess.show_span() {
161         front::show_span::run(sess, &krate);
162     }
163
164     krate
165 }
166
167 // For continuing compilation after a parsed crate has been
168 // modified
169
170 /// Run the "early phases" of the compiler: initial `cfg` processing,
171 /// syntax expansion, secondary `cfg` expansion, synthesis of a test
172 /// harness if one is to be provided and injection of a dependency on the
173 /// standard library and prelude.
174 pub fn phase_2_configure_and_expand(sess: &Session,
175                                     loader: &mut CrateLoader,
176                                     mut krate: ast::Crate,
177                                     crate_id: &CrateId)
178                                     -> (ast::Crate, syntax::ast_map::Map) {
179     let time_passes = sess.time_passes();
180
181     *sess.crate_types.borrow_mut() = collect_crate_types(sess, krate.attrs.as_slice());
182
183     time(time_passes, "gated feature checking", (), |_|
184          front::feature_gate::check_crate(sess, &krate));
185
186     krate = time(time_passes, "crate injection", krate, |krate|
187                  front::std_inject::maybe_inject_crates_ref(sess, krate));
188
189     // strip before expansion to allow macros to depend on
190     // configuration variables e.g/ in
191     //
192     //   #[macro_escape] #[cfg(foo)]
193     //   mod bar { macro_rules! baz!(() => {{}}) }
194     //
195     // baz! should not use this definition unless foo is enabled.
196
197     krate = time(time_passes, "configuration 1", krate, |krate|
198                  front::config::strip_unconfigured_items(krate));
199
200     krate = time(time_passes, "expansion", krate, |krate| {
201         // Windows dlls do not have rpaths, so they don't know how to find their
202         // dependencies. It's up to us to tell the system where to find all the
203         // dependent dlls. Note that this uses cfg!(windows) as opposed to
204         // targ_cfg because syntax extensions are always loaded for the host
205         // compiler, not for the target.
206         if cfg!(windows) {
207             sess.host_filesearch().add_dylib_search_paths();
208         }
209         let cfg = syntax::ext::expand::ExpansionConfig {
210             loader: loader,
211             deriving_hash_type_parameter: sess.features.default_type_params.get(),
212             crate_id: crate_id.clone(),
213         };
214         syntax::ext::expand::expand_crate(&sess.parse_sess,
215                                           cfg,
216                                           krate)
217     });
218
219     // strip again, in case expansion added anything with a #[cfg].
220     krate = time(time_passes, "configuration 2", krate, |krate|
221                  front::config::strip_unconfigured_items(krate));
222
223     krate = time(time_passes, "maybe building test harness", krate, |krate|
224                  front::test::modify_for_testing(sess, krate));
225
226     krate = time(time_passes, "prelude injection", krate, |krate|
227                  front::std_inject::maybe_inject_prelude(sess, krate));
228
229     let (krate, map) = time(time_passes, "assigning node ids and indexing ast", krate, |krate|
230          front::assign_node_ids_and_map::assign_node_ids_and_map(sess, krate));
231
232     if sess.opts.debugging_opts & config::AST_JSON != 0 {
233         let mut stdout = io::BufferedWriter::new(io::stdout());
234         let mut json = json::PrettyEncoder::new(&mut stdout);
235         // unwrapping so IoError isn't ignored
236         krate.encode(&mut json).unwrap();
237     }
238
239     (krate, map)
240 }
241
242 pub struct CrateAnalysis {
243     pub exp_map2: middle::resolve::ExportMap2,
244     pub exported_items: middle::privacy::ExportedItems,
245     pub public_items: middle::privacy::PublicItems,
246     pub ty_cx: ty::ctxt,
247     pub reachable: NodeSet,
248 }
249
250 /// Run the resolution, typechecking, region checking and other
251 /// miscellaneous analysis passes on the crate. Return various
252 /// structures carrying the results of the analysis.
253 pub fn phase_3_run_analysis_passes(sess: Session,
254                                    krate: &ast::Crate,
255                                    ast_map: syntax::ast_map::Map) -> CrateAnalysis {
256
257     let time_passes = sess.time_passes();
258
259     time(time_passes, "external crate/lib resolution", (), |_|
260          creader::read_crates(&sess, krate));
261
262     let lang_items = time(time_passes, "language item collection", (), |_|
263                           middle::lang_items::collect_language_items(krate, &sess));
264
265     let middle::resolve::CrateMap {
266         def_map: def_map,
267         exp_map2: exp_map2,
268         trait_map: trait_map,
269         external_exports: external_exports,
270         last_private_map: last_private_map
271     } =
272         time(time_passes, "resolution", (), |_|
273              middle::resolve::resolve_crate(&sess, &lang_items, krate));
274
275     // Discard MTWT tables that aren't required past resolution.
276     syntax::ext::mtwt::clear_tables();
277
278     let named_region_map = time(time_passes, "lifetime resolution", (),
279                                 |_| middle::resolve_lifetime::krate(&sess, krate));
280
281     time(time_passes, "looking for entry point", (),
282          |_| middle::entry::find_entry_point(&sess, krate, &ast_map));
283
284     sess.macro_registrar_fn.set(
285         time(time_passes, "looking for macro registrar", (), |_|
286             syntax::ext::registrar::find_macro_registrar(
287                 sess.diagnostic(), krate)));
288
289     let freevars = time(time_passes, "freevar finding", (), |_|
290                         freevars::annotate_freevars(&def_map, krate));
291
292     let region_map = time(time_passes, "region resolution", (), |_|
293                           middle::region::resolve_crate(&sess, krate));
294
295     time(time_passes, "loop checking", (), |_|
296          middle::check_loop::check_crate(&sess, krate));
297
298     let ty_cx = ty::mk_ctxt(sess, def_map, named_region_map, ast_map,
299                             freevars, region_map, lang_items);
300
301     // passes are timed inside typeck
302     typeck::check_crate(&ty_cx, trait_map, krate);
303
304     time(time_passes, "check static items", (), |_|
305          middle::check_static::check_crate(&ty_cx, krate));
306
307     // These next two const passes can probably be merged
308     time(time_passes, "const marking", (), |_|
309          middle::const_eval::process_crate(krate, &ty_cx));
310
311     time(time_passes, "const checking", (), |_|
312          middle::check_const::check_crate(krate, &ty_cx));
313
314     let maps = (external_exports, last_private_map);
315     let (exported_items, public_items) =
316             time(time_passes, "privacy checking", maps, |(a, b)|
317                  middle::privacy::check_crate(&ty_cx, &exp_map2, a, b, krate));
318
319     time(time_passes, "effect checking", (), |_|
320          middle::effect::check_crate(&ty_cx, krate));
321
322     time(time_passes, "match checking", (), |_|
323          middle::check_match::check_crate(&ty_cx, krate));
324
325     time(time_passes, "liveness checking", (), |_|
326          middle::liveness::check_crate(&ty_cx, krate));
327
328     time(time_passes, "borrow checking", (), |_|
329          middle::borrowck::check_crate(&ty_cx, krate));
330
331     time(time_passes, "kind checking", (), |_|
332          kind::check_crate(&ty_cx, krate));
333
334     let reachable_map =
335         time(time_passes, "reachability checking", (), |_|
336              reachable::find_reachable(&ty_cx, &exported_items));
337
338     time(time_passes, "death checking", (), |_| {
339         middle::dead::check_crate(&ty_cx,
340                                   &exported_items,
341                                   &reachable_map,
342                                   krate)
343     });
344
345     time(time_passes, "lint checking", (), |_|
346          lint::check_crate(&ty_cx, &exported_items, krate));
347
348     CrateAnalysis {
349         exp_map2: exp_map2,
350         ty_cx: ty_cx,
351         exported_items: exported_items,
352         public_items: public_items,
353         reachable: reachable_map,
354     }
355 }
356
357 pub struct CrateTranslation {
358     pub context: ContextRef,
359     pub module: ModuleRef,
360     pub metadata_module: ModuleRef,
361     pub link: LinkMeta,
362     pub metadata: Vec<u8>,
363     pub reachable: Vec<String>,
364     pub crate_formats: dependency_format::Dependencies,
365     pub no_builtins: bool,
366 }
367
368 /// Run the translation phase to LLVM, after which the AST and analysis can
369 /// be discarded.
370 pub fn phase_4_translate_to_llvm(krate: ast::Crate,
371                                  analysis: CrateAnalysis,
372                                  outputs: &OutputFilenames) -> (ty::ctxt, CrateTranslation) {
373     let time_passes = analysis.ty_cx.sess.time_passes();
374
375     time(time_passes, "resolving dependency formats", (), |_|
376          dependency_format::calculate(&analysis.ty_cx));
377
378     // Option dance to work around the lack of stack once closures.
379     time(time_passes, "translation", (krate, analysis), |(krate, analysis)|
380          trans::base::trans_crate(krate, analysis, outputs))
381 }
382
383 /// Run LLVM itself, producing a bitcode file, assembly file or object file
384 /// as a side effect.
385 pub fn phase_5_run_llvm_passes(sess: &Session,
386                                trans: &CrateTranslation,
387                                outputs: &OutputFilenames) {
388     if sess.opts.cg.no_integrated_as {
389         let output_type = link::OutputTypeAssembly;
390
391         time(sess.time_passes(), "LLVM passes", (), |_|
392             link::write::run_passes(sess, trans, [output_type], outputs));
393
394         link::write::run_assembler(sess, outputs);
395
396         // Remove assembly source, unless --save-temps was specified
397         if !sess.opts.cg.save_temps {
398             fs::unlink(&outputs.temp_path(link::OutputTypeAssembly)).unwrap();
399         }
400     } else {
401         time(sess.time_passes(), "LLVM passes", (), |_|
402             link::write::run_passes(sess,
403                                     trans,
404                                     sess.opts.output_types.as_slice(),
405                                     outputs));
406     }
407 }
408
409 /// Run the linker on any artifacts that resulted from the LLVM run.
410 /// This should produce either a finished executable or library.
411 pub fn phase_6_link_output(sess: &Session,
412                            trans: &CrateTranslation,
413                            outputs: &OutputFilenames) {
414     time(sess.time_passes(), "linking", (), |_|
415          link::link_binary(sess,
416                            trans,
417                            outputs,
418                            &trans.link.crateid));
419 }
420
421 pub fn stop_after_phase_3(sess: &Session) -> bool {
422    if sess.opts.no_trans {
423         debug!("invoked with --no-trans, returning early from compile_input");
424         return true;
425     }
426     return false;
427 }
428
429 pub fn stop_after_phase_1(sess: &Session) -> bool {
430     if sess.opts.parse_only {
431         debug!("invoked with --parse-only, returning early from compile_input");
432         return true;
433     }
434     if sess.show_span() {
435         return true;
436     }
437     return sess.opts.debugging_opts & config::AST_JSON_NOEXPAND != 0;
438 }
439
440 pub fn stop_after_phase_2(sess: &Session) -> bool {
441     if sess.opts.no_analysis {
442         debug!("invoked with --no-analysis, returning early from compile_input");
443         return true;
444     }
445     return sess.opts.debugging_opts & config::AST_JSON != 0;
446 }
447
448 pub fn stop_after_phase_5(sess: &Session) -> bool {
449     if !sess.opts.output_types.iter().any(|&i| i == link::OutputTypeExe) {
450         debug!("not building executable, returning early from compile_input");
451         return true;
452     }
453     return false;
454 }
455
456 fn write_out_deps(sess: &Session,
457                   input: &Input,
458                   outputs: &OutputFilenames,
459                   krate: &ast::Crate) {
460     let id = link::find_crate_id(krate.attrs.as_slice(),
461                                  outputs.out_filestem.as_slice());
462
463     let mut out_filenames = Vec::new();
464     for output_type in sess.opts.output_types.iter() {
465         let file = outputs.path(*output_type);
466         match *output_type {
467             link::OutputTypeExe => {
468                 for output in sess.crate_types.borrow().iter() {
469                     let p = link::filename_for_input(sess, *output, &id, &file);
470                     out_filenames.push(p);
471                 }
472             }
473             _ => { out_filenames.push(file); }
474         }
475     }
476
477     // Write out dependency rules to the dep-info file if requested with
478     // --dep-info
479     let deps_filename = match sess.opts.write_dependency_info {
480         // Use filename from --dep-file argument if given
481         (true, Some(ref filename)) => filename.clone(),
482         // Use default filename: crate source filename with extension replaced
483         // by ".d"
484         (true, None) => match *input {
485             FileInput(..) => outputs.with_extension("d"),
486             StrInput(..) => {
487                 sess.warn("can not write --dep-info without a filename \
488                            when compiling stdin.");
489                 return
490             },
491         },
492         _ => return,
493     };
494
495     let result = (|| {
496         // Build a list of files used to compile the output and
497         // write Makefile-compatible dependency rules
498         let files: Vec<String> = sess.codemap().files.borrow()
499                                    .iter().filter(|fmap| fmap.is_real_file())
500                                    .map(|fmap| fmap.name.to_string())
501                                    .collect();
502         let mut file = try!(io::File::create(&deps_filename));
503         for path in out_filenames.iter() {
504             try!(write!(&mut file as &mut Writer,
505                           "{}: {}\n\n", path.display(), files.connect(" ")));
506         }
507         Ok(())
508     })();
509
510     match result {
511         Ok(()) => {}
512         Err(e) => {
513             sess.fatal(format!("error writing dependencies to `{}`: {}",
514                                deps_filename.display(), e).as_slice());
515         }
516     }
517 }
518
519 struct IdentifiedAnnotation;
520
521 impl pprust::PpAnn for IdentifiedAnnotation {
522     fn pre(&self,
523            s: &mut pprust::State,
524            node: pprust::AnnNode) -> io::IoResult<()> {
525         match node {
526             pprust::NodeExpr(_) => s.popen(),
527             _ => Ok(())
528         }
529     }
530     fn post(&self,
531             s: &mut pprust::State,
532             node: pprust::AnnNode) -> io::IoResult<()> {
533         match node {
534             pprust::NodeItem(item) => {
535                 try!(pp::space(&mut s.s));
536                 s.synth_comment(item.id.to_str().to_string())
537             }
538             pprust::NodeBlock(blk) => {
539                 try!(pp::space(&mut s.s));
540                 s.synth_comment((format!("block {}", blk.id)).to_string())
541             }
542             pprust::NodeExpr(expr) => {
543                 try!(pp::space(&mut s.s));
544                 try!(s.synth_comment(expr.id.to_str().to_string()));
545                 s.pclose()
546             }
547             pprust::NodePat(pat) => {
548                 try!(pp::space(&mut s.s));
549                 s.synth_comment((format!("pat {}", pat.id)).to_string())
550             }
551         }
552     }
553 }
554
555 struct TypedAnnotation {
556     analysis: CrateAnalysis,
557 }
558
559 impl pprust::PpAnn for TypedAnnotation {
560     fn pre(&self,
561            s: &mut pprust::State,
562            node: pprust::AnnNode) -> io::IoResult<()> {
563         match node {
564             pprust::NodeExpr(_) => s.popen(),
565             _ => Ok(())
566         }
567     }
568     fn post(&self,
569             s: &mut pprust::State,
570             node: pprust::AnnNode) -> io::IoResult<()> {
571         let tcx = &self.analysis.ty_cx;
572         match node {
573             pprust::NodeExpr(expr) => {
574                 try!(pp::space(&mut s.s));
575                 try!(pp::word(&mut s.s, "as"));
576                 try!(pp::space(&mut s.s));
577                 try!(pp::word(&mut s.s,
578                               ppaux::ty_to_str(
579                                   tcx,
580                                   ty::expr_ty(tcx, expr)).as_slice()));
581                 s.pclose()
582             }
583             _ => Ok(())
584         }
585     }
586 }
587
588 pub fn pretty_print_input(sess: Session,
589                           cfg: ast::CrateConfig,
590                           input: &Input,
591                           ppm: PpMode,
592                           ofile: Option<Path>) {
593     let krate = phase_1_parse_input(&sess, cfg, input);
594     let id = link::find_crate_id(krate.attrs.as_slice(),
595                                  input.filestem().as_slice());
596
597     let (krate, ast_map, is_expanded) = match ppm {
598         PpmExpanded | PpmExpandedIdentified | PpmTyped | PpmFlowGraph(_) => {
599             let loader = &mut Loader::new(&sess);
600             let (krate, ast_map) = phase_2_configure_and_expand(&sess,
601                                                                 loader,
602                                                                 krate,
603                                                                 &id);
604             (krate, Some(ast_map), true)
605         }
606         _ => (krate, None, false)
607     };
608
609     let src_name = source_name(input);
610     let src = Vec::from_slice(sess.codemap()
611                                   .get_filemap(src_name.as_slice())
612                                   .src
613                                   .as_bytes());
614     let mut rdr = MemReader::new(src);
615
616     let out = match ofile {
617         None => box io::stdout() as Box<Writer>,
618         Some(p) => {
619             let r = io::File::create(&p);
620             match r {
621                 Ok(w) => box w as Box<Writer>,
622                 Err(e) => fail!("print-print failed to open {} due to {}",
623                                 p.display(), e),
624             }
625         }
626     };
627     match ppm {
628         PpmIdentified | PpmExpandedIdentified => {
629             pprust::print_crate(sess.codemap(),
630                                 sess.diagnostic(),
631                                 &krate,
632                                 src_name.to_string(),
633                                 &mut rdr,
634                                 out,
635                                 &IdentifiedAnnotation,
636                                 is_expanded)
637         }
638         PpmTyped => {
639             let ast_map = ast_map.expect("--pretty=typed missing ast_map");
640             let analysis = phase_3_run_analysis_passes(sess, &krate, ast_map);
641             let annotation = TypedAnnotation {
642                 analysis: analysis
643             };
644             pprust::print_crate(annotation.analysis.ty_cx.sess.codemap(),
645                                 annotation.analysis.ty_cx.sess.diagnostic(),
646                                 &krate,
647                                 src_name.to_string(),
648                                 &mut rdr,
649                                 out,
650                                 &annotation,
651                                 is_expanded)
652         }
653         PpmFlowGraph(nodeid) => {
654             let ast_map = ast_map.expect("--pretty flowgraph missing ast_map");
655             let node = ast_map.find(nodeid).unwrap_or_else(|| {
656                 sess.fatal(format_strbuf!("--pretty flowgraph couldn't find id: {}",
657                                           nodeid).as_slice())
658             });
659             let block = match node {
660                 syntax::ast_map::NodeBlock(block) => block,
661                 _ => {
662                     let message = format_strbuf!("--pretty=flowgraph needs block, got {:?}",
663                                                  node);
664
665                     // point to what was found, if there's an
666                     // accessible span.
667                     match ast_map.opt_span(nodeid) {
668                         Some(sp) => sess.span_fatal(sp, message.as_slice()),
669                         None => sess.fatal(message.as_slice())
670                     }
671                 }
672             };
673             let analysis = phase_3_run_analysis_passes(sess, &krate, ast_map);
674             print_flowgraph(analysis, block, out)
675         }
676         _ => {
677             pprust::print_crate(sess.codemap(),
678                                 sess.diagnostic(),
679                                 &krate,
680                                 src_name.to_string(),
681                                 &mut rdr,
682                                 out,
683                                 &pprust::NoAnn,
684                                 is_expanded)
685         }
686     }.unwrap()
687
688 }
689
690 fn print_flowgraph<W:io::Writer>(analysis: CrateAnalysis,
691                                  block: ast::P<ast::Block>,
692                                  mut out: W) -> io::IoResult<()> {
693     let ty_cx = &analysis.ty_cx;
694     let cfg = cfg::CFG::new(ty_cx, block);
695     let lcfg = LabelledCFG { ast_map: &ty_cx.map,
696                              cfg: &cfg,
697                              name: format!("block{}", block.id).to_string(), };
698     debug!("cfg: {:?}", cfg);
699     let r = dot::render(&lcfg, &mut out);
700     return expand_err_details(r);
701
702     fn expand_err_details(r: io::IoResult<()>) -> io::IoResult<()> {
703         r.map_err(|ioerr| {
704             let orig_detail = ioerr.detail.clone();
705             let m = "graphviz::render failed";
706             io::IoError {
707                 detail: Some(match orig_detail {
708                     None => m.into_string(),
709                     Some(d) => format_strbuf!("{}: {}", m, d)
710                 }),
711                 ..ioerr
712             }
713         })
714     }
715 }
716
717 pub fn collect_crate_types(session: &Session,
718                            attrs: &[ast::Attribute]) -> Vec<config::CrateType> {
719     // Unconditionally collect crate types from attributes to make them used
720     let attr_types: Vec<config::CrateType> = attrs.iter().filter_map(|a| {
721         if a.check_name("crate_type") {
722             match a.value_str() {
723                 Some(ref n) if n.equiv(&("rlib")) => {
724                     Some(config::CrateTypeRlib)
725                 }
726                 Some(ref n) if n.equiv(&("dylib")) => {
727                     Some(config::CrateTypeDylib)
728                 }
729                 Some(ref n) if n.equiv(&("lib")) => {
730                     Some(config::default_lib_output())
731                 }
732                 Some(ref n) if n.equiv(&("staticlib")) => {
733                     Some(config::CrateTypeStaticlib)
734                 }
735                 Some(ref n) if n.equiv(&("bin")) => Some(config::CrateTypeExecutable),
736                 Some(_) => {
737                     session.add_lint(lint::UnknownCrateType,
738                                      ast::CRATE_NODE_ID,
739                                      a.span,
740                                      "invalid `crate_type` \
741                                       value".to_string());
742                     None
743                 }
744                 _ => {
745                     session.add_lint(lint::UnknownCrateType,
746                                      ast::CRATE_NODE_ID,
747                                      a.span,
748                                      "`crate_type` requires a \
749                                       value".to_string());
750                     None
751                 }
752             }
753         } else {
754             None
755         }
756     }).collect();
757
758     // If we're generating a test executable, then ignore all other output
759     // styles at all other locations
760     if session.opts.test {
761         return vec!(config::CrateTypeExecutable)
762     }
763
764     // Only check command line flags if present. If no types are specified by
765     // command line, then reuse the empty `base` Vec to hold the types that
766     // will be found in crate attributes.
767     let mut base = session.opts.crate_types.clone();
768     if base.len() > 0 {
769         return base
770     } else {
771         base.extend(attr_types.move_iter());
772         if base.len() == 0 {
773             base.push(config::CrateTypeExecutable);
774         }
775         base.as_mut_slice().sort();
776         base.dedup();
777         return base;
778     }
779 }
780
781 pub struct OutputFilenames {
782     pub out_directory: Path,
783     pub out_filestem: String,
784     pub single_output_file: Option<Path>,
785 }
786
787 impl OutputFilenames {
788     pub fn path(&self, flavor: link::OutputType) -> Path {
789         match self.single_output_file {
790             Some(ref path) => return path.clone(),
791             None => {}
792         }
793         self.temp_path(flavor)
794     }
795
796     pub fn temp_path(&self, flavor: link::OutputType) -> Path {
797         let base = self.out_directory.join(self.out_filestem.as_slice());
798         match flavor {
799             link::OutputTypeBitcode => base.with_extension("bc"),
800             link::OutputTypeAssembly => base.with_extension("s"),
801             link::OutputTypeLlvmAssembly => base.with_extension("ll"),
802             link::OutputTypeObject => base.with_extension("o"),
803             link::OutputTypeExe => base,
804         }
805     }
806
807     pub fn with_extension(&self, extension: &str) -> Path {
808         let stem = self.out_filestem.as_slice();
809         self.out_directory.join(stem).with_extension(extension)
810     }
811 }
812
813 pub fn build_output_filenames(input: &Input,
814                               odir: &Option<Path>,
815                               ofile: &Option<Path>,
816                               attrs: &[ast::Attribute],
817                               sess: &Session)
818                            -> OutputFilenames {
819     match *ofile {
820         None => {
821             // "-" as input file will cause the parser to read from stdin so we
822             // have to make up a name
823             // We want to toss everything after the final '.'
824             let dirpath = match *odir {
825                 Some(ref d) => d.clone(),
826                 None => Path::new(".")
827             };
828
829             let mut stem = input.filestem();
830
831             // If a crateid is present, we use it as the link name
832             let crateid = attr::find_crateid(attrs);
833             match crateid {
834                 None => {}
835                 Some(crateid) => stem = crateid.name.to_string(),
836             }
837             OutputFilenames {
838                 out_directory: dirpath,
839                 out_filestem: stem,
840                 single_output_file: None,
841             }
842         }
843
844         Some(ref out_file) => {
845             let ofile = if sess.opts.output_types.len() > 1 {
846                 sess.warn("ignoring specified output filename because multiple \
847                            outputs were requested");
848                 None
849             } else {
850                 Some(out_file.clone())
851             };
852             if *odir != None {
853                 sess.warn("ignoring --out-dir flag due to -o flag.");
854             }
855             OutputFilenames {
856                 out_directory: out_file.dir_path(),
857                 out_filestem: out_file.filestem_str().unwrap().to_string(),
858                 single_output_file: ofile,
859             }
860         }
861     }
862 }