]> git.lizzy.rs Git - rust.git/blob - src/librustc/driver/driver.rs
rustc: reorganize driver, replace compile_upto with multiple more-obvious functions.
[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     let crate = phase_1_parse_input(sess, cfg.clone(), input);
413     if stop_after_phase_1(sess) { return; }
414     let expanded_crate = phase_2_configure_and_expand(sess, cfg, crate);
415     let analysis = phase_3_run_analysis_passes(sess, expanded_crate);
416     if stop_after_phase_3(sess) { return; }
417     let trans = phase_4_translate_to_llvm(sess, expanded_crate, &analysis, outputs);
418     phase_5_run_llvm_passes(sess, &trans, outputs);
419     if stop_after_phase_5(sess) { return; }
420     phase_6_link_output(sess, &trans, outputs);
421 }
422
423 pub fn pretty_print_input(sess: Session, cfg: ast::CrateConfig, input: &input,
424                           ppm: pp_mode) {
425
426     fn ann_paren_for_expr(node: pprust::ann_node) {
427         match node {
428           pprust::node_expr(s, _) => pprust::popen(s),
429           _ => ()
430         }
431     }
432     fn ann_typed_post(tcx: ty::ctxt, node: pprust::ann_node) {
433         match node {
434           pprust::node_expr(s, expr) => {
435             pp::space(s.s);
436             pp::word(s.s, "as");
437             pp::space(s.s);
438             pp::word(s.s, ppaux::ty_to_str(tcx, ty::expr_ty(tcx, expr)));
439             pprust::pclose(s);
440           }
441           _ => ()
442         }
443     }
444     fn ann_identified_post(node: pprust::ann_node) {
445         match node {
446           pprust::node_item(s, item) => {
447             pp::space(s.s);
448             pprust::synth_comment(s, int::to_str(item.id));
449           }
450           pprust::node_block(s, ref blk) => {
451             pp::space(s.s);
452             pprust::synth_comment(
453                 s, ~"block " + int::to_str(blk.id));
454           }
455           pprust::node_expr(s, expr) => {
456             pp::space(s.s);
457             pprust::synth_comment(s, int::to_str(expr.id));
458             pprust::pclose(s);
459           }
460           pprust::node_pat(s, pat) => {
461             pp::space(s.s);
462             pprust::synth_comment(s, ~"pat " + int::to_str(pat.id));
463           }
464         }
465     }
466
467     let crate = phase_1_parse_input(sess, cfg.clone(), input);
468
469     let (crate, is_expanded) = match ppm {
470         ppm_expanded | ppm_expanded_identified | ppm_typed => {
471             (phase_2_configure_and_expand(sess, cfg, crate), true)
472         }
473         _ => (crate, false)
474     };
475
476     let annotation = match ppm {
477         ppm_identified | ppm_expanded_identified => {
478             pprust::pp_ann {
479                 pre: ann_paren_for_expr,
480                 post: ann_identified_post
481             }
482         }
483         ppm_typed => {
484             let analysis = phase_3_run_analysis_passes(sess, crate);
485             pprust::pp_ann {
486                 pre: ann_paren_for_expr,
487                 post: |a| ann_typed_post(analysis.ty_cx, a)
488             }
489         }
490         _ => pprust::no_ann()
491     };
492
493     let src = sess.codemap.get_filemap(source_name(input)).src;
494     do io::with_str_reader(src) |rdr| {
495         pprust::print_crate(sess.codemap, token::get_ident_interner(),
496                             sess.span_diagnostic, crate,
497                             source_name(input),
498                             rdr, io::stdout(),
499                             annotation, is_expanded);
500     }
501 }
502
503 pub fn get_os(triple: &str) -> Option<session::os> {
504     for os_names.iter().advance |&(name, os)| {
505         if triple.contains(name) { return Some(os) }
506     }
507     None
508 }
509 static os_names : &'static [(&'static str, session::os)] = &'static [
510     ("mingw32", session::os_win32),
511     ("win32",   session::os_win32),
512     ("darwin",  session::os_macos),
513     ("android", session::os_android),
514     ("linux",   session::os_linux),
515     ("freebsd", session::os_freebsd)];
516
517 pub fn get_arch(triple: &str) -> Option<abi::Architecture> {
518     for architecture_abis.iter().advance |&(arch, abi)| {
519         if triple.contains(arch) { return Some(abi) }
520     }
521     None
522 }
523 static architecture_abis : &'static [(&'static str, abi::Architecture)] = &'static [
524     ("i386",   abi::X86),
525     ("i486",   abi::X86),
526     ("i586",   abi::X86),
527     ("i686",   abi::X86),
528     ("i786",   abi::X86),
529
530     ("x86_64", abi::X86_64),
531
532     ("arm",    abi::Arm),
533     ("xscale", abi::Arm),
534
535     ("mips",   abi::Mips)];
536
537 pub fn build_target_config(sopts: @session::options,
538                            demitter: diagnostic::Emitter)
539                         -> @session::config {
540     let os = match get_os(sopts.target_triple) {
541       Some(os) => os,
542       None => early_error(demitter, ~"unknown operating system")
543     };
544     let arch = match get_arch(sopts.target_triple) {
545       Some(arch) => arch,
546       None => early_error(demitter,
547                           ~"unknown architecture: " + sopts.target_triple)
548     };
549     let (int_type, uint_type, float_type) = match arch {
550       abi::X86 => (ast::ty_i32, ast::ty_u32, ast::ty_f64),
551       abi::X86_64 => (ast::ty_i64, ast::ty_u64, ast::ty_f64),
552       abi::Arm => (ast::ty_i32, ast::ty_u32, ast::ty_f64),
553       abi::Mips => (ast::ty_i32, ast::ty_u32, ast::ty_f64)
554     };
555     let target_strs = match arch {
556       abi::X86 => x86::get_target_strs(os),
557       abi::X86_64 => x86_64::get_target_strs(os),
558       abi::Arm => arm::get_target_strs(os),
559       abi::Mips => mips::get_target_strs(os)
560     };
561     let target_cfg = @session::config {
562         os: os,
563         arch: arch,
564         target_strs: target_strs,
565         int_type: int_type,
566         uint_type: uint_type,
567         float_type: float_type
568     };
569     return target_cfg;
570 }
571
572 pub fn host_triple() -> ~str {
573     // Get the host triple out of the build environment. This ensures that our
574     // idea of the host triple is the same as for the set of libraries we've
575     // actually built.  We can't just take LLVM's host triple because they
576     // normalize all ix86 architectures to i386.
577     //
578     // Instead of grabbing the host triple (for the current host), we grab (at
579     // compile time) the target triple that this rustc is built with and
580     // calling that (at runtime) the host triple.
581     let ht = env!("CFG_COMPILER_TRIPLE");
582     return if ht != "" {
583             ht.to_owned()
584         } else {
585             fail!("rustc built without CFG_COMPILER_TRIPLE")
586         };
587 }
588
589 pub fn build_session_options(binary: @str,
590                              matches: &getopts::Matches,
591                              demitter: diagnostic::Emitter)
592                           -> @session::options {
593     let crate_type = if opt_present(matches, "lib") {
594         session::lib_crate
595     } else if opt_present(matches, "bin") {
596         session::bin_crate
597     } else {
598         session::unknown_crate
599     };
600     let parse_only = opt_present(matches, "parse-only");
601     let no_trans = opt_present(matches, "no-trans");
602
603     let lint_levels = [lint::allow, lint::warn,
604                        lint::deny, lint::forbid];
605     let mut lint_opts = ~[];
606     let lint_dict = lint::get_lint_dict();
607     for lint_levels.iter().advance |level| {
608         let level_name = lint::level_to_str(*level);
609
610         // FIXME: #4318 Instead of to_ascii and to_str_ascii, could use
611         // to_ascii_consume and to_str_consume to not do a unnecessary copy.
612         let level_short = level_name.slice_chars(0, 1);
613         let level_short = level_short.to_ascii().to_upper().to_str_ascii();
614         let flags = vec::append(getopts::opt_strs(matches, level_short),
615                                 getopts::opt_strs(matches, level_name));
616         for flags.iter().advance |lint_name| {
617             let lint_name = lint_name.replace("-", "_");
618             match lint_dict.find_equiv(&lint_name) {
619               None => {
620                 early_error(demitter, fmt!("unknown %s flag: %s",
621                                            level_name, lint_name));
622               }
623               Some(lint) => {
624                 lint_opts.push((lint.lint, *level));
625               }
626             }
627         }
628     }
629
630     let mut debugging_opts = 0u;
631     let debug_flags = getopts::opt_strs(matches, "Z");
632     let debug_map = session::debugging_opts_map();
633     for debug_flags.iter().advance |debug_flag| {
634         let mut this_bit = 0u;
635         for debug_map.iter().advance |tuple| {
636             let (name, bit) = match *tuple { (ref a, _, b) => (a, b) };
637             if name == debug_flag { this_bit = bit; break; }
638         }
639         if this_bit == 0u {
640             early_error(demitter, fmt!("unknown debug flag: %s", *debug_flag))
641         }
642         debugging_opts |= this_bit;
643     }
644     if debugging_opts & session::debug_llvm != 0 {
645         unsafe {
646             llvm::LLVMSetDebug(1);
647         }
648     }
649
650     let output_type =
651         if parse_only || no_trans {
652             link::output_type_none
653         } else if opt_present(matches, "S") &&
654                   opt_present(matches, "emit-llvm") {
655             link::output_type_llvm_assembly
656         } else if opt_present(matches, "S") {
657             link::output_type_assembly
658         } else if opt_present(matches, "c") {
659             link::output_type_object
660         } else if opt_present(matches, "emit-llvm") {
661             link::output_type_bitcode
662         } else { link::output_type_exe };
663     let sysroot_opt = getopts::opt_maybe_str(matches, "sysroot");
664     let sysroot_opt = sysroot_opt.map(|m| @Path(*m));
665     let target_opt = getopts::opt_maybe_str(matches, "target");
666     let target_feature_opt = getopts::opt_maybe_str(matches, "target-feature");
667     let save_temps = getopts::opt_present(matches, "save-temps");
668     let opt_level = {
669         if (debugging_opts & session::no_opt) != 0 {
670             No
671         } else if opt_present(matches, "O") {
672             if opt_present(matches, "opt-level") {
673                 early_error(demitter, ~"-O and --opt-level both provided");
674             }
675             Default
676         } else if opt_present(matches, "opt-level") {
677             match getopts::opt_str(matches, "opt-level") {
678               ~"0" => No,
679               ~"1" => Less,
680               ~"2" => Default,
681               ~"3" => Aggressive,
682               _ => {
683                 early_error(demitter, ~"optimization level needs to be between 0-3")
684               }
685             }
686         } else { No }
687     };
688     let gc = debugging_opts & session::gc != 0;
689     let jit = debugging_opts & session::jit != 0;
690     let extra_debuginfo = debugging_opts & session::extra_debug_info != 0;
691     let debuginfo = debugging_opts & session::debug_info != 0 ||
692         extra_debuginfo;
693     let statik = debugging_opts & session::statik != 0;
694     let target =
695         match target_opt {
696             None => host_triple(),
697             Some(s) => s
698         };
699     let target_feature = match target_feature_opt {
700         None => ~"",
701         Some(s) => s
702     };
703
704     let addl_lib_search_paths = getopts::opt_strs(matches, "L").map(|s| Path(*s));
705     let linker = getopts::opt_maybe_str(matches, "linker");
706     let linker_args = getopts::opt_strs(matches, "link-args").flat_map( |a| {
707         a.split_iter(' ').transform(|arg| arg.to_owned()).collect()
708     });
709
710     let cfg = parse_cfgspecs(getopts::opt_strs(matches, "cfg"), demitter);
711     let test = opt_present(matches, "test");
712     let android_cross_path = getopts::opt_maybe_str(
713         matches, "android-cross-path");
714
715     let custom_passes = match getopts::opt_maybe_str(matches, "passes") {
716         None => ~[],
717         Some(s) => {
718             s.split_iter(|c: char| c == ' ' || c == ',').transform(|s| {
719                 s.trim().to_owned()
720             }).collect()
721         }
722     };
723
724     let sopts = @session::options {
725         crate_type: crate_type,
726         is_static: statik,
727         gc: gc,
728         optimize: opt_level,
729         custom_passes: custom_passes,
730         debuginfo: debuginfo,
731         extra_debuginfo: extra_debuginfo,
732         lint_opts: lint_opts,
733         save_temps: save_temps,
734         jit: jit,
735         output_type: output_type,
736         addl_lib_search_paths: @mut addl_lib_search_paths,
737         linker: linker,
738         linker_args: linker_args,
739         maybe_sysroot: sysroot_opt,
740         target_triple: target,
741         target_feature: target_feature,
742         cfg: cfg,
743         binary: binary,
744         test: test,
745         parse_only: parse_only,
746         no_trans: no_trans,
747         debugging_opts: debugging_opts,
748         android_cross_path: android_cross_path
749     };
750     return sopts;
751 }
752
753 pub fn build_session(sopts: @session::options,
754                      demitter: diagnostic::Emitter) -> Session {
755     let codemap = @codemap::CodeMap::new();
756     let diagnostic_handler =
757         diagnostic::mk_handler(Some(demitter));
758     let span_diagnostic_handler =
759         diagnostic::mk_span_handler(diagnostic_handler, codemap);
760     build_session_(sopts, codemap, demitter, span_diagnostic_handler)
761 }
762
763 pub fn build_session_(sopts: @session::options,
764                       cm: @codemap::CodeMap,
765                       demitter: diagnostic::Emitter,
766                       span_diagnostic_handler: @diagnostic::span_handler)
767                    -> Session {
768     let target_cfg = build_target_config(sopts, demitter);
769     let p_s = parse::new_parse_sess_special_handler(span_diagnostic_handler,
770                                                     cm);
771     let cstore = @mut cstore::mk_cstore(token::get_ident_interner());
772     let filesearch = filesearch::mk_filesearch(
773         &sopts.maybe_sysroot,
774         sopts.target_triple,
775         sopts.addl_lib_search_paths);
776     @Session_ {
777         targ_cfg: target_cfg,
778         opts: sopts,
779         cstore: cstore,
780         parse_sess: p_s,
781         codemap: cm,
782         // For a library crate, this is always none
783         entry_fn: @mut None,
784         entry_type: @mut None,
785         span_diagnostic: span_diagnostic_handler,
786         filesearch: filesearch,
787         building_library: @mut false,
788         working_dir: os::getcwd(),
789         lints: @mut HashMap::new(),
790     }
791 }
792
793 pub fn parse_pretty(sess: Session, name: &str) -> pp_mode {
794     match name {
795       &"normal" => ppm_normal,
796       &"expanded" => ppm_expanded,
797       &"typed" => ppm_typed,
798       &"expanded,identified" => ppm_expanded_identified,
799       &"identified" => ppm_identified,
800       _ => {
801         sess.fatal("argument to `pretty` must be one of `normal`, \
802                     `expanded`, `typed`, `identified`, \
803                     or `expanded,identified`");
804       }
805     }
806 }
807
808 // rustc command line options
809 pub fn optgroups() -> ~[getopts::groups::OptGroup] {
810  ~[
811   optflag("",  "bin", "Compile an executable crate (default)"),
812   optflag("c", "",    "Compile and assemble, but do not link"),
813   optmulti("", "cfg", "Configure the compilation
814                           environment", "SPEC"),
815   optflag("",  "emit-llvm",
816                         "Produce an LLVM bitcode file"),
817   optflag("h", "help","Display this message"),
818   optmulti("L", "",   "Add a directory to the library search path",
819                               "PATH"),
820   optflag("",  "lib", "Compile a library crate"),
821   optopt("", "linker", "Program to use for linking instead of the default.", "LINKER"),
822   optmulti("",  "link-args", "FLAGS is a space-separated list of flags
823                             passed to the linker", "FLAGS"),
824   optflag("",  "ls",  "List the symbols defined by a library crate"),
825   optflag("", "no-trans",
826                         "Run all passes except translation; no output"),
827   optflag("O", "",    "Equivalent to --opt-level=2"),
828   optopt("o", "",     "Write output to <filename>", "FILENAME"),
829   optopt("", "opt-level",
830                         "Optimize with possible levels 0-3", "LEVEL"),
831   optopt("", "passes", "Comma or space separated list of pass names to use. \
832                         Overrides the default passes for optimization levels,\n\
833                         a value of \"list\" will list the available passes.", "NAMES"),
834   optopt( "",  "out-dir",
835                         "Write output to compiler-chosen filename
836                           in <dir>", "DIR"),
837   optflag("", "parse-only",
838                         "Parse only; do not compile, assemble, or link"),
839   optflagopt("", "pretty",
840                         "Pretty-print the input instead of compiling;
841                           valid types are: normal (un-annotated source),
842                           expanded (crates expanded),
843                           typed (crates expanded, with type annotations),
844                           or identified (fully parenthesized,
845                           AST nodes and blocks with IDs)", "TYPE"),
846   optflag("S", "",    "Compile only; do not assemble or link"),
847   optflag("", "save-temps",
848                         "Write intermediate files (.bc, .opt.bc, .o)
849                           in addition to normal output"),
850   optopt("", "sysroot",
851                         "Override the system root", "PATH"),
852   optflag("", "test", "Build a test harness"),
853   optopt("", "target",
854                         "Target triple cpu-manufacturer-kernel[-os]
855                           to compile for (see chapter 3.4 of http://www.sourceware.org/autobook/
856                           for detail)", "TRIPLE"),
857   optopt("", "target-feature",
858                         "Target specific attributes (llc -mattr=help
859                           for detail)", "FEATURE"),
860   optopt("", "android-cross-path",
861          "The path to the Android NDK", "PATH"),
862   optflagopt("W", "warn",
863                         "Set lint warnings", "OPT"),
864   optmulti("A", "allow",
865                         "Set lint allowed", "OPT"),
866   optmulti("D", "deny",
867                         "Set lint denied", "OPT"),
868   optmulti("F", "forbid",
869                         "Set lint forbidden", "OPT"),
870   optmulti("Z", "",   "Set internal debugging options", "FLAG"),
871   optflag( "v", "version",
872                         "Print version info and exit"),
873  ]
874 }
875
876 pub struct OutputFilenames {
877     out_filename: Path,
878     obj_filename: Path
879 }
880
881 pub fn build_output_filenames(input: &input,
882                               odir: &Option<Path>,
883                               ofile: &Option<Path>,
884                               attrs: &[ast::Attribute],
885                               sess: Session)
886                            -> ~OutputFilenames {
887     let obj_path;
888     let out_path;
889     let sopts = sess.opts;
890     let stop_after_codegen =
891         sopts.output_type != link::output_type_exe ||
892             sopts.is_static && *sess.building_library;
893
894     let obj_suffix =
895         match sopts.output_type {
896           link::output_type_none => ~"none",
897           link::output_type_bitcode => ~"bc",
898           link::output_type_assembly => ~"s",
899           link::output_type_llvm_assembly => ~"ll",
900           // Object and exe output both use the '.o' extension here
901           link::output_type_object | link::output_type_exe => ~"o"
902         };
903
904     match *ofile {
905       None => {
906           // "-" as input file will cause the parser to read from stdin so we
907           // have to make up a name
908           // We want to toss everything after the final '.'
909           let dirpath = match *odir {
910               Some(ref d) => (*d).clone(),
911               None => match *input {
912                   str_input(_) => os::getcwd(),
913                   file_input(ref ifile) => (*ifile).dir_path()
914               }
915           };
916
917           let mut stem = match *input {
918               file_input(ref ifile) => (*ifile).filestem().get().to_managed(),
919               str_input(_) => @"rust_out"
920           };
921
922           // If a linkage name meta is present, we use it as the link name
923           let linkage_metas = attr::find_linkage_metas(attrs);
924           if !linkage_metas.is_empty() {
925               // But if a linkage meta is present, that overrides
926               let maybe_name = linkage_metas.iter().find_(|m| "name" == m.name());
927               match maybe_name.chain(|m| m.value_str()) {
928                   Some(s) => stem = s,
929                   _ => ()
930               }
931               // If the name is missing, we just default to the filename
932               // version
933           }
934
935           if *sess.building_library {
936               out_path = dirpath.push(os::dll_filename(stem));
937               obj_path = dirpath.push(stem).with_filetype(obj_suffix);
938           } else {
939               out_path = dirpath.push(stem);
940               obj_path = dirpath.push(stem).with_filetype(obj_suffix);
941           }
942       }
943
944       Some(ref out_file) => {
945         out_path = (*out_file).clone();
946         obj_path = if stop_after_codegen {
947             (*out_file).clone()
948         } else {
949             (*out_file).with_filetype(obj_suffix)
950         };
951
952         if *sess.building_library {
953             // FIXME (#2401): We might want to warn here; we're actually not
954             // going to respect the user's choice of library name when it
955             // comes time to link, we'll be linking to
956             // lib<basename>-<hash>-<version>.so no matter what.
957         }
958
959         if *odir != None {
960             sess.warn("ignoring --out-dir flag due to -o flag.");
961         }
962       }
963     }
964
965     ~OutputFilenames {
966         out_filename: out_path,
967         obj_filename: obj_path
968     }
969 }
970
971 pub fn early_error(emitter: diagnostic::Emitter, msg: ~str) -> ! {
972     emitter(None, msg, diagnostic::fatal);
973     fail!();
974 }
975
976 pub fn list_metadata(sess: Session, path: &Path, out: @io::Writer) {
977     metadata::loader::list_file_metadata(
978         token::get_ident_interner(),
979         session::sess_os_to_meta_os(sess.targ_cfg.os), path, out);
980 }
981
982 #[cfg(test)]
983 mod test {
984
985     use driver::driver::{build_configuration, build_session};
986     use driver::driver::{build_session_options, optgroups, str_input};
987
988     use extra::getopts::groups::getopts;
989     use extra::getopts;
990     use syntax::attr;
991     use syntax::diagnostic;
992
993     // When the user supplies --test we should implicitly supply --cfg test
994     #[test]
995     fn test_switch_implies_cfg_test() {
996         let matches =
997             &match getopts([~"--test"], optgroups()) {
998               Ok(m) => m,
999               Err(f) => fail!("test_switch_implies_cfg_test: %s", getopts::fail_str(f))
1000             };
1001         let sessopts = build_session_options(
1002             @"rustc", matches, diagnostic::emit);
1003         let sess = build_session(sessopts, diagnostic::emit);
1004         let cfg = build_configuration(sess, @"whatever", &str_input(@""));
1005         assert!((attr::contains_name(cfg, "test")));
1006     }
1007
1008     // When the user supplies --test and --cfg test, don't implicitly add
1009     // another --cfg test
1010     #[test]
1011     fn test_switch_implies_cfg_test_unless_cfg_test() {
1012         let matches =
1013             &match getopts([~"--test", ~"--cfg=test"], optgroups()) {
1014               Ok(m) => m,
1015               Err(f) => {
1016                 fail!("test_switch_implies_cfg_test_unless_cfg_test: %s", getopts::fail_str(f));
1017               }
1018             };
1019         let sessopts = build_session_options(
1020             @"rustc", matches, diagnostic::emit);
1021         let sess = build_session(sessopts, diagnostic::emit);
1022         let cfg = build_configuration(sess, @"whatever", &str_input(@""));
1023         let mut test_items = cfg.iter().filter(|m| "test" == m.name());
1024         assert!(test_items.next().is_some());
1025         assert!(test_items.next().is_none());
1026     }
1027 }