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