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