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, CrateTypeExecutable, CrateType,
15 FullDebugInfo, LimitedDebugInfo, NoDebugInfo};
16 use driver::session::{Session, No, Less, Default};
20 use lib::llvm::{ContextRef, ModuleRef};
21 use metadata::common::LinkMeta;
22 use metadata::{creader, filesearch};
23 use metadata::cstore::CStore;
24 use metadata::creader::Loader;
26 use middle::{trans, freevars, kind, ty, typeck, lint, astencode, reachable};
28 use util::common::time;
30 use util::nodemap::{NodeMap, NodeSet};
32 use serialize::{json, Encodable};
34 use std::cell::{Cell, RefCell};
37 use std::io::MemReader;
40 use getopts::{optopt, optmulti, optflag, optflagopt};
45 use syntax::attr::{AttrMetaMethods};
47 use syntax::crateid::CrateId;
48 use syntax::diagnostic;
49 use syntax::diagnostic::Emitter;
50 use syntax::ext::base::CrateLoader;
52 use syntax::parse::token::InternedString;
53 use syntax::parse::token;
54 use syntax::print::{pp, pprust};
66 * The name used for source code that doesn't originate in a file
67 * (e.g. source from stdin or a string)
69 pub fn anon_src() -> ~str {
73 pub fn source_name(input: &Input) -> ~str {
75 // FIXME (#9639): This needs to handle non-utf8 paths
76 FileInput(ref ifile) => ifile.as_str().unwrap().to_str(),
77 StrInput(_) => anon_src()
81 pub fn default_configuration(sess: &Session) ->
83 let tos = match sess.targ_cfg.os {
84 abi::OsWin32 => InternedString::new("win32"),
85 abi::OsMacos => InternedString::new("macos"),
86 abi::OsLinux => InternedString::new("linux"),
87 abi::OsAndroid => InternedString::new("android"),
88 abi::OsFreebsd => InternedString::new("freebsd"),
91 // ARM is bi-endian, however using NDK seems to default
92 // to little-endian unless a flag is provided.
93 let (end,arch,wordsz) = match sess.targ_cfg.arch {
94 abi::X86 => ("little", "x86", "32"),
95 abi::X86_64 => ("little", "x86_64", "64"),
96 abi::Arm => ("little", "arm", "32"),
97 abi::Mips => ("big", "mips", "32")
100 let fam = match sess.targ_cfg.os {
101 abi::OsWin32 => InternedString::new("windows"),
102 _ => InternedString::new("unix")
105 let mk = attr::mk_name_value_item_str;
106 return vec!(// Target bindings.
107 attr::mk_word_item(fam.clone()),
108 mk(InternedString::new("target_os"), tos),
109 mk(InternedString::new("target_family"), fam),
110 mk(InternedString::new("target_arch"), InternedString::new(arch)),
111 mk(InternedString::new("target_endian"), InternedString::new(end)),
112 mk(InternedString::new("target_word_size"),
113 InternedString::new(wordsz))
117 pub fn append_configuration(cfg: &mut ast::CrateConfig,
118 name: InternedString) {
119 if !cfg.iter().any(|mi| mi.name() == name) {
120 cfg.push(attr::mk_word_item(name))
124 pub fn build_configuration(sess: &Session) -> ast::CrateConfig {
125 // Combine the configuration requested by the session (command line) with
126 // some default and generated configuration items
127 let default_cfg = default_configuration(sess);
128 let mut user_cfg = sess.opts.cfg.clone();
129 // If the user wants a test runner, then add the test cfg
131 append_configuration(&mut user_cfg, InternedString::new("test"))
133 // If the user requested GC, then add the GC cfg
134 append_configuration(&mut user_cfg, if sess.opts.gc {
135 InternedString::new("gc")
137 InternedString::new("nogc")
139 user_cfg.move_iter().collect::<Vec<_>>().append(default_cfg.as_slice())
142 // Convert strings provided as --cfg [cfgspec] into a crate_cfg
143 fn parse_cfgspecs(cfgspecs: Vec<~str> )
144 -> ast::CrateConfig {
145 cfgspecs.move_iter().map(|s| {
146 parse::parse_meta_from_source_str("cfgspec".to_str(),
149 &parse::new_parse_sess())
150 }).collect::<ast::CrateConfig>()
154 /// Load source from file
156 /// The string is the source
161 fn filestem(&self) -> ~str {
163 FileInput(ref ifile) => ifile.filestem_str().unwrap().to_str(),
164 StrInput(_) => "rust_out".to_owned(),
170 pub fn phase_1_parse_input(sess: &Session, cfg: ast::CrateConfig, input: &Input)
172 let krate = time(sess.time_passes(), "parsing", (), |_| {
174 FileInput(ref file) => {
175 parse::parse_crate_from_file(&(*file), cfg.clone(), &sess.parse_sess)
177 StrInput(ref src) => {
178 parse::parse_crate_from_source_str(anon_src(),
186 if sess.opts.debugging_opts & session::AST_JSON_NOEXPAND != 0 {
187 let mut stdout = io::BufferedWriter::new(io::stdout());
188 let mut json = json::PrettyEncoder::new(&mut stdout);
189 // unwrapping so IoError isn't ignored
190 krate.encode(&mut json).unwrap();
193 if sess.show_span() {
194 front::show_span::run(sess, &krate);
200 // For continuing compilation after a parsed crate has been
203 /// Run the "early phases" of the compiler: initial `cfg` processing,
204 /// syntax expansion, secondary `cfg` expansion, synthesis of a test
205 /// harness if one is to be provided and injection of a dependency on the
206 /// standard library and prelude.
207 pub fn phase_2_configure_and_expand(sess: &Session,
208 loader: &mut CrateLoader,
209 mut krate: ast::Crate,
211 -> (ast::Crate, syntax::ast_map::Map) {
212 let time_passes = sess.time_passes();
214 sess.building_library.set(session::building_library(&sess.opts, &krate));
215 *sess.crate_types.borrow_mut() = session::collect_crate_types(sess, krate.attrs.as_slice());
217 time(time_passes, "gated feature checking", (), |_|
218 front::feature_gate::check_crate(sess, &krate));
220 krate = time(time_passes, "crate injection", krate, |krate|
221 front::std_inject::maybe_inject_crates_ref(sess, krate));
223 // strip before expansion to allow macros to depend on
224 // configuration variables e.g/ in
226 // #[macro_escape] #[cfg(foo)]
227 // mod bar { macro_rules! baz!(() => {{}}) }
229 // baz! should not use this definition unless foo is enabled.
231 krate = time(time_passes, "configuration 1", krate, |krate|
232 front::config::strip_unconfigured_items(krate));
234 krate = time(time_passes, "expansion", krate, |krate| {
235 let cfg = syntax::ext::expand::ExpansionConfig {
237 deriving_hash_type_parameter: sess.features.default_type_params.get(),
238 crate_id: crate_id.clone(),
240 syntax::ext::expand::expand_crate(&sess.parse_sess,
245 // strip again, in case expansion added anything with a #[cfg].
246 krate = time(time_passes, "configuration 2", krate, |krate|
247 front::config::strip_unconfigured_items(krate));
249 krate = time(time_passes, "maybe building test harness", krate, |krate|
250 front::test::modify_for_testing(sess, krate));
252 krate = time(time_passes, "prelude injection", krate, |krate|
253 front::std_inject::maybe_inject_prelude(sess, krate));
255 let (krate, map) = time(time_passes, "assinging node ids and indexing ast", krate, |krate|
256 front::assign_node_ids_and_map::assign_node_ids_and_map(sess, krate));
258 if sess.opts.debugging_opts & session::AST_JSON != 0 {
259 let mut stdout = io::BufferedWriter::new(io::stdout());
260 let mut json = json::PrettyEncoder::new(&mut stdout);
261 // unwrapping so IoError isn't ignored
262 krate.encode(&mut json).unwrap();
268 pub struct CrateAnalysis {
269 pub exp_map2: middle::resolve::ExportMap2,
270 pub exported_items: middle::privacy::ExportedItems,
271 pub public_items: middle::privacy::PublicItems,
273 pub maps: astencode::Maps,
274 pub reachable: NodeSet,
277 /// Run the resolution, typechecking, region checking and other
278 /// miscellaneous analysis passes on the crate. Return various
279 /// structures carrying the results of the analysis.
280 pub fn phase_3_run_analysis_passes(sess: Session,
282 ast_map: syntax::ast_map::Map) -> CrateAnalysis {
284 let time_passes = sess.time_passes();
286 time(time_passes, "external crate/lib resolution", (), |_|
287 creader::read_crates(&sess, krate,
288 session::sess_os_to_meta_os(sess.targ_cfg.os),
289 token::get_ident_interner()));
291 let lang_items = time(time_passes, "language item collection", (), |_|
292 middle::lang_items::collect_language_items(krate, &sess));
294 let middle::resolve::CrateMap {
297 trait_map: trait_map,
298 external_exports: external_exports,
299 last_private_map: last_private_map
301 time(time_passes, "resolution", (), |_|
302 middle::resolve::resolve_crate(&sess, &lang_items, krate));
304 // Discard MTWT tables that aren't required past resolution.
305 syntax::ext::mtwt::clear_tables();
307 let named_region_map = time(time_passes, "lifetime resolution", (),
308 |_| middle::resolve_lifetime::krate(&sess, krate));
310 time(time_passes, "looking for entry point", (),
311 |_| middle::entry::find_entry_point(&sess, krate, &ast_map));
313 sess.macro_registrar_fn.set(
314 time(time_passes, "looking for macro registrar", (), |_|
315 syntax::ext::registrar::find_macro_registrar(
316 sess.diagnostic(), krate)));
318 let freevars = time(time_passes, "freevar finding", (), |_|
319 freevars::annotate_freevars(&def_map, krate));
321 let region_map = time(time_passes, "region resolution", (), |_|
322 middle::region::resolve_crate(&sess, krate));
324 time(time_passes, "loop checking", (), |_|
325 middle::check_loop::check_crate(&sess, krate));
327 let ty_cx = ty::mk_ctxt(sess, def_map, named_region_map, ast_map,
328 freevars, region_map, lang_items);
330 // passes are timed inside typeck
331 typeck::check_crate(&ty_cx, trait_map, krate);
333 time(time_passes, "check static items", (), |_|
334 middle::check_static::check_crate(&ty_cx, krate));
336 // These next two const passes can probably be merged
337 time(time_passes, "const marking", (), |_|
338 middle::const_eval::process_crate(krate, &ty_cx));
340 time(time_passes, "const checking", (), |_|
341 middle::check_const::check_crate(krate, &ty_cx));
343 let maps = (external_exports, last_private_map);
344 let (exported_items, public_items) =
345 time(time_passes, "privacy checking", maps, |(a, b)|
346 middle::privacy::check_crate(&ty_cx, &exp_map2, a, b, krate));
348 time(time_passes, "effect checking", (), |_|
349 middle::effect::check_crate(&ty_cx, krate));
351 let middle::moves::MoveMaps {moves_map, moved_variables_set,
353 time(time_passes, "compute moves", (), |_|
354 middle::moves::compute_moves(&ty_cx, krate));
356 time(time_passes, "match checking", (), |_|
357 middle::check_match::check_crate(&ty_cx, &moves_map, krate));
359 time(time_passes, "liveness checking", (), |_|
360 middle::liveness::check_crate(&ty_cx, &capture_map, krate));
363 time(time_passes, "borrow checking", (), |_|
364 middle::borrowck::check_crate(&ty_cx, &moves_map,
365 &moved_variables_set,
366 &capture_map, krate));
369 drop(moved_variables_set);
371 time(time_passes, "kind checking", (), |_|
372 kind::check_crate(&ty_cx, krate));
375 time(time_passes, "reachability checking", (), |_|
376 reachable::find_reachable(&ty_cx, &exported_items));
378 time(time_passes, "death checking", (), |_| {
379 middle::dead::check_crate(&ty_cx,
385 time(time_passes, "lint checking", (), |_|
386 lint::check_crate(&ty_cx, &exported_items, krate));
391 exported_items: exported_items,
392 public_items: public_items,
393 maps: astencode::Maps {
395 capture_map: RefCell::new(capture_map)
397 reachable: reachable_map
401 pub struct CrateTranslation {
402 pub context: ContextRef,
403 pub module: ModuleRef,
404 pub metadata_module: ModuleRef,
406 pub metadata: Vec<u8>,
407 pub reachable: Vec<~str>,
410 /// Run the translation phase to LLVM, after which the AST and analysis can
412 pub fn phase_4_translate_to_llvm(krate: ast::Crate,
413 analysis: CrateAnalysis,
414 outputs: &OutputFilenames) -> (ty::ctxt, CrateTranslation) {
415 // Option dance to work around the lack of stack once closures.
416 let time_passes = analysis.ty_cx.sess.time_passes();
417 let mut analysis = Some(analysis);
418 time(time_passes, "translation", krate, |krate|
419 trans::base::trans_crate(krate, analysis.take_unwrap(), outputs))
422 /// Run LLVM itself, producing a bitcode file, assembly file or object file
423 /// as a side effect.
424 pub fn phase_5_run_llvm_passes(sess: &Session,
425 trans: &CrateTranslation,
426 outputs: &OutputFilenames) {
427 if sess.opts.cg.no_integrated_as {
428 let output_type = link::OutputTypeAssembly;
430 time(sess.time_passes(), "LLVM passes", (), |_|
431 link::write::run_passes(sess, trans, [output_type], outputs));
433 link::write::run_assembler(sess, outputs);
435 // Remove assembly source, unless --save-temps was specified
436 if !sess.opts.cg.save_temps {
437 fs::unlink(&outputs.temp_path(link::OutputTypeAssembly)).unwrap();
440 time(sess.time_passes(), "LLVM passes", (), |_|
441 link::write::run_passes(sess,
443 sess.opts.output_types.as_slice(),
448 /// Run the linker on any artifacts that resulted from the LLVM run.
449 /// This should produce either a finished executable or library.
450 pub fn phase_6_link_output(sess: &Session,
451 trans: &CrateTranslation,
452 outputs: &OutputFilenames) {
453 time(sess.time_passes(), "linking", (), |_|
454 link::link_binary(sess,
457 &trans.link.crateid));
460 pub fn stop_after_phase_3(sess: &Session) -> bool {
461 if sess.opts.no_trans {
462 debug!("invoked with --no-trans, returning early from compile_input");
468 pub fn stop_after_phase_1(sess: &Session) -> bool {
469 if sess.opts.parse_only {
470 debug!("invoked with --parse-only, returning early from compile_input");
473 if sess.show_span() {
476 return sess.opts.debugging_opts & session::AST_JSON_NOEXPAND != 0;
479 pub fn stop_after_phase_2(sess: &Session) -> bool {
480 if sess.opts.no_analysis {
481 debug!("invoked with --no-analysis, returning early from compile_input");
484 return sess.opts.debugging_opts & session::AST_JSON != 0;
487 pub fn stop_after_phase_5(sess: &Session) -> bool {
488 if !sess.opts.output_types.iter().any(|&i| i == link::OutputTypeExe) {
489 debug!("not building executable, returning early from compile_input");
495 fn write_out_deps(sess: &Session,
497 outputs: &OutputFilenames,
498 krate: &ast::Crate) -> io::IoResult<()> {
499 let id = link::find_crate_id(krate.attrs.as_slice(), outputs.out_filestem);
501 let mut out_filenames = Vec::new();
502 for output_type in sess.opts.output_types.iter() {
503 let file = outputs.path(*output_type);
505 link::OutputTypeExe => {
506 for output in sess.crate_types.borrow().iter() {
507 let p = link::filename_for_input(sess, *output, &id, &file);
508 out_filenames.push(p);
511 _ => { out_filenames.push(file); }
515 // Write out dependency rules to the dep-info file if requested with
517 let deps_filename = match sess.opts.write_dependency_info {
518 // Use filename from --dep-file argument if given
519 (true, Some(ref filename)) => filename.clone(),
520 // Use default filename: crate source filename with extension replaced
522 (true, None) => match *input {
523 FileInput(..) => outputs.with_extension("d"),
525 sess.warn("can not write --dep-info without a filename \
526 when compiling stdin.");
533 // Build a list of files used to compile the output and
534 // write Makefile-compatible dependency rules
535 let files: Vec<~str> = sess.codemap().files.borrow()
536 .iter().filter_map(|fmap| {
537 if fmap.is_real_file() {
538 Some(fmap.name.clone())
543 let mut file = try!(io::File::create(&deps_filename));
544 for path in out_filenames.iter() {
545 try!(write!(&mut file as &mut Writer,
546 "{}: {}\n\n", path.display(), files.connect(" ")));
551 pub fn compile_input(sess: Session, cfg: ast::CrateConfig, input: &Input,
552 outdir: &Option<Path>, output: &Option<Path>) {
553 // We need nested scopes here, because the intermediate results can keep
554 // large chunks of memory alive and we want to free them as soon as
555 // possible to keep the peak memory usage low
556 let (outputs, trans, sess) = {
557 let (outputs, expanded_crate, ast_map) = {
558 let krate = phase_1_parse_input(&sess, cfg, input);
559 if stop_after_phase_1(&sess) { return; }
560 let outputs = build_output_filenames(input,
563 krate.attrs.as_slice(),
565 let loader = &mut Loader::new(&sess);
566 let id = link::find_crate_id(krate.attrs.as_slice(),
567 outputs.out_filestem);
568 let (expanded_crate, ast_map) = phase_2_configure_and_expand(&sess, loader,
570 (outputs, expanded_crate, ast_map)
572 write_out_deps(&sess, input, &outputs, &expanded_crate).unwrap();
574 if stop_after_phase_2(&sess) { return; }
576 let analysis = phase_3_run_analysis_passes(sess, &expanded_crate, ast_map);
577 if stop_after_phase_3(&analysis.ty_cx.sess) { return; }
578 let (tcx, trans) = phase_4_translate_to_llvm(expanded_crate,
581 // Discard interned strings as they are no longer required.
582 token::get_ident_interner().clear();
584 (outputs, trans, tcx.sess)
586 phase_5_run_llvm_passes(&sess, &trans, &outputs);
587 if stop_after_phase_5(&sess) { return; }
588 phase_6_link_output(&sess, &trans, &outputs);
591 struct IdentifiedAnnotation;
593 impl pprust::PpAnn for IdentifiedAnnotation {
595 s: &mut pprust::State,
596 node: pprust::AnnNode) -> io::IoResult<()> {
598 pprust::NodeExpr(_) => s.popen(),
603 s: &mut pprust::State,
604 node: pprust::AnnNode) -> io::IoResult<()> {
606 pprust::NodeItem(item) => {
607 try!(pp::space(&mut s.s));
608 s.synth_comment(item.id.to_str())
610 pprust::NodeBlock(blk) => {
611 try!(pp::space(&mut s.s));
612 s.synth_comment("block ".to_owned() + blk.id.to_str())
614 pprust::NodeExpr(expr) => {
615 try!(pp::space(&mut s.s));
616 try!(s.synth_comment(expr.id.to_str()));
619 pprust::NodePat(pat) => {
620 try!(pp::space(&mut s.s));
621 s.synth_comment("pat ".to_owned() + pat.id.to_str())
627 struct TypedAnnotation {
628 analysis: CrateAnalysis,
631 impl pprust::PpAnn for TypedAnnotation {
633 s: &mut pprust::State,
634 node: pprust::AnnNode) -> io::IoResult<()> {
636 pprust::NodeExpr(_) => s.popen(),
641 s: &mut pprust::State,
642 node: pprust::AnnNode) -> io::IoResult<()> {
643 let tcx = &self.analysis.ty_cx;
645 pprust::NodeExpr(expr) => {
646 try!(pp::space(&mut s.s));
647 try!(pp::word(&mut s.s, "as"));
648 try!(pp::space(&mut s.s));
649 try!(pp::word(&mut s.s,
650 ppaux::ty_to_str(tcx, ty::expr_ty(tcx, expr))));
658 pub fn pretty_print_input(sess: Session,
659 cfg: ast::CrateConfig,
662 ofile: Option<Path>) {
663 let krate = phase_1_parse_input(&sess, cfg, input);
664 let id = link::find_crate_id(krate.attrs.as_slice(), input.filestem());
666 let (krate, ast_map, is_expanded) = match ppm {
667 PpmExpanded | PpmExpandedIdentified | PpmTyped => {
668 let loader = &mut Loader::new(&sess);
669 let (krate, ast_map) = phase_2_configure_and_expand(&sess, loader,
671 (krate, Some(ast_map), true)
673 _ => (krate, None, false)
676 let src_name = source_name(input);
677 let src = Vec::from_slice(sess.codemap().get_filemap(src_name).src.as_bytes());
678 let mut rdr = MemReader::new(src);
680 let out = match ofile {
681 None => ~io::stdout() as ~Writer,
683 let r = io::File::create(&p);
685 Ok(w) => ~w as ~Writer,
686 Err(e) => fail!("print-print failed to open {} due to {}",
692 PpmIdentified | PpmExpandedIdentified => {
693 pprust::print_crate(sess.codemap(),
699 &IdentifiedAnnotation,
703 let ast_map = ast_map.expect("--pretty=typed missing ast_map");
704 let analysis = phase_3_run_analysis_passes(sess, &krate, ast_map);
705 let annotation = TypedAnnotation {
708 pprust::print_crate(annotation.analysis.ty_cx.sess.codemap(),
709 annotation.analysis.ty_cx.sess.diagnostic(),
718 pprust::print_crate(sess.codemap(),
731 pub fn get_os(triple: &str) -> Option<abi::Os> {
732 for &(name, os) in os_names.iter() {
733 if triple.contains(name) { return Some(os) }
737 static os_names : &'static [(&'static str, abi::Os)] = &'static [
738 ("mingw32", abi::OsWin32),
739 ("win32", abi::OsWin32),
740 ("darwin", abi::OsMacos),
741 ("android", abi::OsAndroid),
742 ("linux", abi::OsLinux),
743 ("freebsd", abi::OsFreebsd)];
745 pub fn get_arch(triple: &str) -> Option<abi::Architecture> {
746 for &(arch, abi) in architecture_abis.iter() {
747 if triple.contains(arch) { return Some(abi) }
751 static architecture_abis : &'static [(&'static str, abi::Architecture)] = &'static [
758 ("x86_64", abi::X86_64),
761 ("xscale", abi::Arm),
764 ("mips", abi::Mips)];
766 pub fn build_target_config(sopts: &session::Options) -> session::Config {
767 let os = match get_os(sopts.target_triple) {
769 None => early_error("unknown operating system")
771 let arch = match get_arch(sopts.target_triple) {
773 None => early_error("unknown architecture: " + sopts.target_triple)
775 let (int_type, uint_type) = match arch {
776 abi::X86 => (ast::TyI32, ast::TyU32),
777 abi::X86_64 => (ast::TyI64, ast::TyU64),
778 abi::Arm => (ast::TyI32, ast::TyU32),
779 abi::Mips => (ast::TyI32, ast::TyU32)
781 let target_triple = sopts.target_triple.clone();
782 let target_strs = match arch {
783 abi::X86 => x86::get_target_strs(target_triple, os),
784 abi::X86_64 => x86_64::get_target_strs(target_triple, os),
785 abi::Arm => arm::get_target_strs(target_triple, os),
786 abi::Mips => mips::get_target_strs(target_triple, os)
791 target_strs: target_strs,
793 uint_type: uint_type,
797 pub fn host_triple() -> ~str {
798 // Get the host triple out of the build environment. This ensures that our
799 // idea of the host triple is the same as for the set of libraries we've
800 // actually built. We can't just take LLVM's host triple because they
801 // normalize all ix86 architectures to i386.
803 // Instead of grabbing the host triple (for the current host), we grab (at
804 // compile time) the target triple that this rustc is built with and
805 // calling that (at runtime) the host triple.
806 (env!("CFG_COMPILER_HOST_TRIPLE")).to_owned()
809 pub fn build_session_options(matches: &getopts::Matches) -> session::Options {
810 let mut crate_types: Vec<CrateType> = Vec::new();
811 let unparsed_crate_types = matches.opt_strs("crate-type");
812 for unparsed_crate_type in unparsed_crate_types.iter() {
813 for part in unparsed_crate_type.split(',') {
814 let new_part = match part {
815 "lib" => session::default_lib_output(),
816 "rlib" => session::CrateTypeRlib,
817 "staticlib" => session::CrateTypeStaticlib,
818 "dylib" => session::CrateTypeDylib,
819 "bin" => session::CrateTypeExecutable,
820 _ => early_error(format!("unknown crate type: `{}`", part))
822 crate_types.push(new_part)
826 let parse_only = matches.opt_present("parse-only");
827 let no_trans = matches.opt_present("no-trans");
828 let no_analysis = matches.opt_present("no-analysis");
830 let lint_levels = [lint::allow, lint::warn,
831 lint::deny, lint::forbid];
832 let mut lint_opts = Vec::new();
833 let lint_dict = lint::get_lint_dict();
834 for level in lint_levels.iter() {
835 let level_name = lint::level_to_str(*level);
837 let level_short = level_name.slice_chars(0, 1);
838 let level_short = level_short.to_ascii().to_upper().into_str();
839 let flags = matches.opt_strs(level_short).move_iter().collect::<Vec<_>>().append(
840 matches.opt_strs(level_name).as_slice());
841 for lint_name in flags.iter() {
842 let lint_name = lint_name.replace("-", "_");
843 match lint_dict.find_equiv(&lint_name) {
845 early_error(format!("unknown {} flag: {}",
846 level_name, lint_name));
849 lint_opts.push((lint.lint, *level));
855 let mut debugging_opts = 0;
856 let debug_flags = matches.opt_strs("Z");
857 let debug_map = session::debugging_opts_map();
858 for debug_flag in debug_flags.iter() {
859 let mut this_bit = 0;
860 for tuple in debug_map.iter() {
861 let (name, bit) = match *tuple { (ref a, _, b) => (a, b) };
862 if *name == *debug_flag { this_bit = bit; break; }
865 early_error(format!("unknown debug flag: {}", *debug_flag))
867 debugging_opts |= this_bit;
870 if debugging_opts & session::DEBUG_LLVM != 0 {
871 unsafe { llvm::LLVMSetDebug(1); }
874 let mut output_types = Vec::new();
875 if !parse_only && !no_trans {
876 let unparsed_output_types = matches.opt_strs("emit");
877 for unparsed_output_type in unparsed_output_types.iter() {
878 for part in unparsed_output_type.split(',') {
879 let output_type = match part.as_slice() {
880 "asm" => link::OutputTypeAssembly,
881 "ir" => link::OutputTypeLlvmAssembly,
882 "bc" => link::OutputTypeBitcode,
883 "obj" => link::OutputTypeObject,
884 "link" => link::OutputTypeExe,
885 _ => early_error(format!("unknown emission type: `{}`", part))
887 output_types.push(output_type)
891 output_types.as_mut_slice().sort();
892 output_types.dedup();
893 if output_types.len() == 0 {
894 output_types.push(link::OutputTypeExe);
897 let sysroot_opt = matches.opt_str("sysroot").map(|m| Path::new(m));
898 let target = matches.opt_str("target").unwrap_or(host_triple());
900 if (debugging_opts & session::NO_OPT) != 0 {
902 } else if matches.opt_present("O") {
903 if matches.opt_present("opt-level") {
904 early_error("-O and --opt-level both provided");
907 } else if matches.opt_present("opt-level") {
908 match matches.opt_str("opt-level").as_ref().map(|s| s.as_slice()) {
912 Some("2") => Default,
913 Some("3") => Aggressive,
915 early_error(format!("optimization level needs to be between 0-3 \
916 (instead was `{}`)", arg));
923 let gc = debugging_opts & session::GC != 0;
924 let debuginfo = if matches.opt_present("g") {
925 if matches.opt_present("debuginfo") {
926 early_error("-g and --debuginfo both provided");
929 } else if matches.opt_present("debuginfo") {
930 match matches.opt_str("debuginfo").as_ref().map(|s| s.as_slice()) {
931 Some("0") => NoDebugInfo,
932 Some("1") => LimitedDebugInfo,
934 Some("2") => FullDebugInfo,
936 early_error(format!("optimization level needs to be between 0-3 \
937 (instead was `{}`)", arg));
944 let addl_lib_search_paths = matches.opt_strs("L").iter().map(|s| {
945 Path::new(s.as_slice())
948 let cfg = parse_cfgspecs(matches.opt_strs("cfg").move_iter().collect());
949 let test = matches.opt_present("test");
950 let write_dependency_info = (matches.opt_present("dep-info"),
951 matches.opt_str("dep-info").map(|p| Path::new(p)));
953 let print_metas = (matches.opt_present("crate-id"),
954 matches.opt_present("crate-name"),
955 matches.opt_present("crate-file-name"));
956 let cg = build_codegen_options(matches);
959 crate_types: crate_types,
962 debuginfo: debuginfo,
963 lint_opts: lint_opts,
964 output_types: output_types,
965 addl_lib_search_paths: RefCell::new(addl_lib_search_paths),
966 maybe_sysroot: sysroot_opt,
967 target_triple: target,
970 parse_only: parse_only,
972 no_analysis: no_analysis,
973 debugging_opts: debugging_opts,
974 write_dependency_info: write_dependency_info,
975 print_metas: print_metas,
980 pub fn build_codegen_options(matches: &getopts::Matches)
981 -> session::CodegenOptions
983 let mut cg = session::basic_codegen_options();
984 for option in matches.opt_strs("C").move_iter() {
985 let mut iter = option.splitn('=', 1);
986 let key = iter.next().unwrap();
987 let value = iter.next();
988 let option_to_lookup = key.replace("-", "_");
989 let mut found = false;
990 for &(candidate, setter, _) in session::CG_OPTIONS.iter() {
991 if option_to_lookup.as_slice() != candidate { continue }
992 if !setter(&mut cg, value) {
994 Some(..) => early_error(format!("codegen option `{}` takes \
996 None => early_error(format!("codegen option `{0}` requires \
997 a value (-C {0}=<value>)",
1005 early_error(format!("unknown codegen option: `{}`", key));
1011 pub fn build_session(sopts: session::Options,
1012 local_crate_source_file: Option<Path>)
1014 let codemap = codemap::CodeMap::new();
1015 let diagnostic_handler =
1016 diagnostic::default_handler();
1017 let span_diagnostic_handler =
1018 diagnostic::mk_span_handler(diagnostic_handler, codemap);
1020 build_session_(sopts, local_crate_source_file, span_diagnostic_handler)
1023 pub fn build_session_(sopts: session::Options,
1024 local_crate_source_file: Option<Path>,
1025 span_diagnostic: diagnostic::SpanHandler)
1027 let target_cfg = build_target_config(&sopts);
1028 let p_s = parse::new_parse_sess_special_handler(span_diagnostic);
1029 let default_sysroot = match sopts.maybe_sysroot {
1031 None => Some(filesearch::get_or_default_sysroot())
1034 // Make the path absolute, if necessary
1035 let local_crate_source_file = local_crate_source_file.map(|path|
1036 if path.is_absolute() {
1039 os::getcwd().join(path.clone())
1044 targ_cfg: target_cfg,
1046 cstore: CStore::new(token::get_ident_interner()),
1048 // For a library crate, this is always none
1049 entry_fn: RefCell::new(None),
1050 entry_type: Cell::new(None),
1051 macro_registrar_fn: Cell::new(None),
1052 default_sysroot: default_sysroot,
1053 building_library: Cell::new(false),
1054 local_crate_source_file: local_crate_source_file,
1055 working_dir: os::getcwd(),
1056 lints: RefCell::new(NodeMap::new()),
1057 node_id: Cell::new(1),
1058 crate_types: RefCell::new(Vec::new()),
1059 features: front::feature_gate::Features::new(),
1060 recursion_limit: Cell::new(64),
1064 pub fn parse_pretty(sess: &Session, name: &str) -> PpMode {
1066 &"normal" => PpmNormal,
1067 &"expanded" => PpmExpanded,
1068 &"typed" => PpmTyped,
1069 &"expanded,identified" => PpmExpandedIdentified,
1070 &"identified" => PpmIdentified,
1072 sess.fatal("argument to `pretty` must be one of `normal`, \
1073 `expanded`, `typed`, `identified`, \
1074 or `expanded,identified`");
1079 // rustc command line options
1080 pub fn optgroups() -> Vec<getopts::OptGroup> {
1082 optflag("h", "help", "Display this message"),
1083 optmulti("", "cfg", "Configure the compilation environment", "SPEC"),
1084 optmulti("L", "", "Add a directory to the library search path", "PATH"),
1085 optmulti("", "crate-type", "Comma separated list of types of crates for the compiler to emit",
1086 "[bin|lib|rlib|dylib|staticlib]"),
1087 optmulti("", "emit", "Comma separated list of types of output for the compiler to emit",
1088 "[asm|bc|ir|obj|link]"),
1089 optflag("", "crate-id", "Output the crate id and exit"),
1090 optflag("", "crate-name", "Output the crate name and exit"),
1091 optflag("", "crate-file-name", "Output the file(s) that would be written if compilation \
1092 continued and exit"),
1093 optflag("g", "", "Equivalent to --debuginfo=2"),
1094 optopt("", "debuginfo", "Emit DWARF debug info to the objects created:
1096 1 = line-tables only (for stacktraces and breakpoints),
1097 2 = full debug info with variable and type information (same as -g)", "LEVEL"),
1098 optflag("", "no-trans", "Run all passes except translation; no output"),
1099 optflag("", "no-analysis",
1100 "Parse and expand the source, but run no analysis and produce no output"),
1101 optflag("O", "", "Equivalent to --opt-level=2"),
1102 optopt("o", "", "Write output to <filename>", "FILENAME"),
1103 optopt("", "opt-level", "Optimize with possible levels 0-3", "LEVEL"),
1104 optopt( "", "out-dir", "Write output to compiler-chosen filename in <dir>", "DIR"),
1105 optflag("", "parse-only", "Parse only; do not compile, assemble, or link"),
1106 optflagopt("", "pretty",
1107 "Pretty-print the input instead of compiling;
1108 valid types are: normal (un-annotated source),
1109 expanded (crates expanded),
1110 typed (crates expanded, with type annotations),
1111 or identified (fully parenthesized,
1112 AST nodes and blocks with IDs)", "TYPE"),
1113 optflagopt("", "dep-info",
1114 "Output dependency info to <filename> after compiling, \
1115 in a format suitable for use by Makefiles", "FILENAME"),
1116 optopt("", "sysroot", "Override the system root", "PATH"),
1117 optflag("", "test", "Build a test harness"),
1118 optopt("", "target", "Target triple cpu-manufacturer-kernel[-os]
1119 to compile for (see chapter 3.4 of http://www.sourceware.org/autobook/
1120 for details)", "TRIPLE"),
1121 optmulti("W", "warn", "Set lint warnings", "OPT"),
1122 optmulti("A", "allow", "Set lint allowed", "OPT"),
1123 optmulti("D", "deny", "Set lint denied", "OPT"),
1124 optmulti("F", "forbid", "Set lint forbidden", "OPT"),
1125 optmulti("C", "codegen", "Set a codegen option", "OPT[=VALUE]"),
1126 optmulti("Z", "", "Set internal debugging options", "FLAG"),
1127 optflag( "v", "version", "Print version info and exit"))
1130 pub struct OutputFilenames {
1131 pub out_directory: Path,
1132 pub out_filestem: ~str,
1133 pub single_output_file: Option<Path>,
1136 impl OutputFilenames {
1137 pub fn path(&self, flavor: link::OutputType) -> Path {
1138 match self.single_output_file {
1139 Some(ref path) => return path.clone(),
1142 self.temp_path(flavor)
1145 pub fn temp_path(&self, flavor: link::OutputType) -> Path {
1146 let base = self.out_directory.join(self.out_filestem.as_slice());
1148 link::OutputTypeBitcode => base.with_extension("bc"),
1149 link::OutputTypeAssembly => base.with_extension("s"),
1150 link::OutputTypeLlvmAssembly => base.with_extension("ll"),
1151 link::OutputTypeObject => base.with_extension("o"),
1152 link::OutputTypeExe => base,
1156 pub fn with_extension(&self, extension: &str) -> Path {
1157 let stem = self.out_filestem.as_slice();
1158 self.out_directory.join(stem).with_extension(extension)
1162 pub fn build_output_filenames(input: &Input,
1163 odir: &Option<Path>,
1164 ofile: &Option<Path>,
1165 attrs: &[ast::Attribute],
1167 -> OutputFilenames {
1170 // "-" as input file will cause the parser to read from stdin so we
1171 // have to make up a name
1172 // We want to toss everything after the final '.'
1173 let dirpath = match *odir {
1174 Some(ref d) => d.clone(),
1175 None => Path::new(".")
1178 let mut stem = input.filestem();
1180 // If a crateid is present, we use it as the link name
1181 let crateid = attr::find_crateid(attrs);
1184 Some(crateid) => stem = crateid.name.to_str(),
1187 out_directory: dirpath,
1189 single_output_file: None,
1193 Some(ref out_file) => {
1194 let ofile = if sess.opts.output_types.len() > 1 {
1195 sess.warn("ignoring specified output filename because multiple \
1196 outputs were requested");
1199 Some(out_file.clone())
1202 sess.warn("ignoring --out-dir flag due to -o flag.");
1205 out_directory: out_file.dir_path(),
1206 out_filestem: out_file.filestem_str().unwrap().to_str(),
1207 single_output_file: ofile,
1213 pub fn early_error(msg: &str) -> ! {
1214 let mut emitter = diagnostic::EmitterWriter::stderr();
1215 emitter.emit(None, msg, diagnostic::Fatal);
1216 fail!(diagnostic::FatalError);
1219 pub fn list_metadata(sess: &Session, path: &Path,
1220 out: &mut io::Writer) -> io::IoResult<()> {
1221 metadata::loader::list_file_metadata(
1222 session::sess_os_to_meta_os(sess.targ_cfg.os), path, out)
1228 use driver::driver::{build_configuration, build_session};
1229 use driver::driver::{build_session_options, optgroups};
1231 use getopts::getopts;
1233 use syntax::attr::AttrMetaMethods;
1235 // When the user supplies --test we should implicitly supply --cfg test
1237 fn test_switch_implies_cfg_test() {
1239 &match getopts(["--test".to_owned()], optgroups().as_slice()) {
1241 Err(f) => fail!("test_switch_implies_cfg_test: {}", f.to_err_msg())
1243 let sessopts = build_session_options(matches);
1244 let sess = build_session(sessopts, None);
1245 let cfg = build_configuration(&sess);
1246 assert!((attr::contains_name(cfg.as_slice(), "test")));
1249 // When the user supplies --test and --cfg test, don't implicitly add
1250 // another --cfg test
1252 fn test_switch_implies_cfg_test_unless_cfg_test() {
1254 &match getopts(["--test".to_owned(), "--cfg=test".to_owned()],
1255 optgroups().as_slice()) {
1258 fail!("test_switch_implies_cfg_test_unless_cfg_test: {}",
1262 let sessopts = build_session_options(matches);
1263 let sess = build_session(sessopts, None);
1264 let cfg = build_configuration(&sess);
1265 let mut test_items = cfg.iter().filter(|m| m.name().equiv(&("test")));
1266 assert!(test_items.next().is_some());
1267 assert!(test_items.next().is_none());