]> git.lizzy.rs Git - rust.git/blob - src/librustc_driver/lib.rs
rollup merge of #21438: taralx/kill-racycell
[rust.git] / src / librustc_driver / lib.rs
1 // Copyright 2014 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
4 //
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
10
11 //! The Rust compiler.
12 //!
13 //! # Note
14 //!
15 //! This API is completely unstable and subject to change.
16
17 #![crate_name = "rustc_driver"]
18 #![unstable]
19 #![staged_api]
20 #![crate_type = "dylib"]
21 #![crate_type = "rlib"]
22 #![doc(html_logo_url = "http://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png",
23       html_favicon_url = "http://www.rust-lang.org/favicon.ico",
24       html_root_url = "http://doc.rust-lang.org/nightly/")]
25
26 #![allow(unknown_features)]
27 #![feature(quote)]
28 #![feature(slicing_syntax, unsafe_destructor)]
29 #![feature(box_syntax)]
30 #![feature(rustc_diagnostic_macros)]
31 #![allow(unknown_features)] #![feature(int_uint)]
32 #![allow(unstable)]
33
34 extern crate arena;
35 extern crate flate;
36 extern crate getopts;
37 extern crate graphviz;
38 extern crate libc;
39 extern crate rustc;
40 extern crate rustc_back;
41 extern crate rustc_borrowck;
42 extern crate rustc_privacy;
43 extern crate rustc_resolve;
44 extern crate rustc_trans;
45 extern crate rustc_typeck;
46 extern crate serialize;
47 extern crate "rustc_llvm" as llvm;
48 #[macro_use] extern crate log;
49 #[macro_use] extern crate syntax;
50
51 pub use syntax::diagnostic;
52
53 use driver::CompileController;
54
55 use rustc_resolve as resolve;
56 use rustc_trans::back::link;
57 use rustc_trans::save;
58 use rustc::session::{config, Session, build_session};
59 use rustc::session::config::{Input, PrintRequest, UnstableFeatures};
60 use rustc::lint::Lint;
61 use rustc::lint;
62 use rustc::metadata;
63 use rustc::metadata::creader::CrateOrString::Str;
64 use rustc::util::common::time;
65
66 use std::cmp::Ordering::Equal;
67 use std::io;
68 use std::iter::repeat;
69 use std::os;
70 use std::sync::mpsc::channel;
71 use std::thread;
72
73 use rustc::session::early_error;
74
75 use syntax::ast;
76 use syntax::parse;
77 use syntax::diagnostic::Emitter;
78 use syntax::diagnostics;
79
80 #[cfg(test)]
81 pub mod test;
82
83 pub mod driver;
84 pub mod pretty;
85
86 pub fn run(args: Vec<String>) -> int {
87     monitor(move |:| run_compiler(args.as_slice()));
88     0
89 }
90
91 static BUG_REPORT_URL: &'static str =
92     "http://doc.rust-lang.org/complement-bugreport.html";
93
94 fn run_compiler(args: &[String]) {
95     let matches = match handle_options(args.to_vec()) {
96         Some(matches) => matches,
97         None => return
98     };
99
100     let descriptions = diagnostics_registry();
101     match matches.opt_str("explain") {
102         Some(ref code) => {
103             match descriptions.find_description(&code[]) {
104                 Some(ref description) => {
105                     println!("{}", description);
106                 }
107                 None => {
108                     early_error(&format!("no extended information for {}", code)[]);
109                 }
110             }
111             return;
112         },
113         None => ()
114     }
115
116     let sopts = config::build_session_options(&matches);
117     let odir = matches.opt_str("out-dir").map(|o| Path::new(o));
118     let ofile = matches.opt_str("o").map(|o| Path::new(o));
119     let (input, input_file_path) = match matches.free.len() {
120         0u => {
121             if sopts.describe_lints {
122                 let mut ls = lint::LintStore::new();
123                 ls.register_builtin(None);
124                 describe_lints(&ls, false);
125                 return;
126             }
127             let sess = build_session(sopts, None, descriptions);
128             if print_crate_info(&sess, None, &odir, &ofile) {
129                 return;
130             }
131             early_error("no input filename given");
132         }
133         1u => {
134             let ifile = &matches.free[0][];
135             if ifile == "-" {
136                 let contents = io::stdin().read_to_end().unwrap();
137                 let src = String::from_utf8(contents).unwrap();
138                 (Input::Str(src), None)
139             } else {
140                 (Input::File(Path::new(ifile)), Some(Path::new(ifile)))
141             }
142         }
143         _ => early_error("multiple input filenames provided")
144     };
145
146     let mut sopts = sopts;
147     sopts.unstable_features = get_unstable_features_setting();
148
149     let mut sess = build_session(sopts, input_file_path, descriptions);
150
151     let cfg = config::build_configuration(&sess);
152     if print_crate_info(&sess, Some(&input), &odir, &ofile) {
153         return
154     }
155
156     let pretty = matches.opt_default("pretty", "normal").map(|a| {
157         // stable pretty-print variants only
158         pretty::parse_pretty(&sess, a.as_slice(), false)
159     });
160     let pretty = if pretty.is_none() &&
161         sess.unstable_options() {
162             matches.opt_str("xpretty").map(|a| {
163                 // extended with unstable pretty-print variants
164                 pretty::parse_pretty(&sess, a.as_slice(), true)
165             })
166         } else {
167             pretty
168         };
169
170     match pretty.into_iter().next() {
171         Some((ppm, opt_uii)) => {
172             pretty::pretty_print_input(sess, cfg, &input, ppm, opt_uii, ofile);
173             return;
174         }
175         None => {/* continue */ }
176     }
177
178     if sess.unstable_options() {
179         sess.opts.show_span = matches.opt_str("show-span");
180     }
181
182     let r = matches.opt_strs("Z");
183     if r.contains(&("ls".to_string())) {
184         match input {
185             Input::File(ref ifile) => {
186                 let mut stdout = io::stdout();
187                 list_metadata(&sess, &(*ifile), &mut stdout).unwrap();
188             }
189             Input::Str(_) => {
190                 early_error("cannot list metadata for stdin");
191             }
192         }
193         return;
194     }
195
196     let plugins = sess.opts.debugging_opts.extra_plugins.clone();
197     let control = build_controller(&sess);
198     driver::compile_input(sess, cfg, &input, &odir, &ofile, Some(plugins), control);
199 }
200
201 fn build_controller<'a>(sess: &Session) -> CompileController<'a> {
202     let mut control = CompileController::basic();
203
204     if sess.opts.parse_only ||
205        sess.opts.show_span.is_some() ||
206        sess.opts.debugging_opts.ast_json_noexpand {
207         control.after_parse.stop = true;
208     }
209
210     if sess.opts.no_analysis || sess.opts.debugging_opts.ast_json {
211         control.after_write_deps.stop = true;
212     }
213
214     if sess.opts.no_trans {
215         control.after_analysis.stop = true;
216     }
217
218     if !sess.opts.output_types.iter().any(|&i| i == config::OutputTypeExe) {
219         control.after_llvm.stop = true;
220     }
221
222     if sess.opts.debugging_opts.save_analysis {
223         control.after_analysis.callback = box |state| {
224             time(state.session.time_passes(), "save analysis", state.krate.unwrap(), |krate|
225                  save::process_crate(state.session,
226                                      krate,
227                                      state.analysis.unwrap(),
228                                      state.out_dir));
229         };
230         control.make_glob_map = resolve::MakeGlobMap::Yes;
231     }
232
233     control
234 }
235
236 pub fn get_unstable_features_setting() -> UnstableFeatures {
237     // Whether this is a feature-staged build, i.e. on the beta or stable channel
238     let disable_unstable_features = option_env!("CFG_DISABLE_UNSTABLE_FEATURES").is_some();
239     // The secret key needed to get through the rustc build itself by
240     // subverting the unstable features lints
241     let bootstrap_secret_key = option_env!("CFG_BOOTSTRAP_KEY");
242     // The matching key to the above, only known by the build system
243     let bootstrap_provided_key = os::getenv("RUSTC_BOOTSTRAP_KEY");
244     match (disable_unstable_features, bootstrap_secret_key, bootstrap_provided_key) {
245         (_, Some(ref s), Some(ref p)) if s == p => UnstableFeatures::Cheat,
246         (true, _, _) => UnstableFeatures::Disallow,
247         (false, _, _) => UnstableFeatures::Default
248     }
249 }
250
251 /// Returns a version string such as "0.12.0-dev".
252 pub fn release_str() -> Option<&'static str> {
253     option_env!("CFG_RELEASE")
254 }
255
256 /// Returns the full SHA1 hash of HEAD of the Git repo from which rustc was built.
257 pub fn commit_hash_str() -> Option<&'static str> {
258     option_env!("CFG_VER_HASH")
259 }
260
261 /// Returns the "commit date" of HEAD of the Git repo from which rustc was built as a static string.
262 pub fn commit_date_str() -> Option<&'static str> {
263     option_env!("CFG_VER_DATE")
264 }
265
266 /// Prints version information and returns None on success or an error
267 /// message on panic.
268 pub fn version(binary: &str, matches: &getopts::Matches) {
269     let verbose = matches.opt_present("verbose");
270
271     println!("{} {}", binary, option_env!("CFG_VERSION").unwrap_or("unknown version"));
272     if verbose {
273         fn unw(x: Option<&str>) -> &str { x.unwrap_or("unknown") }
274         println!("binary: {}", binary);
275         println!("commit-hash: {}", unw(commit_hash_str()));
276         println!("commit-date: {}", unw(commit_date_str()));
277         println!("host: {}", config::host_triple());
278         println!("release: {}", unw(release_str()));
279     }
280 }
281
282 fn usage(verbose: bool, include_unstable_options: bool) {
283     let groups = if verbose {
284         config::rustc_optgroups()
285     } else {
286         config::rustc_short_optgroups()
287     };
288     let groups : Vec<_> = groups.into_iter()
289         .filter(|x| include_unstable_options || x.is_stable())
290         .map(|x|x.opt_group)
291         .collect();
292     let message = format!("Usage: rustc [OPTIONS] INPUT");
293     let extra_help = if verbose {
294         ""
295     } else {
296         "\n    --help -v           Print the full set of options rustc accepts"
297     };
298     println!("{}\n\
299 Additional help:
300     -C help             Print codegen options
301     -W help             Print 'lint' options and default settings
302     -Z help             Print internal options for debugging rustc{}\n",
303               getopts::usage(message.as_slice(), groups.as_slice()),
304               extra_help);
305 }
306
307 fn describe_lints(lint_store: &lint::LintStore, loaded_plugins: bool) {
308     println!("
309 Available lint options:
310     -W <foo>           Warn about <foo>
311     -A <foo>           Allow <foo>
312     -D <foo>           Deny <foo>
313     -F <foo>           Forbid <foo> (deny, and deny all overrides)
314
315 ");
316
317     fn sort_lints(lints: Vec<(&'static Lint, bool)>) -> Vec<&'static Lint> {
318         let mut lints: Vec<_> = lints.into_iter().map(|(x, _)| x).collect();
319         lints.sort_by(|x: &&Lint, y: &&Lint| {
320             match x.default_level.cmp(&y.default_level) {
321                 // The sort doesn't case-fold but it's doubtful we care.
322                 Equal => x.name.cmp(y.name),
323                 r => r,
324             }
325         });
326         lints
327     }
328
329     fn sort_lint_groups(lints: Vec<(&'static str, Vec<lint::LintId>, bool)>)
330                      -> Vec<(&'static str, Vec<lint::LintId>)> {
331         let mut lints: Vec<_> = lints.into_iter().map(|(x, y, _)| (x, y)).collect();
332         lints.sort_by(|&(x, _): &(&'static str, Vec<lint::LintId>),
333                        &(y, _): &(&'static str, Vec<lint::LintId>)| {
334             x.cmp(y)
335         });
336         lints
337     }
338
339     let (plugin, builtin): (Vec<_>, _) = lint_store.get_lints()
340         .iter().cloned().partition(|&(_, p)| p);
341     let plugin = sort_lints(plugin);
342     let builtin = sort_lints(builtin);
343
344     let (plugin_groups, builtin_groups): (Vec<_>, _) = lint_store.get_lint_groups()
345         .iter().cloned().partition(|&(_, _, p)| p);
346     let plugin_groups = sort_lint_groups(plugin_groups);
347     let builtin_groups = sort_lint_groups(builtin_groups);
348
349     let max_name_len = plugin.iter().chain(builtin.iter())
350         .map(|&s| s.name.width(true))
351         .max().unwrap_or(0);
352     let padded = |&: x: &str| {
353         let mut s = repeat(" ").take(max_name_len - x.chars().count())
354                                .collect::<String>();
355         s.push_str(x);
356         s
357     };
358
359     println!("Lint checks provided by rustc:\n");
360     println!("    {}  {:7.7}  {}", padded("name"), "default", "meaning");
361     println!("    {}  {:7.7}  {}", padded("----"), "-------", "-------");
362
363     let print_lints = |&: lints: Vec<&Lint>| {
364         for lint in lints.into_iter() {
365             let name = lint.name_lower().replace("_", "-");
366             println!("    {}  {:7.7}  {}",
367                      padded(&name[]), lint.default_level.as_str(), lint.desc);
368         }
369         println!("\n");
370     };
371
372     print_lints(builtin);
373
374
375
376     let max_name_len = plugin_groups.iter().chain(builtin_groups.iter())
377         .map(|&(s, _)| s.width(true))
378         .max().unwrap_or(0);
379     let padded = |&: x: &str| {
380         let mut s = repeat(" ").take(max_name_len - x.chars().count())
381                                .collect::<String>();
382         s.push_str(x);
383         s
384     };
385
386     println!("Lint groups provided by rustc:\n");
387     println!("    {}  {}", padded("name"), "sub-lints");
388     println!("    {}  {}", padded("----"), "---------");
389
390     let print_lint_groups = |&: lints: Vec<(&'static str, Vec<lint::LintId>)>| {
391         for (name, to) in lints.into_iter() {
392             let name = name.chars().map(|x| x.to_lowercase())
393                            .collect::<String>().replace("_", "-");
394             let desc = to.into_iter().map(|x| x.as_str().replace("_", "-"))
395                          .collect::<Vec<String>>().connect(", ");
396             println!("    {}  {}",
397                      padded(&name[]), desc);
398         }
399         println!("\n");
400     };
401
402     print_lint_groups(builtin_groups);
403
404     match (loaded_plugins, plugin.len(), plugin_groups.len()) {
405         (false, 0, _) | (false, _, 0) => {
406             println!("Compiler plugins can provide additional lints and lint groups. To see a \
407                       listing of these, re-run `rustc -W help` with a crate filename.");
408         }
409         (false, _, _) => panic!("didn't load lint plugins but got them anyway!"),
410         (true, 0, 0) => println!("This crate does not load any lint plugins or lint groups."),
411         (true, l, g) => {
412             if l > 0 {
413                 println!("Lint checks provided by plugins loaded by this crate:\n");
414                 print_lints(plugin);
415             }
416             if g > 0 {
417                 println!("Lint groups provided by plugins loaded by this crate:\n");
418                 print_lint_groups(plugin_groups);
419             }
420         }
421     }
422 }
423
424 fn describe_debug_flags() {
425     println!("\nAvailable debug options:\n");
426     for &(name, _, opt_type_desc, desc) in config::DB_OPTIONS.iter() {
427         let (width, extra) = match opt_type_desc {
428             Some(..) => (21, "=val"),
429             None => (25, "")
430         };
431         println!("    -Z {:>width$}{} -- {}", name.replace("_", "-"),
432                  extra, desc, width=width);
433     }
434 }
435
436 fn describe_codegen_flags() {
437     println!("\nAvailable codegen options:\n");
438     for &(name, _, opt_type_desc, desc) in config::CG_OPTIONS.iter() {
439         let (width, extra) = match opt_type_desc {
440             Some(..) => (21, "=val"),
441             None => (25, "")
442         };
443         println!("    -C {:>width$}{} -- {}", name.replace("_", "-"),
444                  extra, desc, width=width);
445     }
446 }
447
448 /// Process command line options. Emits messages as appropriate. If compilation
449 /// should continue, returns a getopts::Matches object parsed from args, otherwise
450 /// returns None.
451 pub fn handle_options(mut args: Vec<String>) -> Option<getopts::Matches> {
452     // Throw away the first argument, the name of the binary
453     let _binary = args.remove(0);
454
455     if args.is_empty() {
456         // user did not write `-v` nor `-Z unstable-options`, so do not
457         // include that extra information.
458         usage(false, false);
459         return None;
460     }
461
462     let matches =
463         match getopts::getopts(&args[], &config::optgroups()[]) {
464             Ok(m) => m,
465             Err(f_stable_attempt) => {
466                 // redo option parsing, including unstable options this time,
467                 // in anticipation that the mishandled option was one of the
468                 // unstable ones.
469                 let all_groups : Vec<getopts::OptGroup>
470                     = config::rustc_optgroups().into_iter().map(|x|x.opt_group).collect();
471                 match getopts::getopts(args.as_slice(), all_groups.as_slice()) {
472                     Ok(m_unstable) => {
473                         let r = m_unstable.opt_strs("Z");
474                         let include_unstable_options = r.iter().any(|x| *x == "unstable-options");
475                         if include_unstable_options {
476                             m_unstable
477                         } else {
478                             early_error(f_stable_attempt.to_string().as_slice());
479                         }
480                     }
481                     Err(_) => {
482                         // ignore the error from the unstable attempt; just
483                         // pass the error we got from the first try.
484                         early_error(f_stable_attempt.to_string().as_slice());
485                     }
486                 }
487             }
488         };
489
490     let r = matches.opt_strs("Z");
491     let include_unstable_options = r.iter().any(|x| *x == "unstable-options");
492
493     if matches.opt_present("h") || matches.opt_present("help") {
494         usage(matches.opt_present("verbose"), include_unstable_options);
495         return None;
496     }
497
498     // Don't handle -W help here, because we might first load plugins.
499
500     let r = matches.opt_strs("Z");
501     if r.iter().any(|x| *x == "help") {
502         describe_debug_flags();
503         return None;
504     }
505
506     let cg_flags = matches.opt_strs("C");
507     if cg_flags.iter().any(|x| *x == "help") {
508         describe_codegen_flags();
509         return None;
510     }
511
512     if cg_flags.contains(&"passes=list".to_string()) {
513         unsafe { ::llvm::LLVMRustPrintPasses(); }
514         return None;
515     }
516
517     if matches.opt_present("version") {
518         version("rustc", &matches);
519         return None;
520     }
521
522     Some(matches)
523 }
524
525 fn print_crate_info(sess: &Session,
526                     input: Option<&Input>,
527                     odir: &Option<Path>,
528                     ofile: &Option<Path>)
529                     -> bool {
530     if sess.opts.prints.len() == 0 { return false }
531
532     let attrs = input.map(|input| parse_crate_attrs(sess, input));
533     for req in sess.opts.prints.iter() {
534         match *req {
535             PrintRequest::Sysroot => println!("{}", sess.sysroot().display()),
536             PrintRequest::FileNames |
537             PrintRequest::CrateName => {
538                 let input = match input {
539                     Some(input) => input,
540                     None => early_error("no input file provided"),
541                 };
542                 let attrs = attrs.as_ref().unwrap().as_slice();
543                 let t_outputs = driver::build_output_filenames(input,
544                                                                odir,
545                                                                ofile,
546                                                                attrs,
547                                                                sess);
548                 let id = link::find_crate_name(Some(sess), attrs.as_slice(),
549                                                input);
550                 if *req == PrintRequest::CrateName {
551                     println!("{}", id);
552                     continue
553                 }
554                 let crate_types = driver::collect_crate_types(sess, attrs);
555                 let metadata = driver::collect_crate_metadata(sess, attrs);
556                 *sess.crate_metadata.borrow_mut() = metadata;
557                 for &style in crate_types.iter() {
558                     let fname = link::filename_for_input(sess, style,
559                                                          id.as_slice(),
560                                                          &t_outputs.with_extension(""));
561                     println!("{}", fname.filename_display());
562                 }
563             }
564         }
565     }
566     return true;
567 }
568
569 fn parse_crate_attrs(sess: &Session, input: &Input) ->
570                      Vec<ast::Attribute> {
571     let result = match *input {
572         Input::File(ref ifile) => {
573             parse::parse_crate_attrs_from_file(ifile,
574                                                Vec::new(),
575                                                &sess.parse_sess)
576         }
577         Input::Str(ref src) => {
578             parse::parse_crate_attrs_from_source_str(
579                 driver::anon_src().to_string(),
580                 src.to_string(),
581                 Vec::new(),
582                 &sess.parse_sess)
583         }
584     };
585     result.into_iter().collect()
586 }
587
588 pub fn list_metadata(sess: &Session, path: &Path,
589                      out: &mut io::Writer) -> io::IoResult<()> {
590     metadata::loader::list_file_metadata(sess.target.target.options.is_like_osx, path, out)
591 }
592
593 /// Run a procedure which will detect panics in the compiler and print nicer
594 /// error messages rather than just failing the test.
595 ///
596 /// The diagnostic emitter yielded to the procedure should be used for reporting
597 /// errors of the compiler.
598 pub fn monitor<F:FnOnce()+Send>(f: F) {
599     static STACK_SIZE: uint = 8 * 1024 * 1024; // 8MB
600
601     let (tx, rx) = channel();
602     let w = io::ChanWriter::new(tx);
603     let mut r = io::ChanReader::new(rx);
604
605     let mut cfg = thread::Builder::new().name("rustc".to_string());
606
607     // FIXME: Hacks on hacks. If the env is trying to override the stack size
608     // then *don't* set it explicitly.
609     if os::getenv("RUST_MIN_STACK").is_none() {
610         cfg = cfg.stack_size(STACK_SIZE);
611     }
612
613     match cfg.scoped(move || { std::io::stdio::set_stderr(box w); f() }).join() {
614         Ok(()) => { /* fallthrough */ }
615         Err(value) => {
616             // Thread panicked without emitting a fatal diagnostic
617             if !value.is::<diagnostic::FatalError>() {
618                 let mut emitter = diagnostic::EmitterWriter::stderr(diagnostic::Auto, None);
619
620                 // a .span_bug or .bug call has already printed what
621                 // it wants to print.
622                 if !value.is::<diagnostic::ExplicitBug>() {
623                     emitter.emit(
624                         None,
625                         "unexpected panic",
626                         None,
627                         diagnostic::Bug);
628                 }
629
630                 let xs = [
631                     "the compiler unexpectedly panicked. this is a bug.".to_string(),
632                     format!("we would appreciate a bug report: {}",
633                             BUG_REPORT_URL),
634                     "run with `RUST_BACKTRACE=1` for a backtrace".to_string(),
635                 ];
636                 for note in xs.iter() {
637                     emitter.emit(None, &note[], None, diagnostic::Note)
638                 }
639
640                 match r.read_to_string() {
641                     Ok(s) => println!("{}", s),
642                     Err(e) => {
643                         emitter.emit(None,
644                                      &format!("failed to read internal \
645                                               stderr: {}", e)[],
646                                      None,
647                                      diagnostic::Error)
648                     }
649                 }
650             }
651
652             // Panic so the process returns a failure code, but don't pollute the
653             // output with some unnecessary panic messages, we've already
654             // printed everything that we needed to.
655             io::stdio::set_stderr(box io::util::NullWriter);
656             panic!();
657         }
658     }
659 }
660
661 pub fn diagnostics_registry() -> diagnostics::registry::Registry {
662     use syntax::diagnostics::registry::Registry;
663
664     let all_errors = Vec::new() +
665         rustc::diagnostics::DIAGNOSTICS.as_slice() +
666         rustc_typeck::diagnostics::DIAGNOSTICS.as_slice() +
667         rustc_resolve::diagnostics::DIAGNOSTICS.as_slice();
668
669     Registry::new(&*all_errors)
670 }
671
672 pub fn main() {
673     let args = std::os::args();
674     let result = run(args);
675     std::os::set_exit_status(result);
676 }
677