]> git.lizzy.rs Git - rust.git/blob - src/librustc/driver/driver.rs
libsyntax: De-`@str` pathnames
[rust.git] / src / librustc / driver / driver.rs
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.
4 //
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
10
11
12 use back::link;
13 use back::{arm, x86, x86_64, mips};
14 use driver::session::{Aggressive, OutputExecutable};
15 use driver::session::{Session, Session_, No, Less, Default};
16 use driver::session;
17 use front;
18 use lib::llvm::llvm;
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;
24 use metadata;
25 use middle::{trans, freevars, kind, ty, typeck, lint, astencode, reachable};
26 use middle;
27 use util::common::time;
28 use util::ppaux;
29
30 use std::cell::{Cell, RefCell};
31 use std::hashmap::{HashMap,HashSet};
32 use std::io;
33 use std::io::fs;
34 use std::io::MemReader;
35 use std::os;
36 use std::vec;
37 use extra::getopts::groups::{optopt, optmulti, optflag, optflagopt};
38 use extra::getopts;
39 use syntax::ast;
40 use syntax::abi;
41 use syntax::attr;
42 use syntax::attr::{AttrMetaMethods};
43 use syntax::codemap;
44 use syntax::diagnostic;
45 use syntax::ext::base::CrateLoader;
46 use syntax::parse;
47 use syntax::parse::token::InternedString;
48 use syntax::parse::token;
49 use syntax::print::{pp, pprust};
50 use syntax;
51
52 pub enum PpMode {
53     PpmNormal,
54     PpmExpanded,
55     PpmTyped,
56     PpmIdentified,
57     PpmExpandedIdentified
58 }
59
60 /**
61  * The name used for source code that doesn't originate in a file
62  * (e.g. source from stdin or a string)
63  */
64 pub fn anon_src() -> ~str {
65     "<anon>".to_str()
66 }
67
68 pub fn source_name(input: &Input) -> ~str {
69     match *input {
70       // FIXME (#9639): This needs to handle non-utf8 paths
71       FileInput(ref ifile) => ifile.as_str().unwrap().to_str(),
72       StrInput(_) => anon_src()
73     }
74 }
75
76 pub fn default_configuration(sess: Session) ->
77    ast::CrateConfig {
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"),
84     };
85
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")
93     };
94
95     let fam = match sess.targ_cfg.os {
96         abi::OsWin32 => InternedString::new("windows"),
97         _ => InternedString::new("unix")
98     };
99
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)),
109     ];
110 }
111
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))
116     }
117 }
118
119 pub fn build_configuration(sess: Session) ->
120    ast::CrateConfig {
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
126     if sess.opts.test {
127         append_configuration(&mut user_cfg, InternedString::new("test"))
128     }
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")
132     } else {
133         InternedString::new("nogc")
134     });
135     return vec::append(user_cfg, default_cfg);
136 }
137
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>()
145 }
146
147 pub enum Input {
148     /// Load source from file
149     FileInput(Path),
150     /// The string is the source
151     StrInput(~str)
152 }
153
154 pub fn phase_1_parse_input(sess: Session, cfg: ast::CrateConfig, input: &Input)
155     -> ast::Crate {
156     time(sess.time_passes(), "parsing", (), |_| {
157         match *input {
158             FileInput(ref file) => {
159                 parse::parse_crate_from_file(&(*file), cfg.clone(), sess.parse_sess)
160             }
161             StrInput(ref src) => {
162                 parse::parse_crate_from_source_str(anon_src(),
163                                                    (*src).clone(),
164                                                    cfg.clone(),
165                                                    sess.parse_sess)
166             }
167         }
168     })
169 }
170
171 // For continuing compilation after a parsed crate has been
172 // modified
173
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();
184
185     sess.building_library.set(session::building_library(sess.opts, &crate));
186     sess.outputs.set(session::collect_outputs(&sess, crate.attrs));
187
188     time(time_passes, "gated feature checking", (), |_|
189          front::feature_gate::check_crate(sess, &crate));
190
191     crate = time(time_passes, "crate injection", crate, |crate|
192                  front::std_inject::maybe_inject_crates_ref(sess, crate));
193
194     // strip before expansion to allow macros to depend on
195     // configuration variables e.g/ in
196     //
197     //   #[macro_escape] #[cfg(foo)]
198     //   mod bar { macro_rules! baz!(() => {{}}) }
199     //
200     // baz! should not use this definition unless foo is enabled.
201
202     crate = time(time_passes, "configuration 1", crate, |crate|
203                  front::config::strip_unconfigured_items(crate));
204
205     crate = time(time_passes, "expansion", crate, |crate| {
206         syntax::ext::expand::expand_crate(sess.parse_sess,
207                                           loader,
208                                           cfg.clone(),
209                                           crate)
210     });
211     // dump the syntax-time crates
212     sess.cstore.reset();
213
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));
217
218     crate = time(time_passes, "maybe building test harness", crate, |crate|
219                  front::test::modify_for_testing(sess, crate));
220
221     crate = time(time_passes, "prelude injection", crate, |crate|
222                  front::std_inject::maybe_inject_prelude(sess, crate));
223
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))
226 }
227
228 pub struct CrateAnalysis {
229     exp_map2: middle::resolve::ExportMap2,
230     exported_items: middle::privacy::ExportedItems,
231     public_items: middle::privacy::PublicItems,
232     ty_cx: ty::ctxt,
233     maps: astencode::Maps,
234     reachable: @RefCell<HashSet<ast::NodeId>>
235 }
236
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,
241                                    crate: &ast::Crate,
242                                    ast_map: syntax::ast_map::Map) -> CrateAnalysis {
243
244     let time_passes = sess.time_passes();
245
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()));
250
251     let lang_items = time(time_passes, "language item collection", (), |_|
252                           middle::lang_items::collect_language_items(crate, sess));
253
254     let middle::resolve::CrateMap {
255         def_map: def_map,
256         exp_map2: exp_map2,
257         trait_map: trait_map,
258         external_exports: external_exports,
259         last_private_map: last_private_map
260     } =
261         time(time_passes, "resolution", (), |_|
262              middle::resolve::resolve_crate(sess, lang_items, crate));
263
264     let named_region_map = time(time_passes, "lifetime resolution", (),
265                                 |_| middle::resolve_lifetime::crate(sess, crate));
266
267     time(time_passes, "looking for entry point", (),
268          |_| middle::entry::find_entry_point(sess, crate, ast_map));
269
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)));
274
275     let freevars = time(time_passes, "freevar finding", (), |_|
276                         freevars::annotate_freevars(def_map, crate));
277
278     let region_map = time(time_passes, "region resolution", (), |_|
279                           middle::region::resolve_crate(sess, crate));
280
281     let ty_cx = ty::mk_ctxt(sess, def_map, named_region_map, ast_map, freevars,
282                             region_map, lang_items);
283
284     // passes are timed inside typeck
285     let (method_map, vtable_map) = typeck::check_crate(ty_cx, trait_map, crate);
286
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));
290
291     time(time_passes, "const checking", (), |_|
292          middle::check_const::check_crate(sess, crate, ast_map, def_map,
293                                           method_map, ty_cx));
294
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,
299                                               a, b, crate));
300
301     time(time_passes, "effect checking", (), |_|
302          middle::effect::check_crate(ty_cx, method_map, crate));
303
304     time(time_passes, "loop checking", (), |_|
305          middle::check_loop::check_crate(ty_cx, crate));
306
307     let middle::moves::MoveMaps {moves_map, moved_variables_set,
308                                  capture_map} =
309         time(time_passes, "compute moves", (), |_|
310              middle::moves::compute_moves(ty_cx, method_map, crate));
311
312     time(time_passes, "match checking", (), |_|
313          middle::check_match::check_crate(ty_cx, method_map,
314                                           moves_map, crate));
315
316     time(time_passes, "liveness checking", (), |_|
317          middle::liveness::check_crate(ty_cx, method_map,
318                                        capture_map, crate));
319
320     let root_map =
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));
325
326     time(time_passes, "kind checking", (), |_|
327          kind::check_crate(ty_cx, method_map, crate));
328
329     let reachable_map =
330         time(time_passes, "reachability checking", (), |_|
331              reachable::find_reachable(ty_cx, method_map, &exported_items));
332
333     {
334         let reachable_map = reachable_map.borrow();
335         time(time_passes, "death checking", (), |_| {
336              middle::dead::check_crate(ty_cx,
337                                        method_map,
338                                        &exported_items,
339                                        reachable_map.get(),
340                                        crate)
341         });
342     }
343
344     time(time_passes, "lint checking", (), |_|
345          lint::check_crate(ty_cx, method_map, &exported_items, crate));
346
347     CrateAnalysis {
348         exp_map2: exp_map2,
349         ty_cx: ty_cx,
350         exported_items: exported_items,
351         public_items: public_items,
352         maps: astencode::Maps {
353             root_map: root_map,
354             method_map: method_map,
355             vtable_map: vtable_map,
356             capture_map: capture_map
357         },
358         reachable: reachable_map
359     }
360 }
361
362 pub struct CrateTranslation {
363     context: ContextRef,
364     module: ModuleRef,
365     metadata_module: ModuleRef,
366     link: LinkMeta,
367     metadata: ~[u8],
368     reachable: ~[~str],
369 }
370
371 /// Run the translation phase to LLVM, after which the AST and analysis can
372 /// be discarded.
373 pub fn phase_4_translate_to_llvm(sess: Session,
374                                  crate: ast::Crate,
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))
380 }
381
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) {
387
388     if sess.no_integrated_as() {
389         let output_type = link::OutputTypeAssembly;
390         let asm_filename = outputs.obj_filename.with_extension("s");
391
392         time(sess.time_passes(), "LLVM passes", (), |_|
393             link::write::run_passes(sess,
394                                     trans,
395                                     output_type,
396                                     &asm_filename));
397
398         link::write::run_assembler(sess, &asm_filename, &outputs.obj_filename);
399
400         // Remove assembly source, unless --save-temps was specified
401         if !sess.opts.save_temps {
402             fs::unlink(&asm_filename);
403         }
404     } else {
405         time(sess.time_passes(), "LLVM passes", (), |_|
406             link::write::run_passes(sess,
407                                     trans,
408                                     sess.opts.output_type,
409                                     &outputs.obj_filename));
410     }
411 }
412
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,
420                            trans,
421                            &outputs.obj_filename,
422                            &outputs.out_filename,
423                            &trans.link));
424 }
425
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");
429         return true;
430     }
431     return false;
432 }
433
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");
437         return true;
438     }
439     return false;
440 }
441
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");
445         return true;
446     }
447     return false;
448 }
449
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");
453         return true;
454     }
455     return false;
456 }
457
458 fn write_out_deps(sess: Session, input: &Input, outputs: &OutputFilenames, crate: &ast::Crate)
459 {
460     let lm = link::build_link_meta(sess, crate.attrs, &outputs.obj_filename,
461                                        &mut ::util::sha2::Sha256::new());
462
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))
466         .to_owned_vec();
467
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");
477                 filename
478             },
479             StrInput(..) => {
480                 sess.warn("can not write --dep-info without a filename when compiling stdin.");
481                 return;
482             },
483         },
484         _ => return,
485     };
486
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();
491         files.get()
492              .iter()
493              .filter_map(|fmap| {
494                  if fmap.is_real_file() {
495                      Some(fmap.name.clone())
496                  } else {
497                      None
498                  }
499              })
500              .collect()
501     };
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(" "));
506     }
507 }
508
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)
520         };
521         let outputs = build_output_filenames(input, outdir, output,
522                                              expanded_crate.attrs, sess);
523
524         write_out_deps(sess, input, outputs, &expanded_crate);
525
526         if stop_after_phase_2(sess) { return; }
527
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,
531                                               &analysis, outputs);
532         (outputs, trans)
533     };
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);
537 }
538
539 struct IdentifiedAnnotation {
540     contents: (),
541 }
542
543 impl pprust::PpAnn for IdentifiedAnnotation {
544     fn pre(&self, node: pprust::AnnNode) {
545         match node {
546             pprust::NodeExpr(s, _) => pprust::popen(s),
547             _ => ()
548         }
549     }
550     fn post(&self, node: pprust::AnnNode) {
551         match node {
552             pprust::NodeItem(s, item) => {
553                 pp::space(&mut s.s);
554                 pprust::synth_comment(s, item.id.to_str());
555             }
556             pprust::NodeBlock(s, blk) => {
557                 pp::space(&mut s.s);
558                 pprust::synth_comment(s, ~"block " + blk.id.to_str());
559             }
560             pprust::NodeExpr(s, expr) => {
561                 pp::space(&mut s.s);
562                 pprust::synth_comment(s, expr.id.to_str());
563                 pprust::pclose(s);
564             }
565             pprust::NodePat(s, pat) => {
566                 pp::space(&mut s.s);
567                 pprust::synth_comment(s, ~"pat " + pat.id.to_str());
568             }
569         }
570     }
571 }
572
573 struct TypedAnnotation {
574     analysis: CrateAnalysis,
575 }
576
577 impl pprust::PpAnn for TypedAnnotation {
578     fn pre(&self, node: pprust::AnnNode) {
579         match node {
580             pprust::NodeExpr(s, _) => pprust::popen(s),
581             _ => ()
582         }
583     }
584     fn post(&self, node: pprust::AnnNode) {
585         let tcx = self.analysis.ty_cx;
586         match node {
587             pprust::NodeExpr(s, expr) => {
588                 pp::space(&mut s.s);
589                 pp::word(&mut s.s, "as");
590                 pp::space(&mut s.s);
591                 pp::word(&mut s.s, ppaux::ty_to_str(tcx, ty::expr_ty(tcx, expr)));
592                 pprust::pclose(s);
593             }
594             _ => ()
595         }
596     }
597 }
598
599 pub fn pretty_print_input(sess: Session,
600                           cfg: ast::CrateConfig,
601                           input: &Input,
602                           ppm: PpMode) {
603     let crate = phase_1_parse_input(sess, cfg.clone(), input);
604
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)
610         }
611         _ => (crate, None, false)
612     };
613
614     let annotation = match ppm {
615         PpmIdentified | PpmExpandedIdentified => {
616             @IdentifiedAnnotation {
617                 contents: (),
618             } as @pprust::PpAnn
619         }
620         PpmTyped => {
621             let ast_map = ast_map.expect("--pretty=typed missing ast_map");
622             let analysis = phase_3_run_analysis_passes(sess, &crate, ast_map);
623             @TypedAnnotation {
624                 analysis: analysis
625             } as @pprust::PpAnn
626         }
627         _ => @pprust::NoAnn as @pprust::PpAnn,
628     };
629
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,
636                         &crate,
637                         source_name(input),
638                         &mut rdr,
639                         ~stdout as ~io::Writer,
640                         annotation,
641                         is_expanded);
642 }
643
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) }
647     }
648     None
649 }
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)];
657
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) }
661     }
662     None
663 }
664 static architecture_abis : &'static [(&'static str, abi::Architecture)] = &'static [
665     ("i386",   abi::X86),
666     ("i486",   abi::X86),
667     ("i586",   abi::X86),
668     ("i686",   abi::X86),
669     ("i786",   abi::X86),
670
671     ("x86_64", abi::X86_64),
672
673     ("arm",    abi::Arm),
674     ("xscale", abi::Arm),
675     ("thumb",  abi::Arm),
676
677     ("mips",   abi::Mips)];
678
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) {
683       Some(os) => os,
684       None => early_error(demitter, "unknown operating system")
685     };
686     let arch = match get_arch(sopts.target_triple) {
687       Some(arch) => arch,
688       None => early_error(demitter,
689                           "unknown architecture: " + sopts.target_triple)
690     };
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)
696     };
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)
703     };
704     let target_cfg = @session::Config {
705         os: os,
706         arch: arch,
707         target_strs: target_strs,
708         int_type: int_type,
709         uint_type: uint_type,
710     };
711     return target_cfg;
712 }
713
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.
719     //
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()
724 }
725
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());
733     }
734     if matches.opt_present("rlib") {
735         outputs.push(session::OutputRlib)
736     }
737     if matches.opt_present("staticlib") {
738         outputs.push(session::OutputStaticlib)
739     }
740     if matches.opt_present("dylib") {
741         outputs.push(session::OutputDylib)
742     }
743     if matches.opt_present("bin") {
744         outputs.push(session::OutputExecutable)
745     }
746
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");
751
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);
758
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) {
766               None => {
767                 early_error(demitter, format!("unknown {} flag: {}",
768                                            level_name, lint_name));
769               }
770               Some(lint) => {
771                 lint_opts.push((lint.lint, *level));
772               }
773             }
774         }
775     }
776
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; }
785         }
786         if this_bit == 0 {
787             early_error(demitter, format!("unknown debug flag: {}", *debug_flag))
788         }
789         debugging_opts |= this_bit;
790     }
791
792     if debugging_opts & session::DEBUG_LLVM != 0 {
793         unsafe { llvm::LLVMSetDebug(1); }
794     }
795
796     let output_type =
797         if parse_only || no_trans {
798             link::OutputTypeNone
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");
814     let opt_level = {
815         if (debugging_opts & session::NO_OPT) != 0 {
816             No
817         } else if matches.opt_present("O") {
818             if matches.opt_present("opt-level") {
819                 early_error(demitter, "-O and --opt-level both provided");
820             }
821             Default
822         } else if matches.opt_present("opt-level") {
823             match matches.opt_str("opt-level").unwrap() {
824               ~"0" => No,
825               ~"1" => Less,
826               ~"2" => Default,
827               ~"3" => Aggressive,
828               _ => {
829                 early_error(demitter, "optimization level needs to be between 0-3")
830               }
831             }
832         } else { No }
833     };
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 ||
837         extra_debuginfo;
838
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| {
846             if arg.is_empty() {
847                 None
848             } else {
849                 Some(arg.to_owned())
850             }
851         }).collect()
852     });
853
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)));
859
860     let custom_passes = match matches.opt_str("passes") {
861         None => ~[],
862         Some(s) => {
863             s.split(|c: char| c == ' ' || c == ',').map(|s| {
864                 s.trim().to_owned()
865             }).collect()
866         }
867     };
868     let llvm_args = match matches.opt_str("llvm-args") {
869         None => ~[],
870         Some(s) => {
871             s.split(|c: char| c == ' ' || c == ',').map(|s| {
872                 s.trim().to_owned()
873             }).collect()
874         }
875     };
876     let print_metas = (matches.opt_present("crate-id"),
877                        matches.opt_present("crate-name"),
878                        matches.opt_present("crate-file-name"));
879
880     let sopts = @session::Options {
881         outputs: outputs,
882         gc: gc,
883         optimize: opt_level,
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),
892         ar: ar,
893         linker: linker,
894         linker_args: linker_args,
895         maybe_sysroot: sysroot_opt,
896         target_triple: target,
897         target_cpu: target_cpu,
898         target_feature: target_feature,
899         cfg: cfg,
900         binary: binary,
901         test: test,
902         parse_only: parse_only,
903         no_trans: no_trans,
904         no_analysis: no_analysis,
905         no_rpath: no_rpath,
906         debugging_opts: debugging_opts,
907         android_cross_path: android_cross_path,
908         write_dependency_info: write_dependency_info,
909         print_metas: print_metas,
910     };
911     return sopts;
912 }
913
914 pub fn build_session(sopts: @session::Options,
915                      local_crate_source_file: Option<Path>,
916                      demitter: @diagnostic::Emitter)
917                      -> Session {
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);
923
924     build_session_(sopts, local_crate_source_file, codemap, demitter, span_diagnostic_handler)
925 }
926
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)
932                       -> Session {
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,
938         sopts.target_triple,
939         sopts.addl_lib_search_paths);
940
941     // Make the path absolute, if necessary
942     let local_crate_source_file = local_crate_source_file.map(|path|
943         if path.is_absolute() {
944             path.clone()
945         } else {
946             os::getcwd().join(path.clone())
947         }
948     );
949
950     @Session_ {
951         targ_cfg: target_cfg,
952         opts: sopts,
953         cstore: cstore,
954         parse_sess: p_s,
955         codemap: codemap,
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(~[]),
968     }
969 }
970
971 pub fn parse_pretty(sess: Session, name: &str) -> PpMode {
972     match name {
973       &"normal" => PpmNormal,
974       &"expanded" => PpmExpanded,
975       &"typed" => PpmTyped,
976       &"expanded,identified" => PpmExpandedIdentified,
977       &"identified" => PpmIdentified,
978       _ => {
979         sess.fatal("argument to `pretty` must be one of `normal`, \
980                     `expanded`, `typed`, `identified`, \
981                     or `expanded,identified`");
982       }
983     }
984 }
985
986 // rustc command line options
987 pub fn optgroups() -> ~[getopts::groups::OptGroup] {
988  ~[
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",
997                               "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 \
1016                         output"),
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
1030                           in <dir>", "DIR"),
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"),
1072  ]
1073 }
1074
1075 pub struct OutputFilenames {
1076     out_filename: Path,
1077     obj_filename: Path
1078 }
1079
1080 pub fn build_output_filenames(input: &Input,
1081                               odir: &Option<Path>,
1082                               ofile: &Option<Path>,
1083                               attrs: &[ast::Attribute],
1084                               sess: Session)
1085                            -> ~OutputFilenames {
1086     let obj_path;
1087     let out_path;
1088     let sopts = sess.opts;
1089     let stop_after_codegen = sopts.output_type != link::OutputTypeExe;
1090
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"
1098     };
1099
1100     match *ofile {
1101       None => {
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()
1110               }
1111           };
1112
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"
1117           };
1118
1119           // If a crateid is present, we use it as the link name
1120           let crateid = attr::find_crateid(attrs);
1121           match crateid {
1122               None => {}
1123               Some(crateid) => {
1124                   stem = crateid.name.to_managed()
1125               }
1126           }
1127
1128           if sess.building_library.get() {
1129               out_path = dirpath.join(os::dll_filename(stem));
1130               obj_path = {
1131                   let mut p = dirpath.join(stem);
1132                   p.set_extension(obj_suffix);
1133                   p
1134               };
1135           } else {
1136               out_path = dirpath.join(stem);
1137               obj_path = out_path.with_extension(obj_suffix);
1138           }
1139       }
1140
1141       Some(ref out_file) => {
1142         out_path = out_file.clone();
1143         obj_path = if stop_after_codegen {
1144             out_file.clone()
1145         } else {
1146             out_file.with_extension(obj_suffix)
1147         };
1148
1149         if sess.building_library.get() {
1150             sess.warn("ignoring specified output filename for library.");
1151         }
1152
1153         if *odir != None {
1154             sess.warn("ignoring --out-dir flag due to -o flag.");
1155         }
1156       }
1157     }
1158
1159     ~OutputFilenames {
1160         out_filename: out_path,
1161         obj_filename: obj_path
1162     }
1163 }
1164
1165 pub fn early_error(emitter: &diagnostic::Emitter, msg: &str) -> ! {
1166     emitter.emit(None, msg, diagnostic::Fatal);
1167     fail!(diagnostic::FatalError);
1168 }
1169
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);
1174 }
1175
1176 #[cfg(test)]
1177 mod test {
1178
1179     use driver::driver::{build_configuration, build_session};
1180     use driver::driver::{build_session_options, optgroups};
1181
1182     use extra::getopts::groups::getopts;
1183     use syntax::attr;
1184     use syntax::attr::AttrMetaMethods;
1185     use syntax::diagnostic;
1186
1187     // When the user supplies --test we should implicitly supply --cfg test
1188     #[test]
1189     fn test_switch_implies_cfg_test() {
1190         let matches =
1191             &match getopts([~"--test"], optgroups()) {
1192               Ok(m) => m,
1193               Err(f) => fail!("test_switch_implies_cfg_test: {}", f.to_err_msg())
1194             };
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")));
1199     }
1200
1201     // When the user supplies --test and --cfg test, don't implicitly add
1202     // another --cfg test
1203     #[test]
1204     fn test_switch_implies_cfg_test_unless_cfg_test() {
1205         let matches =
1206             &match getopts([~"--test", ~"--cfg=test"], optgroups()) {
1207               Ok(m) => m,
1208               Err(f) => {
1209                 fail!("test_switch_implies_cfg_test_unless_cfg_test: {}",
1210                        f.to_err_msg());
1211               }
1212             };
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());
1219     }
1220 }