]> git.lizzy.rs Git - rust.git/blob - src/librustc/driver/driver.rs
auto merge of #13704 : edwardw/rust/doc-hidden, r=alexcrichton
[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 back::{arm, x86, x86_64, mips};
14 use driver::session::{Aggressive, CrateTypeExecutable, CrateType,
15                       FullDebugInfo, LimitedDebugInfo, NoDebugInfo};
16 use driver::session::{Session, No, Less, Default};
17 use driver::session;
18 use front;
19 use lib::llvm::llvm;
20 use lib::llvm::{ContextRef, ModuleRef};
21 use metadata::common::LinkMeta;
22 use metadata::{creader, filesearch};
23 use metadata::cstore::CStore;
24 use metadata::creader::Loader;
25 use metadata;
26 use middle::{trans, freevars, kind, ty, typeck, lint, astencode, reachable};
27 use middle;
28 use util::common::time;
29 use util::ppaux;
30 use util::nodemap::{NodeMap, NodeSet};
31
32 use serialize::{json, Encodable};
33
34 use std::cell::{Cell, RefCell};
35 use std::io;
36 use std::io::fs;
37 use std::io::MemReader;
38 use std::mem::drop;
39 use std::os;
40 use getopts::{optopt, optmulti, optflag, optflagopt};
41 use getopts;
42 use syntax::ast;
43 use syntax::abi;
44 use syntax::attr;
45 use syntax::attr::{AttrMetaMethods};
46 use syntax::codemap;
47 use syntax::crateid::CrateId;
48 use syntax::diagnostic;
49 use syntax::diagnostic::Emitter;
50 use syntax::ext::base::CrateLoader;
51 use syntax::parse;
52 use syntax::parse::token::InternedString;
53 use syntax::parse::token;
54 use syntax::print::{pp, pprust};
55 use syntax;
56
57 pub enum PpMode {
58     PpmNormal,
59     PpmExpanded,
60     PpmTyped,
61     PpmIdentified,
62     PpmExpandedIdentified
63 }
64
65 /**
66  * The name used for source code that doesn't originate in a file
67  * (e.g. source from stdin or a string)
68  */
69 pub fn anon_src() -> ~str {
70     "<anon>".to_str()
71 }
72
73 pub fn source_name(input: &Input) -> ~str {
74     match *input {
75         // FIXME (#9639): This needs to handle non-utf8 paths
76         FileInput(ref ifile) => ifile.as_str().unwrap().to_str(),
77         StrInput(_) => anon_src()
78     }
79 }
80
81 pub fn default_configuration(sess: &Session) ->
82    ast::CrateConfig {
83     let tos = match sess.targ_cfg.os {
84         abi::OsWin32 =>   InternedString::new("win32"),
85         abi::OsMacos =>   InternedString::new("macos"),
86         abi::OsLinux =>   InternedString::new("linux"),
87         abi::OsAndroid => InternedString::new("android"),
88         abi::OsFreebsd => InternedString::new("freebsd"),
89     };
90
91     // ARM is bi-endian, however using NDK seems to default
92     // to little-endian unless a flag is provided.
93     let (end,arch,wordsz) = match sess.targ_cfg.arch {
94         abi::X86 =>    ("little", "x86",    "32"),
95         abi::X86_64 => ("little", "x86_64", "64"),
96         abi::Arm =>    ("little", "arm",    "32"),
97         abi::Mips =>   ("big",    "mips",   "32")
98     };
99
100     let fam = match sess.targ_cfg.os {
101         abi::OsWin32 => InternedString::new("windows"),
102         _ => InternedString::new("unix")
103     };
104
105     let mk = attr::mk_name_value_item_str;
106     return vec!(// Target bindings.
107          attr::mk_word_item(fam.clone()),
108          mk(InternedString::new("target_os"), tos),
109          mk(InternedString::new("target_family"), fam),
110          mk(InternedString::new("target_arch"), InternedString::new(arch)),
111          mk(InternedString::new("target_endian"), InternedString::new(end)),
112          mk(InternedString::new("target_word_size"),
113             InternedString::new(wordsz))
114     );
115 }
116
117 pub fn append_configuration(cfg: &mut ast::CrateConfig,
118                             name: InternedString) {
119     if !cfg.iter().any(|mi| mi.name() == name) {
120         cfg.push(attr::mk_word_item(name))
121     }
122 }
123
124 pub fn build_configuration(sess: &Session) -> ast::CrateConfig {
125     // Combine the configuration requested by the session (command line) with
126     // some default and generated configuration items
127     let default_cfg = default_configuration(sess);
128     let mut user_cfg = sess.opts.cfg.clone();
129     // If the user wants a test runner, then add the test cfg
130     if sess.opts.test {
131         append_configuration(&mut user_cfg, InternedString::new("test"))
132     }
133     // If the user requested GC, then add the GC cfg
134     append_configuration(&mut user_cfg, if sess.opts.gc {
135         InternedString::new("gc")
136     } else {
137         InternedString::new("nogc")
138     });
139     user_cfg.move_iter().collect::<Vec<_>>().append(default_cfg.as_slice())
140 }
141
142 // Convert strings provided as --cfg [cfgspec] into a crate_cfg
143 fn parse_cfgspecs(cfgspecs: Vec<~str> )
144                   -> ast::CrateConfig {
145     cfgspecs.move_iter().map(|s| {
146         parse::parse_meta_from_source_str("cfgspec".to_str(),
147                                           s,
148                                           Vec::new(),
149                                           &parse::new_parse_sess())
150     }).collect::<ast::CrateConfig>()
151 }
152
153 pub enum Input {
154     /// Load source from file
155     FileInput(Path),
156     /// The string is the source
157     StrInput(~str)
158 }
159
160 impl Input {
161     fn filestem(&self) -> ~str {
162         match *self {
163             FileInput(ref ifile) => ifile.filestem_str().unwrap().to_str(),
164             StrInput(_) => "rust_out".to_owned(),
165         }
166     }
167 }
168
169
170 pub fn phase_1_parse_input(sess: &Session, cfg: ast::CrateConfig, input: &Input)
171     -> ast::Crate {
172     let krate = time(sess.time_passes(), "parsing", (), |_| {
173         match *input {
174             FileInput(ref file) => {
175                 parse::parse_crate_from_file(&(*file), cfg.clone(), &sess.parse_sess)
176             }
177             StrInput(ref src) => {
178                 parse::parse_crate_from_source_str(anon_src(),
179                                                    (*src).clone(),
180                                                    cfg.clone(),
181                                                    &sess.parse_sess)
182             }
183         }
184     });
185
186     if sess.opts.debugging_opts & session::AST_JSON_NOEXPAND != 0 {
187         let mut stdout = io::BufferedWriter::new(io::stdout());
188         let mut json = json::PrettyEncoder::new(&mut stdout);
189         // unwrapping so IoError isn't ignored
190         krate.encode(&mut json).unwrap();
191     }
192
193     if sess.show_span() {
194         front::show_span::run(sess, &krate);
195     }
196
197     krate
198 }
199
200 // For continuing compilation after a parsed crate has been
201 // modified
202
203 /// Run the "early phases" of the compiler: initial `cfg` processing,
204 /// syntax expansion, secondary `cfg` expansion, synthesis of a test
205 /// harness if one is to be provided and injection of a dependency on the
206 /// standard library and prelude.
207 pub fn phase_2_configure_and_expand(sess: &Session,
208                                     loader: &mut CrateLoader,
209                                     mut krate: ast::Crate,
210                                     crate_id: &CrateId)
211                                     -> (ast::Crate, syntax::ast_map::Map) {
212     let time_passes = sess.time_passes();
213
214     sess.building_library.set(session::building_library(&sess.opts, &krate));
215     *sess.crate_types.borrow_mut() = session::collect_crate_types(sess, krate.attrs.as_slice());
216
217     time(time_passes, "gated feature checking", (), |_|
218          front::feature_gate::check_crate(sess, &krate));
219
220     krate = time(time_passes, "crate injection", krate, |krate|
221                  front::std_inject::maybe_inject_crates_ref(sess, krate));
222
223     // strip before expansion to allow macros to depend on
224     // configuration variables e.g/ in
225     //
226     //   #[macro_escape] #[cfg(foo)]
227     //   mod bar { macro_rules! baz!(() => {{}}) }
228     //
229     // baz! should not use this definition unless foo is enabled.
230
231     krate = time(time_passes, "configuration 1", krate, |krate|
232                  front::config::strip_unconfigured_items(krate));
233
234     krate = time(time_passes, "expansion", krate, |krate| {
235         let cfg = syntax::ext::expand::ExpansionConfig {
236             loader: loader,
237             deriving_hash_type_parameter: sess.features.default_type_params.get(),
238             crate_id: crate_id.clone(),
239         };
240         syntax::ext::expand::expand_crate(&sess.parse_sess,
241                                           cfg,
242                                           krate)
243     });
244
245     // strip again, in case expansion added anything with a #[cfg].
246     krate = time(time_passes, "configuration 2", krate, |krate|
247                  front::config::strip_unconfigured_items(krate));
248
249     krate = time(time_passes, "maybe building test harness", krate, |krate|
250                  front::test::modify_for_testing(sess, krate));
251
252     krate = time(time_passes, "prelude injection", krate, |krate|
253                  front::std_inject::maybe_inject_prelude(sess, krate));
254
255     let (krate, map) = time(time_passes, "assinging node ids and indexing ast", krate, |krate|
256          front::assign_node_ids_and_map::assign_node_ids_and_map(sess, krate));
257
258     if sess.opts.debugging_opts & session::AST_JSON != 0 {
259         let mut stdout = io::BufferedWriter::new(io::stdout());
260         let mut json = json::PrettyEncoder::new(&mut stdout);
261         // unwrapping so IoError isn't ignored
262         krate.encode(&mut json).unwrap();
263     }
264
265     (krate, map)
266 }
267
268 pub struct CrateAnalysis {
269     pub exp_map2: middle::resolve::ExportMap2,
270     pub exported_items: middle::privacy::ExportedItems,
271     pub public_items: middle::privacy::PublicItems,
272     pub ty_cx: ty::ctxt,
273     pub maps: astencode::Maps,
274     pub reachable: NodeSet,
275 }
276
277 /// Run the resolution, typechecking, region checking and other
278 /// miscellaneous analysis passes on the crate. Return various
279 /// structures carrying the results of the analysis.
280 pub fn phase_3_run_analysis_passes(sess: Session,
281                                    krate: &ast::Crate,
282                                    ast_map: syntax::ast_map::Map) -> CrateAnalysis {
283
284     let time_passes = sess.time_passes();
285
286     time(time_passes, "external crate/lib resolution", (), |_|
287          creader::read_crates(&sess, krate));
288
289     let lang_items = time(time_passes, "language item collection", (), |_|
290                           middle::lang_items::collect_language_items(krate, &sess));
291
292     let middle::resolve::CrateMap {
293         def_map: def_map,
294         exp_map2: exp_map2,
295         trait_map: trait_map,
296         external_exports: external_exports,
297         last_private_map: last_private_map
298     } =
299         time(time_passes, "resolution", (), |_|
300              middle::resolve::resolve_crate(&sess, &lang_items, krate));
301
302     // Discard MTWT tables that aren't required past resolution.
303     syntax::ext::mtwt::clear_tables();
304
305     let named_region_map = time(time_passes, "lifetime resolution", (),
306                                 |_| middle::resolve_lifetime::krate(&sess, krate));
307
308     time(time_passes, "looking for entry point", (),
309          |_| middle::entry::find_entry_point(&sess, krate, &ast_map));
310
311     sess.macro_registrar_fn.set(
312         time(time_passes, "looking for macro registrar", (), |_|
313             syntax::ext::registrar::find_macro_registrar(
314                 sess.diagnostic(), krate)));
315
316     let freevars = time(time_passes, "freevar finding", (), |_|
317                         freevars::annotate_freevars(&def_map, krate));
318
319     let region_map = time(time_passes, "region resolution", (), |_|
320                           middle::region::resolve_crate(&sess, krate));
321
322     time(time_passes, "loop checking", (), |_|
323          middle::check_loop::check_crate(&sess, krate));
324
325     let ty_cx = ty::mk_ctxt(sess, def_map, named_region_map, ast_map,
326                             freevars, region_map, lang_items);
327
328     // passes are timed inside typeck
329     typeck::check_crate(&ty_cx, trait_map, krate);
330
331     time(time_passes, "check static items", (), |_|
332          middle::check_static::check_crate(&ty_cx, krate));
333
334     // These next two const passes can probably be merged
335     time(time_passes, "const marking", (), |_|
336          middle::const_eval::process_crate(krate, &ty_cx));
337
338     time(time_passes, "const checking", (), |_|
339          middle::check_const::check_crate(krate, &ty_cx));
340
341     let maps = (external_exports, last_private_map);
342     let (exported_items, public_items) =
343             time(time_passes, "privacy checking", maps, |(a, b)|
344                  middle::privacy::check_crate(&ty_cx, &exp_map2, a, b, krate));
345
346     time(time_passes, "effect checking", (), |_|
347          middle::effect::check_crate(&ty_cx, krate));
348
349     let middle::moves::MoveMaps {moves_map, moved_variables_set,
350                                  capture_map} =
351         time(time_passes, "compute moves", (), |_|
352              middle::moves::compute_moves(&ty_cx, krate));
353
354     time(time_passes, "match checking", (), |_|
355          middle::check_match::check_crate(&ty_cx, &moves_map, krate));
356
357     time(time_passes, "liveness checking", (), |_|
358          middle::liveness::check_crate(&ty_cx, &capture_map, krate));
359
360     let root_map =
361         time(time_passes, "borrow checking", (), |_|
362              middle::borrowck::check_crate(&ty_cx, &moves_map,
363                                            &moved_variables_set,
364                                            &capture_map, krate));
365
366     drop(moves_map);
367     drop(moved_variables_set);
368
369     time(time_passes, "kind checking", (), |_|
370          kind::check_crate(&ty_cx, krate));
371
372     let reachable_map =
373         time(time_passes, "reachability checking", (), |_|
374              reachable::find_reachable(&ty_cx, &exported_items));
375
376     time(time_passes, "death checking", (), |_| {
377         middle::dead::check_crate(&ty_cx,
378                                   &exported_items,
379                                   &reachable_map,
380                                   krate)
381     });
382
383     time(time_passes, "lint checking", (), |_|
384          lint::check_crate(&ty_cx, &exported_items, krate));
385
386     CrateAnalysis {
387         exp_map2: exp_map2,
388         ty_cx: ty_cx,
389         exported_items: exported_items,
390         public_items: public_items,
391         maps: astencode::Maps {
392             root_map: root_map,
393             capture_map: RefCell::new(capture_map)
394         },
395         reachable: reachable_map
396     }
397 }
398
399 pub struct CrateTranslation {
400     pub context: ContextRef,
401     pub module: ModuleRef,
402     pub metadata_module: ModuleRef,
403     pub link: LinkMeta,
404     pub metadata: Vec<u8>,
405     pub reachable: Vec<~str>,
406 }
407
408 /// Run the translation phase to LLVM, after which the AST and analysis can
409 /// be discarded.
410 pub fn phase_4_translate_to_llvm(krate: ast::Crate,
411                                  analysis: CrateAnalysis,
412                                  outputs: &OutputFilenames) -> (ty::ctxt, CrateTranslation) {
413     // Option dance to work around the lack of stack once closures.
414     let time_passes = analysis.ty_cx.sess.time_passes();
415     let mut analysis = Some(analysis);
416     time(time_passes, "translation", krate, |krate|
417          trans::base::trans_crate(krate, analysis.take_unwrap(), outputs))
418 }
419
420 /// Run LLVM itself, producing a bitcode file, assembly file or object file
421 /// as a side effect.
422 pub fn phase_5_run_llvm_passes(sess: &Session,
423                                trans: &CrateTranslation,
424                                outputs: &OutputFilenames) {
425     if sess.opts.cg.no_integrated_as {
426         let output_type = link::OutputTypeAssembly;
427
428         time(sess.time_passes(), "LLVM passes", (), |_|
429             link::write::run_passes(sess, trans, [output_type], outputs));
430
431         link::write::run_assembler(sess, outputs);
432
433         // Remove assembly source, unless --save-temps was specified
434         if !sess.opts.cg.save_temps {
435             fs::unlink(&outputs.temp_path(link::OutputTypeAssembly)).unwrap();
436         }
437     } else {
438         time(sess.time_passes(), "LLVM passes", (), |_|
439             link::write::run_passes(sess,
440                                     trans,
441                                     sess.opts.output_types.as_slice(),
442                                     outputs));
443     }
444 }
445
446 /// Run the linker on any artifacts that resulted from the LLVM run.
447 /// This should produce either a finished executable or library.
448 pub fn phase_6_link_output(sess: &Session,
449                            trans: &CrateTranslation,
450                            outputs: &OutputFilenames) {
451     time(sess.time_passes(), "linking", (), |_|
452          link::link_binary(sess,
453                            trans,
454                            outputs,
455                            &trans.link.crateid));
456 }
457
458 pub fn stop_after_phase_3(sess: &Session) -> bool {
459    if sess.opts.no_trans {
460         debug!("invoked with --no-trans, returning early from compile_input");
461         return true;
462     }
463     return false;
464 }
465
466 pub fn stop_after_phase_1(sess: &Session) -> bool {
467     if sess.opts.parse_only {
468         debug!("invoked with --parse-only, returning early from compile_input");
469         return true;
470     }
471     if sess.show_span() {
472         return true;
473     }
474     return sess.opts.debugging_opts & session::AST_JSON_NOEXPAND != 0;
475 }
476
477 pub fn stop_after_phase_2(sess: &Session) -> bool {
478     if sess.opts.no_analysis {
479         debug!("invoked with --no-analysis, returning early from compile_input");
480         return true;
481     }
482     return sess.opts.debugging_opts & session::AST_JSON != 0;
483 }
484
485 pub fn stop_after_phase_5(sess: &Session) -> bool {
486     if !sess.opts.output_types.iter().any(|&i| i == link::OutputTypeExe) {
487         debug!("not building executable, returning early from compile_input");
488         return true;
489     }
490     return false;
491 }
492
493 fn write_out_deps(sess: &Session,
494                   input: &Input,
495                   outputs: &OutputFilenames,
496                   krate: &ast::Crate) -> io::IoResult<()> {
497     let id = link::find_crate_id(krate.attrs.as_slice(), outputs.out_filestem);
498
499     let mut out_filenames = Vec::new();
500     for output_type in sess.opts.output_types.iter() {
501         let file = outputs.path(*output_type);
502         match *output_type {
503             link::OutputTypeExe => {
504                 for output in sess.crate_types.borrow().iter() {
505                     let p = link::filename_for_input(sess, *output, &id, &file);
506                     out_filenames.push(p);
507                 }
508             }
509             _ => { out_filenames.push(file); }
510         }
511     }
512
513     // Write out dependency rules to the dep-info file if requested with
514     // --dep-info
515     let deps_filename = match sess.opts.write_dependency_info {
516         // Use filename from --dep-file argument if given
517         (true, Some(ref filename)) => filename.clone(),
518         // Use default filename: crate source filename with extension replaced
519         // by ".d"
520         (true, None) => match *input {
521             FileInput(..) => outputs.with_extension("d"),
522             StrInput(..) => {
523                 sess.warn("can not write --dep-info without a filename \
524                            when compiling stdin.");
525                 return Ok(());
526             },
527         },
528         _ => return Ok(()),
529     };
530
531     // Build a list of files used to compile the output and
532     // write Makefile-compatible dependency rules
533     let files: Vec<~str> = sess.codemap().files.borrow()
534                                .iter().filter_map(|fmap| {
535                                     if fmap.is_real_file() {
536                                         Some(fmap.name.clone())
537                                     } else {
538                                         None
539                                     }
540                                 }).collect();
541     let mut file = try!(io::File::create(&deps_filename));
542     for path in out_filenames.iter() {
543         try!(write!(&mut file as &mut Writer,
544                       "{}: {}\n\n", path.display(), files.connect(" ")));
545     }
546     Ok(())
547 }
548
549 pub fn compile_input(sess: Session, cfg: ast::CrateConfig, input: &Input,
550                      outdir: &Option<Path>, output: &Option<Path>) {
551     // We need nested scopes here, because the intermediate results can keep
552     // large chunks of memory alive and we want to free them as soon as
553     // possible to keep the peak memory usage low
554     let (outputs, trans, sess) = {
555         let (outputs, expanded_crate, ast_map) = {
556             let krate = phase_1_parse_input(&sess, cfg, input);
557             if stop_after_phase_1(&sess) { return; }
558             let outputs = build_output_filenames(input,
559                                                  outdir,
560                                                  output,
561                                                  krate.attrs.as_slice(),
562                                                  &sess);
563             let loader = &mut Loader::new(&sess);
564             let id = link::find_crate_id(krate.attrs.as_slice(),
565                                          outputs.out_filestem);
566             let (expanded_crate, ast_map) = phase_2_configure_and_expand(&sess, loader,
567                                                                          krate, &id);
568             (outputs, expanded_crate, ast_map)
569         };
570         write_out_deps(&sess, input, &outputs, &expanded_crate).unwrap();
571
572         if stop_after_phase_2(&sess) { return; }
573
574         let analysis = phase_3_run_analysis_passes(sess, &expanded_crate, ast_map);
575         if stop_after_phase_3(&analysis.ty_cx.sess) { return; }
576         let (tcx, trans) = phase_4_translate_to_llvm(expanded_crate,
577                                                      analysis, &outputs);
578
579         // Discard interned strings as they are no longer required.
580         token::get_ident_interner().clear();
581
582         (outputs, trans, tcx.sess)
583     };
584     phase_5_run_llvm_passes(&sess, &trans, &outputs);
585     if stop_after_phase_5(&sess) { return; }
586     phase_6_link_output(&sess, &trans, &outputs);
587 }
588
589 struct IdentifiedAnnotation;
590
591 impl pprust::PpAnn for IdentifiedAnnotation {
592     fn pre(&self,
593            s: &mut pprust::State,
594            node: pprust::AnnNode) -> io::IoResult<()> {
595         match node {
596             pprust::NodeExpr(_) => s.popen(),
597             _ => Ok(())
598         }
599     }
600     fn post(&self,
601             s: &mut pprust::State,
602             node: pprust::AnnNode) -> io::IoResult<()> {
603         match node {
604             pprust::NodeItem(item) => {
605                 try!(pp::space(&mut s.s));
606                 s.synth_comment(item.id.to_str())
607             }
608             pprust::NodeBlock(blk) => {
609                 try!(pp::space(&mut s.s));
610                 s.synth_comment("block ".to_owned() + blk.id.to_str())
611             }
612             pprust::NodeExpr(expr) => {
613                 try!(pp::space(&mut s.s));
614                 try!(s.synth_comment(expr.id.to_str()));
615                 s.pclose()
616             }
617             pprust::NodePat(pat) => {
618                 try!(pp::space(&mut s.s));
619                 s.synth_comment("pat ".to_owned() + pat.id.to_str())
620             }
621         }
622     }
623 }
624
625 struct TypedAnnotation {
626     analysis: CrateAnalysis,
627 }
628
629 impl pprust::PpAnn for TypedAnnotation {
630     fn pre(&self,
631            s: &mut pprust::State,
632            node: pprust::AnnNode) -> io::IoResult<()> {
633         match node {
634             pprust::NodeExpr(_) => s.popen(),
635             _ => Ok(())
636         }
637     }
638     fn post(&self,
639             s: &mut pprust::State,
640             node: pprust::AnnNode) -> io::IoResult<()> {
641         let tcx = &self.analysis.ty_cx;
642         match node {
643             pprust::NodeExpr(expr) => {
644                 try!(pp::space(&mut s.s));
645                 try!(pp::word(&mut s.s, "as"));
646                 try!(pp::space(&mut s.s));
647                 try!(pp::word(&mut s.s,
648                                 ppaux::ty_to_str(tcx, ty::expr_ty(tcx, expr))));
649                 s.pclose()
650             }
651             _ => Ok(())
652         }
653     }
654 }
655
656 pub fn pretty_print_input(sess: Session,
657                           cfg: ast::CrateConfig,
658                           input: &Input,
659                           ppm: PpMode,
660                           ofile: Option<Path>) {
661     let krate = phase_1_parse_input(&sess, cfg, input);
662     let id = link::find_crate_id(krate.attrs.as_slice(), input.filestem());
663
664     let (krate, ast_map, is_expanded) = match ppm {
665         PpmExpanded | PpmExpandedIdentified | PpmTyped => {
666             let loader = &mut Loader::new(&sess);
667             let (krate, ast_map) = phase_2_configure_and_expand(&sess, loader,
668                                                                 krate, &id);
669             (krate, Some(ast_map), true)
670         }
671         _ => (krate, None, false)
672     };
673
674     let src_name = source_name(input);
675     let src = Vec::from_slice(sess.codemap().get_filemap(src_name).src.as_bytes());
676     let mut rdr = MemReader::new(src);
677
678     let out = match ofile {
679         None => ~io::stdout() as ~Writer,
680         Some(p) => {
681             let r = io::File::create(&p);
682             match r {
683                 Ok(w) => ~w as ~Writer,
684                 Err(e) => fail!("print-print failed to open {} due to {}",
685                                 p.display(), e),
686             }
687         }
688     };
689     match ppm {
690         PpmIdentified | PpmExpandedIdentified => {
691             pprust::print_crate(sess.codemap(),
692                                 sess.diagnostic(),
693                                 &krate,
694                                 src_name,
695                                 &mut rdr,
696                                 out,
697                                 &IdentifiedAnnotation,
698                                 is_expanded)
699         }
700         PpmTyped => {
701             let ast_map = ast_map.expect("--pretty=typed missing ast_map");
702             let analysis = phase_3_run_analysis_passes(sess, &krate, ast_map);
703             let annotation = TypedAnnotation {
704                 analysis: analysis
705             };
706             pprust::print_crate(annotation.analysis.ty_cx.sess.codemap(),
707                                 annotation.analysis.ty_cx.sess.diagnostic(),
708                                 &krate,
709                                 src_name,
710                                 &mut rdr,
711                                 out,
712                                 &annotation,
713                                 is_expanded)
714         }
715         _ => {
716             pprust::print_crate(sess.codemap(),
717                                 sess.diagnostic(),
718                                 &krate,
719                                 src_name,
720                                 &mut rdr,
721                                 out,
722                                 &pprust::NoAnn,
723                                 is_expanded)
724         }
725     }.unwrap()
726
727 }
728
729 pub fn get_os(triple: &str) -> Option<abi::Os> {
730     for &(name, os) in os_names.iter() {
731         if triple.contains(name) { return Some(os) }
732     }
733     None
734 }
735 static os_names : &'static [(&'static str, abi::Os)] = &'static [
736     ("mingw32", abi::OsWin32),
737     ("win32",   abi::OsWin32),
738     ("darwin",  abi::OsMacos),
739     ("android", abi::OsAndroid),
740     ("linux",   abi::OsLinux),
741     ("freebsd", abi::OsFreebsd)];
742
743 pub fn get_arch(triple: &str) -> Option<abi::Architecture> {
744     for &(arch, abi) in architecture_abis.iter() {
745         if triple.contains(arch) { return Some(abi) }
746     }
747     None
748 }
749 static architecture_abis : &'static [(&'static str, abi::Architecture)] = &'static [
750     ("i386",   abi::X86),
751     ("i486",   abi::X86),
752     ("i586",   abi::X86),
753     ("i686",   abi::X86),
754     ("i786",   abi::X86),
755
756     ("x86_64", abi::X86_64),
757
758     ("arm",    abi::Arm),
759     ("xscale", abi::Arm),
760     ("thumb",  abi::Arm),
761
762     ("mips",   abi::Mips)];
763
764 pub fn build_target_config(sopts: &session::Options) -> session::Config {
765     let os = match get_os(sopts.target_triple) {
766       Some(os) => os,
767       None => early_error("unknown operating system")
768     };
769     let arch = match get_arch(sopts.target_triple) {
770       Some(arch) => arch,
771       None => early_error("unknown architecture: " + sopts.target_triple)
772     };
773     let (int_type, uint_type) = match arch {
774       abi::X86 => (ast::TyI32, ast::TyU32),
775       abi::X86_64 => (ast::TyI64, ast::TyU64),
776       abi::Arm => (ast::TyI32, ast::TyU32),
777       abi::Mips => (ast::TyI32, ast::TyU32)
778     };
779     let target_triple = sopts.target_triple.clone();
780     let target_strs = match arch {
781       abi::X86 => x86::get_target_strs(target_triple, os),
782       abi::X86_64 => x86_64::get_target_strs(target_triple, os),
783       abi::Arm => arm::get_target_strs(target_triple, os),
784       abi::Mips => mips::get_target_strs(target_triple, os)
785     };
786     session::Config {
787         os: os,
788         arch: arch,
789         target_strs: target_strs,
790         int_type: int_type,
791         uint_type: uint_type,
792     }
793 }
794
795 pub fn host_triple() -> &'static str {
796     // Get the host triple out of the build environment. This ensures that our
797     // idea of the host triple is the same as for the set of libraries we've
798     // actually built.  We can't just take LLVM's host triple because they
799     // normalize all ix86 architectures to i386.
800     //
801     // Instead of grabbing the host triple (for the current host), we grab (at
802     // compile time) the target triple that this rustc is built with and
803     // calling that (at runtime) the host triple.
804     env!("CFG_COMPILER_HOST_TRIPLE")
805 }
806
807 pub fn build_session_options(matches: &getopts::Matches) -> session::Options {
808     let mut crate_types: Vec<CrateType> = Vec::new();
809     let unparsed_crate_types = matches.opt_strs("crate-type");
810     for unparsed_crate_type in unparsed_crate_types.iter() {
811         for part in unparsed_crate_type.split(',') {
812             let new_part = match part {
813                 "lib"       => session::default_lib_output(),
814                 "rlib"      => session::CrateTypeRlib,
815                 "staticlib" => session::CrateTypeStaticlib,
816                 "dylib"     => session::CrateTypeDylib,
817                 "bin"       => session::CrateTypeExecutable,
818                 _ => early_error(format!("unknown crate type: `{}`", part))
819             };
820             crate_types.push(new_part)
821         }
822     }
823
824     let parse_only = matches.opt_present("parse-only");
825     let no_trans = matches.opt_present("no-trans");
826     let no_analysis = matches.opt_present("no-analysis");
827
828     let lint_levels = [lint::allow, lint::warn,
829                        lint::deny, lint::forbid];
830     let mut lint_opts = Vec::new();
831     let lint_dict = lint::get_lint_dict();
832     for level in lint_levels.iter() {
833         let level_name = lint::level_to_str(*level);
834
835         let level_short = level_name.slice_chars(0, 1);
836         let level_short = level_short.to_ascii().to_upper().into_str();
837         let flags = matches.opt_strs(level_short).move_iter().collect::<Vec<_>>().append(
838                                    matches.opt_strs(level_name).as_slice());
839         for lint_name in flags.iter() {
840             let lint_name = lint_name.replace("-", "_");
841             match lint_dict.find_equiv(&lint_name) {
842               None => {
843                 early_error(format!("unknown {} flag: {}",
844                                     level_name, lint_name));
845               }
846               Some(lint) => {
847                 lint_opts.push((lint.lint, *level));
848               }
849             }
850         }
851     }
852
853     let mut debugging_opts = 0;
854     let debug_flags = matches.opt_strs("Z");
855     let debug_map = session::debugging_opts_map();
856     for debug_flag in debug_flags.iter() {
857         let mut this_bit = 0;
858         for tuple in debug_map.iter() {
859             let (name, bit) = match *tuple { (ref a, _, b) => (a, b) };
860             if *name == *debug_flag { this_bit = bit; break; }
861         }
862         if this_bit == 0 {
863             early_error(format!("unknown debug flag: {}", *debug_flag))
864         }
865         debugging_opts |= this_bit;
866     }
867
868     if debugging_opts & session::DEBUG_LLVM != 0 {
869         unsafe { llvm::LLVMSetDebug(1); }
870     }
871
872     let mut output_types = Vec::new();
873     if !parse_only && !no_trans {
874         let unparsed_output_types = matches.opt_strs("emit");
875         for unparsed_output_type in unparsed_output_types.iter() {
876             for part in unparsed_output_type.split(',') {
877                 let output_type = match part.as_slice() {
878                     "asm"  => link::OutputTypeAssembly,
879                     "ir"   => link::OutputTypeLlvmAssembly,
880                     "bc"   => link::OutputTypeBitcode,
881                     "obj"  => link::OutputTypeObject,
882                     "link" => link::OutputTypeExe,
883                     _ => early_error(format!("unknown emission type: `{}`", part))
884                 };
885                 output_types.push(output_type)
886             }
887         }
888     };
889     output_types.as_mut_slice().sort();
890     output_types.dedup();
891     if output_types.len() == 0 {
892         output_types.push(link::OutputTypeExe);
893     }
894
895     let sysroot_opt = matches.opt_str("sysroot").map(|m| Path::new(m));
896     let target = matches.opt_str("target").unwrap_or(host_triple().to_owned());
897     let opt_level = {
898         if (debugging_opts & session::NO_OPT) != 0 {
899             No
900         } else if matches.opt_present("O") {
901             if matches.opt_present("opt-level") {
902                 early_error("-O and --opt-level both provided");
903             }
904             Default
905         } else if matches.opt_present("opt-level") {
906             match matches.opt_str("opt-level").as_ref().map(|s| s.as_slice()) {
907                 None      |
908                 Some("0") => No,
909                 Some("1") => Less,
910                 Some("2") => Default,
911                 Some("3") => Aggressive,
912                 Some(arg) => {
913                     early_error(format!("optimization level needs to be between 0-3 \
914                                         (instead was `{}`)", arg));
915                 }
916             }
917         } else {
918             No
919         }
920     };
921     let gc = debugging_opts & session::GC != 0;
922     let debuginfo = if matches.opt_present("g") {
923         if matches.opt_present("debuginfo") {
924             early_error("-g and --debuginfo both provided");
925         }
926         FullDebugInfo
927     } else if matches.opt_present("debuginfo") {
928         match matches.opt_str("debuginfo").as_ref().map(|s| s.as_slice()) {
929             Some("0") => NoDebugInfo,
930             Some("1") => LimitedDebugInfo,
931             None      |
932             Some("2") => FullDebugInfo,
933             Some(arg) => {
934                 early_error(format!("optimization level needs to be between 0-3 \
935                                     (instead was `{}`)", arg));
936             }
937         }
938     } else {
939         NoDebugInfo
940     };
941
942     let addl_lib_search_paths = matches.opt_strs("L").iter().map(|s| {
943         Path::new(s.as_slice())
944     }).collect();
945
946     let cfg = parse_cfgspecs(matches.opt_strs("cfg").move_iter().collect());
947     let test = matches.opt_present("test");
948     let write_dependency_info = (matches.opt_present("dep-info"),
949                                  matches.opt_str("dep-info").map(|p| Path::new(p)));
950
951     let print_metas = (matches.opt_present("crate-id"),
952                        matches.opt_present("crate-name"),
953                        matches.opt_present("crate-file-name"));
954     let cg = build_codegen_options(matches);
955
956     session::Options {
957         crate_types: crate_types,
958         gc: gc,
959         optimize: opt_level,
960         debuginfo: debuginfo,
961         lint_opts: lint_opts,
962         output_types: output_types,
963         addl_lib_search_paths: RefCell::new(addl_lib_search_paths),
964         maybe_sysroot: sysroot_opt,
965         target_triple: target,
966         cfg: cfg,
967         test: test,
968         parse_only: parse_only,
969         no_trans: no_trans,
970         no_analysis: no_analysis,
971         debugging_opts: debugging_opts,
972         write_dependency_info: write_dependency_info,
973         print_metas: print_metas,
974         cg: cg,
975     }
976 }
977
978 pub fn build_codegen_options(matches: &getopts::Matches)
979         -> session::CodegenOptions
980 {
981     let mut cg = session::basic_codegen_options();
982     for option in matches.opt_strs("C").move_iter() {
983         let mut iter = option.splitn('=', 1);
984         let key = iter.next().unwrap();
985         let value = iter.next();
986         let option_to_lookup = key.replace("-", "_");
987         let mut found = false;
988         for &(candidate, setter, _) in session::CG_OPTIONS.iter() {
989             if option_to_lookup.as_slice() != candidate { continue }
990             if !setter(&mut cg, value) {
991                 match value {
992                     Some(..) => early_error(format!("codegen option `{}` takes \
993                                                      no value", key)),
994                     None => early_error(format!("codegen option `{0}` requires \
995                                                  a value (-C {0}=<value>)",
996                                                 key))
997                 }
998             }
999             found = true;
1000             break;
1001         }
1002         if !found {
1003             early_error(format!("unknown codegen option: `{}`", key));
1004         }
1005     }
1006     return cg;
1007 }
1008
1009 pub fn build_session(sopts: session::Options,
1010                      local_crate_source_file: Option<Path>)
1011                      -> Session {
1012     let codemap = codemap::CodeMap::new();
1013     let diagnostic_handler =
1014         diagnostic::default_handler();
1015     let span_diagnostic_handler =
1016         diagnostic::mk_span_handler(diagnostic_handler, codemap);
1017
1018     build_session_(sopts, local_crate_source_file, span_diagnostic_handler)
1019 }
1020
1021 pub fn build_session_(sopts: session::Options,
1022                       local_crate_source_file: Option<Path>,
1023                       span_diagnostic: diagnostic::SpanHandler)
1024                       -> Session {
1025     let target_cfg = build_target_config(&sopts);
1026     let p_s = parse::new_parse_sess_special_handler(span_diagnostic);
1027     let default_sysroot = match sopts.maybe_sysroot {
1028         Some(_) => None,
1029         None => Some(filesearch::get_or_default_sysroot())
1030     };
1031
1032     // Make the path absolute, if necessary
1033     let local_crate_source_file = local_crate_source_file.map(|path|
1034         if path.is_absolute() {
1035             path.clone()
1036         } else {
1037             os::getcwd().join(path.clone())
1038         }
1039     );
1040
1041     Session {
1042         targ_cfg: target_cfg,
1043         opts: sopts,
1044         cstore: CStore::new(token::get_ident_interner()),
1045         parse_sess: p_s,
1046         // For a library crate, this is always none
1047         entry_fn: RefCell::new(None),
1048         entry_type: Cell::new(None),
1049         macro_registrar_fn: Cell::new(None),
1050         default_sysroot: default_sysroot,
1051         building_library: Cell::new(false),
1052         local_crate_source_file: local_crate_source_file,
1053         working_dir: os::getcwd(),
1054         lints: RefCell::new(NodeMap::new()),
1055         node_id: Cell::new(1),
1056         crate_types: RefCell::new(Vec::new()),
1057         features: front::feature_gate::Features::new(),
1058         recursion_limit: Cell::new(64),
1059     }
1060 }
1061
1062 pub fn parse_pretty(sess: &Session, name: &str) -> PpMode {
1063     match name {
1064       &"normal" => PpmNormal,
1065       &"expanded" => PpmExpanded,
1066       &"typed" => PpmTyped,
1067       &"expanded,identified" => PpmExpandedIdentified,
1068       &"identified" => PpmIdentified,
1069       _ => {
1070         sess.fatal("argument to `pretty` must be one of `normal`, \
1071                     `expanded`, `typed`, `identified`, \
1072                     or `expanded,identified`");
1073       }
1074     }
1075 }
1076
1077 // rustc command line options
1078 pub fn optgroups() -> Vec<getopts::OptGroup> {
1079  vec!(
1080   optflag("h", "help", "Display this message"),
1081   optmulti("", "cfg", "Configure the compilation environment", "SPEC"),
1082   optmulti("L", "",   "Add a directory to the library search path", "PATH"),
1083   optmulti("", "crate-type", "Comma separated list of types of crates for the compiler to emit",
1084            "[bin|lib|rlib|dylib|staticlib]"),
1085   optmulti("", "emit", "Comma separated list of types of output for the compiler to emit",
1086            "[asm|bc|ir|obj|link]"),
1087   optflag("", "crate-id", "Output the crate id and exit"),
1088   optflag("", "crate-name", "Output the crate name and exit"),
1089   optflag("", "crate-file-name", "Output the file(s) that would be written if compilation \
1090           continued and exit"),
1091   optflag("g",  "",  "Equivalent to --debuginfo=2"),
1092   optopt("",  "debuginfo",  "Emit DWARF debug info to the objects created:
1093          0 = no debug info,
1094          1 = line-tables only (for stacktraces and breakpoints),
1095          2 = full debug info with variable and type information (same as -g)", "LEVEL"),
1096   optflag("", "no-trans", "Run all passes except translation; no output"),
1097   optflag("", "no-analysis",
1098           "Parse and expand the source, but run no analysis and produce no output"),
1099   optflag("O", "", "Equivalent to --opt-level=2"),
1100   optopt("o", "", "Write output to <filename>", "FILENAME"),
1101   optopt("", "opt-level", "Optimize with possible levels 0-3", "LEVEL"),
1102   optopt( "",  "out-dir", "Write output to compiler-chosen filename in <dir>", "DIR"),
1103   optflag("", "parse-only", "Parse only; do not compile, assemble, or link"),
1104   optflagopt("", "pretty",
1105              "Pretty-print the input instead of compiling;
1106               valid types are: normal (un-annotated source),
1107               expanded (crates expanded),
1108               typed (crates expanded, with type annotations),
1109               or identified (fully parenthesized,
1110               AST nodes and blocks with IDs)", "TYPE"),
1111   optflagopt("", "dep-info",
1112              "Output dependency info to <filename> after compiling, \
1113               in a format suitable for use by Makefiles", "FILENAME"),
1114   optopt("", "sysroot", "Override the system root", "PATH"),
1115   optflag("", "test", "Build a test harness"),
1116   optopt("", "target", "Target triple cpu-manufacturer-kernel[-os]
1117                         to compile for (see chapter 3.4 of http://www.sourceware.org/autobook/
1118                         for details)", "TRIPLE"),
1119   optmulti("W", "warn", "Set lint warnings", "OPT"),
1120   optmulti("A", "allow", "Set lint allowed", "OPT"),
1121   optmulti("D", "deny", "Set lint denied", "OPT"),
1122   optmulti("F", "forbid", "Set lint forbidden", "OPT"),
1123   optmulti("C", "codegen", "Set a codegen option", "OPT[=VALUE]"),
1124   optmulti("Z", "", "Set internal debugging options", "FLAG"),
1125   optflag( "v", "version", "Print version info and exit"))
1126 }
1127
1128 pub struct OutputFilenames {
1129     pub out_directory: Path,
1130     pub out_filestem: ~str,
1131     pub single_output_file: Option<Path>,
1132 }
1133
1134 impl OutputFilenames {
1135     pub fn path(&self, flavor: link::OutputType) -> Path {
1136         match self.single_output_file {
1137             Some(ref path) => return path.clone(),
1138             None => {}
1139         }
1140         self.temp_path(flavor)
1141     }
1142
1143     pub fn temp_path(&self, flavor: link::OutputType) -> Path {
1144         let base = self.out_directory.join(self.out_filestem.as_slice());
1145         match flavor {
1146             link::OutputTypeBitcode => base.with_extension("bc"),
1147             link::OutputTypeAssembly => base.with_extension("s"),
1148             link::OutputTypeLlvmAssembly => base.with_extension("ll"),
1149             link::OutputTypeObject => base.with_extension("o"),
1150             link::OutputTypeExe => base,
1151         }
1152     }
1153
1154     pub fn with_extension(&self, extension: &str) -> Path {
1155         let stem = self.out_filestem.as_slice();
1156         self.out_directory.join(stem).with_extension(extension)
1157     }
1158 }
1159
1160 pub fn build_output_filenames(input: &Input,
1161                               odir: &Option<Path>,
1162                               ofile: &Option<Path>,
1163                               attrs: &[ast::Attribute],
1164                               sess: &Session)
1165                            -> OutputFilenames {
1166     match *ofile {
1167         None => {
1168             // "-" as input file will cause the parser to read from stdin so we
1169             // have to make up a name
1170             // We want to toss everything after the final '.'
1171             let dirpath = match *odir {
1172                 Some(ref d) => d.clone(),
1173                 None => Path::new(".")
1174             };
1175
1176             let mut stem = input.filestem();
1177
1178             // If a crateid is present, we use it as the link name
1179             let crateid = attr::find_crateid(attrs);
1180             match crateid {
1181                 None => {}
1182                 Some(crateid) => stem = crateid.name.to_str(),
1183             }
1184             OutputFilenames {
1185                 out_directory: dirpath,
1186                 out_filestem: stem,
1187                 single_output_file: None,
1188             }
1189         }
1190
1191         Some(ref out_file) => {
1192             let ofile = if sess.opts.output_types.len() > 1 {
1193                 sess.warn("ignoring specified output filename because multiple \
1194                            outputs were requested");
1195                 None
1196             } else {
1197                 Some(out_file.clone())
1198             };
1199             if *odir != None {
1200                 sess.warn("ignoring --out-dir flag due to -o flag.");
1201             }
1202             OutputFilenames {
1203                 out_directory: out_file.dir_path(),
1204                 out_filestem: out_file.filestem_str().unwrap().to_str(),
1205                 single_output_file: ofile,
1206             }
1207         }
1208     }
1209 }
1210
1211 pub fn early_error(msg: &str) -> ! {
1212     let mut emitter = diagnostic::EmitterWriter::stderr();
1213     emitter.emit(None, msg, diagnostic::Fatal);
1214     fail!(diagnostic::FatalError);
1215 }
1216
1217 pub fn list_metadata(sess: &Session, path: &Path,
1218                      out: &mut io::Writer) -> io::IoResult<()> {
1219     metadata::loader::list_file_metadata(
1220         session::sess_os_to_meta_os(sess.targ_cfg.os), path, out)
1221 }
1222
1223 #[cfg(test)]
1224 mod test {
1225
1226     use driver::driver::{build_configuration, build_session};
1227     use driver::driver::{build_session_options, optgroups};
1228
1229     use getopts::getopts;
1230     use syntax::attr;
1231     use syntax::attr::AttrMetaMethods;
1232
1233     // When the user supplies --test we should implicitly supply --cfg test
1234     #[test]
1235     fn test_switch_implies_cfg_test() {
1236         let matches =
1237             &match getopts(["--test".to_owned()], optgroups().as_slice()) {
1238               Ok(m) => m,
1239               Err(f) => fail!("test_switch_implies_cfg_test: {}", f.to_err_msg())
1240             };
1241         let sessopts = build_session_options(matches);
1242         let sess = build_session(sessopts, None);
1243         let cfg = build_configuration(&sess);
1244         assert!((attr::contains_name(cfg.as_slice(), "test")));
1245     }
1246
1247     // When the user supplies --test and --cfg test, don't implicitly add
1248     // another --cfg test
1249     #[test]
1250     fn test_switch_implies_cfg_test_unless_cfg_test() {
1251         let matches =
1252             &match getopts(["--test".to_owned(), "--cfg=test".to_owned()],
1253                            optgroups().as_slice()) {
1254               Ok(m) => m,
1255               Err(f) => {
1256                 fail!("test_switch_implies_cfg_test_unless_cfg_test: {}",
1257                        f.to_err_msg());
1258               }
1259             };
1260         let sessopts = build_session_options(matches);
1261         let sess = build_session(sessopts, None);
1262         let cfg = build_configuration(&sess);
1263         let mut test_items = cfg.iter().filter(|m| m.name().equiv(&("test")));
1264         assert!(test_items.next().is_some());
1265         assert!(test_items.next().is_none());
1266     }
1267 }