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