]> git.lizzy.rs Git - rust.git/blob - src/librustc/driver/config.rs
auto merge of #15411 : mitchmindtree/rust/master, r=alexcrichton
[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     no_rpath: bool = (false, parse_bool,
310         "disables setting the rpath 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         optflagopt("", "pretty",
536                    "Pretty-print the input instead of compiling;
537                    valid types are: `normal` (un-annotated source),
538                    `expanded` (crates expanded),
539                    `typed` (crates expanded, with type annotations),
540                    `expanded,identified` (fully parenthesized, AST nodes with IDs), or
541                    `flowgraph=<nodeid>` (graphviz formatted flowgraph for node)",
542                  "TYPE"),
543         optflagopt("", "dep-info",
544                  "Output dependency info to <filename> after compiling, \
545                   in a format suitable for use by Makefiles", "FILENAME"),
546         optopt("", "sysroot", "Override the system root", "PATH"),
547         optflag("", "test", "Build a test harness"),
548         optopt("", "target", "Target triple cpu-manufacturer-kernel[-os]
549                             to compile for (see chapter 3.4 of http://www.sourceware.org/autobook/
550                             for details)", "TRIPLE"),
551         optmulti("W", "warn", "Set lint warnings", "OPT"),
552         optmulti("A", "allow", "Set lint allowed", "OPT"),
553         optmulti("D", "deny", "Set lint denied", "OPT"),
554         optmulti("F", "forbid", "Set lint forbidden", "OPT"),
555         optmulti("C", "codegen", "Set a codegen option", "OPT[=VALUE]"),
556         optmulti("Z", "", "Set internal debugging options", "FLAG"),
557         optflagopt("v", "version", "Print version info and exit", "verbose"),
558         optopt("", "color", "Configure coloring of output:
559             auto   = colorize, if output goes to a tty (default);
560             always = always colorize output;
561             never  = never colorize output", "auto|always|never"),
562         optmulti("", "extern", "Specify where an external rust library is located",
563                  "PATH"),
564     )
565 }
566
567
568 // Convert strings provided as --cfg [cfgspec] into a crate_cfg
569 fn parse_cfgspecs(cfgspecs: Vec<String> ) -> ast::CrateConfig {
570     cfgspecs.move_iter().map(|s| {
571         parse::parse_meta_from_source_str("cfgspec".to_string(),
572                                           s.to_string(),
573                                           Vec::new(),
574                                           &parse::new_parse_sess())
575     }).collect::<ast::CrateConfig>()
576 }
577
578 pub fn build_session_options(matches: &getopts::Matches) -> Options {
579     let mut crate_types: Vec<CrateType> = Vec::new();
580     let unparsed_crate_types = matches.opt_strs("crate-type");
581     for unparsed_crate_type in unparsed_crate_types.iter() {
582         for part in unparsed_crate_type.as_slice().split(',') {
583             let new_part = match part {
584                 "lib"       => default_lib_output(),
585                 "rlib"      => CrateTypeRlib,
586                 "staticlib" => CrateTypeStaticlib,
587                 "dylib"     => CrateTypeDylib,
588                 "bin"       => CrateTypeExecutable,
589                 _ => {
590                     early_error(format!("unknown crate type: `{}`",
591                                         part).as_slice())
592                 }
593             };
594             crate_types.push(new_part)
595         }
596     }
597
598     let parse_only = matches.opt_present("parse-only");
599     let no_trans = matches.opt_present("no-trans");
600     let no_analysis = matches.opt_present("no-analysis");
601
602     let mut lint_opts = vec!();
603     let mut describe_lints = false;
604
605     for &level in [lint::Allow, lint::Warn, lint::Deny, lint::Forbid].iter() {
606         for lint_name in matches.opt_strs(level.as_str()).move_iter() {
607             if lint_name.as_slice() == "help" {
608                 describe_lints = true;
609             } else {
610                 lint_opts.push((lint_name.replace("-", "_").into_string(), level));
611             }
612         }
613     }
614
615     let mut debugging_opts = 0;
616     let debug_flags = matches.opt_strs("Z");
617     let debug_map = debugging_opts_map();
618     for debug_flag in debug_flags.iter() {
619         let mut this_bit = 0;
620         for tuple in debug_map.iter() {
621             let (name, bit) = match *tuple { (ref a, _, b) => (a, b) };
622             if *name == debug_flag.as_slice() {
623                 this_bit = bit;
624                 break;
625             }
626         }
627         if this_bit == 0 {
628             early_error(format!("unknown debug flag: {}",
629                                 *debug_flag).as_slice())
630         }
631         debugging_opts |= this_bit;
632     }
633
634     if debugging_opts & DEBUG_LLVM != 0 {
635         unsafe { llvm::LLVMSetDebug(1); }
636     }
637
638     let mut output_types = Vec::new();
639     if !parse_only && !no_trans {
640         let unparsed_output_types = matches.opt_strs("emit");
641         for unparsed_output_type in unparsed_output_types.iter() {
642             for part in unparsed_output_type.as_slice().split(',') {
643                 let output_type = match part.as_slice() {
644                     "asm"  => link::OutputTypeAssembly,
645                     "ir"   => link::OutputTypeLlvmAssembly,
646                     "bc"   => link::OutputTypeBitcode,
647                     "obj"  => link::OutputTypeObject,
648                     "link" => link::OutputTypeExe,
649                     _ => {
650                         early_error(format!("unknown emission type: `{}`",
651                                             part).as_slice())
652                     }
653                 };
654                 output_types.push(output_type)
655             }
656         }
657     };
658     output_types.as_mut_slice().sort();
659     output_types.dedup();
660     if output_types.len() == 0 {
661         output_types.push(link::OutputTypeExe);
662     }
663
664     let sysroot_opt = matches.opt_str("sysroot").map(|m| Path::new(m));
665     let target = matches.opt_str("target").unwrap_or(
666         driver::host_triple().to_string());
667     let opt_level = {
668         if (debugging_opts & NO_OPT) != 0 {
669             No
670         } else if matches.opt_present("O") {
671             if matches.opt_present("opt-level") {
672                 early_error("-O and --opt-level both provided");
673             }
674             Default
675         } else if matches.opt_present("opt-level") {
676             match matches.opt_str("opt-level").as_ref().map(|s| s.as_slice()) {
677                 None      |
678                 Some("0") => No,
679                 Some("1") => Less,
680                 Some("2") => Default,
681                 Some("3") => Aggressive,
682                 Some(arg) => {
683                     early_error(format!("optimization level needs to be \
684                                          between 0-3 (instead was `{}`)",
685                                         arg).as_slice());
686                 }
687             }
688         } else {
689             No
690         }
691     };
692     let gc = debugging_opts & GC != 0;
693     let debuginfo = if matches.opt_present("g") {
694         if matches.opt_present("debuginfo") {
695             early_error("-g and --debuginfo both provided");
696         }
697         FullDebugInfo
698     } else if matches.opt_present("debuginfo") {
699         match matches.opt_str("debuginfo").as_ref().map(|s| s.as_slice()) {
700             Some("0") => NoDebugInfo,
701             Some("1") => LimitedDebugInfo,
702             None      |
703             Some("2") => FullDebugInfo,
704             Some(arg) => {
705                 early_error(format!("optimization level needs to be between \
706                                      0-3 (instead was `{}`)",
707                                     arg).as_slice());
708             }
709         }
710     } else {
711         NoDebugInfo
712     };
713
714     let addl_lib_search_paths = matches.opt_strs("L").iter().map(|s| {
715         Path::new(s.as_slice())
716     }).collect();
717
718     let cfg = parse_cfgspecs(matches.opt_strs("cfg"));
719     let test = matches.opt_present("test");
720     let write_dependency_info = (matches.opt_present("dep-info"),
721                                  matches.opt_str("dep-info")
722                                         .map(|p| Path::new(p)));
723
724     let print_metas = (matches.opt_present("print-crate-name"),
725                        matches.opt_present("print-file-name") ||
726                        matches.opt_present("crate-file-name"));
727     if matches.opt_present("crate-file-name") {
728         early_warn("the --crate-file-name argument has been renamed to \
729                     --print-file-name");
730     }
731     let cg = build_codegen_options(matches);
732
733     let color = match matches.opt_str("color").as_ref().map(|s| s.as_slice()) {
734         Some("auto")   => Auto,
735         Some("always") => Always,
736         Some("never")  => Never,
737
738         None => Auto,
739
740         Some(arg) => {
741             early_error(format!("argument for --color must be auto, always \
742                                  or never (instead was `{}`)",
743                                 arg).as_slice())
744         }
745     };
746
747     let mut externs = HashMap::new();
748     for arg in matches.opt_strs("extern").iter() {
749         let mut parts = arg.as_slice().splitn('=', 1);
750         let name = match parts.next() {
751             Some(s) => s,
752             None => early_error("--extern value must not be empty"),
753         };
754         let location = match parts.next() {
755             Some(s) => s,
756             None => early_error("--extern value must be of the format `foo=bar`"),
757         };
758         let locs = externs.find_or_insert(name.to_string(), Vec::new());
759         locs.push(location.to_string());
760     }
761
762     let crate_name = matches.opt_str("crate-name");
763
764     Options {
765         crate_types: crate_types,
766         gc: gc,
767         optimize: opt_level,
768         debuginfo: debuginfo,
769         lint_opts: lint_opts,
770         describe_lints: describe_lints,
771         output_types: output_types,
772         addl_lib_search_paths: RefCell::new(addl_lib_search_paths),
773         maybe_sysroot: sysroot_opt,
774         target_triple: target,
775         cfg: cfg,
776         test: test,
777         parse_only: parse_only,
778         no_trans: no_trans,
779         no_analysis: no_analysis,
780         debugging_opts: debugging_opts,
781         write_dependency_info: write_dependency_info,
782         print_metas: print_metas,
783         cg: cg,
784         color: color,
785         externs: externs,
786         crate_name: crate_name,
787     }
788 }
789
790 impl fmt::Show for CrateType {
791     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
792         match *self {
793             CrateTypeExecutable => "bin".fmt(f),
794             CrateTypeDylib => "dylib".fmt(f),
795             CrateTypeRlib => "rlib".fmt(f),
796             CrateTypeStaticlib => "staticlib".fmt(f)
797         }
798     }
799 }
800
801 #[cfg(test)]
802 mod test {
803
804     use driver::config::{build_configuration, optgroups, build_session_options};
805     use driver::session::build_session;
806
807     use getopts::getopts;
808     use syntax::attr;
809     use syntax::attr::AttrMetaMethods;
810
811     // When the user supplies --test we should implicitly supply --cfg test
812     #[test]
813     fn test_switch_implies_cfg_test() {
814         let matches =
815             &match getopts(["--test".to_string()], optgroups().as_slice()) {
816               Ok(m) => m,
817               Err(f) => fail!("test_switch_implies_cfg_test: {}", f)
818             };
819         let sessopts = build_session_options(matches);
820         let sess = build_session(sessopts, None);
821         let cfg = build_configuration(&sess);
822         assert!((attr::contains_name(cfg.as_slice(), "test")));
823     }
824
825     // When the user supplies --test and --cfg test, don't implicitly add
826     // another --cfg test
827     #[test]
828     fn test_switch_implies_cfg_test_unless_cfg_test() {
829         let matches =
830             &match getopts(["--test".to_string(), "--cfg=test".to_string()],
831                            optgroups().as_slice()) {
832               Ok(m) => m,
833               Err(f) => {
834                 fail!("test_switch_implies_cfg_test_unless_cfg_test: {}", f)
835               }
836             };
837         let sessopts = build_session_options(matches);
838         let sess = build_session(sessopts, None);
839         let cfg = build_configuration(&sess);
840         let mut test_items = cfg.iter().filter(|m| m.name().equiv(&("test")));
841         assert!(test_items.next().is_some());
842         assert!(test_items.next().is_none());
843     }
844 }