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