1 // Copyright 2012-2013 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.
13 use back::{arm, x86, x86_64, mips};
14 use driver::session::{Aggressive, OutputExecutable};
15 use driver::session::{Session, Session_, No, Less, Default};
19 use lib::llvm::{ContextRef, ModuleRef};
20 use metadata::common::LinkMeta;
21 use metadata::{creader, filesearch};
22 use metadata::cstore::CStore;
23 use metadata::creader::Loader;
25 use middle::{trans, freevars, kind, ty, typeck, lint, astencode, reachable};
27 use util::common::time;
30 use std::cell::{Cell, RefCell};
31 use std::hashmap::{HashMap,HashSet};
34 use std::io::MemReader;
37 use extra::getopts::groups::{optopt, optmulti, optflag, optflagopt};
42 use syntax::attr::{AttrMetaMethods};
44 use syntax::diagnostic;
45 use syntax::ext::base::CrateLoader;
47 use syntax::parse::token::InternedString;
48 use syntax::parse::token;
49 use syntax::print::{pp, pprust};
61 * The name used for source code that doesn't originate in a file
62 * (e.g. source from stdin or a string)
64 pub fn anon_src() -> ~str {
68 pub fn source_name(input: &Input) -> ~str {
70 // FIXME (#9639): This needs to handle non-utf8 paths
71 FileInput(ref ifile) => ifile.as_str().unwrap().to_str(),
72 StrInput(_) => anon_src()
76 pub fn default_configuration(sess: Session) ->
78 let tos = match sess.targ_cfg.os {
79 abi::OsWin32 => InternedString::new("win32"),
80 abi::OsMacos => InternedString::new("macos"),
81 abi::OsLinux => InternedString::new("linux"),
82 abi::OsAndroid => InternedString::new("android"),
83 abi::OsFreebsd => InternedString::new("freebsd"),
86 // ARM is bi-endian, however using NDK seems to default
87 // to little-endian unless a flag is provided.
88 let (end,arch,wordsz) = match sess.targ_cfg.arch {
89 abi::X86 => ("little", "x86", "32"),
90 abi::X86_64 => ("little", "x86_64", "64"),
91 abi::Arm => ("little", "arm", "32"),
92 abi::Mips => ("big", "mips", "32")
95 let fam = match sess.targ_cfg.os {
96 abi::OsWin32 => InternedString::new("windows"),
97 _ => InternedString::new("unix")
100 let mk = attr::mk_name_value_item_str;
101 return ~[ // Target bindings.
102 attr::mk_word_item(fam.clone()),
103 mk(InternedString::new("target_os"), tos),
104 mk(InternedString::new("target_family"), fam),
105 mk(InternedString::new("target_arch"), InternedString::new(arch)),
106 mk(InternedString::new("target_endian"), InternedString::new(end)),
107 mk(InternedString::new("target_word_size"),
108 InternedString::new(wordsz)),
112 pub fn append_configuration(cfg: &mut ast::CrateConfig,
113 name: InternedString) {
114 if !cfg.iter().any(|mi| mi.name() == name) {
115 cfg.push(attr::mk_word_item(name))
119 pub fn build_configuration(sess: Session) ->
121 // Combine the configuration requested by the session (command line) with
122 // some default and generated configuration items
123 let default_cfg = default_configuration(sess);
124 let mut user_cfg = sess.opts.cfg.clone();
125 // If the user wants a test runner, then add the test cfg
127 append_configuration(&mut user_cfg, InternedString::new("test"))
129 // If the user requested GC, then add the GC cfg
130 append_configuration(&mut user_cfg, if sess.opts.gc {
131 InternedString::new("gc")
133 InternedString::new("nogc")
135 return vec::append(user_cfg, default_cfg);
138 // Convert strings provided as --cfg [cfgspec] into a crate_cfg
139 fn parse_cfgspecs(cfgspecs: ~[~str], demitter: @diagnostic::Emitter)
140 -> ast::CrateConfig {
141 cfgspecs.move_iter().map(|s| {
142 let sess = parse::new_parse_sess(Some(demitter));
143 parse::parse_meta_from_source_str("cfgspec".to_str(), s, ~[], sess)
144 }).collect::<ast::CrateConfig>()
148 /// Load source from file
150 /// The string is the source
154 pub fn phase_1_parse_input(sess: Session, cfg: ast::CrateConfig, input: &Input)
156 time(sess.time_passes(), "parsing", (), |_| {
158 FileInput(ref file) => {
159 parse::parse_crate_from_file(&(*file), cfg.clone(), sess.parse_sess)
161 StrInput(ref src) => {
162 parse::parse_crate_from_source_str(anon_src(),
171 // For continuing compilation after a parsed crate has been
174 /// Run the "early phases" of the compiler: initial `cfg` processing,
175 /// syntax expansion, secondary `cfg` expansion, synthesis of a test
176 /// harness if one is to be provided and injection of a dependency on the
177 /// standard library and prelude.
178 pub fn phase_2_configure_and_expand(sess: Session,
179 cfg: ast::CrateConfig,
180 loader: &mut CrateLoader,
181 mut crate: ast::Crate)
182 -> (ast::Crate, syntax::ast_map::Map) {
183 let time_passes = sess.time_passes();
185 sess.building_library.set(session::building_library(sess.opts, &crate));
186 sess.outputs.set(session::collect_outputs(&sess, crate.attrs));
188 time(time_passes, "gated feature checking", (), |_|
189 front::feature_gate::check_crate(sess, &crate));
191 crate = time(time_passes, "crate injection", crate, |crate|
192 front::std_inject::maybe_inject_crates_ref(sess, crate));
194 // strip before expansion to allow macros to depend on
195 // configuration variables e.g/ in
197 // #[macro_escape] #[cfg(foo)]
198 // mod bar { macro_rules! baz!(() => {{}}) }
200 // baz! should not use this definition unless foo is enabled.
202 crate = time(time_passes, "configuration 1", crate, |crate|
203 front::config::strip_unconfigured_items(crate));
205 crate = time(time_passes, "expansion", crate, |crate| {
206 syntax::ext::expand::expand_crate(sess.parse_sess,
211 // dump the syntax-time crates
214 // strip again, in case expansion added anything with a #[cfg].
215 crate = time(time_passes, "configuration 2", crate, |crate|
216 front::config::strip_unconfigured_items(crate));
218 crate = time(time_passes, "maybe building test harness", crate, |crate|
219 front::test::modify_for_testing(sess, crate));
221 crate = time(time_passes, "prelude injection", crate, |crate|
222 front::std_inject::maybe_inject_prelude(sess, crate));
224 time(time_passes, "assinging node ids and indexing ast", crate, |crate|
225 front::assign_node_ids_and_map::assign_node_ids_and_map(sess, crate))
228 pub struct CrateAnalysis {
229 exp_map2: middle::resolve::ExportMap2,
230 exported_items: middle::privacy::ExportedItems,
231 public_items: middle::privacy::PublicItems,
233 maps: astencode::Maps,
234 reachable: @RefCell<HashSet<ast::NodeId>>
237 /// Run the resolution, typechecking, region checking and other
238 /// miscellaneous analysis passes on the crate. Return various
239 /// structures carrying the results of the analysis.
240 pub fn phase_3_run_analysis_passes(sess: Session,
242 ast_map: syntax::ast_map::Map) -> CrateAnalysis {
244 let time_passes = sess.time_passes();
246 time(time_passes, "external crate/lib resolution", (), |_|
247 creader::read_crates(sess, crate,
248 session::sess_os_to_meta_os(sess.targ_cfg.os),
249 token::get_ident_interner()));
251 let lang_items = time(time_passes, "language item collection", (), |_|
252 middle::lang_items::collect_language_items(crate, sess));
254 let middle::resolve::CrateMap {
257 trait_map: trait_map,
258 external_exports: external_exports,
259 last_private_map: last_private_map
261 time(time_passes, "resolution", (), |_|
262 middle::resolve::resolve_crate(sess, lang_items, crate));
264 let named_region_map = time(time_passes, "lifetime resolution", (),
265 |_| middle::resolve_lifetime::crate(sess, crate));
267 time(time_passes, "looking for entry point", (),
268 |_| middle::entry::find_entry_point(sess, crate, ast_map));
270 sess.macro_registrar_fn.with_mut(|r| *r =
271 time(time_passes, "looking for macro registrar", (), |_|
272 syntax::ext::registrar::find_macro_registrar(
273 sess.span_diagnostic, crate)));
275 let freevars = time(time_passes, "freevar finding", (), |_|
276 freevars::annotate_freevars(def_map, crate));
278 let region_map = time(time_passes, "region resolution", (), |_|
279 middle::region::resolve_crate(sess, crate));
281 let ty_cx = ty::mk_ctxt(sess, def_map, named_region_map, ast_map, freevars,
282 region_map, lang_items);
284 // passes are timed inside typeck
285 let (method_map, vtable_map) = typeck::check_crate(ty_cx, trait_map, crate);
287 // These next two const passes can probably be merged
288 time(time_passes, "const marking", (), |_|
289 middle::const_eval::process_crate(crate, ty_cx));
291 time(time_passes, "const checking", (), |_|
292 middle::check_const::check_crate(sess, crate, ast_map, def_map,
295 let maps = (external_exports, last_private_map);
296 let (exported_items, public_items) =
297 time(time_passes, "privacy checking", maps, |(a, b)|
298 middle::privacy::check_crate(ty_cx, &method_map, &exp_map2,
301 time(time_passes, "effect checking", (), |_|
302 middle::effect::check_crate(ty_cx, method_map, crate));
304 time(time_passes, "loop checking", (), |_|
305 middle::check_loop::check_crate(ty_cx, crate));
307 let middle::moves::MoveMaps {moves_map, moved_variables_set,
309 time(time_passes, "compute moves", (), |_|
310 middle::moves::compute_moves(ty_cx, method_map, crate));
312 time(time_passes, "match checking", (), |_|
313 middle::check_match::check_crate(ty_cx, method_map,
316 time(time_passes, "liveness checking", (), |_|
317 middle::liveness::check_crate(ty_cx, method_map,
318 capture_map, crate));
321 time(time_passes, "borrow checking", (), |_|
322 middle::borrowck::check_crate(ty_cx, method_map,
323 moves_map, moved_variables_set,
324 capture_map, crate));
326 time(time_passes, "kind checking", (), |_|
327 kind::check_crate(ty_cx, method_map, crate));
330 time(time_passes, "reachability checking", (), |_|
331 reachable::find_reachable(ty_cx, method_map, &exported_items));
334 let reachable_map = reachable_map.borrow();
335 time(time_passes, "death checking", (), |_| {
336 middle::dead::check_crate(ty_cx,
344 time(time_passes, "lint checking", (), |_|
345 lint::check_crate(ty_cx, method_map, &exported_items, crate));
350 exported_items: exported_items,
351 public_items: public_items,
352 maps: astencode::Maps {
354 method_map: method_map,
355 vtable_map: vtable_map,
356 capture_map: capture_map
358 reachable: reachable_map
362 pub struct CrateTranslation {
365 metadata_module: ModuleRef,
371 /// Run the translation phase to LLVM, after which the AST and analysis can
373 pub fn phase_4_translate_to_llvm(sess: Session,
375 analysis: &CrateAnalysis,
376 outputs: &OutputFilenames) -> CrateTranslation {
377 time(sess.time_passes(), "translation", crate, |crate|
378 trans::base::trans_crate(sess, crate, analysis,
379 &outputs.obj_filename))
382 /// Run LLVM itself, producing a bitcode file, assembly file or object file
383 /// as a side effect.
384 pub fn phase_5_run_llvm_passes(sess: Session,
385 trans: &CrateTranslation,
386 outputs: &OutputFilenames) {
388 if sess.no_integrated_as() {
389 let output_type = link::OutputTypeAssembly;
390 let asm_filename = outputs.obj_filename.with_extension("s");
392 time(sess.time_passes(), "LLVM passes", (), |_|
393 link::write::run_passes(sess,
398 link::write::run_assembler(sess, &asm_filename, &outputs.obj_filename);
400 // Remove assembly source, unless --save-temps was specified
401 if !sess.opts.save_temps {
402 fs::unlink(&asm_filename);
405 time(sess.time_passes(), "LLVM passes", (), |_|
406 link::write::run_passes(sess,
408 sess.opts.output_type,
409 &outputs.obj_filename));
413 /// Run the linker on any artifacts that resulted from the LLVM run.
414 /// This should produce either a finished executable or library.
415 pub fn phase_6_link_output(sess: Session,
416 trans: &CrateTranslation,
417 outputs: &OutputFilenames) {
418 time(sess.time_passes(), "linking", (), |_|
419 link::link_binary(sess,
421 &outputs.obj_filename,
422 &outputs.out_filename,
426 pub fn stop_after_phase_3(sess: Session) -> bool {
427 if sess.opts.no_trans {
428 debug!("invoked with --no-trans, returning early from compile_input");
434 pub fn stop_after_phase_1(sess: Session) -> bool {
435 if sess.opts.parse_only {
436 debug!("invoked with --parse-only, returning early from compile_input");
442 pub fn stop_after_phase_2(sess: Session) -> bool {
443 if sess.opts.no_analysis {
444 debug!("invoked with --no-analysis, returning early from compile_input");
450 pub fn stop_after_phase_5(sess: Session) -> bool {
451 if sess.opts.output_type != link::OutputTypeExe {
452 debug!("not building executable, returning early from compile_input");
458 fn write_out_deps(sess: Session, input: &Input, outputs: &OutputFilenames, crate: &ast::Crate)
460 let lm = link::build_link_meta(sess, crate.attrs, &outputs.obj_filename,
461 &mut ::util::sha2::Sha256::new());
463 let sess_outputs = sess.outputs.borrow();
464 let out_filenames = sess_outputs.get().iter()
465 .map(|&output| link::filename_for_input(&sess, output, &lm, &outputs.out_filename))
468 // Write out dependency rules to the dep-info file if requested with --dep-info
469 let deps_filename = match sess.opts.write_dependency_info {
470 // Use filename from --dep-file argument if given
471 (true, Some(ref filename)) => filename.clone(),
472 // Use default filename: crate source filename with extension replaced by ".d"
473 (true, None) => match *input {
474 FileInput(ref input_path) => {
475 let filestem = input_path.filestem().expect("input file must have stem");
476 let filename = out_filenames[0].dir_path().join(filestem).with_extension("d");
480 sess.warn("can not write --dep-info without a filename when compiling stdin.");
487 // Build a list of files used to compile the output and
488 // write Makefile-compatible dependency rules
489 let files: ~[~str] = {
490 let files = sess.codemap.files.borrow();
494 if fmap.is_real_file() {
495 Some(fmap.name.clone())
502 let mut file = io::File::create(&deps_filename);
503 for path in out_filenames.iter() {
504 write!(&mut file as &mut Writer,
505 "{}: {}\n\n", path.display(), files.connect(" "));
509 pub fn compile_input(sess: Session, cfg: ast::CrateConfig, input: &Input,
510 outdir: &Option<Path>, output: &Option<Path>) {
511 // We need nested scopes here, because the intermediate results can keep
512 // large chunks of memory alive and we want to free them as soon as
513 // possible to keep the peak memory usage low
514 let (outputs, trans) = {
515 let (expanded_crate, ast_map) = {
516 let crate = phase_1_parse_input(sess, cfg.clone(), input);
517 if stop_after_phase_1(sess) { return; }
518 let loader = &mut Loader::new(sess);
519 phase_2_configure_and_expand(sess, cfg, loader, crate)
521 let outputs = build_output_filenames(input, outdir, output,
522 expanded_crate.attrs, sess);
524 write_out_deps(sess, input, outputs, &expanded_crate);
526 if stop_after_phase_2(sess) { return; }
528 let analysis = phase_3_run_analysis_passes(sess, &expanded_crate, ast_map);
529 if stop_after_phase_3(sess) { return; }
530 let trans = phase_4_translate_to_llvm(sess, expanded_crate,
534 phase_5_run_llvm_passes(sess, &trans, outputs);
535 if stop_after_phase_5(sess) { return; }
536 phase_6_link_output(sess, &trans, outputs);
539 struct IdentifiedAnnotation {
543 impl pprust::PpAnn for IdentifiedAnnotation {
544 fn pre(&self, node: pprust::AnnNode) {
546 pprust::NodeExpr(s, _) => pprust::popen(s),
550 fn post(&self, node: pprust::AnnNode) {
552 pprust::NodeItem(s, item) => {
554 pprust::synth_comment(s, item.id.to_str());
556 pprust::NodeBlock(s, blk) => {
558 pprust::synth_comment(s, ~"block " + blk.id.to_str());
560 pprust::NodeExpr(s, expr) => {
562 pprust::synth_comment(s, expr.id.to_str());
565 pprust::NodePat(s, pat) => {
567 pprust::synth_comment(s, ~"pat " + pat.id.to_str());
573 struct TypedAnnotation {
574 analysis: CrateAnalysis,
577 impl pprust::PpAnn for TypedAnnotation {
578 fn pre(&self, node: pprust::AnnNode) {
580 pprust::NodeExpr(s, _) => pprust::popen(s),
584 fn post(&self, node: pprust::AnnNode) {
585 let tcx = self.analysis.ty_cx;
587 pprust::NodeExpr(s, expr) => {
589 pp::word(&mut s.s, "as");
591 pp::word(&mut s.s, ppaux::ty_to_str(tcx, ty::expr_ty(tcx, expr)));
599 pub fn pretty_print_input(sess: Session,
600 cfg: ast::CrateConfig,
603 let crate = phase_1_parse_input(sess, cfg.clone(), input);
605 let (crate, ast_map, is_expanded) = match ppm {
606 PpmExpanded | PpmExpandedIdentified | PpmTyped => {
607 let loader = &mut Loader::new(sess);
608 let (crate, ast_map) = phase_2_configure_and_expand(sess, cfg, loader, crate);
609 (crate, Some(ast_map), true)
611 _ => (crate, None, false)
614 let annotation = match ppm {
615 PpmIdentified | PpmExpandedIdentified => {
616 @IdentifiedAnnotation {
621 let ast_map = ast_map.expect("--pretty=typed missing ast_map");
622 let analysis = phase_3_run_analysis_passes(sess, &crate, ast_map);
627 _ => @pprust::NoAnn as @pprust::PpAnn,
630 let src = &sess.codemap.get_filemap(source_name(input)).src;
631 let mut rdr = MemReader::new(src.as_bytes().to_owned());
632 let stdout = io::stdout();
633 pprust::print_crate(sess.codemap,
634 token::get_ident_interner(),
635 sess.span_diagnostic,
639 ~stdout as ~io::Writer,
644 pub fn get_os(triple: &str) -> Option<abi::Os> {
645 for &(name, os) in os_names.iter() {
646 if triple.contains(name) { return Some(os) }
650 static os_names : &'static [(&'static str, abi::Os)] = &'static [
651 ("mingw32", abi::OsWin32),
652 ("win32", abi::OsWin32),
653 ("darwin", abi::OsMacos),
654 ("android", abi::OsAndroid),
655 ("linux", abi::OsLinux),
656 ("freebsd", abi::OsFreebsd)];
658 pub fn get_arch(triple: &str) -> Option<abi::Architecture> {
659 for &(arch, abi) in architecture_abis.iter() {
660 if triple.contains(arch) { return Some(abi) }
664 static architecture_abis : &'static [(&'static str, abi::Architecture)] = &'static [
671 ("x86_64", abi::X86_64),
674 ("xscale", abi::Arm),
677 ("mips", abi::Mips)];
679 pub fn build_target_config(sopts: @session::Options,
680 demitter: @diagnostic::Emitter)
681 -> @session::Config {
682 let os = match get_os(sopts.target_triple) {
684 None => early_error(demitter, "unknown operating system")
686 let arch = match get_arch(sopts.target_triple) {
688 None => early_error(demitter,
689 "unknown architecture: " + sopts.target_triple)
691 let (int_type, uint_type) = match arch {
692 abi::X86 => (ast::TyI32, ast::TyU32),
693 abi::X86_64 => (ast::TyI64, ast::TyU64),
694 abi::Arm => (ast::TyI32, ast::TyU32),
695 abi::Mips => (ast::TyI32, ast::TyU32)
697 let target_triple = sopts.target_triple.clone();
698 let target_strs = match arch {
699 abi::X86 => x86::get_target_strs(target_triple, os),
700 abi::X86_64 => x86_64::get_target_strs(target_triple, os),
701 abi::Arm => arm::get_target_strs(target_triple, os),
702 abi::Mips => mips::get_target_strs(target_triple, os)
704 let target_cfg = @session::Config {
707 target_strs: target_strs,
709 uint_type: uint_type,
714 pub fn host_triple() -> ~str {
715 // Get the host triple out of the build environment. This ensures that our
716 // idea of the host triple is the same as for the set of libraries we've
717 // actually built. We can't just take LLVM's host triple because they
718 // normalize all ix86 architectures to i386.
720 // Instead of grabbing the host triple (for the current host), we grab (at
721 // compile time) the target triple that this rustc is built with and
722 // calling that (at runtime) the host triple.
723 (env!("CFG_COMPILER")).to_owned()
726 pub fn build_session_options(binary: ~str,
727 matches: &getopts::Matches,
728 demitter: @diagnostic::Emitter)
729 -> @session::Options {
730 let mut outputs = ~[];
731 if matches.opt_present("lib") {
732 outputs.push(session::default_lib_output());
734 if matches.opt_present("rlib") {
735 outputs.push(session::OutputRlib)
737 if matches.opt_present("staticlib") {
738 outputs.push(session::OutputStaticlib)
740 if matches.opt_present("dylib") {
741 outputs.push(session::OutputDylib)
743 if matches.opt_present("bin") {
744 outputs.push(session::OutputExecutable)
747 let parse_only = matches.opt_present("parse-only");
748 let no_trans = matches.opt_present("no-trans");
749 let no_analysis = matches.opt_present("no-analysis");
750 let no_rpath = matches.opt_present("no-rpath");
752 let lint_levels = [lint::allow, lint::warn,
753 lint::deny, lint::forbid];
754 let mut lint_opts = ~[];
755 let lint_dict = lint::get_lint_dict();
756 for level in lint_levels.iter() {
757 let level_name = lint::level_to_str(*level);
759 let level_short = level_name.slice_chars(0, 1);
760 let level_short = level_short.to_ascii().to_upper().into_str();
761 let flags = vec::append(matches.opt_strs(level_short),
762 matches.opt_strs(level_name));
763 for lint_name in flags.iter() {
764 let lint_name = lint_name.replace("-", "_");
765 match lint_dict.find_equiv(&lint_name) {
767 early_error(demitter, format!("unknown {} flag: {}",
768 level_name, lint_name));
771 lint_opts.push((lint.lint, *level));
777 let mut debugging_opts = 0;
778 let debug_flags = matches.opt_strs("Z");
779 let debug_map = session::debugging_opts_map();
780 for debug_flag in debug_flags.iter() {
781 let mut this_bit = 0;
782 for tuple in debug_map.iter() {
783 let (name, bit) = match *tuple { (ref a, _, b) => (a, b) };
784 if *name == *debug_flag { this_bit = bit; break; }
787 early_error(demitter, format!("unknown debug flag: {}", *debug_flag))
789 debugging_opts |= this_bit;
792 if debugging_opts & session::DEBUG_LLVM != 0 {
793 unsafe { llvm::LLVMSetDebug(1); }
797 if parse_only || no_trans {
799 } else if matches.opt_present("S") &&
800 matches.opt_present("emit-llvm") {
801 link::OutputTypeLlvmAssembly
802 } else if matches.opt_present("S") {
803 link::OutputTypeAssembly
804 } else if matches.opt_present("c") {
805 link::OutputTypeObject
806 } else if matches.opt_present("emit-llvm") {
807 link::OutputTypeBitcode
808 } else { link::OutputTypeExe };
809 let sysroot_opt = matches.opt_str("sysroot").map(|m| @Path::new(m));
810 let target = matches.opt_str("target").unwrap_or(host_triple());
811 let target_cpu = matches.opt_str("target-cpu").unwrap_or(~"generic");
812 let target_feature = matches.opt_str("target-feature").unwrap_or(~"");
813 let save_temps = matches.opt_present("save-temps");
815 if (debugging_opts & session::NO_OPT) != 0 {
817 } else if matches.opt_present("O") {
818 if matches.opt_present("opt-level") {
819 early_error(demitter, "-O and --opt-level both provided");
822 } else if matches.opt_present("opt-level") {
823 match matches.opt_str("opt-level").unwrap() {
829 early_error(demitter, "optimization level needs to be between 0-3")
834 let gc = debugging_opts & session::GC != 0;
835 let extra_debuginfo = debugging_opts & session::EXTRA_DEBUG_INFO != 0;
836 let debuginfo = debugging_opts & session::DEBUG_INFO != 0 ||
839 let addl_lib_search_paths = matches.opt_strs("L").map(|s| {
840 Path::new(s.as_slice())
841 }).move_iter().collect();
842 let ar = matches.opt_str("ar");
843 let linker = matches.opt_str("linker");
844 let linker_args = matches.opt_strs("link-args").flat_map( |a| {
845 a.split(' ').filter_map(|arg| {
854 let cfg = parse_cfgspecs(matches.opt_strs("cfg"), demitter);
855 let test = matches.opt_present("test");
856 let android_cross_path = matches.opt_str("android-cross-path");
857 let write_dependency_info = (matches.opt_present("dep-info"),
858 matches.opt_str("dep-info").map(|p| Path::new(p)));
860 let custom_passes = match matches.opt_str("passes") {
863 s.split(|c: char| c == ' ' || c == ',').map(|s| {
868 let llvm_args = match matches.opt_str("llvm-args") {
871 s.split(|c: char| c == ' ' || c == ',').map(|s| {
876 let print_metas = (matches.opt_present("crate-id"),
877 matches.opt_present("crate-name"),
878 matches.opt_present("crate-file-name"));
880 let sopts = @session::Options {
884 custom_passes: custom_passes,
885 llvm_args: llvm_args,
886 debuginfo: debuginfo,
887 extra_debuginfo: extra_debuginfo,
888 lint_opts: lint_opts,
889 save_temps: save_temps,
890 output_type: output_type,
891 addl_lib_search_paths: @RefCell::new(addl_lib_search_paths),
894 linker_args: linker_args,
895 maybe_sysroot: sysroot_opt,
896 target_triple: target,
897 target_cpu: target_cpu,
898 target_feature: target_feature,
902 parse_only: parse_only,
904 no_analysis: no_analysis,
906 debugging_opts: debugging_opts,
907 android_cross_path: android_cross_path,
908 write_dependency_info: write_dependency_info,
909 print_metas: print_metas,
914 pub fn build_session(sopts: @session::Options,
915 local_crate_source_file: Option<Path>,
916 demitter: @diagnostic::Emitter)
918 let codemap = @codemap::CodeMap::new();
919 let diagnostic_handler =
920 diagnostic::mk_handler(Some(demitter));
921 let span_diagnostic_handler =
922 diagnostic::mk_span_handler(diagnostic_handler, codemap);
924 build_session_(sopts, local_crate_source_file, codemap, demitter, span_diagnostic_handler)
927 pub fn build_session_(sopts: @session::Options,
928 local_crate_source_file: Option<Path>,
929 codemap: @codemap::CodeMap,
930 demitter: @diagnostic::Emitter,
931 span_diagnostic_handler: @diagnostic::SpanHandler)
933 let target_cfg = build_target_config(sopts, demitter);
934 let p_s = parse::new_parse_sess_special_handler(span_diagnostic_handler, codemap);
935 let cstore = @CStore::new(token::get_ident_interner());
936 let filesearch = @filesearch::FileSearch::new(
937 &sopts.maybe_sysroot,
939 sopts.addl_lib_search_paths);
941 // Make the path absolute, if necessary
942 let local_crate_source_file = local_crate_source_file.map(|path|
943 if path.is_absolute() {
946 os::getcwd().join(path.clone())
951 targ_cfg: target_cfg,
956 // For a library crate, this is always none
957 entry_fn: RefCell::new(None),
958 entry_type: Cell::new(None),
959 macro_registrar_fn: RefCell::new(None),
960 span_diagnostic: span_diagnostic_handler,
961 filesearch: filesearch,
962 building_library: Cell::new(false),
963 local_crate_source_file: local_crate_source_file,
964 working_dir: os::getcwd(),
965 lints: RefCell::new(HashMap::new()),
966 node_id: Cell::new(1),
967 outputs: @RefCell::new(~[]),
971 pub fn parse_pretty(sess: Session, name: &str) -> PpMode {
973 &"normal" => PpmNormal,
974 &"expanded" => PpmExpanded,
975 &"typed" => PpmTyped,
976 &"expanded,identified" => PpmExpandedIdentified,
977 &"identified" => PpmIdentified,
979 sess.fatal("argument to `pretty` must be one of `normal`, \
980 `expanded`, `typed`, `identified`, \
981 or `expanded,identified`");
986 // rustc command line options
987 pub fn optgroups() -> ~[getopts::groups::OptGroup] {
989 optflag("c", "", "Compile and assemble, but do not link"),
990 optmulti("", "cfg", "Configure the compilation
991 environment", "SPEC"),
992 optflag("", "emit-llvm",
993 "Produce an LLVM assembly file if used with -S option;
994 produce an LLVM bitcode file otherwise"),
995 optflag("h", "help","Display this message"),
996 optmulti("L", "", "Add a directory to the library search path",
998 optflag("", "bin", "Compile an executable crate (default)"),
999 optflag("", "lib", "Compile a rust library crate using the compiler's default"),
1000 optflag("", "rlib", "Compile a rust library crate as an rlib file"),
1001 optflag("", "staticlib", "Compile a static library crate"),
1002 optflag("", "dylib", "Compile a dynamic library crate"),
1003 optopt("", "linker", "Program to use for linking instead of the default.", "LINKER"),
1004 optopt("", "ar", "Program to use for managing archives instead of the default.", "AR"),
1005 optflag("", "crate-id", "Output the crate id and exit"),
1006 optflag("", "crate-name", "Output the crate name and exit"),
1007 optflag("", "crate-file-name", "Output the file(s) that would be written if compilation \
1008 continued and exit"),
1009 optmulti("", "link-args", "FLAGS is a space-separated list of flags
1010 passed to the linker", "FLAGS"),
1011 optflag("", "ls", "List the symbols defined by a library crate"),
1012 optflag("", "no-trans",
1013 "Run all passes except translation; no output"),
1014 optflag("", "no-analysis",
1015 "Parse and expand the output, but run no analysis or produce \
1017 optflag("O", "", "Equivalent to --opt-level=2"),
1018 optopt("o", "", "Write output to <filename>", "FILENAME"),
1019 optopt("", "opt-level",
1020 "Optimize with possible levels 0-3", "LEVEL"),
1021 optopt("", "passes", "Comma or space separated list of pass names to use. \
1022 Appends to the default list of passes to run for the \
1023 specified current optimization level. A value of \
1024 \"list\" will list all of the available passes", "NAMES"),
1025 optopt("", "llvm-args", "A list of arguments to pass to llvm, comma \
1026 separated", "ARGS"),
1027 optflag("", "no-rpath", "Disables setting the rpath in libs/exes"),
1028 optopt( "", "out-dir",
1029 "Write output to compiler-chosen filename
1031 optflag("", "parse-only",
1032 "Parse only; do not compile, assemble, or link"),
1033 optflagopt("", "pretty",
1034 "Pretty-print the input instead of compiling;
1035 valid types are: normal (un-annotated source),
1036 expanded (crates expanded),
1037 typed (crates expanded, with type annotations),
1038 or identified (fully parenthesized,
1039 AST nodes and blocks with IDs)", "TYPE"),
1040 optflag("S", "", "Compile only; do not assemble or link"),
1041 optflagopt("", "dep-info",
1042 "Output dependency info to <filename> after compiling", "FILENAME"),
1043 optflag("", "save-temps",
1044 "Write intermediate files (.bc, .opt.bc, .o)
1045 in addition to normal output"),
1046 optopt("", "sysroot",
1047 "Override the system root", "PATH"),
1048 optflag("", "test", "Build a test harness"),
1049 optopt("", "target",
1050 "Target triple cpu-manufacturer-kernel[-os]
1051 to compile for (see chapter 3.4 of http://www.sourceware.org/autobook/
1052 for details)", "TRIPLE"),
1053 optopt("", "target-cpu",
1054 "Select target processor (llc -mcpu=help
1055 for details)", "CPU"),
1056 optopt("", "target-feature",
1057 "Target specific attributes (llc -mattr=help
1058 for details)", "FEATURE"),
1059 optopt("", "android-cross-path",
1060 "The path to the Android NDK", "PATH"),
1061 optmulti("W", "warn",
1062 "Set lint warnings", "OPT"),
1063 optmulti("A", "allow",
1064 "Set lint allowed", "OPT"),
1065 optmulti("D", "deny",
1066 "Set lint denied", "OPT"),
1067 optmulti("F", "forbid",
1068 "Set lint forbidden", "OPT"),
1069 optmulti("Z", "", "Set internal debugging options", "FLAG"),
1070 optflag( "v", "version",
1071 "Print version info and exit"),
1075 pub struct OutputFilenames {
1080 pub fn build_output_filenames(input: &Input,
1081 odir: &Option<Path>,
1082 ofile: &Option<Path>,
1083 attrs: &[ast::Attribute],
1085 -> ~OutputFilenames {
1088 let sopts = sess.opts;
1089 let stop_after_codegen = sopts.output_type != link::OutputTypeExe;
1091 let obj_suffix = match sopts.output_type {
1092 link::OutputTypeNone => ~"none",
1093 link::OutputTypeBitcode => ~"bc",
1094 link::OutputTypeAssembly => ~"s",
1095 link::OutputTypeLlvmAssembly => ~"ll",
1096 // Object and exe output both use the '.o' extension here
1097 link::OutputTypeObject | link::OutputTypeExe => ~"o"
1102 // "-" as input file will cause the parser to read from stdin so we
1103 // have to make up a name
1104 // We want to toss everything after the final '.'
1105 let dirpath = match *odir {
1106 Some(ref d) => (*d).clone(),
1107 None => match *input {
1108 StrInput(_) => os::getcwd(),
1109 FileInput(ref ifile) => (*ifile).dir_path()
1113 let mut stem = match *input {
1114 // FIXME (#9639): This needs to handle non-utf8 paths
1115 FileInput(ref ifile) => (*ifile).filestem_str().unwrap().to_managed(),
1116 StrInput(_) => @"rust_out"
1119 // If a crateid is present, we use it as the link name
1120 let crateid = attr::find_crateid(attrs);
1124 stem = crateid.name.to_managed()
1128 if sess.building_library.get() {
1129 out_path = dirpath.join(os::dll_filename(stem));
1131 let mut p = dirpath.join(stem);
1132 p.set_extension(obj_suffix);
1136 out_path = dirpath.join(stem);
1137 obj_path = out_path.with_extension(obj_suffix);
1141 Some(ref out_file) => {
1142 out_path = out_file.clone();
1143 obj_path = if stop_after_codegen {
1146 out_file.with_extension(obj_suffix)
1149 if sess.building_library.get() {
1150 sess.warn("ignoring specified output filename for library.");
1154 sess.warn("ignoring --out-dir flag due to -o flag.");
1160 out_filename: out_path,
1161 obj_filename: obj_path
1165 pub fn early_error(emitter: &diagnostic::Emitter, msg: &str) -> ! {
1166 emitter.emit(None, msg, diagnostic::Fatal);
1167 fail!(diagnostic::FatalError);
1170 pub fn list_metadata(sess: Session, path: &Path, out: &mut io::Writer) {
1171 metadata::loader::list_file_metadata(
1172 token::get_ident_interner(),
1173 session::sess_os_to_meta_os(sess.targ_cfg.os), path, out);
1179 use driver::driver::{build_configuration, build_session};
1180 use driver::driver::{build_session_options, optgroups};
1182 use extra::getopts::groups::getopts;
1184 use syntax::attr::AttrMetaMethods;
1185 use syntax::diagnostic;
1187 // When the user supplies --test we should implicitly supply --cfg test
1189 fn test_switch_implies_cfg_test() {
1191 &match getopts([~"--test"], optgroups()) {
1193 Err(f) => fail!("test_switch_implies_cfg_test: {}", f.to_err_msg())
1195 let sessopts = build_session_options(~"rustc", matches, @diagnostic::DefaultEmitter);
1196 let sess = build_session(sessopts, None, @diagnostic::DefaultEmitter);
1197 let cfg = build_configuration(sess);
1198 assert!((attr::contains_name(cfg, "test")));
1201 // When the user supplies --test and --cfg test, don't implicitly add
1202 // another --cfg test
1204 fn test_switch_implies_cfg_test_unless_cfg_test() {
1206 &match getopts([~"--test", ~"--cfg=test"], optgroups()) {
1209 fail!("test_switch_implies_cfg_test_unless_cfg_test: {}",
1213 let sessopts = build_session_options(~"rustc", matches, @diagnostic::DefaultEmitter);
1214 let sess = build_session(sessopts, None, @diagnostic::DefaultEmitter);
1215 let cfg = build_configuration(sess);
1216 let mut test_items = cfg.iter().filter(|m| "test" == m.name());
1217 assert!(test_items.next().is_some());
1218 assert!(test_items.next().is_none());