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