]> git.lizzy.rs Git - rust.git/blob - src/librustc/driver/driver.rs
auto merge of #13711 : alexcrichton/rust/snapshots, r=brson
[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, capture_map} =
350         time(time_passes, "compute moves", (), |_|
351              middle::moves::compute_moves(&ty_cx, krate));
352
353     time(time_passes, "match checking", (), |_|
354          middle::check_match::check_crate(&ty_cx, &moves_map, krate));
355
356     time(time_passes, "liveness checking", (), |_|
357          middle::liveness::check_crate(&ty_cx, &capture_map, krate));
358
359     time(time_passes, "borrow checking", (), |_|
360          middle::borrowck::check_crate(&ty_cx, &moves_map,
361                                        &capture_map, krate));
362
363     drop(moves_map);
364
365     time(time_passes, "kind checking", (), |_|
366          kind::check_crate(&ty_cx, krate));
367
368     let reachable_map =
369         time(time_passes, "reachability checking", (), |_|
370              reachable::find_reachable(&ty_cx, &exported_items));
371
372     time(time_passes, "death checking", (), |_| {
373         middle::dead::check_crate(&ty_cx,
374                                   &exported_items,
375                                   &reachable_map,
376                                   krate)
377     });
378
379     time(time_passes, "lint checking", (), |_|
380          lint::check_crate(&ty_cx, &exported_items, krate));
381
382     CrateAnalysis {
383         exp_map2: exp_map2,
384         ty_cx: ty_cx,
385         exported_items: exported_items,
386         public_items: public_items,
387         maps: astencode::Maps {
388             capture_map: RefCell::new(capture_map)
389         },
390         reachable: reachable_map
391     }
392 }
393
394 pub struct CrateTranslation {
395     pub context: ContextRef,
396     pub module: ModuleRef,
397     pub metadata_module: ModuleRef,
398     pub link: LinkMeta,
399     pub metadata: Vec<u8>,
400     pub reachable: Vec<~str>,
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     // Option dance to work around the lack of stack once closures.
409     let time_passes = analysis.ty_cx.sess.time_passes();
410     let mut analysis = Some(analysis);
411     time(time_passes, "translation", krate, |krate|
412          trans::base::trans_crate(krate, analysis.take_unwrap(), outputs))
413 }
414
415 /// Run LLVM itself, producing a bitcode file, assembly file or object file
416 /// as a side effect.
417 pub fn phase_5_run_llvm_passes(sess: &Session,
418                                trans: &CrateTranslation,
419                                outputs: &OutputFilenames) {
420     if sess.opts.cg.no_integrated_as {
421         let output_type = link::OutputTypeAssembly;
422
423         time(sess.time_passes(), "LLVM passes", (), |_|
424             link::write::run_passes(sess, trans, [output_type], outputs));
425
426         link::write::run_assembler(sess, outputs);
427
428         // Remove assembly source, unless --save-temps was specified
429         if !sess.opts.cg.save_temps {
430             fs::unlink(&outputs.temp_path(link::OutputTypeAssembly)).unwrap();
431         }
432     } else {
433         time(sess.time_passes(), "LLVM passes", (), |_|
434             link::write::run_passes(sess,
435                                     trans,
436                                     sess.opts.output_types.as_slice(),
437                                     outputs));
438     }
439 }
440
441 /// Run the linker on any artifacts that resulted from the LLVM run.
442 /// This should produce either a finished executable or library.
443 pub fn phase_6_link_output(sess: &Session,
444                            trans: &CrateTranslation,
445                            outputs: &OutputFilenames) {
446     time(sess.time_passes(), "linking", (), |_|
447          link::link_binary(sess,
448                            trans,
449                            outputs,
450                            &trans.link.crateid));
451 }
452
453 pub fn stop_after_phase_3(sess: &Session) -> bool {
454    if sess.opts.no_trans {
455         debug!("invoked with --no-trans, returning early from compile_input");
456         return true;
457     }
458     return false;
459 }
460
461 pub fn stop_after_phase_1(sess: &Session) -> bool {
462     if sess.opts.parse_only {
463         debug!("invoked with --parse-only, returning early from compile_input");
464         return true;
465     }
466     if sess.show_span() {
467         return true;
468     }
469     return sess.opts.debugging_opts & session::AST_JSON_NOEXPAND != 0;
470 }
471
472 pub fn stop_after_phase_2(sess: &Session) -> bool {
473     if sess.opts.no_analysis {
474         debug!("invoked with --no-analysis, returning early from compile_input");
475         return true;
476     }
477     return sess.opts.debugging_opts & session::AST_JSON != 0;
478 }
479
480 pub fn stop_after_phase_5(sess: &Session) -> bool {
481     if !sess.opts.output_types.iter().any(|&i| i == link::OutputTypeExe) {
482         debug!("not building executable, returning early from compile_input");
483         return true;
484     }
485     return false;
486 }
487
488 fn write_out_deps(sess: &Session,
489                   input: &Input,
490                   outputs: &OutputFilenames,
491                   krate: &ast::Crate) {
492     let id = link::find_crate_id(krate.attrs.as_slice(), outputs.out_filestem);
493
494     let mut out_filenames = Vec::new();
495     for output_type in sess.opts.output_types.iter() {
496         let file = outputs.path(*output_type);
497         match *output_type {
498             link::OutputTypeExe => {
499                 for output in sess.crate_types.borrow().iter() {
500                     let p = link::filename_for_input(sess, *output, &id, &file);
501                     out_filenames.push(p);
502                 }
503             }
504             _ => { out_filenames.push(file); }
505         }
506     }
507
508     // Write out dependency rules to the dep-info file if requested with
509     // --dep-info
510     let deps_filename = match sess.opts.write_dependency_info {
511         // Use filename from --dep-file argument if given
512         (true, Some(ref filename)) => filename.clone(),
513         // Use default filename: crate source filename with extension replaced
514         // by ".d"
515         (true, None) => match *input {
516             FileInput(..) => outputs.with_extension("d"),
517             StrInput(..) => {
518                 sess.warn("can not write --dep-info without a filename \
519                            when compiling stdin.");
520                 return
521             },
522         },
523         _ => return,
524     };
525
526     let result = (|| {
527         // Build a list of files used to compile the output and
528         // write Makefile-compatible dependency rules
529         let files: Vec<~str> = sess.codemap().files.borrow()
530                                    .iter().filter(|fmap| fmap.is_real_file())
531                                    .map(|fmap| fmap.name.clone())
532                                    .collect();
533         let mut file = try!(io::File::create(&deps_filename));
534         for path in out_filenames.iter() {
535             try!(write!(&mut file as &mut Writer,
536                           "{}: {}\n\n", path.display(), files.connect(" ")));
537         }
538         Ok(())
539     })();
540
541     match result {
542         Ok(()) => {}
543         Err(e) => {
544             sess.fatal(format!("error writing dependencies to `{}`: {}",
545                                deps_filename.display(), e));
546         }
547     }
548 }
549
550 pub fn compile_input(sess: Session, cfg: ast::CrateConfig, input: &Input,
551                      outdir: &Option<Path>, output: &Option<Path>) {
552     // We need nested scopes here, because the intermediate results can keep
553     // large chunks of memory alive and we want to free them as soon as
554     // possible to keep the peak memory usage low
555     let (outputs, trans, sess) = {
556         let (outputs, expanded_crate, ast_map) = {
557             let krate = phase_1_parse_input(&sess, cfg, input);
558             if stop_after_phase_1(&sess) { return; }
559             let outputs = build_output_filenames(input,
560                                                  outdir,
561                                                  output,
562                                                  krate.attrs.as_slice(),
563                                                  &sess);
564             let loader = &mut Loader::new(&sess);
565             let id = link::find_crate_id(krate.attrs.as_slice(),
566                                          outputs.out_filestem);
567             let (expanded_crate, ast_map) = phase_2_configure_and_expand(&sess, loader,
568                                                                          krate, &id);
569             (outputs, expanded_crate, ast_map)
570         };
571         write_out_deps(&sess, input, &outputs, &expanded_crate);
572
573         if stop_after_phase_2(&sess) { return; }
574
575         let analysis = phase_3_run_analysis_passes(sess, &expanded_crate, ast_map);
576         if stop_after_phase_3(&analysis.ty_cx.sess) { return; }
577         let (tcx, trans) = phase_4_translate_to_llvm(expanded_crate,
578                                                      analysis, &outputs);
579
580         // Discard interned strings as they are no longer required.
581         token::get_ident_interner().clear();
582
583         (outputs, trans, tcx.sess)
584     };
585     phase_5_run_llvm_passes(&sess, &trans, &outputs);
586     if stop_after_phase_5(&sess) { return; }
587     phase_6_link_output(&sess, &trans, &outputs);
588 }
589
590 struct IdentifiedAnnotation;
591
592 impl pprust::PpAnn for IdentifiedAnnotation {
593     fn pre(&self,
594            s: &mut pprust::State,
595            node: pprust::AnnNode) -> io::IoResult<()> {
596         match node {
597             pprust::NodeExpr(_) => s.popen(),
598             _ => Ok(())
599         }
600     }
601     fn post(&self,
602             s: &mut pprust::State,
603             node: pprust::AnnNode) -> io::IoResult<()> {
604         match node {
605             pprust::NodeItem(item) => {
606                 try!(pp::space(&mut s.s));
607                 s.synth_comment(item.id.to_str())
608             }
609             pprust::NodeBlock(blk) => {
610                 try!(pp::space(&mut s.s));
611                 s.synth_comment("block ".to_owned() + blk.id.to_str())
612             }
613             pprust::NodeExpr(expr) => {
614                 try!(pp::space(&mut s.s));
615                 try!(s.synth_comment(expr.id.to_str()));
616                 s.pclose()
617             }
618             pprust::NodePat(pat) => {
619                 try!(pp::space(&mut s.s));
620                 s.synth_comment("pat ".to_owned() + pat.id.to_str())
621             }
622         }
623     }
624 }
625
626 struct TypedAnnotation {
627     analysis: CrateAnalysis,
628 }
629
630 impl pprust::PpAnn for TypedAnnotation {
631     fn pre(&self,
632            s: &mut pprust::State,
633            node: pprust::AnnNode) -> io::IoResult<()> {
634         match node {
635             pprust::NodeExpr(_) => s.popen(),
636             _ => Ok(())
637         }
638     }
639     fn post(&self,
640             s: &mut pprust::State,
641             node: pprust::AnnNode) -> io::IoResult<()> {
642         let tcx = &self.analysis.ty_cx;
643         match node {
644             pprust::NodeExpr(expr) => {
645                 try!(pp::space(&mut s.s));
646                 try!(pp::word(&mut s.s, "as"));
647                 try!(pp::space(&mut s.s));
648                 try!(pp::word(&mut s.s,
649                                 ppaux::ty_to_str(tcx, ty::expr_ty(tcx, expr))));
650                 s.pclose()
651             }
652             _ => Ok(())
653         }
654     }
655 }
656
657 pub fn pretty_print_input(sess: Session,
658                           cfg: ast::CrateConfig,
659                           input: &Input,
660                           ppm: PpMode,
661                           ofile: Option<Path>) {
662     let krate = phase_1_parse_input(&sess, cfg, input);
663     let id = link::find_crate_id(krate.attrs.as_slice(), input.filestem());
664
665     let (krate, ast_map, is_expanded) = match ppm {
666         PpmExpanded | PpmExpandedIdentified | PpmTyped => {
667             let loader = &mut Loader::new(&sess);
668             let (krate, ast_map) = phase_2_configure_and_expand(&sess, loader,
669                                                                 krate, &id);
670             (krate, Some(ast_map), true)
671         }
672         _ => (krate, None, false)
673     };
674
675     let src_name = source_name(input);
676     let src = Vec::from_slice(sess.codemap().get_filemap(src_name).src.as_bytes());
677     let mut rdr = MemReader::new(src);
678
679     let out = match ofile {
680         None => ~io::stdout() as ~Writer,
681         Some(p) => {
682             let r = io::File::create(&p);
683             match r {
684                 Ok(w) => ~w as ~Writer,
685                 Err(e) => fail!("print-print failed to open {} due to {}",
686                                 p.display(), e),
687             }
688         }
689     };
690     match ppm {
691         PpmIdentified | PpmExpandedIdentified => {
692             pprust::print_crate(sess.codemap(),
693                                 sess.diagnostic(),
694                                 &krate,
695                                 src_name,
696                                 &mut rdr,
697                                 out,
698                                 &IdentifiedAnnotation,
699                                 is_expanded)
700         }
701         PpmTyped => {
702             let ast_map = ast_map.expect("--pretty=typed missing ast_map");
703             let analysis = phase_3_run_analysis_passes(sess, &krate, ast_map);
704             let annotation = TypedAnnotation {
705                 analysis: analysis
706             };
707             pprust::print_crate(annotation.analysis.ty_cx.sess.codemap(),
708                                 annotation.analysis.ty_cx.sess.diagnostic(),
709                                 &krate,
710                                 src_name,
711                                 &mut rdr,
712                                 out,
713                                 &annotation,
714                                 is_expanded)
715         }
716         _ => {
717             pprust::print_crate(sess.codemap(),
718                                 sess.diagnostic(),
719                                 &krate,
720                                 src_name,
721                                 &mut rdr,
722                                 out,
723                                 &pprust::NoAnn,
724                                 is_expanded)
725         }
726     }.unwrap()
727
728 }
729
730 pub fn get_os(triple: &str) -> Option<abi::Os> {
731     for &(name, os) in os_names.iter() {
732         if triple.contains(name) { return Some(os) }
733     }
734     None
735 }
736 static os_names : &'static [(&'static str, abi::Os)] = &'static [
737     ("mingw32", abi::OsWin32),
738     ("win32",   abi::OsWin32),
739     ("darwin",  abi::OsMacos),
740     ("android", abi::OsAndroid),
741     ("linux",   abi::OsLinux),
742     ("freebsd", abi::OsFreebsd)];
743
744 pub fn get_arch(triple: &str) -> Option<abi::Architecture> {
745     for &(arch, abi) in architecture_abis.iter() {
746         if triple.contains(arch) { return Some(abi) }
747     }
748     None
749 }
750 static architecture_abis : &'static [(&'static str, abi::Architecture)] = &'static [
751     ("i386",   abi::X86),
752     ("i486",   abi::X86),
753     ("i586",   abi::X86),
754     ("i686",   abi::X86),
755     ("i786",   abi::X86),
756
757     ("x86_64", abi::X86_64),
758
759     ("arm",    abi::Arm),
760     ("xscale", abi::Arm),
761     ("thumb",  abi::Arm),
762
763     ("mips",   abi::Mips)];
764
765 pub fn build_target_config(sopts: &session::Options) -> session::Config {
766     let os = match get_os(sopts.target_triple) {
767       Some(os) => os,
768       None => early_error("unknown operating system")
769     };
770     let arch = match get_arch(sopts.target_triple) {
771       Some(arch) => arch,
772       None => early_error("unknown architecture: " + sopts.target_triple)
773     };
774     let (int_type, uint_type) = match arch {
775       abi::X86 => (ast::TyI32, ast::TyU32),
776       abi::X86_64 => (ast::TyI64, ast::TyU64),
777       abi::Arm => (ast::TyI32, ast::TyU32),
778       abi::Mips => (ast::TyI32, ast::TyU32)
779     };
780     let target_triple = sopts.target_triple.clone();
781     let target_strs = match arch {
782       abi::X86 => x86::get_target_strs(target_triple, os),
783       abi::X86_64 => x86_64::get_target_strs(target_triple, os),
784       abi::Arm => arm::get_target_strs(target_triple, os),
785       abi::Mips => mips::get_target_strs(target_triple, os)
786     };
787     session::Config {
788         os: os,
789         arch: arch,
790         target_strs: target_strs,
791         int_type: int_type,
792         uint_type: uint_type,
793     }
794 }
795
796 pub fn host_triple() -> &'static str {
797     // Get the host triple out of the build environment. This ensures that our
798     // idea of the host triple is the same as for the set of libraries we've
799     // actually built.  We can't just take LLVM's host triple because they
800     // normalize all ix86 architectures to i386.
801     //
802     // Instead of grabbing the host triple (for the current host), we grab (at
803     // compile time) the target triple that this rustc is built with and
804     // calling that (at runtime) the host triple.
805     env!("CFG_COMPILER_HOST_TRIPLE")
806 }
807
808 pub fn build_session_options(matches: &getopts::Matches) -> session::Options {
809     let mut crate_types: Vec<CrateType> = Vec::new();
810     let unparsed_crate_types = matches.opt_strs("crate-type");
811     for unparsed_crate_type in unparsed_crate_types.iter() {
812         for part in unparsed_crate_type.split(',') {
813             let new_part = match part {
814                 "lib"       => session::default_lib_output(),
815                 "rlib"      => session::CrateTypeRlib,
816                 "staticlib" => session::CrateTypeStaticlib,
817                 "dylib"     => session::CrateTypeDylib,
818                 "bin"       => session::CrateTypeExecutable,
819                 _ => early_error(format!("unknown crate type: `{}`", part))
820             };
821             crate_types.push(new_part)
822         }
823     }
824
825     let parse_only = matches.opt_present("parse-only");
826     let no_trans = matches.opt_present("no-trans");
827     let no_analysis = matches.opt_present("no-analysis");
828
829     let lint_levels = [lint::allow, lint::warn,
830                        lint::deny, lint::forbid];
831     let mut lint_opts = Vec::new();
832     let lint_dict = lint::get_lint_dict();
833     for level in lint_levels.iter() {
834         let level_name = lint::level_to_str(*level);
835
836         let level_short = level_name.slice_chars(0, 1);
837         let level_short = level_short.to_ascii().to_upper().into_str();
838         let flags = matches.opt_strs(level_short).move_iter().collect::<Vec<_>>().append(
839                                    matches.opt_strs(level_name).as_slice());
840         for lint_name in flags.iter() {
841             let lint_name = lint_name.replace("-", "_");
842             match lint_dict.find_equiv(&lint_name) {
843               None => {
844                 early_error(format!("unknown {} flag: {}",
845                                     level_name, lint_name));
846               }
847               Some(lint) => {
848                 lint_opts.push((lint.lint, *level));
849               }
850             }
851         }
852     }
853
854     let mut debugging_opts = 0;
855     let debug_flags = matches.opt_strs("Z");
856     let debug_map = session::debugging_opts_map();
857     for debug_flag in debug_flags.iter() {
858         let mut this_bit = 0;
859         for tuple in debug_map.iter() {
860             let (name, bit) = match *tuple { (ref a, _, b) => (a, b) };
861             if *name == *debug_flag { this_bit = bit; break; }
862         }
863         if this_bit == 0 {
864             early_error(format!("unknown debug flag: {}", *debug_flag))
865         }
866         debugging_opts |= this_bit;
867     }
868
869     if debugging_opts & session::DEBUG_LLVM != 0 {
870         unsafe { llvm::LLVMSetDebug(1); }
871     }
872
873     let mut output_types = Vec::new();
874     if !parse_only && !no_trans {
875         let unparsed_output_types = matches.opt_strs("emit");
876         for unparsed_output_type in unparsed_output_types.iter() {
877             for part in unparsed_output_type.split(',') {
878                 let output_type = match part.as_slice() {
879                     "asm"  => link::OutputTypeAssembly,
880                     "ir"   => link::OutputTypeLlvmAssembly,
881                     "bc"   => link::OutputTypeBitcode,
882                     "obj"  => link::OutputTypeObject,
883                     "link" => link::OutputTypeExe,
884                     _ => early_error(format!("unknown emission type: `{}`", part))
885                 };
886                 output_types.push(output_type)
887             }
888         }
889     };
890     output_types.as_mut_slice().sort();
891     output_types.dedup();
892     if output_types.len() == 0 {
893         output_types.push(link::OutputTypeExe);
894     }
895
896     let sysroot_opt = matches.opt_str("sysroot").map(|m| Path::new(m));
897     let target = matches.opt_str("target").unwrap_or(host_triple().to_owned());
898     let opt_level = {
899         if (debugging_opts & session::NO_OPT) != 0 {
900             No
901         } else if matches.opt_present("O") {
902             if matches.opt_present("opt-level") {
903                 early_error("-O and --opt-level both provided");
904             }
905             Default
906         } else if matches.opt_present("opt-level") {
907             match matches.opt_str("opt-level").as_ref().map(|s| s.as_slice()) {
908                 None      |
909                 Some("0") => No,
910                 Some("1") => Less,
911                 Some("2") => Default,
912                 Some("3") => Aggressive,
913                 Some(arg) => {
914                     early_error(format!("optimization level needs to be between 0-3 \
915                                         (instead was `{}`)", arg));
916                 }
917             }
918         } else {
919             No
920         }
921     };
922     let gc = debugging_opts & session::GC != 0;
923     let debuginfo = if matches.opt_present("g") {
924         if matches.opt_present("debuginfo") {
925             early_error("-g and --debuginfo both provided");
926         }
927         FullDebugInfo
928     } else if matches.opt_present("debuginfo") {
929         match matches.opt_str("debuginfo").as_ref().map(|s| s.as_slice()) {
930             Some("0") => NoDebugInfo,
931             Some("1") => LimitedDebugInfo,
932             None      |
933             Some("2") => FullDebugInfo,
934             Some(arg) => {
935                 early_error(format!("optimization level needs to be between 0-3 \
936                                     (instead was `{}`)", arg));
937             }
938         }
939     } else {
940         NoDebugInfo
941     };
942
943     let addl_lib_search_paths = matches.opt_strs("L").iter().map(|s| {
944         Path::new(s.as_slice())
945     }).collect();
946
947     let cfg = parse_cfgspecs(matches.opt_strs("cfg").move_iter().collect());
948     let test = matches.opt_present("test");
949     let write_dependency_info = (matches.opt_present("dep-info"),
950                                  matches.opt_str("dep-info").map(|p| Path::new(p)));
951
952     let print_metas = (matches.opt_present("crate-id"),
953                        matches.opt_present("crate-name"),
954                        matches.opt_present("crate-file-name"));
955     let cg = build_codegen_options(matches);
956
957     session::Options {
958         crate_types: crate_types,
959         gc: gc,
960         optimize: opt_level,
961         debuginfo: debuginfo,
962         lint_opts: lint_opts,
963         output_types: output_types,
964         addl_lib_search_paths: RefCell::new(addl_lib_search_paths),
965         maybe_sysroot: sysroot_opt,
966         target_triple: target,
967         cfg: cfg,
968         test: test,
969         parse_only: parse_only,
970         no_trans: no_trans,
971         no_analysis: no_analysis,
972         debugging_opts: debugging_opts,
973         write_dependency_info: write_dependency_info,
974         print_metas: print_metas,
975         cg: cg,
976     }
977 }
978
979 pub fn build_codegen_options(matches: &getopts::Matches)
980         -> session::CodegenOptions
981 {
982     let mut cg = session::basic_codegen_options();
983     for option in matches.opt_strs("C").move_iter() {
984         let mut iter = option.splitn('=', 1);
985         let key = iter.next().unwrap();
986         let value = iter.next();
987         let option_to_lookup = key.replace("-", "_");
988         let mut found = false;
989         for &(candidate, setter, _) in session::CG_OPTIONS.iter() {
990             if option_to_lookup.as_slice() != candidate { continue }
991             if !setter(&mut cg, value) {
992                 match value {
993                     Some(..) => early_error(format!("codegen option `{}` takes \
994                                                      no value", key)),
995                     None => early_error(format!("codegen option `{0}` requires \
996                                                  a value (-C {0}=<value>)",
997                                                 key))
998                 }
999             }
1000             found = true;
1001             break;
1002         }
1003         if !found {
1004             early_error(format!("unknown codegen option: `{}`", key));
1005         }
1006     }
1007     return cg;
1008 }
1009
1010 pub fn build_session(sopts: session::Options,
1011                      local_crate_source_file: Option<Path>)
1012                      -> Session {
1013     let codemap = codemap::CodeMap::new();
1014     let diagnostic_handler =
1015         diagnostic::default_handler();
1016     let span_diagnostic_handler =
1017         diagnostic::mk_span_handler(diagnostic_handler, codemap);
1018
1019     build_session_(sopts, local_crate_source_file, span_diagnostic_handler)
1020 }
1021
1022 pub fn build_session_(sopts: session::Options,
1023                       local_crate_source_file: Option<Path>,
1024                       span_diagnostic: diagnostic::SpanHandler)
1025                       -> Session {
1026     let target_cfg = build_target_config(&sopts);
1027     let p_s = parse::new_parse_sess_special_handler(span_diagnostic);
1028     let default_sysroot = match sopts.maybe_sysroot {
1029         Some(_) => None,
1030         None => Some(filesearch::get_or_default_sysroot())
1031     };
1032
1033     // Make the path absolute, if necessary
1034     let local_crate_source_file = local_crate_source_file.map(|path|
1035         if path.is_absolute() {
1036             path.clone()
1037         } else {
1038             os::getcwd().join(path.clone())
1039         }
1040     );
1041
1042     Session {
1043         targ_cfg: target_cfg,
1044         opts: sopts,
1045         cstore: CStore::new(token::get_ident_interner()),
1046         parse_sess: p_s,
1047         // For a library crate, this is always none
1048         entry_fn: RefCell::new(None),
1049         entry_type: Cell::new(None),
1050         macro_registrar_fn: Cell::new(None),
1051         default_sysroot: default_sysroot,
1052         building_library: Cell::new(false),
1053         local_crate_source_file: local_crate_source_file,
1054         working_dir: os::getcwd(),
1055         lints: RefCell::new(NodeMap::new()),
1056         node_id: Cell::new(1),
1057         crate_types: RefCell::new(Vec::new()),
1058         features: front::feature_gate::Features::new(),
1059         recursion_limit: Cell::new(64),
1060     }
1061 }
1062
1063 pub fn parse_pretty(sess: &Session, name: &str) -> PpMode {
1064     match name {
1065       &"normal" => PpmNormal,
1066       &"expanded" => PpmExpanded,
1067       &"typed" => PpmTyped,
1068       &"expanded,identified" => PpmExpandedIdentified,
1069       &"identified" => PpmIdentified,
1070       _ => {
1071         sess.fatal("argument to `pretty` must be one of `normal`, \
1072                     `expanded`, `typed`, `identified`, \
1073                     or `expanded,identified`");
1074       }
1075     }
1076 }
1077
1078 // rustc command line options
1079 pub fn optgroups() -> Vec<getopts::OptGroup> {
1080  vec!(
1081   optflag("h", "help", "Display this message"),
1082   optmulti("", "cfg", "Configure the compilation environment", "SPEC"),
1083   optmulti("L", "",   "Add a directory to the library search path", "PATH"),
1084   optmulti("", "crate-type", "Comma separated list of types of crates for the compiler to emit",
1085            "[bin|lib|rlib|dylib|staticlib]"),
1086   optmulti("", "emit", "Comma separated list of types of output for the compiler to emit",
1087            "[asm|bc|ir|obj|link]"),
1088   optflag("", "crate-id", "Output the crate id and exit"),
1089   optflag("", "crate-name", "Output the crate name and exit"),
1090   optflag("", "crate-file-name", "Output the file(s) that would be written if compilation \
1091           continued and exit"),
1092   optflag("g",  "",  "Equivalent to --debuginfo=2"),
1093   optopt("",  "debuginfo",  "Emit DWARF debug info to the objects created:
1094          0 = no debug info,
1095          1 = line-tables only (for stacktraces and breakpoints),
1096          2 = full debug info with variable and type information (same as -g)", "LEVEL"),
1097   optflag("", "no-trans", "Run all passes except translation; no output"),
1098   optflag("", "no-analysis",
1099           "Parse and expand the source, but run no analysis and produce no output"),
1100   optflag("O", "", "Equivalent to --opt-level=2"),
1101   optopt("o", "", "Write output to <filename>", "FILENAME"),
1102   optopt("", "opt-level", "Optimize with possible levels 0-3", "LEVEL"),
1103   optopt( "",  "out-dir", "Write output to compiler-chosen filename in <dir>", "DIR"),
1104   optflag("", "parse-only", "Parse only; do not compile, assemble, or link"),
1105   optflagopt("", "pretty",
1106              "Pretty-print the input instead of compiling;
1107               valid types are: normal (un-annotated source),
1108               expanded (crates expanded),
1109               typed (crates expanded, with type annotations),
1110               or identified (fully parenthesized,
1111               AST nodes and blocks with IDs)", "TYPE"),
1112   optflagopt("", "dep-info",
1113              "Output dependency info to <filename> after compiling, \
1114               in a format suitable for use by Makefiles", "FILENAME"),
1115   optopt("", "sysroot", "Override the system root", "PATH"),
1116   optflag("", "test", "Build a test harness"),
1117   optopt("", "target", "Target triple cpu-manufacturer-kernel[-os]
1118                         to compile for (see chapter 3.4 of http://www.sourceware.org/autobook/
1119                         for details)", "TRIPLE"),
1120   optmulti("W", "warn", "Set lint warnings", "OPT"),
1121   optmulti("A", "allow", "Set lint allowed", "OPT"),
1122   optmulti("D", "deny", "Set lint denied", "OPT"),
1123   optmulti("F", "forbid", "Set lint forbidden", "OPT"),
1124   optmulti("C", "codegen", "Set a codegen option", "OPT[=VALUE]"),
1125   optmulti("Z", "", "Set internal debugging options", "FLAG"),
1126   optflag( "v", "version", "Print version info and exit"))
1127 }
1128
1129 pub struct OutputFilenames {
1130     pub out_directory: Path,
1131     pub out_filestem: ~str,
1132     pub single_output_file: Option<Path>,
1133 }
1134
1135 impl OutputFilenames {
1136     pub fn path(&self, flavor: link::OutputType) -> Path {
1137         match self.single_output_file {
1138             Some(ref path) => return path.clone(),
1139             None => {}
1140         }
1141         self.temp_path(flavor)
1142     }
1143
1144     pub fn temp_path(&self, flavor: link::OutputType) -> Path {
1145         let base = self.out_directory.join(self.out_filestem.as_slice());
1146         match flavor {
1147             link::OutputTypeBitcode => base.with_extension("bc"),
1148             link::OutputTypeAssembly => base.with_extension("s"),
1149             link::OutputTypeLlvmAssembly => base.with_extension("ll"),
1150             link::OutputTypeObject => base.with_extension("o"),
1151             link::OutputTypeExe => base,
1152         }
1153     }
1154
1155     pub fn with_extension(&self, extension: &str) -> Path {
1156         let stem = self.out_filestem.as_slice();
1157         self.out_directory.join(stem).with_extension(extension)
1158     }
1159 }
1160
1161 pub fn build_output_filenames(input: &Input,
1162                               odir: &Option<Path>,
1163                               ofile: &Option<Path>,
1164                               attrs: &[ast::Attribute],
1165                               sess: &Session)
1166                            -> OutputFilenames {
1167     match *ofile {
1168         None => {
1169             // "-" as input file will cause the parser to read from stdin so we
1170             // have to make up a name
1171             // We want to toss everything after the final '.'
1172             let dirpath = match *odir {
1173                 Some(ref d) => d.clone(),
1174                 None => Path::new(".")
1175             };
1176
1177             let mut stem = input.filestem();
1178
1179             // If a crateid is present, we use it as the link name
1180             let crateid = attr::find_crateid(attrs);
1181             match crateid {
1182                 None => {}
1183                 Some(crateid) => stem = crateid.name.to_str(),
1184             }
1185             OutputFilenames {
1186                 out_directory: dirpath,
1187                 out_filestem: stem,
1188                 single_output_file: None,
1189             }
1190         }
1191
1192         Some(ref out_file) => {
1193             let ofile = if sess.opts.output_types.len() > 1 {
1194                 sess.warn("ignoring specified output filename because multiple \
1195                            outputs were requested");
1196                 None
1197             } else {
1198                 Some(out_file.clone())
1199             };
1200             if *odir != None {
1201                 sess.warn("ignoring --out-dir flag due to -o flag.");
1202             }
1203             OutputFilenames {
1204                 out_directory: out_file.dir_path(),
1205                 out_filestem: out_file.filestem_str().unwrap().to_str(),
1206                 single_output_file: ofile,
1207             }
1208         }
1209     }
1210 }
1211
1212 pub fn early_error(msg: &str) -> ! {
1213     let mut emitter = diagnostic::EmitterWriter::stderr();
1214     emitter.emit(None, msg, diagnostic::Fatal);
1215     fail!(diagnostic::FatalError);
1216 }
1217
1218 pub fn list_metadata(sess: &Session, path: &Path,
1219                      out: &mut io::Writer) -> io::IoResult<()> {
1220     metadata::loader::list_file_metadata(
1221         session::sess_os_to_meta_os(sess.targ_cfg.os), path, out)
1222 }
1223
1224 #[cfg(test)]
1225 mod test {
1226
1227     use driver::driver::{build_configuration, build_session};
1228     use driver::driver::{build_session_options, optgroups};
1229
1230     use getopts::getopts;
1231     use syntax::attr;
1232     use syntax::attr::AttrMetaMethods;
1233
1234     // When the user supplies --test we should implicitly supply --cfg test
1235     #[test]
1236     fn test_switch_implies_cfg_test() {
1237         let matches =
1238             &match getopts(["--test".to_owned()], optgroups().as_slice()) {
1239               Ok(m) => m,
1240               Err(f) => fail!("test_switch_implies_cfg_test: {}", f.to_err_msg())
1241             };
1242         let sessopts = build_session_options(matches);
1243         let sess = build_session(sessopts, None);
1244         let cfg = build_configuration(&sess);
1245         assert!((attr::contains_name(cfg.as_slice(), "test")));
1246     }
1247
1248     // When the user supplies --test and --cfg test, don't implicitly add
1249     // another --cfg test
1250     #[test]
1251     fn test_switch_implies_cfg_test_unless_cfg_test() {
1252         let matches =
1253             &match getopts(["--test".to_owned(), "--cfg=test".to_owned()],
1254                            optgroups().as_slice()) {
1255               Ok(m) => m,
1256               Err(f) => {
1257                 fail!("test_switch_implies_cfg_test_unless_cfg_test: {}",
1258                        f.to_err_msg());
1259               }
1260             };
1261         let sessopts = build_session_options(matches);
1262         let sess = build_session(sessopts, None);
1263         let cfg = build_configuration(&sess);
1264         let mut test_items = cfg.iter().filter(|m| m.name().equiv(&("test")));
1265         assert!(test_items.next().is_some());
1266         assert!(test_items.next().is_none());
1267     }
1268 }