]> git.lizzy.rs Git - rust.git/blob - src/librustc/driver/driver.rs
auto merge of #7931 : blake2-ppc/rust/chain-mut-ref, r=pcwalton
[rust.git] / src / librustc / driver / driver.rs
1 // Copyright 2012 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};
15 use driver::session::{Session, Session_, No, Less, Default};
16 use driver::session;
17 use front;
18 use lib::llvm::llvm;
19 use metadata::{creader, cstore, filesearch};
20 use metadata;
21 use middle::{trans, freevars, kind, ty, typeck, lint, astencode, reachable};
22 use middle;
23 use util::common::time;
24 use util::ppaux;
25
26 use std::hashmap::HashMap;
27 use std::int;
28 use std::io;
29 use std::os;
30 use std::vec;
31 use extra::getopts::groups::{optopt, optmulti, optflag, optflagopt};
32 use extra::getopts::{opt_present};
33 use extra::getopts;
34 use syntax::ast;
35 use syntax::abi;
36 use syntax::attr;
37 use syntax::attr::{AttrMetaMethods};
38 use syntax::codemap;
39 use syntax::diagnostic;
40 use syntax::parse;
41 use syntax::parse::token;
42 use syntax::print::{pp, pprust};
43 use syntax;
44
45 pub enum pp_mode {
46     ppm_normal,
47     ppm_expanded,
48     ppm_typed,
49     ppm_identified,
50     ppm_expanded_identified
51 }
52
53 /**
54  * The name used for source code that doesn't originate in a file
55  * (e.g. source from stdin or a string)
56  */
57 pub fn anon_src() -> @str { @"<anon>" }
58
59 pub fn source_name(input: &input) -> @str {
60     match *input {
61       file_input(ref ifile) => ifile.to_str().to_managed(),
62       str_input(_) => anon_src()
63     }
64 }
65
66 pub fn default_configuration(sess: Session, argv0: @str, input: &input) ->
67    ast::CrateConfig {
68     let (libc, tos) = match sess.targ_cfg.os {
69         session::os_win32 =>   (@"msvcrt.dll", @"win32"),
70         session::os_macos =>   (@"libc.dylib", @"macos"),
71         session::os_linux =>   (@"libc.so.6",  @"linux"),
72         session::os_android => (@"libc.so",    @"android"),
73         session::os_freebsd => (@"libc.so.7",  @"freebsd")
74     };
75
76     // ARM is bi-endian, however using NDK seems to default
77     // to little-endian unless a flag is provided.
78     let (end,arch,wordsz) = match sess.targ_cfg.arch {
79         abi::X86 =>    (@"little", @"x86",    @"32"),
80         abi::X86_64 => (@"little", @"x86_64", @"64"),
81         abi::Arm =>    (@"little", @"arm",    @"32"),
82         abi::Mips =>   (@"big",    @"mips",   @"32")
83     };
84
85     let mk = attr::mk_name_value_item_str;
86     return ~[ // Target bindings.
87          attr::mk_word_item(os::FAMILY.to_managed()),
88          mk(@"target_os", tos),
89          mk(@"target_family", os::FAMILY.to_managed()),
90          mk(@"target_arch", arch),
91          mk(@"target_endian", end),
92          mk(@"target_word_size", wordsz),
93          mk(@"target_libc", libc),
94          // Build bindings.
95          mk(@"build_compiler", argv0),
96          mk(@"build_input", source_name(input))];
97 }
98
99 pub fn append_configuration(cfg: &mut ast::CrateConfig, name: @str) {
100     if !cfg.iter().any(|mi| mi.name() == name) {
101         cfg.push(attr::mk_word_item(name))
102     }
103 }
104
105 pub fn build_configuration(sess: Session, argv0: @str, input: &input) ->
106    ast::CrateConfig {
107     // Combine the configuration requested by the session (command line) with
108     // some default and generated configuration items
109     let default_cfg = default_configuration(sess, argv0, input);
110     let mut user_cfg = sess.opts.cfg.clone();
111     // If the user wants a test runner, then add the test cfg
112     if sess.opts.test { append_configuration(&mut user_cfg, @"test") }
113     // If the user requested GC, then add the GC cfg
114     append_configuration(&mut user_cfg, if sess.opts.gc { @"gc" } else { @"nogc" });
115     return vec::append(user_cfg, default_cfg);
116 }
117
118 // Convert strings provided as --cfg [cfgspec] into a crate_cfg
119 fn parse_cfgspecs(cfgspecs: ~[~str],
120                   demitter: diagnostic::Emitter) -> ast::CrateConfig {
121     do cfgspecs.consume_iter().transform |s| {
122         let sess = parse::new_parse_sess(Some(demitter));
123         parse::parse_meta_from_source_str(@"cfgspec", s.to_managed(), ~[], sess)
124     }.collect::<ast::CrateConfig>()
125 }
126
127 pub enum input {
128     /// Load source from file
129     file_input(Path),
130     /// The string is the source
131     // FIXME (#2319): Don't really want to box the source string
132     str_input(@str)
133 }
134
135 pub fn parse_input(sess: Session, cfg: ast::CrateConfig, input: &input)
136     -> @ast::Crate {
137     match *input {
138       file_input(ref file) => {
139         parse::parse_crate_from_file(&(*file), cfg, sess.parse_sess)
140       }
141       str_input(src) => {
142         parse::parse_crate_from_source_str(
143             anon_src(), src, cfg, sess.parse_sess)
144       }
145     }
146 }
147
148 /// First phase to do, last phase to do
149 #[deriving(Eq)]
150 pub struct compile_upto {
151     from: compile_phase,
152     to: compile_phase
153 }
154
155 #[deriving(Eq)]
156 pub enum compile_phase {
157     cu_parse,
158     cu_expand, // means "it's already expanded"
159     cu_typeck,
160     cu_no_trans,
161     cu_everything,
162 }
163
164 // For continuing compilation after a parsed crate has been
165 // modified
166
167
168 #[fixed_stack_segment]
169 pub fn compile_rest(sess: Session,
170                     cfg: ast::CrateConfig,
171                     phases: compile_upto,
172                     outputs: Option<@OutputFilenames>,
173                     curr: Option<@ast::Crate>)
174     -> (Option<@ast::Crate>, Option<ty::ctxt>) {
175
176     let time_passes = sess.time_passes();
177
178     let mut crate = curr.unwrap();
179
180     if phases.from == cu_parse || phases.from == cu_everything {
181
182         *sess.building_library = session::building_library(
183             sess.opts.crate_type, crate, sess.opts.test);
184
185         // strip before expansion to allow macros to depend on
186         // configuration variables e.g/ in
187         //
188         //   #[macro_escape] #[cfg(foo)]
189         //   mod bar { macro_rules! baz!(() => {{}}) }
190         //
191         // baz! should not use this definition unless foo is enabled.
192         crate = time(time_passes, ~"std macros injection", ||
193                      syntax::ext::expand::inject_std_macros(sess.parse_sess,
194                                                             cfg.clone(),
195                                                             crate));
196
197         crate = time(time_passes, ~"configuration 1", ||
198                      front::config::strip_unconfigured_items(crate));
199
200         crate = time(time_passes, ~"expansion", ||
201                      syntax::ext::expand::expand_crate(sess.parse_sess,
202                                                        cfg.clone(),
203                                                        crate));
204
205         // strip again, in case expansion added anything with a #[cfg].
206         crate = time(time_passes, ~"configuration 2", ||
207                      front::config::strip_unconfigured_items(crate));
208
209
210         crate = time(time_passes, ~"maybe building test harness", ||
211                      front::test::modify_for_testing(sess, crate));
212     }
213
214     if phases.to == cu_expand {
215         return (Some(crate), None);
216     }
217
218     assert!(phases.from != cu_no_trans);
219
220     let (llcx, llmod, link_meta) = {
221         crate = time(time_passes, ~"std injection", ||
222                      front::std_inject::maybe_inject_libstd_ref(sess, crate));
223
224         let ast_map = time(time_passes, ~"ast indexing", ||
225                            syntax::ast_map::map_crate(sess.diagnostic(), crate));
226
227         time(time_passes, ~"external crate/lib resolution", ||
228              creader::read_crates(sess.diagnostic(), crate, sess.cstore,
229                                   sess.filesearch,
230                                   session::sess_os_to_meta_os(sess.targ_cfg.os),
231                                   sess.opts.is_static,
232                                   token::get_ident_interner()));
233
234         let lang_items = time(time_passes, ~"language item collection", ||
235                               middle::lang_items::collect_language_items(crate, sess));
236
237         let middle::resolve::CrateMap {
238             def_map: def_map,
239             exp_map2: exp_map2,
240             trait_map: trait_map
241         } =
242             time(time_passes, ~"resolution", ||
243                  middle::resolve::resolve_crate(sess, lang_items, crate));
244
245         time(time_passes, ~"looking for entry point",
246              || middle::entry::find_entry_point(sess, crate, ast_map));
247
248         let freevars = time(time_passes, ~"freevar finding", ||
249                             freevars::annotate_freevars(def_map, crate));
250
251         let region_map = time(time_passes, ~"region resolution", ||
252                               middle::region::resolve_crate(sess, def_map, crate));
253
254         let rp_set = time(time_passes, ~"region parameterization inference", ||
255                           middle::region::determine_rp_in_crate(sess, ast_map, def_map, crate));
256
257         let ty_cx = ty::mk_ctxt(sess, def_map, ast_map, freevars,
258                                 region_map, rp_set, lang_items);
259
260         // passes are timed inside typeck
261         let (method_map, vtable_map) = typeck::check_crate(
262             ty_cx, trait_map, crate);
263
264         // These next two const passes can probably be merged
265         time(time_passes, ~"const marking", ||
266              middle::const_eval::process_crate(crate, ty_cx));
267
268         time(time_passes, ~"const checking", ||
269              middle::check_const::check_crate(sess, crate, ast_map, def_map,
270                                               method_map, ty_cx));
271
272         if phases.to == cu_typeck { return (Some(crate), Some(ty_cx)); }
273
274         time(time_passes, ~"privacy checking", ||
275              middle::privacy::check_crate(ty_cx, &method_map, crate));
276
277         time(time_passes, ~"effect checking", ||
278              middle::effect::check_crate(ty_cx, method_map, crate));
279
280         time(time_passes, ~"loop checking", ||
281              middle::check_loop::check_crate(ty_cx, crate));
282
283         let middle::moves::MoveMaps {moves_map, moved_variables_set,
284                                      capture_map} =
285             time(time_passes, ~"compute moves", ||
286                  middle::moves::compute_moves(ty_cx, method_map, crate));
287
288         time(time_passes, ~"match checking", ||
289              middle::check_match::check_crate(ty_cx, method_map,
290                                               moves_map, crate));
291
292         time(time_passes, ~"liveness checking", ||
293              middle::liveness::check_crate(ty_cx, method_map,
294                                            capture_map, crate));
295
296         let (root_map, write_guard_map) =
297             time(time_passes, ~"borrow checking", ||
298                  middle::borrowck::check_crate(ty_cx, method_map,
299                                                moves_map, moved_variables_set,
300                                                capture_map, crate));
301
302         time(time_passes, ~"kind checking", ||
303              kind::check_crate(ty_cx, method_map, crate));
304
305         let reachable_map =
306             time(time_passes, ~"reachability checking", ||
307                 reachable::find_reachable(ty_cx, method_map, crate));
308
309         time(time_passes, ~"lint checking", ||
310              lint::check_crate(ty_cx, crate));
311
312         if phases.to == cu_no_trans {
313             return (Some(crate), Some(ty_cx));
314         }
315
316         let maps = astencode::Maps {
317             root_map: root_map,
318             method_map: method_map,
319             vtable_map: vtable_map,
320             write_guard_map: write_guard_map,
321             capture_map: capture_map
322         };
323
324         let outputs = outputs.get_ref();
325         time(time_passes, ~"translation", ||
326              trans::base::trans_crate(sess,
327                                       crate,
328                                       ty_cx,
329                                       &outputs.obj_filename,
330                                       exp_map2,
331                                       reachable_map,
332                                       maps))
333     };
334
335     let outputs = outputs.get_ref();
336     if (sess.opts.debugging_opts & session::print_link_args) != 0 {
337         io::println(link::link_args(sess, &outputs.obj_filename,
338                                     &outputs.out_filename, link_meta).connect(" "));
339     }
340
341     // NB: Android hack
342     if sess.targ_cfg.arch == abi::Arm &&
343             (sess.opts.output_type == link::output_type_object ||
344              sess.opts.output_type == link::output_type_exe) {
345         let output_type = link::output_type_assembly;
346         let obj_filename = outputs.obj_filename.with_filetype("s");
347
348         time(time_passes, ~"LLVM passes", ||
349             link::write::run_passes(sess, llcx, llmod, output_type,
350                                     &obj_filename));
351
352         link::write::run_ndk(sess, &obj_filename, &outputs.obj_filename);
353     } else {
354         time(time_passes, ~"LLVM passes", ||
355             link::write::run_passes(sess, llcx, llmod, sess.opts.output_type,
356                                     &outputs.obj_filename));
357     }
358
359     let stop_after_codegen =
360         sess.opts.output_type != link::output_type_exe ||
361         (sess.opts.is_static && *sess.building_library)   ||
362         sess.opts.jit;
363
364     if stop_after_codegen { return (None, None); }
365
366     time(time_passes, ~"linking", ||
367          link::link_binary(sess,
368                            &outputs.obj_filename,
369                            &outputs.out_filename, link_meta));
370
371     return (None, None);
372 }
373
374 pub fn compile_upto(sess: Session,
375                     cfg: ast::CrateConfig,
376                     input: &input,
377                     upto: compile_phase,
378                     outputs: Option<@OutputFilenames>)
379                     -> (Option<@ast::Crate>, Option<ty::ctxt>) {
380     let time_passes = sess.time_passes();
381     let crate = time(time_passes,
382                      ~"parsing",
383                      || parse_input(sess, cfg.clone(), input) );
384     if upto == cu_parse {
385         return (Some(crate), None);
386     }
387
388     compile_rest(sess,
389                  cfg,
390                  compile_upto {
391                     from: cu_parse,
392                     to: upto
393                  },
394                  outputs,
395                  Some(crate))
396 }
397
398 pub fn compile_input(sess: Session, cfg: ast::CrateConfig, input: &input,
399                      outdir: &Option<Path>, output: &Option<Path>) {
400     let upto = if sess.opts.parse_only { cu_parse }
401                else if sess.opts.no_trans { cu_no_trans }
402                else { cu_everything };
403     let outputs = build_output_filenames(input, outdir, output, [], sess); // ???
404     compile_upto(sess, cfg, input, upto, Some(outputs));
405 }
406
407 pub fn pretty_print_input(sess: Session, cfg: ast::CrateConfig, input: &input,
408                           ppm: pp_mode) {
409     fn ann_paren_for_expr(node: pprust::ann_node) {
410         match node {
411           pprust::node_expr(s, _) => pprust::popen(s),
412           _ => ()
413         }
414     }
415     fn ann_typed_post(tcx: ty::ctxt, node: pprust::ann_node) {
416         match node {
417           pprust::node_expr(s, expr) => {
418             pp::space(s.s);
419             pp::word(s.s, "as");
420             pp::space(s.s);
421             pp::word(s.s, ppaux::ty_to_str(tcx, ty::expr_ty(tcx, expr)));
422             pprust::pclose(s);
423           }
424           _ => ()
425         }
426     }
427     fn ann_identified_post(node: pprust::ann_node) {
428         match node {
429           pprust::node_item(s, item) => {
430             pp::space(s.s);
431             pprust::synth_comment(s, int::to_str(item.id));
432           }
433           pprust::node_block(s, ref blk) => {
434             pp::space(s.s);
435             pprust::synth_comment(
436                 s, ~"block " + int::to_str(blk.id));
437           }
438           pprust::node_expr(s, expr) => {
439             pp::space(s.s);
440             pprust::synth_comment(s, int::to_str(expr.id));
441             pprust::pclose(s);
442           }
443           pprust::node_pat(s, pat) => {
444             pp::space(s.s);
445             pprust::synth_comment(s, ~"pat " + int::to_str(pat.id));
446           }
447         }
448     }
449
450     // Because the pretty printer needs to make a pass over the source
451     // to collect comments and literals, and we need to support reading
452     // from stdin, we're going to just suck the source into a string
453     // so both the parser and pretty-printer can use it.
454     let upto = match ppm {
455       ppm_expanded | ppm_expanded_identified => cu_expand,
456       ppm_typed => cu_typeck,
457       _ => cu_parse
458     };
459     let (crate, tcx) = compile_upto(sess, cfg, input, upto, None);
460
461     let ann = match ppm {
462       ppm_typed => {
463           pprust::pp_ann {pre: ann_paren_for_expr,
464                           post: |a| ann_typed_post(tcx.get(), a) }
465       }
466       ppm_identified | ppm_expanded_identified => {
467           pprust::pp_ann {pre: ann_paren_for_expr,
468                           post: ann_identified_post}
469       }
470       ppm_expanded | ppm_normal => {
471           pprust::no_ann()
472       }
473     };
474     let is_expanded = upto != cu_parse;
475     let src = sess.codemap.get_filemap(source_name(input)).src;
476     do io::with_str_reader(src) |rdr| {
477         pprust::print_crate(sess.codemap, token::get_ident_interner(),
478                             sess.span_diagnostic, crate.unwrap(),
479                             source_name(input),
480                             rdr, io::stdout(), ann, is_expanded);
481     }
482 }
483
484 pub fn get_os(triple: &str) -> Option<session::os> {
485     for os_names.iter().advance |&(name, os)| {
486         if triple.contains(name) { return Some(os) }
487     }
488     None
489 }
490 static os_names : &'static [(&'static str, session::os)] = &'static [
491     ("mingw32", session::os_win32),
492     ("win32",   session::os_win32),
493     ("darwin",  session::os_macos),
494     ("android", session::os_android),
495     ("linux",   session::os_linux),
496     ("freebsd", session::os_freebsd)];
497
498 pub fn get_arch(triple: &str) -> Option<abi::Architecture> {
499     for architecture_abis.iter().advance |&(arch, abi)| {
500         if triple.contains(arch) { return Some(abi) }
501     }
502     None
503 }
504 static architecture_abis : &'static [(&'static str, abi::Architecture)] = &'static [
505     ("i386",   abi::X86),
506     ("i486",   abi::X86),
507     ("i586",   abi::X86),
508     ("i686",   abi::X86),
509     ("i786",   abi::X86),
510
511     ("x86_64", abi::X86_64),
512
513     ("arm",    abi::Arm),
514     ("xscale", abi::Arm),
515
516     ("mips",   abi::Mips)];
517
518 pub fn build_target_config(sopts: @session::options,
519                            demitter: diagnostic::Emitter)
520                         -> @session::config {
521     let os = match get_os(sopts.target_triple) {
522       Some(os) => os,
523       None => early_error(demitter, ~"unknown operating system")
524     };
525     let arch = match get_arch(sopts.target_triple) {
526       Some(arch) => arch,
527       None => early_error(demitter,
528                           ~"unknown architecture: " + sopts.target_triple)
529     };
530     let (int_type, uint_type, float_type) = match arch {
531       abi::X86 => (ast::ty_i32, ast::ty_u32, ast::ty_f64),
532       abi::X86_64 => (ast::ty_i64, ast::ty_u64, ast::ty_f64),
533       abi::Arm => (ast::ty_i32, ast::ty_u32, ast::ty_f64),
534       abi::Mips => (ast::ty_i32, ast::ty_u32, ast::ty_f64)
535     };
536     let target_strs = match arch {
537       abi::X86 => x86::get_target_strs(os),
538       abi::X86_64 => x86_64::get_target_strs(os),
539       abi::Arm => arm::get_target_strs(os),
540       abi::Mips => mips::get_target_strs(os)
541     };
542     let target_cfg = @session::config {
543         os: os,
544         arch: arch,
545         target_strs: target_strs,
546         int_type: int_type,
547         uint_type: uint_type,
548         float_type: float_type
549     };
550     return target_cfg;
551 }
552
553 pub fn host_triple() -> ~str {
554     // Get the host triple out of the build environment. This ensures that our
555     // idea of the host triple is the same as for the set of libraries we've
556     // actually built.  We can't just take LLVM's host triple because they
557     // normalize all ix86 architectures to i386.
558     //
559     // Instead of grabbing the host triple (for the current host), we grab (at
560     // compile time) the target triple that this rustc is built with and
561     // calling that (at runtime) the host triple.
562     let ht = env!("CFG_COMPILER_TRIPLE");
563     return if ht != "" {
564             ht.to_owned()
565         } else {
566             fail!("rustc built without CFG_COMPILER_TRIPLE")
567         };
568 }
569
570 pub fn build_session_options(binary: @str,
571                              matches: &getopts::Matches,
572                              demitter: diagnostic::Emitter)
573                           -> @session::options {
574     let crate_type = if opt_present(matches, "lib") {
575         session::lib_crate
576     } else if opt_present(matches, "bin") {
577         session::bin_crate
578     } else {
579         session::unknown_crate
580     };
581     let parse_only = opt_present(matches, "parse-only");
582     let no_trans = opt_present(matches, "no-trans");
583
584     let lint_levels = [lint::allow, lint::warn,
585                        lint::deny, lint::forbid];
586     let mut lint_opts = ~[];
587     let lint_dict = lint::get_lint_dict();
588     for lint_levels.iter().advance |level| {
589         let level_name = lint::level_to_str(*level);
590
591         // FIXME: #4318 Instead of to_ascii and to_str_ascii, could use
592         // to_ascii_consume and to_str_consume to not do a unnecessary copy.
593         let level_short = level_name.slice_chars(0, 1);
594         let level_short = level_short.to_ascii().to_upper().to_str_ascii();
595         let flags = vec::append(getopts::opt_strs(matches, level_short),
596                                 getopts::opt_strs(matches, level_name));
597         for flags.iter().advance |lint_name| {
598             let lint_name = lint_name.replace("-", "_");
599             match lint_dict.find_equiv(&lint_name) {
600               None => {
601                 early_error(demitter, fmt!("unknown %s flag: %s",
602                                            level_name, lint_name));
603               }
604               Some(lint) => {
605                 lint_opts.push((lint.lint, *level));
606               }
607             }
608         }
609     }
610
611     let mut debugging_opts = 0u;
612     let debug_flags = getopts::opt_strs(matches, "Z");
613     let debug_map = session::debugging_opts_map();
614     for debug_flags.iter().advance |debug_flag| {
615         let mut this_bit = 0u;
616         for debug_map.iter().advance |tuple| {
617             let (name, bit) = match *tuple { (ref a, _, b) => (a, b) };
618             if name == debug_flag { this_bit = bit; break; }
619         }
620         if this_bit == 0u {
621             early_error(demitter, fmt!("unknown debug flag: %s", *debug_flag))
622         }
623         debugging_opts |= this_bit;
624     }
625     if debugging_opts & session::debug_llvm != 0 {
626         unsafe {
627             llvm::LLVMSetDebug(1);
628         }
629     }
630
631     let output_type =
632         if parse_only || no_trans {
633             link::output_type_none
634         } else if opt_present(matches, "S") &&
635                   opt_present(matches, "emit-llvm") {
636             link::output_type_llvm_assembly
637         } else if opt_present(matches, "S") {
638             link::output_type_assembly
639         } else if opt_present(matches, "c") {
640             link::output_type_object
641         } else if opt_present(matches, "emit-llvm") {
642             link::output_type_bitcode
643         } else { link::output_type_exe };
644     let sysroot_opt = getopts::opt_maybe_str(matches, "sysroot");
645     let sysroot_opt = sysroot_opt.map(|m| @Path(*m));
646     let target_opt = getopts::opt_maybe_str(matches, "target");
647     let target_feature_opt = getopts::opt_maybe_str(matches, "target-feature");
648     let save_temps = getopts::opt_present(matches, "save-temps");
649     let opt_level = {
650         if (debugging_opts & session::no_opt) != 0 {
651             No
652         } else if opt_present(matches, "O") {
653             if opt_present(matches, "opt-level") {
654                 early_error(demitter, ~"-O and --opt-level both provided");
655             }
656             Default
657         } else if opt_present(matches, "opt-level") {
658             match getopts::opt_str(matches, "opt-level") {
659               ~"0" => No,
660               ~"1" => Less,
661               ~"2" => Default,
662               ~"3" => Aggressive,
663               _ => {
664                 early_error(demitter, ~"optimization level needs to be between 0-3")
665               }
666             }
667         } else { No }
668     };
669     let gc = debugging_opts & session::gc != 0;
670     let jit = debugging_opts & session::jit != 0;
671     let extra_debuginfo = debugging_opts & session::extra_debug_info != 0;
672     let debuginfo = debugging_opts & session::debug_info != 0 ||
673         extra_debuginfo;
674     let statik = debugging_opts & session::statik != 0;
675     let target =
676         match target_opt {
677             None => host_triple(),
678             Some(s) => s
679         };
680     let target_feature = match target_feature_opt {
681         None => ~"",
682         Some(s) => s
683     };
684
685     let addl_lib_search_paths = getopts::opt_strs(matches, "L").map(|s| Path(*s));
686     let linker = getopts::opt_maybe_str(matches, "linker");
687     let linker_args = getopts::opt_strs(matches, "link-args").flat_map( |a| {
688         a.split_iter(' ').transform(|arg| arg.to_owned()).collect()
689     });
690
691     let cfg = parse_cfgspecs(getopts::opt_strs(matches, "cfg"), demitter);
692     let test = opt_present(matches, "test");
693     let android_cross_path = getopts::opt_maybe_str(
694         matches, "android-cross-path");
695
696     let custom_passes = match getopts::opt_maybe_str(matches, "passes") {
697         None => ~[],
698         Some(s) => {
699             s.split_iter(|c: char| c == ' ' || c == ',').transform(|s| {
700                 s.trim().to_owned()
701             }).collect()
702         }
703     };
704
705     let sopts = @session::options {
706         crate_type: crate_type,
707         is_static: statik,
708         gc: gc,
709         optimize: opt_level,
710         custom_passes: custom_passes,
711         debuginfo: debuginfo,
712         extra_debuginfo: extra_debuginfo,
713         lint_opts: lint_opts,
714         save_temps: save_temps,
715         jit: jit,
716         output_type: output_type,
717         addl_lib_search_paths: @mut addl_lib_search_paths,
718         linker: linker,
719         linker_args: linker_args,
720         maybe_sysroot: sysroot_opt,
721         target_triple: target,
722         target_feature: target_feature,
723         cfg: cfg,
724         binary: binary,
725         test: test,
726         parse_only: parse_only,
727         no_trans: no_trans,
728         debugging_opts: debugging_opts,
729         android_cross_path: android_cross_path
730     };
731     return sopts;
732 }
733
734 pub fn build_session(sopts: @session::options,
735                      demitter: diagnostic::Emitter) -> Session {
736     let codemap = @codemap::CodeMap::new();
737     let diagnostic_handler =
738         diagnostic::mk_handler(Some(demitter));
739     let span_diagnostic_handler =
740         diagnostic::mk_span_handler(diagnostic_handler, codemap);
741     build_session_(sopts, codemap, demitter, span_diagnostic_handler)
742 }
743
744 pub fn build_session_(sopts: @session::options,
745                       cm: @codemap::CodeMap,
746                       demitter: diagnostic::Emitter,
747                       span_diagnostic_handler: @diagnostic::span_handler)
748                    -> Session {
749     let target_cfg = build_target_config(sopts, demitter);
750     let p_s = parse::new_parse_sess_special_handler(span_diagnostic_handler,
751                                                     cm);
752     let cstore = @mut cstore::mk_cstore(token::get_ident_interner());
753     let filesearch = filesearch::mk_filesearch(
754         &sopts.maybe_sysroot,
755         sopts.target_triple,
756         sopts.addl_lib_search_paths);
757     @Session_ {
758         targ_cfg: target_cfg,
759         opts: sopts,
760         cstore: cstore,
761         parse_sess: p_s,
762         codemap: cm,
763         // For a library crate, this is always none
764         entry_fn: @mut None,
765         entry_type: @mut None,
766         span_diagnostic: span_diagnostic_handler,
767         filesearch: filesearch,
768         building_library: @mut false,
769         working_dir: os::getcwd(),
770         lints: @mut HashMap::new(),
771     }
772 }
773
774 pub fn parse_pretty(sess: Session, name: &str) -> pp_mode {
775     match name {
776       &"normal" => ppm_normal,
777       &"expanded" => ppm_expanded,
778       &"typed" => ppm_typed,
779       &"expanded,identified" => ppm_expanded_identified,
780       &"identified" => ppm_identified,
781       _ => {
782         sess.fatal("argument to `pretty` must be one of `normal`, \
783                     `expanded`, `typed`, `identified`, \
784                     or `expanded,identified`");
785       }
786     }
787 }
788
789 // rustc command line options
790 pub fn optgroups() -> ~[getopts::groups::OptGroup] {
791  ~[
792   optflag("",  "bin", "Compile an executable crate (default)"),
793   optflag("c", "",    "Compile and assemble, but do not link"),
794   optmulti("", "cfg", "Configure the compilation
795                           environment", "SPEC"),
796   optflag("",  "emit-llvm",
797                         "Produce an LLVM bitcode file"),
798   optflag("h", "help","Display this message"),
799   optmulti("L", "",   "Add a directory to the library search path",
800                               "PATH"),
801   optflag("",  "lib", "Compile a library crate"),
802   optopt("", "linker", "Program to use for linking instead of the default.", "LINKER"),
803   optmulti("",  "link-args", "FLAGS is a space-separated list of flags
804                             passed to the linker", "FLAGS"),
805   optflag("",  "ls",  "List the symbols defined by a library crate"),
806   optflag("", "no-trans",
807                         "Run all passes except translation; no output"),
808   optflag("O", "",    "Equivalent to --opt-level=2"),
809   optopt("o", "",     "Write output to <filename>", "FILENAME"),
810   optopt("", "opt-level",
811                         "Optimize with possible levels 0-3", "LEVEL"),
812   optopt("", "passes", "Comma or space separated list of pass names to use. \
813                         Overrides the default passes for optimization levels,\n\
814                         a value of \"list\" will list the available passes.", "NAMES"),
815   optopt( "",  "out-dir",
816                         "Write output to compiler-chosen filename
817                           in <dir>", "DIR"),
818   optflag("", "parse-only",
819                         "Parse only; do not compile, assemble, or link"),
820   optflagopt("", "pretty",
821                         "Pretty-print the input instead of compiling;
822                           valid types are: normal (un-annotated source),
823                           expanded (crates expanded),
824                           typed (crates expanded, with type annotations),
825                           or identified (fully parenthesized,
826                           AST nodes and blocks with IDs)", "TYPE"),
827   optflag("S", "",    "Compile only; do not assemble or link"),
828   optflag("", "save-temps",
829                         "Write intermediate files (.bc, .opt.bc, .o)
830                           in addition to normal output"),
831   optopt("", "sysroot",
832                         "Override the system root", "PATH"),
833   optflag("", "test", "Build a test harness"),
834   optopt("", "target",
835                         "Target triple cpu-manufacturer-kernel[-os]
836                           to compile for (see chapter 3.4 of http://www.sourceware.org/autobook/
837                           for detail)", "TRIPLE"),
838   optopt("", "target-feature",
839                         "Target specific attributes (llc -mattr=help
840                           for detail)", "FEATURE"),
841   optopt("", "android-cross-path",
842          "The path to the Android NDK", "PATH"),
843   optflagopt("W", "warn",
844                         "Set lint warnings", "OPT"),
845   optmulti("A", "allow",
846                         "Set lint allowed", "OPT"),
847   optmulti("D", "deny",
848                         "Set lint denied", "OPT"),
849   optmulti("F", "forbid",
850                         "Set lint forbidden", "OPT"),
851   optmulti("Z", "",   "Set internal debugging options", "FLAG"),
852   optflag( "v", "version",
853                         "Print version info and exit"),
854  ]
855 }
856
857 pub struct OutputFilenames {
858     out_filename: Path,
859     obj_filename: Path
860 }
861
862 pub fn build_output_filenames(input: &input,
863                               odir: &Option<Path>,
864                               ofile: &Option<Path>,
865                               attrs: &[ast::Attribute],
866                               sess: Session)
867                            -> @OutputFilenames {
868     let obj_path;
869     let out_path;
870     let sopts = sess.opts;
871     let stop_after_codegen =
872         sopts.output_type != link::output_type_exe ||
873             sopts.is_static && *sess.building_library;
874
875     let obj_suffix =
876         match sopts.output_type {
877           link::output_type_none => ~"none",
878           link::output_type_bitcode => ~"bc",
879           link::output_type_assembly => ~"s",
880           link::output_type_llvm_assembly => ~"ll",
881           // Object and exe output both use the '.o' extension here
882           link::output_type_object | link::output_type_exe => ~"o"
883         };
884
885     match *ofile {
886       None => {
887           // "-" as input file will cause the parser to read from stdin so we
888           // have to make up a name
889           // We want to toss everything after the final '.'
890           let dirpath = match *odir {
891               Some(ref d) => (*d).clone(),
892               None => match *input {
893                   str_input(_) => os::getcwd(),
894                   file_input(ref ifile) => (*ifile).dir_path()
895               }
896           };
897
898           let mut stem = match *input {
899               file_input(ref ifile) => (*ifile).filestem().get().to_managed(),
900               str_input(_) => @"rust_out"
901           };
902
903           // If a linkage name meta is present, we use it as the link name
904           let linkage_metas = attr::find_linkage_metas(attrs);
905           if !linkage_metas.is_empty() {
906               // But if a linkage meta is present, that overrides
907               let maybe_name = linkage_metas.iter().find_(|m| "name" == m.name());
908               match maybe_name.chain(|m| m.value_str()) {
909                   Some(s) => stem = s,
910                   _ => ()
911               }
912               // If the name is missing, we just default to the filename
913               // version
914           }
915
916           if *sess.building_library {
917               out_path = dirpath.push(os::dll_filename(stem));
918               obj_path = dirpath.push(stem).with_filetype(obj_suffix);
919           } else {
920               out_path = dirpath.push(stem);
921               obj_path = dirpath.push(stem).with_filetype(obj_suffix);
922           }
923       }
924
925       Some(ref out_file) => {
926         out_path = (*out_file).clone();
927         obj_path = if stop_after_codegen {
928             (*out_file).clone()
929         } else {
930             (*out_file).with_filetype(obj_suffix)
931         };
932
933         if *sess.building_library {
934             // FIXME (#2401): We might want to warn here; we're actually not
935             // going to respect the user's choice of library name when it
936             // comes time to link, we'll be linking to
937             // lib<basename>-<hash>-<version>.so no matter what.
938         }
939
940         if *odir != None {
941             sess.warn("ignoring --out-dir flag due to -o flag.");
942         }
943       }
944     }
945
946     @OutputFilenames {
947         out_filename: out_path,
948         obj_filename: obj_path
949     }
950 }
951
952 pub fn early_error(emitter: diagnostic::Emitter, msg: ~str) -> ! {
953     emitter(None, msg, diagnostic::fatal);
954     fail!();
955 }
956
957 pub fn list_metadata(sess: Session, path: &Path, out: @io::Writer) {
958     metadata::loader::list_file_metadata(
959         token::get_ident_interner(),
960         session::sess_os_to_meta_os(sess.targ_cfg.os), path, out);
961 }
962
963 #[cfg(test)]
964 mod test {
965
966     use driver::driver::{build_configuration, build_session};
967     use driver::driver::{build_session_options, optgroups, str_input};
968
969     use extra::getopts::groups::getopts;
970     use extra::getopts;
971     use syntax::attr;
972     use syntax::diagnostic;
973
974     // When the user supplies --test we should implicitly supply --cfg test
975     #[test]
976     fn test_switch_implies_cfg_test() {
977         let matches =
978             &match getopts([~"--test"], optgroups()) {
979               Ok(m) => m,
980               Err(f) => fail!("test_switch_implies_cfg_test: %s", getopts::fail_str(f))
981             };
982         let sessopts = build_session_options(
983             @"rustc", matches, diagnostic::emit);
984         let sess = build_session(sessopts, diagnostic::emit);
985         let cfg = build_configuration(sess, @"whatever", &str_input(@""));
986         assert!((attr::contains_name(cfg, "test")));
987     }
988
989     // When the user supplies --test and --cfg test, don't implicitly add
990     // another --cfg test
991     #[test]
992     fn test_switch_implies_cfg_test_unless_cfg_test() {
993         let matches =
994             &match getopts([~"--test", ~"--cfg=test"], optgroups()) {
995               Ok(m) => m,
996               Err(f) => {
997                 fail!("test_switch_implies_cfg_test_unless_cfg_test: %s", getopts::fail_str(f));
998               }
999             };
1000         let sessopts = build_session_options(
1001             @"rustc", matches, diagnostic::emit);
1002         let sess = build_session(sessopts, diagnostic::emit);
1003         let cfg = build_configuration(sess, @"whatever", &str_input(@""));
1004         let mut test_items = cfg.iter().filter(|m| "test" == m.name());
1005         assert!(test_items.next().is_some());
1006         assert!(test_items.next().is_none());
1007     }
1008 }