]> git.lizzy.rs Git - rust.git/blob - src/librustc/driver/driver.rs
Add externfn macro and correctly label fixed_stack_segments
[rust.git] / src / librustc / driver / driver.rs
1 // Copyright 2012-2013 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
4 //
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
10
11
12 use back::link;
13 use back::{arm, x86, x86_64, mips};
14 use driver::session::{Aggressive};
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) ->
69    ast::CrateConfig {
70     let tos = match sess.targ_cfg.os {
71         session::os_win32 =>   @"win32",
72         session::os_macos =>   @"macos",
73         session::os_linux =>   @"linux",
74         session::os_android => @"android",
75         session::os_freebsd => @"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     ];
96 }
97
98 pub fn append_configuration(cfg: &mut ast::CrateConfig, name: @str) {
99     if !cfg.iter().any(|mi| mi.name() == name) {
100         cfg.push(attr::mk_word_item(name))
101     }
102 }
103
104 pub fn build_configuration(sess: Session) ->
105    ast::CrateConfig {
106     // Combine the configuration requested by the session (command line) with
107     // some default and generated configuration items
108     let default_cfg = default_configuration(sess);
109     let mut user_cfg = sess.opts.cfg.clone();
110     // If the user wants a test runner, then add the test cfg
111     if sess.opts.test { append_configuration(&mut user_cfg, @"test") }
112     // If the user requested GC, then add the GC cfg
113     append_configuration(&mut user_cfg, if sess.opts.gc { @"gc" } else { @"nogc" });
114     return vec::append(user_cfg, default_cfg);
115 }
116
117 // Convert strings provided as --cfg [cfgspec] into a crate_cfg
118 fn parse_cfgspecs(cfgspecs: ~[~str],
119                   demitter: diagnostic::Emitter) -> ast::CrateConfig {
120     do cfgspecs.move_iter().map |s| {
121         let sess = parse::new_parse_sess(Some(demitter));
122         parse::parse_meta_from_source_str(@"cfgspec", s.to_managed(), ~[], sess)
123     }.collect::<ast::CrateConfig>()
124 }
125
126 pub enum input {
127     /// Load source from file
128     file_input(Path),
129     /// The string is the source
130     // FIXME (#2319): Don't really want to box the source string
131     str_input(@str)
132 }
133
134 pub fn phase_1_parse_input(sess: Session, cfg: ast::CrateConfig, input: &input)
135     -> @ast::Crate {
136     time(sess.time_passes(), ~"parsing", || {
137         match *input {
138             file_input(ref file) => {
139                 parse::parse_crate_from_file(&(*file), cfg.clone(), sess.parse_sess)
140             }
141             str_input(src) => {
142                 parse::parse_crate_from_source_str(
143                     anon_src(), src, cfg.clone(), sess.parse_sess)
144             }
145         }
146     })
147 }
148
149 // For continuing compilation after a parsed crate has been
150 // modified
151
152 /// Run the "early phases" of the compiler: initial `cfg` processing,
153 /// syntax expansion, secondary `cfg` expansion, synthesis of a test
154 /// harness if one is to be provided and injection of a dependency on the
155 /// standard library and prelude.
156 pub fn phase_2_configure_and_expand(sess: Session,
157                                     cfg: ast::CrateConfig,
158                                     mut crate: @ast::Crate) -> @ast::Crate {
159     let time_passes = sess.time_passes();
160
161     *sess.building_library = session::building_library(sess.opts.crate_type,
162                                                        crate, sess.opts.test);
163
164
165     // strip before expansion to allow macros to depend on
166     // configuration variables e.g/ in
167     //
168     //   #[macro_escape] #[cfg(foo)]
169     //   mod bar { macro_rules! baz!(() => {{}}) }
170     //
171     // baz! should not use this definition unless foo is enabled.
172     crate = time(time_passes, ~"std macros injection", ||
173                  syntax::ext::expand::inject_std_macros(sess.parse_sess,
174                                                         cfg.clone(),
175                                                         crate));
176
177     crate = time(time_passes, ~"configuration 1", ||
178                  front::config::strip_unconfigured_items(crate));
179
180     crate = time(time_passes, ~"expansion", ||
181                  syntax::ext::expand::expand_crate(sess.parse_sess, cfg.clone(),
182                                                    crate));
183
184     // strip again, in case expansion added anything with a #[cfg].
185     crate = time(time_passes, ~"configuration 2", ||
186                  front::config::strip_unconfigured_items(crate));
187
188     crate = time(time_passes, ~"maybe building test harness", ||
189                  front::test::modify_for_testing(sess, crate));
190
191     crate = time(time_passes, ~"std injection", ||
192                  front::std_inject::maybe_inject_libstd_ref(sess, crate));
193
194     return crate;
195 }
196
197 pub struct CrateAnalysis {
198     exp_map2: middle::resolve::ExportMap2,
199     ty_cx: ty::ctxt,
200     maps: astencode::Maps,
201     reachable: @mut HashSet<ast::NodeId>
202 }
203
204 /// Run the resolution, typechecking, region checking and other
205 /// miscellaneous analysis passes on the crate. Return various
206 /// structures carrying the results of the analysis.
207 pub fn phase_3_run_analysis_passes(sess: Session,
208                                    crate: @ast::Crate) -> CrateAnalysis {
209
210     let time_passes = sess.time_passes();
211     let ast_map = time(time_passes, ~"ast indexing", ||
212                        syntax::ast_map::map_crate(sess.diagnostic(), crate));
213
214     time(time_passes, ~"external crate/lib resolution", ||
215          creader::read_crates(sess.diagnostic(), crate, sess.cstore,
216                               sess.filesearch,
217                               session::sess_os_to_meta_os(sess.targ_cfg.os),
218                               sess.opts.is_static,
219                               token::get_ident_interner()));
220
221     let lang_items = time(time_passes, ~"language item collection", ||
222                           middle::lang_items::collect_language_items(crate, sess));
223
224     let middle::resolve::CrateMap {
225         def_map: def_map,
226         exp_map2: exp_map2,
227         trait_map: trait_map
228     } =
229         time(time_passes, ~"resolution", ||
230              middle::resolve::resolve_crate(sess, lang_items, crate));
231
232     time(time_passes, ~"looking for entry point",
233          || middle::entry::find_entry_point(sess, crate, ast_map));
234
235     let freevars = time(time_passes, ~"freevar finding", ||
236                         freevars::annotate_freevars(def_map, crate));
237
238     let region_map = time(time_passes, ~"region resolution", ||
239                           middle::region::resolve_crate(sess, def_map, crate));
240
241     let rp_set = time(time_passes, ~"region parameterization inference", ||
242                       middle::region::determine_rp_in_crate(sess, ast_map, def_map, crate));
243
244     let ty_cx = ty::mk_ctxt(sess, def_map, ast_map, freevars,
245                             region_map, rp_set, lang_items);
246
247     // passes are timed inside typeck
248     let (method_map, vtable_map) = typeck::check_crate(
249         ty_cx, trait_map, crate);
250
251     // These next two const passes can probably be merged
252     time(time_passes, ~"const marking", ||
253          middle::const_eval::process_crate(crate, ty_cx));
254
255     time(time_passes, ~"const checking", ||
256          middle::check_const::check_crate(sess, crate, ast_map, def_map,
257                                           method_map, ty_cx));
258
259     time(time_passes, ~"privacy checking", ||
260          middle::privacy::check_crate(ty_cx, &method_map, crate));
261
262     time(time_passes, ~"effect checking", ||
263          middle::effect::check_crate(ty_cx, method_map, crate));
264
265     time(time_passes, ~"loop checking", ||
266          middle::check_loop::check_crate(ty_cx, crate));
267
268     time(time_passes, ~"stack checking", ||
269          middle::stack_check::stack_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.os == session::os_android &&
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     // We need nested scopes here, because the intermediate results can keep
412     // large chunks of memory alive and we want to free them as soon as
413     // possible to keep the peak memory usage low
414     let (outputs, trans) = {
415         let expanded_crate = {
416             let crate = phase_1_parse_input(sess, cfg.clone(), input);
417             if stop_after_phase_1(sess) { return; }
418             phase_2_configure_and_expand(sess, cfg, crate)
419         };
420         let analysis = phase_3_run_analysis_passes(sess, expanded_crate);
421         if stop_after_phase_3(sess) { return; }
422         let outputs = build_output_filenames(input, outdir, output, [], sess);
423         let trans = phase_4_translate_to_llvm(sess, expanded_crate,
424                                               &analysis, outputs);
425         (outputs, trans)
426     };
427     phase_5_run_llvm_passes(sess, &trans, outputs);
428     if stop_after_phase_5(sess) { return; }
429     phase_6_link_output(sess, &trans, outputs);
430 }
431
432 pub fn pretty_print_input(sess: Session, cfg: ast::CrateConfig, input: &input,
433                           ppm: pp_mode) {
434
435     fn ann_paren_for_expr(node: pprust::ann_node) {
436         match node {
437           pprust::node_expr(s, _) => pprust::popen(s),
438           _ => ()
439         }
440     }
441     fn ann_typed_post(tcx: ty::ctxt, node: pprust::ann_node) {
442         match node {
443           pprust::node_expr(s, expr) => {
444             pp::space(s.s);
445             pp::word(s.s, "as");
446             pp::space(s.s);
447             pp::word(s.s, ppaux::ty_to_str(tcx, ty::expr_ty(tcx, expr)));
448             pprust::pclose(s);
449           }
450           _ => ()
451         }
452     }
453     fn ann_identified_post(node: pprust::ann_node) {
454         match node {
455           pprust::node_item(s, item) => {
456             pp::space(s.s);
457             pprust::synth_comment(s, int::to_str(item.id));
458           }
459           pprust::node_block(s, ref blk) => {
460             pp::space(s.s);
461             pprust::synth_comment(
462                 s, ~"block " + int::to_str(blk.id));
463           }
464           pprust::node_expr(s, expr) => {
465             pp::space(s.s);
466             pprust::synth_comment(s, int::to_str(expr.id));
467             pprust::pclose(s);
468           }
469           pprust::node_pat(s, pat) => {
470             pp::space(s.s);
471             pprust::synth_comment(s, ~"pat " + int::to_str(pat.id));
472           }
473         }
474     }
475
476     let crate = phase_1_parse_input(sess, cfg.clone(), input);
477
478     let (crate, is_expanded) = match ppm {
479         ppm_expanded | ppm_expanded_identified | ppm_typed => {
480             (phase_2_configure_and_expand(sess, cfg, crate), true)
481         }
482         _ => (crate, false)
483     };
484
485     let annotation = match ppm {
486         ppm_identified | ppm_expanded_identified => {
487             pprust::pp_ann {
488                 pre: ann_paren_for_expr,
489                 post: ann_identified_post
490             }
491         }
492         ppm_typed => {
493             let analysis = phase_3_run_analysis_passes(sess, crate);
494             pprust::pp_ann {
495                 pre: ann_paren_for_expr,
496                 post: |a| ann_typed_post(analysis.ty_cx, a)
497             }
498         }
499         _ => pprust::no_ann()
500     };
501
502     let src = sess.codemap.get_filemap(source_name(input)).src;
503     do io::with_str_reader(src) |rdr| {
504         pprust::print_crate(sess.codemap, token::get_ident_interner(),
505                             sess.span_diagnostic, crate,
506                             source_name(input),
507                             rdr, io::stdout(),
508                             annotation, is_expanded);
509     }
510 }
511
512 pub fn get_os(triple: &str) -> Option<session::os> {
513     for &(name, os) in os_names.iter() {
514         if triple.contains(name) { return Some(os) }
515     }
516     None
517 }
518 static os_names : &'static [(&'static str, session::os)] = &'static [
519     ("mingw32", session::os_win32),
520     ("win32",   session::os_win32),
521     ("darwin",  session::os_macos),
522     ("android", session::os_android),
523     ("linux",   session::os_linux),
524     ("freebsd", session::os_freebsd)];
525
526 pub fn get_arch(triple: &str) -> Option<abi::Architecture> {
527     for &(arch, abi) in architecture_abis.iter() {
528         if triple.contains(arch) { return Some(abi) }
529     }
530     None
531 }
532 static architecture_abis : &'static [(&'static str, abi::Architecture)] = &'static [
533     ("i386",   abi::X86),
534     ("i486",   abi::X86),
535     ("i586",   abi::X86),
536     ("i686",   abi::X86),
537     ("i786",   abi::X86),
538
539     ("x86_64", abi::X86_64),
540
541     ("arm",    abi::Arm),
542     ("xscale", abi::Arm),
543
544     ("mips",   abi::Mips)];
545
546 pub fn build_target_config(sopts: @session::options,
547                            demitter: diagnostic::Emitter)
548                         -> @session::config {
549     let os = match get_os(sopts.target_triple) {
550       Some(os) => os,
551       None => early_error(demitter, ~"unknown operating system")
552     };
553     let arch = match get_arch(sopts.target_triple) {
554       Some(arch) => arch,
555       None => early_error(demitter,
556                           ~"unknown architecture: " + sopts.target_triple)
557     };
558     let (int_type, uint_type, float_type) = match arch {
559       abi::X86 => (ast::ty_i32, ast::ty_u32, ast::ty_f64),
560       abi::X86_64 => (ast::ty_i64, ast::ty_u64, ast::ty_f64),
561       abi::Arm => (ast::ty_i32, ast::ty_u32, ast::ty_f64),
562       abi::Mips => (ast::ty_i32, ast::ty_u32, ast::ty_f64)
563     };
564     let target_strs = match arch {
565       abi::X86 => x86::get_target_strs(os),
566       abi::X86_64 => x86_64::get_target_strs(os),
567       abi::Arm => arm::get_target_strs(os),
568       abi::Mips => mips::get_target_strs(os)
569     };
570     let target_cfg = @session::config {
571         os: os,
572         arch: arch,
573         target_strs: target_strs,
574         int_type: int_type,
575         uint_type: uint_type,
576         float_type: float_type
577     };
578     return target_cfg;
579 }
580
581 pub fn host_triple() -> ~str {
582     // Get the host triple out of the build environment. This ensures that our
583     // idea of the host triple is the same as for the set of libraries we've
584     // actually built.  We can't just take LLVM's host triple because they
585     // normalize all ix86 architectures to i386.
586     //
587     // Instead of grabbing the host triple (for the current host), we grab (at
588     // compile time) the target triple that this rustc is built with and
589     // calling that (at runtime) the host triple.
590     (env!("CFG_COMPILER_TRIPLE")).to_owned()
591 }
592
593 pub fn build_session_options(binary: @str,
594                              matches: &getopts::Matches,
595                              demitter: diagnostic::Emitter)
596                           -> @session::options {
597     let crate_type = if opt_present(matches, "lib") {
598         session::lib_crate
599     } else if opt_present(matches, "bin") {
600         session::bin_crate
601     } else {
602         session::unknown_crate
603     };
604     let parse_only = opt_present(matches, "parse-only");
605     let no_trans = opt_present(matches, "no-trans");
606
607     let lint_levels = [lint::allow, lint::warn,
608                        lint::deny, lint::forbid];
609     let mut lint_opts = ~[];
610     let lint_dict = lint::get_lint_dict();
611     for level in lint_levels.iter() {
612         let level_name = lint::level_to_str(*level);
613
614         // FIXME: #4318 Instead of to_ascii and to_str_ascii, could use
615         // to_ascii_move and to_str_move to not do a unnecessary copy.
616         let level_short = level_name.slice_chars(0, 1);
617         let level_short = level_short.to_ascii().to_upper().to_str_ascii();
618         let flags = vec::append(getopts::opt_strs(matches, level_short),
619                                 getopts::opt_strs(matches, level_name));
620         for lint_name in flags.iter() {
621             let lint_name = lint_name.replace("-", "_");
622             match lint_dict.find_equiv(&lint_name) {
623               None => {
624                 early_error(demitter, fmt!("unknown %s flag: %s",
625                                            level_name, lint_name));
626               }
627               Some(lint) => {
628                 lint_opts.push((lint.lint, *level));
629               }
630             }
631         }
632     }
633
634     let mut debugging_opts = 0u;
635     let debug_flags = getopts::opt_strs(matches, "Z");
636     let debug_map = session::debugging_opts_map();
637     for debug_flag in debug_flags.iter() {
638         let mut this_bit = 0u;
639         for tuple in debug_map.iter() {
640             let (name, bit) = match *tuple { (ref a, _, b) => (a, b) };
641             if name == debug_flag { this_bit = bit; break; }
642         }
643         if this_bit == 0u {
644             early_error(demitter, fmt!("unknown debug flag: %s", *debug_flag))
645         }
646         debugging_opts |= this_bit;
647     }
648
649     if debugging_opts & session::debug_llvm != 0 {
650         set_llvm_debug();
651
652         fn set_llvm_debug() {
653             #[fixed_stack_segment]; #[inline(never)];
654             unsafe { llvm::LLVMSetDebug(1); }
655         }
656     }
657
658     let output_type =
659         if parse_only || no_trans {
660             link::output_type_none
661         } else if opt_present(matches, "S") &&
662                   opt_present(matches, "emit-llvm") {
663             link::output_type_llvm_assembly
664         } else if opt_present(matches, "S") {
665             link::output_type_assembly
666         } else if opt_present(matches, "c") {
667             link::output_type_object
668         } else if opt_present(matches, "emit-llvm") {
669             link::output_type_bitcode
670         } else { link::output_type_exe };
671     let sysroot_opt = getopts::opt_maybe_str(matches, "sysroot").map_move(|m| @Path(m));
672     let target = getopts::opt_maybe_str(matches, "target").unwrap_or_default(host_triple());
673     let target_cpu = getopts::opt_maybe_str(matches, "target-cpu").unwrap_or_default(~"generic");
674     let target_feature = getopts::opt_maybe_str(matches, "target-feature").unwrap_or_default(~"");
675     let save_temps = getopts::opt_present(matches, "save-temps");
676     let opt_level = {
677         if (debugging_opts & session::no_opt) != 0 {
678             No
679         } else if opt_present(matches, "O") {
680             if opt_present(matches, "opt-level") {
681                 early_error(demitter, ~"-O and --opt-level both provided");
682             }
683             Default
684         } else if opt_present(matches, "opt-level") {
685             match getopts::opt_str(matches, "opt-level") {
686               ~"0" => No,
687               ~"1" => Less,
688               ~"2" => Default,
689               ~"3" => Aggressive,
690               _ => {
691                 early_error(demitter, ~"optimization level needs to be between 0-3")
692               }
693             }
694         } else { No }
695     };
696     let gc = debugging_opts & session::gc != 0;
697     let jit = debugging_opts & session::jit != 0;
698     let extra_debuginfo = debugging_opts & session::extra_debug_info != 0;
699     let debuginfo = debugging_opts & session::debug_info != 0 ||
700         extra_debuginfo;
701
702     // If debugging info is generated, do not collapse monomorphized function instances.
703     // Functions with equivalent llvm code still need separate debugging descriptions because names
704     // might differ.
705     if debuginfo {
706         debugging_opts |= session::no_monomorphic_collapse;
707     }
708
709     let statik = debugging_opts & session::statik != 0;
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(' ').map(|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 == ',').map(|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_cpu: target_cpu,
749         target_feature: target_feature,
750         cfg: cfg,
751         binary: binary,
752         test: test,
753         parse_only: parse_only,
754         no_trans: no_trans,
755         debugging_opts: debugging_opts,
756         android_cross_path: android_cross_path
757     };
758     return sopts;
759 }
760
761 pub fn build_session(sopts: @session::options,
762                      demitter: diagnostic::Emitter) -> Session {
763     let codemap = @codemap::CodeMap::new();
764     let diagnostic_handler =
765         diagnostic::mk_handler(Some(demitter));
766     let span_diagnostic_handler =
767         diagnostic::mk_span_handler(diagnostic_handler, codemap);
768     build_session_(sopts, codemap, demitter, span_diagnostic_handler)
769 }
770
771 pub fn build_session_(sopts: @session::options,
772                       cm: @codemap::CodeMap,
773                       demitter: diagnostic::Emitter,
774                       span_diagnostic_handler: @mut diagnostic::span_handler)
775                    -> Session {
776     let target_cfg = build_target_config(sopts, demitter);
777     let p_s = parse::new_parse_sess_special_handler(span_diagnostic_handler,
778                                                     cm);
779     let cstore = @mut cstore::mk_cstore(token::get_ident_interner());
780     let filesearch = filesearch::mk_filesearch(
781         &sopts.maybe_sysroot,
782         sopts.target_triple,
783         sopts.addl_lib_search_paths);
784     @Session_ {
785         targ_cfg: target_cfg,
786         opts: sopts,
787         cstore: cstore,
788         parse_sess: p_s,
789         codemap: cm,
790         // For a library crate, this is always none
791         entry_fn: @mut None,
792         entry_type: @mut None,
793         span_diagnostic: span_diagnostic_handler,
794         filesearch: filesearch,
795         building_library: @mut false,
796         working_dir: os::getcwd(),
797         lints: @mut HashMap::new(),
798     }
799 }
800
801 pub fn parse_pretty(sess: Session, name: &str) -> pp_mode {
802     match name {
803       &"normal" => ppm_normal,
804       &"expanded" => ppm_expanded,
805       &"typed" => ppm_typed,
806       &"expanded,identified" => ppm_expanded_identified,
807       &"identified" => ppm_identified,
808       _ => {
809         sess.fatal("argument to `pretty` must be one of `normal`, \
810                     `expanded`, `typed`, `identified`, \
811                     or `expanded,identified`");
812       }
813     }
814 }
815
816 // rustc command line options
817 pub fn optgroups() -> ~[getopts::groups::OptGroup] {
818  ~[
819   optflag("",  "bin", "Compile an executable crate (default)"),
820   optflag("c", "",    "Compile and assemble, but do not link"),
821   optmulti("", "cfg", "Configure the compilation
822                           environment", "SPEC"),
823   optflag("",  "emit-llvm",
824                         "Produce an LLVM assembly file if used with -S option;
825                          produce an LLVM bitcode file otherwise"),
826   optflag("h", "help","Display this message"),
827   optmulti("L", "",   "Add a directory to the library search path",
828                               "PATH"),
829   optflag("",  "lib", "Compile a library crate"),
830   optopt("", "linker", "Program to use for linking instead of the default.", "LINKER"),
831   optmulti("",  "link-args", "FLAGS is a space-separated list of flags
832                             passed to the linker", "FLAGS"),
833   optflag("",  "ls",  "List the symbols defined by a library crate"),
834   optflag("", "no-trans",
835                         "Run all passes except translation; no output"),
836   optflag("O", "",    "Equivalent to --opt-level=2"),
837   optopt("o", "",     "Write output to <filename>", "FILENAME"),
838   optopt("", "opt-level",
839                         "Optimize with possible levels 0-3", "LEVEL"),
840   optopt("", "passes", "Comma or space separated list of pass names to use. \
841                         Overrides the default passes for optimization levels,\n\
842                         a value of \"list\" will list the available passes.", "NAMES"),
843   optopt( "",  "out-dir",
844                         "Write output to compiler-chosen filename
845                           in <dir>", "DIR"),
846   optflag("", "parse-only",
847                         "Parse only; do not compile, assemble, or link"),
848   optflagopt("", "pretty",
849                         "Pretty-print the input instead of compiling;
850                           valid types are: normal (un-annotated source),
851                           expanded (crates expanded),
852                           typed (crates expanded, with type annotations),
853                           or identified (fully parenthesized,
854                           AST nodes and blocks with IDs)", "TYPE"),
855   optflag("S", "",    "Compile only; do not assemble or link"),
856   optflag("", "save-temps",
857                         "Write intermediate files (.bc, .opt.bc, .o)
858                           in addition to normal output"),
859   optopt("", "sysroot",
860                         "Override the system root", "PATH"),
861   optflag("", "test", "Build a test harness"),
862   optopt("", "target",
863                         "Target triple cpu-manufacturer-kernel[-os]
864                           to compile for (see chapter 3.4 of http://www.sourceware.org/autobook/
865                           for details)", "TRIPLE"),
866   optopt("", "target-cpu",
867                         "Select target processor (llc -mcpu=help
868                           for details)", "CPU"),
869   optopt("", "target-feature",
870                         "Target specific attributes (llc -mattr=help
871                           for details)", "FEATURE"),
872   optopt("", "android-cross-path",
873          "The path to the Android NDK", "PATH"),
874   optflagopt("W", "warn",
875                         "Set lint warnings", "OPT"),
876   optmulti("A", "allow",
877                         "Set lint allowed", "OPT"),
878   optmulti("D", "deny",
879                         "Set lint denied", "OPT"),
880   optmulti("F", "forbid",
881                         "Set lint forbidden", "OPT"),
882   optmulti("Z", "",   "Set internal debugging options", "FLAG"),
883   optflag( "v", "version",
884                         "Print version info and exit"),
885  ]
886 }
887
888 pub struct OutputFilenames {
889     out_filename: Path,
890     obj_filename: Path
891 }
892
893 pub fn build_output_filenames(input: &input,
894                               odir: &Option<Path>,
895                               ofile: &Option<Path>,
896                               attrs: &[ast::Attribute],
897                               sess: Session)
898                            -> ~OutputFilenames {
899     let obj_path;
900     let out_path;
901     let sopts = sess.opts;
902     let stop_after_codegen =
903         sopts.output_type != link::output_type_exe ||
904             sopts.is_static && *sess.building_library;
905
906     let obj_suffix =
907         match sopts.output_type {
908           link::output_type_none => ~"none",
909           link::output_type_bitcode => ~"bc",
910           link::output_type_assembly => ~"s",
911           link::output_type_llvm_assembly => ~"ll",
912           // Object and exe output both use the '.o' extension here
913           link::output_type_object | link::output_type_exe => ~"o"
914         };
915
916     match *ofile {
917       None => {
918           // "-" as input file will cause the parser to read from stdin so we
919           // have to make up a name
920           // We want to toss everything after the final '.'
921           let dirpath = match *odir {
922               Some(ref d) => (*d).clone(),
923               None => match *input {
924                   str_input(_) => os::getcwd(),
925                   file_input(ref ifile) => (*ifile).dir_path()
926               }
927           };
928
929           let mut stem = match *input {
930               file_input(ref ifile) => (*ifile).filestem().unwrap().to_managed(),
931               str_input(_) => @"rust_out"
932           };
933
934           // If a linkage name meta is present, we use it as the link name
935           let linkage_metas = attr::find_linkage_metas(attrs);
936           if !linkage_metas.is_empty() {
937               // But if a linkage meta is present, that overrides
938               let maybe_name = linkage_metas.iter().find(|m| "name" == m.name());
939               match maybe_name.chain(|m| m.value_str()) {
940                   Some(s) => stem = s,
941                   _ => ()
942               }
943               // If the name is missing, we just default to the filename
944               // version
945           }
946
947           if *sess.building_library {
948               out_path = dirpath.push(os::dll_filename(stem));
949               obj_path = dirpath.push(stem).with_filetype(obj_suffix);
950           } else {
951               out_path = dirpath.push(stem);
952               obj_path = dirpath.push(stem).with_filetype(obj_suffix);
953           }
954       }
955
956       Some(ref out_file) => {
957         out_path = (*out_file).clone();
958         obj_path = if stop_after_codegen {
959             (*out_file).clone()
960         } else {
961             (*out_file).with_filetype(obj_suffix)
962         };
963
964         if *sess.building_library {
965             sess.warn("ignoring specified output filename for library.");
966         }
967
968         if *odir != None {
969             sess.warn("ignoring --out-dir flag due to -o flag.");
970         }
971       }
972     }
973
974     ~OutputFilenames {
975         out_filename: out_path,
976         obj_filename: obj_path
977     }
978 }
979
980 pub fn early_error(emitter: diagnostic::Emitter, msg: ~str) -> ! {
981     emitter(None, msg, diagnostic::fatal);
982     fail!();
983 }
984
985 pub fn list_metadata(sess: Session, path: &Path, out: @io::Writer) {
986     metadata::loader::list_file_metadata(
987         token::get_ident_interner(),
988         session::sess_os_to_meta_os(sess.targ_cfg.os), path, out);
989 }
990
991 #[cfg(test)]
992 mod test {
993
994     use driver::driver::{build_configuration, build_session};
995     use driver::driver::{build_session_options, optgroups};
996
997     use extra::getopts::groups::getopts;
998     use extra::getopts;
999     use syntax::attr;
1000     use syntax::diagnostic;
1001
1002     // When the user supplies --test we should implicitly supply --cfg test
1003     #[test]
1004     fn test_switch_implies_cfg_test() {
1005         let matches =
1006             &match getopts([~"--test"], optgroups()) {
1007               Ok(m) => m,
1008               Err(f) => fail!("test_switch_implies_cfg_test: %s", getopts::fail_str(f))
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);
1014         assert!((attr::contains_name(cfg, "test")));
1015     }
1016
1017     // When the user supplies --test and --cfg test, don't implicitly add
1018     // another --cfg test
1019     #[test]
1020     fn test_switch_implies_cfg_test_unless_cfg_test() {
1021         let matches =
1022             &match getopts([~"--test", ~"--cfg=test"], optgroups()) {
1023               Ok(m) => m,
1024               Err(f) => {
1025                 fail!("test_switch_implies_cfg_test_unless_cfg_test: %s", getopts::fail_str(f));
1026               }
1027             };
1028         let sessopts = build_session_options(
1029             @"rustc", matches, diagnostic::emit);
1030         let sess = build_session(sessopts, diagnostic::emit);
1031         let cfg = build_configuration(sess);
1032         let mut test_items = cfg.iter().filter(|m| "test" == m.name());
1033         assert!(test_items.next().is_some());
1034         assert!(test_items.next().is_none());
1035     }
1036 }