]> git.lizzy.rs Git - rust.git/blob - src/librustc/driver/driver.rs
ae9cd37fe69286f21abf4b1ec4022bde323b0f64
[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 std::vec;
41 use getopts::{optopt, optmulti, optflag, optflagopt};
42 use getopts;
43 use syntax::ast;
44 use syntax::abi;
45 use syntax::attr;
46 use syntax::attr::{AttrMetaMethods};
47 use syntax::codemap;
48 use syntax::crateid::CrateId;
49 use syntax::diagnostic;
50 use syntax::diagnostic::Emitter;
51 use syntax::ext::base::CrateLoader;
52 use syntax::parse;
53 use syntax::parse::token::InternedString;
54 use syntax::parse::token;
55 use syntax::print::{pp, pprust};
56 use syntax;
57
58 pub enum PpMode {
59     PpmNormal,
60     PpmExpanded,
61     PpmTyped,
62     PpmIdentified,
63     PpmExpandedIdentified
64 }
65
66 /**
67  * The name used for source code that doesn't originate in a file
68  * (e.g. source from stdin or a string)
69  */
70 pub fn anon_src() -> ~str {
71     "<anon>".to_str()
72 }
73
74 pub fn source_name(input: &Input) -> ~str {
75     match *input {
76         // FIXME (#9639): This needs to handle non-utf8 paths
77         FileInput(ref ifile) => ifile.as_str().unwrap().to_str(),
78         StrInput(_) => anon_src()
79     }
80 }
81
82 pub fn default_configuration(sess: &Session) ->
83    ast::CrateConfig {
84     let tos = match sess.targ_cfg.os {
85         abi::OsWin32 =>   InternedString::new("win32"),
86         abi::OsMacos =>   InternedString::new("macos"),
87         abi::OsLinux =>   InternedString::new("linux"),
88         abi::OsAndroid => InternedString::new("android"),
89         abi::OsFreebsd => InternedString::new("freebsd"),
90     };
91
92     // ARM is bi-endian, however using NDK seems to default
93     // to little-endian unless a flag is provided.
94     let (end,arch,wordsz) = match sess.targ_cfg.arch {
95         abi::X86 =>    ("little", "x86",    "32"),
96         abi::X86_64 => ("little", "x86_64", "64"),
97         abi::Arm =>    ("little", "arm",    "32"),
98         abi::Mips =>   ("big",    "mips",   "32")
99     };
100
101     let fam = match sess.targ_cfg.os {
102         abi::OsWin32 => InternedString::new("windows"),
103         _ => InternedString::new("unix")
104     };
105
106     let mk = attr::mk_name_value_item_str;
107     return vec!(// Target bindings.
108          attr::mk_word_item(fam.clone()),
109          mk(InternedString::new("target_os"), tos),
110          mk(InternedString::new("target_family"), fam),
111          mk(InternedString::new("target_arch"), InternedString::new(arch)),
112          mk(InternedString::new("target_endian"), InternedString::new(end)),
113          mk(InternedString::new("target_word_size"),
114             InternedString::new(wordsz))
115     );
116 }
117
118 pub fn append_configuration(cfg: &mut ast::CrateConfig,
119                             name: InternedString) {
120     if !cfg.iter().any(|mi| mi.name() == name) {
121         cfg.push(attr::mk_word_item(name))
122     }
123 }
124
125 pub fn build_configuration(sess: &Session) -> ast::CrateConfig {
126     // Combine the configuration requested by the session (command line) with
127     // some default and generated configuration items
128     let default_cfg = default_configuration(sess);
129     let mut user_cfg = sess.opts.cfg.clone();
130     // If the user wants a test runner, then add the test cfg
131     if sess.opts.test {
132         append_configuration(&mut user_cfg, InternedString::new("test"))
133     }
134     // If the user requested GC, then add the GC cfg
135     append_configuration(&mut user_cfg, if sess.opts.gc {
136         InternedString::new("gc")
137     } else {
138         InternedString::new("nogc")
139     });
140     return vec::append(user_cfg.move_iter().collect(),
141                           default_cfg.as_slice());
142 }
143
144 // Convert strings provided as --cfg [cfgspec] into a crate_cfg
145 fn parse_cfgspecs(cfgspecs: Vec<~str> )
146                   -> ast::CrateConfig {
147     cfgspecs.move_iter().map(|s| {
148         parse::parse_meta_from_source_str("cfgspec".to_str(),
149                                           s,
150                                           Vec::new(),
151                                           &parse::new_parse_sess())
152     }).collect::<ast::CrateConfig>()
153 }
154
155 pub enum Input {
156     /// Load source from file
157     FileInput(Path),
158     /// The string is the source
159     StrInput(~str)
160 }
161
162 impl Input {
163     fn filestem(&self) -> ~str {
164         match *self {
165             FileInput(ref ifile) => ifile.filestem_str().unwrap().to_str(),
166             StrInput(_) => ~"rust_out",
167         }
168     }
169 }
170
171 pub fn phase_1_parse_input(sess: &Session, cfg: ast::CrateConfig, input: &Input)
172     -> ast::Crate {
173     let krate = time(sess.time_passes(), "parsing", (), |_| {
174         match *input {
175             FileInput(ref file) => {
176                 parse::parse_crate_from_file(&(*file), cfg.clone(), &sess.parse_sess)
177             }
178             StrInput(ref src) => {
179                 parse::parse_crate_from_source_str(anon_src(),
180                                                    (*src).clone(),
181                                                    cfg.clone(),
182                                                    &sess.parse_sess)
183             }
184         }
185     });
186
187     if sess.opts.debugging_opts & session::AST_JSON_NOEXPAND != 0 {
188         let mut stdout = io::BufferedWriter::new(io::stdout());
189         let mut json = json::PrettyEncoder::new(&mut stdout);
190         krate.encode(&mut json);
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.set(session::collect_crate_types(sess,
216                                                       krate.attrs
217                                                            .as_slice()));
218
219     time(time_passes, "gated feature checking", (), |_|
220          front::feature_gate::check_crate(sess, &krate));
221
222     krate = time(time_passes, "crate injection", krate, |krate|
223                  front::std_inject::maybe_inject_crates_ref(sess, krate));
224
225     // strip before expansion to allow macros to depend on
226     // configuration variables e.g/ in
227     //
228     //   #[macro_escape] #[cfg(foo)]
229     //   mod bar { macro_rules! baz!(() => {{}}) }
230     //
231     // baz! should not use this definition unless foo is enabled.
232
233     krate = time(time_passes, "configuration 1", krate, |krate|
234                  front::config::strip_unconfigured_items(krate));
235
236     krate = time(time_passes, "expansion", krate, |krate| {
237         let cfg = syntax::ext::expand::ExpansionConfig {
238             loader: loader,
239             deriving_hash_type_parameter: sess.features.default_type_params.get(),
240             crate_id: crate_id.clone(),
241         };
242         syntax::ext::expand::expand_crate(&sess.parse_sess,
243                                           cfg,
244                                           krate)
245     });
246     // dump the syntax-time crates
247     sess.cstore.reset();
248
249     // strip again, in case expansion added anything with a #[cfg].
250     krate = time(time_passes, "configuration 2", krate, |krate|
251                  front::config::strip_unconfigured_items(krate));
252
253     krate = time(time_passes, "maybe building test harness", krate, |krate|
254                  front::test::modify_for_testing(sess, krate));
255
256     krate = time(time_passes, "prelude injection", krate, |krate|
257                  front::std_inject::maybe_inject_prelude(sess, krate));
258
259     let (krate, map) = time(time_passes, "assinging node ids and indexing ast", krate, |krate|
260          front::assign_node_ids_and_map::assign_node_ids_and_map(sess, krate));
261
262     if sess.opts.debugging_opts & session::AST_JSON != 0 {
263         let mut stdout = io::BufferedWriter::new(io::stdout());
264         let mut json = json::PrettyEncoder::new(&mut stdout);
265         krate.encode(&mut json);
266     }
267
268     (krate, map)
269 }
270
271 pub struct CrateAnalysis {
272     exp_map2: middle::resolve::ExportMap2,
273     exported_items: middle::privacy::ExportedItems,
274     public_items: middle::privacy::PublicItems,
275     ty_cx: ty::ctxt,
276     maps: astencode::Maps,
277     reachable: NodeSet,
278 }
279
280 /// Run the resolution, typechecking, region checking and other
281 /// miscellaneous analysis passes on the crate. Return various
282 /// structures carrying the results of the analysis.
283 pub fn phase_3_run_analysis_passes(sess: Session,
284                                    krate: &ast::Crate,
285                                    ast_map: syntax::ast_map::Map) -> CrateAnalysis {
286
287     let time_passes = sess.time_passes();
288
289     time(time_passes, "external crate/lib resolution", (), |_|
290          creader::read_crates(&sess, krate,
291                               session::sess_os_to_meta_os(sess.targ_cfg.os),
292                               token::get_ident_interner()));
293
294     let lang_items = time(time_passes, "language item collection", (), |_|
295                           middle::lang_items::collect_language_items(krate, &sess));
296
297     let middle::resolve::CrateMap {
298         def_map: def_map,
299         exp_map2: exp_map2,
300         trait_map: trait_map,
301         external_exports: external_exports,
302         last_private_map: last_private_map
303     } =
304         time(time_passes, "resolution", (), |_|
305              middle::resolve::resolve_crate(&sess, lang_items, krate));
306
307     // Discard MTWT tables that aren't required past resolution.
308     syntax::ext::mtwt::clear_tables();
309
310     let named_region_map = time(time_passes, "lifetime resolution", (),
311                                 |_| middle::resolve_lifetime::krate(&sess, krate));
312
313     time(time_passes, "looking for entry point", (),
314          |_| middle::entry::find_entry_point(&sess, krate, &ast_map));
315
316     sess.macro_registrar_fn.with_mut(|r| *r =
317         time(time_passes, "looking for macro registrar", (), |_|
318             syntax::ext::registrar::find_macro_registrar(
319                 sess.diagnostic(), krate)));
320
321     let freevars = time(time_passes, "freevar finding", (), |_|
322                         freevars::annotate_freevars(def_map, krate));
323
324     let region_map = time(time_passes, "region resolution", (), |_|
325                           middle::region::resolve_crate(&sess, krate));
326
327     let ty_cx = ty::mk_ctxt(sess, def_map, named_region_map, ast_map,
328                             freevars, region_map, lang_items);
329
330     // passes are timed inside typeck
331     let (method_map, vtable_map) = typeck::check_crate(&ty_cx, trait_map, krate);
332
333     time(time_passes, "check static items", (), |_|
334          middle::check_static::check_crate(&ty_cx, krate));
335
336     // These next two const passes can probably be merged
337     time(time_passes, "const marking", (), |_|
338          middle::const_eval::process_crate(krate, &ty_cx));
339
340     time(time_passes, "const checking", (), |_|
341          middle::check_const::check_crate(krate, def_map, method_map, &ty_cx));
342
343     let maps = (external_exports, last_private_map);
344     let (exported_items, public_items) =
345             time(time_passes, "privacy checking", maps, |(a, b)|
346                  middle::privacy::check_crate(&ty_cx, &method_map, &exp_map2,
347                                               a, b, krate));
348
349     time(time_passes, "effect checking", (), |_|
350          middle::effect::check_crate(&ty_cx, method_map, krate));
351
352     time(time_passes, "loop checking", (), |_|
353          middle::check_loop::check_crate(&ty_cx, krate));
354
355     let middle::moves::MoveMaps {moves_map, moved_variables_set,
356                                  capture_map} =
357         time(time_passes, "compute moves", (), |_|
358              middle::moves::compute_moves(&ty_cx, method_map, krate));
359
360     time(time_passes, "match checking", (), |_|
361          middle::check_match::check_crate(&ty_cx, method_map,
362                                           &moves_map, krate));
363
364     time(time_passes, "liveness checking", (), |_|
365          middle::liveness::check_crate(&ty_cx, method_map,
366                                        &capture_map, krate));
367
368     let root_map =
369         time(time_passes, "borrow checking", (), |_|
370              middle::borrowck::check_crate(&ty_cx, method_map,
371                                            &moves_map, &moved_variables_set,
372                                            &capture_map, krate));
373
374     drop(moves_map);
375     drop(moved_variables_set);
376
377     time(time_passes, "kind checking", (), |_|
378          kind::check_crate(&ty_cx, method_map, krate));
379
380     let reachable_map =
381         time(time_passes, "reachability checking", (), |_|
382              reachable::find_reachable(&ty_cx, method_map, &exported_items));
383
384     time(time_passes, "death checking", (), |_| {
385         middle::dead::check_crate(&ty_cx,
386                                   method_map,
387                                   &exported_items,
388                                   &reachable_map,
389                                   krate)
390     });
391
392     time(time_passes, "lint checking", (), |_|
393          lint::check_crate(&ty_cx, method_map, &exported_items, krate));
394
395     CrateAnalysis {
396         exp_map2: exp_map2,
397         ty_cx: ty_cx,
398         exported_items: exported_items,
399         public_items: public_items,
400         maps: astencode::Maps {
401             root_map: root_map,
402             method_map: method_map,
403             vtable_map: vtable_map,
404             capture_map: RefCell::new(capture_map)
405         },
406         reachable: reachable_map
407     }
408 }
409
410 pub struct CrateTranslation {
411     context: ContextRef,
412     module: ModuleRef,
413     metadata_module: ModuleRef,
414     link: LinkMeta,
415     metadata: Vec<u8> ,
416     reachable: Vec<~str> ,
417 }
418
419 /// Run the translation phase to LLVM, after which the AST and analysis can
420 /// be discarded.
421 pub fn phase_4_translate_to_llvm(krate: ast::Crate,
422                                  analysis: CrateAnalysis,
423                                  outputs: &OutputFilenames) -> (ty::ctxt, CrateTranslation) {
424     // Option dance to work around the lack of stack once closures.
425     let time_passes = analysis.ty_cx.sess.time_passes();
426     let mut analysis = Some(analysis);
427     time(time_passes, "translation", krate, |krate|
428          trans::base::trans_crate(krate, analysis.take_unwrap(), outputs))
429 }
430
431 /// Run LLVM itself, producing a bitcode file, assembly file or object file
432 /// as a side effect.
433 pub fn phase_5_run_llvm_passes(sess: &Session,
434                                trans: &CrateTranslation,
435                                outputs: &OutputFilenames) {
436     if sess.opts.cg.no_integrated_as {
437         let output_type = link::OutputTypeAssembly;
438
439         time(sess.time_passes(), "LLVM passes", (), |_|
440             link::write::run_passes(sess, trans, [output_type], outputs));
441
442         link::write::run_assembler(sess, outputs);
443
444         // Remove assembly source, unless --save-temps was specified
445         if !sess.opts.cg.save_temps {
446             fs::unlink(&outputs.temp_path(link::OutputTypeAssembly)).unwrap();
447         }
448     } else {
449         time(sess.time_passes(), "LLVM passes", (), |_|
450             link::write::run_passes(sess,
451                                     trans,
452                                     sess.opts.output_types.as_slice(),
453                                     outputs));
454     }
455 }
456
457 /// Run the linker on any artifacts that resulted from the LLVM run.
458 /// This should produce either a finished executable or library.
459 pub fn phase_6_link_output(sess: &Session,
460                            trans: &CrateTranslation,
461                            outputs: &OutputFilenames) {
462     time(sess.time_passes(), "linking", (), |_|
463          link::link_binary(sess,
464                            trans,
465                            outputs,
466                            &trans.link.crateid));
467 }
468
469 pub fn stop_after_phase_3(sess: &Session) -> bool {
470    if sess.opts.no_trans {
471         debug!("invoked with --no-trans, returning early from compile_input");
472         return true;
473     }
474     return false;
475 }
476
477 pub fn stop_after_phase_1(sess: &Session) -> bool {
478     if sess.opts.parse_only {
479         debug!("invoked with --parse-only, returning early from compile_input");
480         return true;
481     }
482     if sess.show_span() {
483         return true;
484     }
485     return sess.opts.debugging_opts & session::AST_JSON_NOEXPAND != 0;
486 }
487
488 pub fn stop_after_phase_2(sess: &Session) -> bool {
489     if sess.opts.no_analysis {
490         debug!("invoked with --no-analysis, returning early from compile_input");
491         return true;
492     }
493     return sess.opts.debugging_opts & session::AST_JSON != 0;
494 }
495
496 pub fn stop_after_phase_5(sess: &Session) -> bool {
497     if !sess.opts.output_types.iter().any(|&i| i == link::OutputTypeExe) {
498         debug!("not building executable, returning early from compile_input");
499         return true;
500     }
501     return false;
502 }
503
504 fn write_out_deps(sess: &Session,
505                   input: &Input,
506                   outputs: &OutputFilenames,
507                   krate: &ast::Crate) -> io::IoResult<()> {
508     let id = link::find_crate_id(krate.attrs.as_slice(), outputs.out_filestem);
509
510     let mut out_filenames = Vec::new();
511     for output_type in sess.opts.output_types.iter() {
512         let file = outputs.path(*output_type);
513         match *output_type {
514             link::OutputTypeExe => {
515                 let crate_types = sess.crate_types.borrow();
516                 for output in crate_types.get().iter() {
517                     let p = link::filename_for_input(sess, *output, &id, &file);
518                     out_filenames.push(p);
519                 }
520             }
521             _ => { out_filenames.push(file); }
522         }
523     }
524
525     // Write out dependency rules to the dep-info file if requested with
526     // --dep-info
527     let deps_filename = match sess.opts.write_dependency_info {
528         // Use filename from --dep-file argument if given
529         (true, Some(ref filename)) => filename.clone(),
530         // Use default filename: crate source filename with extension replaced
531         // by ".d"
532         (true, None) => match *input {
533             FileInput(..) => outputs.with_extension("d"),
534             StrInput(..) => {
535                 sess.warn("can not write --dep-info without a filename \
536                            when compiling stdin.");
537                 return Ok(());
538             },
539         },
540         _ => return Ok(()),
541     };
542
543     // Build a list of files used to compile the output and
544     // write Makefile-compatible dependency rules
545     let files: Vec<~str> = sess.codemap().files.borrow().get()
546                                .iter().filter_map(|fmap| {
547                                     if fmap.deref().is_real_file() {
548                                         Some(fmap.deref().name.clone())
549                                     } else {
550                                         None
551                                     }
552                                 }).collect();
553     let mut file = try!(io::File::create(&deps_filename));
554     for path in out_filenames.iter() {
555         try!(write!(&mut file as &mut Writer,
556                       "{}: {}\n\n", path.display(), files.connect(" ")));
557     }
558     Ok(())
559 }
560
561 pub fn compile_input(sess: Session, cfg: ast::CrateConfig, input: &Input,
562                      outdir: &Option<Path>, output: &Option<Path>) {
563     // We need nested scopes here, because the intermediate results can keep
564     // large chunks of memory alive and we want to free them as soon as
565     // possible to keep the peak memory usage low
566     let (outputs, trans, sess) = {
567         let (outputs, expanded_crate, ast_map) = {
568             let krate = phase_1_parse_input(&sess, cfg, input);
569             if stop_after_phase_1(&sess) { return; }
570             let outputs = build_output_filenames(input,
571                                                  outdir,
572                                                  output,
573                                                  krate.attrs.as_slice(),
574                                                  &sess);
575             let loader = &mut Loader::new(&sess);
576             let id = link::find_crate_id(krate.attrs.as_slice(),
577                                          outputs.out_filestem);
578             let (expanded_crate, ast_map) = phase_2_configure_and_expand(&sess, loader,
579                                                                          krate, &id);
580             (outputs, expanded_crate, ast_map)
581         };
582         write_out_deps(&sess, input, &outputs, &expanded_crate).unwrap();
583
584         if stop_after_phase_2(&sess) { return; }
585
586         let analysis = phase_3_run_analysis_passes(sess, &expanded_crate, ast_map);
587         if stop_after_phase_3(&analysis.ty_cx.sess) { return; }
588         let (tcx, trans) = phase_4_translate_to_llvm(expanded_crate,
589                                                      analysis, &outputs);
590
591         // Discard interned strings as they are no longer required.
592         token::get_ident_interner().clear();
593
594         (outputs, trans, tcx.sess)
595     };
596     phase_5_run_llvm_passes(&sess, &trans, &outputs);
597     if stop_after_phase_5(&sess) { return; }
598     phase_6_link_output(&sess, &trans, &outputs);
599 }
600
601 struct IdentifiedAnnotation;
602
603 impl pprust::PpAnn for IdentifiedAnnotation {
604     fn pre(&self,
605            s: &mut pprust::State,
606            node: pprust::AnnNode) -> io::IoResult<()> {
607         match node {
608             pprust::NodeExpr(_) => s.popen(),
609             _ => Ok(())
610         }
611     }
612     fn post(&self,
613             s: &mut pprust::State,
614             node: pprust::AnnNode) -> io::IoResult<()> {
615         match node {
616             pprust::NodeItem(item) => {
617                 try!(pp::space(&mut s.s));
618                 s.synth_comment(item.id.to_str())
619             }
620             pprust::NodeBlock(blk) => {
621                 try!(pp::space(&mut s.s));
622                 s.synth_comment(~"block " + blk.id.to_str())
623             }
624             pprust::NodeExpr(expr) => {
625                 try!(pp::space(&mut s.s));
626                 try!(s.synth_comment(expr.id.to_str()));
627                 s.pclose()
628             }
629             pprust::NodePat(pat) => {
630                 try!(pp::space(&mut s.s));
631                 s.synth_comment(~"pat " + pat.id.to_str())
632             }
633         }
634     }
635 }
636
637 struct TypedAnnotation {
638     analysis: CrateAnalysis,
639 }
640
641 impl pprust::PpAnn for TypedAnnotation {
642     fn pre(&self,
643            s: &mut pprust::State,
644            node: pprust::AnnNode) -> io::IoResult<()> {
645         match node {
646             pprust::NodeExpr(_) => s.popen(),
647             _ => Ok(())
648         }
649     }
650     fn post(&self,
651             s: &mut pprust::State,
652             node: pprust::AnnNode) -> io::IoResult<()> {
653         let tcx = &self.analysis.ty_cx;
654         match node {
655             pprust::NodeExpr(expr) => {
656                 try!(pp::space(&mut s.s));
657                 try!(pp::word(&mut s.s, "as"));
658                 try!(pp::space(&mut s.s));
659                 try!(pp::word(&mut s.s,
660                                 ppaux::ty_to_str(tcx, ty::expr_ty(tcx, expr))));
661                 s.pclose()
662             }
663             _ => Ok(())
664         }
665     }
666 }
667
668 pub fn pretty_print_input(sess: Session,
669                           cfg: ast::CrateConfig,
670                           input: &Input,
671                           ppm: PpMode) {
672     let krate = phase_1_parse_input(&sess, cfg, input);
673     let id = link::find_crate_id(krate.attrs.as_slice(), input.filestem());
674
675     let (krate, ast_map, is_expanded) = match ppm {
676         PpmExpanded | PpmExpandedIdentified | PpmTyped => {
677             let loader = &mut Loader::new(&sess);
678             let (krate, ast_map) = phase_2_configure_and_expand(&sess, loader,
679                                                                 krate, &id);
680             (krate, Some(ast_map), true)
681         }
682         _ => (krate, None, false)
683     };
684
685     let src_name = source_name(input);
686     let src = sess.codemap().get_filemap(src_name).deref().src.as_bytes().to_owned();
687     let mut rdr = MemReader::new(src);
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                                 ~io::stdout(),
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                                 ~io::stdout(),
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                                 ~io::stdout(),
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() -> ~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")).to_owned()
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 = vec::append(matches.opt_strs(level_short)
838                                           .move_iter()
839                                           .collect(),
840                                    matches.opt_strs(level_name));
841         for lint_name in flags.iter() {
842             let lint_name = lint_name.replace("-", "_");
843             match lint_dict.find_equiv(&lint_name) {
844               None => {
845                 early_error(format!("unknown {} flag: {}",
846                                     level_name, lint_name));
847               }
848               Some(lint) => {
849                 lint_opts.push((lint.lint, *level));
850               }
851             }
852         }
853     }
854
855     let mut debugging_opts = 0;
856     let debug_flags = matches.opt_strs("Z");
857     let debug_map = session::debugging_opts_map();
858     for debug_flag in debug_flags.iter() {
859         let mut this_bit = 0;
860         for tuple in debug_map.iter() {
861             let (name, bit) = match *tuple { (ref a, _, b) => (a, b) };
862             if *name == *debug_flag { this_bit = bit; break; }
863         }
864         if this_bit == 0 {
865             early_error(format!("unknown debug flag: {}", *debug_flag))
866         }
867         debugging_opts |= this_bit;
868     }
869
870     if debugging_opts & session::DEBUG_LLVM != 0 {
871         unsafe { llvm::LLVMSetDebug(1); }
872     }
873
874     let mut output_types = Vec::new();
875     if !parse_only && !no_trans {
876         let unparsed_output_types = matches.opt_strs("emit");
877         for unparsed_output_type in unparsed_output_types.iter() {
878             for part in unparsed_output_type.split(',') {
879                 let output_type = match part.as_slice() {
880                     "asm"  => link::OutputTypeAssembly,
881                     "ir"   => link::OutputTypeLlvmAssembly,
882                     "bc"   => link::OutputTypeBitcode,
883                     "obj"  => link::OutputTypeObject,
884                     "link" => link::OutputTypeExe,
885                     _ => early_error(format!("unknown emission type: `{}`", part))
886                 };
887                 output_types.push(output_type)
888             }
889         }
890     };
891     output_types.as_mut_slice().sort();
892     output_types.dedup();
893     if output_types.len() == 0 {
894         output_types.push(link::OutputTypeExe);
895     }
896
897     let sysroot_opt = matches.opt_str("sysroot").map(|m| Path::new(m));
898     let target = matches.opt_str("target").unwrap_or(host_triple());
899     let opt_level = {
900         if (debugging_opts & session::NO_OPT) != 0 {
901             No
902         } else if matches.opt_present("O") {
903             if matches.opt_present("opt-level") {
904                 early_error("-O and --opt-level both provided");
905             }
906             Default
907         } else if matches.opt_present("opt-level") {
908             match matches.opt_str("opt-level").as_ref().map(|s| s.as_slice()) {
909                 None      |
910                 Some("0") => No,
911                 Some("1") => Less,
912                 Some("2") => Default,
913                 Some("3") => Aggressive,
914                 Some(arg) => {
915                     early_error(format!("optimization level needs to be between 0-3 \
916                                         (instead was `{}`)", arg));
917                 }
918             }
919         } else {
920             No
921         }
922     };
923     let gc = debugging_opts & session::GC != 0;
924     let debuginfo = if matches.opt_present("g") {
925         if matches.opt_present("debuginfo") {
926             early_error("-g and --debuginfo both provided");
927         }
928         FullDebugInfo
929     } else if matches.opt_present("debuginfo") {
930         match matches.opt_str("debuginfo").as_ref().map(|s| s.as_slice()) {
931             Some("0") => NoDebugInfo,
932             Some("1") => LimitedDebugInfo,
933             None      |
934             Some("2") => FullDebugInfo,
935             Some(arg) => {
936                 early_error(format!("optimization level needs to be between 0-3 \
937                                     (instead was `{}`)", arg));
938             }
939         }
940     } else {
941         NoDebugInfo
942     };
943
944     let addl_lib_search_paths = matches.opt_strs("L").map(|s| {
945         Path::new(s.as_slice())
946     }).move_iter().collect();
947
948     let cfg = parse_cfgspecs(matches.opt_strs("cfg").move_iter().collect());
949     let test = matches.opt_present("test");
950     let write_dependency_info = (matches.opt_present("dep-info"),
951                                  matches.opt_str("dep-info").map(|p| Path::new(p)));
952
953     let print_metas = (matches.opt_present("crate-id"),
954                        matches.opt_present("crate-name"),
955                        matches.opt_present("crate-file-name"));
956     let cg = build_codegen_options(matches);
957
958     session::Options {
959         crate_types: crate_types,
960         gc: gc,
961         optimize: opt_level,
962         debuginfo: debuginfo,
963         lint_opts: lint_opts,
964         output_types: output_types,
965         addl_lib_search_paths: RefCell::new(addl_lib_search_paths),
966         maybe_sysroot: sysroot_opt,
967         target_triple: target,
968         cfg: cfg,
969         test: test,
970         parse_only: parse_only,
971         no_trans: no_trans,
972         no_analysis: no_analysis,
973         debugging_opts: debugging_opts,
974         write_dependency_info: write_dependency_info,
975         print_metas: print_metas,
976         cg: cg,
977     }
978 }
979
980 pub fn build_codegen_options(matches: &getopts::Matches)
981         -> session::CodegenOptions
982 {
983     let mut cg = session::basic_codegen_options();
984     for option in matches.opt_strs("C").move_iter() {
985         let mut iter = option.splitn('=', 1);
986         let key = iter.next().unwrap();
987         let value = iter.next();
988         let option_to_lookup = key.replace("-", "_");
989         let mut found = false;
990         for &(candidate, setter, _) in session::CG_OPTIONS.iter() {
991             if option_to_lookup.as_slice() != candidate { continue }
992             if !setter(&mut cg, value) {
993                 match value {
994                     Some(..) => early_error(format!("codegen option `{}` takes \
995                                                      no value", key)),
996                     None => early_error(format!("codegen option `{0}` requires \
997                                                  a value (-C {0}=<value>)",
998                                                 key))
999                 }
1000             }
1001             found = true;
1002             break;
1003         }
1004         if !found {
1005             early_error(format!("unknown codegen option: `{}`", key));
1006         }
1007     }
1008     return cg;
1009 }
1010
1011 pub fn build_session(sopts: session::Options,
1012                      local_crate_source_file: Option<Path>)
1013                      -> Session {
1014     let codemap = codemap::CodeMap::new();
1015     let diagnostic_handler =
1016         diagnostic::default_handler();
1017     let span_diagnostic_handler =
1018         diagnostic::mk_span_handler(diagnostic_handler, codemap);
1019
1020     build_session_(sopts, local_crate_source_file, span_diagnostic_handler)
1021 }
1022
1023 pub fn build_session_(sopts: session::Options,
1024                       local_crate_source_file: Option<Path>,
1025                       span_diagnostic: diagnostic::SpanHandler)
1026                       -> Session {
1027     let target_cfg = build_target_config(&sopts);
1028     let p_s = parse::new_parse_sess_special_handler(span_diagnostic);
1029     let default_sysroot = match sopts.maybe_sysroot {
1030         Some(_) => None,
1031         None => Some(filesearch::get_or_default_sysroot())
1032     };
1033
1034     // Make the path absolute, if necessary
1035     let local_crate_source_file = local_crate_source_file.map(|path|
1036         if path.is_absolute() {
1037             path.clone()
1038         } else {
1039             os::getcwd().join(path.clone())
1040         }
1041     );
1042
1043     Session {
1044         targ_cfg: target_cfg,
1045         opts: sopts,
1046         cstore: CStore::new(token::get_ident_interner()),
1047         parse_sess: p_s,
1048         // For a library crate, this is always none
1049         entry_fn: RefCell::new(None),
1050         entry_type: Cell::new(None),
1051         macro_registrar_fn: RefCell::new(None),
1052         default_sysroot: default_sysroot,
1053         building_library: Cell::new(false),
1054         local_crate_source_file: local_crate_source_file,
1055         working_dir: os::getcwd(),
1056         lints: RefCell::new(NodeMap::new()),
1057         node_id: Cell::new(1),
1058         crate_types: RefCell::new(Vec::new()),
1059         features: front::feature_gate::Features::new(),
1060         recursion_limit: Cell::new(64),
1061     }
1062 }
1063
1064 pub fn parse_pretty(sess: &Session, name: &str) -> PpMode {
1065     match name {
1066       &"normal" => PpmNormal,
1067       &"expanded" => PpmExpanded,
1068       &"typed" => PpmTyped,
1069       &"expanded,identified" => PpmExpandedIdentified,
1070       &"identified" => PpmIdentified,
1071       _ => {
1072         sess.fatal("argument to `pretty` must be one of `normal`, \
1073                     `expanded`, `typed`, `identified`, \
1074                     or `expanded,identified`");
1075       }
1076     }
1077 }
1078
1079 // rustc command line options
1080 pub fn optgroups() -> Vec<getopts::OptGroup> {
1081  vec!(
1082   optflag("h", "help", "Display this message"),
1083   optmulti("", "cfg", "Configure the compilation environment", "SPEC"),
1084   optmulti("L", "",   "Add a directory to the library search path", "PATH"),
1085   optmulti("", "crate-type", "Comma separated list of types of crates for the compiler to emit",
1086            "[bin|lib|rlib|dylib|staticlib]"),
1087   optmulti("", "emit", "Comma separated list of types of output for the compiler to emit",
1088            "[asm|bc|ir|obj|link]"),
1089   optflag("", "crate-id", "Output the crate id and exit"),
1090   optflag("", "crate-name", "Output the crate name and exit"),
1091   optflag("", "crate-file-name", "Output the file(s) that would be written if compilation \
1092           continued and exit"),
1093   optflag("",  "ls",  "List the symbols defined by a library crate"),
1094   optflag("g",  "",  "Equivalent to --debuginfo=2"),
1095   optopt("",  "debuginfo",  "Emit DWARF debug info to the objects created:
1096          0 = no debug info,
1097          1 = line-tables only (for stacktraces and breakpoints),
1098          2 = full debug info with variable and type information (same as -g)", "LEVEL"),
1099   optflag("", "no-trans", "Run all passes except translation; no output"),
1100   optflag("", "no-analysis", "Parse and expand the output, but run no analysis or produce output"),
1101   optflag("O", "", "Equivalent to --opt-level=2"),
1102   optopt("o", "", "Write output to <filename>", "FILENAME"),
1103   optopt("", "opt-level", "Optimize with possible levels 0-3", "LEVEL"),
1104   optopt( "",  "out-dir", "Write output to compiler-chosen filename in <dir>", "DIR"),
1105   optflag("", "parse-only", "Parse only; do not compile, assemble, or link"),
1106   optflagopt("", "pretty",
1107              "Pretty-print the input instead of compiling;
1108               valid types are: normal (un-annotated source),
1109               expanded (crates expanded),
1110               typed (crates expanded, with type annotations),
1111               or identified (fully parenthesized,
1112               AST nodes and blocks with IDs)", "TYPE"),
1113   optflagopt("", "dep-info", "Output dependency info to <filename> after compiling", "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     out_directory: Path,
1130     out_filestem: ~str,
1131     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"], 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", ~"--cfg=test"],
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 }