]> git.lizzy.rs Git - rust.git/blob - src/librustc/driver/driver.rs
2aa2746959fcbbd037f3103380de4391070da751
[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 middle::cfg;
22 use middle::cfg::graphviz::LabelledCFG;
23 use middle::{trans, freevars, stability, kind, ty, typeck, reachable};
24 use middle::dependency_format;
25 use middle;
26 use plugin::load::Plugins;
27 use plugin::registry::Registry;
28 use plugin;
29 use lint;
30 use util::common::time;
31 use util::ppaux;
32 use util::nodemap::{NodeSet};
33
34 use dot = graphviz;
35
36 use serialize::{json, Encodable};
37
38 use std::io;
39 use std::io::fs;
40 use std::io::MemReader;
41 use syntax::ast;
42 use syntax::attr;
43 use syntax::attr::{AttrMetaMethods};
44 use syntax::crateid::CrateId;
45 use syntax::parse;
46 use syntax::parse::token;
47 use syntax::print::{pp, pprust};
48 use syntax;
49
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.
55     //
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")
61 }
62
63 pub fn compile_input(sess: Session,
64                      cfg: ast::CrateConfig,
65                      input: &Input,
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,
76                                                  outdir,
77                                                  output,
78                                                  krate.attrs.as_slice(),
79                                                  &sess);
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)
85         };
86         write_out_deps(&sess, input, &outputs, &expanded_crate);
87
88         if stop_after_phase_2(&sess) { return; }
89
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,
94                                                      analysis, &outputs);
95
96         // Discard interned strings as they are no longer required.
97         token::get_ident_interner().clear();
98
99         (outputs, trans, tcx.sess)
100     };
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);
104 }
105
106 /**
107  * The name used for source code that doesn't originate in a file
108  * (e.g. source from stdin or a string)
109  */
110 pub fn anon_src() -> String {
111     "<anon>".to_string()
112 }
113
114 pub fn source_name(input: &Input) -> String {
115     match *input {
116         // FIXME (#9639): This needs to handle non-utf8 paths
117         FileInput(ref ifile) => ifile.as_str().unwrap().to_string(),
118         StrInput(_) => anon_src()
119     }
120 }
121
122 pub enum Input {
123     /// Load source from file
124     FileInput(Path),
125     /// The string is the source
126     StrInput(String)
127 }
128
129 impl Input {
130     fn filestem(&self) -> String {
131         match *self {
132             FileInput(ref ifile) => ifile.filestem_str().unwrap().to_string(),
133             StrInput(_) => "rust_out".to_string(),
134         }
135     }
136 }
137
138
139 pub fn phase_1_parse_input(sess: &Session, cfg: ast::CrateConfig, input: &Input)
140     -> ast::Crate {
141     let krate = time(sess.time_passes(), "parsing", (), |_| {
142         match *input {
143             FileInput(ref file) => {
144                 parse::parse_crate_from_file(&(*file), cfg.clone(), &sess.parse_sess)
145             }
146             StrInput(ref src) => {
147                 parse::parse_crate_from_source_str(anon_src().to_string(),
148                                                    src.to_string(),
149                                                    cfg.clone(),
150                                                    &sess.parse_sess)
151             }
152         }
153     });
154
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();
160     }
161
162     if sess.show_span() {
163         front::show_span::run(sess, &krate);
164     }
165
166     krate
167 }
168
169 // For continuing compilation after a parsed crate has been
170 // modified
171
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,
178                                     crate_id: &CrateId)
179                                     -> (ast::Crate, syntax::ast_map::Map) {
180     let time_passes = sess.time_passes();
181
182     *sess.crate_types.borrow_mut() = collect_crate_types(sess, krate.attrs.as_slice());
183
184     time(time_passes, "gated feature checking", (), |_|
185          front::feature_gate::check_crate(sess, &krate));
186
187     krate = time(time_passes, "crate injection", krate, |krate|
188                  front::std_inject::maybe_inject_crates_ref(sess, krate));
189
190     // strip before expansion to allow macros to depend on
191     // configuration variables e.g/ in
192     //
193     //   #[macro_escape] #[cfg(foo)]
194     //   mod bar { macro_rules! baz!(() => {{}}) }
195     //
196     // baz! should not use this definition unless foo is enabled.
197
198     krate = time(time_passes, "configuration 1", krate, |krate|
199                  front::config::strip_unconfigured_items(krate));
200
201     let Plugins { macros, registrars }
202         = time(time_passes, "plugin loading", (), |_|
203                plugin::load::load_plugins(sess, &krate));
204
205     let mut registry = Registry::new(&krate);
206
207     time(time_passes, "plugin registration", (), |_| {
208         for &registrar in registrars.iter() {
209             registrar(&mut registry);
210         }
211     });
212
213     let Registry { syntax_exts, .. } = registry;
214
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.
222             if cfg!(windows) {
223                 sess.host_filesearch().add_dylib_search_paths();
224             }
225             let cfg = syntax::ext::expand::ExpansionConfig {
226                 deriving_hash_type_parameter: sess.features.default_type_params.get(),
227                 crate_id: crate_id.clone(),
228             };
229             syntax::ext::expand::expand_crate(&sess.parse_sess,
230                                               cfg,
231                                               macros,
232                                               syntax_exts,
233                                               krate)
234         }
235     );
236
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));
240
241     krate = time(time_passes, "maybe building test harness", krate, |krate|
242                  front::test::modify_for_testing(sess, krate));
243
244     krate = time(time_passes, "prelude injection", krate, |krate|
245                  front::std_inject::maybe_inject_prelude(sess, krate));
246
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));
249
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();
255     }
256
257     (krate, map)
258 }
259
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,
264     pub ty_cx: ty::ctxt,
265     pub reachable: NodeSet,
266 }
267
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,
272                                    krate: &ast::Crate,
273                                    ast_map: syntax::ast_map::Map) -> CrateAnalysis {
274
275     let time_passes = sess.time_passes();
276
277     time(time_passes, "external crate/lib resolution", (), |_|
278          creader::read_crates(&sess, krate));
279
280     let lang_items = time(time_passes, "language item collection", (), |_|
281                           middle::lang_items::collect_language_items(krate, &sess));
282
283     let middle::resolve::CrateMap {
284         def_map: def_map,
285         exp_map2: exp_map2,
286         trait_map: trait_map,
287         external_exports: external_exports,
288         last_private_map: last_private_map
289     } =
290         time(time_passes, "resolution", (), |_|
291              middle::resolve::resolve_crate(&sess, &lang_items, krate));
292
293     // Discard MTWT tables that aren't required past resolution.
294     syntax::ext::mtwt::clear_tables();
295
296     let named_region_map = time(time_passes, "lifetime resolution", (),
297                                 |_| middle::resolve_lifetime::krate(&sess, krate));
298
299     time(time_passes, "looking for entry point", (),
300          |_| middle::entry::find_entry_point(&sess, krate, &ast_map));
301
302     sess.plugin_registrar_fn.set(
303         time(time_passes, "looking for plugin registrar", (), |_|
304             plugin::build::find_plugin_registrar(
305                 sess.diagnostic(), krate)));
306
307     let freevars = time(time_passes, "freevar finding", (), |_|
308                         freevars::annotate_freevars(&def_map, krate));
309
310     let region_map = time(time_passes, "region resolution", (), |_|
311                           middle::region::resolve_crate(&sess, krate));
312
313     time(time_passes, "loop checking", (), |_|
314          middle::check_loop::check_crate(&sess, krate));
315
316     let stability_index = time(time_passes, "stability index", (), |_|
317                                stability::Index::build(krate));
318
319     let ty_cx = ty::mk_ctxt(sess, def_map, named_region_map, ast_map,
320                             freevars, region_map, lang_items, stability_index);
321
322     // passes are timed inside typeck
323     typeck::check_crate(&ty_cx, trait_map, krate);
324
325     time(time_passes, "check static items", (), |_|
326          middle::check_static::check_crate(&ty_cx, krate));
327
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));
331
332     time(time_passes, "const checking", (), |_|
333          middle::check_const::check_crate(krate, &ty_cx));
334
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));
339
340     time(time_passes, "intrinsic checking", (), |_|
341          middle::intrinsicck::check_crate(&ty_cx, krate));
342
343     time(time_passes, "effect checking", (), |_|
344          middle::effect::check_crate(&ty_cx, krate));
345
346     time(time_passes, "match checking", (), |_|
347          middle::check_match::check_crate(&ty_cx, krate));
348
349     time(time_passes, "liveness checking", (), |_|
350          middle::liveness::check_crate(&ty_cx, krate));
351
352     time(time_passes, "borrow checking", (), |_|
353          middle::borrowck::check_crate(&ty_cx, krate));
354
355     time(time_passes, "kind checking", (), |_|
356          kind::check_crate(&ty_cx, krate));
357
358     let reachable_map =
359         time(time_passes, "reachability checking", (), |_|
360              reachable::find_reachable(&ty_cx, &exported_items));
361
362     time(time_passes, "death checking", (), |_| {
363         middle::dead::check_crate(&ty_cx,
364                                   &exported_items,
365                                   &reachable_map,
366                                   krate)
367     });
368
369     time(time_passes, "lint checking", (), |_|
370          lint::check_crate(&ty_cx, &exported_items, krate));
371
372     CrateAnalysis {
373         exp_map2: exp_map2,
374         ty_cx: ty_cx,
375         exported_items: exported_items,
376         public_items: public_items,
377         reachable: reachable_map,
378     }
379 }
380
381 pub fn phase_save_analysis(sess: &Session,
382                            krate: &ast::Crate,
383                            analysis: &CrateAnalysis,
384                            odir: &Option<Path>) {
385     if (sess.opts.debugging_opts & config::SAVE_ANALYSIS) == 0 {
386         return;
387     }
388     time(sess.time_passes(), "save analysis", krate, |krate|
389          middle::save::process_crate(sess, krate, analysis, odir));
390 }
391
392 pub struct CrateTranslation {
393     pub context: ContextRef,
394     pub module: ModuleRef,
395     pub metadata_module: ModuleRef,
396     pub link: LinkMeta,
397     pub metadata: Vec<u8>,
398     pub reachable: Vec<String>,
399     pub crate_formats: dependency_format::Dependencies,
400     pub no_builtins: bool,
401 }
402
403 /// Run the translation phase to LLVM, after which the AST and analysis can
404 /// be discarded.
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();
409
410     time(time_passes, "resolving dependency formats", (), |_|
411          dependency_format::calculate(&analysis.ty_cx));
412
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))
416 }
417
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;
425
426         time(sess.time_passes(), "LLVM passes", (), |_|
427             link::write::run_passes(sess, trans, [output_type], outputs));
428
429         link::write::run_assembler(sess, outputs);
430
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();
434         }
435     } else {
436         time(sess.time_passes(), "LLVM passes", (), |_|
437             link::write::run_passes(sess,
438                                     trans,
439                                     sess.opts.output_types.as_slice(),
440                                     outputs));
441     }
442 }
443
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,
451                            trans,
452                            outputs,
453                            &trans.link.crateid));
454 }
455
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");
459         return true;
460     }
461     return false;
462 }
463
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");
467         return true;
468     }
469     if sess.show_span() {
470         return true;
471     }
472     return sess.opts.debugging_opts & config::AST_JSON_NOEXPAND != 0;
473 }
474
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");
478         return true;
479     }
480     return sess.opts.debugging_opts & config::AST_JSON != 0;
481 }
482
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");
486         return true;
487     }
488     return false;
489 }
490
491 fn write_out_deps(sess: &Session,
492                   input: &Input,
493                   outputs: &OutputFilenames,
494                   krate: &ast::Crate) {
495     let id = link::find_crate_id(krate.attrs.as_slice(),
496                                  outputs.out_filestem.as_slice());
497
498     let mut out_filenames = Vec::new();
499     for output_type in sess.opts.output_types.iter() {
500         let file = outputs.path(*output_type);
501         match *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);
506                 }
507             }
508             _ => { out_filenames.push(file); }
509         }
510     }
511
512     // Write out dependency rules to the dep-info file if requested with
513     // --dep-info
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
518         // by ".d"
519         (true, None) => match *input {
520             FileInput(..) => outputs.with_extension("d"),
521             StrInput(..) => {
522                 sess.warn("can not write --dep-info without a filename \
523                            when compiling stdin.");
524                 return
525             },
526         },
527         _ => return,
528     };
529
530     let result = (|| {
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())
536                                    .collect();
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(" ")));
541         }
542         Ok(())
543     })();
544
545     match result {
546         Ok(()) => {}
547         Err(e) => {
548             sess.fatal(format!("error writing dependencies to `{}`: {}",
549                                deps_filename.display(), e).as_slice());
550         }
551     }
552 }
553
554 struct IdentifiedAnnotation;
555
556 impl pprust::PpAnn for IdentifiedAnnotation {
557     fn pre(&self,
558            s: &mut pprust::State,
559            node: pprust::AnnNode) -> io::IoResult<()> {
560         match node {
561             pprust::NodeExpr(_) => s.popen(),
562             _ => Ok(())
563         }
564     }
565     fn post(&self,
566             s: &mut pprust::State,
567             node: pprust::AnnNode) -> io::IoResult<()> {
568         match node {
569             pprust::NodeItem(item) => {
570                 try!(pp::space(&mut s.s));
571                 s.synth_comment(item.id.to_str())
572             }
573             pprust::NodeBlock(blk) => {
574                 try!(pp::space(&mut s.s));
575                 s.synth_comment((format!("block {}", blk.id)).to_string())
576             }
577             pprust::NodeExpr(expr) => {
578                 try!(pp::space(&mut s.s));
579                 try!(s.synth_comment(expr.id.to_str()));
580                 s.pclose()
581             }
582             pprust::NodePat(pat) => {
583                 try!(pp::space(&mut s.s));
584                 s.synth_comment((format!("pat {}", pat.id)).to_string())
585             }
586         }
587     }
588 }
589
590 struct TypedAnnotation {
591     analysis: CrateAnalysis,
592 }
593
594 impl pprust::PpAnn for TypedAnnotation {
595     fn pre(&self,
596            s: &mut pprust::State,
597            node: pprust::AnnNode) -> io::IoResult<()> {
598         match node {
599             pprust::NodeExpr(_) => s.popen(),
600             _ => Ok(())
601         }
602     }
603     fn post(&self,
604             s: &mut pprust::State,
605             node: pprust::AnnNode) -> io::IoResult<()> {
606         let tcx = &self.analysis.ty_cx;
607         match node {
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,
613                               ppaux::ty_to_str(
614                                   tcx,
615                                   ty::expr_ty(tcx, expr)).as_slice()));
616                 s.pclose()
617             }
618             _ => Ok(())
619         }
620     }
621 }
622
623 pub fn pretty_print_input(sess: Session,
624                           cfg: ast::CrateConfig,
625                           input: &Input,
626                           ppm: PpMode,
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());
631
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,
635                                                                 krate,
636                                                                 &id);
637             (krate, Some(ast_map), true)
638         }
639         _ => (krate, None, false)
640     };
641
642     let src_name = source_name(input);
643     let src = Vec::from_slice(sess.codemap()
644                                   .get_filemap(src_name.as_slice())
645                                   .src
646                                   .as_bytes());
647     let mut rdr = MemReader::new(src);
648
649     let out = match ofile {
650         None => box io::stdout() as Box<Writer>,
651         Some(p) => {
652             let r = io::File::create(&p);
653             match r {
654                 Ok(w) => box w as Box<Writer>,
655                 Err(e) => fail!("print-print failed to open {} due to {}",
656                                 p.display(), e),
657             }
658         }
659     };
660     match ppm {
661         PpmIdentified | PpmExpandedIdentified => {
662             pprust::print_crate(sess.codemap(),
663                                 sess.diagnostic(),
664                                 &krate,
665                                 src_name.to_string(),
666                                 &mut rdr,
667                                 out,
668                                 &IdentifiedAnnotation,
669                                 is_expanded)
670         }
671         PpmTyped => {
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 {
675                 analysis: analysis
676             };
677             pprust::print_crate(annotation.analysis.ty_cx.sess.codemap(),
678                                 annotation.analysis.ty_cx.sess.diagnostic(),
679                                 &krate,
680                                 src_name.to_string(),
681                                 &mut rdr,
682                                 out,
683                                 &annotation,
684                                 is_expanded)
685         }
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: {}",
690                                    nodeid).as_slice())
691             });
692             let block = match node {
693                 syntax::ast_map::NodeBlock(block) => block,
694                 _ => {
695                     let message = format!("--pretty=flowgraph needs block, got {:?}",
696                                           node);
697
698                     // point to what was found, if there's an
699                     // accessible span.
700                     match ast_map.opt_span(nodeid) {
701                         Some(sp) => sess.span_fatal(sp, message.as_slice()),
702                         None => sess.fatal(message.as_slice())
703                     }
704                 }
705             };
706             let analysis = phase_3_run_analysis_passes(sess, &krate, ast_map);
707             print_flowgraph(analysis, block, out)
708         }
709         _ => {
710             pprust::print_crate(sess.codemap(),
711                                 sess.diagnostic(),
712                                 &krate,
713                                 src_name.to_string(),
714                                 &mut rdr,
715                                 out,
716                                 &pprust::NoAnn,
717                                 is_expanded)
718         }
719     }.unwrap()
720
721 }
722
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,
729                              cfg: &cfg,
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);
734
735     fn expand_err_details(r: io::IoResult<()>) -> io::IoResult<()> {
736         r.map_err(|ioerr| {
737             let orig_detail = ioerr.detail.clone();
738             let m = "graphviz::render failed";
739             io::IoError {
740                 detail: Some(match orig_detail {
741                     None => m.into_string(),
742                     Some(d) => format!("{}: {}", m, d)
743                 }),
744                 ..ioerr
745             }
746         })
747     }
748 }
749
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)
758                 }
759                 Some(ref n) if n.equiv(&("dylib")) => {
760                     Some(config::CrateTypeDylib)
761                 }
762                 Some(ref n) if n.equiv(&("lib")) => {
763                     Some(config::default_lib_output())
764                 }
765                 Some(ref n) if n.equiv(&("staticlib")) => {
766                     Some(config::CrateTypeStaticlib)
767                 }
768                 Some(ref n) if n.equiv(&("bin")) => Some(config::CrateTypeExecutable),
769                 Some(_) => {
770                     session.add_lint(lint::builtin::unknown_crate_type,
771                                      ast::CRATE_NODE_ID,
772                                      a.span,
773                                      "invalid `crate_type` \
774                                       value".to_string());
775                     None
776                 }
777                 _ => {
778                     session.add_lint(lint::builtin::unknown_crate_type,
779                                      ast::CRATE_NODE_ID,
780                                      a.span,
781                                      "`crate_type` requires a \
782                                       value".to_string());
783                     None
784                 }
785             }
786         } else {
787             None
788         }
789     }).collect();
790
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)
795     }
796
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();
801     if base.len() == 0 {
802         base.extend(attr_types.move_iter());
803         if base.len() == 0 {
804             base.push(link::default_output_for_target(session));
805         }
806         base.as_mut_slice().sort();
807         base.dedup();
808     }
809
810     base.move_iter().filter(|crate_type| {
811         let res = !link::invalid_output_for_target(session, *crate_type);
812
813         if !res {
814             session.warn(format!("dropping unsupported crate type `{}` \
815                                    for target os `{}`",
816                                  *crate_type, session.targ_cfg.os).as_slice());
817         }
818
819         res
820     }).collect()
821 }
822
823 pub struct OutputFilenames {
824     pub out_directory: Path,
825     pub out_filestem: String,
826     pub single_output_file: Option<Path>,
827 }
828
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(),
833             None => {}
834         }
835         self.temp_path(flavor)
836     }
837
838     pub fn temp_path(&self, flavor: link::OutputType) -> Path {
839         let base = self.out_directory.join(self.out_filestem.as_slice());
840         match flavor {
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,
846         }
847     }
848
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)
852     }
853 }
854
855 pub fn build_output_filenames(input: &Input,
856                               odir: &Option<Path>,
857                               ofile: &Option<Path>,
858                               attrs: &[ast::Attribute],
859                               sess: &Session)
860                            -> OutputFilenames {
861     match *ofile {
862         None => {
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(".")
869             };
870
871             let mut stem = input.filestem();
872
873             // If a crateid is present, we use it as the link name
874             let crateid = attr::find_crateid(attrs);
875             match crateid {
876                 None => {}
877                 Some(crateid) => stem = crateid.name.to_string(),
878             }
879             OutputFilenames {
880                 out_directory: dirpath,
881                 out_filestem: stem,
882                 single_output_file: None,
883             }
884         }
885
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");
890                 None
891             } else {
892                 Some(out_file.clone())
893             };
894             if *odir != None {
895                 sess.warn("ignoring --out-dir flag due to -o flag.");
896             }
897             OutputFilenames {
898                 out_directory: out_file.dir_path(),
899                 out_filestem: out_file.filestem_str().unwrap().to_string(),
900                 single_output_file: ofile,
901             }
902         }
903     }
904 }