]> git.lizzy.rs Git - rust.git/blob - src/librustc/driver/config.rs
auto merge of #15668 : steveklabnik/rust/tree_set_example, 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 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     metadata: Vec<String> = (Vec::new(), parse_list,
340          "metadata to mangle symbol names with"),
341     extra_filename: String = ("".to_string(), parse_string,
342          "extra data to put in each output filename"),
343 )
344
345 pub fn build_codegen_options(matches: &getopts::Matches) -> CodegenOptions
346 {
347     let mut cg = basic_codegen_options();
348     for option in matches.opt_strs("C").move_iter() {
349         let mut iter = option.as_slice().splitn('=', 1);
350         let key = iter.next().unwrap();
351         let value = iter.next();
352         let option_to_lookup = key.replace("-", "_");
353         let mut found = false;
354         for &(candidate, setter, _) in CG_OPTIONS.iter() {
355             if option_to_lookup.as_slice() != candidate { continue }
356             if !setter(&mut cg, value) {
357                 match value {
358                     Some(..) => {
359                         early_error(format!("codegen option `{}` takes no \
360                                              value", key).as_slice())
361                     }
362                     None => {
363                         early_error(format!("codegen option `{0}` requires \
364                                              a value (-C {0}=<value>)",
365                                             key).as_slice())
366                     }
367                 }
368             }
369             found = true;
370             break;
371         }
372         if !found {
373             early_error(format!("unknown codegen option: `{}`",
374                                 key).as_slice());
375         }
376     }
377     return cg;
378 }
379
380 pub fn default_lib_output() -> CrateType {
381     CrateTypeRlib
382 }
383
384 pub fn default_configuration(sess: &Session) -> ast::CrateConfig {
385     let tos = match sess.targ_cfg.os {
386         abi::OsWin32 =>   InternedString::new("win32"),
387         abi::OsMacos =>   InternedString::new("macos"),
388         abi::OsLinux =>   InternedString::new("linux"),
389         abi::OsAndroid => InternedString::new("android"),
390         abi::OsFreebsd => InternedString::new("freebsd"),
391         abi::OsiOS =>     InternedString::new("ios"),
392     };
393
394     // ARM is bi-endian, however using NDK seems to default
395     // to little-endian unless a flag is provided.
396     let (end,arch,wordsz) = match sess.targ_cfg.arch {
397         abi::X86 =>    ("little", "x86",    "32"),
398         abi::X86_64 => ("little", "x86_64", "64"),
399         abi::Arm =>    ("little", "arm",    "32"),
400         abi::Mips =>   ("big",    "mips",   "32"),
401         abi::Mipsel => ("little", "mipsel", "32")
402     };
403
404     let fam = match sess.targ_cfg.os {
405         abi::OsWin32 => InternedString::new("windows"),
406         _ => InternedString::new("unix")
407     };
408
409     let mk = attr::mk_name_value_item_str;
410     return vec!(// Target bindings.
411          attr::mk_word_item(fam.clone()),
412          mk(InternedString::new("target_os"), tos),
413          mk(InternedString::new("target_family"), fam),
414          mk(InternedString::new("target_arch"), InternedString::new(arch)),
415          mk(InternedString::new("target_endian"), InternedString::new(end)),
416          mk(InternedString::new("target_word_size"),
417             InternedString::new(wordsz))
418     );
419 }
420
421 pub fn append_configuration(cfg: &mut ast::CrateConfig,
422                             name: InternedString) {
423     if !cfg.iter().any(|mi| mi.name() == name) {
424         cfg.push(attr::mk_word_item(name))
425     }
426 }
427
428 pub fn build_configuration(sess: &Session) -> ast::CrateConfig {
429     // Combine the configuration requested by the session (command line) with
430     // some default and generated configuration items
431     let default_cfg = default_configuration(sess);
432     let mut user_cfg = sess.opts.cfg.clone();
433     // If the user wants a test runner, then add the test cfg
434     if sess.opts.test {
435         append_configuration(&mut user_cfg, InternedString::new("test"))
436     }
437     // If the user requested GC, then add the GC cfg
438     append_configuration(&mut user_cfg, if sess.opts.gc {
439         InternedString::new("gc")
440     } else {
441         InternedString::new("nogc")
442     });
443     user_cfg.move_iter().collect::<Vec<_>>().append(default_cfg.as_slice())
444 }
445
446 pub fn get_os(triple: &str) -> Option<abi::Os> {
447     for &(name, os) in os_names.iter() {
448         if triple.contains(name) { return Some(os) }
449     }
450     None
451 }
452 static os_names : &'static [(&'static str, abi::Os)] = &[
453     ("mingw32", abi::OsWin32),
454     ("win32",   abi::OsWin32),
455     ("darwin",  abi::OsMacos),
456     ("android", abi::OsAndroid),
457     ("linux",   abi::OsLinux),
458     ("freebsd", abi::OsFreebsd),
459     ("ios",     abi::OsiOS)];
460
461 pub fn get_arch(triple: &str) -> Option<abi::Architecture> {
462     for &(arch, abi) in architecture_abis.iter() {
463         if triple.contains(arch) { return Some(abi) }
464     }
465     None
466 }
467 static architecture_abis : &'static [(&'static str, abi::Architecture)] = &[
468     ("i386",   abi::X86),
469     ("i486",   abi::X86),
470     ("i586",   abi::X86),
471     ("i686",   abi::X86),
472     ("i786",   abi::X86),
473
474     ("x86_64", abi::X86_64),
475
476     ("arm",    abi::Arm),
477     ("xscale", abi::Arm),
478     ("thumb",  abi::Arm),
479
480     ("mipsel", abi::Mipsel),
481     ("mips",   abi::Mips)];
482
483 pub fn build_target_config(sopts: &Options) -> Config {
484     let os = match get_os(sopts.target_triple.as_slice()) {
485       Some(os) => os,
486       None => early_error("unknown operating system")
487     };
488     let arch = match get_arch(sopts.target_triple.as_slice()) {
489       Some(arch) => arch,
490       None => {
491           early_error(format!("unknown architecture: {}",
492                               sopts.target_triple.as_slice()).as_slice())
493       }
494     };
495     let (int_type, uint_type) = match arch {
496       abi::X86 => (ast::TyI32, ast::TyU32),
497       abi::X86_64 => (ast::TyI64, ast::TyU64),
498       abi::Arm => (ast::TyI32, ast::TyU32),
499       abi::Mips => (ast::TyI32, ast::TyU32),
500       abi::Mipsel => (ast::TyI32, ast::TyU32)
501     };
502     let target_triple = sopts.target_triple.clone();
503     let target_strs = match arch {
504       abi::X86 => x86::get_target_strs(target_triple, os),
505       abi::X86_64 => x86_64::get_target_strs(target_triple, os),
506       abi::Arm => arm::get_target_strs(target_triple, os),
507       abi::Mips => mips::get_target_strs(target_triple, os),
508       abi::Mipsel => mipsel::get_target_strs(target_triple, os)
509     };
510     Config {
511         os: os,
512         arch: arch,
513         target_strs: target_strs,
514         int_type: int_type,
515         uint_type: uint_type,
516     }
517 }
518
519 // rustc command line options
520 pub fn optgroups() -> Vec<getopts::OptGroup> {
521     vec!(
522         optflag("h", "help", "Display this message"),
523         optmulti("", "cfg", "Configure the compilation environment", "SPEC"),
524         optmulti("L", "",   "Add a directory to the library search path", "PATH"),
525         optmulti("", "crate-type", "Comma separated list of types of crates
526                                     for the compiler to emit",
527                  "[bin|lib|rlib|dylib|staticlib]"),
528         optmulti("", "emit", "Comma separated list of types of output for the compiler to emit",
529                  "[asm|bc|ir|obj|link]"),
530         optopt("", "crate-name", "Specify the name of the crate being built",
531                "NAME"),
532         optflag("", "print-crate-name", "Output the crate name and exit"),
533         optflag("", "print-file-name", "Output the file(s) that would be written if compilation \
534               continued and exit"),
535         optflag("", "crate-file-name", "deprecated in favor of --print-file-name"),
536         optflag("g",  "",  "Equivalent to --debuginfo=2"),
537         optopt("",  "debuginfo",  "Emit DWARF debug info to the objects created:
538              0 = no debug info,
539              1 = line-tables only (for stacktraces and breakpoints),
540              2 = full debug info with variable and type information (same as -g)", "LEVEL"),
541         optflag("", "no-trans", "Run all passes except translation; no output"),
542         optflag("", "no-analysis",
543               "Parse and expand the source, but run no analysis and produce no output"),
544         optflag("O", "", "Equivalent to --opt-level=2"),
545         optopt("o", "", "Write output to <filename>", "FILENAME"),
546         optopt("", "opt-level", "Optimize with possible levels 0-3", "LEVEL"),
547         optopt( "",  "out-dir", "Write output to compiler-chosen filename in <dir>", "DIR"),
548         optflag("", "parse-only", "Parse only; do not compile, assemble, or link"),
549         optopt("", "explain", "Provide a detailed explanation of an error message", "OPT"),
550         optflagopt("", "pretty",
551                    "Pretty-print the input instead of compiling;
552                    valid types are: `normal` (un-annotated source),
553                    `expanded` (crates expanded),
554                    `typed` (crates expanded, with type annotations),
555                    `expanded,identified` (fully parenthesized, AST nodes with IDs), or
556                    `flowgraph=<nodeid>` (graphviz formatted flowgraph for node)",
557                  "TYPE"),
558         optflagopt("", "dep-info",
559                  "Output dependency info to <filename> after compiling, \
560                   in a format suitable for use by Makefiles", "FILENAME"),
561         optopt("", "sysroot", "Override the system root", "PATH"),
562         optflag("", "test", "Build a test harness"),
563         optopt("", "target", "Target triple cpu-manufacturer-kernel[-os]
564                             to compile for (see chapter 3.4 of http://www.sourceware.org/autobook/
565                             for details)", "TRIPLE"),
566         optmulti("W", "warn", "Set lint warnings", "OPT"),
567         optmulti("A", "allow", "Set lint allowed", "OPT"),
568         optmulti("D", "deny", "Set lint denied", "OPT"),
569         optmulti("F", "forbid", "Set lint forbidden", "OPT"),
570         optmulti("C", "codegen", "Set a codegen option", "OPT[=VALUE]"),
571         optmulti("Z", "", "Set internal debugging options", "FLAG"),
572         optflagopt("v", "version", "Print version info and exit", "verbose"),
573         optopt("", "color", "Configure coloring of output:
574             auto   = colorize, if output goes to a tty (default);
575             always = always colorize output;
576             never  = never colorize output", "auto|always|never"),
577         optmulti("", "extern", "Specify where an external rust library is located",
578                  "PATH"),
579     )
580 }
581
582
583 // Convert strings provided as --cfg [cfgspec] into a crate_cfg
584 fn parse_cfgspecs(cfgspecs: Vec<String> ) -> ast::CrateConfig {
585     cfgspecs.move_iter().map(|s| {
586         parse::parse_meta_from_source_str("cfgspec".to_string(),
587                                           s.to_string(),
588                                           Vec::new(),
589                                           &parse::new_parse_sess())
590     }).collect::<ast::CrateConfig>()
591 }
592
593 pub fn build_session_options(matches: &getopts::Matches) -> Options {
594     let mut crate_types: Vec<CrateType> = Vec::new();
595     let unparsed_crate_types = matches.opt_strs("crate-type");
596     for unparsed_crate_type in unparsed_crate_types.iter() {
597         for part in unparsed_crate_type.as_slice().split(',') {
598             let new_part = match part {
599                 "lib"       => default_lib_output(),
600                 "rlib"      => CrateTypeRlib,
601                 "staticlib" => CrateTypeStaticlib,
602                 "dylib"     => CrateTypeDylib,
603                 "bin"       => CrateTypeExecutable,
604                 _ => {
605                     early_error(format!("unknown crate type: `{}`",
606                                         part).as_slice())
607                 }
608             };
609             crate_types.push(new_part)
610         }
611     }
612
613     let parse_only = matches.opt_present("parse-only");
614     let no_trans = matches.opt_present("no-trans");
615     let no_analysis = matches.opt_present("no-analysis");
616
617     let mut lint_opts = vec!();
618     let mut describe_lints = false;
619
620     for &level in [lint::Allow, lint::Warn, lint::Deny, lint::Forbid].iter() {
621         for lint_name in matches.opt_strs(level.as_str()).move_iter() {
622             if lint_name.as_slice() == "help" {
623                 describe_lints = true;
624             } else {
625                 lint_opts.push((lint_name.replace("-", "_").into_string(), level));
626             }
627         }
628     }
629
630     let mut debugging_opts = 0;
631     let debug_flags = matches.opt_strs("Z");
632     let debug_map = debugging_opts_map();
633     for debug_flag in debug_flags.iter() {
634         let mut this_bit = 0;
635         for tuple in debug_map.iter() {
636             let (name, bit) = match *tuple { (ref a, _, b) => (a, b) };
637             if *name == debug_flag.as_slice() {
638                 this_bit = bit;
639                 break;
640             }
641         }
642         if this_bit == 0 {
643             early_error(format!("unknown debug flag: {}",
644                                 *debug_flag).as_slice())
645         }
646         debugging_opts |= this_bit;
647     }
648
649     if debugging_opts & DEBUG_LLVM != 0 {
650         unsafe { llvm::LLVMSetDebug(1); }
651     }
652
653     let mut output_types = Vec::new();
654     if !parse_only && !no_trans {
655         let unparsed_output_types = matches.opt_strs("emit");
656         for unparsed_output_type in unparsed_output_types.iter() {
657             for part in unparsed_output_type.as_slice().split(',') {
658                 let output_type = match part.as_slice() {
659                     "asm"  => link::OutputTypeAssembly,
660                     "ir"   => link::OutputTypeLlvmAssembly,
661                     "bc"   => link::OutputTypeBitcode,
662                     "obj"  => link::OutputTypeObject,
663                     "link" => link::OutputTypeExe,
664                     _ => {
665                         early_error(format!("unknown emission type: `{}`",
666                                             part).as_slice())
667                     }
668                 };
669                 output_types.push(output_type)
670             }
671         }
672     };
673     output_types.as_mut_slice().sort();
674     output_types.dedup();
675     if output_types.len() == 0 {
676         output_types.push(link::OutputTypeExe);
677     }
678
679     let sysroot_opt = matches.opt_str("sysroot").map(|m| Path::new(m));
680     let target = matches.opt_str("target").unwrap_or(
681         driver::host_triple().to_string());
682     let opt_level = {
683         if (debugging_opts & NO_OPT) != 0 {
684             No
685         } else if matches.opt_present("O") {
686             if matches.opt_present("opt-level") {
687                 early_error("-O and --opt-level both provided");
688             }
689             Default
690         } else if matches.opt_present("opt-level") {
691             match matches.opt_str("opt-level").as_ref().map(|s| s.as_slice()) {
692                 None      |
693                 Some("0") => No,
694                 Some("1") => Less,
695                 Some("2") => Default,
696                 Some("3") => Aggressive,
697                 Some(arg) => {
698                     early_error(format!("optimization level needs to be \
699                                          between 0-3 (instead was `{}`)",
700                                         arg).as_slice());
701                 }
702             }
703         } else {
704             No
705         }
706     };
707     let gc = debugging_opts & GC != 0;
708     let debuginfo = if matches.opt_present("g") {
709         if matches.opt_present("debuginfo") {
710             early_error("-g and --debuginfo both provided");
711         }
712         FullDebugInfo
713     } else if matches.opt_present("debuginfo") {
714         match matches.opt_str("debuginfo").as_ref().map(|s| s.as_slice()) {
715             Some("0") => NoDebugInfo,
716             Some("1") => LimitedDebugInfo,
717             None      |
718             Some("2") => FullDebugInfo,
719             Some(arg) => {
720                 early_error(format!("optimization level needs to be between \
721                                      0-3 (instead was `{}`)",
722                                     arg).as_slice());
723             }
724         }
725     } else {
726         NoDebugInfo
727     };
728
729     let addl_lib_search_paths = matches.opt_strs("L").iter().map(|s| {
730         Path::new(s.as_slice())
731     }).collect();
732
733     let cfg = parse_cfgspecs(matches.opt_strs("cfg"));
734     let test = matches.opt_present("test");
735     let write_dependency_info = (matches.opt_present("dep-info"),
736                                  matches.opt_str("dep-info")
737                                         .map(|p| Path::new(p)));
738
739     let print_metas = (matches.opt_present("print-crate-name"),
740                        matches.opt_present("print-file-name") ||
741                        matches.opt_present("crate-file-name"));
742     if matches.opt_present("crate-file-name") {
743         early_warn("the --crate-file-name argument has been renamed to \
744                     --print-file-name");
745     }
746     let cg = build_codegen_options(matches);
747
748     let color = match matches.opt_str("color").as_ref().map(|s| s.as_slice()) {
749         Some("auto")   => Auto,
750         Some("always") => Always,
751         Some("never")  => Never,
752
753         None => Auto,
754
755         Some(arg) => {
756             early_error(format!("argument for --color must be auto, always \
757                                  or never (instead was `{}`)",
758                                 arg).as_slice())
759         }
760     };
761
762     let mut externs = HashMap::new();
763     for arg in matches.opt_strs("extern").iter() {
764         let mut parts = arg.as_slice().splitn('=', 1);
765         let name = match parts.next() {
766             Some(s) => s,
767             None => early_error("--extern value must not be empty"),
768         };
769         let location = match parts.next() {
770             Some(s) => s,
771             None => early_error("--extern value must be of the format `foo=bar`"),
772         };
773         let locs = externs.find_or_insert(name.to_string(), Vec::new());
774         locs.push(location.to_string());
775     }
776
777     let crate_name = matches.opt_str("crate-name");
778
779     Options {
780         crate_types: crate_types,
781         gc: gc,
782         optimize: opt_level,
783         debuginfo: debuginfo,
784         lint_opts: lint_opts,
785         describe_lints: describe_lints,
786         output_types: output_types,
787         addl_lib_search_paths: RefCell::new(addl_lib_search_paths),
788         maybe_sysroot: sysroot_opt,
789         target_triple: target,
790         cfg: cfg,
791         test: test,
792         parse_only: parse_only,
793         no_trans: no_trans,
794         no_analysis: no_analysis,
795         debugging_opts: debugging_opts,
796         write_dependency_info: write_dependency_info,
797         print_metas: print_metas,
798         cg: cg,
799         color: color,
800         externs: externs,
801         crate_name: crate_name,
802     }
803 }
804
805 impl fmt::Show for CrateType {
806     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
807         match *self {
808             CrateTypeExecutable => "bin".fmt(f),
809             CrateTypeDylib => "dylib".fmt(f),
810             CrateTypeRlib => "rlib".fmt(f),
811             CrateTypeStaticlib => "staticlib".fmt(f)
812         }
813     }
814 }
815
816 #[cfg(test)]
817 mod test {
818
819     use driver::config::{build_configuration, optgroups, build_session_options};
820     use driver::session::build_session;
821
822     use getopts::getopts;
823     use syntax::attr;
824     use syntax::attr::AttrMetaMethods;
825     use syntax::diagnostics;
826
827     // When the user supplies --test we should implicitly supply --cfg test
828     #[test]
829     fn test_switch_implies_cfg_test() {
830         let matches =
831             &match getopts(["--test".to_string()], optgroups().as_slice()) {
832               Ok(m) => m,
833               Err(f) => fail!("test_switch_implies_cfg_test: {}", f)
834             };
835         let registry = diagnostics::registry::Registry::new([]);
836         let sessopts = build_session_options(matches);
837         let sess = build_session(sessopts, None, registry);
838         let cfg = build_configuration(&sess);
839         assert!((attr::contains_name(cfg.as_slice(), "test")));
840     }
841
842     // When the user supplies --test and --cfg test, don't implicitly add
843     // another --cfg test
844     #[test]
845     fn test_switch_implies_cfg_test_unless_cfg_test() {
846         let matches =
847             &match getopts(["--test".to_string(), "--cfg=test".to_string()],
848                            optgroups().as_slice()) {
849               Ok(m) => m,
850               Err(f) => {
851                 fail!("test_switch_implies_cfg_test_unless_cfg_test: {}", f)
852               }
853             };
854         let registry = diagnostics::registry::Registry::new([]);
855         let sessopts = build_session_options(matches);
856         let sess = build_session(sessopts, None, registry);
857         let cfg = build_configuration(&sess);
858         let mut test_items = cfg.iter().filter(|m| m.name().equiv(&("test")));
859         assert!(test_items.next().is_some());
860         assert!(test_items.next().is_none());
861     }
862 }