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