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