]> git.lizzy.rs Git - rust.git/blob - src/librustc/driver/config.rs
auto merge of #15565 : alexcrichton/rust/issue-15475, r=huonw
[rust.git] / src / librustc / driver / config.rs
1 // Copyright 2014 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 //! Contains infrastructure for configuring the compiler, including parsing
12 //! command line options.
13
14 use driver::{early_error, early_warn};
15 use driver::driver;
16 use driver::session::Session;
17
18 use back;
19 use back::link;
20 use back::target_strs;
21 use back::{arm, x86, x86_64, mips, mipsel};
22 use lint;
23
24 use syntax::abi;
25 use syntax::ast;
26 use syntax::ast::{IntTy, UintTy};
27 use syntax::attr;
28 use syntax::attr::AttrMetaMethods;
29 use syntax::diagnostic::{ColorConfig, Auto, Always, Never};
30 use syntax::parse;
31 use syntax::parse::token::InternedString;
32
33 use std::collections::{HashSet, HashMap};
34 use getopts::{optopt, optmulti, optflag, optflagopt};
35 use getopts;
36 use lib::llvm::llvm;
37 use std::cell::{RefCell};
38 use std::fmt;
39
40
41 pub struct Config {
42     pub os: abi::Os,
43     pub arch: abi::Architecture,
44     pub target_strs: target_strs::t,
45     pub int_type: IntTy,
46     pub uint_type: UintTy,
47 }
48
49 #[deriving(Clone, PartialEq)]
50 pub enum OptLevel {
51     No, // -O0
52     Less, // -O1
53     Default, // -O2
54     Aggressive // -O3
55 }
56
57 #[deriving(Clone, PartialEq)]
58 pub enum DebugInfoLevel {
59     NoDebugInfo,
60     LimitedDebugInfo,
61     FullDebugInfo,
62 }
63
64 #[deriving(Clone)]
65 pub struct Options {
66     // The crate config requested for the session, which may be combined
67     // with additional crate configurations during the compile process
68     pub crate_types: Vec<CrateType>,
69
70     pub gc: bool,
71     pub optimize: OptLevel,
72     pub debuginfo: DebugInfoLevel,
73     pub lint_opts: Vec<(String, lint::Level)>,
74     pub describe_lints: bool,
75     pub output_types: Vec<back::link::OutputType> ,
76     // This was mutable for rustpkg, which updates search paths based on the
77     // parsed code. It remains mutable in case its replacements wants to use
78     // this.
79     pub addl_lib_search_paths: RefCell<HashSet<Path>>,
80     pub maybe_sysroot: Option<Path>,
81     pub target_triple: String,
82     // User-specified cfg meta items. The compiler itself will add additional
83     // items to the crate config, and during parsing the entire crate config
84     // will be added to the crate AST node.  This should not be used for
85     // anything except building the full crate config prior to parsing.
86     pub cfg: ast::CrateConfig,
87     pub test: bool,
88     pub parse_only: bool,
89     pub no_trans: bool,
90     pub no_analysis: bool,
91     pub debugging_opts: u64,
92     /// Whether to write dependency files. It's (enabled, optional filename).
93     pub write_dependency_info: (bool, Option<Path>),
94     /// Crate id-related things to maybe print. It's (crate_name, crate_file_name).
95     pub print_metas: (bool, bool),
96     pub cg: CodegenOptions,
97     pub color: ColorConfig,
98     pub externs: HashMap<String, Vec<String>>,
99     pub crate_name: Option<String>,
100 }
101
102 /// Some reasonable defaults
103 pub fn basic_options() -> Options {
104     Options {
105         crate_types: Vec::new(),
106         gc: false,
107         optimize: No,
108         debuginfo: NoDebugInfo,
109         lint_opts: Vec::new(),
110         describe_lints: false,
111         output_types: Vec::new(),
112         addl_lib_search_paths: RefCell::new(HashSet::new()),
113         maybe_sysroot: None,
114         target_triple: driver::host_triple().to_string(),
115         cfg: Vec::new(),
116         test: false,
117         parse_only: false,
118         no_trans: false,
119         no_analysis: false,
120         debugging_opts: 0,
121         write_dependency_info: (false, None),
122         print_metas: (false, false),
123         cg: basic_codegen_options(),
124         color: Auto,
125         externs: HashMap::new(),
126         crate_name: None,
127     }
128 }
129
130 // The type of entry function, so
131 // users can have their own entry
132 // functions that don't start a
133 // scheduler
134 #[deriving(PartialEq)]
135 pub enum EntryFnType {
136     EntryMain,
137     EntryStart,
138     EntryNone,
139 }
140
141 #[deriving(PartialEq, PartialOrd, Clone, Ord, Eq, Hash)]
142 pub enum CrateType {
143     CrateTypeExecutable,
144     CrateTypeDylib,
145     CrateTypeRlib,
146     CrateTypeStaticlib,
147 }
148
149 macro_rules! debugging_opts(
150     ([ $opt:ident ] $cnt:expr ) => (
151         pub static $opt: u64 = 1 << $cnt;
152     );
153     ([ $opt:ident, $($rest:ident),* ] $cnt:expr ) => (
154         pub static $opt: u64 = 1 << $cnt;
155         debugging_opts!([ $($rest),* ] $cnt + 1)
156     )
157 )
158
159 debugging_opts!(
160     [
161         VERBOSE,
162         TIME_PASSES,
163         COUNT_LLVM_INSNS,
164         TIME_LLVM_PASSES,
165         TRANS_STATS,
166         ASM_COMMENTS,
167         NO_VERIFY,
168         BORROWCK_STATS,
169         NO_LANDING_PADS,
170         DEBUG_LLVM,
171         SHOW_SPAN,
172         COUNT_TYPE_SIZES,
173         META_STATS,
174         NO_OPT,
175         GC,
176         PRINT_LINK_ARGS,
177         PRINT_LLVM_PASSES,
178         LTO,
179         AST_JSON,
180         AST_JSON_NOEXPAND,
181         LS,
182         SAVE_ANALYSIS
183     ]
184     0
185 )
186
187 pub fn debugging_opts_map() -> Vec<(&'static str, &'static str, u64)> {
188     vec!(("verbose", "in general, enable more debug printouts", VERBOSE),
189      ("time-passes", "measure time of each rustc pass", TIME_PASSES),
190      ("count-llvm-insns", "count where LLVM \
191                            instrs originate", COUNT_LLVM_INSNS),
192      ("time-llvm-passes", "measure time of each LLVM pass",
193       TIME_LLVM_PASSES),
194      ("trans-stats", "gather trans statistics", TRANS_STATS),
195      ("asm-comments", "generate comments into the assembly (may change behavior)",
196       ASM_COMMENTS),
197      ("no-verify", "skip LLVM verification", NO_VERIFY),
198      ("borrowck-stats", "gather borrowck statistics",  BORROWCK_STATS),
199      ("no-landing-pads", "omit landing pads for unwinding",
200       NO_LANDING_PADS),
201      ("debug-llvm", "enable debug output from LLVM", DEBUG_LLVM),
202      ("show-span", "show spans for compiler debugging", SHOW_SPAN),
203      ("count-type-sizes", "count the sizes of aggregate types",
204       COUNT_TYPE_SIZES),
205      ("meta-stats", "gather metadata statistics", META_STATS),
206      ("no-opt", "do not optimize, even if -O is passed", NO_OPT),
207      ("print-link-args", "Print the arguments passed to the linker",
208       PRINT_LINK_ARGS),
209      ("gc", "Garbage collect shared data (experimental)", GC),
210      ("print-llvm-passes",
211       "Prints the llvm optimization passes being run",
212       PRINT_LLVM_PASSES),
213      ("lto", "Perform LLVM link-time optimizations", LTO),
214      ("ast-json", "Print the AST as JSON and halt", AST_JSON),
215      ("ast-json-noexpand", "Print the pre-expansion AST as JSON and halt", AST_JSON_NOEXPAND),
216      ("ls", "List the symbols defined by a library crate", LS),
217      ("save-analysis", "Write syntax and type analysis information \
218                         in addition to normal output", SAVE_ANALYSIS))
219 }
220
221 /// Declare a macro that will define all CodegenOptions fields and parsers all
222 /// at once. The goal of this macro is to define an interface that can be
223 /// programmatically used by the option parser in order to initialize the struct
224 /// without hardcoding field names all over the place.
225 ///
226 /// The goal is to invoke this macro once with the correct fields, and then this
227 /// macro generates all necessary code. The main gotcha of this macro is the
228 /// cgsetters module which is a bunch of generated code to parse an option into
229 /// its respective field in the struct. There are a few hand-written parsers for
230 /// parsing specific types of values in this module.
231 macro_rules! cgoptions(
232     ($($opt:ident : $t:ty = ($init:expr, $parse:ident, $desc:expr)),* ,) =>
233 (
234     #[deriving(Clone)]
235     pub struct CodegenOptions { $(pub $opt: $t),* }
236
237     pub fn basic_codegen_options() -> CodegenOptions {
238         CodegenOptions { $($opt: $init),* }
239     }
240
241     pub type CodegenSetter = fn(&mut CodegenOptions, v: Option<&str>) -> bool;
242     pub static CG_OPTIONS: &'static [(&'static str, CodegenSetter,
243                                       &'static str)] =
244         &[ $( (stringify!($opt), cgsetters::$opt, $desc) ),* ];
245
246     mod cgsetters {
247         use super::CodegenOptions;
248
249         $(
250             pub fn $opt(cg: &mut CodegenOptions, v: Option<&str>) -> bool {
251                 $parse(&mut cg.$opt, v)
252             }
253         )*
254
255         fn parse_bool(slot: &mut bool, v: Option<&str>) -> bool {
256             match v {
257                 Some(..) => false,
258                 None => { *slot = true; true }
259             }
260         }
261
262         fn parse_opt_string(slot: &mut Option<String>, v: Option<&str>) -> bool {
263             match v {
264                 Some(s) => { *slot = Some(s.to_string()); true },
265                 None => false,
266             }
267         }
268
269         fn parse_string(slot: &mut String, v: Option<&str>) -> bool {
270             match v {
271                 Some(s) => { *slot = s.to_string(); true },
272                 None => false,
273             }
274         }
275
276         fn parse_list(slot: &mut Vec<String>, v: Option<&str>)
277                       -> bool {
278             match v {
279                 Some(s) => {
280                     for s in s.words() {
281                         slot.push(s.to_string());
282                     }
283                     true
284                 },
285                 None => false,
286             }
287         }
288
289     }
290 ) )
291
292 cgoptions!(
293     ar: Option<String> = (None, parse_opt_string,
294         "tool to assemble archives with"),
295     linker: Option<String> = (None, parse_opt_string,
296         "system linker to link outputs with"),
297     link_args: Vec<String> = (Vec::new(), parse_list,
298         "extra arguments to pass to the linker (space separated)"),
299     target_cpu: String = ("generic".to_string(), parse_string,
300         "select target processor (llc -mcpu=help for details)"),
301     target_feature: String = ("".to_string(), parse_string,
302         "target specific attributes (llc -mattr=help for details)"),
303     passes: Vec<String> = (Vec::new(), parse_list,
304         "a list of extra LLVM passes to run (space separated)"),
305     llvm_args: Vec<String> = (Vec::new(), parse_list,
306         "a list of arguments to pass to llvm (space separated)"),
307     save_temps: bool = (false, parse_bool,
308         "save all temporary output files during compilation"),
309     rpath: bool = (false, parse_bool,
310         "set rpath values in libs/exes"),
311     no_prepopulate_passes: bool = (false, parse_bool,
312         "don't pre-populate the pass manager with a list of passes"),
313     no_vectorize_loops: bool = (false, parse_bool,
314         "don't run the loop vectorization optimization passes"),
315     no_vectorize_slp: bool = (false, parse_bool,
316         "don't run LLVM's SLP vectorization pass"),
317     soft_float: bool = (false, parse_bool,
318         "generate software floating point library calls"),
319     prefer_dynamic: bool = (false, parse_bool,
320         "prefer dynamic linking to static linking"),
321     no_integrated_as: bool = (false, parse_bool,
322         "use an external assembler rather than LLVM's integrated one"),
323     relocation_model: String = ("pic".to_string(), parse_string,
324          "choose the relocation model to use (llc -relocation-model for details)"),
325     metadata: Vec<String> = (Vec::new(), parse_list,
326          "metadata to mangle symbol names with"),
327     extra_filename: String = ("".to_string(), parse_string,
328          "extra data to put in each output filename"),
329 )
330
331 pub fn build_codegen_options(matches: &getopts::Matches) -> CodegenOptions
332 {
333     let mut cg = basic_codegen_options();
334     for option in matches.opt_strs("C").move_iter() {
335         let mut iter = option.as_slice().splitn('=', 1);
336         let key = iter.next().unwrap();
337         let value = iter.next();
338         let option_to_lookup = key.replace("-", "_");
339         let mut found = false;
340         for &(candidate, setter, _) in CG_OPTIONS.iter() {
341             if option_to_lookup.as_slice() != candidate { continue }
342             if !setter(&mut cg, value) {
343                 match value {
344                     Some(..) => {
345                         early_error(format!("codegen option `{}` takes no \
346                                              value", key).as_slice())
347                     }
348                     None => {
349                         early_error(format!("codegen option `{0}` requires \
350                                              a value (-C {0}=<value>)",
351                                             key).as_slice())
352                     }
353                 }
354             }
355             found = true;
356             break;
357         }
358         if !found {
359             early_error(format!("unknown codegen option: `{}`",
360                                 key).as_slice());
361         }
362     }
363     return cg;
364 }
365
366 pub fn default_lib_output() -> CrateType {
367     CrateTypeRlib
368 }
369
370 pub fn default_configuration(sess: &Session) -> ast::CrateConfig {
371     let tos = match sess.targ_cfg.os {
372         abi::OsWin32 =>   InternedString::new("win32"),
373         abi::OsMacos =>   InternedString::new("macos"),
374         abi::OsLinux =>   InternedString::new("linux"),
375         abi::OsAndroid => InternedString::new("android"),
376         abi::OsFreebsd => InternedString::new("freebsd"),
377         abi::OsiOS =>     InternedString::new("ios"),
378     };
379
380     // ARM is bi-endian, however using NDK seems to default
381     // to little-endian unless a flag is provided.
382     let (end,arch,wordsz) = match sess.targ_cfg.arch {
383         abi::X86 =>    ("little", "x86",    "32"),
384         abi::X86_64 => ("little", "x86_64", "64"),
385         abi::Arm =>    ("little", "arm",    "32"),
386         abi::Mips =>   ("big",    "mips",   "32"),
387         abi::Mipsel => ("little", "mipsel", "32")
388     };
389
390     let fam = match sess.targ_cfg.os {
391         abi::OsWin32 => InternedString::new("windows"),
392         _ => InternedString::new("unix")
393     };
394
395     let mk = attr::mk_name_value_item_str;
396     return vec!(// Target bindings.
397          attr::mk_word_item(fam.clone()),
398          mk(InternedString::new("target_os"), tos),
399          mk(InternedString::new("target_family"), fam),
400          mk(InternedString::new("target_arch"), InternedString::new(arch)),
401          mk(InternedString::new("target_endian"), InternedString::new(end)),
402          mk(InternedString::new("target_word_size"),
403             InternedString::new(wordsz))
404     );
405 }
406
407 pub fn append_configuration(cfg: &mut ast::CrateConfig,
408                             name: InternedString) {
409     if !cfg.iter().any(|mi| mi.name() == name) {
410         cfg.push(attr::mk_word_item(name))
411     }
412 }
413
414 pub fn build_configuration(sess: &Session) -> ast::CrateConfig {
415     // Combine the configuration requested by the session (command line) with
416     // some default and generated configuration items
417     let default_cfg = default_configuration(sess);
418     let mut user_cfg = sess.opts.cfg.clone();
419     // If the user wants a test runner, then add the test cfg
420     if sess.opts.test {
421         append_configuration(&mut user_cfg, InternedString::new("test"))
422     }
423     // If the user requested GC, then add the GC cfg
424     append_configuration(&mut user_cfg, if sess.opts.gc {
425         InternedString::new("gc")
426     } else {
427         InternedString::new("nogc")
428     });
429     user_cfg.move_iter().collect::<Vec<_>>().append(default_cfg.as_slice())
430 }
431
432 pub fn get_os(triple: &str) -> Option<abi::Os> {
433     for &(name, os) in os_names.iter() {
434         if triple.contains(name) { return Some(os) }
435     }
436     None
437 }
438 static os_names : &'static [(&'static str, abi::Os)] = &[
439     ("mingw32", abi::OsWin32),
440     ("win32",   abi::OsWin32),
441     ("darwin",  abi::OsMacos),
442     ("android", abi::OsAndroid),
443     ("linux",   abi::OsLinux),
444     ("freebsd", abi::OsFreebsd),
445     ("ios",     abi::OsiOS)];
446
447 pub fn get_arch(triple: &str) -> Option<abi::Architecture> {
448     for &(arch, abi) in architecture_abis.iter() {
449         if triple.contains(arch) { return Some(abi) }
450     }
451     None
452 }
453 static architecture_abis : &'static [(&'static str, abi::Architecture)] = &[
454     ("i386",   abi::X86),
455     ("i486",   abi::X86),
456     ("i586",   abi::X86),
457     ("i686",   abi::X86),
458     ("i786",   abi::X86),
459
460     ("x86_64", abi::X86_64),
461
462     ("arm",    abi::Arm),
463     ("xscale", abi::Arm),
464     ("thumb",  abi::Arm),
465
466     ("mipsel", abi::Mipsel),
467     ("mips",   abi::Mips)];
468
469 pub fn build_target_config(sopts: &Options) -> Config {
470     let os = match get_os(sopts.target_triple.as_slice()) {
471       Some(os) => os,
472       None => early_error("unknown operating system")
473     };
474     let arch = match get_arch(sopts.target_triple.as_slice()) {
475       Some(arch) => arch,
476       None => {
477           early_error(format!("unknown architecture: {}",
478                               sopts.target_triple.as_slice()).as_slice())
479       }
480     };
481     let (int_type, uint_type) = match arch {
482       abi::X86 => (ast::TyI32, ast::TyU32),
483       abi::X86_64 => (ast::TyI64, ast::TyU64),
484       abi::Arm => (ast::TyI32, ast::TyU32),
485       abi::Mips => (ast::TyI32, ast::TyU32),
486       abi::Mipsel => (ast::TyI32, ast::TyU32)
487     };
488     let target_triple = sopts.target_triple.clone();
489     let target_strs = match arch {
490       abi::X86 => x86::get_target_strs(target_triple, os),
491       abi::X86_64 => x86_64::get_target_strs(target_triple, os),
492       abi::Arm => arm::get_target_strs(target_triple, os),
493       abi::Mips => mips::get_target_strs(target_triple, os),
494       abi::Mipsel => mipsel::get_target_strs(target_triple, os)
495     };
496     Config {
497         os: os,
498         arch: arch,
499         target_strs: target_strs,
500         int_type: int_type,
501         uint_type: uint_type,
502     }
503 }
504
505 // rustc command line options
506 pub fn optgroups() -> Vec<getopts::OptGroup> {
507     vec!(
508         optflag("h", "help", "Display this message"),
509         optmulti("", "cfg", "Configure the compilation environment", "SPEC"),
510         optmulti("L", "",   "Add a directory to the library search path", "PATH"),
511         optmulti("", "crate-type", "Comma separated list of types of crates
512                                     for the compiler to emit",
513                  "[bin|lib|rlib|dylib|staticlib]"),
514         optmulti("", "emit", "Comma separated list of types of output for the compiler to emit",
515                  "[asm|bc|ir|obj|link]"),
516         optopt("", "crate-name", "Specify the name of the crate being built",
517                "NAME"),
518         optflag("", "print-crate-name", "Output the crate name and exit"),
519         optflag("", "print-file-name", "Output the file(s) that would be written if compilation \
520               continued and exit"),
521         optflag("", "crate-file-name", "deprecated in favor of --print-file-name"),
522         optflag("g",  "",  "Equivalent to --debuginfo=2"),
523         optopt("",  "debuginfo",  "Emit DWARF debug info to the objects created:
524              0 = no debug info,
525              1 = line-tables only (for stacktraces and breakpoints),
526              2 = full debug info with variable and type information (same as -g)", "LEVEL"),
527         optflag("", "no-trans", "Run all passes except translation; no output"),
528         optflag("", "no-analysis",
529               "Parse and expand the source, but run no analysis and produce no output"),
530         optflag("O", "", "Equivalent to --opt-level=2"),
531         optopt("o", "", "Write output to <filename>", "FILENAME"),
532         optopt("", "opt-level", "Optimize with possible levels 0-3", "LEVEL"),
533         optopt( "",  "out-dir", "Write output to compiler-chosen filename in <dir>", "DIR"),
534         optflag("", "parse-only", "Parse only; do not compile, assemble, or link"),
535         optopt("", "explain", "Provide a detailed explanation of an error message", "OPT"),
536         optflagopt("", "pretty",
537                    "Pretty-print the input instead of compiling;
538                    valid types are: `normal` (un-annotated source),
539                    `expanded` (crates expanded),
540                    `typed` (crates expanded, with type annotations),
541                    `expanded,identified` (fully parenthesized, AST nodes with IDs), or
542                    `flowgraph=<nodeid>` (graphviz formatted flowgraph for node)",
543                  "TYPE"),
544         optflagopt("", "dep-info",
545                  "Output dependency info to <filename> after compiling, \
546                   in a format suitable for use by Makefiles", "FILENAME"),
547         optopt("", "sysroot", "Override the system root", "PATH"),
548         optflag("", "test", "Build a test harness"),
549         optopt("", "target", "Target triple cpu-manufacturer-kernel[-os]
550                             to compile for (see chapter 3.4 of http://www.sourceware.org/autobook/
551                             for details)", "TRIPLE"),
552         optmulti("W", "warn", "Set lint warnings", "OPT"),
553         optmulti("A", "allow", "Set lint allowed", "OPT"),
554         optmulti("D", "deny", "Set lint denied", "OPT"),
555         optmulti("F", "forbid", "Set lint forbidden", "OPT"),
556         optmulti("C", "codegen", "Set a codegen option", "OPT[=VALUE]"),
557         optmulti("Z", "", "Set internal debugging options", "FLAG"),
558         optflagopt("v", "version", "Print version info and exit", "verbose"),
559         optopt("", "color", "Configure coloring of output:
560             auto   = colorize, if output goes to a tty (default);
561             always = always colorize output;
562             never  = never colorize output", "auto|always|never"),
563         optmulti("", "extern", "Specify where an external rust library is located",
564                  "PATH"),
565     )
566 }
567
568
569 // Convert strings provided as --cfg [cfgspec] into a crate_cfg
570 fn parse_cfgspecs(cfgspecs: Vec<String> ) -> ast::CrateConfig {
571     cfgspecs.move_iter().map(|s| {
572         parse::parse_meta_from_source_str("cfgspec".to_string(),
573                                           s.to_string(),
574                                           Vec::new(),
575                                           &parse::new_parse_sess())
576     }).collect::<ast::CrateConfig>()
577 }
578
579 pub fn build_session_options(matches: &getopts::Matches) -> Options {
580     let mut crate_types: Vec<CrateType> = Vec::new();
581     let unparsed_crate_types = matches.opt_strs("crate-type");
582     for unparsed_crate_type in unparsed_crate_types.iter() {
583         for part in unparsed_crate_type.as_slice().split(',') {
584             let new_part = match part {
585                 "lib"       => default_lib_output(),
586                 "rlib"      => CrateTypeRlib,
587                 "staticlib" => CrateTypeStaticlib,
588                 "dylib"     => CrateTypeDylib,
589                 "bin"       => CrateTypeExecutable,
590                 _ => {
591                     early_error(format!("unknown crate type: `{}`",
592                                         part).as_slice())
593                 }
594             };
595             crate_types.push(new_part)
596         }
597     }
598
599     let parse_only = matches.opt_present("parse-only");
600     let no_trans = matches.opt_present("no-trans");
601     let no_analysis = matches.opt_present("no-analysis");
602
603     let mut lint_opts = vec!();
604     let mut describe_lints = false;
605
606     for &level in [lint::Allow, lint::Warn, lint::Deny, lint::Forbid].iter() {
607         for lint_name in matches.opt_strs(level.as_str()).move_iter() {
608             if lint_name.as_slice() == "help" {
609                 describe_lints = true;
610             } else {
611                 lint_opts.push((lint_name.replace("-", "_").into_string(), level));
612             }
613         }
614     }
615
616     let mut debugging_opts = 0;
617     let debug_flags = matches.opt_strs("Z");
618     let debug_map = debugging_opts_map();
619     for debug_flag in debug_flags.iter() {
620         let mut this_bit = 0;
621         for tuple in debug_map.iter() {
622             let (name, bit) = match *tuple { (ref a, _, b) => (a, b) };
623             if *name == debug_flag.as_slice() {
624                 this_bit = bit;
625                 break;
626             }
627         }
628         if this_bit == 0 {
629             early_error(format!("unknown debug flag: {}",
630                                 *debug_flag).as_slice())
631         }
632         debugging_opts |= this_bit;
633     }
634
635     if debugging_opts & DEBUG_LLVM != 0 {
636         unsafe { llvm::LLVMSetDebug(1); }
637     }
638
639     let mut output_types = Vec::new();
640     if !parse_only && !no_trans {
641         let unparsed_output_types = matches.opt_strs("emit");
642         for unparsed_output_type in unparsed_output_types.iter() {
643             for part in unparsed_output_type.as_slice().split(',') {
644                 let output_type = match part.as_slice() {
645                     "asm"  => link::OutputTypeAssembly,
646                     "ir"   => link::OutputTypeLlvmAssembly,
647                     "bc"   => link::OutputTypeBitcode,
648                     "obj"  => link::OutputTypeObject,
649                     "link" => link::OutputTypeExe,
650                     _ => {
651                         early_error(format!("unknown emission type: `{}`",
652                                             part).as_slice())
653                     }
654                 };
655                 output_types.push(output_type)
656             }
657         }
658     };
659     output_types.as_mut_slice().sort();
660     output_types.dedup();
661     if output_types.len() == 0 {
662         output_types.push(link::OutputTypeExe);
663     }
664
665     let sysroot_opt = matches.opt_str("sysroot").map(|m| Path::new(m));
666     let target = matches.opt_str("target").unwrap_or(
667         driver::host_triple().to_string());
668     let opt_level = {
669         if (debugging_opts & NO_OPT) != 0 {
670             No
671         } else if matches.opt_present("O") {
672             if matches.opt_present("opt-level") {
673                 early_error("-O and --opt-level both provided");
674             }
675             Default
676         } else if matches.opt_present("opt-level") {
677             match matches.opt_str("opt-level").as_ref().map(|s| s.as_slice()) {
678                 None      |
679                 Some("0") => No,
680                 Some("1") => Less,
681                 Some("2") => Default,
682                 Some("3") => Aggressive,
683                 Some(arg) => {
684                     early_error(format!("optimization level needs to be \
685                                          between 0-3 (instead was `{}`)",
686                                         arg).as_slice());
687                 }
688             }
689         } else {
690             No
691         }
692     };
693     let gc = debugging_opts & GC != 0;
694     let debuginfo = if matches.opt_present("g") {
695         if matches.opt_present("debuginfo") {
696             early_error("-g and --debuginfo both provided");
697         }
698         FullDebugInfo
699     } else if matches.opt_present("debuginfo") {
700         match matches.opt_str("debuginfo").as_ref().map(|s| s.as_slice()) {
701             Some("0") => NoDebugInfo,
702             Some("1") => LimitedDebugInfo,
703             None      |
704             Some("2") => FullDebugInfo,
705             Some(arg) => {
706                 early_error(format!("optimization level needs to be between \
707                                      0-3 (instead was `{}`)",
708                                     arg).as_slice());
709             }
710         }
711     } else {
712         NoDebugInfo
713     };
714
715     let addl_lib_search_paths = matches.opt_strs("L").iter().map(|s| {
716         Path::new(s.as_slice())
717     }).collect();
718
719     let cfg = parse_cfgspecs(matches.opt_strs("cfg"));
720     let test = matches.opt_present("test");
721     let write_dependency_info = (matches.opt_present("dep-info"),
722                                  matches.opt_str("dep-info")
723                                         .map(|p| Path::new(p)));
724
725     let print_metas = (matches.opt_present("print-crate-name"),
726                        matches.opt_present("print-file-name") ||
727                        matches.opt_present("crate-file-name"));
728     if matches.opt_present("crate-file-name") {
729         early_warn("the --crate-file-name argument has been renamed to \
730                     --print-file-name");
731     }
732     let cg = build_codegen_options(matches);
733
734     let color = match matches.opt_str("color").as_ref().map(|s| s.as_slice()) {
735         Some("auto")   => Auto,
736         Some("always") => Always,
737         Some("never")  => Never,
738
739         None => Auto,
740
741         Some(arg) => {
742             early_error(format!("argument for --color must be auto, always \
743                                  or never (instead was `{}`)",
744                                 arg).as_slice())
745         }
746     };
747
748     let mut externs = HashMap::new();
749     for arg in matches.opt_strs("extern").iter() {
750         let mut parts = arg.as_slice().splitn('=', 1);
751         let name = match parts.next() {
752             Some(s) => s,
753             None => early_error("--extern value must not be empty"),
754         };
755         let location = match parts.next() {
756             Some(s) => s,
757             None => early_error("--extern value must be of the format `foo=bar`"),
758         };
759         let locs = externs.find_or_insert(name.to_string(), Vec::new());
760         locs.push(location.to_string());
761     }
762
763     let crate_name = matches.opt_str("crate-name");
764
765     Options {
766         crate_types: crate_types,
767         gc: gc,
768         optimize: opt_level,
769         debuginfo: debuginfo,
770         lint_opts: lint_opts,
771         describe_lints: describe_lints,
772         output_types: output_types,
773         addl_lib_search_paths: RefCell::new(addl_lib_search_paths),
774         maybe_sysroot: sysroot_opt,
775         target_triple: target,
776         cfg: cfg,
777         test: test,
778         parse_only: parse_only,
779         no_trans: no_trans,
780         no_analysis: no_analysis,
781         debugging_opts: debugging_opts,
782         write_dependency_info: write_dependency_info,
783         print_metas: print_metas,
784         cg: cg,
785         color: color,
786         externs: externs,
787         crate_name: crate_name,
788     }
789 }
790
791 impl fmt::Show for CrateType {
792     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
793         match *self {
794             CrateTypeExecutable => "bin".fmt(f),
795             CrateTypeDylib => "dylib".fmt(f),
796             CrateTypeRlib => "rlib".fmt(f),
797             CrateTypeStaticlib => "staticlib".fmt(f)
798         }
799     }
800 }
801
802 #[cfg(test)]
803 mod test {
804
805     use driver::config::{build_configuration, optgroups, build_session_options};
806     use driver::session::build_session;
807
808     use getopts::getopts;
809     use syntax::attr;
810     use syntax::attr::AttrMetaMethods;
811     use syntax::diagnostics;
812
813     // When the user supplies --test we should implicitly supply --cfg test
814     #[test]
815     fn test_switch_implies_cfg_test() {
816         let matches =
817             &match getopts(["--test".to_string()], optgroups().as_slice()) {
818               Ok(m) => m,
819               Err(f) => fail!("test_switch_implies_cfg_test: {}", f)
820             };
821         let registry = diagnostics::registry::Registry::new([]);
822         let sessopts = build_session_options(matches);
823         let sess = build_session(sessopts, None, registry);
824         let cfg = build_configuration(&sess);
825         assert!((attr::contains_name(cfg.as_slice(), "test")));
826     }
827
828     // When the user supplies --test and --cfg test, don't implicitly add
829     // another --cfg test
830     #[test]
831     fn test_switch_implies_cfg_test_unless_cfg_test() {
832         let matches =
833             &match getopts(["--test".to_string(), "--cfg=test".to_string()],
834                            optgroups().as_slice()) {
835               Ok(m) => m,
836               Err(f) => {
837                 fail!("test_switch_implies_cfg_test_unless_cfg_test: {}", f)
838               }
839             };
840         let registry = diagnostics::registry::Registry::new([]);
841         let sessopts = build_session_options(matches);
842         let sess = build_session(sessopts, None, registry);
843         let cfg = build_configuration(&sess);
844         let mut test_items = cfg.iter().filter(|m| m.name().equiv(&("test")));
845         assert!(test_items.next().is_some());
846         assert!(test_items.next().is_none());
847     }
848 }