]> git.lizzy.rs Git - rust.git/blob - src/librustc/session/config.rs
dcc4ca137ead99fd72bc3e09088e63cc9408af90
[rust.git] / src / librustc / session / 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 pub use self::EntryFnType::*;
15 pub use self::CrateType::*;
16 pub use self::Passes::*;
17 pub use self::OptLevel::*;
18 pub use self::OutputType::*;
19 pub use self::DebugInfoLevel::*;
20
21 use session::{early_error, early_warn, Session};
22 use session::search_paths::SearchPaths;
23
24 use rustc_back::target::Target;
25 use lint;
26 use metadata::cstore;
27
28 use syntax::ast::{self, IntTy, UintTy};
29 use syntax::attr;
30 use syntax::attr::AttrMetaMethods;
31 use syntax::diagnostic::{ColorConfig, Auto, Always, Never, SpanHandler};
32 use syntax::parse;
33 use syntax::parse::token::InternedString;
34 use syntax::feature_gate::UnstableFeatures;
35
36 use getopts;
37 use std::collections::HashMap;
38 use std::env;
39 use std::fmt;
40 use std::path::PathBuf;
41
42 use llvm;
43
44 pub struct Config {
45     pub target: Target,
46     pub int_type: IntTy,
47     pub uint_type: UintTy,
48 }
49
50 #[derive(Clone, Copy, PartialEq)]
51 pub enum OptLevel {
52     No, // -O0
53     Less, // -O1
54     Default, // -O2
55     Aggressive // -O3
56 }
57
58 #[derive(Clone, Copy, PartialEq)]
59 pub enum DebugInfoLevel {
60     NoDebugInfo,
61     LimitedDebugInfo,
62     FullDebugInfo,
63 }
64
65 #[derive(Clone, Copy, PartialEq, PartialOrd, Ord, Eq)]
66 pub enum OutputType {
67     OutputTypeBitcode,
68     OutputTypeAssembly,
69     OutputTypeLlvmAssembly,
70     OutputTypeObject,
71     OutputTypeExe,
72     OutputTypeDepInfo,
73 }
74
75 #[derive(Clone)]
76 pub struct Options {
77     // The crate config requested for the session, which may be combined
78     // with additional crate configurations during the compile process
79     pub crate_types: Vec<CrateType>,
80
81     pub gc: bool,
82     pub optimize: OptLevel,
83     pub debug_assertions: bool,
84     pub debuginfo: DebugInfoLevel,
85     pub lint_opts: Vec<(String, lint::Level)>,
86     pub lint_cap: Option<lint::Level>,
87     pub describe_lints: bool,
88     pub output_types: Vec<OutputType>,
89     // This was mutable for rustpkg, which updates search paths based on the
90     // parsed code. It remains mutable in case its replacements wants to use
91     // this.
92     pub search_paths: SearchPaths,
93     pub libs: Vec<(String, cstore::NativeLibraryKind)>,
94     pub maybe_sysroot: Option<PathBuf>,
95     pub target_triple: String,
96     // User-specified cfg meta items. The compiler itself will add additional
97     // items to the crate config, and during parsing the entire crate config
98     // will be added to the crate AST node.  This should not be used for
99     // anything except building the full crate config prior to parsing.
100     pub cfg: ast::CrateConfig,
101     pub test: bool,
102     pub parse_only: bool,
103     pub no_trans: bool,
104     pub treat_err_as_bug: bool,
105     pub always_build_mir: bool,
106     pub no_analysis: bool,
107     pub debugging_opts: DebuggingOptions,
108     /// Whether to write dependency files. It's (enabled, optional filename).
109     pub write_dependency_info: (bool, Option<PathBuf>),
110     pub prints: Vec<PrintRequest>,
111     pub cg: CodegenOptions,
112     pub color: ColorConfig,
113     pub show_span: Option<String>,
114     pub externs: HashMap<String, Vec<String>>,
115     pub crate_name: Option<String>,
116     /// An optional name to use as the crate for std during std injection,
117     /// written `extern crate std = "name"`. Default to "std". Used by
118     /// out-of-tree drivers.
119     pub alt_std_name: Option<String>,
120     /// Indicates how the compiler should treat unstable features
121     pub unstable_features: UnstableFeatures
122 }
123
124 #[derive(Clone, PartialEq, Eq)]
125 pub enum PrintRequest {
126     FileNames,
127     Sysroot,
128     CrateName,
129 }
130
131 pub enum Input {
132     /// Load source from file
133     File(PathBuf),
134     /// The string is the source
135     Str(String)
136 }
137
138 impl Input {
139     pub fn filestem(&self) -> String {
140         match *self {
141             Input::File(ref ifile) => ifile.file_stem().unwrap()
142                                            .to_str().unwrap().to_string(),
143             Input::Str(_) => "rust_out".to_string(),
144         }
145     }
146 }
147
148 #[derive(Clone)]
149 pub struct OutputFilenames {
150     pub out_directory: PathBuf,
151     pub out_filestem: String,
152     pub single_output_file: Option<PathBuf>,
153     pub extra: String,
154 }
155
156 impl OutputFilenames {
157     pub fn path(&self, flavor: OutputType) -> PathBuf {
158         match self.single_output_file {
159             Some(ref path) => return path.clone(),
160             None => {}
161         }
162         self.temp_path(flavor)
163     }
164
165     pub fn temp_path(&self, flavor: OutputType) -> PathBuf {
166         let base = self.out_directory.join(&self.filestem());
167         match flavor {
168             OutputTypeBitcode => base.with_extension("bc"),
169             OutputTypeAssembly => base.with_extension("s"),
170             OutputTypeLlvmAssembly => base.with_extension("ll"),
171             OutputTypeObject => base.with_extension("o"),
172             OutputTypeDepInfo => base.with_extension("d"),
173             OutputTypeExe => base,
174         }
175     }
176
177     pub fn with_extension(&self, extension: &str) -> PathBuf {
178         self.out_directory.join(&self.filestem()).with_extension(extension)
179     }
180
181     pub fn filestem(&self) -> String {
182         format!("{}{}", self.out_filestem, self.extra)
183     }
184 }
185
186 pub fn host_triple() -> &'static str {
187     // Get the host triple out of the build environment. This ensures that our
188     // idea of the host triple is the same as for the set of libraries we've
189     // actually built.  We can't just take LLVM's host triple because they
190     // normalize all ix86 architectures to i386.
191     //
192     // Instead of grabbing the host triple (for the current host), we grab (at
193     // compile time) the target triple that this rustc is built with and
194     // calling that (at runtime) the host triple.
195     (option_env!("CFG_COMPILER_HOST_TRIPLE")).
196         expect("CFG_COMPILER_HOST_TRIPLE")
197 }
198
199 /// Some reasonable defaults
200 pub fn basic_options() -> Options {
201     Options {
202         crate_types: Vec::new(),
203         gc: false,
204         optimize: No,
205         debuginfo: NoDebugInfo,
206         lint_opts: Vec::new(),
207         lint_cap: None,
208         describe_lints: false,
209         output_types: Vec::new(),
210         search_paths: SearchPaths::new(),
211         maybe_sysroot: None,
212         target_triple: host_triple().to_string(),
213         cfg: Vec::new(),
214         test: false,
215         parse_only: false,
216         no_trans: false,
217         treat_err_as_bug: false,
218         always_build_mir: false,
219         no_analysis: false,
220         debugging_opts: basic_debugging_options(),
221         write_dependency_info: (false, None),
222         prints: Vec::new(),
223         cg: basic_codegen_options(),
224         color: Auto,
225         show_span: None,
226         externs: HashMap::new(),
227         crate_name: None,
228         alt_std_name: None,
229         libs: Vec::new(),
230         unstable_features: UnstableFeatures::Disallow,
231         debug_assertions: true,
232     }
233 }
234
235 // The type of entry function, so
236 // users can have their own entry
237 // functions that don't start a
238 // scheduler
239 #[derive(Copy, Clone, PartialEq)]
240 pub enum EntryFnType {
241     EntryMain,
242     EntryStart,
243     EntryNone,
244 }
245
246 #[derive(Copy, PartialEq, PartialOrd, Clone, Ord, Eq, Hash, Debug)]
247 pub enum CrateType {
248     CrateTypeExecutable,
249     CrateTypeDylib,
250     CrateTypeRlib,
251     CrateTypeStaticlib,
252 }
253
254 #[derive(Clone)]
255 pub enum Passes {
256     SomePasses(Vec<String>),
257     AllPasses,
258 }
259
260 impl Passes {
261     pub fn is_empty(&self) -> bool {
262         match *self {
263             SomePasses(ref v) => v.is_empty(),
264             AllPasses => false,
265         }
266     }
267 }
268
269 /// Declare a macro that will define all CodegenOptions/DebuggingOptions fields and parsers all
270 /// at once. The goal of this macro is to define an interface that can be
271 /// programmatically used by the option parser in order to initialize the struct
272 /// without hardcoding field names all over the place.
273 ///
274 /// The goal is to invoke this macro once with the correct fields, and then this
275 /// macro generates all necessary code. The main gotcha of this macro is the
276 /// cgsetters module which is a bunch of generated code to parse an option into
277 /// its respective field in the struct. There are a few hand-written parsers for
278 /// parsing specific types of values in this module.
279 macro_rules! options {
280     ($struct_name:ident, $setter_name:ident, $defaultfn:ident,
281      $buildfn:ident, $prefix:expr, $outputname:expr,
282      $stat:ident, $mod_desc:ident, $mod_set:ident,
283      $($opt:ident : $t:ty = ($init:expr, $parse:ident, $desc:expr)),* ,) =>
284 (
285     #[derive(Clone)]
286     pub struct $struct_name { $(pub $opt: $t),* }
287
288     pub fn $defaultfn() -> $struct_name {
289         $struct_name { $($opt: $init),* }
290     }
291
292     pub fn $buildfn(matches: &getopts::Matches, color: ColorConfig) -> $struct_name
293     {
294         let mut op = $defaultfn();
295         for option in matches.opt_strs($prefix) {
296             let mut iter = option.splitn(2, '=');
297             let key = iter.next().unwrap();
298             let value = iter.next();
299             let option_to_lookup = key.replace("-", "_");
300             let mut found = false;
301             for &(candidate, setter, opt_type_desc, _) in $stat {
302                 if option_to_lookup != candidate { continue }
303                 if !setter(&mut op, value) {
304                     match (value, opt_type_desc) {
305                         (Some(..), None) => {
306                             early_error(color, &format!("{} option `{}` takes no \
307                                                          value", $outputname, key))
308                         }
309                         (None, Some(type_desc)) => {
310                             early_error(color, &format!("{0} option `{1}` requires \
311                                                          {2} ({3} {1}=<value>)",
312                                                         $outputname, key,
313                                                         type_desc, $prefix))
314                         }
315                         (Some(value), Some(type_desc)) => {
316                             early_error(color, &format!("incorrect value `{}` for {} \
317                                                          option `{}` - {} was expected",
318                                                         value, $outputname,
319                                                         key, type_desc))
320                         }
321                         (None, None) => unreachable!()
322                     }
323                 }
324                 found = true;
325                 break;
326             }
327             if !found {
328                 early_error(color, &format!("unknown {} option: `{}`",
329                                             $outputname, key));
330             }
331         }
332         return op;
333     }
334
335     pub type $setter_name = fn(&mut $struct_name, v: Option<&str>) -> bool;
336     pub const $stat: &'static [(&'static str, $setter_name,
337                                      Option<&'static str>, &'static str)] =
338         &[ $( (stringify!($opt), $mod_set::$opt, $mod_desc::$parse, $desc) ),* ];
339
340     #[allow(non_upper_case_globals, dead_code)]
341     mod $mod_desc {
342         pub const parse_bool: Option<&'static str> = None;
343         pub const parse_opt_bool: Option<&'static str> =
344             Some("one of: `y`, `yes`, `on`, `n`, `no`, or `off`");
345         pub const parse_string: Option<&'static str> = Some("a string");
346         pub const parse_opt_string: Option<&'static str> = Some("a string");
347         pub const parse_list: Option<&'static str> = Some("a space-separated list of strings");
348         pub const parse_opt_list: Option<&'static str> = Some("a space-separated list of strings");
349         pub const parse_uint: Option<&'static str> = Some("a number");
350         pub const parse_passes: Option<&'static str> =
351             Some("a space-separated list of passes, or `all`");
352         pub const parse_opt_uint: Option<&'static str> =
353             Some("a number");
354     }
355
356     #[allow(dead_code)]
357     mod $mod_set {
358         use super::{$struct_name, Passes, SomePasses, AllPasses};
359
360         $(
361             pub fn $opt(cg: &mut $struct_name, v: Option<&str>) -> bool {
362                 $parse(&mut cg.$opt, v)
363             }
364         )*
365
366         fn parse_bool(slot: &mut bool, v: Option<&str>) -> bool {
367             match v {
368                 Some(..) => false,
369                 None => { *slot = true; true }
370             }
371         }
372
373         fn parse_opt_bool(slot: &mut Option<bool>, v: Option<&str>) -> bool {
374             match v {
375                 Some(s) => {
376                     match s {
377                         "n" | "no" | "off" => {
378                             *slot = Some(false);
379                         }
380                         "y" | "yes" | "on" => {
381                             *slot = Some(true);
382                         }
383                         _ => { return false; }
384                     }
385
386                     true
387                 },
388                 None => { *slot = Some(true); true }
389             }
390         }
391
392         fn parse_opt_string(slot: &mut Option<String>, v: Option<&str>) -> bool {
393             match v {
394                 Some(s) => { *slot = Some(s.to_string()); true },
395                 None => false,
396             }
397         }
398
399         fn parse_string(slot: &mut String, v: Option<&str>) -> bool {
400             match v {
401                 Some(s) => { *slot = s.to_string(); true },
402                 None => false,
403             }
404         }
405
406         fn parse_list(slot: &mut Vec<String>, v: Option<&str>)
407                       -> bool {
408             match v {
409                 Some(s) => {
410                     for s in s.split_whitespace() {
411                         slot.push(s.to_string());
412                     }
413                     true
414                 },
415                 None => false,
416             }
417         }
418
419         fn parse_opt_list(slot: &mut Option<Vec<String>>, v: Option<&str>)
420                       -> bool {
421             match v {
422                 Some(s) => {
423                     let v = s.split_whitespace().map(|s| s.to_string()).collect();
424                     *slot = Some(v);
425                     true
426                 },
427                 None => false,
428             }
429         }
430
431         fn parse_uint(slot: &mut usize, v: Option<&str>) -> bool {
432             match v.and_then(|s| s.parse().ok()) {
433                 Some(i) => { *slot = i; true },
434                 None => false
435             }
436         }
437
438         fn parse_opt_uint(slot: &mut Option<usize>, v: Option<&str>) -> bool {
439             match v {
440                 Some(s) => { *slot = s.parse().ok(); slot.is_some() }
441                 None => { *slot = None; true }
442             }
443         }
444
445         fn parse_passes(slot: &mut Passes, v: Option<&str>) -> bool {
446             match v {
447                 Some("all") => {
448                     *slot = AllPasses;
449                     true
450                 }
451                 v => {
452                     let mut passes = vec!();
453                     if parse_list(&mut passes, v) {
454                         *slot = SomePasses(passes);
455                         true
456                     } else {
457                         false
458                     }
459                 }
460             }
461         }
462     }
463 ) }
464
465 options! {CodegenOptions, CodegenSetter, basic_codegen_options,
466          build_codegen_options, "C", "codegen",
467          CG_OPTIONS, cg_type_desc, cgsetters,
468     ar: Option<String> = (None, parse_opt_string,
469         "tool to assemble archives with"),
470     linker: Option<String> = (None, parse_opt_string,
471         "system linker to link outputs with"),
472     link_args: Option<Vec<String>> = (None, parse_opt_list,
473         "extra arguments to pass to the linker (space separated)"),
474     lto: bool = (false, parse_bool,
475         "perform LLVM link-time optimizations"),
476     target_cpu: Option<String> = (None, parse_opt_string,
477         "select target processor (llc -mcpu=help for details)"),
478     target_feature: String = ("".to_string(), parse_string,
479         "target specific attributes (llc -mattr=help for details)"),
480     passes: Vec<String> = (Vec::new(), parse_list,
481         "a list of extra LLVM passes to run (space separated)"),
482     llvm_args: Vec<String> = (Vec::new(), parse_list,
483         "a list of arguments to pass to llvm (space separated)"),
484     save_temps: bool = (false, parse_bool,
485         "save all temporary output files during compilation"),
486     rpath: bool = (false, parse_bool,
487         "set rpath values in libs/exes"),
488     no_prepopulate_passes: bool = (false, parse_bool,
489         "don't pre-populate the pass manager with a list of passes"),
490     no_vectorize_loops: bool = (false, parse_bool,
491         "don't run the loop vectorization optimization passes"),
492     no_vectorize_slp: bool = (false, parse_bool,
493         "don't run LLVM's SLP vectorization pass"),
494     soft_float: bool = (false, parse_bool,
495         "generate software floating point library calls"),
496     prefer_dynamic: bool = (false, parse_bool,
497         "prefer dynamic linking to static linking"),
498     no_integrated_as: bool = (false, parse_bool,
499         "use an external assembler rather than LLVM's integrated one"),
500     no_redzone: Option<bool> = (None, parse_opt_bool,
501         "disable the use of the redzone"),
502     relocation_model: Option<String> = (None, parse_opt_string,
503          "choose the relocation model to use (llc -relocation-model for details)"),
504     code_model: Option<String> = (None, parse_opt_string,
505          "choose the code model to use (llc -code-model for details)"),
506     metadata: Vec<String> = (Vec::new(), parse_list,
507          "metadata to mangle symbol names with"),
508     extra_filename: String = ("".to_string(), parse_string,
509          "extra data to put in each output filename"),
510     codegen_units: usize = (1, parse_uint,
511         "divide crate into N units to optimize in parallel"),
512     remark: Passes = (SomePasses(Vec::new()), parse_passes,
513         "print remarks for these optimization passes (space separated, or \"all\")"),
514     no_stack_check: bool = (false, parse_bool,
515         "disable checks for stack exhaustion (a memory-safety hazard!)"),
516     debuginfo: Option<usize> = (None, parse_opt_uint,
517         "debug info emission level, 0 = no debug info, 1 = line tables only, \
518          2 = full debug info with variable and type information"),
519     opt_level: Option<usize> = (None, parse_opt_uint,
520         "Optimize with possible levels 0-3"),
521     debug_assertions: Option<bool> = (None, parse_opt_bool,
522         "explicitly enable the cfg(debug_assertions) directive"),
523 }
524
525
526 options! {DebuggingOptions, DebuggingSetter, basic_debugging_options,
527          build_debugging_options, "Z", "debugging",
528          DB_OPTIONS, db_type_desc, dbsetters,
529     verbose: bool = (false, parse_bool,
530         "in general, enable more debug printouts"),
531     time_passes: bool = (false, parse_bool,
532         "measure time of each rustc pass"),
533     count_llvm_insns: bool = (false, parse_bool,
534         "count where LLVM instrs originate"),
535     time_llvm_passes: bool = (false, parse_bool,
536         "measure time of each LLVM pass"),
537     trans_stats: bool = (false, parse_bool,
538         "gather trans statistics"),
539     asm_comments: bool = (false, parse_bool,
540         "generate comments into the assembly (may change behavior)"),
541     no_verify: bool = (false, parse_bool,
542         "skip LLVM verification"),
543     borrowck_stats: bool = (false, parse_bool,
544         "gather borrowck statistics"),
545     no_landing_pads: bool = (false, parse_bool,
546         "omit landing pads for unwinding"),
547     debug_llvm: bool = (false, parse_bool,
548         "enable debug output from LLVM"),
549     count_type_sizes: bool = (false, parse_bool,
550         "count the sizes of aggregate types"),
551     meta_stats: bool = (false, parse_bool,
552         "gather metadata statistics"),
553     print_link_args: bool = (false, parse_bool,
554         "Print the arguments passed to the linker"),
555     gc: bool = (false, parse_bool,
556         "Garbage collect shared data (experimental)"),
557     print_llvm_passes: bool = (false, parse_bool,
558         "Prints the llvm optimization passes being run"),
559     ast_json: bool = (false, parse_bool,
560         "Print the AST as JSON and halt"),
561     ast_json_noexpand: bool = (false, parse_bool,
562         "Print the pre-expansion AST as JSON and halt"),
563     ls: bool = (false, parse_bool,
564         "List the symbols defined by a library crate"),
565     save_analysis: bool = (false, parse_bool,
566         "Write syntax and type analysis information in addition to normal output"),
567     print_move_fragments: bool = (false, parse_bool,
568         "Print out move-fragment data for every fn"),
569     flowgraph_print_loans: bool = (false, parse_bool,
570         "Include loan analysis data in --pretty flowgraph output"),
571     flowgraph_print_moves: bool = (false, parse_bool,
572         "Include move analysis data in --pretty flowgraph output"),
573     flowgraph_print_assigns: bool = (false, parse_bool,
574         "Include assignment analysis data in --pretty flowgraph output"),
575     flowgraph_print_all: bool = (false, parse_bool,
576         "Include all dataflow analysis data in --pretty flowgraph output"),
577     print_region_graph: bool = (false, parse_bool,
578          "Prints region inference graph. \
579           Use with RUST_REGION_GRAPH=help for more info"),
580     parse_only: bool = (false, parse_bool,
581           "Parse only; do not compile, assemble, or link"),
582     no_trans: bool = (false, parse_bool,
583           "Run all passes except translation; no output"),
584     treat_err_as_bug: bool = (false, parse_bool,
585           "Treat all errors that occur as bugs"),
586     always_build_mir: bool = (false, parse_bool,
587           "Always build MIR for all fns, even without a #[rustc_mir] annotation"),
588     no_analysis: bool = (false, parse_bool,
589           "Parse and expand the source, but run no analysis"),
590     extra_plugins: Vec<String> = (Vec::new(), parse_list,
591         "load extra plugins"),
592     unstable_options: bool = (false, parse_bool,
593           "Adds unstable command line options to rustc interface"),
594     print_enum_sizes: bool = (false, parse_bool,
595           "Print the size of enums and their variants"),
596     force_overflow_checks: Option<bool> = (None, parse_opt_bool,
597           "Force overflow checks on or off"),
598     force_dropflag_checks: Option<bool> = (None, parse_opt_bool,
599           "Force drop flag checks on or off"),
600     trace_macros: bool = (false, parse_bool,
601           "For every macro invocation, print its name and arguments"),
602     enable_nonzeroing_move_hints: bool = (false, parse_bool,
603           "Force nonzeroing move optimization on"),
604     keep_mtwt_tables: bool = (false, parse_bool,
605           "Don't clear the resolution tables after analysis"),
606 }
607
608 pub fn default_lib_output() -> CrateType {
609     CrateTypeRlib
610 }
611
612 pub fn default_configuration(sess: &Session) -> ast::CrateConfig {
613     use syntax::parse::token::intern_and_get_ident as intern;
614
615     let end = &sess.target.target.target_endian;
616     let arch = &sess.target.target.arch;
617     let wordsz = &sess.target.target.target_pointer_width;
618     let os = &sess.target.target.target_os;
619     let env = &sess.target.target.target_env;
620     let vendor = &sess.target.target.target_vendor;
621
622     let fam = match sess.target.target.options.is_like_windows {
623         true  => InternedString::new("windows"),
624         false => InternedString::new("unix")
625     };
626
627     let mk = attr::mk_name_value_item_str;
628     let mut ret = vec![ // Target bindings.
629          attr::mk_word_item(fam.clone()),
630          mk(InternedString::new("target_os"), intern(os)),
631          mk(InternedString::new("target_family"), fam),
632          mk(InternedString::new("target_arch"), intern(arch)),
633          mk(InternedString::new("target_endian"), intern(end)),
634          mk(InternedString::new("target_pointer_width"), intern(wordsz)),
635          mk(InternedString::new("target_env"), intern(env)),
636          mk(InternedString::new("target_vendor"), intern(vendor)),
637     ];
638     if sess.opts.debug_assertions {
639         ret.push(attr::mk_word_item(InternedString::new("debug_assertions")));
640     }
641     return ret;
642 }
643
644 pub fn append_configuration(cfg: &mut ast::CrateConfig,
645                             name: InternedString) {
646     if !cfg.iter().any(|mi| mi.name() == name) {
647         cfg.push(attr::mk_word_item(name))
648     }
649 }
650
651 pub fn build_configuration(sess: &Session) -> ast::CrateConfig {
652     // Combine the configuration requested by the session (command line) with
653     // some default and generated configuration items
654     let default_cfg = default_configuration(sess);
655     let mut user_cfg = sess.opts.cfg.clone();
656     // If the user wants a test runner, then add the test cfg
657     if sess.opts.test {
658         append_configuration(&mut user_cfg, InternedString::new("test"))
659     }
660     let mut v = user_cfg.into_iter().collect::<Vec<_>>();
661     v.push_all(&default_cfg[..]);
662     v
663 }
664
665 pub fn build_target_config(opts: &Options, sp: &SpanHandler) -> Config {
666     let target = match Target::search(&opts.target_triple) {
667         Ok(t) => t,
668         Err(e) => {
669             sp.handler().fatal(&format!("Error loading target specification: {}", e));
670         }
671     };
672
673     let (int_type, uint_type) = match &target.target_pointer_width[..] {
674         "32" => (ast::TyI32, ast::TyU32),
675         "64" => (ast::TyI64, ast::TyU64),
676         w    => sp.handler().fatal(&format!("target specification was invalid: unrecognized \
677                                              target-pointer-width {}", w))
678     };
679
680     Config {
681         target: target,
682         int_type: int_type,
683         uint_type: uint_type,
684     }
685 }
686
687 /// Returns the "short" subset of the stable rustc command line options.
688 pub fn short_optgroups() -> Vec<getopts::OptGroup> {
689     rustc_short_optgroups().into_iter()
690         .filter(|g|g.is_stable())
691         .map(|g|g.opt_group)
692         .collect()
693 }
694
695 /// Returns all of the stable rustc command line options.
696 pub fn optgroups() -> Vec<getopts::OptGroup> {
697     rustc_optgroups().into_iter()
698         .filter(|g|g.is_stable())
699         .map(|g|g.opt_group)
700         .collect()
701 }
702
703 #[derive(Copy, Clone, PartialEq, Eq, Debug)]
704 pub enum OptionStability { Stable, Unstable }
705
706 #[derive(Clone, PartialEq, Eq)]
707 pub struct RustcOptGroup {
708     pub opt_group: getopts::OptGroup,
709     pub stability: OptionStability,
710 }
711
712 impl RustcOptGroup {
713     pub fn is_stable(&self) -> bool {
714         self.stability == OptionStability::Stable
715     }
716
717     fn stable(g: getopts::OptGroup) -> RustcOptGroup {
718         RustcOptGroup { opt_group: g, stability: OptionStability::Stable }
719     }
720
721     fn unstable(g: getopts::OptGroup) -> RustcOptGroup {
722         RustcOptGroup { opt_group: g, stability: OptionStability::Unstable }
723     }
724 }
725
726 // The `opt` local module holds wrappers around the `getopts` API that
727 // adds extra rustc-specific metadata to each option; such metadata
728 // is exposed by .  The public
729 // functions below ending with `_u` are the functions that return
730 // *unstable* options, i.e. options that are only enabled when the
731 // user also passes the `-Z unstable-options` debugging flag.
732 mod opt {
733     // The `fn opt_u` etc below are written so that we can use them
734     // in the future; do not warn about them not being used right now.
735     #![allow(dead_code)]
736
737     use getopts;
738     use super::RustcOptGroup;
739
740     pub type R = RustcOptGroup;
741     pub type S<'a> = &'a str;
742
743     fn stable(g: getopts::OptGroup) -> R { RustcOptGroup::stable(g) }
744     fn unstable(g: getopts::OptGroup) -> R { RustcOptGroup::unstable(g) }
745
746     // FIXME (pnkfelix): We default to stable since the current set of
747     // options is defacto stable.  However, it would be good to revise the
748     // code so that a stable option is the thing that takes extra effort
749     // to encode.
750
751     pub fn     opt(a: S, b: S, c: S, d: S) -> R { stable(getopts::optopt(a, b, c, d)) }
752     pub fn   multi(a: S, b: S, c: S, d: S) -> R { stable(getopts::optmulti(a, b, c, d)) }
753     pub fn    flag(a: S, b: S, c: S)       -> R { stable(getopts::optflag(a, b, c)) }
754     pub fn flagopt(a: S, b: S, c: S, d: S) -> R { stable(getopts::optflagopt(a, b, c, d)) }
755     pub fn flagmulti(a: S, b: S, c: S)     -> R { stable(getopts::optflagmulti(a, b, c)) }
756
757
758     pub fn     opt_u(a: S, b: S, c: S, d: S) -> R { unstable(getopts::optopt(a, b, c, d)) }
759     pub fn   multi_u(a: S, b: S, c: S, d: S) -> R { unstable(getopts::optmulti(a, b, c, d)) }
760     pub fn    flag_u(a: S, b: S, c: S)       -> R { unstable(getopts::optflag(a, b, c)) }
761     pub fn flagopt_u(a: S, b: S, c: S, d: S) -> R { unstable(getopts::optflagopt(a, b, c, d)) }
762     pub fn flagmulti_u(a: S, b: S, c: S)     -> R { unstable(getopts::optflagmulti(a, b, c)) }
763 }
764
765 /// Returns the "short" subset of the rustc command line options,
766 /// including metadata for each option, such as whether the option is
767 /// part of the stable long-term interface for rustc.
768 pub fn rustc_short_optgroups() -> Vec<RustcOptGroup> {
769     vec![
770         opt::flag("h", "help", "Display this message"),
771         opt::multi("", "cfg", "Configure the compilation environment", "SPEC"),
772         opt::multi("L", "",   "Add a directory to the library search path",
773                    "[KIND=]PATH"),
774         opt::multi("l", "",   "Link the generated crate(s) to the specified native
775                              library NAME. The optional KIND can be one of,
776                              static, dylib, or framework. If omitted, dylib is
777                              assumed.", "[KIND=]NAME"),
778         opt::multi("", "crate-type", "Comma separated list of types of crates
779                                     for the compiler to emit",
780                    "[bin|lib|rlib|dylib|staticlib]"),
781         opt::opt("", "crate-name", "Specify the name of the crate being built",
782                "NAME"),
783         opt::multi("", "emit", "Comma separated list of types of output for \
784                               the compiler to emit",
785                  "[asm|llvm-bc|llvm-ir|obj|link|dep-info]"),
786         opt::multi("", "print", "Comma separated list of compiler information to \
787                                print on stdout",
788                  "[crate-name|file-names|sysroot]"),
789         opt::flagmulti("g",  "",  "Equivalent to -C debuginfo=2"),
790         opt::flagmulti("O", "", "Equivalent to -C opt-level=2"),
791         opt::opt("o", "", "Write output to <filename>", "FILENAME"),
792         opt::opt("",  "out-dir", "Write output to compiler-chosen filename \
793                                 in <dir>", "DIR"),
794         opt::opt("", "explain", "Provide a detailed explanation of an error \
795                                message", "OPT"),
796         opt::flag("", "test", "Build a test harness"),
797         opt::opt("", "target", "Target triple cpu-manufacturer-kernel[-os] \
798                               to compile for (see chapter 3.4 of \
799                               http://www.sourceware.org/autobook/
800                               for details)",
801                "TRIPLE"),
802         opt::multi("W", "warn", "Set lint warnings", "OPT"),
803         opt::multi("A", "allow", "Set lint allowed", "OPT"),
804         opt::multi("D", "deny", "Set lint denied", "OPT"),
805         opt::multi("F", "forbid", "Set lint forbidden", "OPT"),
806         opt::multi("", "cap-lints", "Set the most restrictive lint level. \
807                                      More restrictive lints are capped at this \
808                                      level", "LEVEL"),
809         opt::multi("C", "codegen", "Set a codegen option", "OPT[=VALUE]"),
810         opt::flag("V", "version", "Print version info and exit"),
811         opt::flag("v", "verbose", "Use verbose output"),
812     ]
813 }
814
815 /// Returns all rustc command line options, including metadata for
816 /// each option, such as whether the option is part of the stable
817 /// long-term interface for rustc.
818 pub fn rustc_optgroups() -> Vec<RustcOptGroup> {
819     let mut opts = rustc_short_optgroups();
820     opts.push_all(&[
821         opt::multi("", "extern", "Specify where an external rust library is \
822                                 located",
823                  "NAME=PATH"),
824         opt::opt("", "sysroot", "Override the system root", "PATH"),
825         opt::multi("Z", "", "Set internal debugging options", "FLAG"),
826         opt::opt("", "color", "Configure coloring of output:
827             auto   = colorize, if output goes to a tty (default);
828             always = always colorize output;
829             never  = never colorize output", "auto|always|never"),
830
831         opt::flagopt_u("", "pretty",
832                    "Pretty-print the input instead of compiling;
833                    valid types are: `normal` (un-annotated source),
834                    `expanded` (crates expanded), or
835                    `expanded,identified` (fully parenthesized, AST nodes with IDs).",
836                  "TYPE"),
837         opt::flagopt_u("", "unpretty",
838                      "Present the input source, unstable (and less-pretty) variants;
839                       valid types are any of the types for `--pretty`, as well as:
840                       `flowgraph=<nodeid>` (graphviz formatted flowgraph for node),
841                       `everybody_loops` (all function bodies replaced with `loop {}`),
842                       `hir` (the HIR), `hir,identified`, or
843                       `hir,typed` (HIR with types for each node).",
844                      "TYPE"),
845         opt::opt_u("", "show-span", "Show spans for compiler debugging", "expr|pat|ty"),
846     ]);
847     opts
848 }
849
850 // Convert strings provided as --cfg [cfgspec] into a crate_cfg
851 pub fn parse_cfgspecs(cfgspecs: Vec<String> ) -> ast::CrateConfig {
852     cfgspecs.into_iter().map(|s| {
853         parse::parse_meta_from_source_str("cfgspec".to_string(),
854                                           s.to_string(),
855                                           Vec::new(),
856                                           &parse::ParseSess::new())
857     }).collect::<ast::CrateConfig>()
858 }
859
860 pub fn build_session_options(matches: &getopts::Matches) -> Options {
861     let color = match matches.opt_str("color").as_ref().map(|s| &s[..]) {
862         Some("auto")   => Auto,
863         Some("always") => Always,
864         Some("never")  => Never,
865
866         None => Auto,
867
868         Some(arg) => {
869             early_error(Auto, &format!("argument for --color must be auto, always \
870                                         or never (instead was `{}`)",
871                                        arg))
872         }
873     };
874
875     let unparsed_crate_types = matches.opt_strs("crate-type");
876     let crate_types = parse_crate_types_from_list(unparsed_crate_types)
877         .unwrap_or_else(|e| early_error(color, &e[..]));
878
879     let mut lint_opts = vec!();
880     let mut describe_lints = false;
881
882     for &level in &[lint::Allow, lint::Warn, lint::Deny, lint::Forbid] {
883         for lint_name in matches.opt_strs(level.as_str()) {
884             if lint_name == "help" {
885                 describe_lints = true;
886             } else {
887                 lint_opts.push((lint_name.replace("-", "_"), level));
888             }
889         }
890     }
891
892     let lint_cap = matches.opt_str("cap-lints").map(|cap| {
893         lint::Level::from_str(&cap).unwrap_or_else(|| {
894             early_error(color, &format!("unknown lint level: `{}`", cap))
895         })
896     });
897
898     let debugging_opts = build_debugging_options(matches, color);
899
900     let parse_only = debugging_opts.parse_only;
901     let no_trans = debugging_opts.no_trans;
902     let treat_err_as_bug = debugging_opts.treat_err_as_bug;
903     let always_build_mir = debugging_opts.always_build_mir;
904     let no_analysis = debugging_opts.no_analysis;
905
906     if debugging_opts.debug_llvm {
907         unsafe { llvm::LLVMSetDebug(1); }
908     }
909
910     let mut output_types = Vec::new();
911     if !debugging_opts.parse_only && !no_trans {
912         let unparsed_output_types = matches.opt_strs("emit");
913         for unparsed_output_type in &unparsed_output_types {
914             for part in unparsed_output_type.split(',') {
915                 let output_type = match part {
916                     "asm" => OutputTypeAssembly,
917                     "llvm-ir" => OutputTypeLlvmAssembly,
918                     "llvm-bc" => OutputTypeBitcode,
919                     "obj" => OutputTypeObject,
920                     "link" => OutputTypeExe,
921                     "dep-info" => OutputTypeDepInfo,
922                     _ => {
923                         early_error(color, &format!("unknown emission type: `{}`",
924                                                     part))
925                     }
926                 };
927                 output_types.push(output_type)
928             }
929         }
930     };
931     output_types.sort();
932     output_types.dedup();
933     if output_types.is_empty() {
934         output_types.push(OutputTypeExe);
935     }
936
937     let cg = build_codegen_options(matches, color);
938
939     let sysroot_opt = matches.opt_str("sysroot").map(|m| PathBuf::from(&m));
940     let target = matches.opt_str("target").unwrap_or(
941         host_triple().to_string());
942     let opt_level = {
943         if matches.opt_present("O") {
944             if cg.opt_level.is_some() {
945                 early_error(color, "-O and -C opt-level both provided");
946             }
947             Default
948         } else {
949             match cg.opt_level {
950                 None => No,
951                 Some(0) => No,
952                 Some(1) => Less,
953                 Some(2) => Default,
954                 Some(3) => Aggressive,
955                 Some(arg) => {
956                     early_error(color, &format!("optimization level needs to be \
957                                                  between 0-3 (instead was `{}`)",
958                                                 arg));
959                 }
960             }
961         }
962     };
963     let debug_assertions = cg.debug_assertions.unwrap_or(opt_level == No);
964     let gc = debugging_opts.gc;
965     let debuginfo = if matches.opt_present("g") {
966         if cg.debuginfo.is_some() {
967             early_error(color, "-g and -C debuginfo both provided");
968         }
969         FullDebugInfo
970     } else {
971         match cg.debuginfo {
972             None | Some(0) => NoDebugInfo,
973             Some(1) => LimitedDebugInfo,
974             Some(2) => FullDebugInfo,
975             Some(arg) => {
976                 early_error(color, &format!("debug info level needs to be between \
977                                              0-2 (instead was `{}`)",
978                                             arg));
979             }
980         }
981     };
982
983     let mut search_paths = SearchPaths::new();
984     for s in &matches.opt_strs("L") {
985         search_paths.add_path(&s[..], color);
986     }
987
988     let libs = matches.opt_strs("l").into_iter().map(|s| {
989         let mut parts = s.splitn(2, '=');
990         let kind = parts.next().unwrap();
991         let (name, kind) = match (parts.next(), kind) {
992             (None, name) |
993             (Some(name), "dylib") => (name, cstore::NativeUnknown),
994             (Some(name), "framework") => (name, cstore::NativeFramework),
995             (Some(name), "static") => (name, cstore::NativeStatic),
996             (_, s) => {
997                 early_error(color, &format!("unknown library kind `{}`, expected \
998                                              one of dylib, framework, or static",
999                                             s));
1000             }
1001         };
1002         (name.to_string(), kind)
1003     }).collect();
1004
1005     let cfg = parse_cfgspecs(matches.opt_strs("cfg"));
1006     let test = matches.opt_present("test");
1007     let write_dependency_info = (output_types.contains(&OutputTypeDepInfo), None);
1008
1009     let prints = matches.opt_strs("print").into_iter().map(|s| {
1010         match &*s {
1011             "crate-name" => PrintRequest::CrateName,
1012             "file-names" => PrintRequest::FileNames,
1013             "sysroot" => PrintRequest::Sysroot,
1014             req => {
1015                 early_error(color, &format!("unknown print request `{}`", req))
1016             }
1017         }
1018     }).collect::<Vec<_>>();
1019
1020     if !cg.remark.is_empty() && debuginfo == NoDebugInfo {
1021         early_warn(color, "-C remark will not show source locations without \
1022                            --debuginfo");
1023     }
1024
1025     let mut externs = HashMap::new();
1026     for arg in &matches.opt_strs("extern") {
1027         let mut parts = arg.splitn(2, '=');
1028         let name = match parts.next() {
1029             Some(s) => s,
1030             None => early_error(color, "--extern value must not be empty"),
1031         };
1032         let location = match parts.next() {
1033             Some(s) => s,
1034             None => early_error(color, "--extern value must be of the format `foo=bar`"),
1035         };
1036
1037         externs.entry(name.to_string()).or_insert(vec![]).push(location.to_string());
1038     }
1039
1040     let crate_name = matches.opt_str("crate-name");
1041
1042     Options {
1043         crate_types: crate_types,
1044         gc: gc,
1045         optimize: opt_level,
1046         debuginfo: debuginfo,
1047         lint_opts: lint_opts,
1048         lint_cap: lint_cap,
1049         describe_lints: describe_lints,
1050         output_types: output_types,
1051         search_paths: search_paths,
1052         maybe_sysroot: sysroot_opt,
1053         target_triple: target,
1054         cfg: cfg,
1055         test: test,
1056         parse_only: parse_only,
1057         no_trans: no_trans,
1058         treat_err_as_bug: treat_err_as_bug,
1059         always_build_mir: always_build_mir,
1060         no_analysis: no_analysis,
1061         debugging_opts: debugging_opts,
1062         write_dependency_info: write_dependency_info,
1063         prints: prints,
1064         cg: cg,
1065         color: color,
1066         show_span: None,
1067         externs: externs,
1068         crate_name: crate_name,
1069         alt_std_name: None,
1070         libs: libs,
1071         unstable_features: get_unstable_features_setting(),
1072         debug_assertions: debug_assertions,
1073     }
1074 }
1075
1076 pub fn get_unstable_features_setting() -> UnstableFeatures {
1077     // Whether this is a feature-staged build, i.e. on the beta or stable channel
1078     let disable_unstable_features = option_env!("CFG_DISABLE_UNSTABLE_FEATURES").is_some();
1079     // The secret key needed to get through the rustc build itself by
1080     // subverting the unstable features lints
1081     let bootstrap_secret_key = option_env!("CFG_BOOTSTRAP_KEY");
1082     // The matching key to the above, only known by the build system
1083     let bootstrap_provided_key = env::var("RUSTC_BOOTSTRAP_KEY").ok();
1084     match (disable_unstable_features, bootstrap_secret_key, bootstrap_provided_key) {
1085         (_, Some(ref s), Some(ref p)) if s == p => UnstableFeatures::Cheat,
1086         (true, _, _) => UnstableFeatures::Disallow,
1087         (false, _, _) => UnstableFeatures::Allow
1088     }
1089 }
1090
1091 pub fn parse_crate_types_from_list(list_list: Vec<String>) -> Result<Vec<CrateType>, String> {
1092
1093     let mut crate_types: Vec<CrateType> = Vec::new();
1094     for unparsed_crate_type in &list_list {
1095         for part in unparsed_crate_type.split(',') {
1096             let new_part = match part {
1097                 "lib"       => default_lib_output(),
1098                 "rlib"      => CrateTypeRlib,
1099                 "staticlib" => CrateTypeStaticlib,
1100                 "dylib"     => CrateTypeDylib,
1101                 "bin"       => CrateTypeExecutable,
1102                 _ => {
1103                     return Err(format!("unknown crate type: `{}`",
1104                                        part));
1105                 }
1106             };
1107             if !crate_types.contains(&new_part) {
1108                 crate_types.push(new_part)
1109             }
1110         }
1111     }
1112
1113     return Ok(crate_types);
1114 }
1115
1116 impl fmt::Display for CrateType {
1117     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1118         match *self {
1119             CrateTypeExecutable => "bin".fmt(f),
1120             CrateTypeDylib => "dylib".fmt(f),
1121             CrateTypeRlib => "rlib".fmt(f),
1122             CrateTypeStaticlib => "staticlib".fmt(f)
1123         }
1124     }
1125 }
1126
1127 #[cfg(test)]
1128 mod tests {
1129
1130     use session::config::{build_configuration, optgroups, build_session_options};
1131     use session::build_session;
1132
1133     use getopts::getopts;
1134     use syntax::attr;
1135     use syntax::attr::AttrMetaMethods;
1136     use syntax::diagnostics;
1137
1138     // When the user supplies --test we should implicitly supply --cfg test
1139     #[test]
1140     fn test_switch_implies_cfg_test() {
1141         let matches =
1142             &match getopts(&["--test".to_string()], &optgroups()) {
1143               Ok(m) => m,
1144               Err(f) => panic!("test_switch_implies_cfg_test: {}", f)
1145             };
1146         let registry = diagnostics::registry::Registry::new(&[]);
1147         let sessopts = build_session_options(matches);
1148         let sess = build_session(sessopts, None, registry);
1149         let cfg = build_configuration(&sess);
1150         assert!((attr::contains_name(&cfg[..], "test")));
1151     }
1152
1153     // When the user supplies --test and --cfg test, don't implicitly add
1154     // another --cfg test
1155     #[test]
1156     fn test_switch_implies_cfg_test_unless_cfg_test() {
1157         let matches =
1158             &match getopts(&["--test".to_string(), "--cfg=test".to_string()],
1159                            &optgroups()) {
1160               Ok(m) => m,
1161               Err(f) => {
1162                 panic!("test_switch_implies_cfg_test_unless_cfg_test: {}", f)
1163               }
1164             };
1165         let registry = diagnostics::registry::Registry::new(&[]);
1166         let sessopts = build_session_options(matches);
1167         let sess = build_session(sessopts, None, registry);
1168         let cfg = build_configuration(&sess);
1169         let mut test_items = cfg.iter().filter(|m| m.name() == "test");
1170         assert!(test_items.next().is_some());
1171         assert!(test_items.next().is_none());
1172     }
1173
1174     #[test]
1175     fn test_can_print_warnings() {
1176         {
1177             let matches = getopts(&[
1178                 "-Awarnings".to_string()
1179             ], &optgroups()).unwrap();
1180             let registry = diagnostics::registry::Registry::new(&[]);
1181             let sessopts = build_session_options(&matches);
1182             let sess = build_session(sessopts, None, registry);
1183             assert!(!sess.can_print_warnings);
1184         }
1185
1186         {
1187             let matches = getopts(&[
1188                 "-Awarnings".to_string(),
1189                 "-Dwarnings".to_string()
1190             ], &optgroups()).unwrap();
1191             let registry = diagnostics::registry::Registry::new(&[]);
1192             let sessopts = build_session_options(&matches);
1193             let sess = build_session(sessopts, None, registry);
1194             assert!(sess.can_print_warnings);
1195         }
1196
1197         {
1198             let matches = getopts(&[
1199                 "-Adead_code".to_string()
1200             ], &optgroups()).unwrap();
1201             let registry = diagnostics::registry::Registry::new(&[]);
1202             let sessopts = build_session_options(&matches);
1203             let sess = build_session(sessopts, None, registry);
1204             assert!(sess.can_print_warnings);
1205         }
1206     }
1207 }