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