]> git.lizzy.rs Git - rust.git/blob - src/librustc/driver/driver.rs
Merge remote-tracking branch 'brson/codemap'
[rust.git] / src / librustc / driver / driver.rs
1 // -*- rust -*-
2 use metadata::{creader, cstore, filesearch};
3 use session::{Session, Session_, OptLevel, No, Less, Default, Aggressive};
4 use syntax::parse;
5 use syntax::{ast, codemap};
6 use syntax::attr;
7 use middle::{trans, freevars, kind, ty, typeck, lint};
8 use syntax::print::{pp, pprust};
9 use util::ppaux;
10 use back::link;
11 use result::{Ok, Err};
12 use std::getopts;
13 use std::getopts::{opt_present};
14 use std::getopts::groups;
15 use std::getopts::groups::{optopt, optmulti, optflag, optflagopt, getopts};
16 use io::WriterUtil;
17 use back::{x86, x86_64};
18 use std::map::HashMap;
19 use lib::llvm::llvm;
20
21 enum pp_mode {ppm_normal, ppm_expanded, ppm_typed, ppm_identified,
22               ppm_expanded_identified }
23
24 /**
25  * The name used for source code that doesn't originate in a file
26  * (e.g. source from stdin or a string)
27  */
28 fn anon_src() -> ~str { ~"<anon>" }
29
30 fn source_name(input: input) -> ~str {
31     match input {
32       file_input(ifile) => ifile.to_str(),
33       str_input(_) => anon_src()
34     }
35 }
36
37 fn default_configuration(sess: Session, argv0: ~str, input: input) ->
38    ast::crate_cfg {
39     let libc = match sess.targ_cfg.os {
40       session::os_win32 => ~"msvcrt.dll",
41       session::os_macos => ~"libc.dylib",
42       session::os_linux => ~"libc.so.6",
43       session::os_freebsd => ~"libc.so.7"
44       // _ { "libc.so" }
45     };
46
47     let mk = attr::mk_name_value_item_str;
48
49     let (arch,wordsz) = match sess.targ_cfg.arch {
50       session::arch_x86 => (~"x86",~"32"),
51       session::arch_x86_64 => (~"x86_64",~"64"),
52       session::arch_arm => (~"arm",~"32")
53     };
54
55     return ~[ // Target bindings.
56          attr::mk_word_item(os::family()),
57          mk(~"target_os", os::sysname()),
58          mk(~"target_family", os::family()),
59          mk(~"target_arch", arch),
60          mk(~"target_word_size", wordsz),
61          mk(~"target_libc", libc),
62          // Build bindings.
63          mk(~"build_compiler", argv0),
64          mk(~"build_input", source_name(input))];
65 }
66
67 fn append_configuration(cfg: ast::crate_cfg, name: ~str) -> ast::crate_cfg {
68     if attr::contains_name(cfg, name) {
69         return cfg;
70     } else {
71         return vec::append_one(cfg, attr::mk_word_item(name));
72     }
73 }
74
75 fn build_configuration(sess: Session, argv0: ~str, input: input) ->
76    ast::crate_cfg {
77     // Combine the configuration requested by the session (command line) with
78     // some default and generated configuration items
79     let default_cfg = default_configuration(sess, argv0, input);
80     let user_cfg = sess.opts.cfg;
81     // If the user wants a test runner, then add the test cfg
82     let user_cfg = append_configuration(
83         user_cfg,
84         if sess.opts.test { ~"test" } else { ~"notest" });
85     // If the user requested GC, then add the GC cfg
86     let user_cfg = append_configuration(
87         user_cfg,
88         if sess.opts.gc { ~"gc" } else { ~"nogc" });
89     return vec::append(user_cfg, default_cfg);
90 }
91
92 // Convert strings provided as --cfg [cfgspec] into a crate_cfg
93 fn parse_cfgspecs(cfgspecs: ~[~str]) -> ast::crate_cfg {
94     // FIXME (#2399): It would be nice to use the parser to parse all
95     // varieties of meta_item here. At the moment we just support the
96     // meta_word variant.
97     let mut words = ~[];
98     for cfgspecs.each |s| {
99         words.push(attr::mk_word_item(*s));
100     }
101     return words;
102 }
103
104 enum input {
105     /// Load source from file
106     file_input(Path),
107     /// The string is the source
108     str_input(~str)
109 }
110
111 fn parse_input(sess: Session, cfg: ast::crate_cfg, input: input)
112     -> @ast::crate {
113     match input {
114       file_input(file) => {
115         parse::parse_crate_from_file(&file, cfg, sess.parse_sess)
116       }
117       str_input(src) => {
118         // FIXME (#2319): Don't really want to box the source string
119         parse::parse_crate_from_source_str(
120             anon_src(), @src, cfg, sess.parse_sess)
121       }
122     }
123 }
124
125 fn time<T>(do_it: bool, what: ~str, thunk: fn() -> T) -> T {
126     if !do_it { return thunk(); }
127     let start = std::time::precise_time_s();
128     let rv = thunk();
129     let end = std::time::precise_time_s();
130     io::stdout().write_str(fmt!("time: %3.3f s\t%s\n",
131                                 end - start, what));
132     move rv
133 }
134
135 enum compile_upto {
136     cu_parse,
137     cu_expand,
138     cu_typeck,
139     cu_no_trans,
140     cu_everything,
141 }
142
143 impl compile_upto : cmp::Eq {
144     pure fn eq(other: &compile_upto) -> bool {
145         (self as uint) == ((*other) as uint)
146     }
147     pure fn ne(other: &compile_upto) -> bool { !self.eq(other) }
148 }
149
150 fn compile_upto(sess: Session, cfg: ast::crate_cfg,
151                 input: input, upto: compile_upto,
152                 outputs: Option<output_filenames>)
153     -> {crate: @ast::crate, tcx: Option<ty::ctxt>} {
154     let time_passes = sess.time_passes();
155     let mut crate = time(time_passes, ~"parsing",
156                          ||parse_input(sess, cfg, input) );
157     if upto == cu_parse { return {crate: crate, tcx: None}; }
158
159     sess.building_library = session::building_library(
160         sess.opts.crate_type, crate, sess.opts.test);
161
162     crate = time(time_passes, ~"configuration", ||
163         front::config::strip_unconfigured_items(crate));
164
165     crate = time(time_passes, ~"maybe building test harness", ||
166         front::test::modify_for_testing(sess, crate));
167
168     crate = time(time_passes, ~"expansion", ||
169         syntax::ext::expand::expand_crate(sess.parse_sess, cfg,
170                                           crate));
171
172     if upto == cu_expand { return {crate: crate, tcx: None}; }
173
174     crate = time(time_passes, ~"intrinsic injection", ||
175         front::intrinsic_inject::inject_intrinsic(sess, crate));
176
177     crate = time(time_passes, ~"core injection", ||
178         front::core_inject::maybe_inject_libcore_ref(sess, crate));
179
180     time(time_passes, ~"building lint settings table", ||
181         lint::build_settings_crate(sess, crate));
182
183     let ast_map = time(time_passes, ~"ast indexing", ||
184             syntax::ast_map::map_crate(sess.diagnostic(), *crate));
185
186     time(time_passes, ~"external crate/lib resolution", ||
187         creader::read_crates(sess.diagnostic(), *crate, sess.cstore,
188                              sess.filesearch,
189                              session::sess_os_to_meta_os(sess.targ_cfg.os),
190                              sess.opts.static,
191                              sess.parse_sess.interner));
192
193     let lang_items = time(time_passes, ~"language item collection", ||
194          middle::lang_items::collect_language_items(crate, sess));
195
196     let { def_map: def_map,
197           exp_map2: exp_map2,
198           trait_map: trait_map } =
199         time(time_passes, ~"resolution", ||
200              middle::resolve::resolve_crate(sess, lang_items, crate));
201
202     let freevars = time(time_passes, ~"freevar finding", ||
203         freevars::annotate_freevars(def_map, crate));
204
205     let region_map = time(time_passes, ~"region resolution", ||
206         middle::region::resolve_crate(sess, def_map, crate));
207
208     let rp_set = time(time_passes, ~"region parameterization inference", ||
209         middle::region::determine_rp_in_crate(sess, ast_map, def_map, crate));
210
211
212     let outputs = outputs.get();
213
214     let (llmod, link_meta) = {
215
216         let ty_cx = ty::mk_ctxt(sess, def_map, ast_map, freevars,
217                                 region_map, rp_set, move lang_items, crate);
218
219         let (method_map, vtable_map) =
220             time(time_passes, ~"typechecking", ||
221                  typeck::check_crate(ty_cx,
222                                      trait_map,
223                                      crate));
224
225         // These next two const passes can probably be merged
226         time(time_passes, ~"const marking", ||
227              middle::const_eval::process_crate(crate, def_map, ty_cx));
228
229         time(time_passes, ~"const checking", ||
230              middle::check_const::check_crate(sess, crate, ast_map, def_map,
231                                               method_map, ty_cx));
232
233         if upto == cu_typeck { return {crate: crate, tcx: Some(ty_cx)}; }
234
235         time(time_passes, ~"privacy checking", ||
236              middle::privacy::check_crate(ty_cx, &method_map, crate));
237
238         time(time_passes, ~"loop checking", ||
239              middle::check_loop::check_crate(ty_cx, crate));
240
241         time(time_passes, ~"alt checking", ||
242              middle::check_alt::check_crate(ty_cx, crate));
243
244         let last_use_map =
245             time(time_passes, ~"liveness checking", ||
246                  middle::liveness::check_crate(ty_cx, method_map, crate));
247
248         let (root_map, mutbl_map) =
249             time(time_passes, ~"borrow checking", ||
250                  middle::borrowck::check_crate(ty_cx, method_map,
251                                                last_use_map, crate));
252
253         time(time_passes, ~"kind checking", ||
254              kind::check_crate(ty_cx, method_map, last_use_map, crate));
255
256         time(time_passes, ~"lint checking", ||
257              lint::check_crate(ty_cx, crate));
258
259         if upto == cu_no_trans { return {crate: crate, tcx: Some(ty_cx)}; }
260
261         let maps = {mutbl_map: mutbl_map,
262                     root_map: root_map,
263                     last_use_map: last_use_map,
264                     method_map: method_map,
265                     vtable_map: vtable_map};
266
267         time(time_passes, ~"translation", ||
268              trans::base::trans_crate(sess, crate, ty_cx,
269                                       &outputs.obj_filename,
270                                       exp_map2, maps))
271
272     };
273
274
275     time(time_passes, ~"LLVM passes", ||
276         link::write::run_passes(sess, llmod,
277                                 &outputs.obj_filename));
278
279     let stop_after_codegen =
280         sess.opts.output_type != link::output_type_exe ||
281         (sess.opts.static && sess.building_library)    ||
282         sess.opts.jit;
283
284     if stop_after_codegen { return {crate: crate, tcx: None}; }
285
286     time(time_passes, ~"linking", ||
287          link::link_binary(sess,
288                            &outputs.obj_filename,
289                            &outputs.out_filename, link_meta));
290
291     return {crate: crate, tcx: None};
292 }
293
294 fn compile_input(sess: Session, cfg: ast::crate_cfg, input: input,
295                  outdir: &Option<Path>, output: &Option<Path>) {
296
297     let upto = if sess.opts.parse_only { cu_parse }
298                else if sess.opts.no_trans { cu_no_trans }
299                else { cu_everything };
300     let outputs = build_output_filenames(input, outdir, output, sess);
301     compile_upto(sess, cfg, input, upto, Some(outputs));
302 }
303
304 fn pretty_print_input(sess: Session, cfg: ast::crate_cfg, input: input,
305                       ppm: pp_mode) {
306     fn ann_paren_for_expr(node: pprust::ann_node) {
307         match node {
308           pprust::node_expr(s, _) => pprust::popen(s),
309           _ => ()
310         }
311     }
312     fn ann_typed_post(tcx: ty::ctxt, node: pprust::ann_node) {
313         match node {
314           pprust::node_expr(s, expr) => {
315             pp::space(s.s);
316             pp::word(s.s, ~"as");
317             pp::space(s.s);
318             pp::word(s.s, ppaux::ty_to_str(tcx, ty::expr_ty(tcx, expr)));
319             pprust::pclose(s);
320           }
321           _ => ()
322         }
323     }
324     fn ann_identified_post(node: pprust::ann_node) {
325         match node {
326           pprust::node_item(s, item) => {
327             pp::space(s.s);
328             pprust::synth_comment(s, int::to_str(item.id, 10u));
329           }
330           pprust::node_block(s, blk) => {
331             pp::space(s.s);
332             pprust::synth_comment(s,
333                                   ~"block " + int::to_str(blk.node.id, 10u));
334           }
335           pprust::node_expr(s, expr) => {
336             pp::space(s.s);
337             pprust::synth_comment(s, int::to_str(expr.id, 10u));
338             pprust::pclose(s);
339           }
340           pprust::node_pat(s, pat) => {
341             pp::space(s.s);
342             pprust::synth_comment(s, ~"pat " + int::to_str(pat.id, 10u));
343           }
344         }
345     }
346
347     // Because the pretty printer needs to make a pass over the source
348     // to collect comments and literals, and we need to support reading
349     // from stdin, we're going to just suck the source into a string
350     // so both the parser and pretty-printer can use it.
351     let upto = match ppm {
352       ppm_expanded | ppm_expanded_identified => cu_expand,
353       ppm_typed => cu_typeck,
354       _ => cu_parse
355     };
356     let {crate, tcx} = compile_upto(sess, cfg, input, upto, None);
357
358     let ann = match ppm {
359       ppm_typed => {
360         {pre: ann_paren_for_expr,
361          post: |a| ann_typed_post(tcx.get(), a) }
362       }
363       ppm_identified | ppm_expanded_identified => {
364         {pre: ann_paren_for_expr, post: ann_identified_post}
365       }
366       ppm_expanded | ppm_normal => pprust::no_ann()
367     };
368     let is_expanded = upto != cu_parse;
369     let src = sess.codemap.get_filemap(source_name(input)).src;
370     do io::with_str_reader(*src) |rdr| {
371         pprust::print_crate(sess.codemap, sess.parse_sess.interner,
372                             sess.span_diagnostic, crate,
373                             source_name(input),
374                             rdr, io::stdout(), ann, is_expanded);
375     }
376 }
377
378 fn get_os(triple: ~str) -> Option<session::os> {
379     if str::contains(triple, ~"win32") ||
380                str::contains(triple, ~"mingw32") {
381             Some(session::os_win32)
382         } else if str::contains(triple, ~"darwin") {
383             Some(session::os_macos)
384         } else if str::contains(triple, ~"linux") {
385             Some(session::os_linux)
386         } else if str::contains(triple, ~"freebsd") {
387             Some(session::os_freebsd)
388         } else { None }
389 }
390
391 fn get_arch(triple: ~str) -> Option<session::arch> {
392     if str::contains(triple, ~"i386") ||
393         str::contains(triple, ~"i486") ||
394                str::contains(triple, ~"i586") ||
395                str::contains(triple, ~"i686") ||
396                str::contains(triple, ~"i786") {
397             Some(session::arch_x86)
398         } else if str::contains(triple, ~"x86_64") {
399             Some(session::arch_x86_64)
400         } else if str::contains(triple, ~"arm") ||
401                       str::contains(triple, ~"xscale") {
402             Some(session::arch_arm)
403         } else { None }
404 }
405
406 fn build_target_config(sopts: @session::options,
407                        demitter: diagnostic::emitter) -> @session::config {
408     let os = match get_os(sopts.target_triple) {
409       Some(os) => os,
410       None => early_error(demitter, ~"unknown operating system")
411     };
412     let arch = match get_arch(sopts.target_triple) {
413       Some(arch) => arch,
414       None => early_error(demitter,
415                           ~"unknown architecture: " + sopts.target_triple)
416     };
417     let (int_type, uint_type, float_type) = match arch {
418       session::arch_x86 => (ast::ty_i32, ast::ty_u32, ast::ty_f64),
419       session::arch_x86_64 => (ast::ty_i64, ast::ty_u64, ast::ty_f64),
420       session::arch_arm => (ast::ty_i32, ast::ty_u32, ast::ty_f64)
421     };
422     let target_strs = match arch {
423       session::arch_x86 => x86::get_target_strs(os),
424       session::arch_x86_64 => x86_64::get_target_strs(os),
425       session::arch_arm => x86::get_target_strs(os)
426     };
427     let target_cfg: @session::config =
428         @{os: os, arch: arch, target_strs: target_strs, int_type: int_type,
429           uint_type: uint_type, float_type: float_type};
430     return target_cfg;
431 }
432
433 fn host_triple() -> ~str {
434     // Get the host triple out of the build environment. This ensures that our
435     // idea of the host triple is the same as for the set of libraries we've
436     // actually built.  We can't just take LLVM's host triple because they
437     // normalize all ix86 architectures to i386.
438
439     // FIXME (#2400): Instead of grabbing the host triple we really should
440     // be grabbing (at compile time) the target triple that this rustc is
441     // built with and calling that (at runtime) the host triple.
442     let ht = env!("CFG_HOST_TRIPLE");
443     return if ht != ~"" {
444             ht
445         } else {
446             fail ~"rustc built without CFG_HOST_TRIPLE"
447         };
448 }
449
450 fn build_session_options(binary: ~str,
451                          matches: getopts::Matches,
452                          demitter: diagnostic::emitter) -> @session::options {
453     let crate_type = if opt_present(matches, ~"lib") {
454         session::lib_crate
455     } else if opt_present(matches, ~"bin") {
456         session::bin_crate
457     } else {
458         session::unknown_crate
459     };
460     let static = opt_present(matches, ~"static");
461     let gc = opt_present(matches, ~"gc");
462
463     let parse_only = opt_present(matches, ~"parse-only");
464     let no_trans = opt_present(matches, ~"no-trans");
465
466     let lint_levels = [lint::allow, lint::warn,
467                        lint::deny, lint::forbid];
468     let mut lint_opts = ~[];
469     let lint_dict = lint::get_lint_dict();
470     for lint_levels.each |level| {
471         let level_name = lint::level_to_str(*level);
472         let level_short = level_name.substr(0,1).to_upper();
473         let flags = vec::append(getopts::opt_strs(matches, level_short),
474                                 getopts::opt_strs(matches, level_name));
475         for flags.each |lint_name| {
476             let lint_name = str::replace(*lint_name, ~"-", ~"_");
477             match lint_dict.find(lint_name) {
478               None => {
479                 early_error(demitter, fmt!("unknown %s flag: %s",
480                                            level_name, lint_name));
481               }
482               Some(lint) => {
483                 lint_opts.push((lint.lint, *level));
484               }
485             }
486         }
487     }
488
489     let mut debugging_opts = 0u;
490     let debug_flags = getopts::opt_strs(matches, ~"Z");
491     let debug_map = session::debugging_opts_map();
492     for debug_flags.each |debug_flag| {
493         let mut this_bit = 0u;
494         for debug_map.each |pair| {
495             let (name, _, bit) = *pair;
496             if name == *debug_flag { this_bit = bit; break; }
497         }
498         if this_bit == 0u {
499             early_error(demitter, fmt!("unknown debug flag: %s", *debug_flag))
500         }
501         debugging_opts |= this_bit;
502     }
503     if debugging_opts & session::debug_llvm != 0 {
504         llvm::LLVMSetDebug(1);
505     }
506
507     let jit = opt_present(matches, ~"jit");
508     let output_type =
509         if parse_only || no_trans {
510             link::output_type_none
511         } else if opt_present(matches, ~"S") &&
512                   opt_present(matches, ~"emit-llvm") {
513             link::output_type_llvm_assembly
514         } else if opt_present(matches, ~"S") {
515             link::output_type_assembly
516         } else if opt_present(matches, ~"c") {
517             link::output_type_object
518         } else if opt_present(matches, ~"emit-llvm") {
519             link::output_type_bitcode
520         } else { link::output_type_exe };
521     let extra_debuginfo = opt_present(matches, ~"xg");
522     let debuginfo = opt_present(matches, ~"g") || extra_debuginfo;
523     let sysroot_opt = getopts::opt_maybe_str(matches, ~"sysroot");
524     let sysroot_opt = sysroot_opt.map(|m| Path(*m));
525     let target_opt = getopts::opt_maybe_str(matches, ~"target");
526     let save_temps = getopts::opt_present(matches, ~"save-temps");
527     match output_type {
528       // unless we're emitting huamn-readable assembly, omit comments.
529       link::output_type_llvm_assembly | link::output_type_assembly => (),
530       _ => debugging_opts |= session::no_asm_comments
531     }
532     let opt_level = {
533         if (debugging_opts & session::no_opt) != 0 {
534             No
535         } else if opt_present(matches, ~"O") {
536             if opt_present(matches, ~"opt-level") {
537                 early_error(demitter, ~"-O and --opt-level both provided");
538             }
539             Default
540         } else if opt_present(matches, ~"opt-level") {
541             match getopts::opt_str(matches, ~"opt-level") {
542               ~"0" => No,
543               ~"1" => Less,
544               ~"2" => Default,
545               ~"3" => Aggressive,
546               _ => {
547                 early_error(demitter, ~"optimization level needs " +
548                             ~"to be between 0-3")
549               }
550             }
551         } else { No }
552     };
553     let target =
554         match target_opt {
555             None => host_triple(),
556             Some(s) => s
557         };
558
559     let addl_lib_search_paths =
560         getopts::opt_strs(matches, ~"L")
561         .map(|s| Path(*s));
562     let cfg = parse_cfgspecs(getopts::opt_strs(matches, ~"cfg"));
563     let test = opt_present(matches, ~"test");
564     let sopts: @session::options =
565         @{crate_type: crate_type,
566           static: static,
567           gc: gc,
568           optimize: opt_level,
569           debuginfo: debuginfo,
570           extra_debuginfo: extra_debuginfo,
571           lint_opts: lint_opts,
572           save_temps: save_temps,
573           jit: jit,
574           output_type: output_type,
575           addl_lib_search_paths: addl_lib_search_paths,
576           maybe_sysroot: sysroot_opt,
577           target_triple: target,
578           cfg: cfg,
579           binary: binary,
580           test: test,
581           parse_only: parse_only,
582           no_trans: no_trans,
583           debugging_opts: debugging_opts};
584     return sopts;
585 }
586
587 fn build_session(sopts: @session::options,
588                  demitter: diagnostic::emitter) -> Session {
589     let codemap = @codemap::CodeMap::new();
590     let diagnostic_handler =
591         diagnostic::mk_handler(Some(demitter));
592     let span_diagnostic_handler =
593         diagnostic::mk_span_handler(diagnostic_handler, codemap);
594     build_session_(sopts, codemap, demitter, span_diagnostic_handler)
595 }
596
597 fn build_session_(sopts: @session::options,
598                   cm: @codemap::CodeMap,
599                   demitter: diagnostic::emitter,
600                   span_diagnostic_handler: diagnostic::span_handler)
601                -> Session {
602     let target_cfg = build_target_config(sopts, demitter);
603     let p_s = parse::new_parse_sess_special_handler(span_diagnostic_handler,
604                                                     cm);
605     let cstore = cstore::mk_cstore(p_s.interner);
606     let filesearch = filesearch::mk_filesearch(
607         sopts.maybe_sysroot,
608         sopts.target_triple,
609         sopts.addl_lib_search_paths);
610     let lint_settings = lint::mk_lint_settings();
611     Session_(@{targ_cfg: target_cfg,
612                opts: sopts,
613                cstore: cstore,
614                parse_sess: p_s,
615                codemap: cm,
616                // For a library crate, this is always none
617                mut main_fn: None,
618                span_diagnostic: span_diagnostic_handler,
619                filesearch: filesearch,
620                mut building_library: false,
621                working_dir: os::getcwd(),
622                lint_settings: lint_settings})
623 }
624
625 fn parse_pretty(sess: Session, &&name: ~str) -> pp_mode {
626     match name {
627       ~"normal" => ppm_normal,
628       ~"expanded" => ppm_expanded,
629       ~"typed" => ppm_typed,
630       ~"expanded,identified" => ppm_expanded_identified,
631       ~"identified" => ppm_identified,
632       _ => {
633         sess.fatal(~"argument to `pretty` must be one of `normal`, \
634                      `expanded`, `typed`, `identified`, \
635                      or `expanded,identified`");
636       }
637     }
638 }
639
640 // rustc command line options
641 fn optgroups() -> ~[getopts::groups::OptGroup] {
642  ~[
643   optflag(~"",  ~"bin", ~"Compile an executable crate (default)"),
644   optflag(~"c", ~"",    ~"Compile and assemble, but do not link"),
645   optmulti(~"", ~"cfg", ~"Configure the compilation
646                           environment", ~"SPEC"),
647   optflag(~"",  ~"emit-llvm",
648                         ~"Produce an LLVM bitcode file"),
649   optflag(~"g", ~"",    ~"Produce debug info (experimental)"),
650   optflag(~"",  ~"gc",  ~"Garbage collect shared data (experimental)"),
651   optflag(~"h", ~"help",~"Display this message"),
652   optmulti(~"L", ~"",   ~"Add a directory to the library search path",
653                               ~"PATH"),
654   optflag(~"",  ~"lib", ~"Compile a library crate"),
655   optflag(~"",  ~"ls",  ~"List the symbols defined by a library crate"),
656   optflag(~"",  ~"jit", ~"Execute using JIT (experimental)"),
657   optflag(~"", ~"no-trans",
658                         ~"Run all passes except translation; no output"),
659   optflag(~"O", ~"",    ~"Equivalent to --opt-level=2"),
660   optopt(~"o", ~"",     ~"Write output to <filename>", ~"FILENAME"),
661   optopt(~"", ~"opt-level",
662                         ~"Optimize with possible levels 0-3", ~"LEVEL"),
663   optopt( ~"",  ~"out-dir",
664                         ~"Write output to compiler-chosen filename
665                           in <dir>", ~"DIR"),
666   optflag(~"", ~"parse-only",
667                         ~"Parse only; do not compile, assemble, or link"),
668   optflagopt(~"", ~"pretty",
669                         ~"Pretty-print the input instead of compiling;
670                           valid types are: normal (un-annotated source),
671                           expanded (crates expanded),
672                           typed (crates expanded, with type annotations),
673                           or identified (fully parenthesized,
674                           AST nodes and blocks with IDs)", ~"TYPE"),
675   optflag(~"S", ~"",    ~"Compile only; do not assemble or link"),
676   optflag(~"", ~"xg",   ~"Extra debugging info (experimental)"),
677   optflag(~"", ~"save-temps",
678                         ~"Write intermediate files (.bc, .opt.bc, .o)
679                           in addition to normal output"),
680   optflag(~"", ~"static",
681                         ~"Use or produce static libraries or binaries
682                          (experimental)"),
683   optopt(~"", ~"sysroot",
684                         ~"Override the system root", ~"PATH"),
685   optflag(~"", ~"test", ~"Build a test harness"),
686   optopt(~"", ~"target",
687                         ~"Target triple cpu-manufacturer-kernel[-os]
688                           to compile for (see
689          http://sources.redhat.com/autobook/autobook/autobook_17.html
690                           for detail)", ~"TRIPLE"),
691   optmulti(~"W", ~"warn",
692                         ~"Set lint warnings", ~"OPT"),
693   optmulti(~"A", ~"allow",
694                         ~"Set lint allowed", ~"OPT"),
695   optmulti(~"D", ~"deny",
696                         ~"Set lint denied", ~"OPT"),
697   optmulti(~"F", ~"forbid",
698                         ~"Set lint forbidden", ~"OPT"),
699   optmulti(~"Z", ~"",   ~"Set internal debugging options", "FLAG"),
700   optflag( ~"v", ~"version",
701                         ~"Print version info and exit"),
702  ]
703 }
704
705 type output_filenames = @{out_filename:Path, obj_filename:Path};
706
707 fn build_output_filenames(input: input,
708                           odir: &Option<Path>,
709                           ofile: &Option<Path>,
710                           sess: Session)
711         -> output_filenames {
712     let obj_path;
713     let out_path;
714     let sopts = sess.opts;
715     let stop_after_codegen =
716         sopts.output_type != link::output_type_exe ||
717             sopts.static && sess.building_library;
718
719
720     let obj_suffix =
721         match sopts.output_type {
722           link::output_type_none => ~"none",
723           link::output_type_bitcode => ~"bc",
724           link::output_type_assembly => ~"s",
725           link::output_type_llvm_assembly => ~"ll",
726           // Object and exe output both use the '.o' extension here
727           link::output_type_object | link::output_type_exe => ~"o"
728         };
729
730     match *ofile {
731       None => {
732         // "-" as input file will cause the parser to read from stdin so we
733         // have to make up a name
734         // We want to toss everything after the final '.'
735         let dirpath = match *odir {
736           Some(d) => d,
737           None => match input {
738             str_input(_) => os::getcwd(),
739             file_input(ifile) => ifile.dir_path()
740           }
741         };
742
743         let stem = match input {
744           file_input(ifile) => ifile.filestem().get(),
745           str_input(_) => ~"rust_out"
746         };
747
748         if sess.building_library {
749             out_path = dirpath.push(os::dll_filename(stem));
750             obj_path = dirpath.push(stem).with_filetype(obj_suffix);
751         } else {
752             out_path = dirpath.push(stem);
753             obj_path = dirpath.push(stem).with_filetype(obj_suffix);
754         }
755       }
756
757       Some(out_file) => {
758         out_path = out_file;
759         obj_path = if stop_after_codegen {
760             out_file
761         } else {
762             out_file.with_filetype(obj_suffix)
763         };
764
765         if sess.building_library {
766             // FIXME (#2401): We might want to warn here; we're actually not
767             // going to respect the user's choice of library name when it
768             // comes time to link, we'll be linking to
769             // lib<basename>-<hash>-<version>.so no matter what.
770         }
771
772         if *odir != None {
773             sess.warn(~"ignoring --out-dir flag due to -o flag.");
774         }
775       }
776     }
777     return @{out_filename: out_path,
778              obj_filename: obj_path};
779 }
780
781 fn early_error(emitter: diagnostic::emitter, msg: ~str) -> ! {
782     emitter(None, msg, diagnostic::fatal);
783     fail;
784 }
785
786 fn list_metadata(sess: Session, path: &Path, out: io::Writer) {
787     metadata::loader::list_file_metadata(
788         sess.parse_sess.interner,
789         session::sess_os_to_meta_os(sess.targ_cfg.os), path, out);
790 }
791
792 #[cfg(test)]
793 mod test {
794     #[legacy_exports];
795
796     // When the user supplies --test we should implicitly supply --cfg test
797     #[test]
798     fn test_switch_implies_cfg_test() {
799         let matches =
800             match getopts(~[~"--test"], optgroups()) {
801               Ok(m) => m,
802               Err(f) => fail ~"test_switch_implies_cfg_test: " +
803                              getopts::fail_str(f)
804             };
805         let sessopts = build_session_options(
806             ~"rustc", matches, diagnostic::emit);
807         let sess = build_session(sessopts, diagnostic::emit);
808         let cfg = build_configuration(sess, ~"whatever", str_input(~""));
809         assert (attr::contains_name(cfg, ~"test"));
810     }
811
812     // When the user supplies --test and --cfg test, don't implicitly add
813     // another --cfg test
814     #[test]
815     fn test_switch_implies_cfg_test_unless_cfg_test() {
816         let matches =
817             match getopts(~[~"--test", ~"--cfg=test"], optgroups()) {
818               Ok(m) => m,
819               Err(f) => {
820                 fail ~"test_switch_implies_cfg_test_unless_cfg_test: " +
821                     getopts::fail_str(f);
822               }
823             };
824         let sessopts = build_session_options(
825             ~"rustc", matches, diagnostic::emit);
826         let sess = build_session(sessopts, diagnostic::emit);
827         let cfg = build_configuration(sess, ~"whatever", str_input(~""));
828         let test_items = attr::find_meta_items_by_name(cfg, ~"test");
829         assert (vec::len(test_items) == 1u);
830     }
831 }
832
833 // Local Variables:
834 // mode: rust
835 // fill-column: 78;
836 // indent-tabs-mode: nil
837 // c-basic-offset: 4
838 // buffer-file-coding-system: utf-8-unix
839 // End: