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.
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.
11 //! Contains infrastructure for configuring the compiler, including parsing
12 //! command line options.
14 use driver::early_error;
16 use driver::session::Session;
20 use back::target_strs;
21 use back::{arm, x86, x86_64, mips};
27 use syntax::ast::{IntTy, UintTy};
29 use syntax::attr::AttrMetaMethods;
31 use syntax::parse::token::InternedString;
33 use collections::HashSet;
34 use getopts::{optopt, optmulti, optflag, optflagopt};
37 use std::cell::{RefCell};
42 pub arch: abi::Architecture,
43 pub target_strs: target_strs::t,
45 pub uint_type: UintTy,
48 #[deriving(Clone, Eq)]
56 #[deriving(Clone, Eq)]
57 pub enum DebugInfoLevel {
65 // The crate config requested for the session, which may be combined
66 // with additional crate configurations during the compile process
67 pub crate_types: Vec<CrateType>,
70 pub optimize: OptLevel,
71 pub debuginfo: DebugInfoLevel,
72 pub lint_opts: Vec<(lint::Lint, lint::level)> ,
73 pub output_types: Vec<back::link::OutputType> ,
74 // This was mutable for rustpkg, which updates search paths based on the
75 // parsed code. It remains mutable in case its replacements wants to use
77 pub addl_lib_search_paths: RefCell<HashSet<Path>>,
78 pub maybe_sysroot: Option<Path>,
79 pub target_triple: StrBuf,
80 // User-specified cfg meta items. The compiler itself will add additional
81 // items to the crate config, and during parsing the entire crate config
82 // will be added to the crate AST node. This should not be used for
83 // anything except building the full crate config prior to parsing.
84 pub cfg: ast::CrateConfig,
88 pub no_analysis: bool,
89 pub debugging_opts: u64,
90 /// Whether to write dependency files. It's (enabled, optional filename).
91 pub write_dependency_info: (bool, Option<Path>),
92 /// Crate id-related things to maybe print. It's (crate_id, crate_name, crate_file_name).
93 pub print_metas: (bool, bool, bool),
94 pub cg: CodegenOptions,
97 /// Some reasonable defaults
98 pub fn basic_options() -> Options {
100 crate_types: Vec::new(),
103 debuginfo: NoDebugInfo,
104 lint_opts: Vec::new(),
105 output_types: Vec::new(),
106 addl_lib_search_paths: RefCell::new(HashSet::new()),
108 target_triple: driver::host_triple().to_strbuf(),
115 write_dependency_info: (false, None),
116 print_metas: (false, false, false),
117 cg: basic_codegen_options(),
121 // The type of entry function, so
122 // users can have their own entry
123 // functions that don't start a
126 pub enum EntryFnType {
132 #[deriving(Eq, Ord, Clone, TotalOrd, TotalEq, Hash)]
140 macro_rules! debugging_opts(
141 ([ $opt:ident ] $cnt:expr ) => (
142 pub static $opt: u64 = 1 << $cnt;
144 ([ $opt:ident, $($rest:ident),* ] $cnt:expr ) => (
145 pub static $opt: u64 = 1 << $cnt;
146 debugging_opts!([ $($rest),* ] $cnt + 1)
177 pub fn debugging_opts_map() -> Vec<(&'static str, &'static str, u64)> {
178 vec!(("verbose", "in general, enable more debug printouts", VERBOSE),
179 ("time-passes", "measure time of each rustc pass", TIME_PASSES),
180 ("count-llvm-insns", "count where LLVM \
181 instrs originate", COUNT_LLVM_INSNS),
182 ("time-llvm-passes", "measure time of each LLVM pass",
184 ("trans-stats", "gather trans statistics", TRANS_STATS),
185 ("asm-comments", "generate comments into the assembly (may change behavior)",
187 ("no-verify", "skip LLVM verification", NO_VERIFY),
188 ("borrowck-stats", "gather borrowck statistics", BORROWCK_STATS),
189 ("no-landing-pads", "omit landing pads for unwinding",
191 ("debug-llvm", "enable debug output from LLVM", DEBUG_LLVM),
192 ("show-span", "show spans for compiler debugging", SHOW_SPAN),
193 ("count-type-sizes", "count the sizes of aggregate types",
195 ("meta-stats", "gather metadata statistics", META_STATS),
196 ("no-opt", "do not optimize, even if -O is passed", NO_OPT),
197 ("print-link-args", "Print the arguments passed to the linker",
199 ("gc", "Garbage collect shared data (experimental)", GC),
200 ("print-llvm-passes",
201 "Prints the llvm optimization passes being run",
203 ("lto", "Perform LLVM link-time optimizations", LTO),
204 ("ast-json", "Print the AST as JSON and halt", AST_JSON),
205 ("ast-json-noexpand", "Print the pre-expansion AST as JSON and halt", AST_JSON_NOEXPAND),
206 ("ls", "List the symbols defined by a library crate", LS))
209 /// Declare a macro that will define all CodegenOptions fields and parsers all
210 /// at once. The goal of this macro is to define an interface that can be
211 /// programmatically used by the option parser in order to initialize the struct
212 /// without hardcoding field names all over the place.
214 /// The goal is to invoke this macro once with the correct fields, and then this
215 /// macro generates all necessary code. The main gotcha of this macro is the
216 /// cgsetters module which is a bunch of generated code to parse an option into
217 /// its respective field in the struct. There are a few hand-written parsers for
218 /// parsing specific types of values in this module.
219 macro_rules! cgoptions(
220 ($($opt:ident : $t:ty = ($init:expr, $parse:ident, $desc:expr)),* ,) =>
223 pub struct CodegenOptions { $(pub $opt: $t),* }
225 pub fn basic_codegen_options() -> CodegenOptions {
226 CodegenOptions { $($opt: $init),* }
229 pub type CodegenSetter = fn(&mut CodegenOptions, v: Option<&str>) -> bool;
230 pub static CG_OPTIONS: &'static [(&'static str, CodegenSetter,
232 &[ $( (stringify!($opt), cgsetters::$opt, $desc) ),* ];
235 use super::CodegenOptions;
238 pub fn $opt(cg: &mut CodegenOptions, v: Option<&str>) -> bool {
239 $parse(&mut cg.$opt, v)
243 fn parse_bool(slot: &mut bool, v: Option<&str>) -> bool {
246 None => { *slot = true; true }
250 fn parse_opt_string(slot: &mut Option<StrBuf>, v: Option<&str>) -> bool {
252 Some(s) => { *slot = Some(s.to_strbuf()); true },
257 fn parse_string(slot: &mut StrBuf, v: Option<&str>) -> bool {
259 Some(s) => { *slot = s.to_strbuf(); true },
264 fn parse_list(slot: &mut Vec<StrBuf>, v: Option<&str>)
269 slot.push(s.to_strbuf());
281 ar: Option<StrBuf> = (None, parse_opt_string,
282 "tool to assemble archives with"),
283 linker: Option<StrBuf> = (None, parse_opt_string,
284 "system linker to link outputs with"),
285 link_args: Vec<StrBuf> = (Vec::new(), parse_list,
286 "extra arguments to pass to the linker (space separated)"),
287 target_cpu: StrBuf = ("generic".to_strbuf(), parse_string,
288 "select target processor (llc -mcpu=help for details)"),
289 target_feature: StrBuf = ("".to_strbuf(), parse_string,
290 "target specific attributes (llc -mattr=help for details)"),
291 passes: Vec<StrBuf> = (Vec::new(), parse_list,
292 "a list of extra LLVM passes to run (space separated)"),
293 llvm_args: Vec<StrBuf> = (Vec::new(), parse_list,
294 "a list of arguments to pass to llvm (space separated)"),
295 save_temps: bool = (false, parse_bool,
296 "save all temporary output files during compilation"),
297 no_rpath: bool = (false, parse_bool,
298 "disables setting the rpath in libs/exes"),
299 no_prepopulate_passes: bool = (false, parse_bool,
300 "don't pre-populate the pass manager with a list of passes"),
301 no_vectorize_loops: bool = (false, parse_bool,
302 "don't run the loop vectorization optimization passes"),
303 no_vectorize_slp: bool = (false, parse_bool,
304 "don't run LLVM's SLP vectorization pass"),
305 soft_float: bool = (false, parse_bool,
306 "generate software floating point library calls"),
307 prefer_dynamic: bool = (false, parse_bool,
308 "prefer dynamic linking to static linking"),
309 no_integrated_as: bool = (false, parse_bool,
310 "use an external assembler rather than LLVM's integrated one"),
311 relocation_model: StrBuf = ("pic".to_strbuf(), parse_string,
312 "choose the relocation model to use (llc -relocation-model for details)"),
315 pub fn build_codegen_options(matches: &getopts::Matches) -> CodegenOptions
317 let mut cg = basic_codegen_options();
318 for option in matches.opt_strs("C").move_iter() {
319 let mut iter = option.splitn('=', 1);
320 let key = iter.next().unwrap();
321 let value = iter.next();
322 let option_to_lookup = key.replace("-", "_");
323 let mut found = false;
324 for &(candidate, setter, _) in CG_OPTIONS.iter() {
325 if option_to_lookup.as_slice() != candidate { continue }
326 if !setter(&mut cg, value) {
328 Some(..) => early_error(format!("codegen option `{}` takes \
330 None => early_error(format!("codegen option `{0}` requires \
331 a value (-C {0}=<value>)",
339 early_error(format!("unknown codegen option: `{}`", key));
345 pub fn default_lib_output() -> CrateType {
349 pub fn cfg_os_to_meta_os(os: abi::Os) -> metadata::loader::Os {
350 use metadata::loader;
353 abi::OsWin32 => loader::OsWin32,
354 abi::OsLinux => loader::OsLinux,
355 abi::OsAndroid => loader::OsAndroid,
356 abi::OsMacos => loader::OsMacos,
357 abi::OsFreebsd => loader::OsFreebsd
361 pub fn default_configuration(sess: &Session) -> ast::CrateConfig {
362 let tos = match sess.targ_cfg.os {
363 abi::OsWin32 => InternedString::new("win32"),
364 abi::OsMacos => InternedString::new("macos"),
365 abi::OsLinux => InternedString::new("linux"),
366 abi::OsAndroid => InternedString::new("android"),
367 abi::OsFreebsd => InternedString::new("freebsd"),
370 // ARM is bi-endian, however using NDK seems to default
371 // to little-endian unless a flag is provided.
372 let (end,arch,wordsz) = match sess.targ_cfg.arch {
373 abi::X86 => ("little", "x86", "32"),
374 abi::X86_64 => ("little", "x86_64", "64"),
375 abi::Arm => ("little", "arm", "32"),
376 abi::Mips => ("big", "mips", "32")
379 let fam = match sess.targ_cfg.os {
380 abi::OsWin32 => InternedString::new("windows"),
381 _ => InternedString::new("unix")
384 let mk = attr::mk_name_value_item_str;
385 return vec!(// Target bindings.
386 attr::mk_word_item(fam.clone()),
387 mk(InternedString::new("target_os"), tos),
388 mk(InternedString::new("target_family"), fam),
389 mk(InternedString::new("target_arch"), InternedString::new(arch)),
390 mk(InternedString::new("target_endian"), InternedString::new(end)),
391 mk(InternedString::new("target_word_size"),
392 InternedString::new(wordsz))
396 pub fn append_configuration(cfg: &mut ast::CrateConfig,
397 name: InternedString) {
398 if !cfg.iter().any(|mi| mi.name() == name) {
399 cfg.push(attr::mk_word_item(name))
403 pub fn build_configuration(sess: &Session) -> ast::CrateConfig {
404 // Combine the configuration requested by the session (command line) with
405 // some default and generated configuration items
406 let default_cfg = default_configuration(sess);
407 let mut user_cfg = sess.opts.cfg.clone();
408 // If the user wants a test runner, then add the test cfg
410 append_configuration(&mut user_cfg, InternedString::new("test"))
412 // If the user requested GC, then add the GC cfg
413 append_configuration(&mut user_cfg, if sess.opts.gc {
414 InternedString::new("gc")
416 InternedString::new("nogc")
418 user_cfg.move_iter().collect::<Vec<_>>().append(default_cfg.as_slice())
421 pub fn get_os(triple: &str) -> Option<abi::Os> {
422 for &(name, os) in os_names.iter() {
423 if triple.contains(name) { return Some(os) }
427 static os_names : &'static [(&'static str, abi::Os)] = &'static [
428 ("mingw32", abi::OsWin32),
429 ("win32", abi::OsWin32),
430 ("darwin", abi::OsMacos),
431 ("android", abi::OsAndroid),
432 ("linux", abi::OsLinux),
433 ("freebsd", abi::OsFreebsd)];
435 pub fn get_arch(triple: &str) -> Option<abi::Architecture> {
436 for &(arch, abi) in architecture_abis.iter() {
437 if triple.contains(arch) { return Some(abi) }
441 static architecture_abis : &'static [(&'static str, abi::Architecture)] = &'static [
448 ("x86_64", abi::X86_64),
451 ("xscale", abi::Arm),
454 ("mips", abi::Mips)];
456 pub fn build_target_config(sopts: &Options) -> Config {
457 let os = match get_os(sopts.target_triple.as_slice()) {
459 None => early_error("unknown operating system")
461 let arch = match get_arch(sopts.target_triple.as_slice()) {
464 early_error("unknown architecture: " +
465 sopts.target_triple.as_slice())
468 let (int_type, uint_type) = match arch {
469 abi::X86 => (ast::TyI32, ast::TyU32),
470 abi::X86_64 => (ast::TyI64, ast::TyU64),
471 abi::Arm => (ast::TyI32, ast::TyU32),
472 abi::Mips => (ast::TyI32, ast::TyU32)
474 let target_triple = sopts.target_triple.clone();
475 let target_strs = match arch {
476 abi::X86 => x86::get_target_strs(target_triple, os),
477 abi::X86_64 => x86_64::get_target_strs(target_triple, os),
478 abi::Arm => arm::get_target_strs(target_triple, os),
479 abi::Mips => mips::get_target_strs(target_triple, os)
484 target_strs: target_strs,
486 uint_type: uint_type,
490 // rustc command line options
491 pub fn optgroups() -> Vec<getopts::OptGroup> {
493 optflag("h", "help", "Display this message"),
494 optmulti("", "cfg", "Configure the compilation environment", "SPEC"),
495 optmulti("L", "", "Add a directory to the library search path", "PATH"),
496 optmulti("", "crate-type", "Comma separated list of types of crates
497 for the compiler to emit",
498 "[bin|lib|rlib|dylib|staticlib]"),
499 optmulti("", "emit", "Comma separated list of types of output for the compiler to emit",
500 "[asm|bc|ir|obj|link]"),
501 optflag("", "crate-id", "Output the crate id and exit"),
502 optflag("", "crate-name", "Output the crate name and exit"),
503 optflag("", "crate-file-name", "Output the file(s) that would be written if compilation \
504 continued and exit"),
505 optflag("g", "", "Equivalent to --debuginfo=2"),
506 optopt("", "debuginfo", "Emit DWARF debug info to the objects created:
508 1 = line-tables only (for stacktraces and breakpoints),
509 2 = full debug info with variable and type information (same as -g)", "LEVEL"),
510 optflag("", "no-trans", "Run all passes except translation; no output"),
511 optflag("", "no-analysis",
512 "Parse and expand the source, but run no analysis and produce no output"),
513 optflag("O", "", "Equivalent to --opt-level=2"),
514 optopt("o", "", "Write output to <filename>", "FILENAME"),
515 optopt("", "opt-level", "Optimize with possible levels 0-3", "LEVEL"),
516 optopt( "", "out-dir", "Write output to compiler-chosen filename in <dir>", "DIR"),
517 optflag("", "parse-only", "Parse only; do not compile, assemble, or link"),
518 optflagopt("", "pretty",
519 "Pretty-print the input instead of compiling;
520 valid types are: normal (un-annotated source),
521 expanded (crates expanded),
522 typed (crates expanded, with type annotations),
523 or identified (fully parenthesized,
524 AST nodes and blocks with IDs)", "TYPE"),
525 optflagopt("", "dep-info",
526 "Output dependency info to <filename> after compiling, \
527 in a format suitable for use by Makefiles", "FILENAME"),
528 optopt("", "sysroot", "Override the system root", "PATH"),
529 optflag("", "test", "Build a test harness"),
530 optopt("", "target", "Target triple cpu-manufacturer-kernel[-os]
531 to compile for (see chapter 3.4 of http://www.sourceware.org/autobook/
532 for details)", "TRIPLE"),
533 optmulti("W", "warn", "Set lint warnings", "OPT"),
534 optmulti("A", "allow", "Set lint allowed", "OPT"),
535 optmulti("D", "deny", "Set lint denied", "OPT"),
536 optmulti("F", "forbid", "Set lint forbidden", "OPT"),
537 optmulti("C", "codegen", "Set a codegen option", "OPT[=VALUE]"),
538 optmulti("Z", "", "Set internal debugging options", "FLAG"),
539 optflag( "v", "version", "Print version info and exit")
544 // Convert strings provided as --cfg [cfgspec] into a crate_cfg
545 fn parse_cfgspecs(cfgspecs: Vec<StrBuf> ) -> ast::CrateConfig {
546 cfgspecs.move_iter().map(|s| {
547 parse::parse_meta_from_source_str("cfgspec".to_strbuf(),
550 &parse::new_parse_sess())
551 }).collect::<ast::CrateConfig>()
554 pub fn build_session_options(matches: &getopts::Matches) -> Options {
555 let mut crate_types: Vec<CrateType> = Vec::new();
556 let unparsed_crate_types = matches.opt_strs("crate-type");
557 for unparsed_crate_type in unparsed_crate_types.iter() {
558 for part in unparsed_crate_type.split(',') {
559 let new_part = match part {
560 "lib" => default_lib_output(),
561 "rlib" => CrateTypeRlib,
562 "staticlib" => CrateTypeStaticlib,
563 "dylib" => CrateTypeDylib,
564 "bin" => CrateTypeExecutable,
565 _ => early_error(format!("unknown crate type: `{}`", part))
567 crate_types.push(new_part)
571 let parse_only = matches.opt_present("parse-only");
572 let no_trans = matches.opt_present("no-trans");
573 let no_analysis = matches.opt_present("no-analysis");
575 let lint_levels = [lint::allow, lint::warn,
576 lint::deny, lint::forbid];
577 let mut lint_opts = Vec::new();
578 let lint_dict = lint::get_lint_dict();
579 for level in lint_levels.iter() {
580 let level_name = lint::level_to_str(*level);
582 let level_short = level_name.slice_chars(0, 1);
583 let level_short = level_short.to_ascii().to_upper().into_str();
584 let flags = matches.opt_strs(level_short).move_iter().collect::<Vec<_>>().append(
585 matches.opt_strs(level_name).as_slice());
586 for lint_name in flags.iter() {
587 let lint_name = lint_name.replace("-", "_");
588 match lint_dict.find_equiv(&lint_name) {
590 early_error(format!("unknown {} flag: {}",
591 level_name, lint_name));
594 lint_opts.push((lint.lint, *level));
600 let mut debugging_opts = 0;
601 let debug_flags = matches.opt_strs("Z");
602 let debug_map = debugging_opts_map();
603 for debug_flag in debug_flags.iter() {
604 let mut this_bit = 0;
605 for tuple in debug_map.iter() {
606 let (name, bit) = match *tuple { (ref a, _, b) => (a, b) };
607 if *name == *debug_flag { this_bit = bit; break; }
610 early_error(format!("unknown debug flag: {}", *debug_flag))
612 debugging_opts |= this_bit;
615 if debugging_opts & DEBUG_LLVM != 0 {
616 unsafe { llvm::LLVMSetDebug(1); }
619 let mut output_types = Vec::new();
620 if !parse_only && !no_trans {
621 let unparsed_output_types = matches.opt_strs("emit");
622 for unparsed_output_type in unparsed_output_types.iter() {
623 for part in unparsed_output_type.split(',') {
624 let output_type = match part.as_slice() {
625 "asm" => link::OutputTypeAssembly,
626 "ir" => link::OutputTypeLlvmAssembly,
627 "bc" => link::OutputTypeBitcode,
628 "obj" => link::OutputTypeObject,
629 "link" => link::OutputTypeExe,
630 _ => early_error(format!("unknown emission type: `{}`", part))
632 output_types.push(output_type)
636 output_types.as_mut_slice().sort();
637 output_types.dedup();
638 if output_types.len() == 0 {
639 output_types.push(link::OutputTypeExe);
642 let sysroot_opt = matches.opt_str("sysroot").map(|m| Path::new(m));
643 let target = match matches.opt_str("target") {
644 Some(supplied_target) => supplied_target.to_strbuf(),
645 None => driver::host_triple().to_strbuf(),
648 if (debugging_opts & NO_OPT) != 0 {
650 } else if matches.opt_present("O") {
651 if matches.opt_present("opt-level") {
652 early_error("-O and --opt-level both provided");
655 } else if matches.opt_present("opt-level") {
656 match matches.opt_str("opt-level").as_ref().map(|s| s.as_slice()) {
660 Some("2") => Default,
661 Some("3") => Aggressive,
663 early_error(format!("optimization level needs to be between 0-3 \
664 (instead was `{}`)", arg));
671 let gc = debugging_opts & GC != 0;
672 let debuginfo = if matches.opt_present("g") {
673 if matches.opt_present("debuginfo") {
674 early_error("-g and --debuginfo both provided");
677 } else if matches.opt_present("debuginfo") {
678 match matches.opt_str("debuginfo").as_ref().map(|s| s.as_slice()) {
679 Some("0") => NoDebugInfo,
680 Some("1") => LimitedDebugInfo,
682 Some("2") => FullDebugInfo,
684 early_error(format!("optimization level needs to be between 0-3 \
685 (instead was `{}`)", arg));
692 let addl_lib_search_paths = matches.opt_strs("L").iter().map(|s| {
693 Path::new(s.as_slice())
696 let cfg = parse_cfgspecs(matches.opt_strs("cfg")
698 .map(|x| x.to_strbuf())
700 let test = matches.opt_present("test");
701 let write_dependency_info = (matches.opt_present("dep-info"),
702 matches.opt_str("dep-info")
703 .map(|p| Path::new(p)));
705 let print_metas = (matches.opt_present("crate-id"),
706 matches.opt_present("crate-name"),
707 matches.opt_present("crate-file-name"));
708 let cg = build_codegen_options(matches);
711 crate_types: crate_types,
714 debuginfo: debuginfo,
715 lint_opts: lint_opts,
716 output_types: output_types,
717 addl_lib_search_paths: RefCell::new(addl_lib_search_paths),
718 maybe_sysroot: sysroot_opt,
719 target_triple: target,
722 parse_only: parse_only,
724 no_analysis: no_analysis,
725 debugging_opts: debugging_opts,
726 write_dependency_info: write_dependency_info,
727 print_metas: print_metas,
736 use driver::config::{build_configuration, optgroups, build_session_options};
737 use driver::session::build_session;
739 use getopts::getopts;
741 use syntax::attr::AttrMetaMethods;
743 // When the user supplies --test we should implicitly supply --cfg test
745 fn test_switch_implies_cfg_test() {
747 &match getopts(["--test".to_owned()], optgroups().as_slice()) {
749 Err(f) => fail!("test_switch_implies_cfg_test: {}", f.to_err_msg())
751 let sessopts = build_session_options(matches);
752 let sess = build_session(sessopts, None);
753 let cfg = build_configuration(&sess);
754 assert!((attr::contains_name(cfg.as_slice(), "test")));
757 // When the user supplies --test and --cfg test, don't implicitly add
758 // another --cfg test
760 fn test_switch_implies_cfg_test_unless_cfg_test() {
762 &match getopts(["--test".to_owned(), "--cfg=test".to_owned()],
763 optgroups().as_slice()) {
766 fail!("test_switch_implies_cfg_test_unless_cfg_test: {}",
770 let sessopts = build_session_options(matches);
771 let sess = build_session(sessopts, None);
772 let cfg = build_configuration(&sess);
773 let mut test_items = cfg.iter().filter(|m| m.name().equiv(&("test")));
774 assert!(test_items.next().is_some());
775 assert!(test_items.next().is_none());